How can I use fastify request logger in other classes without having to pass it as a parameter?
Asked Answered
B

3

7

I'm new in nodejs, I'm using fastify and I want to be able to use the req.logger in all the classes functions of the flow, this because I have a the request-id on req.logger, the first solution that came to my mind is to pass as a parameter the logger through all the function/classes but I think that would make the code kind of dirty, here is an example of my code:

app.ts

import pino from 'pino';
import fastify from 'fastify';

declare module 'fastify' {
  interface FastifyInstance {
    // augment fastify instance with the config object types
    config: Config;
  }
}

function build() {

  const app = fastify({
    logger: pino({
      name: process.env.NAME,
      level: process.env.LOG_LEVEL,
    }),
    disableRequestLogging: true,
    requestIdHeader: 'correlation-id',
    requestIdLogLabel: 'correlationId',
  });
  // register plugins 
  
  app.register(apiRoutes, fastify => ({
    getObjectUseCase: new GetObjectUseCase(
      new TestClass()),
  }));

  return app;
}
export { build };

routes.ts

import { FastifyPluginCallback } from 'fastify';
import { StatusCodes } from 'http-status-codes';

export const apiRoutes: FastifyPluginCallback<RoutesOpts> = async (fastify, options, done) => {
  const getObjectUseCase = options.getObjectUseCase; 

  fastify.get<object>('/v1/api/:id', async (req, reply) => {
    const id = req.params.payoutId;
    req.logger.info('This is a logger print'); // has the correlation id inside it while printing
    const storedObject = await getObjectCase.execute(id);
    reply.code(StatusCodes.OK).send(storedObject);
  });
}

GetObjectUseCase.ts

export class GetObjectUseCase {
  private anotherClass: TestClass;

  constructor(anotherClass: TestClass) {
    this. anotherClass = anotherClass;
  }

  async execute(id: string): Promise<StoredObject> {
    // I want to use the logger here with have the correlation id on it without having to pass it as an argument on the method, how is it posible?
    return this.anotherClass.getById(id);
    // also needed to use it inside anotherClass.getById so I will need to pass the logger also in the method

  }
}

Hope I have been clear. Thanks!

Barbiebarbieri answered 16/6, 2022 at 15:36 Comment(2)
Did you call new GetObjectUsecase? You could set as construction parameterGambrel
@ManuelSpigolon thanks for the response, sorry just updated the question, the GetObjectUseCase instance is created while registering the routes, if I put the logger on the constructor param I would need to create a new instance of the object on each request and also propagate the logger to all the classes/funtions that are used inside GetObjectUseCase, that's what I'm trying to avoid, do you know any other possible workaround?Trademark
R
6

This may not be the best or only way to do it, but this has worked for me in the past.

Typically I structure my projects with an app.ts that just instantiates my FastifyInstance and then exports the log from that created instance. This allows me to use the log where ever I want to.

It looks something like this.

app.ts

import fastify from 'fastify';

const app = fastify({ logger: true /* Your logging configuration */ });

export default app;
export const logger = app.log; // Allows me to log where ever I want.

server.ts

import app from './app';

... // All your fastify configuration and other stuff.

app.listen({ ... });

Now I can use the logger outside of fastify stuff.

get-object-use-case.ts

import { logger } from './app'; // Import your fastify logger to use in this class.

export class GetObjectUseCase {
  private anotherClass: TestClass;

  constructor(anotherClass: TestClass) {
    this. anotherClass = anotherClass;
  }

  async execute(id: string): Promise<StoredObject> {
    logger.info({/* Whatever you want to log here. */}); // Now you can use the logger here.
    return this.anotherClass.getById(id); // You can just import the logger into the TestClass file to get logging enabled there.
  }
}

This even allows you to log before your FastifyInstance is started. Check out this codesandbox for a running example.

Rosamariarosamond answered 11/9, 2022 at 2:41 Comment(0)
P
0

My solution

/utils/logger.ts

import { FastifyBaseLogger } from 'fastify';

export const logger: {
  instance: FastifyBaseLogger | null;
  init: (fastifyLogger: FastifyBaseLogger) => void;
  get: () => FastifyBaseLogger | null;
} = {
  instance: null,
  init: (fastifyLogger: FastifyBaseLogger) => (logger.instance = fastifyLogger),
  get: () => logger.instance,
};

/app.ts

export const app = (options: { logger: { level?: LogLevel } | boolean }) => {
  const fastify = Fastify({
    ...options,
  });

  ...
  logger.init(fastify.log);
  ...

  return fastify;
};

randomFile.ts

import { logger } from 'src/utils/logger';

const randomFunction = () => {
...
  logger.get()?.debug('🛠️ ~ Test debug log');
...
}
Permeability answered 9/9, 2023 at 18:25 Comment(0)
V
0

I have a solution which improves on Илья Хоришко's solution. It defines a logger class that then calls the desired logger instance.

The app initialization works the same as in their example.

logger.ts

import { FastifyBaseLogger } from "fastify";
// @ts-expect-error abstract-logging has no types
import nullLogger from "abstract-logging";

class Logger {
  private instance: FastifyBaseLogger = nullLogger;

  constructor() {
    this.instance = nullLogger;
  }

  init(fastifyLogger: FastifyBaseLogger) {
    this.instance = fastifyLogger;
  }

  info(obj: object | unknown, msg?: string, ...args: string[]) {
    this.instance?.info(obj, msg, ...args);
  }
  error(obj: object | unknown, msg?: string, ...args: string[]) {
    this.instance?.error(obj, msg, ...args);
  }
  fatal(obj: object | unknown, msg?: string, ...args: string[]) {
    this.instance?.fatal(obj, msg, ...args);
  }
  warn(obj: object | unknown, msg?: string, ...args: string[]) {
    this.instance?.warn(obj, msg, ...args);
  }
  debug(obj: object | unknown, msg?: string, ...args: string[]) {
    this.instance?.debug(obj, msg, ...args);
  }
  trace(obj: object | unknown, msg?: string, ...args: string[]) {
    this.instance?.trace(obj, msg, ...args);
  }
}

export const logger = new Logger();

It can then be used in randomFile.ts:

import { logger } from 'path/to/logger.js';

const randomFunction = () => {
    logger.debug('🛠️ ~ Test debug log');
}
Vex answered 10/10, 2024 at 23:8 Comment(0)

© 2022 - 2025 — McMap. All rights reserved.