npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

nestjs-pino-stackdriver

v2.2.1

Published

NestJS Pino logger with Stackdriver support

Downloads

1,771

Readme

NestJs Pino Stackdriver

Pino logger for Nestjs that logs context and execution-context labels using nest-context

About

The logger exported in this project implements the LoggerService interface of Nest and includes the logger context, as the default logger implementation in Nest. It can be configured to log labels from the execution context of the application.

Furthermore:

  • If you have not called by yourself Logger::setContext, it adds the name of the provider / controller class using the logger as context
  • It includes predefined logger formats (fex: "stackdriver")
  • It includes a tool that allows to use the logger as your application logger
  • It includes a module GcloudTraceModule that allows to add the gcloud trace-url to the context and response headers

Basic Usage

Include the module as an import into your main module:

import { Module } from '@nestjs/common';
import { CqrsModule } from '@nestjs/cqrs';
import { LoggerModule } from 'nestjs-pino-context';
import { ExampleController } from './example.controller';
import { ExampleHandler } from './command/handler/example.handler';

@Module({
  imports: [
    CqrsModule,
    LoggerModule,
  ],
  controllers: [ExampleController],
  providers: [ExampleHandler],
})
export class ExampleModule {}

Now you can inject the logger in your providers or controllers and use it:

import { Controller, Post, Body } from '@nestjs/common';
import { CommandBus } from '@nestjs/cqrs';
import { Logger } from 'nestjs-pino-context';
import { ExampleCommand } from './command/impl/example.command';

@Controller()
export class ExampleController {
  constructor(
    private readonly commandBus: CommandBus,
    private readonly logger: Logger,
  ) {
    // if you do not call setContext, "ExampleController" will be used as context
    // logger.setContext('my custom context');
  }

  @Post('/example')
  async example(
    @Body()
    command: ExampleCommand,
  ) {
    this.logger.verbose('Simple verbose message');
    this.logger.debug({
      msg: 'Object-like debug message',
      sample: 'another field',
    });
    this.logger.warn('Warning passing custom context', 'custom-context');
    this.logger.setLabel(
            'my-custom-label',
            'my-custom-label for my logger.error',
    );
    this.logger.error(
      'Error',
      `An error trace`,
    );
    this.logger.log(
      'An interpolation message: %o correlation-id %s',
      undefined,
      { try: 1 },
      'xxx',
    );

    return this.commandBus.execute(command);
  }
}

Application Logger

You can use the logger to log your application logs:

import { NestFactory } from '@nestjs/core';
import { GcloudTraceService } from 'nestjs-gcloud-trace';
import { createLoggerTool, createStackdriverLoggerTool } from 'nestjs-pino-context';
import { MyModule } from './my.module';

async function bootstrap() {
  const app = await NestFactory.create(MyModule);
  
  // Pass the app and it will instantiate your logger from the DI
  app.useLogger(createStackdriverLoggerTool(app));
  return await app.listen(3000);
}
GcloudTraceService.start();
bootstrap();

You can use also use the logger to log your application + initialization logs:

import { NestFactory } from '@nestjs/core';
import { GcloudTraceService } from 'nestjs-gcloud-trace';
import { createLoggerTool } from 'nestjs-pino-context';
import { MyModule } from './my.module';
import { myLoggerConfig } from './my-logger.config';

async function bootstrap() {
  const app = await NestFactory.create(MyModule, {logger: createStackdriverLoggerTool()});
  // You could also use a custom logger createLoggerTool instead of the stackdriver one
  const app2 = await NestFactory.create(MyModule, {logger: createLoggerTool(myLoggerConfig)});
  
  return await app.listen(3000);
}
GcloudTraceService.start();
bootstrap();

Further Configuration

When you register the Logger, you can pass as configuration either a string representing the name of one of the bundled configurations (Fex: 'stackdriver'), or an object containing zero or more of:

  • base?: PredefinedConfigOptionType: A string representing one of the bundled configurations (``Fex: 'stackdriver'`). The "base" configuration will be loaded and customised with the rest of given the configurations.
  • loggerOptions?: LoggerOptions: Pino logger can be configured using LoggerOptions
  • logFieldNames?: { context: string; labels: string; trace: string; }: Can be used to override the logger output names of the context, labels and error trace fields
  • context?: Configuration for nestjs-context
import { Module } from '@nestjs/common';
import { CqrsModule } from '@nestjs/cqrs';
import { LoggerModule } from 'nestjs-pino-context';
import { ExampleController } from './example.controller';
import { ExampleHandler } from './command/handler/example.handler';

@Module({
  imports: [
    CqrsModule,
    LoggerModule.register({
      
    }),
  ],
  controllers: [ExampleController],
  providers: [ExampleHandler],
})
export class ExampleModule {}

The default Logger uses the stackdriver as default bundled configuration. If you want an empty configuration by default, you can use PinoContextLogger instead:

Gcloud Trace Module

GcloudTraceModule uses internally nest-context to store the trace url (something like "projects//traces/"), so you can use the context later to show your trace-url in your logs.

Furthermore, it allows to:

  • Get the cloud trace agent anywhere in your code as a DI (see GcloudTracerService)
  • Force requests to be traced (using a middleware that sets the "x-cloud-trace-context" header)

Basic Usage

Include the module as an import into your main module:

import { Module } from '@nestjs/common';
import { ConfigModule } from '@nestjs/config';
import { GcloudTraceModule } from 'nestjs-gcloud-trace';
import { ExampleController } from './example.controller';

@Module({
  imports: [ConfigModule.forRoot(), GcloudTraceModule],
  controllers: [ExampleController],
  providers: [],
})
export class ExampleModule {}

Call GcloudTraceService::start (static method) before your Nest bootstrap:

import { NestFactory } from '@nestjs/core';
import { GcloudTraceService } from 'nestjs-gcloud-tracer';
import { ExampleModule } from './example.module';

async function bootstrap() {
  const app = await NestFactory.create(ExampleModule);
  await app.listen(9191);
}
GcloudTraceService.start();
bootstrap();

Now now you can the gcloud trace context id from your headers and you can use the context key exported within this module to get the current trace url from the default context:

import { Controller, Get } from '@nestjs/common';
import { Context } from 'nestjs-context';
import {
  CONTEXT_GCLOUD_TRACE,
  HEADER_GCLOUD_TRACE_CONTEXT_ID,
} from 'nestjs-gcloud-trace/constants';

@Controller()
export class ExampleController {
  constructor(
    private readonly context: Context,
  ) {}

  @Get('/example')
  async example(@Headers('HEADER_GCLOUD_TRACE_CONTEXT_ID') header: string) {
    return `Your Gcloud Trace url is ${this.context.get(
      CONTEXT_GCLOUD_TRACE,
    )} and your current context id is ${header}`;
  }
}

You can also use the gcloud trace agent directly:

import { Controller, Get } from '@nestjs/common';
import { GcloudTraceService } from 'nestjs-gcloud-trace';

@Controller()
export class ExampleController {
  constructor(
    private readonly gcloudTracerService: GcloudTraceService,
  ) {}

  @Get('/example')
  async example() {
    return `Your Gcloud trace current context id is ${this.gcloudTracerService.get().getCurrentContextId();
  }
}

Advanced Usage

You may want one, multiple or all requests to be traced by Gcloud Trace: this module includes a middleware that allows to filter requests to force them to be traced:

import { NestFactory } from '@nestjs/core';
import { Request } from 'express';
import { GcloudTraceService, forceRequestToBeTracedMiddleware } from 'nestjs-gcloud-trace';
import { ExampleModule } from './example.module';

async function bootstrap() {
  const app = await NestFactory.create(ExampleModule);
  // example: force all "GET" requests to be traced
  app.use(
    forceRequestToBeTracedMiddleware(
      app.get(GcloudTraceService),
      (req: Request) => req.method === 'GET',
    ),
  );
  await app.listen(9191);
}
GcloudTraceService.start();
bootstrap();

Reporting issues

Create an issue.

Resources

Examples

There is a full working example in the directory "example" of this project (here!).

Use "yarn start" to execute the example script (from the "example" directory):

yarn install
NODE_ENV=development yarn start

Now you can open another terminal and execute a curl to see the results of a POST:

curl --location --request POST 'http://127.0.0.1:9191/example/param-id' \
--header 'Content-Type: application/json' \
--data-raw '{
    "id": "body-id",
    "override-id": "override-id",
    "deep": {"id": "deep-id"}
}'

Try now with a different NODE_ENV environment variable and a different CURL, for example:

curl --location --request POST 'http://127.0.0.1:9191/example/param-id?fallback-id=ok' \
--header 'Content-Type: application/json' \
--data-raw '{
    "id": "body-id",
    "override-id": "override-id",
    "block-override": 1
}'

If you want to include GCloud Trace:

  • Remember to change the value of the .env included in the example to correctly trace your application.
  • You can force the traces for all your GET requests with FORCE_REQUEST_TRACE=true.
  • Now you can open another terminal and execute a curl to see the results of a GET:
curl --location --request GET 'http://127.0.0.1:9191/example'
  • You can check your trace in the url:
https://console.cloud.google.com/traces/list?project=<project-id>&tid=<trace-id>

WIP

  • When we have created Nestjs-Gcloud-Trace module, for the default context config to use it
  • Test if it's working with Stackdriver. If tracing is not working, we need to change 'logging.googleapis.com/trace' field to be 3 fields instead: trace, spanId and traceSampled (as defined here). To be able to do that, we only need to add the 3 fields in the context key and to define deep-labels for each one

TODO

  • Unit Tests
  • Only tested with Express: try it on other platforms
  • RegisterAsync to be able to use configService for logger configs