Logger
The Jüsto Logger is designed to enhance the standardization of logging across our services. By implementing a unified logging strategy, we can ensure consistency and reliability in how we capture, manage, and analyze log data. This logger not only simplifies debugging and monitoring processes but also enhances the overall traceability of our applications.
Each log entry automatically includes essential metadata such as the x-justo-trace-id and user information (if available). This feature significantly aids in tracing requests and understanding the flow of data through various components of our system. By leveraging this logger, teams can easily diagnose issues, monitor application behavior, and gain valuable insights into system performance.
Installation
Section titled “Installation”To install the Jüsto Logger, use npm:
npm install --save @justomx/loggerConstructor API
Section titled “Constructor API”All loggers must provide a “name” and an “asyncHook”.
import { BaseLogger, PinoLoggerService } from "@justomx/logger";
export const logger: BaseLogger = new PinoLoggerService({ name, asyncHook, level = "info", transport, }: { name: string; asyncHook: AsyncHook; level?: string; transport?: | TransportSingleOptions | TransportMultiOptions | TransportPipelineOptions; };Log Method API
Section titled “Log Method API”The log levels debug, info, and warn share the same signature:
logger.info({ event: string; msg: string; data?: any; req?: any; res?: any;});Level: error
Section titled “Level: error”logger.error({ err: any; event: string; msg: string; data?: any; req?: any; res?: any;});File Organization
Section titled “File Organization”Create a justo-logger.ts file and export an instance. You can use your AsyncHook implementation or the existing requestContext.
import { BaseLogger, PinoLoggerService } from '@justomx/logger'import { requestContext } from '@justomx/shared/lib/request-context-continuation/request-context'
export const logger: BaseLogger = new PinoLoggerService({ name: process?.env?.npm_package_name || '@justomx/logger', asyncHook: requestContext})Create a log-events.ts file to store all the events you will use in each log. Events should be written in “screaming snake case”:
export const PICKING_SUPPLIES_CONTROLLER = { ASSIGN_MANY: 'PICKING_SUPPLIES_CONTROLLER.ASSIGN_MANY', FIND_ALL: 'PICKING_SUPPLIES_CONTROLLER.FIND_ALL'}
export const PICKING_SUPPLIES_SERVICE = { ASSIGN_MANY: 'PICKING_SUPPLIES_SERVICE.ASSIGN_MANY', ASSIGN_ONE: 'PICKING_SUPPLIES_SERVICE.ASSIGN_ONE'}To use events, we recommend importing them as follows:
import { PICKING_SUPPLIES_CONTROLLER as events, logger } from '#shared'Middlewares
Section titled “Middlewares”Use ResponseMiddleware.addBodyToResponse to add the body to the response so it can be logged.
Use UserMiddleware.addUserToContext to add a user to the context so it can be logged.
import { ResponseMiddleware, UserMiddleware } from '@justomx/logger/lib'import { requestContext } from '@justomx/shared/lib/request-context-continuation/request-context'
const api = express()
api.use(UserMiddleware.addUserToContext(requestContext))api.use(ResponseMiddleware.addBodyToResponse)// Middlewares should be added before the app routersapi.use(myAppRouters)Examples
Section titled “Examples”Controller
Section titled “Controller”import { PICKING_SUPPLIES_CONTROLLER as events, logger } from '#shared'
export async function assignOne(req: Request, res: Response): Promise<void> { // The "req" logging automatically logs params, query, body, etc., so there's no need to add them in the data. logger.info({ event: events.ASSIGN_ONE, msg: 'Assign one, for (userId, orderId)', req }) try { const output = await this.service.assignOne(param1, param2) res.status(201).json(output) // Log the "res" after using the response. It automatically logs the "body", statusCode, etc., so there's no need to add it to the data. logger.info({ event: events.ASSIGN_ONE, msg: 'Assign one OK', res }) } catch (err) { // The error should always receive event, msg, and err. logger.error({ event: events.ASSIGN_ONE, msg: 'Assign one ERROR', err }) }}Service
Section titled “Service”import { PICKING_SUPPLIES_SERVICE as events, logger } from '#shared'
export async function assignOne( param1: string, param2: string, param3?: string, param4?: string): Promise<Supply> { // Use the "data" parameter to add optional relevant information that you consider necessary. logger.info({ event: events.ASSIGN_ONE, msg: 'Assign one supply to user', data: { param1, param2 } }) const result = await this.repository.insertOne(supply) return result}