Winston Object Logging - node.js

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

Related

Meta is null in Winston-mongodb

I am using winston-mongodb modlue to log the request response.
Logger.service.ts
const options = {
console: {
db: DB_URL,
level: 'info',
format: format.combine(format.timestamp(), format.json()),
collection: 'logs',
},
error: {
db: DB_URL,
level: 'error',
format: format.combine(format.timestamp(), format.json()),
collection: 'logs',
}
};
const logger = createLogger({
transports: [
new MongoDB(options.console),
new MongoDB(options.error)
],
});
Server.ts
server.listen(port, () => {
logger.info({message:`Server is up and running on port ${port}`, meta: {
myProp: 'foo'
}});
);
Result:
{
"_id" : ObjectId("5ed8bd2c726d273004b082ca"),
"timestamp" : ISODate("2020-06-04T09:21:48.835Z"),
"level" : "info",
"message" : "Server is up and running on port 4000 HI",
**"meta" : null**
}
I am trying to add meta data to logger. But it is adding null to the result.
To get err object stored in the meta (with all its javascript properties), you need to specify :
format.metadata()
In the createLogger call.
Here is a working sample code:
// Declare winston
const winston = require("winston");
// Import all needed using Object Destructuring
const { createLogger, format, transports } = require("winston");
const { combine, timestamp, printf } = format;
// Export the module
module.exports = function (err, req, res, next) {
const logger = 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 `${timestamp} ${level}: ${message} - ${stack}`;
}),
format.metadata() // >>>> ADD THIS LINE TO STORE the ERR OBJECT IN META field
),
transports: [
new transports.Console(), // show the full stack error on the console
new winston.transports.File({
// log full stack error on the file
filename: "logfile.log",
format: format.combine(
format.colorize({
all: false,
})
),
}),
new winston.transports.MongoDB({
db: "mongodb://localhost/logdb",
// collection: "log",
level: "error",
storeHost: true,
capped: true,
// metaKey: 'meta'
}),
],
});
logger.log({
level: "error",
message: err,
});
// Response sent to client but nothing related to winston
res.status(500).json(err.message);
};
try this
// create new meta object to store in database
let logMeta={
stack: err.stack, // error stack
otherInfo: otherInfoData // any other info you want to store
}
// use metadata propery for meta
logger.error(err.message, {metadata: logMeta });
OR another way, if you are exporting the function as error middleware (using nodejs, express):
module.exports = function (err, req, res, next) {
logger.log({
level: "error",
message: err.message,
stack: err.stack,
metadata: err.stack, // Put what you like as meta
});
res.status(500).send("Uncaught error caught here.");
};
Specify metadata object explicitly:
logger.error(err.message, {metadata: err.stack});
apparently i had the same problem where the meta property in mongoDB transport was null: This is how i fixed it....
const winston = require("winston")
const { format } = require("winstone")
winstone.add(new
winstone.transports.MongoDB({
format:format.metadata()})

How to write a log file with winston on NodeJS

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`,
})

Json format logs using Winston 3.0 and Nodejs not showing in Kibana

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

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

Resources