Всем привет
У меня возникли проблемы с тем, что winston 3.0 регистрирует пустые объекты ошибок, когда он использует формат json.
module.exports = winston.createLogger({
format: format.combine(
format.label({ label: 'my label'}),
format.timestamp(),
format.prettyPrint()
),
transports: [
new (winston.transports.Console)({
level: error
})
]
});
регистрирует ожидаемый
{error: [stack trace here] }
```
while
module.exports = winston.createLogger ({
формат: format.combine (
format.label ({label: 'мой ярлык'}),
format.timestamp (),
format.json ()
),
транспорты: [
новый (winston.transports.Console) ({
уровень: ошибка
})
]
});
logs the erroneous
{ошибка: {} }
when it is passed
logger.log ({
error: new Error ('мое сообщение об ошибке')
});
Manually creating a format with
winston.format.printf
`` ''
также вернул ошибочный результат.
Кто-нибудь знает обходной путь для этого?
Обходной путь:
function errorReplacer(key, value) {
if (value instanceof Error) {
return { message: value.message, stack: value.stack };
}
return value;
}
const logFormat = winston.format.printf((info) => {
return `${JSON.stringify(info, errorReplacer)}`;
});
module.exports = winston.createLogger({
format: format.combine(
format.label({ label: 'my label' }),
format.timestamp(),
logFormat
), ...
Надеюсь, это поможет.
У меня не сработало обходное решение (где-то теряется информация. Сообщение)
Вариант обходного пути:
const winston = require('winston')
const MESSAGE = Symbol.for('message')
/*
* function replacer (key, value)
* Handles proper stringification of Buffer output.
*/
const replacer = function (key, value) {
return value instanceof Buffer
? value.toString('base64')
: value
}
var json_log = winston.format(function (info, opts) {
var display = info
if (info._error) {
display = Object.assign({}, info, { message: info._error })
delete display._error
}
info[MESSAGE] = JSON.stringify(display, opts.replacer || replacer, opts.space)
return info
})
var preserve_error_message = format(function (info, opts) {
if (_.isError(info)) {
info._error = info.message
}
return info
})
const logger = winston.createLogger({
levels: ...,
format: format.combine(format.timestamp(), preserve_error_message()),
transports: [
new transports.Console({
level: this.config.console.level,
format: json_log({ space: 2 })
})
]
Это не кажется особенно элегантным, поэтому было бы идеальным сохранить info.message
когда info
- это Error
(это работает, когда info
является обычным Объект)
Я тоже сталкиваюсь с этим. Разве Winston не должен справиться с этим из коробки? @indexzero что ты об этом думаешь?
Посмотрев на реализацию json logform, я теперь вижу, как предполагается использовать opts.replacer, и я придумал другое решение при объединении этого сообщения stackoverflow с существующей строковой настройкой буфера:
'use strict';
const winston = require('../');
const logger = winston.createLogger({
format: winston.format.combine(
winston.format.json({ replacer: replaceErrors })
),
transports: [
new winston.transports.Console(),
]
});
function replaceErrors(key, value) {
if (value instanceof Buffer) {
return value.toString('base64');
} else if (value instanceof Error) {
var error = {};
Object.getOwnPropertyNames(value).forEach(function (key) {
error[key] = value[key];
});
return error;
}
return value;
}
var error = new Error('foooo');
error.my_custom_stuff = 'bar';
logger.log('error', 'Hello, this is a raw logging event', { error });
который печатает:
{"error":{"stack":"Error: foooo\n at Object.<anonymous> (/home/dino/work/winston/examples/json.js:31:13)\n at Module._compile (module.js:662:30)\n at Object.Module._extensions..js (module.js:673:10)\n at Module.load (module.js:575:32)\n at tryModuleLoad (module.js:515:12)\n at Function.Module._load (module.js:507:3)\n at Function.Module.runMain (module.js:703:10)\n at startup (bootstrap_node.js:193:16)\n at bootstrap_node.js:660:3","message":"foooo","my_custom_stuff":"bar"},"level":"error","message":"Hello, this is a raw logging event"}
Я больше не думаю, что с этим следует обращаться нестандартно, потому что я думаю, что библиотека ведения журнала не должна определять, как ваша ошибка должна быть сериализована в сообщение журнала, вы определяете это сами.
Не уверен в вашем конкретном варианте использования, но почему вы не регистрируете ошибки напрямую:
logger.log(new Error('my error message'));
(Это работает с https://github.com/winstonjs/winston/pull/1234).
Если вам действительно нужен объект ошибки внутри более крупного объекта, который вы регистрируете, то стоит отметить, что Node.js Errors
не являются объектами JSON, но есть такие вещи, как https://www.npmjs.com/package / utils-error-to-json или, возможно, сделать parse(stringify(error))
, который заставит эту работу работать с форматером JSON. Похоже, что именно это и делает решение
Собираясь отметить это закрытым, не стесняйтесь открывать повторно или создавать новый выпуск, если возникнут новые проблемы / вопросы! Спасибо!
Даже parse(stringify(error))
работает, поскольку вы все равно теряете трассировку стека.
Мне удалось выполнить эту работу, создав специальный модуль форматирования, подобный этому:
const formatErrorConverter = winston.format(
info =>
info instanceof Error
? Object.assign({ level: info.level, message: info.message, stack: info.stack }, info)
: info,
);
И работает неплохо!
instance of Error
работает только для средства форматирования верхнего уровня. Это НЕ работает для форматеров транспортного уровня. Кто-нибудь может объяснить почему?
Более лаконичная версия:
function format() {
const replaceError = ({ label, level, message, stack }) => ({ label, level, message, stack });
const replacer = (key, value) => value instanceof Error ? replaceError(value) : value;
return combine(label({ label: 'ssr server log' }), format.json({ replacer }));
}
const logger = createLogger({
format: format(),
});
Но это то, что я на самом деле делаю, чтобы иметь красивые логи локально и ошибки JSON в продакшене:
function prodFormat() {
const replaceError = ({ label, level, message, stack }) => ({ label, level, message, stack });
const replacer = (key, value) => value instanceof Error ? replaceError(value) : value;
return combine(label({ label: 'ssr server log' }), format.json({ replacer }));
}
function devFormat() {
const formatMessage = info => `${info.level} ${info.message}`;
const formatError = info => `${info.level} ${info.message}\n\n${info.stack}\n`;
const format = info => info instanceof Error ? formatError(info) : formatMessage(info);
return combine(colorize(), printf(format))
}
const logger = createLogger({
level: process.env.LOG_LEVEL || 'info',
exitOnError: false,
transports: [new transports.Console()],
format: isProd ? prodFormat() : devFormat(),
});
что дает мне это в производстве
и это в разработке
Я написал это для поддержки пользовательских классов ошибок с неизвестными свойствами, а также для очистки стека, потому что эти новые строки ужасны при ведении журнала на основе json.
const replacer = (key, value) => {
if (value instanceof Error) {
return Object.getOwnPropertyNames(value).reduce((all, valKey) => {
if(valKey === 'stack') {
return {
...all,
at: value[valKey].split('\n').filter(va => va.trim().slice(0, 5) != 'Error').map((va, i) => `stack ${i} ${va.trim().slice(3).trim()}`)
}
} else {
return {
...all,
[valKey]: value[valKey]
}
}
}, {});
} else {
return value;
}
}
результат:
{"at":["stack 0 /Users/sarahriehl/Documents/code/boilerplate/logging/routes/log.js:24:15","stack 1 Layer.handle [as handle_request] (/Users/sarahriehl/Documents/code/boilerplate/logging/node_modules/express/lib/router/layer.js:95:5)","stack 2 next (/Users/sarahriehl/Documents/code/boilerplate/logging/node_modules/express/lib/router/route.js:137:13)","stack 3 Route.dispatch (/Users/sarahriehl/Documents/code/boilerplate/logging/node_modules/express/lib/router/route.js:112:3)","stack 4 Layer.handle [as handle_request] (/Users/sarahriehl/Documents/code/boilerplate/logging/node_modules/express/lib/router/layer.js:95:5)","stack 5 /Users/sarahriehl/Documents/code/boilerplate/logging/node_modules/express/lib/router/index.js:281:22","stack 6 Function.process_params (/Users/sarahriehl/Documents/code/boilerplate/logging/node_modules/express/lib/router/index.js:335:12)","stack 7 next (/Users/sarahriehl/Documents/code/boilerplate/logging/node_modules/express/lib/router/index.js:275:10)","stack 8 Function.handle (/Users/sarahriehl/Documents/code/boilerplate/logging/node_modules/express/lib/router/index.js:174:3)","stack 9 router (/Users/sarahriehl/Documents/code/boilerplate/logging/node_modules/express/lib/router/index.js:47:12)"],"message":"argh!!!","level":"error","timestamp":"Thu 2019-02-14 01:58:58 -0600"}
Я сделал пример для локальной среды разработки и GCP
Stackdriver Logging
.
Я столкнулся с той же проблемой, поэтому написал этот пакет utils-deep-clone . Проверить это.
Самый полезный комментарий
Более лаконичная версия:
Но это то, что я на самом деле делаю, чтобы иметь красивые логи локально и ошибки JSON в продакшене:
что дает мне это в производстве
и это в разработке