Skip to content
bobby_dreamer

Changing log level at runtime using Winston logger

nodejs1 min read

Changing log level at runtime without restarting the node server is an interesting idea. Didn't see much posts on it but everywhere it was said, its doable.

I am going to accomplish this using following techniques.

  1. Updates to the environment variables needs to be picked by Node Server
  2. Changing the log level programatically in winston

1. Updating environment variables

Below is the content of the environment file. We are going to change NEW_VALUE variable in this test, initially it has 'three'.

.env
1PORT=4000
2NEW_VALUE=three

Below is the simple Node Express program and '/' route has the code to update environment variables. Basically we are reading the .env file and initializing values to process.env variable in a loop.

app_envars.js
1// Express pickup updated environment variables
2const express = require('express')
3var dotenv = require('dotenv')
4
5const app = express()
6
7require('dotenv').config();
8
9app.get('/', function (req, res) {
10 const envConfig = dotenv.config().parsed;
11 console.log(envConfig)
12 for (let k in envConfig) {
13 process.env[k] = envConfig[k]
14 }
15 console.log(`Hello World ${process.env.PORT} - ${process.env.NEW_VALUE}`)
16 res.send(`Hello World ${process.env.PORT} - ${process.env.NEW_VALUE}`)
17})
18
19app.listen(process.env.PORT)

Below is the console output

1>node app_envars
2{ PORT: '4000', NEW_VALUE: 'three' }
3Hello World 4000 - three
4{ PORT: '4000', NEW_VALUE: 'four' }
5Hello World 4000 - four

2. Changing the log level in Winston

Below is a simple NodeJS Winston program where log level is changed at runtime

win1.js
1// Change the log level programatically
2const winston = require('winston');
3
4const transports = {
5 console: new winston.transports.Console({ level: 'warn' }),
6};
7
8const logger = winston.createLogger({
9 transports: [
10 transports.console,
11 ]
12});
13
14transports.console.level = 'info';
15logger.info('Text Info');
16logger.warn('Text Warn');
17logger.error('Text error');
18logger.debug('Text Debug 1');
19
20transports.console.level = 'debug';
21logger.debug('Text Debug 2');

Below is teh output, you can notice 'Text Debug 1' did not appear. But after changing log level to 'debug'. It shows up.

1>node win1
2{"level":"info","message":"Text Info"}
3{"level":"warn","message":"Text Warn"}
4{"level":"error","message":"Text error"}
5{"level":"debug","message":"Text Debug 2"}

Logging levels in winston conform to the severity ordering specified by RFC5424: severity of all levels is assumed to be numerically ascending from most important to least important. When you set log level as info, you get all data above from info like error, warn and info.

LevelsNumbers
error0
warn1
info2
http3
verbose4
debug5
silly6

In the below program, i am going to combine above two concepts together.

Below is the main express program

app.js
1const express = require('express')
2const dotenv = require('dotenv')
3const path = require('path')
4
5let {winston_logger, setLevel} = require('./winston-logger')
6let logger = winston_logger();
7
8dotenv.config({ path: path.resolve(__dirname, `./${process.env.NODE_ENV}.env`)});
9
10if(process.env.NODE_ENV === 'development'){
11 setLevel(process.env.LOG_LEVEL)
12}else if(process.env.NODE_ENV === 'production'){
13 setLevel(process.env.LOG_LEVEL)
14}
15
16// const dotenv = require('dotenv').config()
17
18const app = express()
19const port = 3000;
20
21app.get('/', function (req, res) {
22 res.send('Hello World '+process.env.LOG_LEVEL)
23 console.log(`Started in ${process.env.NODE_ENV} mode`)
24
25 logger.info('Text Info');
26 logger.warn('Text Warn');
27 logger.error('Text error');
28 logger.debug('Text Debug');
29});
30
31app.get('/refresh', function (req, res) {
32 // setLevel('debug')
33 // const envConfig = dotenv.load().parsed;
34 const envConfig = dotenv.config({ path: path.resolve(__dirname, `./${process.env.NODE_ENV}.env`)}).parsed;
35 console.table(envConfig)
36 for (let k in envConfig) {
37 process.env[k] = envConfig[k]
38 console.log(`Assigning : ${k} = ${envConfig[k]}`)
39 }
40
41 setLevel(process.env.LOG_LEVEL)
42 res.send('Refreshed')
43})
44
45
46app.listen(port, () => {
47 console.log(`Winston example app listening at http://localhost:${port}`)
48})

Below program contains the funtions used in the above main program

./winston-logger/index.js
1const winston = require('winston')
2const {format, createLogger} = require('winston');
3const {timestamp, combine, printf, errors} = format;
4
5const transports = {
6 console: new winston.transports.Console({ level: 'info' }),
7};
8
9function setLevel(level){
10 transports.console.level = level;
11}
12
13function winston_logger(){
14 const logFormat = printf(({ level, message, label, timestamp, stack }) => {
15 return `${timestamp} [${level}] ${stack || message}`;
16});
17
18return createLogger({
19 // level: process.env.LOG_LEVEL,
20 // format: winston.format.simple(),
21 format: combine(
22 timestamp({ format: 'YYYY-MM-DD HH:mm:ss'}),
23 errors({ stack:true }),
24 logFormat
25 ),
26 // defaultMeta: { service: 'user-service' },
27 transports: [
28 transports.console,
29 ]
30});
31
32}
33
34module.exports = {winston_logger, setLevel};

Data in environment files

1>cat development.env
2LOG_LEVEL=debug
3>cat production.env
4LOG_LEVEL=info

Below is the output of the program and able to change the log level at runtime.

Express app execution

Thats all for now. Thanks for reading