Как идентифицировать запрос (по идентификатору) через связующее ПО в Express.
Я разрабатываю RESTful-сервер в node.js, используя Express в качестве фреймворка, а Winston - на данный момент в качестве модуля логгера. Этот сервер будет обрабатывать большое количество одновременных запросов, и мне было бы очень полезно иметь возможность отслеживать записи журнала для каждого конкретного запроса, используя что-то вроде "идентификатора запроса". Прямое решение состоит в том, чтобы просто добавить этот идентификатор в качестве другой части информации журнала каждый раз, когда я хочу сделать запись в журнале, но это будет означать передачу "идентификатора запроса" каждому методу, используемому сервером.
Я хотел бы знать, существует ли какой-либо модуль или метод node.js/javascript, который позволил бы мне сделать это более простым способом, без переноса идентификатора запроса для каждого конкретного запроса.
4 ответа
Ты можешь использовать req
Объект, который приходит с каждым запросом в экспресс.
Итак, первый маршрут, который вы бы сделали в своем приложении, был бы:
var logIdIterator = 0;
app.all('*', function(req, res, next) {
req.log = {
id: ++logIdIterator
}
return next();
});
И затем в любом месте в экспресс, вы можете получить доступ к этому id
в req
объект: req.log.id
;
Вам все еще нужно будет передать некоторые данные в функции, которые хотят создать несколько журналов. На самом деле вы можете иметь функцию регистрации внутри req.log
объект, таким образом, будет гарантировано, что регистрация будет происходить только при наличии доступа к req.log
объект.
При автоматическом увеличении последующая аналитика журналов не сможет однозначно идентифицировать запросы, поскольку разные экземпляры будут генерировать конфликтующие идентификаторы, а перезапуск приложения автоматически вызывает конфликты идентификаторов.
Вот еще одно возможное решение.
Установите cuid:
npm install --save cuid
Затем в вашем основном файле приложения:
var cuid = require('cuid');
var requestId = function requestId(req, res, next) {
req.requestId = cuid();
next();
};
// Then, at the top of your middleware:
app.use(requestId);
Теперь вы получите дружественный идентификатор запроса, который вряд ли будет конфликтовать, и вы сможете однозначно идентифицировать ваши запросы для анализа и отладки журналов даже в нескольких экземплярах и при перезапусках сервера.
Я изо всех сил пытался найти решение этой проблемы. Что мне не понравилось в предлагаемых здесь решениях, так это то, что они подразумевают разделение объекта req среди всех функций проекта. Я нашел решение, сочетающее ваш подход (создание uuid для каждого запроса) и библиотеку (extension-local-storage), которая позволяет совместно использовать пространства имен между модулями.
Вы можете найти объяснение в этом другом ответе: /questions/27763592/nodejs-express-globalnyij-unikalnyij-identifikator-zaprosa/27763601#27763601
Если вам нужна дополнительная информация, я записал все эти идеи и весь код в посте, чтобы объяснить все в одном месте: Express.js: регистрация информации с глобальным уникальным идентификатором запроса - Node.js
Как упоминалось в @moka, использование идентификатора запроса в каждом запросе является ключевым моментом решения проблемы. Другой способ абстрагироваться от всего этого - использовать http-context и uuid.
Поэтому установите UUID в httpContext перед всеми вашими промежуточными программами (установленными как промежуточное программное обеспечение приложения, а не как промежуточное программное обеспечение маршрутизатора). теперь вы можете получить uuid в любом месте вашего кода и зарегистрировать его.
Вот пример реализации, которую я использовал
Вы можете получить полную ссылку здесь uuid в запросе
const uuid = require('node-uuid');
const httpContext = require('express-http-context');
....
this.expressApp.use(httpContext.middleware);
this.expressApp.use((req, res, next) => {
httpContext.set('reqId', uuid.v4());
next();
});
Теперь я использовал reqId, установленный здесь, в моем пользовательском регистраторе pino '
public infoLogService (fileName): pino.Logger {
return pino({
level: 'info',
name: this.appService.getApp_name(),
messageKey: 'XXX-Logs',
base: {pid: process.pid, hostname: os.hostname,
timestamp: this.getTimeStamp(),
appName: this.appService.getApp_name(),
fileName: fileName,
request_id: **isNullOrUndefined(httpContext.get('reqId'))** ? 'Not an actual request ' : httpContext.get('reqId')
},
enabled: true,
useLevelLabels: true,
});
}
Если reqId имеет значение null, это означает, что средства ведения журнала были вставлены в код, который используется перед запуском экспресс-приложения. Надеюсь, вы сможете использовать это как альтернативное решение
Вы не должны использовать глобальные переменные.
Мне нравится заполнять объект META перед каждым запросом.
Я использую генератор UUID ( https://github.com/kelektiv/node-uuid) для идентификации запроса
Вот пример
app.all('*', function(req, res, next) {
req.meta = {
ip: req.headers['x-forwarded-for'] || req.connection.remoteAddress,
timestamp: uuid(),
user_agent: req.headers['user-agent'],
body: req.body,
}
return next();
})