prevent to crash node js after uncaughtExceptions - node.js

i have written one middle-ware for handling uncaughtExceptions which is working fine but after that server will crashed.
how do i prevent to crash it?
server.js:
const express = require('express');
const winston = require("winston");
const app = express();
//Logging is responsible to log and display errors
require('./startup/logging')();
//routes will contains all the routes list
require('./startup/routes')(app);
//PORT
const port = process.env.PORT || 3000;
app.listen(port,() => winston.info(`Listening on port ${port}....`));
logging.js
const express = require('express');
const winston = require('winston');
// require('express-async-errors');
module.exports = function() {
winston.handleExceptions(
new winston.transports.File({ filename: 'uncaughtExceptions.log' })
);
process.on('unhandledRejection', (ex) => {
throw ex;
});
winston.add(winston.transports.File, { filename: 'error.log' });
}

As the documentation states,
By default, winston will exit after logging an uncaughtException. If this is not the behavior you want, set exitOnError = false
const logger = winston.createLogger({ exitOnError: false });
//
// or, like this:
//
logger.exitOnError = false;
It is generally considered a bad practice to not exit after an exception because the consequences are unpredictable. If only some of the exceptions are known to be tolerable, they can be specifically handled with a predicate:
const ignoreWarnings = err => !(err instanceof WarningError);
const logger = winston.createLogger({ exitOnError: ignoreWarnings });

Related

Node, Express - Index file not calling files correctly

Hi in my express project, I have my index file where I require different files to startup my application. These require a database connection file, a file for logging stuff using winston and a file for configuring routes.
I use the require() statement within express to call these files, and when I run the application(using nodemon), I expect some messages to be logged to the terminal verifying that the files have been called, however no messages occur.
Here is my code:
index.js:
const express = require('express')
const app = express()
require('./startup/logging') ()
require('./startup/db') ()
require('./startup/routes') (app)
const port = process.env.PORT || 3000
app.listen(port, () => winston.info(`Listening on port: ${port}`))
db.js:
const mongoose = require('mongoose')
const winston = require('winston')
module.exports = function() {
mongoose.connect('mongodb://localhost/dwg', {useNewUrlParser: true, useUnifiedTopology: true})
.then(() => winston.info('Connected to MongoDB...'))
.catch(err => console.error("Error"))
}
logging.js:
const winston = require('winston');
module.exports = function() {
winston.handleExceptions(
new winston.transports.File({ filename: 'uncaughtExceptions.log' }));
process.on('unhandledRejection', (ex) => {
throw ex;
});
winston.add(winston.transports.File, { filename: 'logfile.log' });
}
routes.js:
const express = require('express');
module.exports = function(app) {
app.use(express.json())
}
No database is created when running the application. I can confirm this by looking at mongodb compass. The message that is meant to be printed by app.listen() is also not printed to the console. Does anybody know the issue? Thank you.
The problem doing it this way is your app starts before it gets a chance to do rest of work like creating db connection etc. You should start the app only when these tasks are done. something like this
const express = require('express')
const app = express()
const logging = require('./startup/logging');
const db = require('./startup/db');
const routes = require('./startup/routes');
const port = process.env.PORT || 3000
app.listen(port, async () => {
await logging();
await db();
await routes();
// assuming you have winston here.
winston.info(`Listening on port: ${port}`)
})
Mongo part is defintely async so need await. Check if routes and logging needs await or not.

stream.write is not a function when using morgan with logger

Basically i am trying to implement logger for the nodejs , using morgan and winston.
When i am trying to use morgan , throwing an error of stream.write is not a function.
Since i want get the file name , i am passing the module, from module object there is a property called filename.
Below is my code.
// Winston.js
const appRoot = require('app-root-path');
const { createLogger, format, transports } = require('winston');
const { combine, timestamp, label, printf } = format;
const path = require('path');
// Custom Format
const customFormat = printf(info => {
return `${new Date(info.timestamp)} || [${info.label}] || ${info.level}: ${info.message} `
})
// Return the last folder name in the path and the calling
// module's filename.
const getLabel = function (moduleDetails) {
if (Object.keys(moduleDetails).length > 0) {
let parts = moduleDetails.filename.split(path.sep)
return parts.pop();
}else{
return;
}
}
// define the custom settings for each transport (file, console)
var options = (moduleDetails) => ({
file: {
level: "info",
timestamp: new Date(),
filename: `${appRoot}/logs/app.log`,
handleExceptions: true,
json: true,
maxsize: 5242880,
maxFiles: 5,
colorize: false,
label: getLabel(moduleDetails)
},
console: {
level: "debug",
handleExceptions: true,
json: false,
colorize: true,
}
})
//instantiate a new Winston Logger with the settings defined above
let logger = function (moduleDetails) {
return createLogger({
format: combine(
label({label:getLabel(moduleDetails)}),
timestamp(),
customFormat
),
transports: [
new transports.File(options(moduleDetails).file)
],
exitOnError: false // do not exit on handled exceptions
})
}
// create a stream object with '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)
// }
// }
// If we're not in production then log to the `console` with the format:
// `${info.timestamp} || [${info.label}] || ${info.level}: ${info.message}`
// like in the log file
if (process.env.NODE_ENV !== 'prod') {
logger({}).add(new transports.Console(options({}).console));
}
module.exports = logger
module.exports.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)
}
}
// App.js
var createError = require('http-errors');
var express = require('express');
var path = require('path');
var cookieParser = require('cookie-parser');
var morgan = require('morgan');
var indexRouter = require('./routes/index');
var usersRouter = require('./routes/users');
var winston = require('./config/winston')(module);
var app = express();
// view engine setup
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'jade');
app.use(morgan('combined', { "stream": winston.stream}));
app.use(express.json());
app.use(express.urlencoded({ extended: false }));
app.use(cookieParser());
app.use(express.static(path.join(__dirname, 'public')));
app.use('/', indexRouter);
app.use('/users', usersRouter);
// catch 404 and forward to error handler
app.use(function(req, res, next) {
next(createError(404));
});
// error handler
app.use(function(err, req, res, next) {
// set locals, only providing error in development
res.locals.message = err.message;
res.locals.error = req.app.get('env') === 'development' ? err : {};
// add this line to include winston logging
winston.error(`${err.status || 500} || ${err.message} || ${req.originalUrl} || ${req.method} || ${req.ip}` )
// render the error page
res.status(err.status || 500);
res.render('error');
});
module.exports = app;
On App.js, try changing
app.use(morgan('combined', { "stream": winston.stream}));
to
app.use(morgan('combined', { "stream": winston.stream.write}));
This seems to work even though I don't know why.
I think you didn't export module correctly, should be:
var winston = require('./config/winston');
winston.logger(module);
instead:
var winston = require('./config/winston')(module);

prevent to crash node js after uncaughtExceptions

i have written one middle-ware for handling uncaughtExceptions which is working fine but after that server will crashed.
how do i prevent to crash it?
server.js:
const express = require('express');
const winston = require("winston");
const app = express();
//Logging is responsible to log and display errors
require('./startup/logging')();
//routes will contains all the routes list
require('./startup/routes')(app);
//PORT
const port = process.env.PORT || 3000;
app.listen(port,() => winston.info(`Listening on port ${port}....`));
logging.js
const express = require('express');
const winston = require('winston');
// require('express-async-errors');
module.exports = function() {
winston.handleExceptions(
new winston.transports.File({ filename: 'uncaughtExceptions.log' })
);
process.on('unhandledRejection', (ex) => {
throw ex;
});
winston.add(winston.transports.File, { filename: 'error.log' });
}
If you do throw ex; The program will crash, Rather you should send the crash report and message to the respective reporting mechanism that you are using. Heres a small snippet
process.on('unhandledRejection', (reason, promise) => {
console.log('Unhandled Rejection at:', reason.stack || reason)
// Recommended: send the information to sentry.io
// or whatever crash reporting service you use
})

Log every request and error messages using winston

I am able to log every request and error message into separate logfiles(request.log and uncaughtExceptions.log) but want to merge this two files into one file only called logs.log like
var logmsg = {
'Request IP',
'Method':req.method,
'URL':req.originalUrl,
'statusCode':res.statusCode,
'headers':req.headers,
'Time':new Date(),
'ErrorMessage':'Error Message if any with file name with line number and proper error message'
};
Working Code:
const express = require('express');
const winston = require('winston');
require('express-async-errors');
module.exports = function() {
winston.handleExceptions(
new winston.transports.File({ filename: 'uncaughtExceptions.log' }));
process.on('unhandledRejection', (ex) => {
throw ex;
});
winston.add(winston.transports.File, { filename: 'request.log' });
}
What I have Tried:
logging.js
const express = require('express');
const { createLogger, format, transports } = require('winston');
const { combine, timestamp, label, printf } = format;
const myFormat = printf(info => {
return (info.timestamp + " | " +
info.trace[0].file + ":" + info.trace[0].line + " | " +
info.message.split("\n")[0]);
});
module.exports = function() {
const logger = createLogger({
format: combine(timestamp(), myFormat)
});
logger.exceptions.handle(new transports.File({ filename: 'logs.log' }));
process.on('unhandledRejection', (reason, p) => {
throw p;
});
}
it displays strange error message, i have no idea how to resolve it.
Error message:
server.js
const express = require('express');
const winston = require("winston");
const app = express();
//to Log errors
require('./startup/logging')();
//routes will contains all the routes list
require('./startup/routes')(app);
//PORT
const port = process.env.PORT || 3000;
app.listen(port,() => winston.info(`Listening on port ${port}....`));
routes.js
const express = require('express');
const reqres = require('../middlewares/reqreslog');
module.exports = function(app){
//Every Request Response Logging Middleware
app.use(reqres);
app.get('/', async (req, res) => {
res.json("testing"+a);
});
});
reqreslog.js
var winston = require('winston');
module.exports = function(req, res, next) {
var logmsg = {
'Request IP':req.ip,
'Method':req.method,
'URL':req.originalUrl,
'statusCode':res.statusCode,
'headers':req.headers,
'Time':new Date(),
'ErrorMessage':'Display Error If Any for this request'
};
winston.log('info', logmsg);
next();
}
Winston logging works on the basis of log level info,debug,error etc.. If you want to log everything into the same log file you have to give level info.
const logger = winston.createLogger({
levels: winston.config.syslog.levels,
transports: [
new winston.transports.File({
filename: 'combined.log',
level: 'info'
})
]
});
process.on('unhandledRejection', (reason, p) => {
logger.error('exception occur');
throw reason;
});
Read more about log level in winstonjs - https://github.com/winstonjs/winston#using-logging-levels

Jest tests hanging even after running server.close() on my express app

I have an express app which I need to start on my integration test case.
The express app is exported in the app.js file without listening to any port. So, my test case goes like this:
const app = require('../src/app');
describe('Pact Verification', () => {
const port = 3002;
let server;
beforeAll(done => {
const server = http.createServer(app);
server.listen({ port }, done)
});
afterAll(done => {
server.close(done);
});
it(....
The problem is, once the test is ran with Jest, it hangs. I have to either --forceExit or ^C to exit.
I even updated to Jest 23 to use --detectOpenHandles but I don't see any output in the terminal, it keeps hanging so that's not helping either.
Since the exported app is not listening to any port, and it doesn't have any database connections or such, the problem is unlikely to be there, maybe its in my beforeAll/afterAll block. What am I missing?
Update 1
Here is the content of my app.js
var express = require('express');
var path = require('path');
var logger = require('morgan');
var indexRouter = require('./routes/index');
var app = express();
if (process.env.NODE_ENV !== 'test') {
app.use(logger('dev'));
}
app.use(express.json());
app.use('/api/v1/', indexRouter); // <-- these endpoints just return faker data in JSON format (https://github.com/marak/Faker.js/)
app.use((req, res, next) => {
const err = Error('Not Found');
err.status = 404;
next(err);
});
app.use((err, req, res, next) => {
const { status, message } = err;
const stack = status !== 404 && err.stack || undefined;
res.status(status || 500)
.json({
message,
stack,
});
});
module.exports = app;
The problem is that server is undefined in afterAll because it's assigned in another scope as const server = http.createServer(app). Instead, it should be:
server = http.createServer(app);
There should be an exception, so done is never get called in afterAll. Currently Jest suppresses errors from afterAll, there's Jest open issue that addresses afterAll bug.

Resources