@sqdgn/context-logging
v0.1.1
Published
SQDGN context logging library
Downloads
51
Readme
SQDGN Context Logging Library
Usage
Simple app w/o Kafka
main.ts
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
import { initializeLogger } from '@sqdgn/context-logging/logger';
import { ContextInterceptor } from '@sqdgn/context-logging/context';
// ...
async function bootstrap() {
const logger = initializeLogger({
appName: 'name-of-the-service',
// Optionally some other options...
});
const app = await NestFactory.create(AppModule, {
logger: logger.adapters.NestJs(),
});
app.useGlobalInterceptors(
new ContextInterceptor({
enableHttpDebugLogs: true, // optional
}),
);
// ...
await app.listen(process.env.PORT ?? 3000);
}
void bootstrap();app.module.ts
import { Module } from '@nestjs/common';
import { ExampleController } from './example.controller';
import { HttpRequestModule } from '@sqdgn/context-logging/http-request';
@Module({
imports: [
// ...
HttpRequestModule.register(),
],
controllers: [
// ...
ExampleController,
],
})
export class AppModule {}example.controller.ts
import { Controller, Get, Req } from '@nestjs/common';
import { Request } from 'express';
import { Context, LoggingContext } from '@sqdgn/context-logging/context';
import { HttpRequestService } from '@sqdgn/context-logging/http-request';
@Controller()
export class ExampleController {
constructor(private readonly httpRequestService: HttpRequestService) {}
@Get('example')
example(@LoggingContext() ctx: Context, @Req() req: Request) {
ctx.merge({ some_additional: 'context' });
ctx.logger.info('Something happened');
// Call another service and pass the current context
await this.httpRequestService.get(ctx, `http://some-other-service/api/v1/example`);
}
}Hybrid application with Kafka microservice
main.ts
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';
import { ContextInterceptor } from '@sqdgn/context-logging/context';
import { initializeLogger } from '@sqdgn/context-logging/logger';
import { KafkaOptions, Transport } from '@nestjs/microservices';
async function bootstrap() {
const logger = initializeLogger({
appName: 'name-of-the-service',
// Optionally some other options...
});
const app = await NestFactory.create(AppModule, {
logger: logger.adapters.NestJs(),
});
app.useGlobalInterceptors(
new ContextInterceptor({
enableHttpDebugLogs: true, // optional
enableKafkaDebugLogs: true, // optional
}),
);
app.connectMicroservice<KafkaOptions>(
{
transport: Transport.KAFKA,
options: {
client: {
clientId: 'name-of-the-service',
brokers: ['localhost:9092'],
},
consumer: {
groupId: 'name-of-the-service-consumer',
},
subscribe: {
fromBeginning: true,
},
// Optionally some other options...
},
},
// Important!
{ inheritAppConfig: true },
);
// ...
await app.startAllMicroservices();
await app.listen(process.env.PORT ?? 3000);
}
void bootstrap();app.module.ts
import { Module } from '@nestjs/common';
import { ClientsModule, KafkaOptions, Transport } from '@nestjs/microservices';
import { KafkaProducerModule, KAFKA_PRODUCER_CLIENT } from '@sqdgn/context-logging/kafka-producer';
import { ExampleController } from './example.controller';
@Module({
imports: [
// ...
ClientsModule.register({
isGlobal: true, // required
clients: [
{
name: KAFKA_PRODUCER_CLIENT,
transport: Transport.KAFKA,
options: {
client: {
clientId: 'name-of-the-service',
brokers: ['localhost:9092'],
},
producerOnlyMode: true,
// ... Optionally some other options
},
} satisfies KafkaOptions & { name: string },
],
}),
KafkaProducerModule,
],
controllers: [
// ...
ExampleController,
],
})
export class AppModule {}example.controller.ts
import { EventPattern, Payload } from '@nestjs/microservices';
import { Context, LoggingContext } from '@sqdgn/context-logging/context';
import { Controller } from '@nestjs/common';
import { KafkaProducerService } from '@sqdgn/context-logging/kafka-producer';
@Controller()
export class ExampleController {
constructor(private readonly kafkaProducerService: KafkaProducerService) {}
@EventPattern('example-kafka-topic')
handleKafkaMessage(@LoggingContext() ctx: Context, @Payload() actualMessage: unknown) {
ctx.merge({ some_additional: 'context' });
ctx.logger.info('Something happened');
// Use KafkaProducerService to pass the current context to the produced message
await this.kafkaProducerService.sendMessage<{ data: string }>({ data: 'some-value' }, ['other-kafka-topic']);
}
}Ways of retrieving the Context object
Using @LoggingContext() decorator in controllers
import { Context, LoggingContext } from '@sqdgn/context-logging/context';
import { Controller, Get } from '@nestjs/common';
@Controller()
export class ExampleController {
@Get('example')
example(@LoggingContext() ctx: Context) {
// ...
}
}From Request object (HTTP only)
import { Context } from '@sqdgn/context-logging/context';
import { Controller, Get, Req } from '@nestjs/common';
import { Request } from 'express';
@Controller()
export class ExampleController {
@Get('example')
example(@Req() req: Request) {
const ctx = req.context as Context;
// ...
}
}Using contextProvider (AsyncLocalStorage)
import { contextProvider } from '@sqdgn/context-logging/context';
export class ExampleService {
private get logger() {
const ctx = contextProvider.getContext();
return ctx.child({ service: ExampleService.name }).logger;
}
example() {
this.logger.info('Log something with current context');
// ...
}
}Logger Configuration
.env
LOG_LEVEL=debug
SENTRY_DSN={sentry_dsn_uri}
# Defaults to `RAILWAY_ENVIRONMENT_NAME`
SENTRY_ENVIRONMENT_NAME={environment_name}
GOOGLE_APPLICATION_CREDENTIALS={path_to_credentials_file}
# OR:
GCP_CREDENTIALS_B64={base64_encoded_credentials_json}- Omitting
SENTRY_DSNdisables logging to Sentry - Omitting both
GOOGLE_APPLICATION_CREDENTIALSandGCP_CREDENTIALS_B64disables logging to GCP
Default configuration
The default configuration sends:
- all logs of level >=
process.env.LOG_LEVEL(debugby default) to GCP, warn+ level logs to Sentry.
Changing log levels for GCP / Sentry
const logger = initializeLogger({
appName: 'name-of-the-service',
gcp: {
logLevel: 'warn', // Only send warn+ level logs to GCP
},
sentry: {
logLevel: 'error', // Only send error level logs to Sentry
},
});Using custom type filter for GCP logs
const logger = initializeLogger({
appName: 'name-of-the-service',
gcp: {
logLevel: 'error', // Only send error level logs to GCP
typeFilter: 'gcp', // Require `type: 'gcp'` being set in the log in order to send it to GCP
},
// ...
});