Skip to main content

Interface

interface ApiLoggerOptions {
  logBody?: boolean;
  logResponse?: boolean;
  logQueryParams?: boolean;
  excludedHeaders?: string[];
  getIpInfo?: (ip: string) => Promise<IpInfo>;
  onLog?: (logData: LogData) => void | Promise<void>;
  maskFields?: string[];
  customLogLevel?: (logData: LogData) => LogLevel;
  customFormatter?: (logData: LogData) => unknown;
  autoGenerateRequestId?: boolean;
  shouldLog?: (req: Request, res: Response) => boolean;
  logSampling?: number;
}

Properties

logBody

logBody
boolean
default:"true"
Whether to include the request body in the log output.Set to false to exclude request bodies from logs (useful for large payloads or sensitive data).
logger({ logBody: false })

logResponse

logResponse
boolean
default:"true"
Whether to include the response body in the log output.Set to false to exclude response bodies from logs (useful for large payloads or reducing log size).
logger({ logResponse: false })

logQueryParams

logQueryParams
boolean
default:"true"
Whether to include query parameters in the log output.Set to false to exclude query parameters from logs (useful if query params contain sensitive data).
logger({ logQueryParams: false })

excludedHeaders

excludedHeaders
string[]
default:"[]"
List of header names to exclude from logs. Matching is case-insensitive.Useful for excluding sensitive headers like authorization tokens, cookies, or API keys.
logger({
  excludedHeaders: ['authorization', 'cookie', 'x-api-key']
})

getIpInfo

getIpInfo
(ip: string) => Promise<IpInfo>
Optional async function that takes an IP address string and returns an object containing IP-related information (e.g., geo-location).The function receives the client’s IP address and should return an IpInfo object with location data.
import geoip from 'geoip-lite';

logger({
  getIpInfo: async (ip: string) => {
    const geo = geoip.lookup(ip);
    return {
      ip,
      country: geo?.country,
      region: geo?.region,
      city: geo?.city,
      timezone: geo?.timezone
    };
  }
})

onLog

onLog
(logData: LogData) => void | Promise<void>
Optional callback that receives the log data object for custom processing.This function is called after log data is formatted but before it’s written to console. Use it to send logs to external services, databases, or analytics platforms.
logger({
  onLog: async (logData) => {
    // Send to external service
    await analyticsService.track(logData);
    
    // Store in database
    await db.logs.insert(logData);
    
    // Send alerts for errors
    if (logData.error) {
      await alertingService.notify(logData);
    }
  }
})

maskFields

maskFields
string[]
default:"[]"
List of field names to mask in request body, response body, headers, and query parameters.Masked fields will have their values replaced with "***MASKED***". Works with nested objects and arrays.
logger({
  maskFields: ['password', 'token', 'apiKey', 'creditCard']
})

customLogLevel

customLogLevel
(logData: LogData) => LogLevel
Custom function to determine the log level based on log data.Return 'info', 'warn', or 'error' to control which console method is used. If not provided, the default behavior is:
  • 'error' if error is present
  • 'warn' if status code >= 400
  • 'info' otherwise
logger({
  customLogLevel: (logData) => {
    // Log slow requests as warnings
    if (logData.timeTaken > 1000) return 'warn';
    
    // Log errors
    if (logData.error) return 'error';
    
    // Log client errors as warnings
    if (logData.statusCode >= 400 && logData.statusCode < 500) return 'warn';
    
    // Log server errors
    if (logData.statusCode >= 500) return 'error';
    
    // Everything else as info
    return 'info';
  }
})

customFormatter

customFormatter
(logData: LogData) => unknown
Custom function to format or transform log data before logging.The function receives the complete LogData object and can return a modified version or a completely different structure.
logger({
  customFormatter: (logData) => {
    return {
      // Keep original data
      ...logData,
      
      // Add custom fields
      environment: process.env.NODE_ENV,
      appVersion: process.env.APP_VERSION,
      service: 'api-gateway',
      
      // Transform fields
      duration: `${logData.timeTaken}ms`,
      path: logData.url.split('?')[0],
      
      // Add computed fields
      isError: logData.statusCode >= 400,
      isSlowRequest: logData.timeTaken > 1000
    };
  }
})

autoGenerateRequestId

autoGenerateRequestId
boolean
default:"false"
Whether to automatically generate a request ID if one is not already present in the X-Request-ID header.When enabled, a UUID will be generated and added to response headers as X-Request-ID and included in log data.
logger({
  autoGenerateRequestId: true
})

// Request ID is added to:
// 1. Response header: X-Request-ID
// 2. Log data: logData.requestId

shouldLog

shouldLog
(req: Request, res: Response) => boolean
Function to conditionally determine whether to log a request.Return true to log the request, false to skip logging. The function is called early in the middleware chain before any logging occurs.
logger({
  shouldLog: (req, res) => {
    // Skip health checks
    if (req.url === '/health') return false;
    
    // Skip static assets
    if (req.url.startsWith('/static')) return false;
    
    // Skip successful GET requests in production
    if (process.env.NODE_ENV === 'production' && 
        req.method === 'GET' && 
        res.statusCode < 400) {
      return false;
    }
    
    // Log everything else
    return true;
  }
})

logSampling

logSampling
number
Sampling rate for logging requests, expressed as a number between 0 and 1.
  • 0.1 = log 10% of requests
  • 0.5 = log 50% of requests
  • 1.0 = log 100% of requests (equivalent to not using sampling)
Sampling is applied randomly for each request. Use this to reduce log volume in high-traffic applications.
// Log 10% of requests
logger({ logSampling: 0.1 })

// Log 25% of requests
logger({ logSampling: 0.25 })

// Combined with shouldLog for conditional sampling
logger({
  logSampling: 0.1,  // Sample 10% of requests
  shouldLog: (req, res) => {
    // But always log errors
    if (res.statusCode >= 400) return true;
    // Let sampling decide for others
    return undefined;
  }
})

Complete Example

import express from 'express';
import logger from 'http-ledger-express';
import geoip from 'geoip-lite';
import { sendToAnalytics } from './services/analytics';

const app = express();

app.use(logger({
  // Basic options
  logBody: true,
  logResponse: true,
  logQueryParams: true,
  
  // Security
  excludedHeaders: ['authorization', 'cookie', 'x-api-key'],
  maskFields: ['password', 'token', 'creditCard', 'ssn'],
  
  // IP information
  getIpInfo: async (ip) => {
    const geo = geoip.lookup(ip);
    return {
      ip,
      country: geo?.country,
      city: geo?.city
    };
  },
  
  // Custom processing
  onLog: async (logData) => {
    await sendToAnalytics(logData);
  },
  
  // Custom formatting
  customLogLevel: (logData) => {
    if (logData.error) return 'error';
    if (logData.statusCode >= 400) return 'warn';
    return 'info';
  },
  
  customFormatter: (logData) => ({
    ...logData,
    environment: process.env.NODE_ENV,
    appVersion: process.env.APP_VERSION
  }),
  
  // Request tracking
  autoGenerateRequestId: true,
  
  // Performance optimization
  shouldLog: (req, res) => {
    return !req.url.startsWith('/health');
  },
  
  logSampling: 0.1  // Log 10% of requests
}));

app.listen(3000);

See Also

  • logger - Main logger function documentation
  • LogData - Log data structure reference

Build docs developers (and LLMs) love