Skip to content

Filter Plugin ​

Control which logs are sent to transports based on level, namespace, or custom criteria.

Overview ​

The filter plugin allows you to reduce noise by filtering out unwanted logs before they reach transports. This is especially useful for:

  • Production environments: Filter debug logs
  • Performance: Reduce log volume
  • Cost optimization: Minimize logs sent to paid services
  • Security: Exclude sensitive namespaces
typescript
import { createFilterPlugin, createLogger } from '@crowlog/logger';

const logger = createLogger({
  namespace: 'my-app',
  plugins: [
    createFilterPlugin({
      minLevel: 'info' // Only log info and above
    })
  ]
});

Filter by Log Level ​

Minimum Level ​

Allow only logs at or above a certain severity:

typescript
const logger = createLogger({
  namespace: 'my-app',
  plugins: [
    createFilterPlugin({ minLevel: 'warn' })
  ]
});

logger.debug('Debug message'); // Filtered out
logger.info('Info message'); // Filtered out
logger.warn('Warning message'); // Logged ✓
logger.error('Error message'); // Logged ✓

Level hierarchy: debug < info < warn < error

Maximum Level ​

Allow only logs at or below a certain severity:

typescript
const logger = createLogger({
  namespace: 'my-app',
  plugins: [
    createFilterPlugin({ maxLevel: 'info' })
  ]
});

logger.debug('Debug message'); // Logged ✓
logger.info('Info message'); // Logged ✓
logger.warn('Warning message'); // Filtered out
logger.error('Error message'); // Filtered out

Level Range ​

Combine minLevel and maxLevel for a range:

typescript
const logger = createLogger({
  namespace: 'my-app',
  plugins: [
    createFilterPlugin({
      minLevel: 'info',
      maxLevel: 'warn'
    })
  ]
});

logger.debug('Debug'); // Filtered out
logger.info('Info'); // Logged ✓
logger.warn('Warning'); // Logged ✓
logger.error('Error'); // Filtered out

Specific Levels Only ​

Use onlyLevels to allow specific levels:

typescript
const logger = createLogger({
  namespace: 'my-app',
  plugins: [
    createFilterPlugin({
      onlyLevels: ['warn', 'error'] // Only warnings and errors
    })
  ]
});

logger.debug('Debug'); // Filtered out
logger.info('Info'); // Filtered out
logger.warn('Warning'); // Logged ✓
logger.error('Error'); // Logged ✓

Priority

When onlyLevels is specified, minLevel and maxLevel are ignored.

Filter by Namespace ​

Exclude Namespaces ​

Filter out specific namespaces:

typescript
const logger = createLogger({
  namespace: 'my-app',
  plugins: [
    createFilterPlugin({
      excludedNamespaces: ['debug-utils', 'test-helpers', 'dev-tools']
    })
  ]
});

// These loggers are filtered out
const debugLogger = logger.createChildLogger({ namespace: 'debug-utils' });
const testLogger = logger.createChildLogger({ namespace: 'test-helpers' });

debugLogger.info('This is filtered out');
testLogger.info('This is also filtered out');

// Regular logger still works
logger.info('This is logged'); // ✓

Allow Specific Namespaces Only ​

Use onlyNamespaces to allow only certain namespaces:

typescript
const logger = createLogger({
  namespace: 'my-app',
  plugins: [
    createFilterPlugin({
      onlyNamespaces: ['api', 'database', 'auth']
    })
  ]
});

const apiLogger = logger.createChildLogger({ namespace: 'api' });
const cacheLogger = logger.createChildLogger({ namespace: 'cache' });

apiLogger.info('API request'); // Logged ✓
cacheLogger.info('Cache hit'); // Filtered out (not in allowed list)

Exact Match

Namespace filtering uses exact string matching. 'api' does not match 'api:v1'.

typescript
excludedNamespaces: ['api']; // Only excludes exactly 'api'

For hierarchical filtering, use customFilter.

Custom Filtering ​

Custom Filter Function ​

Implement complex filtering logic:

typescript
const logger = createLogger({
  namespace: 'my-app',
  plugins: [
    createFilterPlugin({
      customFilter: ({ context }) => {
        // Only log if error exists or explicitly marked important
        return context.data.error !== undefined || context.data.important === true;
      }
    })
  ]
});

logger.info('Regular message'); // Filtered out
logger.info({ important: true }, 'Critical update'); // Logged ✓
logger.error({ error: new Error('Failed') }, 'Error occurred'); // Logged ✓

The customFilter function receives:

typescript
type FilterContext = {
  context: {
    level: 'debug' | 'info' | 'warn' | 'error';
    message: string;
    timestampMs: number;
    namespace: string;
    data: Record<string, unknown>;
  };
};

Filter by Message Content ​

typescript
createFilterPlugin({
  customFilter: ({ context }) => {
    // Exclude logs containing 'healthcheck'
    return !context.message.toLowerCase().includes('healthcheck');
  }
});

Filter by Data Properties ​

typescript
createFilterPlugin({
  customFilter: ({ context }) => {
    // Only log slow requests
    return context.data.duration && context.data.duration > 1000;
  }
});

Combining Filters ​

When multiple filter options are provided, ALL filters must pass (AND logic):

typescript
const logger = createLogger({
  namespace: 'my-app',
  plugins: [
    createFilterPlugin({
      minLevel: 'info', // Must be info or above
      excludedNamespaces: ['debug-utils'], // AND not from debug-utils
      customFilter: ({ context }) => { // AND pass custom logic
        return !context.message.includes('ignore');
      }
    })
  ]
});

// All conditions must be true for log to pass

Performance Considerations ​

Efficient Filtering ​

typescript
// Avoid: Expensive operations in customFilter
createFilterPlugin({
  customFilter: ({ context }) => {
    // Bad: Regex on every log
    const result = /complex-pattern/i.test(context.message);

    // Bad: expensive check
    const allowed = expensiveCheck(context.namespace);

    return result && allowed;
  }
});

Pre-compute Where Possible ​

typescript
// Good: Pre-compute expensive operations
const excludedPaths = new Set(['/health', '/metrics', '/ready']);

createFilterPlugin({
  customFilter: ({ context }) => {
    return !excludedPaths.has(context.data.path);
  }
});

Released under the MIT License.