Skip to main content
Version: 1.0.0

FilterHandler

Advanced conditional logging with custom filter functions.

Overview

FilterHandler allows you to filter log records based on custom logic beyond simple level filtering. Filter by message content, attributes, context, or any custom criteria.

Basic Usage

import { New, FilterHandler, JSONHandler } from '@omdxp/jslog';

const logger = New(new FilterHandler({
handler: new JSONHandler(),
filter: (record) => {
// Only log records with 'critical' in the message
return record.message.includes('critical');
}
}));

logger.info('Normal operation'); // Filtered out
logger.info('Critical failure!'); // Logged

Configuration Options

interface FilterHandlerOptions {
handler: Handler;
filter: (record: Record) => boolean;
}

Filter Functions

Level-Based Filtering

import { New, FilterHandler, JSONHandler, Level } from '@omdxp/jslog';

const logger = New(new FilterHandler({
handler: new JSONHandler(),
filter: (record) => {
// Log ERROR and above, OR anything with 'important'
return record.level >= Level.ERROR ||
record.message.includes('important');
}
}));

Attribute-Based Filtering

import { New, FilterHandler, JSONHandler, String } from '@omdxp/jslog';

const logger = New(new FilterHandler({
handler: new JSONHandler(),
filter: (record) => {
// Only log records from production environment
const env = record.attrs.find(a => a.key === 'env');
return env?.value === 'production';
}
}));

logger.info('Test', String('env', 'development')); // Filtered
logger.info('Live', String('env', 'production')); // Logged

Time-Based Filtering

const logger = New(new FilterHandler({
handler: new JSONHandler(),
filter: (record) => {
// Only log during business hours (9 AM - 5 PM)
const hour = new Date().getHours();
return hour >= 9 && hour < 17;
}
}));

Common Patterns

Exclude Noisy Logs

const logger = New(new FilterHandler({
handler: new JSONHandler(),
filter: (record) => {
// Exclude health check logs
return !record.message.includes('health check') &&
!record.message.includes('ping');
}
}));

User-Based Filtering

const logger = New(new FilterHandler({
handler: new JSONHandler(),
filter: (record) => {
// Only log for admin users
const userId = record.attrs.find(a => a.key === 'userId');
return isAdmin(userId?.value);
}
}));

Sampling with Attributes

const logger = New(new FilterHandler({
handler: new JSONHandler(),
filter: (record) => {
// Always log errors, sample everything else at 10%
if (record.level >= Level.ERROR) {
return true;
}
return Math.random() < 0.1;
}
}));

Environment-Specific Filtering

const isDev = process.env.NODE_ENV === 'development';

const logger = New(new FilterHandler({
handler: new ColorHandler(),
filter: (record) => {
if (isDev) {
// Log everything in development
return true;
}
// In production, only log WARN and above
return record.level >= Level.WARN;
}
}));

Combining Filters

Multiple Conditions

const logger = New(new FilterHandler({
handler: new JSONHandler(),
filter: (record) => {
// Complex filter logic
const isError = record.level >= Level.ERROR;
const isCritical = record.message.includes('critical');
const hasUserId = record.attrs.some(a => a.key === 'userId');

return isError || (isCritical && hasUserId);
}
}));

Chainable Filters

import { New, FilterHandler, JSONHandler } from '@omdxp/jslog';

// First filter: Level
const levelFiltered = new FilterHandler({
handler: new JSONHandler(),
filter: (r) => r.level >= Level.INFO
});

// Second filter: Message content
const logger = New(new FilterHandler({
handler: levelFiltered,
filter: (r) => !r.message.includes('debug')
}));

Use Cases

Rate Limiting Per User

const userCounters = new Map<string, { count: number; resetAt: number }>();

const logger = New(new FilterHandler({
handler: new JSONHandler(),
filter: (record) => {
const userId = record.attrs.find(a => a.key === 'userId')?.value as string;
if (!userId) return true;

const now = Date.now();
const counter = userCounters.get(userId);

if (!counter || now > counter.resetAt) {
userCounters.set(userId, { count: 1, resetAt: now + 60000 });
return true;
}

if (counter.count < 100) {
counter.count++;
return true;
}

return false; // Rate limit exceeded
}
}));

Geographic Filtering

const logger = New(new FilterHandler({
handler: new JSONHandler(),
filter: (record) => {
const region = record.attrs.find(a => a.key === 'region')?.value;
// Only log from specific regions
return ['us-east-1', 'eu-west-1'].includes(region as string);
}
}));

Error Code Filtering

const logger = New(new FilterHandler({
handler: new JSONHandler(),
filter: (record) => {
const statusCode = record.attrs.find(a => a.key === 'statusCode')?.value;
// Only log 4xx and 5xx errors
return statusCode >= 400;
}
}));

Performance Considerations

Keep filter functions fast:

// Good: Simple, fast filter
const logger = New(new FilterHandler({
handler: new JSONHandler(),
filter: (record) => record.level >= Level.WARN
}));

// Bad: Slow filter with heavy computation
const logger = New(new FilterHandler({
handler: new JSONHandler(),
filter: (record) => {
// Don't do this!
const result = expensiveComputation(record);
return result.shouldLog;
}
}));

Best Practices

Do:

  • Keep filter functions simple and fast
  • Use early returns for performance
  • Document your filter logic
  • Test filters thoroughly

Don't:

  • Perform expensive operations in filters
  • Mutate the record in filter functions
  • Use filters for transformation (use middleware instead)
  • Forget that filtered logs are lost forever

See Also