From 3a6332a8553d2e9da34559363fd82761db78697b Mon Sep 17 00:00:00 2001 From: Tiara Rodney Date: Fri, 13 Mar 2026 23:57:37 +0100 Subject: [PATCH] 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. --- src/handler.ts | 125 ++++++++++++++++++++++++++++++++----------------- 1 file changed, 83 insertions(+), 42 deletions(-) diff --git a/src/handler.ts b/src/handler.ts index 24ff076..e174133 100644 --- a/src/handler.ts +++ b/src/handler.ts @@ -1,17 +1,9 @@ -import * as stream from 'stream'; - -import { LogLevel, checkLevel, NOTSET } from './log-level'; +import { LogLevel, checkLevel, NOTSET, WARNING, ERROR } from './log-level'; import { LogRecord } from './log-record'; import { Formatter, DEFAULT_FORMATTER } from './formatter'; import { Filterer } from './filter'; import { NotImplementedError } from './helper/error'; - -if (typeof window === 'undefined') { - const stream = require('stream'); -} -else { - const stream = require('./helper/stream'); -} +import { Writable, DEFAULT_STREAM, StderrWritable } from './helper/stream'; //--------------------------------------------------------------------------- // Handler classes and functions @@ -22,7 +14,7 @@ type Handlers = {[key: string]: Handler}; /** * map of handler names to handlers */ -const HANDLERS: Handlers = {}; +const HANDLERS: Handlers = {}; /** * 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. - * + * * If a formatter is set, use it. Otherwise, use the default formatter for * the module. */ @@ -115,14 +107,13 @@ export class Handler extends Filterer { * I/O thread lock. */ 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) { - record = rv as unknown as LogRecord - } - if (rv) { - //locking here - this.emit(record) + filtered = rv as unknown as LogRecord } + this.emit(filtered) } /** @@ -144,23 +135,73 @@ export class Handler extends Filterer { * Handle errors which occur during an emit() call. * * This method should be called from handlers when an exception is - * encountered during an emit() call. If raiseExceptions is false, - * 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. + * encountered during an emit() call. */ handleError(record: LogRecord) { - throw new NotImplementedError( - 'still need to find portable way for stacktracing...' - ) + try { + 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 } 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 { filename: string filemode?: string @@ -168,20 +209,13 @@ export interface FileHandlerOptions { 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 { constructor(options: FileHandlerOptions) { 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 * sys.stderr at handler construction time. */ - export class StderrHandler extends Handler { - /** - * Initialize the handler. - */ +export class StderrHandler extends Handler { constructor(level: LogLevel) { super(level) } + + emit(record: LogRecord) { + try { + const msg = this.format(record); + console.error(msg); + } + catch (e) { + this.handleError(record); + } + } }