Как идентифицировать запрос (по идентификатору) через связующее ПО в 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();
  })
Другие вопросы по тегам