Category: 08. Winston

https://www.americanstandard.com.vn/webassets/images/icon-view-3d.jpg

  • Log Enrichment

    Enrich logs with additional context or metadata. For example, you might add user-specific information or request context.

    const winston = require('winston');
    
    const requestLogger = winston.createLogger({
      level: 'info',
      format: winston.format.combine(
    
    winston.format.timestamp(),
    winston.format.json(),
    winston.format.printf(({ level, message, timestamp, requestId }) => {
      return ${timestamp} [${level}] [RequestId: ${requestId}] ${message};
    })
    ), transports: [
    new winston.transports.Console()
    ] }); // Middleware to attach requestId app.use((req, res, next) => { req.requestId = Math.random().toString(36).substring(2); next(); }); // Log with requestId app.get('/', (req, res) => { requestLogger.info('Handled request', { requestId: req.requestId }); res.send('Hello World!'); });
  • Integration with External Services

    Elasticsearch Integration

    Elasticsearch is commonly used for log storage and search. To integrate Winston with Elasticsearch, you can use the winston-elasticsearch transport.

    1. Install the Transport:
    npm install winston-elasticsearch
    1. Configure the Transport:
    const winston = require('winston'); const ElasticsearchTransport = require('winston-elasticsearch'); const client = new ElasticsearchTransport.Client({ node: 'http://localhost:9200' }); const transport = new ElasticsearchTransport({ level: 'info', client: client, indexPrefix: 'logs', transformer: (log) => ({ message: log.message, level: log.level, timestamp: log.timestamp }) }); const logger = winston.createLogger({ level: 'info', format: winston.format.combine( winston.format.timestamp(), winston.format.json() ), transports: [ transport ] }); logger.info('Log message sent to Elasticsearch');
    Cloud Logging Services

    For cloud-based logging solutions like AWS CloudWatch or Google Cloud Logging:

    • AWS CloudWatch:
    npm install winston-cloudwatch
    const winston = require('winston'); const CloudWatchTransport = require('winston-cloudwatch'); const logger = winston.createLogger({ level: 'info', format: winston.format.combine( winston.format.timestamp(), winston.format.json() ), transports: [ new CloudWatchTransport({ logGroupName: 'my-log-group', logStreamName: 'my-log-stream', awsRegion: 'us-east-1' }) ] }); logger.info('Log message sent to CloudWatch');
    • Google Cloud Logging:
    npm install @google-cloud/logging-winston javascriptCopy codeconst winston = require('winston'); const { LoggingWinston } = require('@google-cloud/logging-winston'); const loggingWinston = new LoggingWinston(); const logger = winston.createLogger({ level: 'info', format: winston.format.combine( winston.format.timestamp(), winston.format.json() ), transports: [ loggingWinston ] }); logger.info('Log message sent to Google Cloud Logging');
  • Log Filtering

    To filter logs based on criteria, you can use custom log formats or transports.

    const winston = require('winston');
    
    const filterTransport = new winston.transports.Console({
      format: winston.format((info) => {
    
    if (info.level === 'error') {
      return info;
    }
    })() }); const logger = winston.createLogger({ level: 'info', format: winston.format.combine(
    winston.format.timestamp(),
    winston.format.json()
    ), transports: [filterTransport] }); logger.info('This will not appear'); logger.error('This is an error message');
  • Performance Optimization

    Logging can impact performance. Here are some tips:

    • Asynchronous Logging: Use async transports to avoid blocking the main thread.
    class AsyncTransport extends winston.TransportStreamOptions { log(info, callback) { setImmediate(() => this.emit('logged', info)); setTimeout(() => { console.log(${info.level}: ${info.message}); if (callback) callback(); }, 100); } }
    • Batching Logs: Collect logs and send them in batches to reduce I/O operations.
    • Log Rotation and Compression: Use log rotation and compression to manage disk usage.
  • Dynamic Log Levels

    You might need to adjust log levels dynamically, for example, via an environment variable or a configuration file.

    const winston = require('winston');
    
    const logLevel = process.env.LOG_LEVEL || 'info';
    
    const logger = winston.createLogger({
      level: logLevel,
      format: winston.format.combine(
    
    winston.format.timestamp(),
    winston.format.json()
    ), transports: [
    new winston.transports.Console()
    ] }); logger.info('This log level is dynamically set');
  • Logging Middleware for Express

    In an Express application, you can create middleware to log HTTP requests and responses.

    const express = require('express');
    const winston = require('winston');
    
    const app = express();
    
    const logger = winston.createLogger({
      level: 'info',
      format: winston.format.combine(
    
    winston.format.timestamp(),
    winston.format.json()
    ), transports: [
    new winston.transports.Console()
    ] }); app.use((req, res, next) => { logger.info('HTTP Request', {
    method: req.method,
    url: req.url,
    headers: req.headers
    }); res.on('finish', () => {
    logger.info('HTTP Response', {
      statusCode: res.statusCode,
      statusMessage: res.statusMessage
    });
    }); next(); }); app.get('/', (req, res) => { res.send('Hello World!'); }); app.listen(3000, () => { console.log('Server is running on port 3000'); });
  • Integrating with APM Tools

    Application Performance Management (APM) tools like New Relic, Datadog, or Dynatrace can be used alongside Winston for enhanced monitoring.

    • Datadog Integration:
    npm install winston-datadog
    const winston = require('winston'); const DatadogTransport = require('winston-datadog'); const logger = winston.createLogger({ level: 'info', format: winston.format.combine( winston.format.timestamp(), winston.format.json() ), transports: [ new DatadogTransport({ apiKey: 'YOUR_DATADOG_API_KEY', hostname: 'my-hostname' }) ] }); logger.info('Log sent to Datadog');
  • Log Aggregation

    For aggregating logs from multiple sources, you can use tools like Fluentd, Logstash, or Graylog. Here’s how you might configure Winston to send logs to Fluentd:

    npm install winston-fluentd
    

    Then set up the transport:

    const winston = require('winston');
    const FluentTransport = require('winston-fluentd');
    
    const logger = winston.createLogger({
      level: 'info',
      format: winston.format.combine(
    
    winston.format.timestamp(),
    winston.format.json()
    ), transports: [
    new FluentTransport({
      host: 'localhost',
      port: 24224,
      timeout: 3.0,
      tag: 'app.log'
    })
    ] }); logger.info('Log sent to Fluentd');
  • Structured Logging

    Structured logging involves logging in a format that makes it easy to search and analyze logs. JSON is a common structured logging format.

    const winston = require('winston');
    
    const logger = winston.createLogger({
      level: 'info',
      format: winston.format.combine(
    
    winston.format.timestamp(),
    winston.format.json()
    ), transports: [
    new winston.transports.Console()
    ] }); logger.info('User login', { userId: 123, username: 'john_doe' });

    Structured logs are easy to query and analyze with tools like Elasticsearch, Kibana, or log aggregators.

  • Asynchronous Logging

    Winston supports asynchronous logging by using promises or callbacks in custom transports. Here’s an example using promises:

    const { TransportStreamOptions } = require('winston-transport');
    
    class AsyncTransport extends TransportStreamOptions {
      constructor(options) {
    
    super(options);
    } async log(info) {
    return new Promise((resolve, reject) => {
      setImmediate(() => this.emit('logged', info));
      // Simulate async operation
      setTimeout(() => {
        console.log(Async log: ${info.level}: ${info.message});
        resolve();
      }, 1000);
    });
    } } const logger = winston.createLogger({ level: 'info', transports: [
    new AsyncTransport()
    ] }); logger.info('This is an async log message');