winston logging to separate files - node.js

I'm new in node.js and have such winson config
const winston = require('winston');
const transports = [];
if (process.env.NODE_ENV !== 'development') {
transports.push(new (winston.transports.DailyRotateFile)({
filename: path.resolve(__dirname, '../logs/%DATE%/source1.log'),
level: 'info',
datePattern: 'YYYY-MM-DD',
}));
transports.push(new (winston.transports.DailyRotateFile)({
filename: path.resolve(__dirname, '../logs/%DATE%/source2.log'),
level: 'info',
datePattern: 'YYYY-MM-DD',
}));
}
let logger = winston.createLogger({
level: 'info',
format: winston.format.json(),
transports,
exitOnError: true,
});
logger.log({
date: (new Date()).toString(),
level,
message,
source,
});
and I need logging to separate files depend on source (all source1 logs in file ../logs/%DATE%/source1.log' and same with source2)
How can do this?

by GoogleTraduction FR -> EN
Hello !
If you want your logs to be separated into several separate files I can offer you the following solution.
Note: this solution is based on log levels.
Winston: 3.0.0
Nodejs: 9.3.0
./logger/index.js
const winston = require('winston');
module.exports = (dirname) => {
const loggers = {
info: null,
warn: null,
query: null,
errDat: null,
errFun: null,
errInt: null,
};
for (let level of Object.keys(loggers)) {
loggers[level] = winston.createLogger({
levels: { [level]: 0 },
format: winston.format.combine(
winston.format.timestamp(),
winston.format.printf(info =>
`${info.timestamp}\t[${info.level}]\t${info.message}`
),
),
transports: [
new winston.transports.Console({
level
}),
new winston.transports.File({
dirname,
filename: `${level}.log`,
level,
format: winston.format.json(),
}),
]
})[level];
}
return loggers;
};
Other file
const path = require('path');
const { info, query, errInt, errDat, errFun } = require('./logger')(
path.join(__dirname, 'logs')
);
info(`Test`);
query(`Test`);
errDat(`Test`);
errInt(`Test`);
errFun(`Test`);

Related

winston - not able to log the console message to file

I want to log all of the messages from Console using Winston,
and use winston-daily-rotate-file to daily store and remove the logs.
In Console:
{"level":"info","message":"Feathers application started on localhost","timestamp":"5/26/2021, 10:44:36 AM"}
You will see this message every 60 second
You will see this message every 60 second
You will see this message every 60 second
When I view the log file application-2021-05-26.log,
{"level":"info","message":"Feathers application started on localhost","timestamp":"5/26/2021, 10:44:36 AM"}
You will see this message every 60 second is missing in the log file.
What's wrong here?
app.js
var CronJob = require('cron').CronJob;
const moment = require('moment');
new CronJob('10 * * * * *', function() {
console.log('You will see this message every 60 second');
}, null, true, 'Asia/Singapore');
logger.js
const { createLogger, format, transports } = require('winston');
const winston = require('winston');
require('winston-daily-rotate-file');
const timezoned = () => {
return new Date().toLocaleString('en-SG', {
timeZone: 'Asia/Singapore'
});
}
const logger = createLogger({
level: 'debug',
format: format.combine(
format.splat(),
format.simple(),
format.json()
),
transports: [
new transports.Console(),
new winston.transports.DailyRotateFile({
filename: 'application-%DATE%.log',
dirname: "logs",
datePattern: 'YYYY-MM-DD',
zippedArchive: true,
maxSize: '20m',
maxFiles: '2d',
format: winston.format.combine(
winston.format.timestamp({ format: timezoned }),
winston.format.json(),
),
})
],
});
module.exports = logger;
You are not using the logger.log function inside your CronJob definition.
Please check out this Minimum Viable Example to see how you should configure your codebase to see the logs both on your console and on your file.
When you run the example (using node index.js), the script will emit logs to the console and a local file inside the project's root.
Here is the entire script for reference:
const { CronJob } = require('cron');
const { createLogger, format, transports } = require('winston');
const moment = require('moment');
require('winston-daily-rotate-file');
const timezoned = () =>
new Date().toLocaleDateString('en-SG', {
timeZone: 'Asia/Singapore'
});
const logger = createLogger({
level: 'debug',
format: format.combine(format.splat(), format.simple(), format.json()),
transports: [
new transports.Console(),
new transports.DailyRotateFile({
filename: 'application-%DATE%.log',
dirname: '.',
datePattern: 'YYYY-MM-DD',
zippedArchive: true,
maxSize: '20m',
maxFiles: '2d',
format: format.combine(
format.timestamp({ format: timezoned }),
format.json()
)
})
]
});
const job = new CronJob(
'10 * * * * *',
() => logger.debug("You'll see this message every 60 seconds"),
null,
null,
'Asia/Singapore'
);
job.start();

How to force nodejs winston log to file before process exit

I am using winston 3 to log my data. But sometimes it didn't log the error before process exit.
The following process will exit, without log to the logfile.log:
const winston = require('winston');
winston.add(new winston.transports.File({
filename: 'logfile.log'
}));
winston.info('please log me in');
process.exit(1);
One of attempted solution is used callback:
const winston = require('winston');
const logger = new winston.createLogger({
new winston.transports.File({
filename: 'logfile.log'
}));
winston.info('please log me in', () => {
process.exit(1);
});
setTimeout(() => {}, 100000000000); // pause the process until process.exit is call
But Winston 3.x have callback issue, so the above code won't work, the process will not exit.
I am looking for a workable solution? Any help will be greatly appreciated. My OS is Ubuntu 16.04, Node 10.17.
Edit 1:
I also have try Prabhjot Singh Kainth's suggestion use finish event to trigger process exit:
const winston = require('winston');
const logger = winston.createLogger({
transports: [
new winston.transports.File({
filename: 'logfile.log'
})
]
});
logger.info('please log me in');
logger.on('finish', () => {
process.exit();
});
logger.end();
setTimeout(() => {}, 100000000000); // pause the process until process.exit is call
In the above case, the process will exit, but no log file will be created.
How about this one?
logger.info('First message...')
logger.info('Last message', () => process.exit(1))
Finally, I find a workable solution. Instead of listening to logger finish event, it should listen to file._dest finish event. But file._dest is only created after file open event. So it need to wait for file open event in initialization process.
const winston = require('winston');
const file = new winston.transports.File({
filename: 'logfile.log'
});
const logger = winston.createLogger({
transports: [file]
});
file.on('open', () => { // wait until file._dest is ready
logger.info('please log me in');
logger.error('logging error message');
logger.warn('logging warning message');
file._dest.on('finish', () => {
process.exit();
});
logger.end();
});
setTimeout(() => {}, 100000000000); // pause the process until process.exit is call
Each instance of winston.Logger is also a Node.js stream.
A finish event will be raised when all logs have flushed to all transports after the stream has been ended.
Just try using this code :
const transport = new winston.transports.Console();
const logger = winston.createLogger({
transports: [transport]
});
logger.on('finish', function (info) {
// All `info` log messages has now been logged
});
logger.info('CHILL WINSTON!', { seriously: true });
logger.end();
The answer from #WanChap was good, but not when there is multiple file transports. I will post my entire logging solution below, which handles exiting the process correctly, including support for Development and Production.
The only caveat is that it does not work with the exceptions log at the moment, but it will work with every other transport based log. If anyone figures out how to make this work with the exception log as well, please post in the comments.
First, you want to create a folder in the same directory that your file that is doing the logs is in, and call it logger. In this folder, create three .js files, one called dev-logger.js, the other prod-logger.js, and finally index.js
In node, when you require a directory, it will assume you want to load the index.js file.
TheFileYouWantToLogFrom.js
const logger = require('./logger');
logger.info("it works!");
index.js
This file will look at your node process environment state and use the correct logger.
const buildDevLogger = require('./dev-logger');
const buildProdLogger = require('./prod-logger');
let logger = null;
if(process.env.NODE_ENV === 'development'){
logger = buildDevLogger();
}else{
logger = buildProdLogger();
}
module.exports = logger;
You can test this later, by simply calling your node process (in my case cron.js) in your terminal, by saying something like NODE_ENV=production node cron.js or NODE_ENV=development node cron.js
dev-logger.js
const { format, createLogger, transports} = require('winston');
const { timestamp, combine, printf, errors } = format;
var numOpenTransports = 0;
function buildDevLogger(){
const logFormat = printf(({ level, message, timestamp, stack, durationMs }) => {
return `${timestamp} ${level}: ${stack || message}${durationMs ? " | "+durationMs+'ms' : ''}`;
});
//For info on the options go here.
//https://github.com/winstonjs/winston/blob/master/docs/transports.md
var options = {
console: {
handleExceptions: true,
level: 'debug',
format: combine(
format.colorize(),
timestamp({format: 'YYYY-MM-DD HH:mm:ss'}),
errors({stack: true}),
logFormat
),
},
combined: {
filename: 'logs/dev/combined.log',
maxsize: 10000000,
maxFiles: 10,
tailable:true,
format: combine(
timestamp({format: 'YYYY-MM-DD HH:mm:ss'}),
errors({stack: true}),
logFormat
),
},
error: {
filename: 'logs/dev/error.log',
level: 'error',
maxsize: 10000000,
maxFiles: 10,
tailable:true ,
format: combine(
timestamp({format: 'YYYY-MM-DD HH:mm:ss'}),
errors({stack: true}),
logFormat
),
},
exception : {
filename: 'logs/dev/exceptions.log',
maxsize: 10000000,
maxFiles: 10,
tailable:true,
format: combine(
timestamp({format: 'YYYY-MM-DD HH:mm:ss'}),
errors({stack: true}),
logFormat
),
}
};
function fileFinished(){
numOpenTransports--;
if(numOpenTransports==0){
process.exit(0);
}
}
var combinedFile = new transports.File(options.combined);
var errorFile = new transports.File(options.error);
var exceptionFile = new transports.File(options.exception);
combinedFile.on('open', () => { // wait until file._dest is ready
numOpenTransports++;
combinedFile._dest.on('finish', () => {
fileFinished();
});
});
errorFile.on('open', () => { // wait until file._dest is ready
numOpenTransports++;
errorFile._dest.on('finish', () => {
fileFinished();
});
});
return createLogger({
defaultMeta: {service:'cron-dev'},
transports: [
new transports.Console(options.console),
combinedFile,
errorFile,
],
exceptionHandlers: [
exceptionFile
]
});
}
module.exports = buildDevLogger;
prod-logger.js
const { format, createLogger, transports} = require('winston');
const { timestamp, combine, errors, json } = format;
var numOpenTransports = 0;
function buildProdLogger(){
var options = {
console: {
handleExceptions: true,
level: 'debug',
format: combine(
timestamp({format: 'YYYY-MM-DD HH:mm:ss'}),
errors({stack: true}),
json()
),
},
combined: {
filename: 'logs/prod/combined.log',
maxsize: 10000000,
maxFiles: 10,
tailable:true,
format: combine(
timestamp({format: 'YYYY-MM-DD HH:mm:ss'}),
errors({stack: true}),
json()
),
},
error: {
filename: 'logs/prod/error.log',
level: 'error',
maxsize: 10000000,
maxFiles: 10,
tailable:true ,
format: combine(
timestamp({format: 'YYYY-MM-DD HH:mm:ss'}),
errors({stack: true}),
json()
),
},
exception : {
filename: 'logs/prod/exceptions.log',
maxsize: 10000000,
maxFiles: 10,
tailable:true,
format: combine(
timestamp({format: 'YYYY-MM-DD HH:mm:ss'}),
errors({stack: true}),
json()
),
}
};
function fileFinished(){
numOpenTransports--;
if(numOpenTransports==0){
process.exit(0);
}
}
var combinedFile = new transports.File(options.combined);
var errorFile = new transports.File(options.error);
var exceptionFile = new transports.File(options.exception);
combinedFile.on('open', () => { // wait until file._dest is ready
numOpenTransports++;
combinedFile._dest.on('finish', () => {
fileFinished();
});
});
errorFile.on('open', () => { // wait until file._dest is ready
numOpenTransports++;
errorFile._dest.on('finish', () => {
fileFinished();
});
});
return createLogger({
defaultMeta: {service:'cron-prod'},
transports: [
new transports.Console(options.console),
combinedFile,
errorFile,
],
exceptionHandlers: [
exceptionFile
]
});
}
module.exports = buildProdLogger;
The way the dev and prod logger files work is by using the numOpenTransports variable to keep track of which 'files transports' are currently open, and once they are all closed, then, and only then, exit the process.

Cannot get the logger (winston) to create a log file

I went through many of the answered related to Winston, but I cannot find what my problem (code) is
const winston = require('winston');
const fs = require('fs');
const logDir = `./logs/`;
if (!fs.existsSync(logDir)) {
fs.mkdirSync(logDir);
}
console.log(`logDir: ${logDir}`);
/* https://github.com/winstonjs/winston */
const logger = winston.createLogger({
level: 'info',
format: winston.format.json(),
exitOnError: false,
transports: [
//
// - Write to all logs with level `info` and below to `combined.log`
// - Write all logs error (and below) to `error.log`.
//
new winston.transports.File({ filename: `${logDir}/error-${process.pid}.log`,
level: 'error',
format: winston.format.combine(
winston.format.timestamp(),
winston.format.json(),
)
}),
new winston.transports.File({ filename: `${logDir}/combined-${process.pid}.log`,
format: winston.format.combine(
winston.format.timestamp(),
winston.format.json(),
)
}),
]
});
logger.info('info test');
logger.warn('warn test');
logger.error('error test');
const combinedLogs = logger.transports.find(transport => {
console.log(`transport.filename: ${transport.filename}, transport.dirname: ${transport.dirname}`);
// console.log(`transport: ${util.inspect(transport)}`);
});
process.exit(0);
The output when I run with nodejs (v10.15.3) is:
# nodejs myscript.js
logDir: ./logs/
transport.filename: error-24238.log, transport.dirname: ./logs/
transport.filename: combined-24238.log, transport.dirname: ./logs/
But nothing is present in the logs dir:
root#someserver:/usr/local/somepath# ls -l logs/
total 0
I am using winston and fs:
+ winston#3.2.1
+ fs#0.0.1-security

nodejs auto maintain winston log

I'm trying to integrate winston to a nodeJs Express app. It works but I don't know how could it be auto maintained. I wanted something like when it reach, for example 50000 rows, it delete the first ones before adding the new ones for terms of space. The idea is doing that without something like cron. Is it possible?
Here is my testing code:
//Winston logger
const { createLogger, format, transports } = require('winston');
const { combine, timestamp, label, printf } = format;
const winstonFormat = printf(info => {
return `${info.timestamp} [${info.label}] ${info.level}: ${info.message}`;
});
const logger = createLogger({
level: 'info',
//maxsize:'10000', //It doesn't works for me
format: combine(
label({ label: config.environment }),
timestamp(),
winstonFormat
),
transports: [
new transports.File({ filename: 'error.log', level: 'error' }),
new transports.File({ filename: 'combined.log' })
]
});
if (process.env.NODE_ENV !== 'production') {
logger.add(new transports.Console({
format: format.simple()
}));
}
You're looking for log rotation, so googling "winston log rotation" points to https://github.com/winstonjs/winston-daily-rotate-file, which is probably just the ticket for you.

how to use winston to setup log in a sub directory instead of root directory?

I want to setup logs for my nodejs project inside a directory named logs
as per documentation here
i am doing :
winston.add(winston.transports.File, { filename: 'logs/mylogs.log' });
But it is doing nothing.
How do i achieve the same?
Put the below code in your server file.
var winston = require('winston');
var fs = require( 'fs' );
var path = require('path');
var logDir = 'log'; // directory path you want to set
if ( !fs.existsSync( logDir ) ) {
// Create the directory if it does not exist
fs.mkdirSync( logDir );
}
var logger = new (winston.Logger)({
transports: [
new (winston.transports.Console)({
colorize: 'all'
}),
new (winston.transports.File)({filename: path.join(logDir, '/log.txt')})
]
});
logger.info("Anything you want to write in logfile");
Winston provides option to set dirname when creating log file.
By default Winston creates directory to project root:
new transports.File({
filename: 'errors.log',
dirname: 'logs',
level: 'error',
})
If you want your logs for example inside src directory:
new transports.File({
filename: 'errors.log',
dirname: './src/logs',
level: 'error',
})
Full code example:
import { createLogger, transports, format } from 'winston'
const { timestamp, combine, json, errors } = format
const buildLogger = () => {
return createLogger({
format: combine(
timestamp(),
errors({stack: true}),
json()
),
defaultMeta: {service: "your-service"},
transports: [
new transports.File({
filename: 'events.log',
dirname: 'logs',
level: 'info',
}),
new transports.File({
filename: 'errors.log',
dirname: 'logs',
level: 'error',
}),
]
})
}
export default buildLogger
You should use a relative path (the dot and backslash):
winston.add(winston.transports.File, { filename: './logs/mylogs.log' });

Resources