loglayer is a free, open source monitoring & observability project written in TypeScript and released under MIT. It has 550 GitHub stars, 11 forks and 2 open issues, and was last pushed 37 hours ago. On this registry it ranks #263 of 270 tracked projects in Monitoring & Observability, with 5 head-to-head comparisons available. It gained 4 stars over the last 30 tracked days.

What is loglayer?

LogLayer is a TypeScript logging library that acts as a unified logger, routing log messages to other logging libraries, cloud providers, files, StatsD and OpenTelemetry through a fluent API, and it is aimed at JavaScript and TypeScript developers building for Node.js, Bun, Deno, the browser, Next.js and Electron.

What it is

LogLayer is a logging abstraction that sits in front of the logger you already use. Rather than emitting log lines itself, it formats a message, metadata and an error into a consistent shape and hands that shape to a transport, which forwards it to a concrete logging library such as Pino, to a cloud provider, to a file, to StatsD, or to OpenTelemetry. The library ships as the loglayer package with companion transport and plugin packages, including @loglayer/transport-pino, StructuredTransport, the Pretty Terminal Transport, and @loglayer/plugin-redaction, and a parallel Go implementation exists at go.loglayer.dev.

The concrete problem it solves is coupling. In a typical application, every call site, file and test reaches directly into one logging library, so changing that library, adding a second destination, or attaching a redaction rule means editing call sites across the codebase. LogLayer replaces that library-specific call surface with one fluent API: withContext(), withPrefix(), withError(), withMetadata(), then a terminal call such as info(). The transport and plugin list can change, multiple loggers can run at the same time, and call sites stay untouched.

Key capabilities

  • Fluent builder API for log entries, with withContext() for data persisted across calls, withMetadata() and withError() for per-entry data, withPrefix() for message prefixes, and errorOnly() or metadataOnly() when only an object or error should be logged.
  • Interchangeable transports, demonstrated with PinoTransport from @loglayer/transport-pino and StructuredTransport, all constructed through new LogLayer({ transport: ... }).
  • Plugins that modify log data before it reaches the logger, such as redactionPlugin({ paths: ['password'], censor: '[REDACTED]' }) from @loglayer/plugin-redaction.
  • Configurable output shape, including contextFieldName and metadataFieldName to place context and metadata in named fields rather than the default flattened layout.
  • Wide Events Mixin from @loglayer/mixin-wide-events, registered with useLogLayerMixin() and driven by AsyncLocalStorage so that withWideEvents() accumulates data and emitWideEvent() produces one self-contained entry for a whole operation.
  • Runtime coverage for browser, Node.js, Bun and Deno, with topics for Electron, Next.js and bun.
  • File and log rotation support, named in the topic list and provided through the transport layer.

Who uses it and how

  • Backend teams on Node.js, Bun or Deno who want to switch logging backends, or run several at once, without rewriting every logging call site.
  • Teams shipping to observability destinations named in the topic list, including Datadog and StatsD, alongside OpenTelemetry instrumentation.
  • Frontend and desktop projects using Next.js, Electron or plain browser targets that need the same logging call style across client and server code.
  • Security-conscious services that must censor sensitive fields such as passwords centrally, using the redaction plugin, instead of relying on each call site to remember.
  • Teams adopting wide events to reduce log volume, collecting operation context over an asynchronous request and emitting a single comprehensive entry instead of many fragmented lines.

Getting started

Add the loglayer package together with the transport package for the target logger, for example loglayer with @loglayer/transport-pino, then construct new LogLayer({ transport: ... }) and call the fluent methods. Full documentation is hosted at loglayer.dev, with the source repository set up as a pnpm and turbo monorepo.

How it compares

No list of paid products it replaces is provided; among the tools named in the facts, LogLayer is a wrapper layer rather than a logging backend, and Pino appears as a transport target, so the two compose instead of competing. Datadog, StatsD and OpenTelemetry are named as destinations that LogLayer routes to, which places it above those clients in the stack rather than beside them.

When to use it — and when not to

Because LogLayer is a library, a self-hoster operates no database, storage or SMTP service, but adopting it means owning one more dependency layer in front of an existing logger, plus the transport and plugin packages that go with it. A project already committed to a single logger and a single destination, with no plans to swap or fan out, gains little from the indirection. The project is also small relative to its ecosystem: 545 stars, 11 forks, and a README excerpt that cuts off partway through the project structure, so teams wanting a large, mature support community should weigh that before standardising on it.

project readme (upstream, from github) — read inline

LogLayer

NPM version NPM Downloads TypeScript npm package minimized gzipped size

loglayer is a unified logger that routes logs to various logging libraries, cloud providers, files, and OpenTelemetry while providing a fluent API for specifying log messages, metadata and errors.

Supports browser, Node.js, Bun, and Deno platforms.

For full documentation, read the docs.

Also available for Go! See go.loglayer.dev for the Go version.

// Example using the Pino logging library with LogLayer
// You can also start out with a console logger and swap to another later!
import { LogLayer } from 'loglayer';
import { pino } from 'pino';
import { PinoTransport } from '@loglayer/transport-pino';
import { redactionPlugin } from '@loglayer/plugin-redaction';

const log = new LogLayer({
  // Multiple loggers can also be used at the same time. 
  transport: new PinoTransport({
    logger: pino()
  }),
  // Plugins modify log data before it's shipped to your logging library.
  plugins: [
    redactionPlugin({
      paths: ['password'],
      censor: '[REDACTED]',
    }),
  ],
  // Put context data in a specific field (default is flattened)
  contextFieldName: 'context',
  // Put metadata in a specific field (default is flattened)
  metadataFieldName: 'metadata',
})

// persisted data that is always included in logs
log.withContext({
   path: "/",
   reqId: "1234"
})

log.withPrefix("[my-app]")
  .withError(new Error('test'))
  // data that is included for this log entry only
  .withMetadata({ some: 'data', password: 'my-pass' })
  // Non-object data only (numbers, booleans, and strings only)
  // this can be omitted to just log an object / error
  // by using .errorOnly() / .metadataOnly() instead of withError() / withMetadata()
  .info('my message')
{
   "level": 30,
   "time": 1735857465669,
   "msg": "[my-app] my message",
   "context": {
      "path": "/",
      "reqId": "1234",
   },
   "metadata": {
      "password": "[REDACTED]",
      "some": "data",
   },
   "err":{
      "type": "Error",
      "message": "test",
      "stack": "Error: test\n ..."
   }
}

With the Pretty Terminal Transport:

Pretty Terminal Transport

The Wide Events Mixin enables comprehensive, self-contained log entries that capture an entire operation's context. For background on the problem this solves, see Why Logging Sucks.

import { AsyncLocalStorage } from "node:async_hooks";
import { LogLayer, StructuredTransport, useLogLayerMixin } from 'loglayer';
import { createWideEventMixin } from '@loglayer/mixin-wide-events';

const asyncLocalStorage = new AsyncLocalStorage<{ logger: LogLayer }>();

useLogLayerMixin(createWideEventMixin({ asyncContext: asyncLocalStorage }));

const log = new LogLayer({
  transport: new StructuredTransport({ logger: console })
});

asyncLocalStorage.run({ logger: log }, () => {
  log.withWideEvents({ userId: "123" });
  // ... do work ...
  log.withWideEvents({ orderId: "456", duration: 150 });
  log.emitWideEvent({ message: "Order processed" });
  // Emits a single log with all accumulated data in one entry
});

Development Setup

This is a monorepo using pnpm for package management and turbo for build orchestration. If you're looking to contribute or work with the source code, follow these steps:

  1. Clone the repository
  2. Install dependencies:
    pnpm install
    
  3. Install git hooks:
    pnpx lefthook install
    
  4. Build all packages:
    turbo build
    

Running Tests

To run tests for all packages, use the following command:

turbo test

Linting and Formatting

This project uses Biome for linting and formatting. To run lint and format across all packages:

turbo lint

To run lint only on staged files (used by git hooks):

turbo lint:staged

Viewing docs

The docs use vitepress. To view the docs locally, run:

pnpm run docs

To check that docs build correctly and are ready for production:

pnpm run docs:build

Project Structure

loglayer/
├── docs/                         # Documentation (vitepress)
├── packages/
│   ├── core/                     # Core packages
│   │   ├── context-manager/      # Context manager system core
│   │   ├── log-level-manager/    # Log level manager system core
│   │   ├── loglayer/             # Main LogLayer implementation
│   │   ├── plugin/               # Plugin system core
│   │   ├── transport/            # Transport system core
│   │   ├── shared/               # Shared utilities and types
│   │   └── tsconfig/             # Shared TypeScript configurations
│   ├── context-managers/         # Official context manager implementations
│   ├── log-level-managers/       # Official log level manager implementations
│   ├── transports/               # Official transport implementations
│   ├── mixins/                   # Official mixins implementations
│   ├── plugins/                  # Official plugins
│   └── integrations/             # Framework integrations (e.g. ElysiaJS)

Documentation

For detailed documentation, visit https://loglayer.dev

Contributing

We welcome contributions! Please see our Contributing Guide for details.

Made with ❤️ by Theo Gravity / Disaresta. Logo by Akshaya Madhavan.

Frequently asked questions

Is loglayer free to use?

loglayer is open source under the MIT licence. There is no licence fee and no seat count — you can self-host it or, where the project offers one, pay a vendor for a managed version instead.

What does loglayer do?

A modern logging library for Javascript / Typescript that routes logs to various logging libraries, cloud providers, files, StatsD, and OpenTelemetry while prov

What is loglayer written in?

loglayer is primarily written in TypeScript. Its source is publicly available at https://github.com/loglayer/loglayer, and it has 550 GitHub stars.