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);
Related
I'm using a local .json file as database for my small web appplication. It works without issues and I can retrieve data from it, but when I look to the console I see the error TypeError: Cannot read property 'name' of undefined... appearing twice each time. The error is not consistent and I can't properly replicate it and I don't understand what may cause it.
JSON
{
"Faction1": {
"name": "Lorem Ipsum",
"id": "lorem-ipsum",
"content": {
"colors": {
"col1": "red",
"col2": "white",
"col3": "grey"
}
}
}
}
App.js
//jshint esversion:6
const express = require("express");
const bodyParser = require('body-parser');
const favicon = require('serve-favicon');
const app = express();
app.set('view engine', 'ejs');
app.use(favicon(__dirname + '/public/favicon.ico'));
app.use(bodyParser.urlencoded({ extended: true }));
app.use(express.static(__dirname + '/public'));
app.use('/scripts', express.static( __dirname + '/node_modules/'));
const factionsDb = require(__dirname + '/views/data/factions.json');
JSON.stringify(factionsDb);
// -------------------------------------------------------------------------- //
app.get('/', (req, res) => {
res.render('pages/index', {
pageTitle: "App Name"
});
});
app.get('/factions', (req, res) => {
res.render('pages/product-list', {
parentPage: "'/'",
pageTitle: "Factions",
section: "factions",
productsList: factionsDb
});
});
app.get('/factions/:productId', (req, res) => {
let requestedProductId = req.params.productId;
let selectedProduct = Object.values(factionsDb).find(product => Object.values(product).includes(requestedProductId));
res.render('pages/product-details', {
parentPage: "'../factions'",
pageTitle: selectedProduct.name,
section: "factions",
product: selectedProduct
});
})
app.listen(3000, function(){
console.log("Server started on port 3000");
});
// -------------------------------------------------------------------------- //
app.use(function (req, res) {
res.status(404).render('pages/404', {parentPage: "'/'", pageTitle: ""},
);
});
If you select nonexisting product, then selectedProduct will be undefined:
let selectedProduct = Object.values(factionsDb).find(product => Object.values(product).includes(requestedProductId));
Then you try to set following pageTitle: selectedProduct.name,, which goes into selectedProduct variable and tries to find object name in it. As the selectedProduct is undefined, it throws error you mentioned.
You should be able to simulate it by calling URL with some nonssense :productId, i.e. /factions/nonexistingfaction
Need to check if there is matched item by find function.
`let selectedProduct = Object.values(factionsDb).find(product => Object.values(product).includes(requestedProductId));
If there is no matched item, it will return undefined So try to check selectedProduct.
Below I have updated pageTitle part.
res.render('pages/product-details', {
parentPage: "'../factions'",
pageTitle: (selectedProduct && selectedProduct !== undefined)? selectedProduct.name : '',
section: "factions",
product: selectedProduct
});
Sync:
var fs = require('fs');
var obj = JSON.parse(fs.readFileSync('file', 'utf8'));
Async:
var fs = require('fs');
var obj;
fs.readFile('file', 'utf8', function (err, data) {
if (err) throw err;
obj = JSON.parse(data);
});
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 });
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
I am new to Node.js and I have been trying to create a small app integrated with KUE library for task queuing.
When I trying to run the app i.e. node app.js I get the following error:
{ ReplyError: ERR wrong number of arguments for 'set' command
at parseError (.......\node_modules\redis-parser\lib\parser.js:193:12)
at parseType (........\node_modules\redis-parser\lib\parser.js:303:14)
command: 'SET',
args: [ 'promotion:lock', 'H5BCCsomeRandomString==', 'PX', 2000, 'NX' ],
code: 'ERR' }
I did see this error at a lot of places but they all dont seem to be a solution for my problem.
Here is my app.js
var express = require('express');
var path = require('path');
var logger = require('morgan');
var cookieParser = require('cookie-parser');
var bodyParser = require('body-parser');
var session = require('express-session');
var dotenv = require('dotenv');
dotenv.load();
var queue = require('./routes/queueJob');
var app = express();
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'jade');
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: false }));
app.use(cookieParser());
app.use(session({
secret: 'secret',
resave: true,
saveUninitialized: true
}));
app.use(express.static(path.join(__dirname, 'public')));
app.use('/jobs', queue);
app.use(function(req, res, next) {
var err = new Error('Not Found');
err.status = 404;
next(err);
});
app.use(function(err, req, res, next) {
res.status(err.status || 500);
res.render('error', {
message: err.message,
error: err
});
});
app.listen(3000);
And here is the routes/queuejob.js
var express = require('express');
var router = express.Router();
var kue = require('kue'),
jobs = kue.createQueue();
var env = {};
jobs.on('ready', () => {
console.info('Queue is ready!');
});
jobs.on('error', (err) => {
console.error('There was an error in the main queue!');
console.error(err);
console.error(err.stack);
});
router.get('/addnewjob', function(req, res) {
let callback = function() {
console.log('Callback has been triggered');
}
newJob('Request Job', callback);
res.end('Successfully added a new job');
});
function newJob(name, callback) {
name = name || 'Default_Name';
var job = jobs.create('new job', {
name: name
});
job
.on('complete', function() {
console.log('Job', job.id, 'with name', job.data.name, 'is done');
callback();
})
.on('failed', function() {
console.log('Job', job.id, 'with name', job.data.name, 'has failed');
callback();
})
job.save();
}
jobs.process('new job', function(job, done) {
setTimeout(function() {
console.log('Job Processing finished');
}, 5000);
done();
});
module.exports = router;
Since there isnt much in the error message I am not sure how to fix this issue. I would really appreciate some help on this.
A SET command in Redis has the following format:
SET key value [EX seconds] [PX milliseconds] [NX|XX]
In your error, it seems that you are trying to use the SET commands with many argurments that do not match the format:
command: 'SET',
args: [ 'promotion:lock', 'H5BCCsomeRandomString==', 'PX', 2000, 'NX' ],
app.js
var express = require('express');
var express_namespace = require('express-namespace');
var path = require('path');
var favicon = require('serve-favicon');
var http = require('http');
var https = require('https');
var e_logger = require('morgan');
var logger = require('./logger.js').getLogger('framework');
var cookieParser = require('cookie-parser');
var bodyParser = require('body-parser');
var routes = require('./routes/index');
var awsControl = require('./routes/awsControl')
var aws = require('aws-sdk');
var ec2 = new aws.EC2();
var app = express();
var server = http.createServer(app);
var env = process.env.NODE_ENV || 'development';
if ('development' == env) {
app.set('port', 80);
app.use(express.static(path.join(__dirname, 'public')));
app.set('views', path.join(__dirname, 'views'));
app.engine('html', require('ejs').renderFile);
app.set('view engine', 'html');
app.use(cookieParser());
app.use(e_logger('dev'));
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: false }));
app.use(require('stylus').middleware(path.join(__dirname, 'public')));
app.use('/', routes);
}
// routes
app.namespace('/', function () {
app.get('describeInstances', awsControl.describeInstances);
});
server.listen(app.get('port'), function () {});
// catch 404 and forward to error handler
app.use(function (req, res, next) {
var err = new Error('Not Found');
err.status = 404
logger.error(err);
next(err);
});
awsControl.js
var aws = require('aws-sdk');
var util = require('util');
aws.config.update({
accessKeyId: "myKey",
secretAccessKey: "mySecretKey",
region: "ap-northeast-1"
});
console.log("config = " + util.inspect(aws.config));
var ec2 = new aws.EC2({ region: "ap-northeast-1" });
var app01 = 'aaaaaaaa';
var DB01 = 'bbbbbbbb';
exports.describeInstances = function (req, res) {
var params = {
Filters: [{
Name: 'vpc-id',
Values: ['vpc-cccccccc']
}],
DryRun: false
};
ec2.describeInstances(params, function (err, data) {
if (err) { // an error occurred
console.log(err, err.stack);
} else { // successful response
console.log(data);
}
});
}
control.js
var Control = function () {
this.initializePage();
};
Control.prototype = new Object();
Control.prototype = {
initializePage : function () {
$.ajax({
type: "GET",
url: "describeInstances",
success : function (data, status, jQxhr) {
console.log("### describeInstances success");
console.log(data);
},
error : function (jQxhr, status, error) {
console.log("### describeInstances error");
console.log(error);
console.log(jQxhr);
},
complete : function (jQxhr, status) {
console.log("### describeInstances complete");
console.log(jQxhr);
}
});
}
}
I programmed like above and the node web server is operating well.
awsControl.js is server-side javascript and control.js is client-side javascript.
When I connect to my web server, Control class is called first.
Here is the trouble. When I send request with startInstance (AWS-SDK API), it's working in server-side.
However, I can't receive response with 503 error(service unavailable).
On client-side, I always receive error callback with 503 error.
I don't know why I can't receive response.
I set security groups(EC2) and NACL(VPC) up so I don't think that it's firewall trouble.
Is there anybody can tell me how I can find the solution out?
I have done this.
ec2.describeInstances(params, function (err, data) {
if (err) { // an error occurred
console.log(err, err.stack);
} else { // successful response
console.log(data);
res.send(data); <-- this line
}
});
I added just a line I focus to awsControl.js file, then done.