Search code examples
typescriptwinston

Winston custom log levels typescript definitions


I am working on a node application and using Typescript. I have winston 3. In my code I have added custom log levels;

const myCustomLevels = {
    levels: {
        data: 10,
        protocol: 9,
        debug: 8,
        info: 7,
        notice: 6,
        note: 5,
        warn: 4,
        error: 3,
        crit: 2,
        alert: 1,
        emerg: 0,
    }
}

then

const logger = winston.createLogger({
    level: data,
    levels: myCustomLevels.levels,
    format: winston.format.combine(
        winston.format.json()
    ),
    transports: [new winston.transports.Console()],
});

The problem I need help with is when I use the logger Typescript complains.

logger.protocol({});

in this case the types are const logger: winston.Logger and the ts says [ts] Property 'protocol' does not exist on type 'Logger'. [2339]. Typescript doesn't know about my levels.

How do I correct this so that tsc knows about my levels on the logger?


Solution

  • The definitions as currently written do not unfortunately allow for custom log levels. The simplest solution is to cast the returned logger to a logger and an intersection with a type that contains the extra methods. We can create this type based on your const declaration using the mapped type Record and keyof

    import * as winston from 'winston'
    const logger = winston.createLogger({
        level: data,
        levels: myCustomLevels.levels,
        format: winston.format.combine(
            winston.format.json()
        ),
        transports: [new winston.transports.Console()],
    }) as winston.Logger & Record<keyof typeof myCustomLevels['levels'], winston.LeveledLogMethod>;
    
    logger.protocol({})
    

    Note I toyed around with augmenting the existing module definition but because createLogger is declared as a variable with an inline function signature we can't really extend it with augmentation.

    If this a general problem for you, you could write a general function that preserves the levels:

    function createWinstonLogger<T>(options?: Pick<winston.LoggerOptions, Exclude<keyof winston.LoggerOptions, 'levels'>> & { levels: T }) {
        return winston.createLogger(options) as winston.Logger & Record<keyof T, winston.LeveledLogMethod>
    }
    const logger = createWinstonLogger({
        level: data,
        levels: myCustomLevels.levels,
        format: winston.format.combine(
            winston.format.json()
        ),
        transports: [new winston.transports.Console()],
    });
    logger.protocol({})