-
Notifications
You must be signed in to change notification settings - Fork 1.8k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Passing multiple parameters to logging functions doesn't behave as expected #1427
Comments
I ran into the same issue today. I believe it is a bug since the upgrade documentation also has an example like that here: logger.info('transaction ok', { creditCard: 123456789012345 }); |
Currently to get around this you can create a function that parses the arguments before it's passed into winston |
@mulligan121 Okay, but I don't want to replace every log statement in my codebase... |
@indexzero Is this under your radar? I think this is a major breaking change. It actually prevents from moving from 2.x to 3.x as it requires us to change every log entry |
Temporary workaround:
|
This is a HUGE breaking change for us. If it is intentional, it should be detailed in https://github.com/winstonjs/winston/blob/master/UPGRADE-3.0.md |
So after some more investigation, I found that you need the splat formatter in order for multiple arguments to be printed. I thought that was just for argument interpolation (i.e. stuff with %s etc. in it), but it seems you need it just to do But this is still being a bit weird for me - I'm getting something that looks like JSON in the output with a key "meta", even though I am not using a JSON format: logger.info('Test: %s', 1, 2, 3, 4); Produces:
Even the example in examples doesn't produce what it says it will: https://github.com/winstonjs/winston/blob/master/examples/interpolation.js#L20-L21
|
I've solved it using something along these lines:
|
Here is a common workaround to allow for multiple parameters: |
Related to: There's just too many things that don't work the same. |
Hi, Any updates here? Thanks. |
Also waiting for proper way to emulate |
We fixed a number of the edge / corner cases around Will make sure this gets handled in |
This was a workaround for me:
|
When I try to log with arguments I get a weird output Getting the output
I used the normal quick start code |
One thing I noticed is this:
Hope this can/will be fixed Note: tested this with the latest released version of Winston, quick look at the code seems to suggest that it's still like this in master (which has other fixes it seems) |
Some improvements over @luislobo's solution 👏
|
Any further update to this that I may be missing? It seems like there's pretty strong support for this feature to be re-added, but the last commitment to this was nearly 6 months ago. |
Hi there, huge Bumper and show stopper, trying to migrate to the 3.X, probably will keep the 2.x for a while. A bit disappointed :( |
This is what worked for me: const { format, createLogger, transports } = require('winston');
const jsonStringify = require('fast-safe-stringify');
const logLikeFormat = {
transform(info) {
const { timestamp, label, message } = info;
const level = info[Symbol.for('level')];
const args = info[Symbol.for('splat')];
const strArgs = args.map(jsonStringify).join(' ');
info[Symbol.for('message')] = `${timestamp} [${label}] ${level}: ${message} ${strArgs}`;
return info;
}
};
const debugFormat = {
transform(info) {
console.log(info);
return info;
}
};
const logger = createLogger({
format: format.combine(
// debugFormat, // uncomment to see the internal log structure
format.timestamp(),
format.label({ label: 'myLabel' }),
logLikeFormat,
// debugFormat, // uncomment to see the internal log structure
),
transports: [
new transports.Console()
]
});
logger.info('foo', 'bar', 1, [2, 3], true, { name: 'John' }); which results in: Basically |
I wanted a solution that supports
My solution for this ended up using the
If you don't want
The problem with using |
Same issue, any update regarding this one? I love Winston, but it drives me crazy when I cannot print all arguments passed to the logger, which I can do with |
@fr1sk Did you try my formatting above? It gives |
It's disappointing that this issue still has not been resolved, and that my related issue has been locked. I've spent a whole day trying things out. IMO, logging to the Console should be delightful. v2 setup for comparisonconst winston = require('winston');
const chalk = require('chalk');
const logger = new winston.Logger({
transports: [
new winston.transports.Console({
level: 'info',
colorize: true,
prettyPrint: true,
timestamp: true
})
]
});
logger.info({ one: 1, two: 2, three: 3 });
logger.info(chalk.blue('[TEST]:'), { one: 1, two: 2, three: 3 }, [4, 5, 6]);
logger.info(chalk.blue('[TEST]:'), null, undefined, 'one', 2, { 3: 3, 4: '4' });
logger.info(chalk.blue('[TEST]:'), chalk.yellow('Bombastic'), () => {}, /foo/);
logger.error(chalk.blue('[ERR]:'), new Error('Error number 1'));
logger.error(new Error('Error number 2')); v3 setupconst { createLogger, format, transports } = require('winston');
const { inspect } = require('util');
const chalk = require('chalk');
const hasAnsi = require('has-ansi');
function isPrimitive(val) {
return val === null || (typeof val !== 'object' && typeof val !== 'function');
}
function formatWithInspect(val) {
const prefix = isPrimitive(val) ? '' : '\n';
const shouldFormat = typeof val !== 'string' || !hasAnsi(val);
return prefix + (shouldFormat ? inspect(val, { depth: null, colors: true }) : val);
}
const logger = createLogger({
level: 'info',
format: format.combine(
format.timestamp(),
format.errors({ stack: true }),
format.colorize(),
format.printf(info => {
const msg = formatWithInspect(info.message);
const splatArgs = info[Symbol.for('splat')] || [];
const rest = splatArgs.map(data => formatWithInspect(data)).join(' ');
return `${info.timestamp} - ${info.level}: ${msg} ${rest}`;
})
),
transports: [new transports.Console()]
});
logger.info({ one: 1, two: 2, three: 3 });
logger.info(chalk.blue('[TEST]:'), { one: 1, two: 2, three: 3 }, [4, 5, 6]);
logger.info(chalk.blue('[TEST]:'), null, undefined, 'one', 2, { 3: 3, 4: '4' });
logger.info(chalk.blue('[TEST]:'), chalk.yellow('Bombastic'), () => {}, /foo/);
logger.error(chalk.blue('[ERR]:'), new Error('Error number 1'));
logger.error(new Error('Error number 2')); There are many things to handle, some of which
Current bugs with this solution
Playing around with the Positive enhancements
Edit:We can handle the errors formatting but it's hacky: function formatWithInspect(val) {
+ if (val instanceof Error) {
+ return '';
+ }
const prefix = isPrimitive(val) ? '' : '\n';
const shouldFormat = typeof val !== 'string' || !hasAnsi(val);
return prefix + (shouldFormat ? inspect(val, { depth: null, colors: true }) : val);
}
...
format.printf((info) => {
const msg = formatWithInspect(info.message);
const splatArgs = info[Symbol.for('splat')] || [];
const rest = splatArgs.map((data) => formatWithInspect(data)).join(' ');
+ const stackTrace = info.stack ? `\n${info.stack}` : '';
return `${info.timestamp} - ${info.level}: ${msg} ${rest}${stackTrace}`;
})
... |
it works for me using const logger = winston.createLogger({
level: 'debug',
transports: [
...
],
format: winston.format.combine(
winston.format.timestamp(),
winston.format.printf((info: any) => {
const timestamp = info.timestamp.trim();
const level = info.level;
const message = (info.message || '').trim();
const args = info[Symbol.for('splat')];
const strArgs = (args || []).map((arg: any) => {
return util.inspect(arg, {
colors: true
});
}).join(' ');
return `[${timestamp}] ${level} ${message} ${strArgs}`;
})
)
}); |
I understand major versions introduce breaking changes, but my goodness... |
None of the solutions gave me same behavior as winston v2.
whereas
Second issue is that the additional arguments are suppressed when using with info level - winston v3 seems to handle that the other way before formatting. Third issue is the missing space between 2 messages (notice "anystringhereError"). My current v3 logger configuration:
I had enough of that and gonna switch back to winston v2 with just this:
This v2 configuration does not have the above-mentioned issues. |
I want to share my winston setup: const util = require('util');
const { createLogger, format, transports } = require('winston');
const { combine, colorize, timestamp, printf, padLevels} = format;
const myFormat = printf(({ level, message, label, timestamp, ...rest }) => {
const splat = rest[Symbol.for('splat')];
const strArgs = splat ? splat.map((s) => util.formatWithOptions({ colors: true, depth: 10 }, s)).join(' ') : '';
return `${timestamp} ${level} ${util.formatWithOptions({ colors: true, depth: 10}, message)} ${strArgs}`;
});
const logger = createLogger({
level: process.env.NODE_ENV === 'production' ? 'info' : 'debug',
format: combine(
colorize(),
timestamp({
format: 'YYYY-M-DD HH:mm:ss',
}),
padLevels(),
myFormat
),
transports: [new transports.Console()],
});
logger.info('test info');
logger.error('test error');
logger.debug('test debug'); |
here is updated link to the proposed solution |
Mine is a variation of @alexilyaev great suggestions:
for logs like:
It outputs like this - which is good for all my scenarios: |
Hey I'm starting with Winston, never used v2, so I'm on v3.2.1 I was trying to simply do: import winston, { format } from 'winston';
winston.format(format.combine(format.splat(), format.simple()));
winston.info('buildFastify dataPath %s', opts.dataPath); And hoping the string interpolation works; but nope.
When I was expecting
This issue is the same thing somehow? Or I'm forced to use the EditEvent trying this doesn't work. winston.log('info', 'buildFastify dataPath %s', opts.dataPath); output:
|
Thanks to who posted the suggestions above. |
This one hit me today - I realized my fancy new logger was tossing all my
|
Really upsetting that they decided to make a breaking change this large and STILL have not mentioned it in the migration guide or the documentation. Regardless I wanted to share my solution which I found quite compact and follows how node js implements console.log using const winstonLogger= createLogger(...);
const writeLogType = (logLevel) => {
return function () {
const args = Array.from(arguments);
winstonLogger[logLevel](util.format(...args));
};
};
const logger = {
silly: writeLogType('silly'),
debug: writeLogType('debug'),
verbose: writeLogType('verbose'),
info: writeLogType('info'),
warn: writeLogType('warn'),
error: writeLogType('error'),
}; |
I would like to chime in and request this feature to be added to the core Winston. Currently, I'm using the custom formatter with |
Any updates? |
Besides the above behaviors for v3, I want to add this as well: logger.info('param 1', { propInJson1: 'propInJson 1', propInJson2: 'propInJson 2' }); will produce this
the version I'm using: (v3.2.1)
|
I still don't understand how to output multiple values in Winston. I just wanted to replace To be honest, trying to use Winston always lead to a lot of time wasted and surprising problems with logging. |
Also, use |
This works for me. const winston = require("winston");
const util = require('util');
const combineMessageAndSplat = () => {
return {transform: (info, opts) => {
//combine message and args if any
info.message = util.format(info.message, ...info[Symbol.for('splat')] || [] )
return info;
}
}
}
const logger = winston.createLogger({
format:
winston.format.combine(
combineMessageAndSplat(),
winston.format.simple()
)
});
logger.add(new winston.transports.Console({
level: 'info'
})
);
logger.info("string"); // info: string
logger.info({a:1,b:[{c:[1]}]}); // info: { a: 1, b: [ { c: [Array] } ] }
logger.info("1","2",{a:1}); // info: 1 2 { a: 1 }
logger.info([{},""]); // info: [ {}, '' ]
logger.error(new Error()); // error: Error ... at Object.<anonymous> |
I had to conform to console.* methods, orientating on all above, and ...
But: The file output should show object details. So I ended up with: // make File and FileList parseable // from: https://stackoverflow.com/a/51939522/1644202
File.prototype.toObject = function () {
return Object({
name: String(this.name),
path: String(this.path),
lastModified: parseInt(this.lastModified),
lastModifiedDate: String(this.lastModifiedDate),
size: parseInt(this.size),
type: String(this.type)
});
};
FileList.prototype.toArray = function () {
return Array.from(this).map(function (file) {
return file.toObject()
});
};
// this fixes: winston console transport to use the original console functions and all the colorization/folding/etc that comes with it
const Transport = require('winston-transport');
class WebDeveloperConsole extends Transport {
constructor(opts) {
super(opts);
}
log(info, callback) {
(window.console[info.level] || window.console.log).apply(window.console, [info.timestamp, ...info.message]);
callback();
}
};
// Electron app console output
class AppConsole extends Transport {
constructor(opts) {
super(opts);
const { remote } = require('electron');
this.electronConsole = remote.getGlobal('console');
}
log(info, callback) {
(this.electronConsole[info.level] || this.electronConsole.log).apply(this.electronConsole, [info.timestamp, ...info.message]);
callback();
}
};
const util = require('util');
const {
createLogger,
transports,
format
} = require('winston');
let logger = createLogger({
level: 'trace',
levels: {
error: 0,
warn: 1,
info: 2,
//http: 3, no console.* methods
//verbose: 4,
debug: 3,
trace: 4
},
format: format.combine(
format.prettyPrint(),
format.timestamp({
format: 'DD-MM-YYYY hh:mm:ss A'
}),
{
transform(info) {
const { timestamp, message } = info;
const level = info[Symbol.for('level')];
const args = [message, ...(info[Symbol.for('splat')] || [])]; // join the args back into one arr
info.message = args; // for all custom transports (mainly the console ones)
let msg = args.map(e => {
if (e.toString() == '[object FileList]')
return util.inspect(e.toArray(), true, 10);
else if (e.toString() == '[object File]')
return util.inspect(e.toObject(), true, 10);
else if (e.toString() == '[object Object]') {
return util.inspect(e, true, 5);
}
else if (e instanceof Error)
return e.stack
else
return e;
}).join(' ');
info[Symbol.for('message')] = `${timestamp} - ${level}: ${msg}`; // for inbuild transport / file-transport
return info;
}
},
),
transports: [
//new transports.Console(),
new WebDeveloperConsole(),
new AppConsole(),
...
],
...
}); |
@indexzero Was wondering if you were still planning to address this at some point? |
i am exploring winston to use in my project, now wondering is it worth taking the risk as this issue is open for more than 2 years. for me this seems like a basic function for any logging framework |
Is the only solution to use Pino or something other than Winston? |
Well, patching Winston yourself as described in this thread is a viable solution. It is disappointing that something so major has to be fixed this way, but the framework is extensible enough to fix this way without forking, so there's that. |
At this point there are 356 open issues and commit activity on the repo has tapered off since the beginning of 2020. So I would guess that the maintainers feel the project is good enough and/or don't have enough time to process all the open issues. Normally you would get some indication in the issue discussion if they are open to a change/PR before you start making a contribution. So fork it and patch it, open a PR and if the PR is ignored use your fork. |
At this point I have lost faith in winston project. I cant believe this issue is not embraced at all. I have switched from winston to pino to bunyan and back to winston and to winston 2. Winston is able to provide very important functionalities the other two cannot, however the disability to log multiple parameters and error is downright unacceptable. With pino, you cannot define a stackdriver service context and transform. With bunyan, you cannot transform the logs as well. With winston you cannot log more than 1 parameter. I am having a hard time believing these are all our 'modern' logger options in 2022. To leave my bits I have put together the following winston configuration to accommodate both errors and multiple parameters in the logs. Thanks to BananaAcid's reply above const winston = require('winston');
const util = require('util');
const enumerateErrorFormat = winston.format(info => {
if (info.message instanceof Error) {
info.message = Object.assign({
message: info.message.message,
stack: info.message.stack
}, info.message);
}
if (info instanceof Error) {
return Object.assign({
message: info.message,
stack: info.stack
}, info);
}
return info;
});
const logger = winston.createLogger({
level: 'silly',
format: winston.format.combine(
enumerateErrorFormat(),
{
transform: (info) => {
const args = [info.message, ...(info[Symbol.for('splat')] || [])];
info.message = args;
const msg = args.map(arg => {
if (typeof arg == 'object')
return util.inspect(arg, {compact: true, depth: Infinity});
return arg;
}).join(' ');
info[Symbol.for('message')] = `${info[Symbol.for('level')]}: ${msg}${info.stack ? ' ' + info.stack : ''}`;
return info;
}
}
)
});
logger.add(new winston.transports.Console())
logger.add(new winston.transports.File({filename: './logs.txt.'}));
logger.silly('hello silly', 1234, 'my condolence', {ahmet:1, meh:[{ahmet:1, meh:'alo'},{ahmet:1, meh:'alo'}]}, [{ahmet:1, meh:'alo'},{ahmet:1, meh:'alo'}]);
logger.debug('hello debug', () => console.log(1+2));
logger.info('hello info');
logger.warn('hello warn');
logger.error('hello error');
logger.error(new Error('error 1'), 123, new Error('error 2')); |
This seems like a reasonable description in general; core maintainers have very little bandwidth even for reviewing issues/PRs and are largely reliant on community members for authoring PRs. |
I'm hoping to be able to take a look at this and verify it with a MWE prior to investigation. My time is limited in the next couple weeks but I'll keep this on my radar regardless. Alternatively if any one of the multiple people who have contributed to this issue thread believes they have a fix, please open a PR, link to this issue, and request a review from me! |
Meanwhile, I workaround this issue with the following code:
|
Facing the same issue. I'm unable to simply do |
Try a wrapper: |
To capture multiple parameters without any external dependencies, you can add a custom formatter, like this. This is possible because Winston stores the extra parameters in a hidden "splat" field:
|
Is there any chance to mimic the behaviour of e.g.:
Outputs in both cases:
When using Winston + JSON Transport + Console Transport, this should output
and
respectively. |
Please tell us about your environment:
winston
version?winston@2
winston@3
node -v
outputs: v8.11.3What is the problem?
With 3.x.x:
Outputs:
With 2.x.x:
Outputs:
What do you expect to happen instead?
It should output:
The text was updated successfully, but these errors were encountered: