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
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
High Traffic API
Moderate Traffic
Low Traffic / Debug
// 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
}));
// For moderate traffic: balanced approach
app . use ( logger ({
shouldLog : ( req , res ) => {
// Skip health and status endpoints
if ([ '/health' , '/status' ]. includes ( req . path )) {
return false ;
}
return true ;
},
logSampling: 0.1 // 10% sampling
}));
// For low traffic or debugging: log everything
app . use ( logger ({
shouldLog : ( req , res ) => {
// Log everything except health checks
return req . path !== '/health' ;
},
logSampling: 1.0 // 100% logging
}));
Selective logging can significantly reduce overhead:
Traffic Volume Sampling Rate Logs Processed Performance Impact 1,000 req/s 100% 1,000/s High 1,000 req/s 10% 100/s Low 1,000 req/s 1% 10/s Minimal 10,000 req/s 1% 100/s Low
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