Skip to content

Instantly share code, notes, and snippets.

@tatsuyasusukida
Last active December 26, 2023 15:34
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save tatsuyasusukida/92b0f5dd8523ebdf19edb4a977a8030a to your computer and use it in GitHub Desktop.
Save tatsuyasusukida/92b0f5dd8523ebdf19edb4a977a8030a to your computer and use it in GitHub Desktop.
🪵 How to collect logs using winston in Node.js

🪵 How to collect logs using winston in Node.js

About this article

This article describes how to collect logs using winston in Node.js. The related resources are shown below.

Workflow

The workflow is shown below.

  1. Coding preparation
  2. Coding
  3. Operation check

Coding preparation

Run the following commands in your terminal to prepare for coding.

mkdir nodejs-winston-logging
cd nodejs-winston-logging
npm init -y
npm install --save dotenv winston
touch touch .env logging.js main.js

Coding

.env

Open .env in your editor and enter the following 3 variables.

  • LOG_CONSOLE: whether to output logs to the console
  • LOG_DIRNAME: logs output directory
  • LOG_ROTATION: whether to rotate log files

An example is shown below.

Click to go to .env.example

logging.js

Open logging.js in your editor and enter the following content.

Click to go to logging.js

The points are shown below.

  1. makeLogger is a function that creates a winston logger. The first argument is the log level such as error and info, the second argument is the output format such as JSON and raw (raw data), and the third argument is the log file name of the output destination.
  2. makeFormat is a function that creates a winston log format. It calls the makeFormatJson and makeFormatRaw functions that create a log format based on the first argument.
  3. makeFormatJson is a function that creates a JSON format log format. It uses the winston.format.timestamp function to add a timestamp to the log.
  4. makeFormatRaw is a function that creates a log format that outputs the message as it is.
  5. makeTransportFile is a function that creates a transport (write destination) for outputting logs to a file.
  6. makeTransportConsole is a function that creates a transport for outputting logs to the console.

main.js

Open main.js in your editor and enter the following content.

Click to go to main.js

The points are shown below.

  1. Call winston.loggers.add function to add a logger.
  2. Call winston.loggers.get function to get a logger, and then call the method corresponding to the log level (error, warn, etc.) to write a log.

Operation check

Run the following command in your terminal to output logs.

node -r dotenv/config main.js

Check that the following contents are output to the console.

ERROR
WARN
INFO
QUERY
ACCESS

Also, check that the log directory is created in the working directory and the following files are created.

  • access.log
  • error.log
  • info.log
  • query.log
  • warn.log

Conclusion

If you deploy your application to Cloud Run, you can check the log by specifying the output destination of log files to /var/log. However, if you always output debug logs (example: SQL query log), storage charges will be high. Therefore, always output only required logs, and output debug logs only when necessary. It is better to switch the output of debug logs through an environment variable.

Collected logs are very helpful in situations such as debugging during development and troubleshooting during production. However, I don't have enough knowledge using log analysis technology, so I haven't been able to use it very well. I'm always looking for a better way to collect logs. If you have any knowledge, I would appreciate your guidance comments, and other comments are welcome. Thank you for reading!

LOG_CONSOLE=1
LOG_DIRNAME=
LOG_ROTATION=1
/log/
/node_modules/
/package-lock.json
/.env
# Do not ignore package-lock.json other than gist.
const path = require('path')
const winston = require('winston')
function makeLogger (level, format, file) { // <1>
return {
level: level,
format: makeFormat(format),
transports: [
makeTransportFile(file),
...(process.env.LOG_CONSOLE === '1' ? [makeTransportConsole()] : [])
],
}
}
function makeFormat (format) { // <2>
if (format === 'json') {
return makeFormatJson()
} else if (format === 'raw') {
return makeFormatRaw()
} else {
throw new TypeError(`invalid format: '${format}'`)
}
}
function makeFormatJson (level, file, format) { // <3>
return winston.format.combine(
winston.format.timestamp(),
winston.format.json(),
)
}
function makeFormatRaw (level, file, format) { // <4>
return winston.format.printf(({message}) => message)
}
function makeTransportFile (filename) { // <5>
const dirname = process.env.LOG_DIRNAME || path.join(process.cwd(), 'log')
if (process.env.LOG_ROTATION === '1') {
return new winston.transports.File({
dirname,
filename,
maxsize: 10 * 1024 * 1024,
maxFiles: 10,
tailable: true,
})
} else {
return new winston.transports.File({
dirname,
filename,
})
}
}
function makeTransportConsole () { // <6>
return new winston.transports.Console({
format: makeFormatRaw(),
})
}
module.exports.makeLogger = makeLogger
module.exports.makeFormat = makeFormat
module.exports.makeFormatJson = makeFormatJson
module.exports.makeFormatRaw = makeFormatRaw
module.exports.makeTransportFile = makeTransportFile
module.exports.makeTransportConsole = makeTransportConsole
const winston = require('winston')
const {makeLogger} = require('./logging')
if (require.main === module) {
main()
}
async function main () {
try {
winston.loggers.add('error', makeLogger('error', 'json', 'error.log'))
winston.loggers.add('warn', makeLogger('warn', 'json', 'warn.log'))
winston.loggers.add('info', makeLogger('info', 'json', 'info.log'))
winston.loggers.add('query', makeLogger('info', 'json', 'query.log'))
winston.loggers.add('access', makeLogger('info', 'raw', 'access.log'))
winston.loggers.get('error').error('ERROR')
winston.loggers.get('warn').warn('WARN')
winston.loggers.get('info').info('INFO')
winston.loggers.get('query').info('QUERY')
winston.loggers.get('access').info('ACCESS')
} catch (err) {
console.error(err)
}
}
{
"name": "nodejs-winston-logging",
"version": "1.0.0",
"description": "",
"main": "main.js",
"scripts": {
"dev": "node -r dotenv/config main.js",
"clean": "rm -rf log"
},
"keywords": [],
"author": "",
"license": "MIT",
"dependencies": {
"dotenv": "^16.0.0",
"winston": "^3.7.2"
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment