Skip to content

Changelog - 2026-01-11

Logger Optimization & High-Frequency Logger

This release focuses on logging performance improvements, including optimized caching, method-scoped logging with .for(), and a new zero-allocation HfLogger for high-frequency trading and performance-critical applications.

Overview

  • Logger Optimization: Pre-computed formatting, cached instances, direct Winston method calls
  • Method-Scoped Logging: New .for() method for contextual logging
  • HfLogger: Zero-allocation logger using SharedArrayBuffer for ~100-300ns latency
  • Configurable File Rotation: Environment variables for file rotation settings
  • JSON/Text Formats: Configurable output format via environment variable

New Features

Method-Scoped Logging with .for()

File: packages/helpers/src/helpers/logger/application-logger.ts

Problem: Tracing logs to specific methods required manual prefix formatting.

Solution: The .for() method creates sub-scoped loggers automatically.

typescript
class UserService {
  private logger = Logger.get('UserService');

  async createUser(data: CreateUserDto) {
    this.logger.for('createUser').info('Creating user: %j', data);
    // Output: [UserService-createUser] Creating user: {...}

    try {
      const user = await this.userRepo.create({ data });
      this.logger.for('createUser').info('User created: %s', user.id);
      return user;
    } catch (error) {
      this.logger.for('createUser').error('Failed: %s', error);
      throw error;
    }
  }
}

Benefits:

  • Clear method context in log output
  • Cached sub-loggers for performance
  • Consistent logging pattern across codebase

HfLogger - High-Frequency Logger

File: packages/helpers/src/helpers/logger/hf-logger.ts

Problem: Standard logging has overhead that's unacceptable for HFT and performance-critical paths.

Solution: Zero-allocation logger using SharedArrayBuffer ring buffer with pre-encoded messages.

typescript
import { HfLogger, HfLogFlusher } from '@venizia/ignis-helpers';

// At initialization (once):
const logger = HfLogger.get('OrderEngine');
const MSG_ORDER_SENT = HfLogger.encodeMessage('Order sent');
const MSG_ORDER_FILLED = HfLogger.encodeMessage('Order filled');

// Start background flusher
const flusher = new HfLogFlusher();
flusher.start(100); // Flush every 100ms

// In hot path (~100-300ns, zero allocation):
logger.log('info', MSG_ORDER_SENT);
logger.log('info', MSG_ORDER_FILLED);

Performance Characteristics:

MetricValue
Log latency~100-300 nanoseconds
Buffer size64K entries (16MB)
Entry size256 bytes fixed
AllocationZero in hot path

Configurable File Rotation

File: packages/helpers/src/helpers/logger/default-logger.ts

Problem: File rotation settings were hardcoded.

Solution: Environment variables and programmatic configuration for rotation settings.

Environment Variables:

VariableDefaultDescription
APP_ENV_LOGGER_FILE_FREQUENCY1hRotation frequency
APP_ENV_LOGGER_FILE_MAX_SIZE100mMax file size before rotation
APP_ENV_LOGGER_FILE_MAX_FILES5dRetention period (days)
APP_ENV_LOGGER_FILE_DATE_PATTERNYYYYMMDD_HHDate pattern in filename

Programmatic Configuration:

typescript
import { defineCustomLogger, applicationLogFormatter } from '@venizia/ignis-helpers';

const customLogger = defineCustomLogger({
  loggerFormatter: applicationLogFormatter,
  transports: {
    info: {
      file: {
        prefix: 'my-app',
        folder: './logs',
        frequency: '24h',
        maxSize: '500m',
        maxFiles: '30d',
        datePattern: 'YYYYMMDD'
      }
    }
  }
});

JSON/Text Output Formats

Problem: Log format was fixed.

Solution: Configurable output format via APP_ENV_LOGGER_FORMAT.

bash
# JSON format (for log aggregators)
APP_ENV_LOGGER_FORMAT=json
# Output: {"level":"info","message":"[UserService] User created","timestamp":"..."}

# Pretty text format (default, for development)
APP_ENV_LOGGER_FORMAT=text
# Output: 2026-01-11T10:30:00.000Z [app] info: [UserService] User created

Performance Improvements

Logger Class Optimization

File: packages/helpers/src/helpers/logger/application-logger.ts

Problem: Logger had per-call overhead from prefix formatting and method binding.

Solution:

  • Pre-computed formatted prefix at construction
  • Static cache inside Logger class
  • Direct Winston method calls instead of bound methods
  • Fast path for default logger (no cache key computation)
typescript
export class Logger {
  private static cache = new Map<string, Logger>();
  private readonly _formattedPrefix: string;

  private constructor(scope: string, logger: winston.Logger) {
    this._formattedPrefix = `[${scope}] `;  // Pre-computed once
    this._logger = logger;
  }

  static get(scope: string): Logger {
    // Fast path with caching
    let cached = this.cache.get(scope);
    if (cached) { return cached; }
    cached = new Logger(scope, applicationLogger);
    this.cache.set(scope, cached);
    return cached;
  }

  info(message: string, ...args: any[]) {
    // Direct call, no binding overhead
    this._logger.info(this._formattedPrefix + message, ...args);
  }
}
ScenarioImprovement
Logger instantiation~5x faster (cached)
Log method call~2x faster (no binding)
Prefix formatting~3x faster (pre-computed)

Files Changed

Helpers Package (packages/helpers)

FileChanges
src/helpers/logger/application-logger.tsOptimized Logger class with caching, .for() method, pre-computed prefix
src/helpers/logger/default-logger.tsConfigurable file rotation options via env vars
src/helpers/logger/hf-logger.tsNew HfLogger for zero-allocation logging
src/helpers/logger/index.tsExport HfLogger and HfLogFlusher
src/__tests__/logger/hf-logger.test.ts50 comprehensive tests for HfLogger

Core Package (packages/core)

FileChanges
src/helpers/inversion/container.tsUpdated to use Logger type
src/base/middlewares/*.tsUpdated imports for Logger

Documentation (packages/docs)

FileChanges
wiki/references/helpers/logger.mdComplete documentation update
wiki/references/helpers/network.mdUpdated with configuration interfaces

No Breaking Changes

All changes are backward compatible:

  • ApplicationLogger is exported as a type alias for Logger
  • Existing code using LoggerFactory.getLogger() continues to work
  • New .for() method is additive
  • Environment variables have sensible defaults

Best Practices

Use Method-Scoped Logging

typescript
// Good - clear context
this.logger.for('createOrder').info('Processing order: %s', orderId);

// Less clear
this.logger.info('[createOrder] Processing order: %s', orderId);

Pre-encode HfLogger Messages

typescript
// Good - pre-encoded at init
const MSG_TICK = HfLogger.encodeMessage('Tick received');
logger.log('debug', MSG_TICK);

// Bad - encodes on every call
logger.log('debug', HfLogger.encodeMessage('Tick received'));

Choose the Right Logger

Use CaseLogger
General applicationLogger / LoggerFactory
High-frequency tradingHfLogger
Performance-critical pathsHfLogger
Debug/developmentLogger with DEBUG=true