Skip to main content

Overview

Configuring HTTP Ledger for production requires balancing comprehensive logging with performance and security. This guide covers best practices for production deployments.

Production Configuration

1

Install the middleware

npm install http-ledger
2

Configure for production

const express = require('express');
const logger = require('http-ledger');

const app = express();

app.use(
  logger({
    // Disable verbose logging in production
    logBody: process.env.NODE_ENV === 'development',
    logResponse: process.env.NODE_ENV === 'development',

    // Mask sensitive fields
    maskFields: ['password', 'token', 'secret', 'apiKey'],

    // Sample logs in high-traffic environments
    logSampling: process.env.NODE_ENV === 'production' ? 0.1 : 1.0,

    // Custom log levels
    customLogLevel: (logData) => {
      if (logData.statusCode >= 500) return 'error';
      if (logData.statusCode >= 400) return 'warn';
      return 'info';
    },

    // Send to external logging service
    onLog: async (logData) => {
      await fetch(process.env.LOG_ENDPOINT, {
        method: 'POST',
        body: JSON.stringify(logData),
        headers: { Authorization: `Bearer ${process.env.LOG_API_KEY}` },
      });
    },
  }),
);

app.listen(3000);
3

Set environment variables

NODE_ENV=production
LOG_ENDPOINT=https://your-logging-service.com/api/logs
LOG_API_KEY=your-api-key-here

Security Best Practices

Field Masking

Always mask sensitive data to prevent exposure in logs:
app.use(
  logger({
    maskFields: [
      'password',
      'token',
      'secret',
      'apiKey',
      'creditCard',
      'ssn',
      'authorization',
    ],
  }),
);
Nested field masking is supported:
app.use(
  logger({
    maskFields: ['user.password', 'data.secret', 'nested.deep.secret'],
  }),
);

Header Filtering

Exclude sensitive headers from logs:
app.use(
  logger({
    excludedHeaders: ['authorization', 'cookie', 'x-api-key', 'x-auth-token'],
  }),
);

Performance Optimization

Log Sampling

Reduce logging overhead in high-traffic environments:
app.use(
  logger({
    logSampling: 0.1, // Log 10% of requests
  }),
);

Selective Logging

Skip logging for specific endpoints:
app.use(
  logger({
    shouldLog: (req, res) => {
      // Skip health check endpoints
      if (req.path === '/health' || req.path === '/ping') return false;

      // Skip successful GET requests to static files
      if (
        req.method === 'GET' &&
        req.path.startsWith('/static/') &&
        res.statusCode === 200
      ) {
        return false;
      }

      // Skip OPTIONS requests
      if (req.method === 'OPTIONS') return false;

      return true;
    },
  }),
);

Disable Body Logging

For large payloads or high-traffic APIs:
app.use(
  logger({
    logBody: false,
    logResponse: false,
    logQueryParams: true, // Still log query params
  }),
);

External Service Integration

Centralized Logging

Send logs to external services like Datadog, Loggly, or custom endpoints:
app.use(
  logger({
    onLog: async (logData) => {
      try {
        // Send to external logging service
        await fetch(process.env.LOG_ENDPOINT, {
          method: 'POST',
          headers: {
            'Content-Type': 'application/json',
            Authorization: `Bearer ${process.env.LOG_API_KEY}`,
          },
          body: JSON.stringify(logData),
        });
      } catch (error) {
        // Fallback: log to console if external service fails
        console.error('Failed to send logs to external service:', error);
      }
    },
  }),
);

Database Logging

Store logs in a database for analysis:
const { MongoClient } = require('mongodb');
const client = new MongoClient(process.env.MONGODB_URI);

app.use(
  logger({
    onLog: async (logData) => {
      try {
        const db = client.db('logs');
        await db.collection('api_logs').insertOne({
          ...logData,
          insertedAt: new Date(),
        });
      } catch (error) {
        console.error('Failed to insert log into database:', error);
      }
    },
  }),
);

Request Tracking

Enable request ID generation for distributed tracing:
app.use(
  logger({
    autoGenerateRequestId: true,
  }),
);
This automatically:
  • Generates a UUID v4 if X-Request-ID header is not present
  • Adds the request ID to response headers
  • Includes the request ID in log output

Custom Log Formatting

Add environment and service metadata:
app.use(
  logger({
    customFormatter: (logData) => ({
      ...logData,
      environment: process.env.NODE_ENV,
      service: process.env.SERVICE_NAME || 'api',
      version: process.env.APP_VERSION,
      region: process.env.AWS_REGION,
      // Add custom metrics
      isSlowRequest: logData.timeTaken > 1000,
      requestCategory: logData.method === 'GET' ? 'read' : 'write',
    }),
  }),
);

Complete Production Example

const express = require('express');
const logger = require('http-ledger');

const app = express();
app.use(express.json());

app.use(
  logger({
    // Security
    maskFields: ['password', 'token', 'secret', 'apiKey', 'creditCard'],
    excludedHeaders: ['authorization', 'cookie', 'x-api-key'],

    // Performance
    logBody: process.env.NODE_ENV === 'development',
    logResponse: process.env.NODE_ENV === 'development',
    logSampling: process.env.LOG_SAMPLING_RATE || 0.1,
    shouldLog: (req, res) => {
      if (req.path === '/health') return false;
      if (req.method === 'OPTIONS') return false;
      return true;
    },

    // Tracking
    autoGenerateRequestId: true,

    // Custom formatting
    customLogLevel: (logData) => {
      if (logData.statusCode >= 500) return 'error';
      if (logData.statusCode >= 400) return 'warn';
      if (logData.timeTaken > 1000) return 'warn';
      return 'info';
    },
    customFormatter: (logData) => ({
      ...logData,
      environment: process.env.NODE_ENV,
      service: process.env.SERVICE_NAME,
      version: process.env.APP_VERSION,
    }),

    // External integration
    onLog: async (logData) => {
      if (process.env.LOG_ENDPOINT) {
        try {
          await fetch(process.env.LOG_ENDPOINT, {
            method: 'POST',
            headers: {
              'Content-Type': 'application/json',
              Authorization: `Bearer ${process.env.LOG_API_KEY}`,
            },
            body: JSON.stringify(logData),
          });
        } catch (error) {
          console.error('Failed to send logs:', error);
        }
      }
    },
  }),
);

const PORT = process.env.PORT || 3000;
app.listen(PORT, () => {
  console.log(`Server running on port ${PORT}`);
});

Environment Variables

VariableDescriptionExample
NODE_ENVApplication environmentproduction
LOG_ENDPOINTExternal logging service URLhttps://logs.example.com/api
LOG_API_KEYAPI key for logging servicesk_live_xxxxx
LOG_SAMPLING_RATEPercentage of requests to log0.1 (10%)
SERVICE_NAMEName of the serviceuser-api
APP_VERSIONApplication version1.0.0
The middleware includes graceful error handling - if onLog throws an error, logging continues normally and the error is logged to console.

Build docs developers (and LLMs) love