Skip to main content

Overview

Selective logging helps you reduce log volume and focus on important requests. HTTP Ledger provides two mechanisms: conditional logging with shouldLog and probabilistic sampling with logSampling.

shouldLog

shouldLog
(req: Request, res: Response) => boolean
Function that determines whether to log a specific request. Return false to skip logging. The function receives Express request and response objects.

Basic Usage

app.use(logger({
  shouldLog: (req, res) => {
    // Only log errors
    return res.statusCode >= 400;
  }
}));

Skip Health Checks

app.use(logger({
  shouldLog: (req, res) => {
    // Don't log health check endpoints
    return req.path !== '/health' && req.path !== '/ping';
  }
}));

Log Only Specific Methods

app.use(logger({
  shouldLog: (req, res) => {
    // Only log write operations
    return ['POST', 'PUT', 'PATCH', 'DELETE'].includes(req.method);
  }
}));

Skip Successful Static File Requests

app.use(logger({
  shouldLog: (req, res) => {
    // Skip successful static file requests
    if (req.path.startsWith('/static/') && res.statusCode === 200) {
      return false;
    }
    return true;
  }
}));

Complex Filtering Logic

app.use(logger({
  shouldLog: (req, res) => {
    // Skip OPTIONS requests (CORS preflight)
    if (req.method === 'OPTIONS') return false;
    
    // Always log errors
    if (res.statusCode >= 400) return true;
    
    // Skip health/status endpoints
    if (['/health', '/status', '/metrics'].includes(req.path)) {
      return false;
    }
    
    // Log all admin routes
    if (req.path.startsWith('/admin')) return true;
    
    // Log API routes
    if (req.path.startsWith('/api')) return true;
    
    // Skip everything else
    return false;
  }
}));

logSampling

logSampling
number
Sampling rate between 0 and 1. For example, 0.1 means log approximately 10% of requests. Defaults to 1.0 (log all requests).

Basic Usage

// Log 10% of requests
app.use(logger({
  logSampling: 0.1
}));

Different Rates for Different Environments

app.use(logger({
  logSampling: process.env.NODE_ENV === 'production' ? 0.1 : 1.0
}));

How Sampling Works

Implementation (from src/utils/advancedFeatures.ts:36-46):
export const shouldLogBasedOnSampling = (logSampling?: number): boolean => {
  if (logSampling === undefined || logSampling === 1) {
    return true;
  }

  if (logSampling <= 0) {
    return false;
  }

  return Math.random() < logSampling;
};
Sampling uses Math.random() for each request, so the percentage is approximate. Over large volumes, it will average out to the specified rate.

Combining shouldLog and logSampling

You can use both options together for sophisticated logging strategies:
app.use(logger({
  // Always log errors, sample everything else at 10%
  shouldLog: (req, res) => {
    // Always log errors
    if (res.statusCode >= 400) return true;
    
    // Skip health checks completely (not even sampled)
    if (req.path === '/health') return false;
    
    // Everything else will be sampled at 10%
    return true;
  },
  
  logSampling: 0.1
}));
shouldLog is evaluated first. If it returns false, the request is never logged. If it returns true, logSampling is then applied.

Advanced Filtering Patterns

Path-Based Sampling

app.use(logger({
  shouldLog: (req, res) => {
    // Different sampling rates for different paths
    if (req.path.startsWith('/api/critical')) {
      return true; // 100% logging for critical APIs
    }
    
    if (req.path.startsWith('/api/analytics')) {
      return Math.random() < 0.01; // 1% sampling for analytics
    }
    
    return Math.random() < 0.1; // 10% for everything else
  }
}));

Time-Based Logging

app.use(logger({
  shouldLog: (req, res) => {
    // Log more during business hours
    const hour = new Date().getHours();
    const isBusinessHours = hour >= 9 && hour < 17;
    
    if (isBusinessHours) {
      return Math.random() < 0.5; // 50% during business hours
    } else {
      return Math.random() < 0.1; // 10% off-hours
    }
  }
}));

User-Based Sampling

app.use(logger({
  shouldLog: (req, res) => {
    // Always log for test users
    const userId = req.headers['x-user-id'];
    if (userId && userId.startsWith('test-')) {
      return true;
    }
    
    // Sample 1% of production traffic
    return Math.random() < 0.01;
  }
}));

Error Priority Logging

app.use(logger({
  shouldLog: (req, res) => {
    // Always log server errors
    if (res.statusCode >= 500) return true;
    
    // Log 50% of client errors
    if (res.statusCode >= 400) {
      return Math.random() < 0.5;
    }
    
    // Log 10% of successes
    return Math.random() < 0.1;
  }
}));

Production Examples

// For high-traffic APIs: aggressive sampling
app.use(logger({
  shouldLog: (req, res) => {
    // Always log errors
    if (res.statusCode >= 400) return true;
    
    // Skip health checks
    if (req.path === '/health') return false;
    
    // Sample success requests
    return true;
  },
  
  logSampling: 0.01 // 1% of non-error requests
}));

Performance Impact

Selective logging can significantly reduce overhead:
Traffic VolumeSampling RateLogs ProcessedPerformance Impact
1,000 req/s100%1,000/sHigh
1,000 req/s10%100/sLow
1,000 req/s1%10/sMinimal
10,000 req/s1%100/sLow
Start with aggressive sampling (1-5%) in production and increase if you need more visibility.

Error Handling

If shouldLog throws an error, the request will still be logged. Errors are logged to console but don’t stop the middleware.
From src/index.ts:70-79:
// Early exit if shouldLog function returns false
if (shouldLog) {
  try {
    if (!shouldLog(req, res)) {
      return next();
    }
  } catch (err) {
    // If shouldLog throws, log the error but continue with logging
    console.warn('shouldLog function threw:', err);
  }
}

Monitoring Sampling Effectiveness

Track sampling effectiveness with custom formatting:
app.use(logger({
  logSampling: 0.1,
  
  customFormatter: (logData) => ({
    ...logData,
    samplingRate: 0.1,
    sampledRequest: true // Mark that this was sampled
  }),
  
  onLog: async (logData) => {
    // Track sampling metrics
    metrics.increment('http.requests.sampled');
  }
}));

Best Practices

Always Log Errors

Never sample out errors (4xx/5xx responses) - always log them

Skip Health Checks

Exclude health check endpoints to reduce noise

Start Conservative

Begin with aggressive sampling (1-5%) and increase as needed

Monitor Sampling

Track what percentage of requests are actually logged

Performance Optimization

Learn more about optimizing logging performance

Custom Logging

Combine with custom formatters for targeted logging

Build docs developers (and LLMs) love