Nodejs winston session id logging - node.js

I'm using winston with node & express & can't find a suitable way to simply augment all log entries with express's session.id (via an extensive google & SO search) - any suggestions?

For Express request logs you could do:
var morgan = require('morgan'),
winston = require('winston');
// Define a new format token
morgan.token('id', function(req, res) {
return req.session ? req.session.id : 'N/A';
});
// Create a stream using Winston for logging
var winstonStream = {
write: function(msg, encoding) {
winston.info(msg);
}
};
// Create a logger middleware that uses the new token in format
// and writes to Winston logging stream
var expressLogger = morgan(morgan.combined + ' - :id', { stream: winstonStream });
app.use(expressLogger);
Don't know how you could achieve the same for every winston[level]() call as you do need to have the request object at hand (apart from passing the object around to every bit of code).

Thanks for your answer vesse, I need it slightly more general than that though - ie. wherever I call winston, not just morgan request logging. I've overridden winston.log so I can call log.info(req, message), or log.warn etc and the session id is passed to the meta info from the req. I'm also using cookie-session which doesnt create the session id for me, so my solution is:
var CustomLogger = function(options){
CustomLogger.super_.apply(this, arguments);
}
util.inherits(CustomLogger, winston.Logger);
CustomLogger.prototype.log = function () {
var args = Array.prototype.slice.call(arguments);
var req = args[1];
if (req.session) {
req.session.id = req.session.id || uuid.v4();
var meta = _.merge((args[3] || {}), {sessionId: req.session.id});
CustomLogger.super_.prototype.log.call(this, args[0], args[2] + ' | ', meta);
}
else {
// no req.session, assume req hasnt been passed in as 1st param
CustomLogger.super_.prototype.log.apply(this, arguments);
}
}
var logger = new CustomLogger({
transports: [ ... ],
exitOnError: false
});

Related

Error: No responses defined for platform: null when listing users google calendar events

I'm trying to implement a reminders dialogflow agent in node js that reminds the user on his google calendar upcoming events. however I'm getting an No responses defined for platform: null error when calling the intent for listing the upcoming events.
This is my code:
const express = require('express');
const google = require('googleapis').google;
const jwt = require('jsonwebtoken');
const dfff = require('dialogflow-fulfillment')
const {googlec} = require('googleapis');
// Google's OAuth2 client
const OAuth2 = google.auth.OAuth2;
// Including our config file
const CONFIG = require('./config');
// Creating our express application
const app = express();
// Allowing ourselves to use cookies
const cookieParser = require('cookie-parser');
app.use(cookieParser());
// Setting up EJS Views
app.set('view engine', 'ejs');
app.set('views', __dirname);
console.log(dfff)
app.get('/', function (req, res) {
// Create an OAuth2 client object from the credentials in our config file
const oauth2Client = new OAuth2(CONFIG.oauth2Credentials.client_id, CONFIG.oauth2Credentials.client_secret, CONFIG.oauth2Credentials.redirect_uris[0]);
// Obtain the google login link to which we'll send our users to give us access
const loginLink = oauth2Client.generateAuthUrl({
access_type: 'offline', // Indicates that we need to be able to access data continously without the user constantly giving us consent
scope: CONFIG.oauth2Credentials.scopes // Using the access scopes from our config file
});
return res.render("index", { loginLink: loginLink });
});
app.get('/auth_callback', function (req, res) {
// Create an OAuth2 client object from the credentials in our config file
const oauth2Client = new OAuth2(CONFIG.oauth2Credentials.client_id, CONFIG.oauth2Credentials.client_secret, CONFIG.oauth2Credentials.redirect_uris[0]);
if (req.query.error) {
// The user did not give us permission.
return res.redirect('/error');
} else {
oauth2Client.getToken(req.query.code, function(err, token) {
if (err)
return res.redirect('/');
// Store the credentials given by google into a jsonwebtoken in a cookie called 'jwt'
res.cookie('jwt', jwt.sign(token, CONFIG.JWTsecret));
return res.redirect('/');
});
}
});
app.post('/', express.json(),(req,res)=>{
//if (!req.cookies.jwt) {
// We haven't logged in
//return res.redirect('/');
//}
const oauth2Client = new OAuth2(CONFIG.oauth2Credentials.client_id, CONFIG.oauth2Credentials.client_secret, CONFIG.oauth2Credentials.redirect_uris[0]);
const calendar = google.calendar({version: 'v3' , auth:oauth2Client});
const agent = new dfff.WebhookClient({
request : req,
response : res
})
function welcome(agent){
agent.add("Hi")
}
function listEvents(agent){
calendar.events.list({
'calendarId': 'primary',
'auth':oauth2Client,
'timeMin': (new Date()).toISOString(),
'showDeleted': false,
'singleEvents': true,
'maxResults': 10,
'singleEvents': true,
'orderBy': 'startTime'
}).then((err,response)=> {
let events = response.result.items;
if (events.length > 0) {
for (let i = 0; i < events.length; i++) {
var event = events[i];
var when = event.start.dateTime;
if (!when) {
when = event.start.date;
}
return agent.add('Be ready for '+ event.summary + ' event '+ 'at ' + when )
}}
else {
return agent.add('You dont have any upcoming events.');
}
});
}
let intenMap = new Map()
intenMap.set('Default_Welcome_Intent',welcome)
intenMap.set('Remind_me',listEvents)
agent.handleRequest(intenMap)
});
// Listen on the port defined in the config file
app.listen(CONFIG.port, function () {
console.log(`Listening on port ${CONFIG.port}`);
});
Whenever listEvents function is processed I'm getting (Error: No responses defined for platform null) any idea why?
The issue is that listEvents() does an asynchronous operation that returns a Promise (the call to calendar.events.list()), which you handle through the .then() block, but you don't have a way for the agent.handleRequest() function to know this and to wait for the Promise to complete. To do this, you need to return a Promise.
Fortunately, in your case, the solution is straightforward, since the call and then() chain return a Promise, you can just return it. This would be done by adding the return keyword before calendar.events.list(). It might look something like this:
function listEvents(agent){
return calendar.events.list({
// Parameters go here
}).then((err,response)=> {
// Code to handle response and return a message go here
});
}

Express logging color format

Newbie here.
In express 3.2.6 I noticed that when you set the logging to app.use(express.logger('dev')) the format of the log in stdout is greyed out and easy on the eyes (dark mode?), like so:
However, when using express 4.0 and using morgan for logging with the following in app.js
var morgan = require('morgan');
app.use(morgan('dev'));
The logging in terminal turns out like this
Anyway to get the "dark mode" theme for logging using express 4.0 and morgan? Or is this just available in express 3.0?
Looks like this can be modified to accomplish this? https://github.com/expressjs/morgan/blob/master/index.js#L183
Well You can customize your logger colors by making a custom logger try this
//logger.js
require('colors');
var _ = require('lodash');
var config = require('../config/config');
// create a noop (no operation) function for when loggin is disabled
var noop = function () {
};
// check if loggin is enabled in the config
// if it is, then use console.log
// if not then noop
var consoleLog = config.logging ? console.log.bind(console) : noop;
var logger = {
log: function () {
var tag = '[ ✨ LOG ✨ ]'.green;
// arguments is an array like object with all the passed
// in arguments to this function
var args = _.toArray(arguments)
.map(function (arg) {
if (typeof arg === 'object') {
// turn the object to a string so we
// can log all the properties and color it
var string = JSON.stringify(arg, null, 2);
return tag + ' ' + string.cyan;
} else {
return tag + ' ' + arg.cyan;
}
});
// call either console.log or noop here
// with the console object as the context
// and the new colored args :)
consoleLog.apply(console, args);
},
error: function () {
var args = _.toArray(arguments)
.map(function (arg) {
arg = arg.stack || arg;
var name = arg.name || '[ ❌ ERROR ❌ ]';
var log = name.yellow + ' ' + arg.red;
return log;
});
consoleLog.apply(console, args);
}
};
module.exports = logger;
usage
//controller
var logger = require('./relative/path/to/logger');
logger.log('some thing');
logger.error('some thing');
// you may use custom colors and create custom functions like warning() etc
hope it helps ;)

express logging response body

The title should be pretty self explanetory.
For debugging purposes, I would like express to print the response code and body for every request serviced. Printing the response code is easy enough, but printing the response body is trickier, since it seems the response body is not readily available as a property.
The following does NOT work:
var express = require('express');
var app = express();
// define custom logging format
express.logger.format('detailed', function (token, req, res) {
return req.method + ': ' + req.path + ' -> ' + res.statusCode + ': ' + res.body + '\n';
});
// register logging middleware and use custom logging format
app.use(express.logger('detailed'));
// setup routes
app.get(..... omitted ...);
// start server
app.listen(8080);
Of course, I could easily print the responses at the client who emitted the request, but I would prefer doing at the server side too.
PS: If it helps, all my responses are json, but hopefully there is a solution that works with general responses.
Not sure if it's the simplest solution, but you can write a middleware to intercept data written to the response. Make sure you disable app.compress().
function logResponseBody(req, res, next) {
var oldWrite = res.write,
oldEnd = res.end;
var chunks = [];
res.write = function (chunk) {
chunks.push(chunk);
return oldWrite.apply(res, arguments);
};
res.end = function (chunk) {
if (chunk)
chunks.push(chunk);
var body = Buffer.concat(chunks).toString('utf8');
console.log(req.path, body);
oldEnd.apply(res, arguments);
};
next();
}
app.use(logResponseBody);
I ran into an issue using the approach suggested by Laurent. Sometimes chunk is a string, and therefore causes problems in the call to Buffer.concat(). Anyways, I found a slight modification fixed things:
function logResponseBody(req, res, next) {
var oldWrite = res.write,
oldEnd = res.end;
var chunks = [];
res.write = function (chunk) {
chunks.push(new Buffer(chunk));
oldWrite.apply(res, arguments);
};
res.end = function (chunk) {
if (chunk)
chunks.push(new Buffer(chunk));
var body = Buffer.concat(chunks).toString('utf8');
console.log(req.path, body);
oldEnd.apply(res, arguments);
};
next();
}
app.use(logResponseBody);
The above accepted code has issues with ES6.
Use the below code
function logReqRes(req, res, next) {
const oldWrite = res.write;
const oldEnd = res.end;
const chunks = [];
res.write = (...restArgs) => {
chunks.push(Buffer.from(restArgs[0]));
oldWrite.apply(res, restArgs);
};
res.end = (...restArgs) => {
if (restArgs[0]) {
chunks.push(Buffer.from(restArgs[0]));
}
const body = Buffer.concat(chunks).toString('utf8');
console.log({
time: new Date().toUTCString(),
fromIP: req.headers['x-forwarded-for'] ||
req.connection.remoteAddress,
method: req.method,
originalUri: req.originalUrl,
uri: req.url,
requestData: req.body,
responseData: body,
referer: req.headers.referer || '',
ua: req.headers['user-agent']
});
// console.log(body);
oldEnd.apply(res, restArgs);
};
next();
}
module.exports = logReqRes;
You can use express-winston and configure using:
expressWinston.requestWhitelist.push('body');
expressWinston.responseWhitelist.push('body');
Example in coffeescript:
expressWinston.requestWhitelist.push('body')
expressWinston.responseWhitelist.push('body')
app.use(expressWinston.logger({
transports: [
new winston.transports.Console({
json: true,
colorize: true
})
],
meta: true, // optional: control whether you want to log the meta data about the request (default to true)
msg: "HTTP {{req.method}} {{req.url}}", // optional: customize the default logging message. E.g. "{{res.statusCode}} {{req.method}} {{res.responseTime}}ms {{req.url}}"
expressFormat: true, // Use the default Express/morgan request formatting, with the same colors. Enabling this will override any msg and colorStatus if true. Will only output colors on transports with colorize set to true
colorStatus: true, // Color the status code, using the Express/morgan color palette (default green, 3XX cyan, 4XX yellow, 5XX red). Will not be recognized if expressFormat is true
ignoreRoute: function (req, res) { return false; } // optional: allows to skip some log messages based on request and/or response
}));
This solution might not be heavyweight enough for some use cases, but I think it's the simplest. It's also typescript compatible. If you only want logging for JSON responses, all you have to do is substitute the send method with the json method in the code below. Note, I took inspiration from Jonathan Turnock's answer, but made it simpler.
app.use((req, res, next) => {
let send = res.send;
res.send = c => {
console.log(`Code: ${res.statusCode}`);
console.log("Body: ", c);
res.send = send;
return res.send(c);
}
next();
});
I found the simplest solution to this problem was to add a body property to the res object when sending the response, which can later be accessed by the logger. I add this to my own namespace that I maintain on the req and res objects to avoid naming collisions. e.g.
res[MY_NAMESPACE].body = ...
I have a utility method that formats all responses to my standardized API/JSON response, so adding this one liner there exposed the response body when the logging gets triggered by onFinished event of res.
Most of the suggestions seemed a little sledgehammer, Spent some time with this issue tonight and wrote up my findings after digging into a few libs to help make something bespoke.
//app.js
...
app.use(requestLoggerMiddleware({ logger: console.log }));
app.get(["/", "/api/health"], (req, res) => {
res.send({ message: "OK", uptime: process.uptime() });
...
});
// middleware.js
/**
* Interceptor function used to monkey patch the res.send until it is invoked
* at which point it intercepts the invokation, executes is logic such as res.contentBody = content
* then restores the original send function and invokes that to finalize the req/res chain.
*
* #param res Original Response Object
* #param send Original UNMODIFIED res.send function
* #return A patched res.send which takes the send content, binds it to contentBody on
* the res and then calls the original res.send after restoring it
*/
const resDotSendInterceptor = (res, send) => (content) => {
res.contentBody = content;
res.send = send;
res.send(content);
};
/**
* Middleware which takes an initial configuration and returns a middleware which will call the
* given logger with the request and response content.
*
* #param logger Logger function to pass the message to
* #return Middleware to perform the logging
*/
const requestLoggerMiddleware = ({ logger }) => (req, res, next) => {
logger("RECV <<<", req.method, req.url, req.hostname);
res.send = resDotSendInterceptor(res, res.send);
res.on("finish", () => {
logger("SEND >>>", res.contentBody);
});
next();
};
module.exports = { requestLoggerMiddleware };
Full working example and article in the git repo
https://github.com/JonathanTurnock/ReqResLoggingExample
I actually made this nifty little npm to solve this exact problem, hope you like it!
https://www.npmjs.com/package/morgan-body
May be this would help someone who is looking to get the response logged
So, we use the middleware to intercept the request just before being served to the client. Then if we are using res.send method to send the data, override the method in the middleware and make sure to console log the body. If you are planning to use res.send alone then this should work fine, but incase if you use res.end or res.sendFile, then overwrite those methods and log only the required things (obviously logging the entire octet stream of file should never be logged for perfomance purposes.
Here I use pino as the logger. Created it as singleton service.
// LoggingResponseRouter.js
var loggingResponseRouter = require('express').Router();
var loggingService = require('./../service/loggingService');
var appMethodInstance = require('./../constants/appMethod');
var path = require('path');
var fs = require('fs');
var timeZone = require('moment-timezone');
var pino = require('pino')();
loggingResponseRouter.use((req, res, next) => {
// set the fileName it needs to log
appMethodInstance.setFileName(__filename.substring(__filename.lastIndexOf(path.sep) + 1, __filename.length - 3));
//loggingService.debugAndInfolog().info('logging response body', appMethodInstance.getFileName());
let send = res.send;
res.send = function(body){
loggingService.debugAndInfolog().info('Response body before sending: ', body);
send.call(this, body);
}
next();
});
module.exports = loggingResponseRouter;
Main file - Main.js
const corsRouter = require('./app/modules/shared/router/corsRouter');
const logRequestRouter = require('./app/modules/shared/router/loggingRequestRouter');
const loggingResponseRouter = require('./app/modules/shared/router/loggingResponseRouter');
const express = require('express');
var path = require('path');
const app = express();
// define bodyparser middleware
const bodyParser = require('body-parser');
const port = process.env.PORT || 3000;
// Now use the middleware prior to any others
app.use(bodyParser.json());
// use this to read url form encoded values as wwell
app.use(bodyParser.urlencoded({extended:true}));
console.log('before calling cors router in main js');
app.use(corsRouter);
app.use(logRequestRouter);
app.use(loggingResponseRouter);
app.get('/api', (req, res) => {
console.log('inside api call');
res.send('aapi');
});
app.listen(port, () => {
console.log('starting the server');
});
And this is the loggingService - loggingService.js
var pino = require('pino');
var os = require('os');
var appMethodInstance = require('./../constants/appMethod');
var pinoPretty = require('pino-pretty');
var moment = require('moment');
var timeZone = require('moment-timezone');
class Logger{
constructor(){
this.appName = 'Feedback-backend';
this.filenameval = '';
}
getFileName(){
console.log('inside get filename');
console.log(appMethodInstance.getFileName());
if(appMethodInstance.getFileName() === null || appMethodInstance.getFileName() === undefined){
this.filenameval = 'bootstrapping...'
}else {
this.filenameval = appMethodInstance.getFileName();
}
console.log('end');
return this.filenameval;
}
debugAndInfolog(){
return pino({
name: 'feedback-backend',
base: {
pid: process.pid,
fileName: this.getFileName(),
moduleName: 'modulename',
timestamp: timeZone().tz('America/New_York').format('YYYY-MM-DD HH:mm:ss.ms'),
hostName: os.hostname()
},
level: 'info',
timestamp: timeZone().tz('America/New_York').format('YYYY-MM-DD HH:mm:ss.ms'),
messageKey: 'logMessage',
prettyPrint: {
messageKey: 'logMessage'
}
});
}
errorAndFatalLog(){
return pino({
name: 'feedback-backend',
base: {
pid: process.pid,
fileName: this.getFileName(),
moduleName: 'modulename',
timestamp: timeZone().tz('America/New_York').format('YYYY-MM-DD HH:mm:ss.ms'),
hostName: os.hostname()
},
level: 'error',
timestamp: timeZone().tz('America/New_York').format('YYYY-MM-DD HH:mm:ss.ms'),
prettyPrint: {
messageKey: 'FeedbackApp'
}
});
}
}
module.exports = new Logger();
Typescript solution based on Laurent's answer:
import { NextFunction, Request, Response } from 'express-serve-static-core';
//...
app.use(logResponseBody);
function logResponseBody(req: Request, res: Response, next: NextFunction | undefined) {
const [oldWrite, oldEnd] = [res.write, res.end];
const chunks: Buffer[] = [];
(res.write as unknown) = function(chunk) {
chunks.push(Buffer.from(chunk));
(oldWrite as Function).apply(res, arguments);
};
res.end = function(chunk) {
if (chunk) {
chunks.push(Buffer.from(chunk));
}
const body = Buffer.concat(chunks).toString('utf8');
console.log(new Date(), ` ↪ [${res.statusCode}]: ${body}`);
(oldEnd as Function).apply(res, arguments);
};
if (next) {
next();
}
}
I have similar need to this question.
Based on accepted answer, I modify it with proxy and trace response body only when it's json.
const traceMiddleware = (req, res, next) => {
const buffers = []
const proxyHandler = {
apply(target, thisArg, argumentsList) {
const contentType = res.getHeader('content-type')
if (
typeof contentType === 'string' && contentType.includes('json') && argumentsList[0]
) {
buffers.push(argumentsList[0])
}
return target.call(thisArg, ...argumentsList)
}
}
res.write = new Proxy(res.write, proxyHandler)
res.end = new Proxy(res.end, proxyHandler)
res.on('finish', () => {
// tracing logic inside
trace(req, res, Buffer.concat(buffers).toString('utf8'))
})
next()
}

How to add prefix to socket.io logs redirected to winston?

In my node.js application I successfully redirect log messages produced by a socket.io library to a winston library:
var express = require('express')
, winston = require('winston')
, http = require('http');
var logger = new (winston.Logger)({
transports: [
// ... configuring transports ...
]
});
var app = express();
var server = http.createServer(app);
var io = require('socket.io').listen(server, {'logger': logger});
Now I would want to add a prefix (something like "socket.io: ") to all these redirected messages for distinguishing them from log messages produced by other parts of the application. Is there a way how to achieve this?
Add label in logger transports.
var logger = new (winston.Logger)({
transports: [
new (winston.transports.Console)({
json : false,
timestamp : true,
label: "socket.io:"
})
]
});
Log messages will look like this -
2013-08-30T08:26:52.703Z - info: [socket.io:] socket.io started
2013-08-30T08:26:52.705Z - info: [socket.io:] hello
Check here more logging options with winston - https://github.com/flatiron/winston
I solved it by adding a following function
var winston = require('winston');
// Add prefix function
winston.prefix = function (filename)
{
var label = '[' + path.parse(filename).name + ']';
var override = function (lvl) {
return function () {
var args = [].slice.call(arguments);
if (args[0]) {
if (typeof(args[0]) === 'string')
args[0] = label + ' ' + args[0];
else
args.unshift(label);
}
winston[lvl].apply(null, args);
};
};
var log = { };
for (var lvl in winston.levels) {
log[lvl] = override(lvl);
}
return log;
}
And then in each module
var log = require('winston').prefix(__filename);
log.debug('hello');
or
var log = require('winston').prefix('socket_io');
log.debug('hello');
This isn't Winston specific but a generalized JS solution to prefixing and middleware...
var log = console.log;
function x(){ // optionally pass in post/pre fix here
// caution do not put (var log = console.log()) here, the arguments will build up [ arg1 ] -> [ [arg1] , arg2 ] -> [ [ [ arg1 ] , arg2] , arg3 ]
console.log = function () {
var args = Array.from(arguments);
args.push('post fix');
log.apply(console, args);
}
}
new x()
console.log(1)
new x()
console.log(2)
OUTPUTS:
1 post fix
2 post fix
OR better yet...
const log = console.log;
export default function middleWare(event) { // optionally pass in post/pre fix here
console.log = (...args) => {
log(...args, event.id);
}
}

Configure Node.js to log to a file instead of the console

Can I configure console.log so that the logs are written on a file instead of being printed in the console?
You could also just overload the default console.log function:
var fs = require('fs');
var util = require('util');
var log_file = fs.createWriteStream(__dirname + '/debug.log', {flags : 'w'});
var log_stdout = process.stdout;
console.log = function(d) { //
log_file.write(util.format(d) + '\n');
log_stdout.write(util.format(d) + '\n');
};
Above example will log to debug.log and stdout.
Edit: See multiparameter version by Clément also on this page.
Update 2013 - This was written around Node v0.2 and v0.4; There are much better utilites now around logging. I highly recommend Winston
Update Late 2013 - We still use winston, but now with a logger library to wrap the functionality around logging of custom objects and formatting. Here is a sample of our logger.js https://gist.github.com/rtgibbons/7354879
Should be as simple as this.
var access = fs.createWriteStream(dir + '/node.access.log', { flags: 'a' })
, error = fs.createWriteStream(dir + '/node.error.log', { flags: 'a' });
// redirect stdout / stderr
proc.stdout.pipe(access);
proc.stderr.pipe(error);
If you are looking for something in production winston is probably the best choice.
If you just want to do dev stuff quickly, output directly to a file (I think this works only for *nix systems):
nohup node simple-server.js > output.log &
I often use many arguments to console.log() and console.error(), so my solution would be:
var fs = require('fs');
var util = require('util');
var logFile = fs.createWriteStream('log.txt', { flags: 'a' });
// Or 'w' to truncate the file every time the process starts.
var logStdout = process.stdout;
console.log = function () {
logFile.write(util.format.apply(null, arguments) + '\n');
logStdout.write(util.format.apply(null, arguments) + '\n');
}
console.error = console.log;
Winston is a very-popular npm-module used for logging.
Here is a how-to.
Install winston in your project as:
npm install winston --save
Here's a configuration ready to use out-of-box that I use frequently in my projects as logger.js under utils.
/**
* Configurations of logger.
*/
const winston = require('winston');
const winstonRotator = require('winston-daily-rotate-file');
const consoleConfig = [
new winston.transports.Console({
'colorize': true
})
];
const createLogger = new winston.Logger({
'transports': consoleConfig
});
const successLogger = createLogger;
successLogger.add(winstonRotator, {
'name': 'access-file',
'level': 'info',
'filename': './logs/access.log',
'json': false,
'datePattern': 'yyyy-MM-dd-',
'prepend': true
});
const errorLogger = createLogger;
errorLogger.add(winstonRotator, {
'name': 'error-file',
'level': 'error',
'filename': './logs/error.log',
'json': false,
'datePattern': 'yyyy-MM-dd-',
'prepend': true
});
module.exports = {
'successlog': successLogger,
'errorlog': errorLogger
};
And then simply import wherever required as this:
const errorLog = require('../util/logger').errorlog;
const successlog = require('../util/logger').successlog;
Then you can log the success as:
successlog.info(`Success Message and variables: ${variable}`);
and Errors as:
errorlog.error(`Error Message : ${error}`);
It also logs all the success-logs and error-logs in a file under logs directory date-wise as you can see here.
const fs = require("fs");
const {keys} = Object;
const {Console} = console;
/**
* Redirect console to a file. Call without path or with false-y
* value to restore original behavior.
* #param {string} [path]
*/
function file(path) {
const con = path ? new Console(fs.createWriteStream(path)) : null;
keys(Console.prototype).forEach(key => {
if (path) {
this[key] = (...args) => con[key](...args);
} else {
delete this[key];
}
});
};
// patch global console object and export
module.exports = console.file = file;
To use it, do something like:
require("./console-file");
console.file("/path/to.log");
console.log("write to file!");
console.error("also write to file!");
console.file(); // go back to writing to stdout
For simple cases, we could redirect the Standard Out (STDOUT) and Standard Error (STDERR) streams directly to a file(say, test.log) using '>' and '2>&1'
Example:
// test.js
(function() {
// Below outputs are sent to Standard Out (STDOUT) stream
console.log("Hello Log");
console.info("Hello Info");
// Below outputs are sent to Standard Error (STDERR) stream
console.error("Hello Error");
console.warn("Hello Warning");
})();
node test.js > test.log 2>&1
As per the POSIX standard, 'input', 'output' and 'error' streams are identified by the positive integer file descriptors (0, 1, 2). i.e., stdin is 0, stdout is 1, and stderr is 2.
Step 1: '2>&1' will redirect from 2 (stderr) to 1 (stdout)
Step 2: '>' will redirect from 1 (stdout) to file (test.log)
If this is for an application, you're probably better off using a logging module. It'll give you more flexibility. Some suggestions.
winston https://github.com/winstonjs/winston
log4js https://github.com/nomiddlename/log4js-node
Straight from nodejs's API docs on Console
const output = fs.createWriteStream('./stdout.log');
const errorOutput = fs.createWriteStream('./stderr.log');
// custom simple logger
const logger = new Console(output, errorOutput);
// use it like console
const count = 5;
logger.log('count: %d', count);
// in stdout.log: count 5
Another solution not mentioned yet is by hooking the Writable streams in process.stdout and process.stderr. This way you don't need to override all the console functions that output to stdout and stderr. This implementation redirects both stdout and stderr to a log file:
var log_file = require('fs').createWriteStream(__dirname + '/log.txt', {flags : 'w'})
function hook_stream(stream, callback) {
var old_write = stream.write
stream.write = (function(write) {
return function(string, encoding, fd) {
write.apply(stream, arguments) // comments this line if you don't want output in the console
callback(string, encoding, fd)
}
})(stream.write)
return function() {
stream.write = old_write
}
}
console.log('a')
console.error('b')
var unhook_stdout = hook_stream(process.stdout, function(string, encoding, fd) {
log_file.write(string, encoding)
})
var unhook_stderr = hook_stream(process.stderr, function(string, encoding, fd) {
log_file.write(string, encoding)
})
console.log('c')
console.error('d')
unhook_stdout()
unhook_stderr()
console.log('e')
console.error('f')
It should print in the console
a
b
c
d
e
f
and in the log file:
c
d
For more info, check this gist.
Overwriting console.log is the way to go. But for it to work in required modules, you also need to export it.
module.exports = console;
To save yourself the trouble of writing log files, rotating and stuff, you might consider using a simple logger module like winston:
// Include the logger module
var winston = require('winston');
// Set up log file. (you can also define size, rotation etc.)
winston.add(winston.transports.File, { filename: 'somefile.log' });
// Overwrite some of the build-in console functions
console.error = winston.error;
console.log = winston.info;
console.info = winston.info;
console.debug = winston.debug;
console.warn = winston.warn;
module.exports = console;
If you're using linux, you can also use output redirection. Not sure about Windows.
node server.js >> file.log 2>> file.log
>> file.log to redirect stdout to the file
2>> file.log to redirect stderr to the file
others use the shorthand &>> for both stdout and stderr but it's not accepted by both my mac and ubuntu :(
extra: > overwrites, while >> appends.
By the way, regarding NodeJS loggers, I use pino + pino-pretty logger
METHOD STDOUT AND STDERR
This approach can help you (I use something similar in my projects) and works for all methods including console.log, console.warn, console.error, console.info
This method write the bytes written in stdout and stderr to file. Is better than changing console.log, console.warn, console.error, console.info methods, because output will be exact the same as this methods output
var fs= require("fs")
var os= require("os")
var HOME= os.homedir()
var stdout_r = fs.createWriteStream(HOME + '/node.stdout.log', { flags: 'a' })
var stderr_r = fs.createWriteStream(HOME + '/node.stderr.log', { flags: 'a' })
var attachToLog= function(std, std_new){
var originalwrite= std.write
std.write= function(data,enc){
try{
var d= data
if(!Buffer.isBuffer(d))
d= Buffer.from(data, (typeof enc === 'string') ? enc : "utf8")
std_new.write.apply(std_new, d)
}catch(e){}
return originalwrite.apply(std, arguments)
}
}
attachToLog(process.stdout, stdout_r)
attachToLog(process.stderr, stderr_r)
// recommended catch error on stdout_r and stderr_r
// stdout_r.on("error", yourfunction)
// stderr_r.on("error", yourfunction)
Adding to the answer above, a lit bit of an expansion to the short and efficient code overriding console.log. Minor additions: set filename with date, wrapper function, also do the original console.logging to keep the console active with the info.
Usage: in the beginning of your code, run setConsoleLogToFile([FILENAME]).
const fs = require("fs"),
util = require('util');
const getPrettyDate = ()=> new Date().toString().replace(":","-").replace(/00\s\(.*\)/, "").replace(` ${new Date().getFullYear()}`, ",").replace(/:\d\d\s/, " ");
module.exports.getPrettyDate = getPrettyDate;
module.exports.setConsoleLogToFile = (filename) => {
const log_file = fs.createWriteStream(`${__dirname}/${filename} - ${getPrettyDate()}.log`, { flags: 'w' }),
log_stdout = process.stdout;
const origConsole = console.log;
console.log = (d) => {
origConsole(d);
log_file.write(util.format(d) + '\n');
log_stdout.write(util.format(d) + '\n');
};
}
Most logger is overkill and does not support the build in console.log correctly. Hence I create console-log-to-file:
import { consoleLogToFile } from "console-log-to-file";
// or `const { consoleLogToFile } = require("console-log-to-file/dist/index.cjs.js")`
consoleLogToFile({
logFilePath: "/log/default.log",
});
// all of your console.log/warn/error/info will work as it does and save to file now.
If you are looking for a solution without modifying any code, here is a simple solution.
It requires pm2, just add it to your node modules and start you app with
pm2 start server.js
And you are done, console.logs are now automatically registered under home/.pm2/logs/server-out.log.
Improve on Andres Riofrio , to handle any number of arguments
var fs = require('fs');
var util = require('util');
var log_file = fs.createWriteStream(__dirname + '/debug.log', {flags : 'w'});
var log_stdout = process.stdout;
console.log = function(...args) {
var output = args.join(' ');
log_file.write(util.format(output) + '\r\n');
log_stdout.write(util.format(output) + '\r\n');
};
You can now use Caterpillar which is a streams based logging system, allowing you to log to it, then pipe the output off to different transforms and locations.
Outputting to a file is as easy as:
var logger = new (require('./').Logger)();
logger.pipe(require('fs').createWriteStream('./debug.log'));
logger.log('your log message');
Complete example on the Caterpillar Website
You can also have a look at this npm module:
https://www.npmjs.com/package/noogger
noogger
simple and straight forward...
For future users. #keshavDulal answer doesn't work for latest version. And I couldn't find a proper fix for the issues that are reporting in the latest version 3.3.3.
Anyway I finally fixed it after researching a bit. Here is the solution for winston version 3.3.3
Install winston and winston-daily-rotate-file
npm install winston
npm install winston-daily-rotate-file
Create a new file utils/logger.js
const winston = require('winston');
const winstonRotator = require('winston-daily-rotate-file');
var logger = new winston.createLogger({
transports: [
new (winston.transports.DailyRotateFile)({
name: 'access-file',
level: 'info',
filename: './logs/access.log',
json: false,
datePattern: 'yyyy-MM-DD',
prepend: true,
maxFiles: 10
}),
new (winston.transports.DailyRotateFile)({
name: 'error-file',
level: 'error',
filename: './logs/error.log',
json: false,
datePattern: 'yyyy-MM-DD',
prepend: true,
maxFiles: 10
})
]
});
module.exports = {
logger
};
Then in any file where you want to use logging import the module like
const logger = require('./utils/logger').logger;
Use logger like the following:
logger.info('Info service started');
logger.error('Service crashed');
if you are using forever to keep your node app running, then typing forever list will show you the path to the log file that console.log is writing too
You can use the nodejs Console constructor
const mylog = new console.Console(
fs.createWriteStream("log/logger.log"),
fs.createWriteStream("log/error.log")
);
And then you can use it just like the normal console class, eg:
mylog.log("Ok!"); // Will be written into 'log/logger.log'
mylog.error("Bad!"); // Will be written into 'log/error.log'
I took on the idea of swapping the output stream to a my stream.
const LogLater = require ('./loglater.js');
var logfile=new LogLater( 'log'+( new Date().toISOString().replace(/[^a-zA-Z0-9]/g,'-') )+'.txt' );
var PassThrough = require('stream').PassThrough;
var myout= new PassThrough();
var wasout=console._stdout;
myout.on('data',(data)=>{logfile.dateline("\r\n"+data);wasout.write(data);});
console._stdout=myout;
var myerr= new PassThrough();
var waserr=console._stderr;
myerr.on('data',(data)=>{logfile.dateline("\r\n"+data);waserr.write(data);});
console._stderr=myerr;
loglater.js:
const fs = require('fs');
function LogLater(filename, noduplicates, interval) {
this.filename = filename || "loglater.txt";
this.arr = [];
this.timeout = false;
this.interval = interval || 1000;
this.noduplicates = noduplicates || true;
this.onsavetimeout_bind = this.onsavetimeout.bind(this);
this.lasttext = "";
process.on('exit',()=>{ if(this.timeout)clearTimeout(this.timeout);this.timeout=false; this.save(); })
}
LogLater.prototype = {
_log: function _log(text) {
this.arr.push(text);
if (!this.timeout) this.timeout = setTimeout(this.onsavetimeout_bind, this.interval);
},
text: function log(text, loglastline) {
if (this.noduplicates) {
if (this.lasttext === text) return;
this.lastline = text;
}
this._log(text);
},
line: function log(text, loglastline) {
if (this.noduplicates) {
if (this.lasttext === text) return;
this.lastline = text;
}
this._log(text + '\r\n');
},
dateline: function dateline(text) {
if (this.noduplicates) {
if (this.lasttext === text) return;
this.lastline = text;
}
this._log(((new Date()).toISOString()) + '\t' + text + '\r\n');
},
onsavetimeout: function onsavetimeout() {
this.timeout = false;
this.save();
},
save: function save() { fs.appendFile(this.filename, this.arr.splice(0, this.arr.length).join(''), function(err) { if (err) console.log(err.stack) }); }
}
module.exports = LogLater;
I just build a pack to do this, hope you like it ;)
https://www.npmjs.com/package/writelog
I for myself simply took the example from winston and added the log(...) method (because winston names it info(..):
Console.js:
"use strict"
// Include the logger module
const winston = require('winston');
const logger = winston.createLogger({
level: 'info',
format: winston.format.json(),
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' })
]
});
//
// If we're not in production then log to the `console` with the format:
// `${info.level}: ${info.message} JSON.stringify({ ...rest }) `
//
if (process.env.NODE_ENV !== 'production') {
logger.add(new winston.transports.Console({
format: winston.format.simple()
}));
}
// Add log command
logger.log=logger.info;
module.exports = logger;
Then simply use in your code:
const console = require('Console')
Now you can simply use the normal log functions in your file and it will create a file AND log it to your console (while debugging/developing). Because of if (process.env.NODE_ENV !== 'production') { (in case you want it also in production)...
Create a utils/logger.js file with:
var fs = require('fs');
var util = require('util');
var log_file = fs.createWriteStream(__dirname + '/../logs/server.log', { flags: 'w' });
var log_stdout = process.stdout;
console.log = function () { //
[...arguments].forEach(element => {
log_file.write(util.format(element) + '\n');
log_stdout.write(util.format(element) + '\n');
});
};
module.exports = {
console
}
Include the logger.js file from any file where you want to console.log like:
const console = require('./utils/logger').console;
Create a logs folder and create an empty server.log file in it and run your app :)
Rudy Huynh's solution worked really well for me. I added a little bit to have it spit out files with today's date and time.
var dateNow = new Date();
var timeNow = dateNow.getHours() + '-' + dateNow.getMinutes();
var logPath = "log/" + dateNow.toDateString() + ' -' + ' Start Time - ' + timeNow + ".log"
consoleLogToFile({
logFilePath: logPath
});
It's not very elegant but this way it'll save different, easy to read, log files instead of just updating the same "default.log" file.
Based on multiparameter version by Clément, just without color codes for the text file
var fs = require('fs');
var util = require('util');
var logFile = fs.createWriteStream('log.txt', { flags: 'a' });
// Or 'w' to truncate the file every time the process starts.
var logStdout = process.stdout;
console.log = function () {
// Storing without color codes
logFile.write(util.format.apply(null,arguments).replace(/\033\[[0-9;]*m/g,"") + '\n');
// Display normally, with colors to Stdout
logStdout.write(util.format.apply(null, arguments) + '\n');
}
Note: Answering since I couldn't comment

Resources