Json format logs using Winston 3.0 and Nodejs not showing in Kibana - node.js

I am creating a nodejs app and using Winston 3.0 for logging. The requirement is logs should be json format in Kibana like so
{"message":"the_log_message","level":"info","log_timestamp":"2019-12-14T21:28:44+05:30","service_name":"foo","service_id":"1.0.0"}
Following are the things I am able to achieve
Logs are printing correctly on local machine. 2. When not using Json format mentioned above Kibana is showing the logs.3. I don't think Kibana configuration is issue as Non Json logs are printing. Need help in printing the logs in kibana in above stated format. Any help is appreciatedFollowing is my logger file which prints on local perfectly.JSON.stringify also does not help
'use strict';
const {createLogger, format, transports} = require('winston');
const moment = require('moment-timezone');
const env = process.env.NODE_ENV || 'development';
const serviceId = process.env.npm_package_name;
const serviceVersion = process.env.npm_package_version;
const appendTimestamp = format((info, opts) => {
if (opts.tz)
info.log_timestamp = moment().tz(opts.tz).format();
return info;
});
const addAppNameFormat = format(info => {
info.service_name = serviceId;
return info;
});
const addAppVersionFormat = format(info => {
info.service_id = serviceVersion;
return info;
});
const logger = createLogger({
// change level if in dev environment versus production
level: env === 'prd' ? 'info' : 'debug',
format: format.combine(
appendTimestamp({tz: Intl.DateTimeFormat().resolvedOptions().timeZone}),
addAppNameFormat(),
addAppVersionFormat(),
format.json()
),
transports: [
new transports.Console()
]
});
module.exports = logger;
Additionally adding the logger file which works fine but does not print Json format logs
const { createLogger, format, transports } = require('winston');
const moment = require('moment-timezone');
const appendTimestamp = format((info, opts) => {
if(opts.tz)
info.timestamp = moment().tz(opts.tz).format();
return info;
});
const logger = createLogger({
// change level if in dev environment versus production
level: env === 'prd' ? 'info' : 'debug',
format: format.combine(
appendTimestamp({ tz: Intl.DateTimeFormat().resolvedOptions().timeZone }),
format.json()
),
transports: [
new transports.Console({
format: format.combine(
format.colorize(),
format.printf(
info =>
`${info.timestamp} ${process.env.npm_package_name} [${process.env.npm_package_version}] ${info.level} [${info.label}]: ${JSON.stringify(info.message)}`
)
)
})
]
});
module.exports = logger;
EDIT1:Made following changes in the code. Adding a space before my logs are generating the logs in Kibana but we are not able to parse it. If we are not using the space in return statement, logs do not generate return ` ${value}`; I am suspecting my Winston configuration is not supporting JSON format. Reiterating that on local machine logs are generating fine.
const logger = createLogger({
// change level if in dev environment versus production
level: env === 'prd' ? 'info' : 'debug',
format: format.combine(
appendTimestamp({tz: Intl.DateTimeFormat().resolvedOptions().timeZone}),
format.json()
),
transports: [
new transports.Console({
format: format.combine(
format.printf(
(info) => {
const value = JSON.stringify({
'log-timestamp': info.timestamp,
'service-id': process.env.NOMAD_TASK_NAME,
'service-version': process.env.npm_package_version,
'level': info.level.toUpperCase(),
'env': process.env.ENV,
'message': info.message,
'logger': process.env.npm_package_name,
'thread': 'main'
});
return ` ${value}`;
}
)
)
})
]
});

Turns out the way the Kibana was configured was incorrect. The code itself was working fine

Related

Winston Object Logging

I am trying to use winston to write logs to my logfiles. I followed the solution mentioned in : Winston logging object
But I am getting a weird issue.
When I use the above solution (in the link) by Pierre C , on my node emailer transporter like so :
transporter.sendMail(mailOptions, function(err, data) {
if (err) {
//not checked
} else {
logger.info(`Looks good`,{...data});
}
});
I works great as expected , but when I use the same format in a try catch block
try {
} catch(err) {
logger.error(`Looks bad`,{...err});
}
My metadata block comes as empty , my logger.js looks like this
const winston = require ('winston');
const path = require('path')
//const{mainModule} = require('process')
const {combine ,timestamp ,json,label,printf,metadata,colorize,splat} = winston.format;
const logFormat = winston.format.printf(info => `${info.timestamp}:level: ${info.level} [${info.label}]: ${info.message}`)
//creating custom logs to feed data in log files
const emaillogger = winston.createLogger({
levels:winston.config.syslog.levels,
// level: process.env.LOG_LEVEL || 'info',
format : combine(
label({label: path.basename(require.main.filename)}),
timestamp({format : 'MM-DD-YYYY hh:mm:ss A'}),
// Format the metadata object
metadata({ fillExcept: ['timestamp','message', 'level', 'label'] })),
transports:[
//new winston.transports.Console({format: combine(colorize())}),
new winston.transports.File({filename:'logs/combined.log',format:combine(json())}),
new winston.transports.File({filename:'logs/errorlogs.log',level : 'error',format:combine(json())})
],
exitOnError: false
});
if (process.env.NODE_ENV !== 'production') {
emaillogger.add(new winston.transports.Console({
format: combine(colorize(),logFormat)
}));
}
module.exports = { emailLogger: emaillogger };

log json with date and time format with winston node logger

I am trying to log json with winston that includes a timestamp, I have the following configuration:
'use strict';
import * as winston from 'winston';
const LOG = !!process.env.LOG;
export const { error, info, debug } = winston.createLogger({
transports: [new winston.transports.Console()],
silent: process.env.NODE_ENV === 'test' && !LOG,
format: winston.format.combine(
winston.format.timestamp({
format: 'YYYY-MM-DD HH:mm:ss'
}),
winston.format.colorize({ all: true }),
winston.format.simple()
)
});
But it is logging messages like this:
info: connecting /closebanner {"timestamp":"2018-08-22 22:09:35"}
Only the timestamp is in json format and not the message.
You can use winston json formatter:
const { createLogger, format, transports } = require('winston');
const { combine, timestamp, json } = format;
const logger = createLogger({
format: combine(
timestamp({
format: 'YYYY-MM-DD HH:mm:ss'
}),
json(),
),
transports: [new transports.Console()]
})
So for example:
app.get('/', (req, res) => {
logger.info('request came');
logger.error({ "errorMessage": "something went wrong " })
return res.status(500).end();
})
app.listen(8080, () => {
logger.info('app started')
})
When You request localhost:8080 using GET HTTP method, it will result in:
{"message":"app started","level":"info","timestamp":"2018-08-23 00:56:48"}
{"message":"request came","level":"info","timestamp":"2018-08-23 00:56:54"}
{"message":{"errorMessage":"something went wrong"},"level":"error","timestamp":"2018-08-23 00:56:54"}
Please note that:
colorize will not work with JSON - check github discussion
format.simple() is not needed since it is just another type (string literal), more info in doc

How to add session id to each log in winston logger

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),
// ...

How do I change my node winston JSON output to be single line

When I create a nodejs winston console logger and set json:true, it always output JSON logs in multiline format. If I pipe these to a file and try to grep that file, my grep hits only include part of the log line. I want winston to output my log lines in JSON format, but not to pretty print the JSON
Here is my config (coffeescript, apologies):
winston = require 'winston'
logger = new (winston.Logger)(
transports: [
new winston.transports.Console({
json: true
})
]
)
And some sample output:
{
"name": "User4",
"level": "info",
"message": "multi line whyyyyy"
}
winston 3.x (current version)
Default formatter
const winston = require('winston');
const logger = winston.createLogger({
format: winston.format.json(),
transports: [
new winston.transports.Console()
]
});
Example
const test = { t: 'test', array: [1, 2, 3] };
logger.info('your message', test);
// logger output:
// {"t":"test","array":[1,2,3],"level":"info","message":"your message"}
Custom formatter
const winston = require('winston');
const { splat, combine, timestamp, printf } = winston.format;
// meta param is ensured by splat()
const myFormat = printf(({ timestamp, level, message, meta }) => {
return `${timestamp};${level};${message};${meta? JSON.stringify(meta) : ''}`;
});
const logger = winston.createLogger({
format: combine(
timestamp(),
splat(),
myFormat
),
transports: [
new winston.transports.Console()
]
});
Example:
const test = { t: 'test', array: [1, 2, 3] };
// NOTE: wrapping object name in `{...}` ensures that JSON.stringify will never
// return an empty string e.g. if `test = 0` you won't get any info if
// you pass `test` instead of `{ test }` to the logger.info(...)
logger.info('your message', { test });
// logger output:
// 2018-09-18T20:21:10.899Z;info;your message;{"test": {"t":"test","array":[1,2,3]}}
winston 2.x (legacy version)
It seems that the accepted answer is outdated. Here is how to do this for current winston version (2.3.1):
var winston = require('winston');
var logger = new (winston.Logger)({
transports: [
new (winston.transports.Console)({
json: true,
stringify: (obj) => JSON.stringify(obj),
})
]
})
Note the parenthesis around winston.transports.Console.
The winston transports provide a way to override the stringify method, so by modifying the config above I got single line JSON output.
New config:
winston = require('winston')
logger = new (winston.Logger)({
transports: [
new winston.transports.Console({
json: true,
stringify: (obj) => JSON.stringify(obj)
})
]
})
"winston": "^3.0.0"
function createAppLogger() {
const { combine, timestamp, printf, colorize } = format;
return createLogger({
level: 'info',
format: combine(
colorize(),
timestamp(),
printf(info => {
return `${info.timestamp} [${info.level}] : ${JSON.stringify(info.message)}`;
})
),
transports: [new transports.Console()]
});
}
Output:
2018-08-11T13:13:37.554Z [info] : {"data":{"hello":"Hello, World"}}
On "winston": "^3.2.1"
This is works great for me
const {createLogger, format} = require('winston');
// instantiate a new Winston Logger with the settings defined above
var logger = createLogger({
format: format.combine(
format.timestamp(),
// format.timestamp({format:'MM/DD/YYYY hh:mm:ss.SSS'}),
format.json(),
format.printf(info => {
return `${info.timestamp} [${info.level}] : ${info.message}`;
})
),
transports: [
new winston.transports.File(options.file),
new winston.transports.Console(options.console)
],
exitOnError: false, // do not exit on handled exceptions
});
winston.format.printf(
info => `${info.timestamp} ${info.level}: ${JSON.stringify(info.message, null, 2)}`))
will pretty print the json object

Node.js - How to add timestamp to logs using Winston library?

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"}

Resources