simple-node-logger icon indicating copy to clipboard operation
simple-node-logger copied to clipboard

Example of how to override/extend FileAppender

Open Rychu-Pawel opened this issue 2 years ago • 0 comments

I lack documentation on how (or maybe even a possibility) to override/extend concrete appenders. This is my workaround for creating a logger with FileAppender that knows how to handle cause property of an Error. Code is in TypeScript. Maybe this will help someone or can be added as a part of documentation.

import OS from "os";
import util from "util";

import SimpleNodeLogger from "simple-node-logger";

private createLogger(logFilePath: string, level: SimpleNodeLogger.STANDARD_LEVELS, category: string): SimpleNodeLogger.Logger {
    const manager = new SimpleNodeLogger();

    const fileAppenderOptions: SimpleNodeLogger.IFileAppenderOptions = { logFilePath, level };

    const fileAppender = new SimpleNodeLogger.appenders.FileAppender(fileAppenderOptions);

    this.overrideObjectFormatter(fileAppender, fileAppenderOptions);

    manager.addAppender(fileAppender);

    return manager.createLogger(category, level);
}

private overrideObjectFormatter(fileAppender: SimpleNodeLogger.appenders.FileAppender, options: SimpleNodeLogger.IFileAppenderOptions) {
    // These are mostly original SimpleNodeLogger implementations with enhanced error formatting
    Object.defineProperty(fileAppender, `formatEntry`, {
        value(this: SimpleNodeLogger.AbstractAppender, entry: SimpleNodeLogger.IEntry): string[] {
            const fields: string[] = [];

            if (entry.domain)
                fields.push(entry.domain);

            fields.push(fileAppender.formatTimestamp(entry.ts));
            fields.push(fileAppender.formatLevel(entry.level));

            if (entry.category)
                fields.push(entry.category);

            fields.push(fileAppender.formatMessage(entry.msg, fileAppender));

            return fields;
        },
        writable: true,
        configurable: true,
    });

    const getErrorParts = (error: Error, prettyPrint?: boolean) => {
        const values: string[] = [];

        values.push(error.message);
        values.push((prettyPrint) ? JSON.stringify(error, null, 2) : JSON.stringify(error));

        if (error.stack)
            values.push(error.stack);

        if (error.cause) {
            values.push(`--- Caused by: ---`);
            values.push(...getErrorParts(error.cause));
        }

        return values;
    };

    Object.defineProperty(fileAppender, `formatObject`, {
        value(this: SimpleNodeLogger.appenders.FileAppender, value: any): string {
            if (!value)
                return ``;

            if (typeof value === `object`) {
                try {
                    if (value instanceof Error)
                        return getErrorParts(value).join(OS.EOL);

                    return (options.prettyPrint) ? JSON.stringify(value, null, 2) : JSON.stringify(value);
                }
                catch (ignore) {
                    return `json error: ${value.toString()}`;
                }
            }
            else {
                const s = value.toString();

                if (s === `[object Object]`)
                    return util.inspect(value);

                return s;
            }
        },
        writable: true,
        configurable: true,
    });
}

Rychu-Pawel avatar Aug 20 '22 12:08 Rychu-Pawel