last modified last modified October 18, 2023
In this article we show how to do logging in JavaScript with Winston.js library, and demonstrates logging in several code examples.
Logging is the process of writing information into log files. Log files include information about various events that happened in operating system, software, or in communication.
Logging is done for the following purposes:
Logging is not limited to identifying errors in software development. It is also used in detecting security incidents, monitoring policy violations, providing information in case of problems, finding application bottlenecks, or generating usage data.
Events that should be logged include input validation failures, authentication and authorization failures, application errors, configuration changes, and application start-ups and shut-downs.
Events that should not be logged include application source code, session identification values, access tokens, sensitive personal data, passwords, database connection strings, encryption keys, bank account and card holder data.
The following are some best practices for doing logging:
Winston is a popular JavaScript logging library. It is designed to be simple and universal. Each Winston logger can have multiple transports configured at different levels. Winston uses JSON format for its logs by default.
$ npm i winston
We install the winston package with the npm tool.
Winston loggers log the messages to log files. A winston logger is created with
the createLogger function. In addition, there is a default logger
if no logger is explicitly specified.
The createLogger function accepts the following options:
| Name | Default | Description |
|---|---|---|
| level | info | maximum level of log messages to log |
| levels | winston.config.npm.levels | the set of level message types chosen |
| format | winston.format.json | the format of log messages |
| transports | no transports | set of logging destinations for log messages |
| exitOnError | true | whether handled exceptions cause process.exit |
| silent | false | if true, all logs are suppressed |
A transport is a storage device or output mechanism for our logs. Each Winston logger can have multiple transports configured at different levels. Winston comes with three core transports: console, file, and HTTP. The transports must be created and added to the loggers.
A transport has the following settings:
Logging levels indicate message priority and are denoted by an integer. They are
specified for a transport. Winston comes with predefined sets of logging levels:
npm, syslog, and cli.
The following are the default npm logging levels:
Anything at a particular level or higher will be logged. For instance, by specifying the info level, anything at level error, warn, or info will be logged. Log levels are specified when calling the logger.
logger.info('Information message').
We log an information message using the info function.
logger.log('info', Information message').
This is an alternative way of logging an information message.
Logging messages are formatted with Winston formats. Winston contains several built-in formats, including:
The default format is json. Formats can be combined with
combine and custom formats can be created with printf.
The following is a simple Winston example.
const winston = require('winston');
const logger = winston.createLogger({
transports: [
new winston.transports.Console()
]
});
logger.info('Information message');
logger.warn('Warning message');
logger.error('Error message');
We create a console logger and log three messages. Since the default
logging level is info, all three messages are logged.
$ node simple.js
{"message":"Information message","level":"info"}
{"message":"Warning message","level":"warn"}
{"message":"Error message","level":"error"}
In the output we can see three messages in JSON.
We can log messages with custom log functions (info,
warn) or use the generic log function and specify the
log level as the first option.
const winston = require('winston');
const logger = winston.createLogger({
transports: [
new winston.transports.Console()
]
});
logger.info('Information message');
logger.warn('Warning message');
logger.log('info', 'Information message')
logger.log('warn', 'Warning message')
In the example, we log messages in both ways.
$ node log_funs.js
{"message":"Information message","level":"info"}
{"message":"Warning message","level":"warn"}
{"level":"info","message":"Information message"}
{"level":"warn","message":"Warning message"}
With the configure function, we can configure and already
created logger.
const winston = require('winston');
const logger = winston.createLogger({
transports: [
new winston.transports.Console()
]
});
logger.info('Information message');
logger.warn('Warning message');
logger.error('Error message');
logger.configure({
level: 'error',
transports: [
new winston.transports.Console()
]
});
logger.info('Information message 2');
logger.warn('Warning message 2');
logger.error('Error message 2');
In the example, we change the logging level to 'error' and call
another three log functions. Only the second error message is outputted from the
additional messages.
$ node configure_logger.js
{"message":"Information message","level":"info"}
{"message":"Warning message","level":"warn"}
{"message":"Error message","level":"error"}
{"message":"Error message 2","level":"error"}
The default logger is accessible through the winston module directly.
Initially, there are no transports set on the default logger. We must add or
remove transports via the add, remove, or
configure methods.
const winston = require('winston');
const console = new winston.transports.Console();
winston.add(console);
winston.info('Information message');
winston.remove(console);
The example uses the default logger to log an information message.
The cli format is a combination of the colorize
and the padLevels formats.
const winston = require('winston');
const myformat = winston.format.cli({ colors: { info: 'blue' }});
const logger = winston.createLogger({
transports: [
new winston.transports.Console({
format: myformat
})
]
});
logger.info('Information message');
In the example, we pass the cli format to the console transport. We choose a blue colour.
Formats can be combined with the combine.
const winston = require('winston');
const myformat = winston.format.combine(
winston.format.colorize(),
winston.format.timestamp(),
winston.format.align(),
winston.format.printf(info => `${info.timestamp} ${info.level}: ${info.message}`)
);
const logger = winston.createLogger({
transports: [
new winston.transports.Console({
format: myformat
})
]
});
logger.info('Information message');
In the example, we combine colorize, timestamp, align,
and printf formats.
$ node format_combine.js 2020年04月04日T17:17:48.882Z info: Information message
The maxsize property of a transport sets the maximum size of a log
file, in bytes, before a new file will be created.
const path = require('path');
const winston = require('winston');
const logger = winston.createLogger({
level: 'info',
format: winston.format.printf(info => `${info.message}`),
transports: [
new winston.transports.File({
filename: path.join(__dirname, 'error.log'),
level: 'info',
maxsize: 500
})
]
});
logger.info(`${'a'.repeat(200)}`);
logger.info(`${'b'.repeat(200)}`);
logger.info(`${'c'.repeat(200)}`);
setTimeout(() => {
logger.info(`${'d'.repeat(200)}`);
logger.info(`${'e'.repeat(200)}`);
}, 1500);
We use a file transport and set the maxsize to 500 bytes. Later we
write more than 500 characters into the file. The first 600 characters are
written to the error.log file, the remaining 400 characters into
the error1.log file.
In the next example, we create two loggers that write to files.
const winston = require('winston');
const loggers = {
mjson: winston.createLogger({
level: 'info',
format: winston.format.json(),
transports: [new winston.transports.File({ filename: 'app-info.log'})],
}),
simple: winston.createLogger({
level: 'error',
format: winston.format.simple(),
transports: [new winston.transports.File({ filename: 'app-error.log'}),],
})
};
loggers.mjson.info('Information message');
loggers.mjson.error('Error message');
loggers.mjson.debug('Some message');
loggers.simple.error('Error message');
loggers.simple.info('Information message');
loggers.simple.warn('Warning message');
loggers.simple.debug('Some message');
The mjson logger writes in a json format, the simple
logger in a simple format. The loggers have different log levels.
$ node loggers.js
$ cat app-error.log
error: Error message
$ cat app-info.log
{"message":"Information message","level":"info"}
{"message":"Error message","level":"error"}
We run the program and show the file contents.
In this article we have worked with the Winston logging library.
My name is Jan Bodnar, and I am a passionate programmer with extensive programming experience. I have been writing programming articles since 2007. To date, I have authored over 1,400 articles and 8 e-books. I possess more than ten years of experience in teaching programming.