birch

A logging library for Gleam with cross-platform support.

The name “birch” comes from birch trees, whose white bark gleams in the light.

Package Version Hex Docs

Features

Quick Start

import birch as log

pub fn main() {
  // Simple logging
  log.info("Application starting")
  log.debug("Debug message")
  log.error("Something went wrong")

  // With metadata
  log.info_m("User logged in", [#("user_id", "123"), #("ip", "192.168.1.1")])
}

Installation

Add birch to your gleam.toml:

[dependencies]
birch = ">= 0.1.0"

Named Loggers

Create named loggers for different components:

import birch as log

pub fn main() {
  let db_logger = log.new("myapp.database")
  let http_logger = log.new("myapp.http")

  db_logger |> log.logger_info("Connected to database", [])
  http_logger |> log.logger_info("Server started on port 8080", [])
}

Logger Context

Add persistent context to a logger:

import birch as log

pub fn handle_request(request_id: String) {
  let logger = log.new("myapp.http")
    |> log.with_context([
      #("request_id", request_id),
      #("service", "api"),
    ])

  // All logs from this logger include the context
  logger |> log.logger_info("Processing request", [])
  logger |> log.logger_info("Request complete", [#("status", "200")])
}

Log Levels

Six log levels are supported, from least to most severe:

LevelUse Case
TraceVery detailed diagnostic information
DebugDebugging information during development
InfoNormal operational messages (default)
WarnWarning conditions that might need attention
ErrorError conditions that should be addressed
FatalCritical errors preventing continuation

Set the minimum level for a logger:

import birch as log
import birch/level

let logger = log.new("myapp")
  |> log.with_level(level.Debug)  // Log Debug and above

Handlers

Console Handler

The default handler outputs to stdout with colors:

import birch/handler/console

let handler = console.handler()
// or with configuration
let handler = console.handler_with_config(console.ConsoleConfig(
  color: True,
  target: handler.Stdout,
))

JSON Handler

For log aggregation systems:

import birch/handler/json

let handler = json.handler()

Output:

{"timestamp":"2024-12-26T10:30:45.123Z","level":"info","logger":"myapp","message":"Request complete","method":"POST","path":"/api/users"}

File Handler

Write to files with optional rotation:

import birch/handler/file

let handler = file.handler(file.FileConfig(
  path: "/var/log/myapp.log",
  rotation: file.SizeRotation(max_bytes: 10_000_000, max_files: 5),
))

Null Handler

For testing or disabling logging:

import birch/handler

let handler = handler.null()

Custom Handlers

Create custom handlers with the handler interface:

import birch/handler
import birch/formatter

let my_handler = handler.new(
  name: "custom",
  write: fn(message) {
    // Send to external service, etc.
  },
  format: formatter.human_readable,
)

Lazy Evaluation

Avoid expensive operations when logs are filtered:

import birch as log

// The closure is only called if debug level is enabled
log.debug_lazy(fn() {
  "Expensive debug info: " <> compute_debug_info()
})

Output Formats

Human-Readable (default)

2024-12-26T10:30:45.123Z | INFO  | myapp.http | Request complete | method=POST path=/api/users

JSON

{"timestamp":"2024-12-26T10:30:45.123Z","level":"info","logger":"myapp.http","message":"Request complete","method":"POST","path":"/api/users"}

Library Authors

For library code, create silent loggers that consumers can configure:

// In your library
import birch as log

const logger = log.silent("mylib.internal")

pub fn do_something() {
  logger |> log.logger_debug("Starting operation", [])
  // ...
}

Consumers control logging by adding handlers to the logger.

Development

# Build for default target (Erlang)
gleam build

# Build for JavaScript
gleam build --target javascript

# Run tests on Erlang
gleam test

# Run tests on JavaScript
gleam test --target javascript

# Check formatting
gleam format --check src test

# Format code
gleam format src test

# Generate docs
gleam docs build

Testing

This project uses:

Unit tests are in test/birch_test.gleam and property tests are in test/property_test.gleam.

CI/CD

GitHub Actions runs on every push and PR:

Code Coverage

Note: Gleam currently has limited support for code coverage tools. Since Gleam compiles to Erlang source (rather than abstract format), integration with Erlang’s cover tool is challenging. We rely on comprehensive unit and property tests instead.

Comparison with Other Logging Libraries

Several logging libraries exist in the Gleam ecosystem. Here’s how they compare:

Featurebirchglightgloggpalabres
Erlang target
JavaScript target
Console output
File output
JSON output
File rotation
Colored output
Structured metadata
Typed metadata values
Named loggers
Logger context
Scoped context
Lazy evaluation
Custom handlers
Sampling
Stacktrace capture
Erlang logger integration
Wisp integration
Zero-config startup

When to Choose Each Library

License

MIT License - see LICENSE for details.

Search Document