From b2d501c935559ccfc54157a86a3f55c0f6c2b1ab Mon Sep 17 00:00:00 2001 From: Evgeny Chaban Date: Wed, 21 Feb 2024 13:40:47 +0300 Subject: [PATCH] feat: add metadata for logs (#280) --- template/apps/api/src/app.ts | 12 +++++-- template/apps/api/src/logger.ts | 34 ++++++++----------- .../route-error-handler.middleware.ts | 16 ++++++++- 3 files changed, 39 insertions(+), 23 deletions(-) diff --git a/template/apps/api/src/app.ts b/template/apps/api/src/app.ts index 532d7471..64161ca6 100644 --- a/template/apps/api/src/app.ts +++ b/template/apps/api/src/app.ts @@ -12,7 +12,7 @@ import cors from '@koa/cors'; import bodyParser from 'koa-bodyparser'; import helmet from 'koa-helmet'; import qs from 'koa-qs'; -import requestLogger from 'koa-logger'; +import koaLogger from 'koa-logger'; import { AppKoa } from 'types'; @@ -41,7 +41,13 @@ const initKoa = () => { ctx.throw(422, 'Unable to parse request JSON.'); }, })); - app.use(requestLogger()); + app.use(koaLogger({ + transporter: (message, args) => { + const [, method, endpoint, status, time, length] = args; + + logger.http(message.trim(), { method, endpoint, status, time, length }); + }, + })); routes(app); @@ -66,7 +72,7 @@ const app = initKoa(); await Promise.all(connections); server.listen(config.PORT, () => { - logger.info(`API server is listening on ${config.PORT}, in ${config.APP_ENV} environment`); + logger.info(`API server is listening on ${config.PORT} in ${config.APP_ENV} environment`); }); })(); diff --git a/template/apps/api/src/logger.ts b/template/apps/api/src/logger.ts index 747c931d..4223775e 100644 --- a/template/apps/api/src/logger.ts +++ b/template/apps/api/src/logger.ts @@ -1,19 +1,24 @@ +import _ from 'lodash'; import winston from 'winston'; import config from 'config'; -const formatToPrettyJson = winston.format.printf(info => { - if (typeof info.message.constructor === 'object' || typeof info.message.constructor === 'function') { - info.message = JSON.stringify(info.message, null, 4); +const formatToPrettyJson = winston.format.printf(({ level, message }) => { + if (_.isPlainObject(message)) { + message = JSON.stringify(message, null, 4); } - return `${info.level}: ${info.message}`; + return `${level}: ${message}`; }); const getFormat = (isDev: boolean) => { if (isDev) { return winston.format.combine( - winston.format.colorize(), + winston.format.colorize({ + colors: { + http: 'cyan', + }, + }), winston.format.splat(), winston.format.simple(), formatToPrettyJson, @@ -23,35 +28,26 @@ const getFormat = (isDev: boolean) => { return winston.format.combine( winston.format.errors({ stack: true }), winston.format.timestamp(), + winston.format.splat(), winston.format.json(), ); }; -const createConsoleLogger = (isDev: boolean) => { +const createConsoleLogger = (isDev = false) => { const transports: winston.transport[] = [ new winston.transports.Console({ - level: isDev ? 'debug' : 'info', - stderrLevels: [ - 'emerg', - 'alert', - 'crit', - 'error', - ], + level: isDev ? 'debug' : 'verbose', }), ]; - const logger = winston.createLogger({ + return winston.createLogger({ exitOnError: false, transports, format: getFormat(isDev), }); - - logger.debug('[Logger] Configured console based logger'); - - return logger; }; -const consoleLogger = createConsoleLogger(config.IS_DEV); +const consoleLogger = createConsoleLogger(config?.IS_DEV ?? process.env.APP_ENV === 'development'); global.logger = consoleLogger; diff --git a/template/apps/api/src/routes/middlewares/route-error-handler.middleware.ts b/template/apps/api/src/routes/middlewares/route-error-handler.middleware.ts index dac1ddfb..5f4a7d7f 100644 --- a/template/apps/api/src/routes/middlewares/route-error-handler.middleware.ts +++ b/template/apps/api/src/routes/middlewares/route-error-handler.middleware.ts @@ -1,6 +1,9 @@ import { AppKoaContext, Next, ValidationErrors } from 'types'; +import { userService } from 'resources/user'; + import logger from 'logger'; +import config from 'config'; interface CustomError extends Error { status?: number; @@ -18,7 +21,18 @@ const routeErrorHandler = async (ctx: AppKoaContext, next: Next) => { const serverError = { global: typedError.message || 'Unknown error' }; const errors = clientError || serverError; - logger.error(errors); + + let loggerMetadata = {}; + + if (!config.IS_DEV) { + loggerMetadata = { + requestBody: ctx.request.body, + requestQuery: ctx.request.query, + user: userService.getPublic(ctx.state.user), + }; + } + + logger.error(JSON.stringify(errors, null, 4), loggerMetadata); if (serverError && process.env.APP_ENV === 'production') { serverError.global = 'Something went wrong';