Skip to content

Async-first TypeScript logger

One install. Every transport.
Never blocks the event loop.

Structured logs, request tracing, redaction and export to files, databases, clouds and OTLP, for Node.js, NestJS and the edge. One TypeScript-first library instead of five glued together.

Get started GitHub
  • MIT
  • TypeScript
  • Node
  • Bun
  • Deno
  • Edge
server.ts
const logger = createLogger({
  appName: 'api',
  traceId: true,
  redact: { paths: ['**.password', '**.token'] },
  transports: {
    console:  { format: 'text' },
    file:     { filename: 'app.log', maxSize: '50MB' },
    database: { type: 'postgresql', database: 'app' },
  },
});

await logger.info('Server started', { port: 3000 });
await logger.warn('slow query', { ms: 812 });
await logger.error('charge failed', { password: 'hunter2' });

INFO12:04:11Server started port=3000 trace=4bf92f35

WARN12:04:12slow query ms=812

ERROR12:04:13charge failed password=[REDACTED]

Ships logs to

  • Console
  • Rotating files
  • PostgreSQL
  • MySQL
  • MongoDB
  • SQLite
  • AWS CloudWatch
  • Google Cloud Logging
  • Azure Monitor
  • Datadog
  • OTLP
  • Mixpanel
  • Segment
  • Google Analytics
  • Slack
  • Discord
  • Worker threads

What you get

Everything a production logger needs, in one package.

Request tracing across async calls

Express, Fastify and NestJS helpers read traceparent or x-trace-id and put the trace ID on every log line in the request, without passing a logger around.

GET /orders/42 traceparent: 00-4bf92f35…-01

4bf92f35auth ok

4bf92f35orders.find 38ms

4bf92f35response 200

Tracing guide

Redaction before anything is written

Paths with wildcards, regex patterns or autoDetect. Nested errors and URLs included.

{ "user": "ada",
  "password": "hunter2" }
{ "user": "ada",
  "password": "[REDACTED]" }
Redaction guide

One call, many destinations

Transports batch and retry off the hot path, so a slow sink never blocks a request.

  • Consolepretty or JSON
  • Filesize and date rotation
  • PostgreSQL · MySQL · MongoDBbatched inserts
  • CloudWatch · GCP · Azuresigned, batched
  • Datadog · OTLPbatched export
  • Slack · Discordthrottled alerts
All transports

OpenTelemetry correlation and OTLP export

Stamp entries with the active span and ship them to any OTLP collector, so logs link straight to their traces.

{
  "level": "info",
  "message": "Order created",
  "traceId": "4bf92f3577b34da6a3ce929d0e0e4736",
  "spanId": "00f067aa0ba902b7",
  "orderId": "ord_42"
}
OpenTelemetry guide

First-class NestJS

A global module, an injectable service, an exception filter and @LogMethod, all from logixia/nest.

import { LogixiaLoggerModule } from 'logixia/nest';

@Module({
  imports: [LogixiaLoggerModule.forRoot({ appName: 'api', traceId: true })],
})
export class AppModule {}
NestJS guide

Browser and edge

logixia/browser has no Node globals. It runs in browsers and Cloudflare Workers, with a batching remote transport.

import { createBrowserLogger }
  from 'logixia/browser';
Edge guide

Quick start ​

bash
npm install logixia
ts
import { createLogger } from 'logixia';

const logger = createLogger({
  appName: 'api',
  environment: 'production',
  traceId: true,
  redact: { paths: ['**.password', '**.authorization'], autoDetect: true },
  transports: {
    console: { format: 'json' },
    file: { filename: 'app.log', dirname: './logs', maxSize: '50MB', maxFiles: 14 },
  },
});

await logger.info('Order created', { orderId: 'ord_42', password: 'hunter2' });
// password is written as "[REDACTED]", a traceId is attached,
// and the entry goes to the console and the rotating file.

Continue with the getting started guide.

One library instead of five ​

A typical Node.js service assembles its logging from several packages. logixia ships those pieces together and tested against each other, so there is less to wire up and keep in sync.

You usually assembleIn logixia
A JSON loggercreateLogger, with text, JSON and CSV formats
An HTTP request logger and trace-ID middlewareExpress, Fastify and NestJS helpers, traceparent
Redaction configurationredact: { paths, patterns, autoDetect }
Log shippers for files, databases and cloudsBuilt-in transports with batching and rotation
OpenTelemetry glue codeTrace correlation and an OTLP log transport

You can still use logixia next to other tools. It is one more option, not a requirement to replace anything.

Works with your framework ​

ts
import express from 'express';
import { createLogger, createExpressContextMiddleware } from 'logixia';

const logger = createLogger({ appName: 'api', traceId: true });
const app = express();

app.use(createExpressContextMiddleware({ responseHeader: 'x-trace-id' }));

app.get('/orders/:id', async (req, res) => {
  await logger.info('Loading order', { id: req.params.id }); // carries the request trace id
  res.json({ ok: true });
});
ts
import Fastify from 'fastify';
import { createLogger, createFastifyContextHook } from 'logixia';
import { createFastifyPlugin } from 'logixia/middleware';

const logger = createLogger({ appName: 'api', traceId: true });
const app = Fastify();
app.addHook('onRequest', createFastifyContextHook());
app.register(createFastifyPlugin(logger)); // logs request start and completion
ts
import { Module } from '@nestjs/common';
import { LogixiaLoggerModule } from 'logixia/nest';

@Module({
  imports: [
    LogixiaLoggerModule.forRoot({
      appName: 'nestjs-api',
      environment: 'production',
      traceId: true,
      transports: { console: {} },
    }),
  ],
})
export class AppModule {}
ts
import { createBrowserLogger } from 'logixia/browser';

const logger = createBrowserLogger({ appName: 'edge-worker', json: true });

export default {
  fetch(request: Request): Response {
    const requestId = request.headers.get('cf-ray') ?? crypto.randomUUID();
    logger.child({ requestId }).info('request received', { url: request.url });
    return Response.json({ ok: true, requestId });
  },
};

Ready to try it? ​

Install the package, add a logger, and ship logs where you need them.

Read the guideBrowse articles

Released under the MIT License.