Is there a possibility to save the current module name in order to be printed automatically in winston log entries when they are called later?
Currently, when I want to print the module name in logs, I have to add it manually:
var logHeader = 'mymodule'
log.info(logHeader + 'Hello')
For example, with debug, you can do (ignore the log format feature %s for now):
var debug = require('debug')('http')
, name = 'My App'
debug('booting %s', name);
This will prin http prefix before the log:
http booting My App
Can this be done in winston? I have searched in the documentation but I couldn't find anything relevant.
I found a better way to do this.
I added an additional layer over the winston logger, in this case a function, that keeps the module name for each module that needs the logger. So when a module requires my new logger, it actually calls the exported function with the module name, in this case __filename.
log.js
var winston = require('winston')
var winstonLogger = new (winston.Logger)({
transports: [
new (winston.transports.File) ({
filename: 'MyLogs.txt',
handleExceptions: true,
humanReadableUnhandledException: true,
level: 'info',
timestamp: true,
json: false
}),
new (winston.transports.Console) ({
level: 'info',
prettyPrint: true,
colorize: true,
timestamp: true
})
]
})
module.exports = function(fileName) {
var myLogger = {
error: function(text) {
winstonLogger.error(fileName + ': ' + text)
},
info: function(text) {
winstonLogger.info(fileName + ': ' + text)
}
}
return myLogger
}
module1.js
var log = require('log')(__filename)
log.info('Info log example')
info: C:\Users\user1\project\module1.js: Info log example
module2.js
var log = require('log')(__filename)
log.error('Error log example')
error: C:\Users\user1\project\module2.js: Error log example
Also, this way, I didn't need to change anywhere the way I submit a text log; log.info('text') stays exactly like before.
This is what Child Loggers are for. They might not have been available when this question was asked, but for future reference, you can create your main logger instance and then export a function that creates a child logger with some default options.
// logging.js
const winston = require('winston')
const logger = winston.createLogger({
transports: [
new winston.transports.Console({
format: winston.format.printf(options => {
// you can pass any custom variable in options by calling
// logger.log({level: 'debug', message: 'hi', moduleName: 'my_module' })
return `[${options.moduleName}] ${options.level}: ${options.message}$`;
})
})
]
});
module.exports = function(name) {
// set the default moduleName of the child
return logger.child({moduleName: name});
}
Then at the top of each module, import the child using:
// my_module.js
const logger = require('./logging.js')('my_module_name');
logger.error('computer says no');
// output:
// [my_module_name] error: computer says no
You can specify a custom log format with Winston -
var moduleName = 'myModule';
var logger = new (winston.Logger)({
transports: [
new (winston.transports.Console)({
formatter: function(options) {
// Return string will be passed to logger.
return moduleName + ' - ' + (options.message ? options.message : '')
}
})
]
});
logger.info('This is a log message');
This will print -
myModule - This is a log message
So your module name will be appended to every log messsage.
With winston v3.3.3 you can do this by using winston.format.label and a customization of winston.format.printf:
const winston = require('winston');
const logger = winston.createLogger({
format: winston.format.combine(
winston.format.label({label: 'mymodule'}),
winston.format.printf(({label, message}) => {
return `${label}: ${message}`;
})
),
transports: [
new winston.transports.Console(),
],
});
logger.info('Hello, World!'); // prints "mymodule: Hello, World!"
Related
I am running this example given in winston timer documentation -
const winston = require('winston');
const logger = winston.createLogger({
level: 'silly',
format: winston.format.simple(),
});
logger.add(new winston.transports.Console({
format: winston.format.simple(),
}));
require('winston-timer')(logger, {"useColors":false});
logger.start_log('something', 'silly');
logger.stop_log('something', 'silly');
and in the output I am supposed to see the time, but I am seeing this instead -
nodejstutorials % node winstontimerbasic.js
silly: Starting timer "something"
silly: Finished timer "something" in
I don't see the elapsed time in the output. I am not sure what am I missing while going through the documentation. Please help.
If you want to use timestamp option you should say that when you're creating winston instance.
Change your winston import with this:
const winston, { format } = require('winston');
Destructure required properties from format:
const { combine, timestamp, printf } = format;
Create custom print option when you're creating your winston instance:
const logger = winston.createLogger({
level: "error",
format: combine(
format.errors({ stack: true }), // log the full stack
timestamp(), // get the time stamp part of the full log message
printf(({ level, message, timestamp, stack }) => {
// formating the log outcome to show/store
return `${level === "error" ? "❌" : "✅"} ${level}: ${message} ${
level === "error" ? "| 🕗 " + timestamp.substring(0, 19) : ""
} ${"\n"} ${level === "error" ? stack : ""}`;
}),
format.metadata() // >>>> ADD THIS LINE TO STORE the ERR OBJECT IN META field
),
transports: [
new winston.transports.Console({ level: "info" }),
new winston.transports.File({ filename: "logs.log", level: "info" }),
],
});
This will create a custom printf option for your winston logger. You can export it and you it like:
logger.error(error)
logger.info("this is an info")
And of course you can custumize printf option accordion to your taste
I'm building on my NodeJS API a custom logger using Winston.
I'm trying to log into a file but is it not working and cannot understand the issue.
I used some online resources to find out the solution but anything worked.
I used the transports.File() and added to my push but nothing happens.
My goal is to add the same logs I'm getting using the logger with the console into the file.
Same format and way of it.
My code
/* eslint-disable object-curly-newline */
/* eslint-disable arrow-parens */
/* eslint-disable comma-dangle */
// Logger
// This logger is used to show error/info messages about the status of the API
import winston from 'winston';
import moment from 'moment';
// import logSymbols from 'log-symbols';
import fs from 'fs-extra';
import { logger, NODE_ENV } from '../config';
const transports = [];
const dir = './logs';
// Creating the logs dir if does not exist
if (!fs.existsSync(dir)) {
fs.mkdirSync(dir);
}
// For development in prod need to check for dev env
// in dev we want more info error tracking
// in prod essential info error message
if (NODE_ENV.env !== 'development') {
transports.push(
new winston.transports.Console({
format: winston.format.combine(
winston.format.cli(),
winston.format.splat()
),
}),
new winston.transports.File({
level: 'error',
filename: `${dir}/logs.log`,
})
);
} else {
transports.push(new winston.transports.Console());
}
// Parse meta keys
const parser = string => {
if (!string) {
return '';
}
if (typeof string === 'string') {
return string;
}
return Object.keys(string).length ? JSON.stringify(string, undefined, 2) : '';
};
// Logger instance
const LoggerInstance = winston.createLogger({
level: logger.level,
levels: winston.config.npm.levels,
format: winston.format.combine(
winston.format.colorize(),
winston.format.timestamp(),
winston.format.errors({ stack: true }),
winston.format.splat(),
winston.format.printf(info => {
const { timestamp, level, message, meta } = info;
const ts = moment(timestamp)
.local()
.format('YYYY-MM-DD HH:MM:ss');
const metaMsg = meta ? `: ${parser(meta)}` : '';
// const symbol = level === 'error' ? logSymbols.error : logSymbols.success;
return `${ts} [${level}] ${parser(message)} ${metaMsg}`;
})
),
transports,
});
export default LoggerInstance;
While you set log level explicitly to 'error' in your winston transporter, you can not see any messages in debug level in the file. Please change your configuration so that your file. transporter accepts debug level messages too.
new winston.transports.File({
level: 'debug',
filename: `${dir}/logs.log`,
})
In my node application I'm using winston module to store my application logs. I am getting the log as:
2017-11-22T07:16:38.632Z - info: asset type is attached successfully
Now I want to add sessionID after timestamp. I want my log as:
2017-11-22T07:16:38.632Z -**sessionId here**- info: asset type is attached successfully.
My code which I used for winston logging is:
var winston = require('winston');
require('winston-daily-rotate-file');
const levels = {
error: 0,
warn: 1,
info: 2,
http: 3,
verbose: 4,
debug: 5,
silly: 6,
trace: 7
};
var transport = new (winston.transports.DailyRotateFile)({
filename: 'logs/./log',
datePattern: 'yyyy-MM-dd.',
prepend: true,
json: false,
level: process.env.ENV === 'development' ? 'debug' : 'info'
});
var logger = new (winston.Logger)({
levels: levels,
transports: [
transport
]
});
module.exports = logger;
You have to custom the log format https://github.com/winstonjs/winston/tree/2.x#custom-log-format
First update your transport :
var transport = new (winston...
...
level: process.env.ENV === 'development' ? 'debug' : 'info',
timestamp: () => {
let today = new Date();
return today.toISOString();
},
formatter: options => `${options.timestamp()} -${options.meta.sessionId}- ${options.level}: ${options.message}`
});
Then, just pass the session ID to your logger meta feature :
logger.info('asset type is attached successfully', {sessionId: 'mySessionID'});
and you get
2017-11-22T14:13:17.697Z -mySessionID- info: asset type is attached successfully
Edit : instead of exporting only the winston.logger object, we export an object which requires a sessionId as a parameter, and contains the winston.logger. We also update the transport, so we customize its formatter property in the new Logger object. In the formatter property, we replace the meta declaration with the new this.sessionId variable, so we don't use the meta property anymore.
logger.js :
var transport = new (winston...
...
level: process.env.ENV === 'development' ? 'debug' : 'info',
timestamp: () => {
let today = new Date();
return today.toISOString();
}
});
class Logger {
constructor(session) {
this.sessionId = session;
this.transport = transport;
this.transport.formatter = options => `${options.timestamp()} -${this.sessionId}- ${options.level}: ${options.message}`;
this.logger = new (winston.Logger)({
levels: levels,
transports: [this.transport]
});
}
}
module.exports = Logger;
server.js :
const Logger = require('./logger.js');
let logman = new Logger('my_session_id');
let logger = logman.logger;
logger.info('asset type is attached successfully');
2017-11-23T13:13:08.769Z -my_session_id- info: asset type is attached successfully
I had the same request and wasn't wild about the solution... I liked the way the standard logger was formatting logs and didn't want to reinvent that wheel. Also, I was hoping for something more compact and I think I found it. I'm not a javascript programmer, so it might not be good coding practice, but it seems to work well for me...
server.js
//Initiate winston logging please
const logger = require('winston');
const common = require('winston/lib/winston/common');
function myFormat(options) {
options.formatter = null
options.label = helpers.getSessionId(logger.req)
return common.log(options);
}
var consoleLoggingConfig = {
timestamp: true,
level: process.env.LOG_LEVEL ? process.env.LOG_LEVEL : "info",
handleExceptions: true,
humanReadableUnhandledException: true,
formatter: myFormat
}
logger.remove(logger.transports.Console);
logger.add(logger.transports.Console, consoleLoggingConfig)
logger.info('Winston logging initiated', consoleLoggingConfig)
//Helper to ensure that logger has access to the request object to obtain the session id
app.use(helpers.attachReqToLogger)
module.exports=logger
helpers.js
// Helper to ensure that the winston logger has the request object on it to obtain the sessionId
helpers.attachReqToLogger = function(req, res, next) {
logger.req = req
next();
}
import winston from "winston";
import { v4 as uuidv4 } from "uuid";
const { format } = winston;
const commonFormat = [format.timestamp(), format.json()];
const withId = [
format.printf(({ message, id, ...rest }) => {
return JSON.stringify(
{
// if frontend privide id, use it, else create one
id: id ?? uuidv4(),
message,
...rest,
},
null,
// prettyPrint seems will overload printf's output
// so i mannually do this
4,
);
}),
];
export const logger = winston.createLogger({
level: "debug",
format: format.combine(...commonFormat, ...withId),
// ...
I have worked on Sails v0.11.5 and used colors:false in development.js to get clean log without color codings
var winston = require('winston');
var logDir = '/var/log/nginx';
var applicationLogger = new winston.Logger({
transports: [
new(winston.transports.File)({
level: 'info',
filename: logDir + '/application.log'
})
]
});
module.exports = {
log: {
colors: false, // To get clean logs without prefixes or color codings
custom: applicationLogger
}
};
Now I am working on Sails v0.12.13 and using same configuration colors:false but this time I am getting color codes in log
var winston = require('winston');
var logDir = '/var/log/nginx';
var applicationLogger = new winston.Logger({
transports: [
new(winston.transports.File)({
level: 'info',
filename: logDir + '/application.log'
})
]
});
module.exports = {
log: {
colors: false, // To get clean logs without prefixes or color codings
custom: applicationLogger
}
};
I am not aware about how this attribute was defined in Sails v0.11.5, but I guess you should try using colorize instead of colors
module.exports = {
log: {
colorize: false, // To get clean logs without prefixes or color codings
custom: applicationLogger
}
};
Please tell me if I am missing something with this attribute.
Edit
Seems colors attribute is part of built-in logger called captains-log, maybe your custom logger winston is overriding these changes, can you check the changes and tell if my answer worked for you?
I want to add timestamp to logs.
What is the best way to achieve this?
Thanks.
Above answers did not work for me. In case you are trying to add timestamp to your logs using the latest version of Winston - 3.0.0-rc1, this worked like charm:
const {transports, createLogger, format} = require('winston');
const logger = createLogger({
format: format.combine(
format.timestamp(),
format.json()
),
transports: [
new transports.Console(),
new transports.File({filename: 'logs/error/error.log', level: 'error'}),
new transports.File({filename: 'logs/activity/activity.log', level:'info'})
]
});
I used 'format.combine()'. Since I needed timestamp on all my transports, I added the formatting option within the createLogger, rather than inside each transport. My output on console and on file (activity.log) are as follows:
{"message":"Connected to mongodb","level":"info","timestamp":"2018-02-01T22:35:27.758Z"}
{"message":"Connected to mongodb","level":"info","timestamp":"2018-02-01T22:35:27.758Z"}
We can add formatting to this timestamp in 'format.combine()' as usual using:
format.timestamp({format:'MM-YY-DD'})
I was dealing with the same issue myself. There are two ways I was able to do this.
When you include Winston, it usually defaults to adding a Console transport. In order to get timestamps to work in this default case, I needed to either:
Remove the console transport and add again with the timestamp option.
Create your own Logger object with the timestamp option set to true.
The first:
var winston = require('winston');
winston.remove(winston.transports.Console);
winston.add(winston.transports.Console, {'timestamp':true});
The second, and cleaner option:
var winston = require('winston');
var logger = new (winston.Logger)({
transports: [
new (winston.transports.Console)({'timestamp':true})
]
});
Some of the other options for Console transport can be found here:
level: Level of messages that this transport should log (default 'debug').
silent: Boolean flag indicating whether to suppress output (default false).
colorize: Boolean flag indicating if we should colorize output (default false).
timestamp: Boolean flag indicating if we should prepend output with timestamps (default false). If function is specified, its return value will be used instead of timestamps.
We can do like this also
var winston = require('winston');
const { createLogger, format, transports } = require('winston');
var config = require('../configurations/envconfig.js');
var loggerLevel = process.env.LOGGERLEVEL || config.get('LOGGERLEVEL');
var logger = winston.createLogger({
format: format.combine(
format.timestamp({
format: 'YYYY-MM-DD HH:mm:ss'
}),
format.printf(info => `${info.timestamp} ${info.level}: ${info.message}`+(info.splat!==undefined?`${info.splat}`:" "))
),
transports: [
new (winston.transports.Console)({ level: loggerLevel }),
]
});
module.exports = logger;
You can use built-in util and forever to achieve logging with timestap for your nodejs server.
When you start a server add log output as part of the parameter:
forever start -ao log/out.log server.js
And then you can write util in your server.js
server.js
var util = require('util');
util.log("something with timestamp");
The output will look something like this to out.log file:
out.log
15 Mar 15:09:28 - something with timestamp
I took Biswadev's answer and created a stringified JSON object. This way if i need to process the logs later it will be in a well structured format.
const winston = require('winston');
const { createLogger, format, transports } = require('winston');
const dotenv = require('dotenv');
dotenv.config();
var logger = createLogger({
level: 'info',
format: format.combine(
format.timestamp({
format: 'YYYY-MM-DD HH:mm:ss',
}),
format.printf((info) =>
JSON.stringify({
t: info.timestamp,
l: info.level,
m: info.message,
s: info.splat !== undefined ? `${info.splat}` : '',
}) + ','
)
),
});
if (process.env.NODE_ENV !== 'PRODUCTION') {
logger.add(new transports.Console({ format: winston.format.cli() }));
// Turn these on to create logs as if it were production
// logger.add(new transports.File({ filename: 'log/output/error.log', level: 'error' }));
// logger.add(new transports.File({ filename: 'log/output/warn.log', level: 'warn' }));
// logger.add(new transports.File({ filename: 'log/output/info.log', level: 'info' }));
} else {
logger.add(new transports.File({ filename: 'log/output/error.log', level: 'error' }));
logger.add(new transports.File({ filename: 'log/output/warn.log', level: 'warn' }));
logger.add(new transports.File({ filename: 'log/output/info.log', level: 'info' }));
}
module.exports = {
logger,
};
Usage:
app.listen(port, () => logger.info(`app is running on port ${port}`));
Output:
info.log file:
{"t":"2020-08-06 08:02:05","l":"info","m":"app is running on port 3001","s":""},
Console:
info: app is running on port 3001
Although I'm not aware of winston, this is a suggestion. I use log4js for logging & my logs by default look like this
[2012-04-23 16:36:02.965] [INFO] Development - Node Application is running on port 8090
[2012-04-23 16:36:02.966] [FATAL] Development - Connection Terminated to '127.0.0.1' '6379'
Development is the environment of my node process & [INFO|FATAL] is log level
Maintaining different profiles for logging is possible in log4js. I have Development & Production profiles. Also there are logger types like rolling file appender, console appender, etc. As a addon your log files will be colorful based on the log level [Trace, Info, Debug, Error, Fatal] ;)
log4js will override your console.log It is a configurable parameter now in 0.5+
we could use console-stamp to add timestamp and log level to the existing console: require('console-stamp')(console, '[yyyy-mm-dd HH:MM:ss.l]')
See https://github.com/starak/node-console-stamp for the details
Sometimes default timestamp format can be not convenient for you.
You can override it with your implementation.
Instead of
var winston = require('winston');
var logger = new (winston.Logger)({
transports: [
new (winston.transports.Console)({'timestamp':true})
]
});
you can write
var winston = require('winston');
var logger = new (winston.Logger)({
transports: [
new (winston.transports.Console)({
'timestamp': function() {
return <write your custom formatted date here>;
}
})
]
});
See https://github.com/winstonjs/winston#custom-log-format for the details
Another solution is wrapping the logger into a file that exports some functions like logger.info(), logger.error(), etc. then you just pass an extra key to be sent on every message log.
loggerService.js
const logger = winston.createLogger({ ... })
function handleLog(message, level) {
const logData = {
timestamp: Date.now(),
message,
}
return logger[level](logData)
}
function info(message) {
handleLog(message, 'info')
}
function error(message) {
handleLog(message, 'error')
}
function warn(message) {
handleLog(message, 'warn')
}
module.exports = {
info,
error,
warn
}
whatever-file.js
const logger = require('./services/loggerService')
logger.info('Hello World!')
your-log.log
{"timestamp":"2019-08-21 06:42:27","message":"Hello World!","level":"info"}