1.0.8 • Published 5 months ago

@develop-x/nest-logger v1.0.8

Weekly downloads
-
License
-
Repository
-
Last release
5 months ago

@develop-x/nest-logger

Overview

@develop-x/nest-logger is a NestJS package that provides structured logging capabilities using Pino logger with OpenTelemetry integration. It offers high-performance logging with trace correlation and configurable output formats.

Installation

npm install @develop-x/nest-logger

Features

  • High Performance: Built on Pino, one of the fastest Node.js loggers
  • Structured Logging: JSON-formatted logs for better parsing and analysis
  • OpenTelemetry Integration: Automatic trace ID correlation in logs
  • Multiple Log Levels: Support for debug, info, warn, and error levels
  • Child Loggers: Create contextual child loggers with additional metadata
  • Pretty Printing: Human-readable output for development environments
  • Configurable: Flexible configuration options for different environments

Usage

Module Import

Import the LoggerModule in your application module:

import { Module } from '@nestjs/common';
import { LoggerModule } from '@develop-x/nest-logger';

@Module({
  imports: [
    LoggerModule.forRoot({
      level: 'info',
      serviceName: 'my-service',
    }),
  ],
})
export class AppModule {}

Basic Usage

Inject the LoggerService in your services or controllers:

import { Injectable } from '@nestjs/common';
import { LoggerService } from '@develop-x/nest-logger';

@Injectable()
export class UserService {
  constructor(private readonly logger: LoggerService) {}

  async createUser(userData: any) {
    this.logger.info('Creating new user', { userId: userData.id });

    try {
      const user = await this.userRepository.save(userData);
      this.logger.info('User created successfully', { 
        userId: user.id,
        email: user.email 
      });
      return user;
    } catch (error) {
      this.logger.error('Failed to create user', {
        error: error.message,
        userData,
        stack: error.stack
      });
      throw error;
    }
  }

  async getUserById(id: string) {
    this.logger.debug('Fetching user by ID', { userId: id });
    
    const user = await this.userRepository.findById(id);
    
    if (!user) {
      this.logger.warn('User not found', { userId: id });
      return null;
    }

    return user;
  }
}

API Reference

LoggerModule Configuration

forRoot(options: LoggerModuleOptions)

Configure the logger module with the following options:

interface LoggerModuleOptions {
  level?: 'debug' | 'info' | 'warn' | 'error';  // Log level (default: 'info')
  serviceName?: string;                          // Service name for logs
  prettyPrint?: boolean;                        // Enable pretty printing (default: false)
  destination?: string;                         // Log file destination (optional)
}

Example:

LoggerModule.forRoot({
  level: 'debug',
  serviceName: 'user-service',
  prettyPrint: process.env.NODE_ENV === 'development',
})

LoggerService Methods

info(message: any, ...args: any[])

Log informational messages.

this.logger.info('User logged in', { userId: '123', timestamp: new Date() });
this.logger.info('Processing request', requestData, additionalContext);

warn(message: any, ...args: any[])

Log warning messages.

this.logger.warn('Rate limit approaching', { 
  userId: '123', 
  currentRequests: 95, 
  limit: 100 
});

error(message: any, ...args: any[])

Log error messages.

this.logger.error('Database connection failed', {
  error: error.message,
  stack: error.stack,
  connectionString: 'postgres://...'
});

debug(message: any, ...args: any[])

Log debug messages (only shown when log level is 'debug').

this.logger.debug('Cache hit', { key: 'user:123', ttl: 300 });

child(bindings: Record<string, any>)

Create a child logger with additional context.

const requestLogger = this.logger.child({ 
  requestId: 'req-123',
  userId: 'user-456' 
});

requestLogger.info('Processing request');  // Will include requestId and userId
requestLogger.error('Request failed');     // Will include requestId and userId

Configuration Examples

Development Environment

LoggerModule.forRoot({
  level: 'debug',
  serviceName: 'my-service-dev',
  prettyPrint: true,  // Human-readable output
})

Production Environment

LoggerModule.forRoot({
  level: 'info',
  serviceName: 'my-service',
  prettyPrint: false,  // JSON output for log aggregation
})

File Logging

LoggerModule.forRoot({
  level: 'info',
  serviceName: 'my-service',
  destination: './logs/app.log',
})

Advanced Usage

Request Context Logging

Create request-scoped loggers with correlation IDs:

import { Injectable, Scope } from '@nestjs/common';
import { LoggerService } from '@develop-x/nest-logger';

@Injectable({ scope: Scope.REQUEST })
export class RequestLoggerService {
  private requestLogger: LoggerService;

  constructor(private readonly logger: LoggerService) {}

  setRequestContext(requestId: string, userId?: string) {
    this.requestLogger = this.logger.child({
      requestId,
      userId,
    });
  }

  info(message: string, data?: any) {
    this.requestLogger.info(message, data);
  }

  error(message: string, data?: any) {
    this.requestLogger.error(message, data);
  }
}

Middleware Integration

Log HTTP requests and responses:

import { Injectable, NestMiddleware } from '@nestjs/common';
import { Request, Response, NextFunction } from 'express';
import { LoggerService } from '@develop-x/nest-logger';

@Injectable()
export class LoggingMiddleware implements NestMiddleware {
  constructor(private readonly logger: LoggerService) {}

  use(req: Request, res: Response, next: NextFunction) {
    const start = Date.now();
    const requestId = req.headers['x-request-id'] || `req-${Date.now()}`;

    const requestLogger = this.logger.child({ requestId });

    requestLogger.info('Incoming request', {
      method: req.method,
      url: req.url,
      userAgent: req.headers['user-agent'],
      ip: req.ip,
    });

    res.on('finish', () => {
      const duration = Date.now() - start;
      
      requestLogger.info('Request completed', {
        method: req.method,
        url: req.url,
        statusCode: res.statusCode,
        duration,
      });
    });

    next();
  }
}

Error Logging with Context

import { Injectable } from '@nestjs/common';
import { LoggerService } from '@develop-x/nest-logger';

@Injectable()
export class PaymentService {
  constructor(private readonly logger: LoggerService) {}

  async processPayment(paymentData: any) {
    const paymentLogger = this.logger.child({
      paymentId: paymentData.id,
      userId: paymentData.userId,
      amount: paymentData.amount,
    });

    paymentLogger.info('Starting payment processing');

    try {
      // Payment processing logic
      const result = await this.paymentGateway.charge(paymentData);
      
      paymentLogger.info('Payment processed successfully', {
        transactionId: result.transactionId,
        status: result.status,
      });

      return result;
    } catch (error) {
      paymentLogger.error('Payment processing failed', {
        error: error.message,
        errorCode: error.code,
        stack: error.stack,
        gatewayResponse: error.response?.data,
      });

      throw error;
    }
  }
}

Log Output Examples

Development (Pretty Print)

[2023-12-01 10:30:00.123] INFO (my-service/12345): User logged in
    userId: "123"
    timestamp: "2023-12-01T10:30:00.123Z"
    traceId: "1234567890abcdef"

Production (JSON)

{
  "level": 30,
  "time": 1701423000123,
  "pid": 12345,
  "hostname": "server-01",
  "serviceName": "my-service",
  "msg": "User logged in",
  "userId": "123",
  "timestamp": "2023-12-01T10:30:00.123Z",
  "traceId": "1234567890abcdef"
}

Integration with Observability

OpenTelemetry Trace Correlation

The logger automatically includes OpenTelemetry trace IDs when available:

// Traces and logs will be correlated automatically
this.logger.info('Processing order', { orderId: '123' });
// Output includes: "traceId": "1234567890abcdef"

Log Aggregation

The structured JSON output works well with log aggregation systems:

  • ELK Stack: Elasticsearch, Logstash, Kibana
  • Fluentd: Log collection and forwarding
  • Grafana Loki: Log aggregation system
  • Datadog: Application monitoring platform

Performance Considerations

Asynchronous Logging

Pino uses asynchronous logging by default for better performance:

// This won't block the event loop
this.logger.info('High-frequency log message', largeDataObject);

Log Level Filtering

Set appropriate log levels for production to reduce overhead:

// Only log info, warn, and error in production
LoggerModule.forRoot({
  level: process.env.NODE_ENV === 'production' ? 'info' : 'debug',
})

Testing

Unit Testing with Logger

import { Test, TestingModule } from '@nestjs/testing';
import { LoggerService } from '@develop-x/nest-logger';
import { UserService } from './user.service';

describe('UserService', () => {
  let service: UserService;
  let logger: LoggerService;

  beforeEach(async () => {
    const module: TestingModule = await Test.createTestingModule({
      providers: [
        UserService,
        {
          provide: LoggerService,
          useValue: {
            info: jest.fn(),
            error: jest.fn(),
            warn: jest.fn(),
            debug: jest.fn(),
            child: jest.fn().mockReturnThis(),
          },
        },
      ],
    }).compile();

    service = module.get<UserService>(UserService);
    logger = module.get<LoggerService>(LoggerService);
  });

  it('should log user creation', async () => {
    const userData = { id: '123', email: 'test@example.com' };
    
    await service.createUser(userData);

    expect(logger.info).toHaveBeenCalledWith(
      'Creating new user',
      { userId: '123' }
    );
  });
});

Best Practices

  1. Structured Data: Always use objects for log data instead of string concatenation
  2. Consistent Fields: Use consistent field names across your application
  3. Sensitive Data: Never log sensitive information like passwords or tokens
  4. Performance: Use appropriate log levels to avoid performance impact
  5. Context: Include relevant context information in logs
  6. Child Loggers: Use child loggers for request-scoped or operation-scoped logging

Dependencies

  • @nestjs/common: NestJS common utilities
  • @nestjs/core: NestJS core functionality
  • nestjs-pino: NestJS integration for Pino logger
  • pino: Fast JSON logger for Node.js
  • pino-pretty: Pretty printer for Pino logs
  • @opentelemetry/api: OpenTelemetry API for trace integration

License

ISC

Support

For issues and questions, please refer to the project repository or contact the development team.

1.0.8

5 months ago

1.0.7

5 months ago

1.0.6

5 months ago

1.0.5

5 months ago

1.0.4

5 months ago

1.0.3

5 months ago

1.0.2

5 months ago

1.0.1

5 months ago

1.0.0

5 months ago