2.1.0 • Published 5 months ago

@empe/logger v2.1.0

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

@empe/logger

A flexible and configurable logging utility for Node.js applications based on Winston, providing structured logging with environment-specific formatting, log rotation, and sensitive data redaction.

Overview

This package provides a standardized logging solution for the Empe ecosystem, making it easy to implement consistent logging across different services and applications. It's built on top of Winston, one of the most popular logging libraries for Node.js.

Features

  • Environment-aware formatting: Different log formats for development and production environments
  • Log rotation: Automatic daily log file rotation with compression
  • Sensitive data redaction: Automatic redaction of sensitive information like passwords and tokens
  • Multiple transports: Console output and file logging
  • Exception handling: Dedicated exception logging
  • Configurable log levels: Support for standard Winston log levels
  • Structured metadata: Support for structured metadata in log entries

Installation

npm install @empe/logger
# or
yarn add @empe/logger

Usage

Basic Usage

import { createLoggerInstance, LogLevelEnum } from '@empe/logger';

// Create a logger for your application
const logger = createLoggerInstance(
  'MyApp',           // Application name
  'development',     // Node environment
  LogLevelEnum.DEBUG // Log level
);

// Use the logger
logger.info('Application started');
logger.debug('Debug information', { userId: 123, action: 'login' });
logger.error('An error occurred', { error: new Error('Something went wrong') });

Integration with Environment Variables

Typically, you'll want to configure the logger based on environment variables:

import { createLoggerInstance, LogLevelEnum } from '@empe/logger';

const nodeEnv = (process.env.NODE_ENV || 'development') as 'development' | 'production' | 'test';
const logLevel = (process.env.LOG_LEVEL || LogLevelEnum.INFO) as LogLevelEnum;
const logDir = process.env.LOG_DIR || 'logs';

const logger = createLoggerInstance(appName, nodeEnv, logLevel, logDir);

Using with Base App

If you're using the @empe/base-app package, you can use the simplified logger creation function:

import { createLogger } from '@empe/base-app/tools';

// This will automatically use environment variables for configuration
const logger = createLogger('MyService');

API Reference

createLoggerInstance

The main function to create a new logger instance.

function createLoggerInstance(
  appName: string,
  nodeEnv: 'development' | 'production' | 'test',
  logLevel: LogLevel,
  logsDirname?: string,
  maxSize?: string,
  maxFiles?: string
): Logger

Parameters

  • appName: The name of your application (will be included in log entries)
  • nodeEnv: The Node.js environment ('development', 'production', or 'test')
  • logLevel: The minimum log level to record
  • logsDirname: (Optional) Directory to store log files (default: 'logs')
  • maxSize: (Optional) Maximum size of each log file before rotation (default: '10m')
  • maxFiles: (Optional) Maximum number of days to keep log files (default: '60d')

Returns

A configured Winston logger instance.

LogLevelEnum

An enum of valid log levels:

enum LogLevelEnum {
  ERROR = 'error',
  WARN = 'warn',
  INFO = 'info',
  HTTP = 'http',
  VERBOSE = 'verbose',
  DEBUG = 'debug',
  SILLY = 'silly',
}

Log Formatting

Development Environment

In development, logs are colorized and formatted for readability:

[2023-05-15 10:15:30.123 AM] info [MyApp]: User logged in {"userId":123,"role":"admin"}

Production Environment

In production, logs are more compact and include sanitized metadata:

[2023-05-15 10:15:30.123 AM] info [MyApp] User logged in {"userId":123,"role":"admin","token":"[REDACTED]"}

Log File Management

Logs are stored in the specified directory with the following structure:

  • Daily log files: app-YYYY-MM-DD.log
  • Exception logs: exceptions.log

Old log files are automatically compressed and eventually deleted based on the maxFiles setting.

Security Features

The logger automatically redacts sensitive information in metadata fields with names like:

  • password
  • token
  • secret
  • key

This helps prevent accidental logging of sensitive data.

Best Practices

  1. Use appropriate log levels:

    • error: For errors that affect functionality
    • warn: For concerning but non-critical issues
    • info: For important operational events
    • debug: For detailed troubleshooting information
    • verbose/silly: For very detailed debugging
  2. Include structured metadata:

    // Good
    logger.info('User authenticated', { userId: 123, method: 'oauth' });
    
    // Not as useful
    logger.info(`User 123 authenticated via oauth`);
  3. Configure log levels by environment:

    • Development: debug or verbose
    • Testing: info
    • Production: info or warn

Integration with Empe Ecosystem

The @empe/logger package is designed to work seamlessly with other packages in the Empe ecosystem:

Integration with Base App

The @empe/base-app package provides a simplified interface to create loggers with environment-based configuration:

// In @empe/base-app/tools/logger.ts
import { createLoggerInstance } from '@empe/logger';
import { getLogLevel, getNodeEnv, getLogsDir } from './env-manager.js';

export const createLogger = (name: string) =>
  createLoggerInstance(name, getNodeEnv(), getLogLevel(), getLogsDir());

Usage in Services

All Empe services use the logger in a consistent way:

// In apps/verifier-service/src/logger.ts
import { createLogger } from '@empe/base-app/tools';

export const logger = createLogger('VerifierService');

// Then in other files
import { logger } from './logger.js';

logger.info('Service starting up');

Error Middleware Integration

The logger integrates with the error handling middleware in @empe/base-app:

// In @empe/base-app/middlewares/error-middleware.ts
export const errorMiddleware = (logger: Logger): ErrorRequestHandler => {
  return (err: Error | BaseAppError, req: Request, res: Response, __: NextFunction) => {
    // Log errors with appropriate level and context
    if (err instanceof BaseAppError) {
      logger.warn('OAuth error occurred', { /* error details */ });
    } else {
      logger.error('An unknown error occurred', { /* error details */ });
    }
    // Send error response
    // ...
  };
};

Environment Configuration

The logger can be configured through environment variables:

# The verbosity level for application logging
# Valid options: error, warn, info, http, verbose, debug, silly
LOG_LEVEL=debug

# The directory where application logs are stored
LOG_DIR=logs

# The current environment (affects log formatting)
NODE_ENV=development

Extending the Logger

Since the logger is built on Winston, you can extend it with custom formatters, transports, or other Winston features.

Adding Custom Transports

import { createLoggerInstance, LogLevelEnum } from '@empe/logger';
import { transports } from 'winston';

// Create the base logger
const logger = createLoggerInstance('MyApp', 'development', LogLevelEnum.DEBUG);

// Add a custom transport
logger.add(new transports.Http({
  host: 'logging-service.example.com',
  port: 8080,
  path: '/logs',
  ssl: true
}));

Creating a Custom Formatter

import { createLoggerInstance, LogLevelEnum } from '@empe/logger';
import { format } from 'winston';

// Create a custom formatter
const customFormat = format.combine(
  format.timestamp(),
  format.json(),
  format.printf(info => {
    return `${info.timestamp} [${info.service}] ${info.level}: ${info.message}`;
  })
);

// Create the logger
const logger = createLoggerInstance('MyApp', 'development', LogLevelEnum.DEBUG);

// Create a new console transport with custom format
const consoleTransport = new transports.Console({
  format: customFormat
});

// Remove default console transport and add the custom one
logger.clear(); // Remove all transports
logger.add(consoleTransport); // Add the custom console transport

// If you need file transport as well, add it back
logger.add(new transports.DailyRotateFile({
  filename: 'app-%DATE%.log',
  dirname: 'logs',
  format: customFormat
}));

Integration with Third-Party Services

You can integrate the logger with services like Sentry, Loggly, or Elasticsearch:

import { createLoggerInstance, LogLevelEnum } from '@empe/logger';
import winston from 'winston';
import winstonSentry from 'winston-sentry';

const logger = createLoggerInstance('MyApp', 'production', LogLevelEnum.INFO);

// Add Sentry transport
logger.add(new winstonSentry({
  dsn: 'your-sentry-dsn',
  level: 'error',
  tags: { service: 'my-service' }
}));

License

MIT

2.1.0

5 months ago

2.0.0

8 months ago

1.2.0

8 months ago

1.1.0

9 months ago

0.0.12

9 months ago

0.0.1

9 months ago