DEV Community

Cover image for LogLayer: A Modern Logging Library for TypeScript / JavaScript
Theo Gravity
Theo Gravity

Posted on

LogLayer: A Modern Logging Library for TypeScript / JavaScript

Ever found yourself wrestling with different logging libraries across projects? Or maybe you've spent hours trying to standardize how errors and metadata are logged across your codebase?

I built LogLayer, an open-source solution to solve these common logging challenges by improving the developer experience around writing logs.

What is LogLayer?

LogLayer is a unified logging layer that sits on top of your favorite logging libraries (like Pino, Winston, Bunyan) and cloud providers (AWS, Google Cloud, DataDog, etc).

It provides a consistent, fluent API for logging messages, metadata, and errors while routing your logs to wherever they need to go.

Here's a quick example of what logging with LogLayer looks like:

import { LogLayer } from 'loglayer'
import { PinoTransport } from '@loglayer/transport-pino'
import pino from 'pino'

// Setup LogLayer with Pino
const log = new LogLayer({
  transport: new PinoTransport({
    logger: pino()
  })
})

// Log with metadata
log.withMetadata({ userId: '123', action: 'login' })
   .info('User logged in successfully')

// Logging with context (persists across log calls)
log.withContext({ requestId: '123' })
log.info('Processing request') // Will include requestId

try {
  // ... some code that might throw
} catch (error) {
  log.withError(error)
     .withMetadata({ userId: '123' })
     .error('Failed to process user request')
}
Enter fullscreen mode Exit fullscreen mode

Benefits of using LogLayer

Swap Logging Libraries

Maybe you've discovered late in the game that Next.js and Pino don't play very well without additional configuration when you're building your Next.js project for the first time. You might want to immediately switch to another library to save yourself the trouble, but doing that might mean you would need to refactor all your logging code.

With LogLayer, you can easily swap logging libraries without changing your code.

Consistent API Across All Logging Libraries

No more remembering different parameter orders or method names:

// With LogLayer - consistent API everywhere
log.withMetadata({ some: 'data' }).info('my message')

// Without LogLayer - different APIs for different libraries
winston.info('my message', { some: 'data' })     // winston
bunyan.info({ some: 'data' }, 'my message')      // bunyan
Enter fullscreen mode Exit fullscreen mode

Multi-Transport Support Out of the Box

Want to send logs to both DataDog and your logger? No problem:

import { LogLayer } from 'loglayer'
import { datadogLogs } from '@datadog/browser-logs'
import { PinoTransport } from "@loglayer/transport-pino"
import { DatadogBrowserLogsTransport } from "@loglayer/transport-datadog-browser-logs"

// Initialize Datadog
datadogLogs.init({
  clientToken: '<CLIENT_TOKEN>',
  site: '<DATADOG_SITE>',
  forwardErrorsToLogs: true,
  sampleRate: 100
})

const log = new LogLayer({
  transport: [
    new PinoTransport({
      logger: pino()
    }),
    new DatadogBrowserLogsTransport({
      logger: datadogLogs
    })
  ]
})

// Logs go to both Pino and DataDog
log.info('User logged in successfully')
Enter fullscreen mode Exit fullscreen mode

Clean Error Handling

Standardized error handling that works the same way across all logging libraries:

// Error handling works consistently
log.withError(new Error('test'))
   .withMetadata({ userId: '123' })
   .error('Operation failed')
Enter fullscreen mode Exit fullscreen mode

Powerful Plugin System

Extend functionality with plugins for things like redacting sensitive information:

const log = new LogLayer({
  plugins: [{
    onBeforeDataOut: (params) => {
      // Redact sensitive information before logging
      if (params.data?.password) {
        params.data.password = '***'
      }
      return params.data
    }
  }]
})
Enter fullscreen mode Exit fullscreen mode

Easy Testing

Built-in mocks make testing a breeze:

import { MockLogLayer } from 'loglayer'

// Use MockLogLayer in your tests - no real logging will occur
const log = new MockLogLayer()
Enter fullscreen mode Exit fullscreen mode

Getting Started

Installation is straightforward:

npm install loglayer
Enter fullscreen mode Exit fullscreen mode
import { LogLayer, ConsoleTransport } from 'loglayer'

const log = new LogLayer({
  transport: new ConsoleTransport({
    logger: console,
  }),
})

log.info('Hello world!')
Enter fullscreen mode Exit fullscreen mode

Learn More

Top comments (0)

A Workflow Copilot. Tailored to You.

Pieces.app image

Our desktop app, with its intelligent copilot, streamlines coding by generating snippets, extracting code from screenshots, and accelerating problem-solving.

Read the docs

👋 Kindness is contagious

Engage with a sea of insights in this enlightening article, highly esteemed within the encouraging DEV Community. Programmers of every skill level are invited to participate and enrich our shared knowledge.

A simple "thank you" can uplift someone's spirits. Express your appreciation in the comments section!

On DEV, sharing knowledge smooths our journey and strengthens our community bonds. Found this useful? A brief thank you to the author can mean a lot.

Okay