Как зарегистрировать запрос и идентификатор транзакции в каждой строке журнала, используя Winston для Node JS?
Я создал службу Node JS REST, используя Express. У каждого запроса, который попадает в этот сервис, есть заголовки, такие как "X-org-ReqId" и "X-org-Tid", которые мне нужны для входа во все строки журнала, которые записываются во время выполнения этого запроса. По сути, мне нужно регистрировать некоторую контекстную информацию в каждой строке журнала, чтобы помочь мне выполнять транзакции / отслеживание запросов через несколько служб.
Я использую Winston Logger, инициализированный так:
var winston = require('winston');
var appLogger = new(winston.Logger)({
transports: [
new(winston.transports.Console)({
level: 'info', //TODO: Should be changed to Error in prod
colorize: true
}),
new(winston.transports.DailyRotateFile)({
filename: '/var/log/org/my-service.log',
datePattern: '.yyyy-MM-dd',
tailable: true,
// handleExceptions: true,
json: true,
logstash: true
})
],
exitOnError: false
});
appLogger.on('error', function(err) {
console.error("Logger in error", err);
});
module.exports.logger = function() {
return appLogger;
};
и в отдельных классах, где бы я ни хотел использовать это, я делаю так:
var logger = require('../config/logger').logger();
myObject.on("error", function (err) {
logger.error("Error connecting bucket=" + bucketId , err);
});
Это создаст журнал следующим образом:
{"level":"info","message":"Error connecting bucket=2.....","timestamp":"2015-06-10T06:44:48.690Z"}
Winston по умолчанию добавляет метку времени к этому оператору журнала, но я также хочу регистрировать такие вещи, как req.headers['X-org-ReqId'] & req.headers['X-org-Tid'], чтобы я знал, какая транзакция была неудачной и в чем была ошибка.
Я хочу войти, как это:
{"level":"info","message":"Error connecting bucket=2....","timestamp":"2015-06-10T06:44:48.690Z", "tid":"a3e8b380-1caf-11e5-9a21-1697f925ec7b", "reqid":"aad28806-1caf-11e5-9a21-1697f925ec7b"}
В мире Java у нас был NDC, есть ли аналог в мире Node JS?
2 ответа
Я столкнулся с той же проблемой, что и вы. Возможно, это не лучший способ сделать что-то, потому что вы должны распространять объект 'req', но он работает.
Я хочу получить любое улучшение:)
Во-первых, я перезаписываю методы регистрации Winston в моем файле log.js:
// MyLogger definition
function MyLogger() {
this.__proto__.__proto__.constructor.apply(this, arguments);
}
// Inheritance
MyLogger.prototype.__proto__ = winston.Logger.prototype;
// Overwriting methods
MyLogger.prototype.log = function() {
var args = [];
// Copying arguments not to modify them
for (var i = 0; i < arguments.length; i++) {
args[i] = arguments[i];
}
// Adding information in logs
var lastArg = args[arguments.length - 1];
if (typeof lastArg === 'object'
&& lastArg.headers) {
args[arguments.length - 1] = {
// Liste des infos ajoutées dans les logs
requestId: lastArg.headers['x-request-id'] ? lastArg.headers['x-request-id'] : arguments[arguments.length - 1].id,
host: arguments[arguments.length - 1].headers.host,
pId: process.pid
};
}
// Calling super
this.__proto__.__proto__.log.apply(this, args);
}
MyLogger.prototype.error = function() {
var args = ["error"];
for (var i = 0; i < arguments.length; i++) {
args[i + 1] = arguments[i];
}
this.__proto__.log.apply(this, args);
}
MyLogger.prototype.warn = function() {
var args = ["warn"];
for (var i = 0; i < arguments.length; i++) {
args[i + 1] = arguments[i];
}
this.__proto__.log.apply(this, args);
}
MyLogger.prototype.info = function() {
var args = ["info"];
for (var i = 0; i < arguments.length; i++) {
args[i + 1] = arguments[i];
}
this.__proto__.log.apply(this, args);
}
MyLogger.prototype.verbose = function() {
var args = ["verbose"];
for (var i = 0; i < arguments.length; i++) {
args[i + 1] = arguments[i];
}
this.__proto__.log.apply(this, args);
}
MyLogger.prototype.debug = function() {
var args = ["debug"];
for (var i = 0; i < arguments.length; i++) {
args[i + 1] = arguments[i];
}
this.__proto__.log.apply(this, args);
}
MyLogger.prototype.silly = function() {
var args = ["silly"];
for (var i = 0; i < arguments.length; i++) {
args[i + 1] = arguments[i];
}
this.__proto__.log.apply(this, args);
}
var logger = new MyLogger({
transports: [
new winston.transports.File({
level: config.LOG_LEVEL,
filename: config.LOG_FILE,
handleExceptions: true,
json: true,
maxsize: 5242880, //5MB
maxFiles: 5,
colorize: false
}),
new winston.transports.Console({
level: config.LOG_LEVEL,
handleExceptions: true,
json: false,
colorize: true
})
],
exitOnError: false
});
module.exports = logger;
Затем в каждом модуле, который нуждается в регистрации:
var logger = require("../log.js");
...
logger.debug("My message", req);
Создает журнал в консоли и файл журнала JSON в журнале с каждой информацией, необходимой для отслеживания потока.
Следующим моим шагом было бы не распространять 'req' и получать необходимую информацию из контекста или сеанса.
Надеюсь, что помогло:)
моя двухцентовая идея,
Используйте logFormatter от winston, чтобы добавить дополнительные поля в каждый журнал: -
Один пример ниже:
const { format} = require("winston");
var reqId = '123123' //function or const request id
const addRequestId = format((info, opts) => {
if(reqId)
info.reqId= reqId;
return info;
});
Затем добавьте приведенную ниже конфигурацию при создании регистратора.
var config = {
format: format.combine(
addRequestId(),
format.timestamp(new Date().toISOString()),
format.json(),
),
transports: [new transports.Console()],
level: 'debug'
}
const logger = createLogger(config);