diff --git a/api/sample.env b/api/sample.env index bf5178cceab..76a608df7e6 100644 --- a/api/sample.env +++ b/api/sample.env @@ -400,6 +400,16 @@ LOG_ENDING_EVENT_DISPATCH=true # type: String LOG_FOR_HUMANS=true + +# Logs for humans format +# +# "compact": Always display logs on a single line with key information. +# +# presence: optional +# type: String +# LOG_FOR_HUMANS_FORMAT=compact + + # Trace email sending in the mailer # DEBUG="pix:mailer:email" diff --git a/api/src/shared/config.js b/api/src/shared/config.js index 8381d8c88c0..d27a6e9c8d5 100644 --- a/api/src/shared/config.js +++ b/api/src/shared/config.js @@ -252,6 +252,7 @@ const configuration = (function () { enabled: toBoolean(process.env.LOG_ENABLED), logLevel: process.env.LOG_LEVEL || 'info', logForHumans: _getLogForHumans(), + logForHumansCompactFormat: process.env.LOG_FOR_HUMANS_FORMAT === 'compact', enableKnexPerformanceMonitoring: toBoolean(process.env.ENABLE_KNEX_PERFORMANCE_MONITORING), enableLogStartingEventDispatch: toBoolean(process.env.LOG_STARTING_EVENT_DISPATCH), enableLogEndingEventDispatch: toBoolean(process.env.LOG_ENDING_EVENT_DISPATCH), diff --git a/api/src/shared/infrastructure/utils/logger.js b/api/src/shared/infrastructure/utils/logger.js index 60cb03e4fa6..d02359db69b 100644 --- a/api/src/shared/infrastructure/utils/logger.js +++ b/api/src/shared/infrastructure/utils/logger.js @@ -1,3 +1,5 @@ +import isEmpty from 'lodash/isEmpty.js'; +import omit from 'lodash/omit.js'; import pino from 'pino'; import pretty from 'pino-pretty'; @@ -13,6 +15,8 @@ if (logging.logForHumans) { colorize: true, translateTime: omitDay, ignore: 'pid,hostname', + messageFormat: logging.logForHumansCompactFormat ? messageFormatCompact : undefined, + hideObject: logging.logForHumansCompactFormat, }); } @@ -25,4 +29,47 @@ const logger = pino( prettyPrint, ); +function messageFormatCompact(log, messageKey, _logLevel, { colors }) { + const message = log[messageKey]; + const { err, req, res, responseTime } = log; + + // compact log for errors + if (err) { + const stack = colors.red(err.stack); + return `${message}\n${stack}`; + } + + // compact log for HTTP requests + if (req && res) { + const method = req.method?.toUpperCase(); + + const queries = req.metrics?.knexQueryCount ? `sql:${req.metrics.knexQueryCount}` : ''; + const queriesTime = req.metrics?.knexTotalTimeSpent ? `sql-time:${req.metrics.knexTotalTimeSpent}` : ''; + + const statusCode = res.statusCode >= 400 ? colors.red(res.statusCode) : colors.greenBright(res.statusCode); + const request = colors.magentaBright([method, req.url].filter(Boolean).join(' ')); + const details = colors.yellow([queries, queriesTime].filter(Boolean).join(' ')); + const time = colors.gray(`(${responseTime}ms)`); + + return [statusCode, request, details, time].filter(Boolean).join(' - '); + } + + // compact log by default + const compactLog = omit(log, [ + messageKey, + 'id', + 'level', + 'time', + 'pid', + 'hostname', + 'uri', + 'address', + 'event', + 'started', + 'created', + ]); + const details = !isEmpty(compactLog) ? colors.gray(JSON.stringify(compactLog)) : ''; + return `${message} ${details}`; +} + export { logger };