Time to learn about another awesome concept:
logging
What does Wikipedia say about
logging
?
In computing, logging is the act of keeping a log of events that occur in a computer system, such as problems, errors, or just information on current operations. A message or log entry is recorded for each such event. These log messages can then be used to monitor and understand the operation of the system, to debug problems, or during an audit. (
Logging (computing)
)
So going by the definition, logging simply means keeping a log of events that occur in a computer system. This can be used to monitor and understand the operation of the system, to debug problems, or during an audit. Many types of logs can be generated by a system, but we will be focusing on
Server Logs
in this article.
Server Logs are logs that are generated by a server. These logs are used to monitor and understand the server's operation. Typical examples of server logs are:
Page Requests Logs
Error Logs
Access Logs
Information Logs, etc.
Logs help in understanding the behavior of the server, how many requests are being made to the server, what kind of requests are being made, what kind of errors are being generated, etc. Understanding the patterns in the logs can help optimize the server's performance and debug issues.
Winston is one such tool that can be used to generate logs in a Node.js application. Its various modifications can be used to generate logs in different formats and at different levels.
Let's create the simplest logger using Winston:
constwinston=require('winston');constlogger=winston.createLogger({level:'info',format:winston.format.json(),transports:[newwinston.transports.Console()// now we can use the logger to log messages as we wantlogger.info('Hello, Winston!');logger.error('An error occurred!');logger.warn('Warning: This is a warning!');
Levels - Levels are used to specify the severity of the log. Winston is pre-configured with the following levels:
error: Severity 0 - This is an error message.
warn: Severity 1 - This is a warning message.
info: Severity 2 - This is an informational message.
http: Severity 3 - This is an HTTP log.
verbose: Severity 4 - This is a verbose log.
debug: Severity 5 - This is a debug message.
silly: Severity 6 - This is a silly message.
The above example of the logger is configured to log messages at the info, warn, and error levels. We can also create custom levels for the Winston logger.
We can use the syslog levels as well. The syslog levels can be used from winston.config.syslog.levels. The default levels are from winston.config.npm.levels.
importwinstonfrom'winston';constcustomLevels={levels:{error:0,warn:1,info:2,colors:{error:'red',warn:'yellow',info:'green',// add colors to the custom levelswinston.addColors(customLevels.colors);// define a logger with custom levelsconstlogger=winston.createLogger({levels:customLevels.levels,format:winston.format.combine(winston.format.colorize(),winston.format.simple()transports:[newwinston.transports.Console()// now we can use the logger to log messages as we wantlogger.error('Hello, Winston!');logger.warn('An error occurred!');logger.info('Warning: This is a warning!');
Output:
Here, we have defined custom levels for the logger and added colors to the custom levels. We have to inform Winston about the custom levels and colors using the addColors method.
JSON - This format logs the message in JSON format.
simple - This format logs the message in a simple format.
colorize - This format logs the message in color.
printf - This format logs the message in a custom format.
timestamp - This format logs the message with a timestamp.
combine - This format combines multiple formats.
We can also create custom formats for the Winston logger.
importwinstonfrom'winston';// define a logger with custom formatconstlogger=winston.createLogger({level:'info',format:winston.format.combine(winston.format.colorize(),winston.format.timestamp({format:'YYYY-MM-DD HH:mm:ss',winston.format.printf(({level,message,timestamp})=>{return`${timestamp} [${level}]: ${message}`;transports:[newwinston.transports.Console()// now we can use the logger to log messages as we wantlogger.info('Hello, Winston!');logger.error('An error occurred!');logger.warn('Warning: This is a warning!');
Output:
Here, we have defined a custom format for the logger. The custom format logs the message in the format [timestamp] [level]: message
Console - This transport logs the message to the console. Mostly used for development purposes.
File - This transport logs the message to a file. We can specify the filename, the maximum size of the file, and the maximum number of files.
HTTP - This transport logs the message to another server using HTTP. Mostly used for remote logging services.
Stream - This transport logs the message to a NodeJS writable stream.
Daily Rotate File - Daily Rotate File is an important way to prevent the accumulation of unnecessary old log files that are of no use in a live server and can be replaced automatically, ensuring the latest log files are available always and the redundant old files are removed/rotated to save the space on the live server
Here, we have defined a Daily Rotate File transport for the logger. The Daily Rotate File transport logs the message to a file with the filename combined.log and rotates the file daily.
The maximum size of the file is 2m i.e. 2MB and the maximum number of days the files will be kept is 14d, where d is days. The %DATE% is a placeholder that will be replaced with the current date in the format YYYY-MM-DD-HH.
// winston.config.jsimportwinstonfrom'winston';import'winston-daily-rotate-file';constfileFormat=winston.format.combine(winston.format.colorize(),winston.format.uncolorize(),winston.format.timestamp({format:'YYYY-MM-DD HH:mm:ss',winston.format.prettyPrint({depth:5winston.format.printf((info)=>`${info.timestamp}${info.level}: ${info.message}`),constconsoleFormat=winston.format.combine(winston.format.colorize(),winston.format.timestamp({format:'YYYY-MM-DD HH:mm:ss',winston.format.prettyPrint({depth:5winston.format.printf((info)=>`${info.timestamp}${info.level}: ${info.message}`),constconsoleTransport=newwinston.transports.Console({format:consoleFormat,constcombinedFileTransport=newwinston.transports.DailyRotateFile({filename:'%DATE%_combined.log',format:fileFormat,//format means - how the log should be formatteddatePattern:'YYYY-MM-DD-HH',maxSize:'2m',dirname:'./logs/combined',maxFiles:'14d',consterrorFileTransport=newwinston.transports.DailyRotateFile({filename:'%DATE%_error.log',level:'error',format:fileFormat,//format means - how the log should be formatteddatePattern:'YYYY-MM-DD-HH',maxSize:'2m',dirname:'./logs/errors',maxFiles:'14d',consthttpTransport=newwinston.transports.Http({format:winston.format.json(),host:'localhost',port:4000,path:'/logs',ssl:false,batch:true,batchCount:10,batchInterval:10000,constlogger=winston.createLogger({levels:winston.config.syslog.levels,transports:[errorFileTransport,combinedFileTransport,consoleTransport,httpTransport,exportdefaultlogger;Enter fullscreen modeExit fullscreen mode
Here, we have defined a logger using the Winston library with the following functionalities:
Console Transport - Logs the message to the console.
Daily Rotate File Transport - Logs the message to a file and rotates the file daily.
HTTP Transport - Logs the message to another server using HTTP.
Error File Transport - Logs the error message to a file and rotates the file daily.
Combined File Transport - Logs the message to a file and rotates the file daily.
Syslog Levels - We have used the syslog levels for the logger.
// index.jsimportexpressfrom'express';importmorganfrom'morgan';importloggerfrom'./config/winston.config.js';constapp=express();app.use(express.json());app.use(morgan('dev',{stream:{write:message=>{logger.info(message);app.get('/',(_req,res)=>{returnres.status(200).json({message:'Hello World'});functionhandleFatalError(err){logger.error(err);process.exit(1);app.listen(process.env.PORT||3000,()=>{logger.info('Server is running on http://localhost:'+(process.env.PORT||3000)+'/');process.on('unhandledRejection',handleFatalError);process.on('SIGTERM',handleFatalError);Enter fullscreen modeExit fullscreen mode
To further enhance the functionality of our logger, we have integrated it with the Morgan library. Morgan is a middleware that logs the HTTP requests to the server. We have used the dev format of Morgan to log the HTTP requests to the server.
The stream option of Morgan is used to specify the destination of the log message. We have used the Winston logger to log the message to the console.
Output:
Files created by the Daily Rotate File Transport:
In this article, we have learned about the concept of logging and how to create a logger using Winston in a Node.js application. We have also learned about the various components of the Winston logger, such as levels, formats, and transports.
Try out the Winston logger setup in your Node.js applications and let me know your thoughts in the comments. If you have any questions or suggestions, feel free to ask.
So... that's it for today.
I would like to thank you 🫡🎉 for reading here and appreciate your patience. It would mean the world to me if you give feedback/suggestions/recommendations below.
See you in the next article, which will be published on......
I typically write these articles in the TIL form, sharing the things I learn during my daily work or afterward. I aim to post once or twice a week with all the things I have learned in the past week.
Implement strategies like structured logging to ensure that dynamic information from requests does not affect the log files' structure. This maintains a consistent format, making it easier to understand and automate the analysis of log files.
Tools to read those files and set up alerts:
There are several production-ready log analysis tools, such as the ELK stack (Elasticsearch, Logstash, Kibana) and Prometheus & Grafana. While I am still learning about these tools in depth, they are highly recommended for their robust capabilities. You can find other good articles on them online.
Built on Forem — the open source software that powers DEV and other inclusive communities.