NestJs Pino Stackdriver
Pino logger for Nestjs adapted to be used with Stackdriver
About
Nestjs Pino Stackdriver exports a LoggerService that includes a logger context, as the default logger implementation
in Nest. It can be configured to log parts from the Request and to log parts of the default context-storage
of the application. You can also set custom (static) labels.
Futhermore:
- No need to call logger::setContext: if you have not called by yourself Logger::setContext, it uses the name of
the provider / controller class as logger context
- Use the logger as application logger
- Get the correlation-id in your provider or controllers with correlation-id decorators
- Build Dtos from any request part with BuildDto decorator
Basic Usage
You can continue using your LoggerModule as you were using it before 1.x:
import { Module } from '@nestjs/common';
import { CqrsModule } from '@nestjs/cqrs';
import {
LoggerModule,
} from 'nestjs-pino-stackdriver';
import { ExampleController } from './example.controller';
import { ExampleHandler } from './command/handler/example.handler';
@Module({
imports: [
LoggerModule,
CqrsModule,
],
controllers: [ExampleController],
providers: [ExampleHandler],
})
export class ExampleModule {}
You just need to stop using CorrelationTracerMiddleware, just call GcloudTraceService.start() instead:
import { NestFactory } from '@nestjs/core';
import { MyModule } from './my/my.module';
import { CorrelationTracerMiddleware } from 'nestjs-pino-stackdriver';
async function bootstrap() {
const app = await NestFactory.create(MyModule);
app.use(CorrelationTracerMiddleware({ app }));
await app.init();
}
bootstrap();
import { NestFactory } from '@nestjs/core';
import { MyModule } from './my/my.module';
import { GcloudTraceService } from 'nestjs-pino-stackdriver';
async function bootstrap() {
const app = await NestFactory.create(MyModule);
await app.init();
}
GcloudTraceService.start();
bootstrap();
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-stackdriver';
import { ExampleCommand } from './command/impl/example.command';
@Controller()
export class ExampleController {
constructor(
private readonly commandBus: CommandBus,
private readonly logger: Logger,
) {
}
@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.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 { createStackdriverLoggerTool, GcloudTraceService } from 'nestjs-pino-stackdriver';
import { MyModule } from './my.module';
async function bootstrap() {
const app = await NestFactory.create(MyModule);
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 { createStackdriverLoggerTool, GcloudTraceService } from 'nestjs-pino-stackdriver';
import { MyModule } from './my.module';
import { myLoggerConfig } from './my-logger.config';
async function bootstrap() {
const app = await NestFactory.create(MyModule, {logger: createStackdriverLoggerTool(myLoggerConfig)});
return await app.listen(3000);
}
GcloudTraceService.start();
bootstrap();
Adding custom labels to your logs
You can configure your logger to include environment, request and context labels on each log:
import { Module } from '@nestjs/common';
import { CqrsModule } from '@nestjs/cqrs';
import {
LoggerModule,
} from 'nestjs-pino-stackdriver';
import { ExampleController } from './example.controller';
import { ExampleHandler } from './command/handler/example.handler';
@Module({
imports: [
LoggerModule.register({
labels: {
env: [
{ label: 'project', value: 'example' },
{ label: 'node-env', path: 'NODE_ENV' },
],
request: [
{
label: 'content-type',
pick: { headers: ['content-type'] },
},
{
label: 'entity-id',
pick: [
{ query: ['fallback-id'], body: ['entity-id', 'id'] },
{ query: ['override-id'] },
],
},
{
label: 'deep-in-body',
pick: [{ body: ['deep.id'] }],
path: 'deep-in-body.id',
},
],
},
}),
CqrsModule,
],
controllers: [ExampleController],
providers: [ExampleHandler],
})
export class ExampleModule {}
Please refer to nestjs-pino-stackdriver for further information
about the configuration of the logger.
Advanced Usage
If you want to use the logger without using the CorrelationIdModule or the GcloudTraceModule,
use the PinoContextModule instead:
import { Module } from '@nestjs/common';
import {
PinoContextModule,
PredefinedConfig,
CorrelationIdModule,
GcloudTraceModule,
} from 'nestjs-pino-stackdriver';
@Module({
imports: [
CorrelationIdModule.register(),
GcloudTraceModule,
PinoContextModule.register({
base: PredefinedConfig.STACKDRIVER,
labels: {
env: [
{ label: 'project', value: 'example' },
],
request: [
{
label: 'content-type',
pick: { headers: ['content-type'] },
},
],
},
}),
]
(...)
import { NestFactory } from '@nestjs/core';
import { GcloudTraceService, PinoContextLogger } from 'nestjs-pino-stackdriver';
import { OnboardingModule } from './onboarding/my.module';
async function bootstrap() {
const app = await NestFactory.create(MyModule);
await app.listen(3000);
}
GcloudTraceService.start();
bootstrap();
You need to inject the PinoContextLogger instead of the default Logger:
import { Controller, Post, Body } from '@nestjs/common';
import { CommandBus } from '@nestjs/cqrs';
import { PinoContextLogger } from 'nestjs-pino-stackdriver';
import { ExampleCommand } from './command/impl/example.command';
@Controller()
export class ExampleController {
constructor(
private readonly commandBus: CommandBus,
private readonly logger: PinoContextLogger,
) {
}
@Post('/example')
async example(
@Body()
command: ExampleCommand,
) {
this.logger.log('Simple message');
return this.commandBus.execute(command);
}
}
Correlation-Id
This project include several decorators to get the correlation-id into your providers and controllers:
- As event metadata (note: this decorator converts your event.metadata into an accessor descriptor instead of data
descriptor)
import { CorrelationIdMetadata } from 'nestjs-correlation-id';
@CorrelationIdMetadata()
export class ExampleEvent {
constructor(public readonly data: any, public readonly metadata: any) {}
}
- Into a class property (note: this decorator converts your object.property into an accessor descriptor instead of data
descriptor)
import { CorrelationId } from 'nestjs-pino-stackdriver';
export class InternalServerErrorException {
@CorrelationId()
private readonly correlationId;
}
- Into a sub-property for a class property
import { AddCorrelationId } from 'nestjs-pino-stackdriver';
@AddCorrelationId('property.correlation_id')
export class ExampleClass {
private readonly property = {};
}
BuildDto
Use the BuildDto decorator to facilitate the creation of your CQRS commands or your DTOs:
import { Controller, Post } from '@nestjs/common';
import { CommandBus } from '@nestjs/cqrs';
import { BuildDto } from 'nestjs-pino-stackdriver';
import { ExampleCommand } from './example/src/command/impl/example.command';
@Controller()
export class ExampleController {
constructor(
private readonly commandBus: CommandBus,
) {}
@Post('/example')
async example(
@BuildDto({
query: false,
body: Object.getOwnPropertyNames(new ExampleCommand()),
headers: ['x-correlation-id'],
})
command: ExampleCommand,
) {
return this.commandBus.execute(command);
}
Further Configuration
Reporting issues
Create an issue.
Ressources
Examples
There is a full working example in the directory "example" of each project inside this project:
TODO
- Separate the 5 libs into 5 repositories
- Add working example in this module (we have an example for each submodule, but not for the main one)
- Add unit tests for LoggerService and LoggerModule
- Add node version engine dependency ?