Skip to main content

Getting Started with SyntropyLog

SyntropyLog is a structured observability framework for Node.js. You declare what your logs should carry (context, level-based fields, retention, masking), and SyntropyLog applies it everywhere—automatically. No manual plumbing, no hidden behavior.

It is built for high demand and regulated environments (banking, healthcare, financial services): HIPAA-style field control via the Logging Matrix, SOX-style audit trails via withRetention, and a pipeline that never lets logging crash your app.

Philosophy: "Simplicity over Complexity" — configure once, use anywhere.

What's in the box

  • Lifecycleinit() / shutdown(); graceful flush on SIGTERM/SIGINT.
  • Context — Correlation ID and transaction ID; single source of truth, propagate via contextManager.run().
  • Logging Matrix — Control which context fields appear per level (lean on info, full on error).
  • Masking — Redact sensitive fields before any transport; built-in + custom rules.
  • Universal Adapter — Send logs to any backend (PostgreSQL, Elasticsearch, S3, etc.) via one executor.
  • Fluent APIwithRetention, withSource, withTransactionId; per-call transport control (.override(), .add(), .remove()).
  • Resilience — Serialization timeout, sanitization, optional Rust native addon; logging never throws.

For the full list and details, see the library README.


Quick Start

Install

npm install syntropylog

A prebuilt native addon (Rust) for Linux, Windows, and macOS installs automatically on Node ≥20. If unavailable, the JS pipeline is used transparently.

Init and first log

Initialization must complete (the ready event) before you log. Until then, getLogger() returns a no-op logger that drops all messages. Listen for ready and error before calling init().

import { syntropyLog } from 'syntropylog';

async function initializeSyntropyLog() {
return new Promise<void>((resolve, reject) => {
syntropyLog.on('ready', () => resolve());
syntropyLog.on('error', (err) => reject(err));
syntropyLog.init({
logger: { level: 'info', serviceName: 'my-app' },
});
});
}

async function main() {
await initializeSyntropyLog(); // must resolve before any log
const log = syntropyLog.getLogger();
log.info('Hello, SyntropyLog.');
}
main();

With a console transport (recommended):

import { syntropyLog, ClassicConsoleTransport } from 'syntropylog';

syntropyLog.on('ready', () => {});
syntropyLog.on('error', (err) => console.error(err));
syntropyLog.init({
logger: {
serviceName: 'my-app',
level: 'info',
transports: [new ClassicConsoleTransport()],
},
});
const logger = syntropyLog.getLogger('main');
logger.info('Application started', { version: '1.0.0' });

Graceful shutdown

Call shutdown() on SIGTERM/SIGINT so pending logs are flushed before exit.

process.on('SIGTERM', async () => {
await syntropyLog.shutdown();
process.exit(0);
});
process.on('SIGINT', async () => {
await syntropyLog.shutdown();
process.exit(0);
});

See Boilerplate for a reusable init/shutdown pattern.

Framework entry points

FrameworkWhere to call await init
Express / FastifyBefore app.listen() in server entry
NestJSAppModule.onModuleInit() or before app.listen() in bootstrap()
Lambda / ServerlessModule-level lazy singleton (outside the handler)

Context and correlation

Set context in init() (e.g. correlationIdHeader, transactionIdHeader). Use the context manager so each request (or job) runs in its own scope with a correlation ID. A common context middleware pattern is to capture the correlation ID from the incoming request header when present, or let the framework generate one:

const contextManager = syntropyLog.getContextManager();

function withContext(
contextManager: ReturnType<typeof syntropyLog.getContextManager>,
handler: () => Promise<void>,
options?: { correlationId?: string }
) {
return contextManager.run(async () => {
const headerName = contextManager.getCorrelationIdHeaderName();
if (options?.correlationId != null && options.correlationId !== '') {
contextManager.setCorrelationId(options.correlationId);
}
contextManager.getCorrelationId(); // ensure ID exists (generate if not set)
await handler();
});
}

// Express/Fastify: read from incoming header, then run the request in context
app.use((req, res, next) => {
const fromHeader = req.get(contextManager.getCorrelationIdHeaderName()) ?? undefined;
withContext(contextManager, () => next(), { correlationId: fromHeader });
});

All logs inside that request will share the same correlation ID. You can create several loggers with different names (e.g. getLogger('service'), getLogger('axios'), getLogger('kafka')) or, for a cleaner setup, use a central LoggerName enum and call getLogger(LoggerName.service), getLogger(LoggerName.axios), etc. from anywhere. See Context and Multiple loggers and transports. Use getCorrelationId() and getCorrelationIdHeaderName() to add the header on outgoing HTTP requests.

HTTP correlation (no built-in HTTP client)

The library does not provide getHttp() or a built-in HTTP client. Use your own client (e.g. Axios) and add a request interceptor that injects the correlation ID from the context manager:

const contextManager = syntropyLog.getContextManager();

axiosInstance.interceptors.request.use((config) => {
config.headers[contextManager.getCorrelationIdHeaderName()] =
contextManager.getCorrelationId();
return config;
});

See HTTP instrumentation and the examples 10-basic-http-correlation and 11-axios-interceptors in syntropylog-examples.

Masking and configuration

Configure masking in init() so sensitive fields are redacted before any transport. Use loggingMatrix to control which context fields appear per level. The only required block is logger (serviceName, level, transports); context is optional.

See Configuration and the library README (MaskingEngine, Logging Matrix, Universal Adapter).


Next steps

  1. Examplessyntropylog-examples (00–17): setup, context, transports, HTTP correlation, testing, benchmark.
  2. Core conceptsLogger, Context, HTTP instrumentation.
  3. ProductionConfiguration patterns, Graceful shutdown.
  4. API ReferenceAPI Reference and Boilerplate.

Need help?