Nodejs Winston Emit to socket - node.js

I have implemented Winston to log my nodejs app.
Currently I am writing the log to log files using the "DailyRotateFile" transport of Winston, and outputting the log data to the console.
I want to add an additional transport that emits the log message to a socket so that I can output it to the web interface I have created using Express, through socket.io.
This is the current way I have set up Winston:
var logger = new winston.Logger({
transports: [
new winston.transports.DailyRotateFile({
level: 'info',
filename: './Logs/server',
datePattern: '.yyyy-MM-dd.log',
handleExceptions: true,
json: true,
maxsize: 5242880, // 5MB
maxFiles: 5,
colorize: false
}),
new winston.transports.Console({
level: 'debug',
handleExceptions: true,
json: false,
colorize: true
})
],
exitOnError: false
})
This is the code the I currently use to emit the log message:
var logStream = logger.stream({
start: -1
})
logStream.on('log', function(log){
console.log('LOG STREAM')
//console.log(log)
io.emit('logger', log);
})
This however continuously outputs "LOG STREAM" to the nodejs console when I start the server, and doesn't emit anything when I use the logger.info command.
EDIT
So it looks like the reason the it continues to output "LOG STREAM" is because it is sending all the messages that have been saved in the log file, even though I set "start -1"
I would really appreciate any suggestions as how to correctly achieve this. TIA!

This is how I managed to achieve sending Winston logs to the browser
I added the following transport:
new winston.transports.Webhook({
host: '192.168.51.242',
port: 8102,
}),
I then created a TCP port to receive the log messages:
http.createServer(function(req, res){
var logMsg = '';
req.on('data', function(data){
logMsg += data.toString()
})
req.on('end', function(data){
io.emit('logger', logMsg)
})
}).listen(8102)
every time I receive data to the port, I emit it using Socket.IO to my Angualr interface

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.

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.

Logging only the message using winston

I am using winston logger in my node app and i want to log my custom message to the log file.
var logger = new winston.Logger({
transports: [
new winston.transports.File({
level: 'info',
filename: '/tmp/test.log',
handleExceptions: true,
json: true,
maxsize: 20971520 //20MB
})
],
exitOnError: false
});
I only need to log message in the log, i dont need to log level and timestamp in the log file. Current logged sample is as below.
{"level":"info","message":"sample entry to log","timestamp":"2015-12-11T09:43:50.507Z"}
My intention is to get entry in the log file as below
sample entry to log
How to achieve this?
Winston 3:
import winston from 'winston';
const logger = winston.createLogger({
level: 'info',
transports: [
new winston.transports.Console({
format: winston.format.printf(log => log.message),
})
]
});
logger.info('eat my shorts');
On Winston 2.x
If you want to print only the message, you can achieve this by setting:
json: false,
timestamp: false,
showLevel: false
Full Logger example:
let logger = new Logger({
transports: [
new (transports.File)({
name: 'info-file',
filename: 'filelog-info.log',
json: false,
timestamp: false,
showLevel: false
})
],
exitOnError: false
});
logger.log('info', 'eat my shorts');
gives in filelog-info.log: eat my shorts
FYI: filters and rewriters didn't help here
If anyone, as did I, finds this question in hopes of making express-winston log only message (to the console in my case)
Here is what I did:
const winston = require('winston');
const expressWinston = require('express-winston');
const { MESSAGE } = require('triple-beam');
expressWinston.logger({
transports: [
new winston.transports.Console({
format: simpleformatter()
})
]
});
const simpleformatter = winston.format(info => {
info[MESSAGE] = info.message;
return info;
});
Yep got the one i was looking for
https://github.com/winstonjs/winston#filters-and-rewriters
https://github.com/winstonjs/winston#custom-log-format

Multiple file transports in using Winston

I am attempting to use multiple transports in Winston. This is working...sorta. I have a transport set up for the app's audit log, with a custom level of 'audit', and a transport for 'info'.
var winston_mongo_options = {
level: 'audit', // level that should be logged
safe: true, //makes sure writes happen before firing log event
db: config.db.db, // db in which to write logs
host: config.db.host,
port: config.db.port,
collection: config.db.audit_collection // collection we want logging to occur
};
if (config.db.user) {
winston_mongo_options.username = config.db.user;
winston_mongo_options.password = config.db.pass;
}
var custom_levels = winston.config.syslog.levels;
custom_levels.audit = 8;
var logger = new (winston.Logger)({
levels: custom_levels,
transports : [
new (winston.transports.MongoDB)(winston_mongo_options),
new (winston.transports.File)({
level: 'info',
silent: false,
colorize: true,
timestamp: true,
filename: config.logs.debug,
maxsize: 500000,
maxFiles: 5,
json: true
})
],
exceptionHandlers: [
new (winston.transports.File)({
silent: false,
colorize: false,
timestamp: true,
filename: config.logs.exception,
maxsize: 500000,
maxFiles: 5,
json: true
})
]
});
module.exports.logger = logger;
Obviously, I'm requiring this file where/when I want to do any logging. The problem comes in when wanting to send certain information separately to the logs.
logger.audit('Server Started - to DB');
logger.info('Server Started - to info');
These two lines should write to separate logs. The first line gets properly written to the database AND to the info log file. What am I doing wrong?
SOLVED: The problem was how I am defining the levels. I didn't realize that the way Winston logging works, is a log will receive everything >= the level defined. So my 'info' transport being a level 0 was receiving messages sent to the 'audit' transport, which was a level 8. I set 'audit' to level 0, and it stopped showing up in the 'info' log. I then found a better solution by creation a whole new logger just for the audit database log.

Winston: how to rotate logs

How can I rotate logs when using Winston to handle logging for node.js. That is, how can I create a new file for each day the app runs?
var logger = new (winston.Logger)({
transports: [
new (winston.transports.Console)(),
new (winston.transports.File)({ filename: '2012-07-09.log' })
]
});
logger.log('info', 'Test Log Message', { anything: 'This is metadata' });
winston author and maintainer here.
Logging to a new file everyday is currently an open feature request: https://github.com/flatiron/winston/issues/10. Would love to see someone implement it.
That said, there are other options:
The File transport accepts a maxsize option which will rotate the logfile when it exceeds a certain size in bytes.
There is also an open pull-request with a new transport I haven't had a chance to really dig into "fileRotate", which it seems does this kind of date-based rotation: https://github.com/flatiron/winston/pull/120/files
The feature is present and we are using it in production, winston.transports.DailyRotateFile:
var timeFormatFn = function() {
'use strict';
return moment().format(cfg.timeFormat);
};
var logger = new(winston.Logger)({
exitOnError: false,
transports: [
new(winston.transports.DailyRotateFile)({
filename: cfg.appLogName,
dirname: __dirname + '/../' + cfg.logsDirectory,
datePattern: cfg.rollingDatePattern,
timestamp: timeFormatFn
}),
new(winston.transports.Console)({
colorize: true,
timestamp: timeFormatFn
})
]
});
You can use the following code to rotate the log files daily:
var winston = require('winston');
require('winston-daily-rotate-file');
var transport = new (winston.transports.DailyRotateFile)({
filename: './log',
datePattern: 'yyyy-MM-dd.',
prepend: true,
level: 'info'
});
var logger = new (winston.createLogger)({
transports: [
transport
]
});
logger.info('Hello World!');
According to the author of winston-filerotatedate it is a:
File transport for winston that allows the log files to be rotated depending on size and time.
The File transport accepts a filename via the 'filename' option and uses that file as the primary logging target.
Should the file grow past 'maxsize' bytes then the current log file is renamed and a new primary log tile is created.
The name of the renamed log file is formated as such 'basenameYYYYMMDD[a-z].bak'.
Available options are:
level: Level of messages that this transport should log.
silent: Boolean flag indicating whether to suppress output.
timestamp: Boolean flag indicating if we should prepend output with timestamps (default true). If function is specified, its return value will be used instead of timestamps.
filename: The filename of the logfile to write output to.
dirname: The folder the logfile will be created in.
maxsize: Max size in bytes of the logfile, if the size is exceeded then a new file is created.
json: If true, messages will be logged as JSON (default true).
Other people have already given good answers for this problem. But for those people looking for an example of how to do this in more modern JavaScript syntax(ES6 and beyond) using winston-daily-rotate-file, the following code should help:
const { createLogger, transports } = require('winston');
require('winston-daily-rotate-file');
const logger = createLogger({
level: 'info',
transports: [
new transports.DailyRotateFile({
filename: 'info-%DATE%.log',
datePattern: 'YYYY-MM-DD',
zippedArchive: true,
level: 'info'
})
]
});
logger.log('info', 'Test Log Message', { anything: 'This is metadata' });
As of Dec 18, 2012, this feature is now available in Winston (see https://github.com/flatiron/winston/pull/205)

Resources