Skip to main content

Overview

The LogLevel type defines the available log severity levels used throughout the HTTP Ledger Express middleware. It helps categorize logs based on their importance and the nature of the request/response cycle.

Type Definition

type LogLevel = 'info' | 'warn' | 'error';

Values

'info'

Represents informational logs for normal, successful operations. Typical use cases:
  • Successful requests with 2xx status codes
  • Normal request-response cycles
  • Standard API operations
Example:
const logLevel: LogLevel = 'info';

'warn'

Represents warning logs for potentially problematic situations that don’t prevent operation. Typical use cases:
  • Client errors with 4xx status codes
  • Slow requests exceeding performance thresholds
  • Deprecated API usage
  • Rate limiting
Example:
const logLevel: LogLevel = 'warn';

'error'

Represents error logs for failures and exceptional conditions. Typical use cases:
  • Server errors with 5xx status codes
  • Unhandled exceptions
  • Database connection failures
  • External service timeouts
Example:
const logLevel: LogLevel = 'error';

Usage Examples

Using in Custom Log Level Function

import { LogData, LogLevel, ApiLoggerOptions } from '@httpledger/express';

const options: ApiLoggerOptions = {
  customLogLevel: (logData: LogData): LogLevel => {
    // Error level for server errors or exceptions
    if (logData.error || logData.statusCode >= 500) {
      return 'error';
    }
    
    // Warning level for client errors or slow requests
    if (logData.statusCode >= 400 || logData.timeTaken > 5000) {
      return 'warn';
    }
    
    // Info level for successful requests
    return 'info';
  }
};

Default Log Level Logic

When no customLogLevel function is provided, the middleware uses default logic:
function getDefaultLogLevel(logData: LogData): LogLevel {
  // Server errors and exceptions
  if (logData.error || logData.statusCode >= 500) {
    return 'error';
  }
  
  // Client errors
  if (logData.statusCode >= 400) {
    return 'warn';
  }
  
  // Success cases
  return 'info';
}

Filtering Logs by Level

import { LogData, LogLevel } from '@httpledger/express';

const options: ApiLoggerOptions = {
  onLog: async (logData: LogData) => {
    const level: LogLevel = logData.logLevel || 'info';
    
    switch (level) {
      case 'error':
        // Send errors to error tracking service
        await sendToSentry(logData);
        console.error(`[ERROR] ${logData.method} ${logData.url}`, logData.error);
        break;
        
      case 'warn':
        // Send warnings to monitoring service
        await sendToDatadog(logData);
        console.warn(`[WARN] ${logData.method} ${logData.url} - ${logData.statusCode}`);
        break;
        
      case 'info':
        // Log normally
        console.log(`[INFO] ${logData.method} ${logData.url} - ${logData.statusCode}`);
        break;
    }
  }
};

Advanced Custom Logic

import { LogData, LogLevel } from '@httpledger/express';

const options: ApiLoggerOptions = {
  customLogLevel: (logData: LogData): LogLevel => {
    // Always error for exceptions
    if (logData.error) {
      return 'error';
    }
    
    // Status code based
    if (logData.statusCode >= 500) return 'error';
    if (logData.statusCode >= 400) return 'warn';
    
    // Performance based
    if (logData.timeTaken > 10000) return 'error';  // > 10s is critical
    if (logData.timeTaken > 5000) return 'warn';    // > 5s is concerning
    
    // Size based warnings
    const totalSize = logData.requestSize + logData.responseSize;
    if (totalSize > 10 * 1024 * 1024) return 'warn'; // > 10MB
    
    // Specific endpoint monitoring
    if (logData.url.includes('/critical-operation') && logData.statusCode !== 200) {
      return 'error';
    }
    
    return 'info';
  }
};

TypeScript Type Checking

import { LogLevel } from '@httpledger/express';

// Type-safe log level handling
function handleLog(level: LogLevel, message: string): void {
  const timestamp = new Date().toISOString();
  const formattedMessage = `[${timestamp}] [${level.toUpperCase()}] ${message}`;
  
  switch (level) {
    case 'error':
      console.error(formattedMessage);
      break;
    case 'warn':
      console.warn(formattedMessage);
      break;
    case 'info':
      console.log(formattedMessage);
      break;
    default:
      // TypeScript will ensure this is unreachable
      const exhaustiveCheck: never = level;
      throw new Error(`Unhandled log level: ${exhaustiveCheck}`);
  }
}

// Usage
handleLog('info', 'Request processed successfully');
handleLog('warn', 'Slow response detected');
handleLog('error', 'Database connection failed');

Integration with Logging Libraries

import { LogData, LogLevel } from '@httpledger/express';
import winston from 'winston';

const logger = winston.createLogger({
  transports: [new winston.transports.Console()]
});

const options: ApiLoggerOptions = {
  onLog: async (logData: LogData) => {
    const level: LogLevel = logData.logLevel || 'info';
    const message = `${logData.method} ${logData.url} - ${logData.statusCode} (${logData.timeTaken}ms)`;
    
    // Winston uses the same levels
    logger[level](message, {
      statusCode: logData.statusCode,
      duration: logData.timeTaken,
      requestId: logData.requestId,
      error: logData.error
    });
  }
};

Best Practices

1. Consistent Level Assignment

Be consistent in how you assign log levels across your application:
// Good: Consistent logic
if (statusCode >= 500) return 'error';
if (statusCode >= 400) return 'warn';
return 'info';

2. Consider Multiple Factors

Don’t rely solely on status codes:
// Consider performance, size, and business logic
customLogLevel: (logData) => {
  if (logData.error) return 'error';
  if (logData.statusCode >= 500) return 'error';
  if (logData.timeTaken > 5000) return 'warn';
  if (logData.statusCode >= 400) return 'warn';
  return 'info';
}

3. Environment-Specific Levels

Adjust log level thresholds based on environment:
const SLOW_REQUEST_THRESHOLD = 
  process.env.NODE_ENV === 'production' ? 3000 : 5000;

customLogLevel: (logData) => {
  if (logData.timeTaken > SLOW_REQUEST_THRESHOLD) return 'warn';
  // ... other logic
}

See Also

  • LogData - The complete log data structure that includes logLevel
  • ApiLoggerOptions - Configuration options including customLogLevel

Build docs developers (and LLMs) love