Skip to main content

Crate ticklog

Crate ticklog 

Source
Expand description

Ticklog is a fast, minimal logging library for latency-critical Rust applications such as high-frequency trading, where the cost of a log call on the hot path must stay in the low tens of nanoseconds.

§How it works

A logging macro runs entirely on the calling thread’s hot path: it checks the level, then encodes a compact binary record into that thread’s private lock-free buffer and returns. Format and source strings are stored by pointer and arguments in their native form, so no text formatting happens here.

A background drain thread does the rest: it decodes each record, formats and timestamps it, and writes the text to a LogSink, keeping all of that cost off the calling thread.

§Benchmarks

Per-call latency on a Mac (M4, macOS 15, Rust 1.85, release profile). Each measurement is the median of 200 criterion samples. Lower is better.

Loggerinfo!(“x={}”, 42u64)info!(“{}”, “hello world”)info!(“{} {} {}”, 42u64, 3.14159, “hello world”)
ticklog8.0 ns9.7 ns11.6 ns
env_logger231 ns232 ns307 ns
slog274 ns269 ns454 ns
tracing386 ns425 ns458 ns

§Quick start

use ticklog::{info, FileSink};

// Build the logger, then keep the returned guard alive for as long as you
// want to log.
let _guard = ticklog::builder()
    .sink(FileSink::new("app.log").unwrap())
    .build()
    .unwrap();

info!("listening on {}", 8080);

Builder::build may be called only once per process and returns a Guard. Hold the guard for as long as you want to log: when it is dropped it flushes the sink, stops the background drain thread, and disables logging, so every log call afterwards is a silent no-op.

§Logging macros

trace!, debug!, info!, warn!, and error! take a format string literal followed by positional arguments:

info!("connected to {}", "db-01");
error!("request {} failed after {} retries", 42, 3);

Placeholders are {} (Display) and {:?} (Debug). The number of placeholders is checked against the number of arguments at compile time, so a mismatch is a build error rather than a runtime surprise.

§Configuration

builder returns a Builder with these knobs:

§Sinks

A LogSink is the final destination for formatted lines. The crate ships:

  • ConsoleSink: stdout or stderr, with automatic or forced ANSI coloring by level (see ColorMode).
  • FileSink: a buffered single file, opened in append or truncate mode.
  • WriterSink: wraps any std::io::Write; the escape hatch for custom destinations such as rotating files or the network.

Compose and filter sinks with FanOut (dispatch one record to several sinks) and LogSinkExt::with_max_level (limit a sink to a level and below):

use ticklog::{ConsoleSink, FanOut, Level, LogSinkExt};

let sink = FanOut::new()
    .add(ConsoleSink::stderr().with_max_level(Level::Warn))
    .add(ConsoleSink::stdout().with_max_level(Level::Info));

§Threads

Any thread may log, and each allocates its own buffer on first use. To move that one-time allocation off a latency-sensitive path, call warm_up on the thread before its first log call.

§Safety

The public API contains no unsafe functions. Internally, unsafe is confined to three areas, each with a documented invariant:

  • Thread-local buffer access: per-thread buffers live in an UnsafeCell guarded by a re-entrancy flag. A re-entrant log call on the same thread is detected and refused before it can form a second mutable reference, preventing aliasing UB.
  • Lock-free ring buffer: the buffer shared between the calling thread and the drain thread uses atomic ordering to coordinate access without locks. The calling thread only writes; the drain thread only reads.
  • Affinity syscalls: platform thread-affinity calls require raw pointer and FFI usage, gated behind #[cfg(target_os)].

Macros§

debug
Logs a message at Level::Debug.
error
Logs a message at Level::Error.
info
Logs a message at Level::Info.
trace
Logs a message at Level::Trace.
warn
Logs a message at Level::Warn.

Structs§

Builder
Collects configuration for the logging system. Create one with builder, then call Builder::build.
ConsoleSink
A LogSink that writes to stdout or stderr, optionally coloring each line by its level.
FanOut
A sink that dispatches to multiple inner sinks.
FileSink
A LogSink that writes lines to a single file, buffered.
Guard
A running logger. Keep it alive for as long as you want to log.
WithLevel
A LogSink adapter that overrides the maximum level of an inner sink.
WriterSink
A LogSink that forwards each line, plus a trailing newline, to a wrapped io::Write.

Enums§

Backpressure
What a logging thread does when its buffer is full.
ColorMode
When ConsoleSink applies ANSI colors.
Level
The severity of a log message, ordered from most severe (Error) to least severe (Trace).
TicklogError
Errors returned by ticklog operations.

Traits§

LogSink
A destination for formatted log lines.
LogSinkExt
Extension trait for LogSink providing per-sink level filtering.

Functions§

builder
Creates a Builder with default configuration.
init
Starts ticklog with a custom sink and default configuration. Equivalent to builder().sink(sink).build().
pin_thread
Pin the calling thread to the given set of logical CPUs.
warm_up
Prepares the calling thread for logging by allocating its buffer up front, moving the one-time, first-log allocation off a latency-sensitive path.