NodeJs WinstonJS very weird start - node.js

I just started using winstonJS for NodeJS app logging.
My code goes like this :
var winston = require('winston');
var logger = new winston.Logger({
levels: { error: 0, warn: 1, info: 2, debug: 3, trace: 4 },
transports: [
new (winston.transports.Console)()
]
});
logger.log('error', 'log0');
logger.log('warn', 'log1');
logger.log('info', 'log2');
logger.log('debug', 'log3');
logger.log('trace', 'log4');
and the ONLY logs that I get in my console are :
info: log2
trace: log4
debug: log3
Note the wrong order as well.
Am I missing something obvious ?
Thanks

You have to consider 2 things:
Console transport is created always with default level "info" unless you specify a different level
when you specify a level for a transport, the logger will print all the message with level >= than the specified level
Also consider that the default logging levels for winston are: silly=0, debug=1, verbose=2, info=3, warn=4, error=5
Analyzing your code I can see you defined a new "info" level, with value of 2: this means the console log will print only messages with levels >= 2, and in your example these levels are exactly info, debug and trace (I guess you define your levels in the wrong order).
If you change your code like this, you will see all the message printed:
transports: [
new (winston.transports.Console)({level: 'error'})
]
About the wrong output order I can't reproduce it, my output is always
info: log2
debug: log3
trace: log4
PS: if you define levels like this error: 0, warn: 1, infoz: 2, debug: 3, trace: 4 with anything different replacing info, the logger will print nothing at all because the default console transport level will remain info. You can discover your transport level with a simple
console.log(logger.transports.console.level);

Related

Structured Logs in Google Cloud Run not being parsed (using Winston for logging)

I'm attempting to format my logs in such a way that Google Cloud will correctly extract the log level. This is running on Cloud Run, with typescript. Cloud Run is grabbing the logs from the container output.
If I do the following, google correctly parses the log line:
console.log(JSON.stringify({
severity: 'ERROR',
message: 'This is testing a structured log error for GCP'
}));
And the log output looks like this:
I've tried a number of different ways to format with winston, ended up with the following:
useFormat = format.combine(
format((info, opts) => {
info['severity'] = info.level;
delete info.level;
return info;
})(),
format.json());
this.winston = winston.createLogger({
level: logLevel,
format: useFormat,
transports: [new winston.transports.Console()]
});
Which looks like it will work (it correctly outputs the json line), I get this in the GCP logs:
Any help appreciated.
Turns out I was close, just needed to .upperCase() the log level (and I'm mapping Verbose -> Debug, I don't really understand why GCP decided to do a totally different log leveling system than everyone else). New code:
useFormat =
format.combine(
format((info, opts) => {
let level = info.level.toUpperCase();
if(level === 'VERBOSE') {
level = 'DEBUG';
}
info['severity'] = level;
delete info.level;
return info;
})(),
format.json());
The last bit of the question is confusing. The problem OP is pointing to is that the json is printed out and the severity is default. The json should not be printed out, only the message, and the severity should be debug. The answer that OP provides does what is wanted.
For others that may be confused in the same way I was.

How to get Winston daily file rotate to log to corresponding level file

I have defined custom levels for my application .They are as follows.
protected levels: Level = {
"error": 0,
"warn": 1,
"info": 2,
"debug": 3,
"trace": 4
};
I am using daily file rotate transport to get daily log in separate files.
const options: Object = {
name: this.level,
filename: logFilePath,
dirname: WinstonLogAgent.DIR_LOG,
datePattern: "yyyyMMdd.",
prepend: true,
level: this.level,
levels: this.levels,
maxsize: this.maxFileSize,
maxFiles: this.maxFileCount,
handleExceptions: true,
humanReadableUnhandledException: true
};
this.transportInstance.push(new (winston.transports.DailyRotateFile)(options));
If i define log level to be 'info', it will create one log file named info.log and will log for levels 'info' , 'warn' and 'error' (trace and debug will be ignored).
But behaviour i wanted was different. If i am specifying level to be 'info' and i am logging levels 'info' , 'warn' and 'error' , then there should be separate files created for each type of log . i.e 'info' level should be logged to info.log and 'warn' level to be logged to warn.log.
I have tried specifying five different daily file rotate transport ,each with unique level. Then the problem i find is that there is duplicate log entries.
For example , if am logging 'error' level , it would log to info.log , warn.log & error.log when logging level is set to info.
How can i achieve my objective?
According to Winston's documentation, the default behavior is to log all the messages which have at least the specifies importance aka logging level.
Winston allows you to define a level property on each transport which specifies the maximum level of messages that a transport should log.
But there are ways to achieve your requirements.
I'll try to show you some of the possibilities, you can choose the method that works the best for you.
1. Custom Transports (Recommended):
You can create a custom transport and log only the levels you want.
Here is an example just to give you an idea:
let mainLogger = new (winston.Logger)({
transports: [
new (winston.transports.Console)(),
]
});
class CustomTransport extends winston.Transport {
constructor(options) {
super(options);
this.name = 'customLogger';
this.level = options && options.level || 'info';
this.levelOnly = options && options.levelOnly;
this.levels = options && options.levels || [];
}
log(level, msg, meta, callback) {
if (!this.levelOnly || this.levels.indexOf(level) > -1) {
mainLogger[level](msg, meta);
}
callback(null, true);
}
}
winston.transports.CustomTransport = CustomTransport;
let myLogger = new winston.Logger({
transports: [
new (winston.transports.CustomTransport)({
levelOnly: true,
levels: ['info'],
}),
]
});
myLogger.info('will be logged');
myLogger.warn('will NOT be logged');
myLogger.info('will be logged as well');
2. Use winston-levelonly
This is a fork of the original winston package. The fork is at https://github.com/damianof/winston
This version adds a levelOnly option to make winston log only the specified level.
In the end, I would like to encourage you to read these relevant discussions:
https://github.com/winstonjs/winston/issues/614
https://github.com/winstonjs/winston/issues/812
https://github.com/winstonjs/winston/pull/628
Winston Logging - separate levels to separate Transports

npm winston set log level for basic logging instance

Setting winston log level to 'debug' in 'easy mode' was not well documented so I've shown an example below (and will submit a PR soon).
the answer is winston.level = 'debug'
I want to use the winston logging package in a node script and not bother with any config, just be able to call winston.debug, winston.info, winston.error and then pass in the log level as a command line param. The docs for 'easy mode' did not include how to set log level so I've shown it below.
The code:
var winston = require('winston');
winston.transports.Console.level = "debug";
winston.log("error", "error test 1");
winston.log("info", "info test 1");
winston.log("debug", "debug test 1");
winston.level = "debug";
winston.log("error", "error test 2");
winston.log("info", "info test 2");
winston.log("debug", "debug test 2");
Will output:
error: error test 1
info: info test 1
error: error test 2
info: info test 2
debug: debug test 2
Hope this helps
the answer is winston.level = 'debug'

Winston : understanding logging levels

Reading and fiddling with Winston, I'm puzzled as to why the logging levels are ordered as they are and why the transports behave in the way they do (well, at least the Console one). I'd appreciate if someone could, perhaps even thoroughly, with real use case examples, explain why logging with Winston works this way?
For example, I setup my logger like this :
var logger = new (winston.Logger)({
levels: winston.config.syslog.levels,
colors: winston.config.syslog.colors,
level: "debug", // I'm not sure what this option even does here???
transports: [
new (winston.transports.Console)({
colorize: true,
handleExceptions: true,
json: false,
level: "debug"
})
]
});
So, if I do logger.debug("Test");, then it will log debug: Test, fine. But if I do logger.info("Test");, then nothing happens.
The problem I have is that, If I want to log to the console eveverything but debug messages, what do I do? ... or even debug and info messages, but log everything else?
Coming from a Java world, using the standard loggers, I am used to having debug being more "fine grained" than warn and the loggers worked backwards; setting the logging level to info, for example, did log everything but debug (or something).
Also, what if I'd like a logger to log only error, warning and info messages, how would I do that with Winston?
* EDIT *
Apparently, this order of level is unique to winston.config.syslog.levels. So the only question remaining is : "Is it possible to, somehow, restrict a transport to a very specific logging level only?"
As per the documentation, you can set your own Logging levels, 0 being lowest, and associate colours with it. Now, if you don't want to log the lowest level, just set the level property to the corresponding level. By default, the console logger has it's level set to info
So, here is an example:
logger = new (winston.Logger)({
levels: {
'info': 0,
'ok': 1,
'error': 2
}
transports: [
new (winston.transports.ConsoleTransport)(silent: options.silent, level: 'ok')
]
});
var logger = new (winston.Logger)({
levels: {
'info': 0,
'ok': 1,
'error': 2
},
colors: {
'info': 'red',
'ok': 'green',
'error': 'yellow'
},
transports: [
new (winston.transports.Console)({level:'info',colorize: true})
]
});
logger.log('info',"This is info level");
logger.info("This is info level");

How to set log level in Winston/Node.js

I am using Winston logging with my Node.js app and have defined a file transport. Throughout my code, I log using either logger.error, logger.warn, or logger.info.
My question is, how do I specify the log level? Is there a config file and value that I can set so that only the appropriate log messages are logged? For example, I'd like the log level to be "info" in my development environment but "error" in production.
If you are using the default logger, you can adjust the log levels like this:
const winston = require('winston');
// ...
winston.level = 'debug';
will set the log level to 'debug'. (Tested with winston 0.7.3, default logger is still around in 3.2.1).
However, the documentation recommends creating a new logger with the appropriate log levels and then using that logger:
const myLogger = winston.createLogger({
level: 'debug'
});
myLogger.debug('hello world');
If you are already using the default logger in your code base this may require you to replace all usages with this new logger that you are using:
const winston = require('winston');
// default logger
winston.log('debug', 'default logger being used');
// custom logger
myLogger.log('debug', 'custom logger being used');
Looks like there is a level option in the options passed covered here
From that doc:
var logger = new (winston.Logger)({
transports: [
new (winston.transports.Console)({ level: 'error' }),
new (winston.transports.File)({ filename: 'somefile.log' })
]
});
Now, those examples show passing level in the option object to the console transport. When you use a file transport, I believe you would pass an options object that not only contains the filepath but also the level.
That should lead to something like:
var logger = new (winston.Logger)({
transports: [
new (winston.transports.File)({ filename: 'somefile.log', level: 'error' })
]
});
Per that doc, note also that as of 2.0, it exposes a setLevel method to change at runtime. Look in the Using Log Levels section of that doc.
There are 6 default levels in winston: silly=0(lowest), debug=1, verbose=2, info=3, warn=4, error=5(highest)
While creating the logger transports, you can specify the log level like:
new (winston.transports.File)({ filename: 'somefile.log', level: 'warn' })
Above code will set log level to warn, which means silly, verbose and info will not be output to somefile.log, while warn, debug and error will.
You can also define your own levels:
var myCustomLevels = {
levels: {
foo: 0,
bar: 1,
baz: 2,
foobar: 3
}
};
var customLevelLogger = new (winston.Logger)({ levels: myCustomLevels.levels });
customLevelLogger.foobar('some foobar level-ed message');
Note that it's better to always include the 6 predefined levels in your own custom levels, in case somewhere used the predefined levels.
You can change the logging level in runtime by modifying the level property of the appropriate transport:
var log = new (winston.Logger)({
transports: [
new (winston.transports.Console)({ level : 'silly' })
]
});
...
// Only messages with level 'info' or higher will be logged after this.
log.transports.Console.level = 'info';
I guess, it works similarly for file but I haven't tried that.
If you want to change the log level on the fly. Like for when you need to trace production issue for short amount of time; then revert to error log level. You can use a dynamic logger provided you can expose a service on the web https://github.com/yannvr/Winston-dynamic-loglevel
apart from this you can cleanly achieve this by imlplementing runtime-node-refresh follow this link for more.

Resources