При развертывании API в рабочей среде очень важно иметь возможность регистрировать все входящие запросы и ответы с вашего сервера для таких целей, как отладка, аудит и оптимизация. Для NodeJS существует ряд популярных библиотек ведения журналов, которые отлично справляются с настройкой базовой конфигурации. Однако ни один из них не поставляется с вводом запроса по умолчанию и ведением журнала тела ответа. Вот краткое руководство о том, как мы можем добиться этого с помощью morgan.

Чтобы регистрировать дополнительную информацию помимо стандартных конфигураций morgan, нам потребуется создать собственные токены. Начнем с пользовательского промежуточного ПО, написанного для расширения morgan.

// /middlewares/morgan.ts
import { Request, Response } from "express";
import morgan from "morgan";
// in my app, I extend express's Request interface to store a 
// `requester` prop. this holds the decoded jwt token data
morgan.token("requester", function getRequester(req: Request): string {
  return JSON.stringify(req.requester);
});
morgan.token("input", function getInput(req: Request): string {
  let input: Record<string, any> = {};
  if (req.method === "GET") {
    input = req.query;
  } else {
    input = req.body;
  }
  
  // mask any input that should be secret
  input = { ...input };
  if (input.password) {
    input.password = "*";
  }
 
  return JSON.stringify(input);
});
morgan.token("response-body", (req: Request, res: Response): string => {
  const body = { ...JSON.parse(res.responseBody) };
  // mask any input that should be secret
  if (body?.data?.accessToken) {
    body.data.accessToken = "*";
  }
  if (body?.data?.refreshToken) {
    body.data.refreshToken = "*";
  }
  
  return JSON.stringify(body);
});
export { morgan };

Теперь мы можем импортировать это в index.ts и определить собственный формат с нашими вновь определенными токенами, которые должен использовать Морган.

// index.ts
import express, { Express, Request, Response } from "express";
import { morgan } from "./middlewares/morgan";
const PORT = process.env.PORT || 3000;
const app: Express = express();
// override send to store response body for morgan token to retrieve
const originalSend = app.response.send;
app.response.send = function sendOverride(body) {
  this.responseBody = body;
  return originalSend.call(this, body);
};
app.use(express.json());
app.use(cookieParser()); // I use this to retrieve jwt from cookies
app.use(
  morgan(
    ':requester :remote-addr [:date[clf]] ":method :url HTTP/:http-version" Input :input Response :response-body'
  )
);
... // other middleware, route handlers, server setup

Наконец, нам также нужно будет расширить типы запросов и ответов от экспресс, чтобы избежать ошибок во время сборки. Обязательно включите этот файл в свой tsconfig.json в опции include или files.

// custom.d.ts
declare namespace Express {
  export interface Request {
    requester: import("./models/Policy").Requester;
  }
  
  export interface Response {
    responseBody: any;
  }
}

Теперь у нас есть все объекты ввода запроса и тела ответа, хранящиеся в каждом журнале запросов!