3

Currently the Winston configuration we are using is hard coded in a Javscript module, though it would be useful to be able to modify this configuration at runtime. This would allow us to change the path of the log file, format or log level, for example, without needing to modify the code.

I am coming from the school of thought of a logger such as log4j in the Java world, where you can specify what you need in an XML file or properties file (see here), rather than programmatically. In the NodeJS world this would be in a JSON file.

I did find the package 'winston-config', but unfortunately it is not compatible with Winston 3.

Does anyone have an approach that could allow for this? I did not see anything of note in the documentation and I would rather avoid having to craft our own logic for this.

Andre M
  • 6,649
  • 7
  • 52
  • 93
  • Check this out too if needed : https://www.npmjs.com/package/morgan Also, you can set the logging level in the process and set it at runtime but when you have to change the logging level, you have to restart the app. – LearningEveryday Apr 20 '20 at 19:31
  • This answer has the setting of logging level - https://stackoverflow.com/a/22934566/957026 Highly recommending it to put in the process as I have mentioned above. – LearningEveryday Apr 20 '20 at 19:33
  • @LearningEveryday This just provides me an alternative logger, without actually providing a solution for adjusting the log configuration from a file. – Andre M Apr 20 '20 at 19:46
  • When you say a file, a ,json file like this might work: https://medium.com/@felipedutratine/manage-config-in-nodejs-to-get-variables-from-file-and-env-variables-87526509aad1 Also, why not use the values from the process like `process.env.loggingLevel` ? – LearningEveryday Apr 20 '20 at 19:56
  • It more than just log level. It is trying to avoid doing this programmatically . Now clarified in question. – Andre M Apr 20 '20 at 20:38

2 Answers2

1

I did not end up finding a package that suited me, so I put something together and have opted to share it. It is basic, but does the job I needed.

The source code. The config is a JSON structure, with an entry for the logging configuration.

const loggingConfig = config.logging;

if (!loggingConfig) {
    throw new Error('No logging configuration was found');
}

const formatter = printf(({ level, message, label, timestamp, stack }) => {
    if (!stack) {
        return `${timestamp} [${label}] ${level}: ${message}`;
    } else {
        return `${timestamp} [${label}] ${level}: ${message}\n  ${stack}`;
    }
});

const logLabel = 'main';

const formats = {
    colorized: combine(
        colorize(),
        errors({ stack: true }),
        label({ label: logLabel }),
        timestamp(),
        formatter
    ),
    'default': combine(
        errors({ stack: true }),
        label({ label: logLabel }),
        timestamp(),
        formatter
    )
};

const transports = Object.values(loggingConfig.outputs).map((transportConfig: any) => {
    const { type, formatName, ...other } = transportConfig;

    let selectedFormat = formats[formatName];
    if (!selectedFormat) {
        selectedFormat = formats['default'];
    }

    if (type === 'console') {
        return new winston.transports.Console(Object.assign(other, { format: selectedFormat }));
    } else if (type === 'file') {
        return new winston.transports.File(Object.assign(other, { format: selectedFormat }));
    } else {
        throw new Error(`Invalid transport configuation in ${JSON.stringify(transportConfig)}`);
    }
});

Then the JSON structure looks as follows:

{
    "logging": {
        "outputs": {
            "console": {
                "type": "console",
                "level": "debug",
                "formatName": "colorized"
            },
            "main": {
                "type": "file",
                "level": "debug",
                "filename": "logs/main.log",
                "formatName": "default"
            }
        }
    }
}

This approach allows me to have a different log configuration in development, on the continuous integration machine and in production.

This works for me with Winston 3.

Andre M
  • 6,649
  • 7
  • 52
  • 93
0

I've had great success using the config package for this. It scales up well, and it makes handoffs from developers to test and operations much MUCH smoother.

You can use it like this:

const config = require( 'config' )
require( 'properties' )  // lets you use property file format as well as JSON.
const logLevel = config.get( 'log.logLevel' ) || 'info'
// then use logLevel to configure winston

This is very cool for projects you move from development to staging to production, because you can have separate configurations for each environment. Your config files go in a config directory.

In that directory you can have default.properties with, maybe, these contents.

[log]
logLevel = warn

And, you can have another file with the name development.properties with this

[log]
logLevel = info

And, if you want, you can have a local.properties file, which you should mention in your .gitignore file, with this

[log]
logLevel = debug

This lets you choose your config values for each deployment environment as you develop.

O. Jones
  • 103,626
  • 17
  • 118
  • 172