Noteworthy Programming Masterpiece

    meta2-logger
    TypeScript icon, indicating that this package has built-in type declarations

    2.1.1 • Public • Published

    meta2-logger

    Simple logging library for NodeJS with support of facilities and multiple transports.

    Logger has also first-class support for TypeScript and is fully documented.

    Table of Contents:

    Features

    Built-in transports:

    • Console
    • Text file
    • JSON file
    • Memory
    • GrayLog

    Log levels:

    Library supports all of standard syslog levels.

    • debug
    • info
    • notice
    • warn
    • error
    • critical
    • alert
    • emergency

    Other features:

    • Facilities
    • Meta-data
    • Custom targets
    • Colorized console output
    • TypeScript decorators to simply enable logging on classes and methods
    • Remote management server with UI and REST API provided by meta2-logger-server package

    Installation

    # Using NPM 
    npm install meta2-logger
     
    # To save as dependency 
    npm install --save meta2-logger

    Usage (TypeScript)

    import {Logger, LOG_LEVEL, default as logger} from "meta2-logger";
     
    // Use global logger
    logger.info("From global logger");
     
    // Or create new instance
    const myLogger = new Logger({
        level: LOG_LEVEL.DEBUG
    });
     
    // Setup targets
    logger.toConsole({
        level: LOG_LEVEL.INFO,
        timestamp: true,
        colorize: true
    }).toFile("demo.log", {
        level: LOG_LEVEL.WARN,
        timestamp: true,
        facilities: [ "test" ]
    }).toJsonFile("demo.json", {
        level: LOG_LEVEL.ERROR
    }).toGrayLog({
        level: LOG_LEVEL.DEBUG,
        graylogHostname: "localhost"
        host"myApp"
    });
     
    // Log some messages
    logger.debug("Hello %s", "debug");
    logger.info("Hello %s", "info");
    logger.notice("Hello %s", "notice");
    logger.warn("Hello %s", "warn"); // or logger.warning("Hello %s", "warn");
    logger.error("Hello %s", "error");
    logger.crit("Hello %s", "critical");
    logger.alert("Hello %s", "alert");
    logger.emerg("Hello %s", "emergency"); // or logger.panic("Hello %s", "emergency");
     
    // Create facility
    const facility = logger.facility("http", {
        level: LOG_LEVEL.INFO
    });
     
    facility.notice("Server started on port %d", 8080);
     
    // Passing meta-data using object as first argument
    facility.warn({ reqId: 123 }, "Bad request");

    Usage (vanilla JS)

    const Logger = require("meta2-logger");
     
    // Accessing global logger instance
    const logger = Logger.default;
     
    // Or create new instance
    const logger = new Logger.Logger();
     
    // Setup targets
    logger.toConsole({
        level: Logger.LOG_LEVEL.INFO,
        timestamp: true,
        colorize: true
    }).toFile("demo.log", {
        level: Logger.LOG_LEVEL.WARN,
        timestamp: true,
        facilities: [ "test" ]
    }).toJsonFile("demo.json", {
        level: Logger.LOG_LEVEL.ERROR
    }).toGrayLog({
        level: Logger.LOG_LEVEL.DEBUG,
        graylogHostname: "localhost"
        host: "myApp"
    });
     
    // Log some messages
    logger.debug("Hello %s", "debug");
    logger.info("Hello %s", "info");
    logger.notice("Hello %s", "notice");
    logger.warn("Hello %s", "warn"); // or logger.warning("Hello %s", "warn");
    logger.error("Hello %s", "error");
    logger.crit("Hello %s", "critical");
    logger.alert("Hello %s", "alert");
    logger.emerg("Hello %s", "emergency"); // or logger.panic("Hello %s", "emergency");
     
    // Create facility
    const facility = logger.facility("http");
     
    facility.notice("Server started on port %d", 8080);
     
    // Passing meta-data using object as first argument
    facility.warn({ reqId: 123 }, "Bad request");

    Facilities

    Logging facility provides a way to decouple logging based on their purpose.

    Creating facility from logger:

    import {default as logger, LOG_LEVEL} from "meta2-logger";
     
    // Create facility from main logger - options are optional
    const facility = logger.facility("facilityName", {
        level: LOG_LEVEL.INFO
    });
     
    // We can change facility log level later
    facility.setLevel(LOG_LEVEL.NOTICE);
    facility.getLevel(); // -> LOG_LEVEL.NOTICE
     
    // Log to facility
    facility.info("Message");
     
    // We can get existing facility instance by calling facility method again
    const facilityAgain = logger.facility("facilityName");
     
    // We can get map of all registered facilities
    logger.getAllFacilities(); // -> { facilityName: LoggerFacility }

    Creating facility manually:

    import {default as logger, LOG_LEVEL, LoggerFacility} from "meta2-logger";
     
    // Create facility and pass logger as first argument, options are optional
    const facility = new LoggerFacility(logger, "facilityName", {
        level: LOG_LEVEL.INFO
    });
     
    // Log to facility
    facility.info("Message");

    Setting log level

    Log levels can be set on logger, facility and on targets.

    Classes Logger, LoggerFacility and logging targets support setLevel and getLevel methods.

    Example:

    import {default as logger, LOG_LEVEL} from "meta2-logger";
     
    logger.setLevel(LOG_LEVEL.INFO);
     
    logger.debug("Message"); //Does not log
    logger.warn("Message"); //Does log
     
    logger.getLevel(); //Returns LOG_LEVEL.INFO;
     
    // Changing log level on target(s)
    logger.toConsole({
        level: LOG_LEVEL.NOTICE
    });
     
    logger.getTarget("__console__").setLevel(LOG_LEVEL.DEBUG);
    logger.getTarget("__console__").getLevel(); // -> LOG_LEVEL.DEBUG
     
    // Also works on LoggerFacility
    const facility = logger.facility("http");
     
    facility.setLevel(LOG_LEVEL.WARN);
    facility.getLevel(); // -> LOG_LEVEL.WARN
     

    Configuring Logging Targets

    Logging target represents destination transport for log messages.

    import {default as logger, ConsoleTarget, LOG_LEVEL} from "meta2-logger";
     
    logger.to("uniqueLoggerId", new ConsoleTarget({
        level: Logger.LOG_LEVEL.INFO,
        timestamp: true,
        colorize: true
    }));

    See built-in targets below. Logging target class must implement ILoggerTarget interface.

    Console Target

    Prints log messages to stdout (console).

    import {default as logger, ConsoleTarget, LOG_LEVEL} from "meta2-logger";
     
    logger.toConsole({
        // Log level
        level: LOG_LEVEL.DEBUG,
     
        // Facilities - null to accept all facilities
        facilities: [ "http", "broker", "etc" ],
     
        // If to print time and date
        timestamp: true,
     
        // If to print with colors
        colorize: true
    });
     
    // Or
    logger.to("myConsole", new ConsoleTarget(opts));

    Sample output:

    2018-01-10 14:24:30 warn: [http] (reqId=123) Bad request
    2018-01-10 14:24:30 info: Something happend
    
    eg.:
    date time level: [facility] (meta=data) Formatted message
    

    Notice: Method toConsole overrides previous console target settings. Use logger.to(...) method to define more targets.

    File Target

    Appends log messages to specified file.

    Messages are formatted the same way as to the console.

    File target can be set for multiple files with different configurations.

    import {default as logger, FileTarget, LOG_LEVEL} from "meta2-logger";
     
    logger.toFile("filename.log", {
        // Log level
        level: LOG_LEVEL.DEBUG,
     
        // Facilities - null to accept all facilities
        facilities: [ "http", "broker", "etc" ],
     
        // If to print time and date
        timestamp: true,
    });
     
    // Or
    logger.to("myFile", new FileTarget("filename.log", opts));

    Sample output:

    2018-01-10 14:24:30 warn: [http] (reqId=123) Bad request
    2018-01-10 14:24:30 info: Something happend
    

    JSON File Target

    Appends log messages to specified JSON file.

    File target can be set for multiple files with different configurations.

    import {default as logger, JsonFileTarget, LOG_LEVEL} from "meta2-logger";
     
    logger.toJsonFile("filename.json", {
        // Log level
        level: LOG_LEVEL.DEBUG,
     
        // Facilities - null to accept all facilities
        facilities: [ "http", "broker", "etc" ]
    });
     
    // Or
    logger.to("myJsonFile", new JsonFileTarget("filename.json", opts));

    Sample output:

    {},
    { timestamp: 1515557050.342, level: 5, facility: "http", msg: "Bad request", meta: { reqId: 123 } },
    { timestamp: 1515557086.342, level: 7, facility: null, msg: "Something happend", meta: {} }
    

    Recommended way to parse JSON log file:

    const fs = require("fs");
     
    const logFile = fs.readFileSync("filename.json", { encoding: "utf-8" });
    const logMessages = JSON.parse("[" + logFile + "]").slice(1);

    Memory Target

    Stores log messages in memory.

    import {default as logger, MemoryTarget, LOG_LEVEL} from "meta2-logger";
     
    logger.toMemory({
        // Log level
        level: LOG_LEVEL.DEBUG,
     
        // Facilities - null to accept all facilities
        facilities: [ "http", "broker", "etc" ],
     
        // How many messages to store, default 1000
        limit: 1000
    });
     
    // Get messages
    logger.getTarget("__memory__").getMessages();
     
    // Or
    const memTarget = new MemoryTarget(opts);
     
    logger.to("myMemory", memTarget);
     
    memTarget.getMessages();

    GrayLog Target

    Sends log messages to GrayLog server using GELF protocol.

    import {default as logger, GraylogTarget, LOG_LEVEL} from "meta2-logger";
     
    logger.toGrayLog({
        // Log level
        level: LOG_LEVEL.DEBUG,
     
        // Facilities - null to accept all facilities
        facilities: [ "http", "broker", "etc" ],
     
        // GrayLog server port
        graylogPort: 12201,
     
        // GrayLog server hostname
        graylogHostname: "localhost",
     
        // Connection type, 'lan' or 'wan'
        connection: "lan",
     
        // Max chunk size for WAN type
        maxChunkSizeWan: 1420,
     
        // Max chunk size for LAN type
        maxChunkSizeLan: 8154,
     
        // Host (application) identifier
        host: "_unspecified_",
     
        // GELF protocol version
        version: "1.0",
     
        // Facility prefix string - is added before facility name
        facilityPrefix: "",
     
        // If to log gelf client debug messages to stdout
        debugGelfClient: false,
     
        // Additional static message fields
        additionalFields: { "myfield": "myValue" }
    });
     
    // Or
    logger.to("myGrayLogTarget", new GraylogTarget(opts));

    Notice: Message meta-data are added as additional fields.

    Custom Logging Target

    To create custom logging target define class which implements ILoggerTarget interface. Or extend class BaseTarget.

    import * as util from "util";
     
    import {
        LOG_LEVEL, ILoggerTarget, ILoggerMetaData, BaseTarget, IBaseTargetOptions,
        default as logger
    } from "./interfaces";
     
    export interface ISuchTargetOptions extends IBaseTargetOptions {
        soFunny?: boolean;
    }
     
    export class SuchTarget extends BaseTarget {
     
        protected soFunny: boolean:
     
        public constructor(options: ISuchTargetOptions) {
     
            super(options);
     
            this.soFunny = options.soFunny || false;
     
        }
     
        /**
         * Log message
         *
         * @param level Log level
         * @param facility Facility
         * @param args Message arguments
         * @param meta Meta-data
         */
        public log(level: LOG_LEVEL, facility: string, args: any, meta: ILoggerMetaData) {
     
            // Check such level
            if (level > this.level) return;
            if (this.facilities.length > 0 && this.facilities.indexOf(facility) < 0) return;
     
            // Format wow message
            const wowMessage = util.format.apply(this, args);
     
            // Create message parts
            const messageParts = [
                "Wow", Date.now(),
                "such", this.levelLabels[LOG_LEVEL.DEBUG].toUpperCase(),
                "many", facility,
                wowMessage,
                "plz",
                JSON.stringify(meta)
            ];
     
            if(this.soFunny)
                messageParts.push("so funny");
     
            // Write message
            this.write(level, facility, messageParts, meta);
     
        }
     
        /**
         * Write formatted log message
         *
         * @param level Log level
         * @param facility Facility
         * @param msg Formated message parts
         * @param meta Meta-data
         */
        protected write(level: LOG_LEVEL, facility: string, message: Array<string>, meta: ILoggerMetaData) {
     
            console.log("So scare", message.join(" "));
     
        }
     
    }
     
    // Use such target
    logger.to("suchTarget", new SuchTarget({
        level: LOG_LEVEL.DEBUG,
        soFunny: true
    }));

    Utility Functions

    parseLogLevel

    This function parses log level from string to number. Is case insensitive.

    Usage:

    import {default as logger, parseLogLevel} from "meta2-logger";
     
    logger.toConsole({
        level: parseLogLevel("criTIcal")
    });

    @Logging decorator

    Decorator to configure and assign LoggerFacility instance to a class.

    Note: decorators are experimental TypeScript feature.

    Usage:

    import {Logger, Logging, LoggerFacility, LOG_LEVEL} from "meta2-logger";
     
    const myLogger = new Logger();
     
    // Second argument can be omitted
    @Logging("facilityName", {
        logger: myLogger, // When omitted default logger will be used
        level: LOG_LEVEL.DEBUG
    })
    class MyClass {
     
        public log: LoggerFacility;
     
        public doSomething(){
     
            this.log.info("Hello");
     
        }
     
    }
     
    const obj = new MyClass();
    obj.doSomething(); // -> will log debug: [facilityName] Hello

    @LogMethodCall decorator

    Decorator to log every method call. When @Logging decorator is applied it's configuration will be used.

    Note: decorators are experimental TypeScript feature.

    Usage:

    import {Logger, Logging, LogMethodCall, LoggerFacility, LOG_LEVEL} from "meta2-logger";
     
    const myLogger = new Logger();
     
    // Second argument can be omitted
    @Logging("facilityName", {
        logger: myLogger, // When omitted default logger will be used
        level: LOG_LEVEL.DEBUG
    })
    class MyClass {
     
        public log: LoggerFacility;
     
        /*
         * Decorator arguments (all are optional):
         * - LOG_LEVEL
         * - If to capture arguments
         * - Message prefix
         */
        @LogMethodCall(LOG_LEVEL.DEBUG, true, "Hey")
        public doSomething(...args){
            return true;
        }
     
    }
     
    const obj = new MyClass();
    obj.doSomething("hello", "world");

    will log:

    debug: [facilityName] (method=doSomething) (class=MyClass) Hey Method MyClass.doSomething called with arguments [ 'hello', 'world' ]
        at class_1.descriptor.value [as doSomething] (/path/to/app/node_modules/meta2-logger/dist/src/util.ts:90:38)
        at Logger.info (/path/to/app/node_modules/meta2-logger/dist/src/Logger.js:286:14)
        at Object.<anonymous> (/path/to/app/index.js:30:24)
        at Module._compile (module.js:571:32)
        at Object.Module._extensions..js (module.js:580:10)
        at Module.load (module.js:488:32)
        at tryModuleLoad (module.js:447:12)
        at Function.Module._load (module.js:439:3)
        at Module.runMain (module.js:605:10)

    Logging Stack Trace (experimental)

    Logger has built-in feature to capture stack trace for every log message. If enabled call stack will be available as trace meta value. Note that internal logger function calls are excluded.

    Warning: Capturing of stack traces has a significant impact on performance and should be used only for temporary debugging.

    This feature is currently experimental.

    Also, note that this feature does not affect possibility to log stack trace by passing an error object as an argument to a log method - eg ... catch(err){ logger.error("Operation failed:", err); } will work always.

    Following example:

    import {default as logger} from "meta2-logger";
     
    logger.enableTrace();
    logger.toConsole();
     
    logger.info("Hello!");
     
    // To turn it off
    logger.enableTrace(false);

    will print to console:

    info: hello
      >>
        at Logger.info (/path/to/app/node_modules/meta2-logger/dist/src/Logger.js:286:14)
        at Object.<anonymous> (/path/to/app/index.js:30:24)
        at Module._compile (module.js:571:32)
        at Object.Module._extensions..js (module.js:580:10)
        at Module.load (module.js:488:32)
        at tryModuleLoad (module.js:447:12)
        at Function.Module._load (module.js:439:3)
        at Module.runMain (module.js:605:10)

    API Reference

    class Logger {
        getLevel(): LOG_LEVEL;
        setLevel(level: LOG_LEVEL);
        log(level: LOG_LEBEL, ...args): void;
        debug(...args): void;
        info(...args): void;
        notice(...args): void;
        warn(...args): void;
        warning(...args): void;
        error(...args): void;
        crit(...args): void;
        alert(...args): void;
        emerg(...args): void;
        panic(...args): void;
        facility(name: string): LoggerFacility;
        getAllFacilities(): { [K: string]: LoggerFacility };
        to(id: string, target: ILoggerTarget): Logger;
        getAllTargets(): { [K: string]: ILoggerTarget };
        getTarget(id: string): ILoggerTarget|null;
        toConsole(options: IConsoleTargetOptions = {}): Logger;
        toFile(filename: string, options: IFileTargetOptions = {}): Logger;
        toJsonFile(filename: string, options: IFileTargetOptions = {}): Logger;
        toGrayLog(options: IGraylogTargetOptions): Logger;
        enableTrace(enabled: boolean);
        isTraceEnabled(): boolean;
     
        // Close all I/O and socket handles
        close();
    }
     
    class LoggerFacility {
        constructor(protected logger: Logger, protected prefix: string);
        getLevel(): LOG_LEVEL;
        setLevel(level: LOG_LEVEL);
        debug(...args): void;
        info(...args): void;
        notice(...args): void;
        warn(...args): void;
        warning(...args): void;
        error(...args): void;
        crit(...args): void;
        alert(...args): void;
        emerg(...args): void;
        panic(...args): void;
    }
     
    enum LOG_LEVEL {
        DEBUG = 7,
        INFO = 6,
        NOTICE = 5,
        WARN = 4,
        ERROR = 3,
        CRITICAL = 2,
        ALERT = 1,
        EMERGENCY = 0
    }
     
    interface ILogger {
        log(...args);
        debug(...args);
        info(...args);
        notice(...args);
        warn(...args);
        warning(...args);
        error(...args);
        crit(...args);
        alert(...args);
        emerg(...args);
        panic(...args);
        getLevel(): LOG_LEVEL;
        setLevel(level: LOG_LEVEL);
    }
     
    interface ILoggerMetaData {
        [ K: string ]: string|number|boolean|Date;
        [ K: number ]: string|number|boolean|Date;
    }
     
    interface ILoggerTarget {
        log: (level: LOG_LEVEL, facility: string, args: Array<any>, meta: ILoggerMetaData) => void;
        close: () => void;
        getLevel(): LOG_LEVEL;
        setLevel(level: LOG_LEVEL);
    }
     
    function parseLogLevel(level: string): LOG_LEVEL;

    Development

    # Install dependencies 
    npm install
     
    # Transpile TypeScript 
    npm run build
     
    # Run linter 
    npm run lint
     
    # Run tests 
    npm test

    License

    This library is published under MIT license.

    Copyright (c) 2017 - 2018 Jiri Hybek, jiri@hybek.cz

    Install

    npm i meta2-logger

    DownloadsWeekly Downloads

    10

    Version

    2.1.1

    License

    MIT

    Unpacked Size

    120 kB

    Total Files

    36

    Last publish

    Collaborators

    • metaplatform