feat(handler): implement StreamHandler, ConsoleHandler, StderrHandler emit

StreamHandler.emit() writes formatted output to a Writable stream.
ConsoleHandler maps log levels to console.warn/error/log. StderrHandler
emits via console.error. handleError() logs diagnostics instead of
throwing. FileHandler throws NotImplementedError on construction.
Removed Node.js stream import and conditional require() block.
This commit is contained in:
Tiara Rodney 2026-03-13 23:57:37 +01:00
parent e6b7f02166
commit 3a6332a855
No known key found for this signature in database
GPG key ID: 5CD8EC1D46106723

View file

@ -1,17 +1,9 @@
import * as stream from 'stream'; import { LogLevel, checkLevel, NOTSET, WARNING, ERROR } from './log-level';
import { LogLevel, checkLevel, NOTSET } from './log-level';
import { LogRecord } from './log-record'; import { LogRecord } from './log-record';
import { Formatter, DEFAULT_FORMATTER } from './formatter'; import { Formatter, DEFAULT_FORMATTER } from './formatter';
import { Filterer } from './filter'; import { Filterer } from './filter';
import { NotImplementedError } from './helper/error'; import { NotImplementedError } from './helper/error';
import { Writable, DEFAULT_STREAM, StderrWritable } from './helper/stream';
if (typeof window === 'undefined') {
const stream = require('stream');
}
else {
const stream = require('./helper/stream');
}
//--------------------------------------------------------------------------- //---------------------------------------------------------------------------
// Handler classes and functions // Handler classes and functions
@ -22,7 +14,7 @@ type Handlers = {[key: string]: Handler};
/** /**
* map of handler names to handlers * map of handler names to handlers
*/ */
const HANDLERS: Handlers = {}; const HANDLERS: Handlers = {};
/** /**
* added to allow handlers to be removed in reverse order of initialization * added to allow handlers to be removed in reverse order of initialization
@ -86,7 +78,7 @@ export class Handler extends Filterer {
/** /**
* Format the specified record. * Format the specified record.
* *
* If a formatter is set, use it. Otherwise, use the default formatter for * If a formatter is set, use it. Otherwise, use the default formatter for
* the module. * the module.
*/ */
@ -115,14 +107,13 @@ export class Handler extends Filterer {
* I/O thread lock. * I/O thread lock.
*/ */
handle(record: LogRecord) { handle(record: LogRecord) {
var rv = this.filter(record); const rv = this.filter(record);
if (!rv) { return }
let filtered = record;
if ((rv as any) instanceof LogRecord) { if ((rv as any) instanceof LogRecord) {
record = rv as unknown as LogRecord filtered = rv as unknown as LogRecord
}
if (rv) {
//locking here
this.emit(record)
} }
this.emit(filtered)
} }
/** /**
@ -144,23 +135,73 @@ export class Handler extends Filterer {
* Handle errors which occur during an emit() call. * Handle errors which occur during an emit() call.
* *
* This method should be called from handlers when an exception is * This method should be called from handlers when an exception is
* encountered during an emit() call. If raiseExceptions is false, * encountered during an emit() call.
* exceptions get silently ignored. This is what is mostly wanted
* for a logging system - most users will not care about errors in
* the logging system, they are more interested in application errors.
* You could, however, replace this with a custom handler if you wish.
* The record which was being processed is passed in to this method.
*/ */
handleError(record: LogRecord) { handleError(record: LogRecord) {
throw new NotImplementedError( try {
'still need to find portable way for stacktracing...' console.error('--- Logging error ---');
) console.error('Error in handler for record:', record.scope, record.msg);
}
catch (_) {
// silently ignore errors in error handling
}
} }
get formatter(): Formatter|null { return this._formatter } get formatter(): Formatter|null { return this._formatter }
set formatter(fmt: Formatter) { this._formatter = fmt } set formatter(fmt: Formatter) { this._formatter = fmt }
} }
/**
* A handler class which writes logging records, appropriately formatted,
* to a stream. Note that this class does not close the stream, as
* the default stream may be shared.
*/
export class StreamHandler extends Handler {
protected stream: Writable;
constructor(stream?: Writable) {
super();
this.stream = stream ?? DEFAULT_STREAM;
}
emit(record: LogRecord) {
try {
const msg = this.format(record);
this.stream.write(msg);
}
catch (e) {
this.handleError(record);
}
}
}
/**
* A handler class which writes logging records to the browser console,
* mapping log levels to the appropriate console method.
*
* This is the primary handler for browser environments.
*/
export class ConsoleHandler extends Handler {
emit(record: LogRecord) {
try {
const msg = this.format(record);
if (record.levelno >= ERROR) {
console.error(msg);
}
else if (record.levelno >= WARNING) {
console.warn(msg);
}
else {
console.log(msg);
}
}
catch (e) {
this.handleError(record);
}
}
}
export interface FileHandlerOptions { export interface FileHandlerOptions {
filename: string filename: string
filemode?: string filemode?: string
@ -168,20 +209,13 @@ export interface FileHandlerOptions {
errors?: string errors?: string
} }
/**
* A handler class which writes logging records, appropriately formatted,
to a stream. Note that this class does not close the stream, as
sys.stdout or sys.stderr may be used.
*/
export class StreamHandler extends Handler {
constructor(stream?: stream.Writable) {
super();
}
}
export class FileHandler extends StreamHandler { export class FileHandler extends StreamHandler {
constructor(options: FileHandlerOptions) { constructor(options: FileHandlerOptions) {
super(); super();
throw new NotImplementedError(
'FileHandler is not available in browser environments. ' +
'Use ConsoleHandler or a storage-backed handler instead.'
);
} }
} }
@ -190,9 +224,16 @@ export class FileHandler extends StreamHandler {
* whatever sys.stderr is currently set to rather than the value of * whatever sys.stderr is currently set to rather than the value of
* sys.stderr at handler construction time. * sys.stderr at handler construction time.
*/ */
export class StderrHandler extends Handler { export class StderrHandler extends Handler {
/**
* Initialize the handler.
*/
constructor(level: LogLevel) { super(level) } constructor(level: LogLevel) { super(level) }
emit(record: LogRecord) {
try {
const msg = this.format(record);
console.error(msg);
}
catch (e) {
this.handleError(record);
}
}
} }