Winston not displaying error details - node.js

I am using winston for logging and most of the time it works well, but when there is an exception, it just does not print any details.
Here is my code for configuring winston:
// Create logger
const logger = winston.createLogger()
// Create timestamp format
const tsFormat = () => (new Date()).toLocaleTimeString()
// Attach transports based on app mode
if (process.env.APP_MODE === 'production') {
// Log to file
logger.add(new (winston.transports.DailyRotateFile)({
filename: path.join(__dirname, '../logs/errors-%DATE%.log'),
datePattern: 'YYYY-MM-DD-HH',
zippedArchive: true,
format: winston.format.json(),
handleExceptions: true
}))
} else {
// Log to the console
logger.add(new (winston.transports.Console)({
timestamp: tsFormat,
colorize: true,
handleExceptions: true
}))
}
module.exports = logger
I am also using Express and in my error handling middleware, I have this code:
const logger = require('../config/winston')
function (err, req, res, next) {
console.log(err)
logger.error(err)
res.status(500).send({ error: 'Please try again later.' })
}
The problem is that when an error occurs all winston logs is:
{"level":"error"}
While the good old console.log() displays:
TypeError: Cannot read property 'filename' of undefined
at router.post (/Users/balazsvincze/Desktop/testapi/app/routes/upload.js:16:33)
at Layer.handle [as handle_request] (/Users/de/Desktop/testapi/node_modules/express/lib/router/layer.js:95:5)
at next (/Users/balazsvincze/Desktop/testapi/node_modules/express/lib/router/route.js:137:13)
at Immediate.<anonymous> (/Users/balazsvincze/Desktop/testapi/node_modules/multer/lib/make-middleware.js:53:37)
at runCallback (timers.js:814:20)
at tryOnImmediate (timers.js:768:5)
at processImmediate [as _immediateCallback] (timers.js:745:5)
How do I get winston to log something like this including the stack trace?
Many thanks!
EDIT: If I change the line logger.error(err) to logger.error(err.message), at least I get this:
{"message":"Cannot read property 'filename' of
undefined","level":"error"}
Still very far off from what I am after.

I think what you're missing is format.errors({ stack: true }) in winston.createLogger.
const logger = winston.createLogger({
level: 'debug',
format: format.combine(
format.errors({ stack: true }),
print,
),
transports: [new transports.Console()],
});
See this GitHub thread for more information.
The reason this is happening is because the interesting Error object properties, like .stack, are non-enumerable. Some functions check if the their parameters are Error instances, like console.error, and other functions ignore all non-enumerable properties, like winston.<log-level> and JSON.stringify.
> console.error(new Error('foo'))
Error: foo
at repl:1:15
at Script.runInThisContext (vm.js:124:20)
...(abbr)
> JSON.stringify(new Error('foo'))
'{}'
All that said, it's horrible usability to have an error logger essentially ignore errors... I just lost too much time to this.

A quick and dirty way would be to log err.stack:
logger.error(err.stack);
A more elaborate method would be to implement a custom format specifically for Error instances. There's some example code on how to implement that in this Github issue.

Using format.
const { combine, timestamp, label, printf } = winston.format;
const myFormat = printf(info => {
if(info instanceof Error) {
return `${info.timestamp} [${info.label}] ${info.level}: ${info.message} ${info.stack}`;
}
return `${info.timestamp} [${info.label}] ${info.level}: ${info.message}`;
});
winston.createLogger({
level: "info",
format: combine(
winston.format.splat(),
label({ label: filename}),
timestamp(),
myFormat,
),
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: path.join(os.tmpdir(), "test", "test.log"), level: "info" }),
],
});

If you are using nodejs and having trouble with the rejection handlers, I wrote a little module that you can use for reference.
You have to make sure that when you perform the Array De-structuring for format, that you include 'errors'.
const { combine, timestamp, json, errors } = format;
Winston documentation states
The errors format allows you to pass in an instance of a JavaScript Error directly to the logger. It allows you to specify whether not to include the stack-trace.
const errorsFormat = errors({ stack: true })
The complete code below is for reference. It is a module that I am using to log some information to a file and some to a mysql database.
require('dotenv').config();
const {createLogger, transports, format} = require('winston');
const { combine, timestamp, json, errors } = format;
const winstonMysql = require('winston-mysql');
const logform = require('logform');
const errorsFormat = errors({ stack: true })
const options_default = {
host: 'localhost',
user: process.env.MYSQL_USER,
password: process.env.MYSQL_LOGIN,
database: process.env.MYSQL_DATABASE,
table: 'sys_logs_default'
};
const logger = createLogger({
level: 'info',
format: format.json(),
defaultMeta: { service: 'user-service' },
transports: [
new transports.Console({
format: combine(timestamp(), json(), errorsFormat)
}),
// or use: options_custom / options_json
new winstonMysql(options_default),
],
exceptionHandlers: [
new transports.File({ filename: 'exception.log' }),
],
rejectionHandlers: [
new transports.File({ filename: 'rejections.log' }),
],
});
module.exports = logger;

I faced the same issue earlier, and I know it's a little but for people who are still looking for a solution can use utils-deep-clone package. Just addded a layer above Winston to handle error objects.
const winston = require('winston')
const { toJSON } = require('utils-deep-clone')
const winstonLogger = new (winston.Logger)({
transports: [
new (winston.transports.Console)()
]
})
const log = (message, payload) => {
try {
payload = toJSON(payload)
return winstonLogger.log('info', message, payload)
} catch(error) {
console.error('some error occured while consoling')
}
}
const testObj = {
error: new Error('It should work')
}
log('Testing few bugs', testObj)
The output will be:
info: Testing few bugs message=It should work, stack=Error: It should work
at Object.<anonymous> (/Users/atishay/Desktop/utils-error-deep-log/logger.js:20:10)
at Module._compile (module.js:652:30)
at Object.Module._extensions..js (module.js:663:10)
at Module.load (module.js:565:32)
at tryModuleLoad (module.js:505:12)
at Function.Module._load (module.js:497:3)
at Function.Module.runMain (module.js:693:10)
at startup (bootstrap_node.js:188:16)
at bootstrap_node.js:609:3

Related

Error [ERR_STREAM_DESTROYED]: Cannot call write after a stream was destroyed with Winston

I am getting an error with winston logging if I am using winston-daily-rotate-file.
When I am building my application with webpack there are no errors but when I execute my build below error is coming(stack Trace):
/app/web-app/server/node_modules/winston/lib/winston/logger.js:307
throw ex;
^
Error [ERR_STREAM_DESTROYED]: Cannot call write after a stream was destroyed
at doWrite (_stream_writable.js:399:19)
at writeOrBuffer (_stream_writable.js:387:5)
at WriteStream.Writable.write (_stream_writable.js:318:11)
at Object.<anonymous> (/mnt/e/workspace/codebase/hermes_dashboard/app/web-app/server/node_modules/file-stream-rotator/FileStreamRotator.js:616:26)
at DailyRotateFile.log (/mnt/e/workspace/codebase/hermes_dashboard/app/web-app/server/node_modules/winston-daily-rotate-file/daily-rotate-file.js:157:20)
at DailyRotateFile._write (/mnt/e/workspace/codebase/hermes_dashboard/app/web-app/server/node_modules/winston-transport/index.js:82:19)
at doWrite (/mnt/e/workspace/codebase/hermes_dashboard/app/web-app/server/node_modules/winston-transport/node_modules/readable-stream/lib/_stream_writable.js:428:64)
at writeOrBuffer (/mnt/e/workspace/codebase/hermes_dashboard/app/web-app/server/node_modules/winston-transport/node_modules/readable-stream/lib/_stream_writable.js:417:5)
at DailyRotateFile.Writable.write (/mnt/e/workspace/codebase/hermes_dashboard/app/web-app/server/node_modules/winston-transport/node_modules/readable-stream/lib/_stream_writable.js:334:11)
at DerivedLogger.ondata (/mnt/e/workspace/codebase/hermes_dashboard/app/web-app/server/node_modules/winston/node_modules/readable-stream/lib/_stream_readable.js:681:20)
at DerivedLogger.emit (events.js:327:22)
My winston config is:
const winston = require('winston');
const DailyRotateFile = require('winston-daily-rotate-file');
var options = {
file: {
zippedArchive: true,
prettyPrint: true,
json: false,
filename: `/app/web-app/server/logs/server_logs/server.log`,
datePattern: '.yyyy-MM-dd',
handleExceptions: true,
maxsize: '10m', // 10MB
maxFiles: 5,
colorize: false,
},
console: {
level: 'debug',
prettyPrint: true,
handleExceptions: true,
json: false,
colorize: true,
},
};
var logger = new winston.createLogger({
transports: [
// new winston.transports.DailyRotateFile(options.file),
new DailyRotateFile(options.file),
new winston.transports.Console(options.console),
],
exitOnError: false, // do not exit on handled exceptions
});
logger.stream = {
write: message => {
logger.info(message);
},
};
module.exports = logger;
I had the exact same error, line number and everything, happening on aws elasticbeanstalk.
It worked locally, but when it got to aws, the error happened because node is running as user 'webapp'. It looks like that user doesn't have permission to write files to /var/log, which seems odd. So I changed it to log to /tmp and the error went away. Note: be sure the log files are owned by webapp (or whatever user runs the node app) or the error will persist.
In my case the directory the logs were supposed to be saved to didn't exist and that was causing the error.

Error after update winston to version 3.2.1. TypeError: self._addDefaultMeta is not a function

After I updated winston to version 3.2.1, I get an error while trying to hot recompile the project (when my project is started and I make changes).
I tried to update all my dependencies to the latest versions, but this did't help.
It seems webpack-hot-middleware doesn't work correctly with the latest version of winston.
I would be grateful for advice on how to fix this.
Error:
i 「wdm」: Compiling...
D:\Dev\MyProjectName\node_modules\winston\lib\winston\create-logger.js:80
self._addDefaultMeta(info);
^
TypeError: self._addDefaultMeta is not a function
at Object.DerivedLogger.<computed> [as log] (D:\Dev\MyProjectName\node_modules\winston\lib\winston\create-logger.js:80:14)
at onInvalid (D:\Dev\MyProjectName\node_modules\webpack-hot-middleware\middleware.js:27:24)
at SyncHook.eval [as call] (eval at create (D:\Dev\MyProjectName\node_modules\tapable\lib\HookCodeFactory.js:19:10), <anonymous>:9:1)
at SyncHook.lazyCompileHook (D:\Dev\MyProjectName\node_modules\tapable\lib\Hook.js:154:20)
at Watchpack.<anonymous> (D:\Dev\MyProjectName\node_modules\webpack\lib\Watching.js:142:33)
at Object.onceWrapper (events.js:300:26)
at Watchpack.emit (events.js:210:5)
at Watchpack._onChange (D:\Dev\MyProjectName\node_modules\watchpack\lib\watchpack.js:118:7)
at Watchpack.<anonymous> (D:\Dev\MyProjectName\node_modules\watchpack\lib\watchpack.js:99:8)
at Watcher.emit (events.js:210:5)
at D:\Dev\MyProjectName\node_modules\watchpack\lib\DirectoryWatcher.js:101:9
at Array.forEach (<anonymous>)
at DirectoryWatcher.setFileTime (D:\Dev\MyProjectName\node_modules\watchpack\lib\DirectoryWatcher.js:99:42)
at DirectoryWatcher.<anonymous> (D:\Dev\MyProjectName\node_modules\watchpack\lib\DirectoryWatcher.js:312:12)
at D:\Dev\MyProjectName\node_modules\graceful-fs\polyfills.js:285:20
at FSReqCallback.oncomplete (fs.js:159:5)
Logger config:
const winston = require('winston');
const transports = [];
const alignColorsAndTime = winston.format.combine(
winston.format.timestamp({
format: 'HH:MM:ss:SS DD.MM.YY',
}),
winston.format.printf(
info => `${info.timestamp} ${info.level}:${info.message}`,
),
);
/**
* Console transporter
*/
transports.push(new winston.transports.Console({
level: config.logging.console.level,
}));
const logger = winston.createLogger({
level: 'debug',
transports,
format: winston.format.combine(winston.format.colorize(), alignColorsAndTime),
exitOnError: false,
});
module.exports = logger;
Dependency versions:
winston#3.2.1
webpack-hot-middleware#2.25.0
webpack#4.41.2
Check this thread for solution.
https://github.com/winstonjs/winston/issues/1591
bind logger back explicitly to its methods
ex:
const logger = winston.createLogger({
transports: [
new winston.transports.Console()
]
});
logger.info.bind(logger)

How to info message and error message stored separate file using winston log file?

I tried to logging info and errors in log file using winston.If any info stored info.log. If uncaught exception errors occurred its stored only error.log file.but If commom errors are occurred its stored both file(info .log and error.log) how to fix it any one give some solution.
winston.js
const winston = require('winston');
require('winston-mongodb');
require('express-async-errors');
const winstonLogger = function () {
winston.add(new winston.transports.Console({
format: winston.format.combine(
winston.format.simple(),
winston.format.timestamp(),
winston.format.prettyPrint(),
winston.format.colorize()
),
handleExceptions: true
}));
// Info Log messages to file
winston.add(new winston.transports.File({
filename: 'logs/info.log',
level: 'info'
}));
// Error Log Messages file
winston.add(new winston.transports.File({
filename: 'logs/error.log',
level: 'error',
handleExceptions: true,
}));
};
module.exports = winstonLogger;
I want info message stored on info.log file and error message stored on error.log file.uncaught exception error stored in separate file using winston
Winston logging is based on the level you specify. All logs below that level go to a specified file.
logging levels 0 to 5 (highest to lowest):
0: error
1: warn
2: info
3: verbose
4: debug
5: silly
Below is the sample code from winston documentation :
const logger = winston.createLogger({
level: 'info',
format: winston.format.json(),
defaultMeta: { service: 'user-service' },
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: 'error.log', level: 'error' }),
new winston.transports.File({ filename: 'combined.log' })
]
});
You can filter out logs by creating two loggers and adding name property to them.
This worked for me.
const winston = require('winston');
const infoLogger = winston.createLogger({
defaultMeta: { service: "log-service" },
transports: [
new winston.transports.File({
name: "info",
filename: "./logs/info.log",
level: "info",
})
],
});
const errorLogger = winston.createLogger({
defaultMeta: { service: "log-service" },
transports: [
new winston.transports.File({
name: "error",
filename: "./logs/error.log",
level: "error",
})
],
});
const logger = {
info: (params) => {
return infoLogger.info(params);
},
error: (params) => {
return errorLogger.error(params);
},
};
logger.info("info 1"); // Will go to info.log
logger.error("error 1"); // Will go to error.log
logger.info("info 2"); // Will go to info.log
logger.error("error 2 "); // Will go to error.log

Writing unhandled exceptions to file with winston#3

I'm trying to create a logger with winston#3 that handles unhandled exceptions in my Node.js application by logging the exception to the console and to a log file, but it is only being logged on the console.
const { path } = require('app-root-path');
const { createLogger, format, transports } = require('winston');
const unhandledExceptions = createLogger({
exceptionHandlers: [
new transports.File({
filename: `${path}/logging/logs/exceptions.log`,
}),
new transports.Console()
],
format: format.combine(
format.label({ label: 'Unhandled exception' }),
format.timestamp({
format: 'YYYY-MM-DD HH:mm:ss'
}),
format.prettyPrint()
)
});
throw new Error('wtf');
Any ideas on how I can get the exception written to the log file as well? Happy to provide more info.
Thanks in advance.

Winston not logging uncaught exceptions

I'm trying to use winston to log unhandled exceptions to file and console. The problem is that it is not logging them. Here my setup:
var winston = require('winston');
let file = process.env.APP_TOOL_SET_API_PROJECT+"/logs/app.log";
console.log(process.env.APP_TOOL_SET_API_PROJECT);
// define the custom settings for each transport (file, console)
var options = {
file: {
level: 'info',
filename: file,
handleExceptions: true,
json: true,
maxsize: 5242880, // 5MB
maxFiles: 5,
colorize: false,
},
console: {
level: 'debug',
handleExceptions: false,
json: true,
colorize: true,
},
};
// instantiate a new Winston Logger with the settings defined above
var logger = winston.createLogger({
format: winston.format.combine(
//winston.format.label({ label: '[my-label]' }),
winston.format.timestamp({
format: 'YYYY-MM-DD HH:mm:ss'
}),
//
// The simple format outputs
// `${level}: ${message} ${[Object with everything else]}`
//
winston.format.simple(),
//
// Alternatively you could use this custom printf format if you
// want to control where the timestamp comes in your final message.
// Try replacing `format.simple()` above with this:
//
winston.format.printf(info => `${info.timestamp} ${info.level}: ${info.message}`)
),
transports: [
new winston.transports.File(options.file),
new winston.transports.Console(options.console)
],
exceptionHandlers: [
new winston.transports.File({ filename: process.env.APP_TOOL_SET_API_PROJECT+"/logs/exceptions.log" }),
],
exitOnError: false, // do not exit on handled exceptions
});
// create a stream object with a 'write' function that will be used by `morgan`
logger.stream = {
write: function(message, encoding) {
// use the 'info' log level so the output will be picked up by both transports (file and console)
logger.info(message);
},
};
module.exports = logger;
To add a bit more of information, if I wrap a thrown exception with setTimer it then is logged correctly.
setTimeout(() => {
throw new Error('hello world');
}, 250);
But this only would work for user thrown exceptions and feels really ugly to have to wrap each throw with setTimeout.
Is there any solution for this?
Well, just after posting the answer, my mental background thread spit the possible problem.
Obviously I was throwing the exception (just testing) too early and I wasn't given winston enough time to init properly.
This is why waiting with the setTimer was logging correctly.

Resources