Skip to content

feat: Add preprocessors and postprocessors for configurable log transformation#7

Open
konard wants to merge 4 commits intomainfrom
issue-6-d76335350d18
Open

feat: Add preprocessors and postprocessors for configurable log transformation#7
konard wants to merge 4 commits intomainfrom
issue-6-d76335350d18

Conversation

@konard
Copy link
Copy Markdown
Member

@konard konard commented Dec 21, 2025

Summary

This PR implements a flexible preprocessor and postprocessor system that allows users to transform log arguments and messages without compromising the library's core performance characteristics.

Fixes #6

Key Features

Preprocessors

  • Transform arguments before processing (receive args array, return args array)
  • Built-in helpers: addContext, filter, map
  • Example: Filter sensitive data before logging

Postprocessors

  • Transform compiled messages before output (receive string, return string)
  • Built-in helpers: timestamp, level, pid, prefix, suffix
  • Example: Add timestamps and log levels to messages

Zero-Overhead Design

  • When no preprocessors/postprocessors are configured, code path is identical to original
  • Processors only execute when the log level is enabled
  • Lazy evaluation is preserved throughout the pipeline

Usage Examples

Adding Timestamps

import makeLog, { postprocessors } from 'log-lazy';

const log = makeLog({
  level: 'info',
  postprocessors: [postprocessors.timestamp('iso')]
});

log('Server started');
// Output: [2024-01-15T10:30:45.123Z] Server started

Adding Log Levels

const log = makeLog({
  level: 'all',
  postprocessors: [postprocessors.level()]
});

log.info('User logged in');
log.error('Connection failed');
// Output:
// [INFO] User logged in
// [ERROR] Connection failed

Combining Multiple Postprocessors

const log = makeLog({
  level: 'all',
  postprocessors: [
    postprocessors.level(),
    postprocessors.timestamp('time'),
    postprocessors.prefix('[MyApp]')
  ]
});

log.info('Processing payment');
// Output: [MyApp] [10:30:45 AM] [INFO] Processing payment

Filtering Sensitive Data with Preprocessors

const log = makeLog({
  level: 'info',
  preprocessors: [
    (args) => args.map(arg => {
      if (typeof arg === 'object' && arg !== null) {
        const sanitized = { ...arg };
        if ('password' in sanitized) sanitized.password = '***';
        if ('token' in sanitized) sanitized.token = '***';
        return sanitized;
      }
      return arg;
    })
  ]
});

log('User data:', { username: 'john', password: 'secret123' });
// Output: User data: { username: 'john', password: '***' }

Custom Processors

const jsonLogger = makeLog({
  level: 'info',
  postprocessors: [
    (message, level, levelName) => {
      return JSON.stringify({
        level: levelName,
        timestamp: new Date().toISOString(),
        message: message
      });
    }
  ]
});

jsonLogger('Database query completed');
// Output: {"level":"info","timestamp":"2024-01-15T10:30:45.123Z","message":"Database query completed"}

Implementation Details

Architecture

  1. Preprocessors run first, transforming the raw arguments array
  2. Arguments are then processed (lazy functions evaluated)
  3. Postprocessors run last, transforming the compiled message string
  4. Both processor types receive level information for context-aware processing

Performance Characteristics

  • No overhead when processors are not configured (checked at logger creation time)
  • Processors only run when the log level is enabled (respects lazy evaluation)
  • Sequential processing ensures predictable transformation order
  • Built-in helpers are lightweight and optimized

Code Quality

  • ✅ All existing tests pass (141 tests)
  • ✅ 28 new tests added for preprocessors/postprocessors
  • ✅ Linter passes with no errors
  • ✅ Full TypeScript definitions included
  • ✅ Zero breaking changes to existing API

Files Changed

Core Implementation

  • src/index.js: Added preprocessor/postprocessor logic with zero-overhead optimization
  • index.d.ts: TypeScript definitions for new features

Tests

  • tests/preprocessors-postprocessors.test.js: 28 comprehensive tests covering:
    • Individual preprocessor functionality
    • Individual postprocessor functionality
    • Combined processor usage
    • Built-in helper functions
    • Zero-overhead verification
    • Lazy evaluation preservation

Examples & Experiments

  • examples/custom-processors.js: Real-world usage examples
  • experiments/preprocessors-postprocessors.js: 11 experiments demonstrating various use cases

Documentation

  • CHANGELOG.md: Updated with new features

Testing

All tests pass successfully:

✓ 141 total tests
✓ 28 new preprocessor/postprocessor tests
✓ 0 failures
✓ Linter passes

Migration Guide

This is a non-breaking change. Existing code continues to work exactly as before:

// Before (still works)
const log = makeLog({ level: 'info' });
log('message');

// After (new features available)
const log = makeLog({
  level: 'info',
  preprocessors: [...],
  postprocessors: [...]
});
log('message');

🤖 Generated with Claude Code

Adding CLAUDE.md with task information for AI processing.
This file will be removed when the task is complete.

Issue: #6
@konard konard self-assigned this Dec 21, 2025
…formation

This commit implements a flexible preprocessor and postprocessor system that allows users to transform log arguments and messages without compromising performance.

Key features:
- Preprocessors: Transform arguments before processing (receive args array, return args array)
- Postprocessors: Transform compiled messages before output (receive string, return string)
- Built-in preprocessor helpers: addContext, filter, map
- Built-in postprocessor helpers: timestamp, level, pid, prefix, suffix
- Zero-overhead implementation when no processors configured
- Full TypeScript support
- Comprehensive test coverage (28 new tests)

The implementation ensures maximum efficiency:
- When no preprocessors/postprocessors are configured, the code path is identical to the original
- Processors only execute when the log level is enabled
- Lazy evaluation is preserved throughout the pipeline

Examples:
- Add timestamps: postprocessors: [postprocessors.timestamp('iso')]
- Add log levels: postprocessors: [postprocessors.level()]
- Filter sensitive data: preprocessors: [preprocessors.filter(arg => !arg.password)]
- Combine multiple: postprocessors: [level(), timestamp(), prefix('[App]')]

Fixes #6

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
@konard konard changed the title [WIP] We need to make sure we can use configurable prefixes as custom post processing functions feat: Add preprocessors and postprocessors for configurable log transformation Dec 21, 2025
@konard konard marked this pull request as ready for review December 21, 2025 19:16
konard and others added 2 commits December 21, 2025 20:19
🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
@konard
Copy link
Copy Markdown
Member Author

konard commented Dec 21, 2025

🤖 Solution Draft Log

This log file contains the complete execution trace of the AI solution draft process.

💰 Cost estimation:

  • Public pricing estimate: $2.939849 USD
  • Calculated by Anthropic: $1.975706 USD
  • Difference: $-0.964143 (-32.80%)
    📎 Log file uploaded as GitHub Gist (673KB)
    🔗 View complete solution draft log

Now working session is ended, feel free to review and add any feedback on the solution draft.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

We need to make sure we can use configurable prefixes as custom post processing functions

1 participant