Skip to content

Logger API ​

The Logger struct is the central component of the Logly library, orchestrating all logging operations, sink management, configuration, and enterprise features like filtering, sampling, redaction, metrics, and distributed tracing.

Quick Reference: Method Aliases ​

Sink Management Aliases ​

Full MethodAlias(es)Description
addSink()add()Add a new sink
removeSink()remove()Remove a sink by ID
removeAllSinks()removeAll(), clear()Remove all sinks
getSinkCount()count(), sinkCount()Get number of sinks
enableSink()enable()Enable a sink by ID
disableSink()disable()Disable a sink by ID
getSink()sink()Get sink by ID

Logging Level Aliases ​

Full MethodAlias(es)LevelDescription
trace()-TRACEVerbose debugging
debug()-DEBUGDebug information
info()-INFOGeneral information
notice()note()NOTICEImportant notices
success()-SUCCESSSuccess messages
warning()warn()WARNINGWarning messages
err()@"error"()ERRORError messages
fail()failure()FAILFailure messages
critical()crit()CRITICALCritical errors
fatal()panic()FATALFatal errors

Formatted Logging Aliases ​

Full MethodAlias(es)Description
tracef()-Formatted TRACE log
debugf()-Formatted DEBUG log
infof()-Formatted INFO log
noticef()notef()Formatted NOTICE log
successf()-Formatted SUCCESS log
warningf()warnf()Formatted WARNING log
errf()errorf()Formatted ERROR log
failf()failuref()Formatted FAIL log
criticalf()critf()Formatted CRITICAL log
fatalf()panicf()Formatted FATAL log
customf()-Formatted custom level log

Lifecycle Aliases ​

Full MethodAlias(es)Description
init()create()Initialize logger
deinit()destroy()Deinitialize logger

Other Aliases ​

Full MethodAlias(es)Description
getStats()stats()Get logger statistics
getRecordCount()recordCount()Get total record count
getUptime()uptime()Get logger uptime in seconds
scoped()-Create scoped logger
ctx()context()Get context logger
with()-Get persistent context logger

Lifecycle Methods ​

init(allocator: std.mem.Allocator) !*Logger ​

Initializes a new Logger instance with default configuration.

  • allocator: The memory allocator used for internal structures.
  • Returns: A pointer to the initialized Logger or an error.

initWithConfig(allocator: std.mem.Allocator, config: Config) !*Logger ​

Initializes a new Logger instance with a specific configuration preset.

  • allocator: The memory allocator used for internal structures.
  • config: The configuration to use (e.g., ConfigPresets.production()).
  • Returns: A pointer to the initialized Logger or an error.

deinit() void ​

Deinitializes the logger, freeing all allocated resources including sinks, context maps, custom levels, and enterprise components.

configure(config: Config) void ​

Updates the global configuration of the logger in a thread-safe manner.

Distributed Logging ​

withTrace(trace_id: ?[]const u8, span_id: ?[]const u8) DistributedLogger ​

Creates a lightweight DistributedLogger handle that wraps the main logger but automatically injects the specified trace context into every log message. This is the preferred way to handle distributed tracing in concurrent environments (like request handlers).

zig
// In a request handler
const req_logger = logger.withTrace(header_trace_id, header_span_id);
try req_logger.info("Processing request"); 
// Result: { "trace_id": "...", "span_id": "...", "message": "Processing request" }

setTraceContext(trace_id: []const u8, span_id: ?[]const u8) !void ​

Legacy. Sets the global trace context for the logger. This affects all subsequent logs from this logger instance. Not recommended for multi-threaded applications where different threads handle different requests.

clearTraceContext() void ​

Legacy. Clears the global trace context.

Arena Allocator Methods ​

When use_arena_allocator is enabled in config, the logger uses an arena allocator for temporary allocations, improving performance.

scratchAllocator() std.mem.Allocator ​

Returns the arena allocator if enabled, otherwise returns the main allocator. Use for temporary allocations that can be batch-freed.

zig
const allocator = logger.scratchAllocator();
const temp = try allocator.alloc(u8, 1024);
defer allocator.free(temp);

resetArena() void ​

Resets the arena allocator, freeing all temporary allocations at once. Call periodically in high-throughput scenarios.

zig
// Reset every 1000 logs to prevent memory growth
if (i % 1000 == 0) {
    logger.resetArena();
}

Callbacks ​

The Logger supports several callbacks for monitoring and extending behavior. These are set directly on the Logger instance fields.

on_record_logged: ?*const fn (Level, []const u8, *const Record) void ​

Invoked when a record is successfully logged.

  • Parameters: level, message, record

on_record_filtered: ?*const fn ([]const u8, *const Record) void ​

Invoked when a record is filtered/dropped before output (e.g., by level or filter).

  • Parameters: reason, record

on_sink_error: ?*const fn ([]const u8, []const u8) void ​

Invoked when a sink encounters an error.

  • Parameters: sink_name, error_msg

on_logger_initialized: ?*const fn (*const LoggerStats) void ​

Invoked when the logger is initialized.

  • Parameters: logger_stats

on_logger_destroyed: ?*const fn (*const LoggerStats) void ​

Invoked when the logger is destroyed.

  • Parameters: final_stats

LoggerStats ​

The LoggerStats struct provides comprehensive statistics for logging operations with cross-platform atomic counters:

zig
pub const LoggerStats = struct {
    /// Total number of records successfully logged.
    total_records_logged: std.atomic.Value(Constants.AtomicUnsigned),
    /// Number of records filtered/dropped before output.
    records_filtered: std.atomic.Value(Constants.AtomicUnsigned),
    /// Number of sink write errors encountered.
    sink_errors: std.atomic.Value(Constants.AtomicUnsigned),
    /// Number of currently active sinks.
    active_sinks: std.atomic.Value(u32),
    /// Total bytes written across all sinks.
    bytes_written: std.atomic.Value(Constants.AtomicUnsigned),

    /// Calculate records per second (requires elapsed seconds).
    pub fn recordsPerSecond(self: *const LoggerStats, elapsed_seconds: f64) f64;

    /// Calculate average bytes per record.
    pub fn avgBytesPerRecord(self: *const LoggerStats) f64;

    /// Calculate filter rate (0.0 - 1.0).
    pub fn filterRate(self: *const LoggerStats) f64;

    /// Calculate sink error rate (0.0 - 1.0).
    pub fn sinkErrorRate(self: *const LoggerStats) f64;

    /// Returns true if any records were filtered.
    pub fn hasFiltered(self: *const LoggerStats) bool;

    /// Returns true if any sink errors occurred.
    pub fn hasSinkErrors(self: *const LoggerStats) bool;

    /// Returns total records logged as u64.
    pub fn getTotalLogged(self: *const LoggerStats) u64;

    /// Returns filtered records count as u64.
    pub fn getFiltered(self: *const LoggerStats) u64;

    /// Returns sink errors count as u64.
    pub fn getSinkErrors(self: *const LoggerStats) u64;

    /// Returns bytes written as u64.
    pub fn getBytesWritten(self: *const LoggerStats) u64;

    /// Returns active sinks count as u32.
    pub fn getActiveSinks(self: *const LoggerStats) u32;

    /// Calculate bytes per second (requires elapsed time in ms).
    pub fn bytesPerSecond(self: *const LoggerStats, elapsed_ms: i64) f64;
};

Usage Example:

zig
const stats = logger.getStats();

// Basic stats
std.debug.print("Total logged: {d}\n", .{stats.getTotalLogged()});
std.debug.print("Bytes written: {d}\n", .{stats.getBytesWritten()});

// Rate calculations
std.debug.print("Filter rate: {d:.2}%\n", .{stats.filterRate() * 100});
std.debug.print("Error rate: {d:.2}%\n", .{stats.sinkErrorRate() * 100});

// State checks
if (stats.hasSinkErrors()) {
    std.debug.print("Sink errors detected!\n", .{});
}

// Throughput (with elapsed time)
const elapsed_ms = std.time.milliTimestamp() - start_time;
std.debug.print("Throughput: {d:.2} bytes/sec\n", .{stats.bytesPerSecond(elapsed_ms)});

log_callback: ?*const fn (*const Record) anyerror!void ​

A generic callback invoked for every log record. Can be used for custom processing or integration with other systems.

Sink Management ​

addSink(config: SinkConfig) !usize ​

Adds a new output sink (e.g., console, file) with the specified configuration.

  • Returns: The unique ID of the added sink.
  • Alias: add()
zig
// Both are equivalent
_ = try logger.addSink(.{ .path = "app.log" });
_ = try logger.add(.{ .path = "app.log" });

removeSink(id: usize) void ​

Removes a sink by its ID.

  • Alias: remove()

removeAllSinks() usize ​

Removes all sinks and returns the count of removed sinks.

  • Aliases: removeAll(), clear()

enableSink(id: usize) void ​

Enables a specific sink by its ID, allowing it to process log records.

disableSink(id: usize) void ​

Disables a specific sink by its ID, preventing it from processing log records.

getSinkCount() usize ​

Returns the current number of sinks.

  • Aliases: count(), sinkCount()

enableAsync() void ​

Enables async logging if an async logger is configured. This allows log records to be processed asynchronously in the background.

disableAsync() void ​

Disables async logging if an async logger is configured. Forces synchronous processing of log records.

isAsyncEnabled() bool ​

Returns true if async logging is enabled and running, false otherwise.

enableAutoFlush() void ​

Enables automatic flushing of all sinks after every log operation. Ensures immediate output visibility but may impact performance.

disableAutoFlush() void ​

Disables automatic flushing. Sinks will only flush when their buffers are full or when manually flushed.

isAutoFlushEnabled() bool ​

Returns true if auto-flush is enabled, false otherwise.

Context Management ​

bind(key: []const u8, value: std.json.Value) !void ​

Binds a structured context variable to the logger. These variables are included in every log record (especially useful for JSON output).

zig
try logger.bind("user_id", .{ .string = "usr_12345" });
try logger.bind("request_id", .{ .string = "req_abc" });

unbind(key: []const u8) void ​

Removes a previously bound context variable.

clearBindings() void ​

Removes all bound context variables.

Enterprise Features ​

Filtering ​

setFilter(filter: *Filter) void ​

Sets a filter for rule-based log filtering. Allows filtering by level, message patterns, modules, and more.

zig
var filter = logly.Filter.init(allocator);
defer filter.deinit();
try filter.addMinLevel(.warning);
logger.setFilter(&filter);

Sampling ​

setSampler(sampler: *Sampler) void ​

Sets a sampler for log sampling and rate limiting. Useful for high-volume logging scenarios.

zig
var sampler = logly.Sampler.init(allocator, logly.SamplerPresets.sample10Percent());
defer sampler.deinit();
logger.setSampler(&sampler);

Redaction ​

setRedactor(redactor: *Redactor) void ​

Sets a redactor for sensitive data masking in log messages.

zig
var redactor = logly.Redactor.init(allocator);
defer redactor.deinit();
try redactor.addPattern("password", .keyword, "password", "[REDACTED]");
logger.setRedactor(&redactor);

Metrics ​

enableMetrics() void ​

Enables metrics collection for logging performance monitoring.

zig
logger.enableMetrics();

getMetrics() ?Metrics.Snapshot ​

Returns a snapshot of current logging metrics, or null if metrics are not enabled.

zig
if (logger.getMetrics()) |metrics| {
    std.debug.print("Total records: {}\n", .{metrics.total_records});
    std.debug.print("Errors: {}\n", .{metrics.error_count});
}

Distributed Tracing ​

setTraceContext(trace_id: []const u8, span_id: ?[]const u8) !void ​

Sets the trace context for distributed tracing. All subsequent log records will include these IDs.

zig
try logger.setTraceContext("trace-abc-123", "span-xyz-789");

setCorrelationId(correlation_id: []const u8) !void ​

Sets a correlation ID for request correlation across services.

zig
try logger.setCorrelationId("corr-12345");

clearTraceContext() void ​

Clears all trace context (trace_id, span_id, correlation_id).

startSpan(name: []const u8) !SpanContext ​

Creates a new span for tracing. The span automatically generates a span ID and tracks duration.

zig
const span = try logger.startSpan("database_query");
defer span.end(null) catch {};

// Your operation here
try logger.info("Executing query");

Module-Level Logging ​

setModuleLevel(module: []const u8, level: Level) !void ​

Sets a log level filter for a specific module.

zig
try logger.setModuleLevel("database", .debug);
try logger.setModuleLevel("http.server", .info);

getModuleLevel(module: []const u8) ?Level ​

Gets the log level for a specific module, or null if not set.

scoped(module: []const u8) ScopedLogger ​

Returns a scoped logger for a specific module.

zig
const db_logger = logger.scoped("database");
try db_logger.info("Connection established");

Custom Levels ​

addCustomLevel(name: []const u8, priority: u8, color: []const u8) !void ​

Registers a custom log level with a name, priority, and ANSI color code. Returns error.LevelAlreadyExists if level name already exists.

zig
// Color code only (no \x1b[ prefix needed)
try logger.addCustomLevel("audit", 35, "35");     // Magenta
try logger.addCustomLevel("security", 55, "91");  // Bright red
try logger.addCustomLevel("notice", 22, "36;1");  // Bold cyan
try logger.addCustomLevel("alert", 42, "31;4");   // Underline red

// Handle duplicate error
logger.addCustomLevel("audit", 40, "31") catch |err| {
    if (err == error.LevelAlreadyExists) {
        std.debug.print("Level already exists!\n", .{});
    }
};

updateCustomLevel(name: []const u8, priority: u8, color: []const u8) !void ​

Updates an existing custom level, or creates it if it doesn't exist. Use this when you want to allow updates without errors.

zig
// This will update if exists, or create if not
try logger.updateCustomLevel("audit", 40, "91;1");

hasCustomLevel(name: []const u8) bool ​

Checks if a custom level with the given name exists.

zig
if (logger.hasCustomLevel("audit")) {
    try logger.custom("audit", "User action", @src());
}

getCustomLevelCount() usize ​

Returns the count of registered custom levels.

zig
std.debug.print("Custom levels: {}\n", .{logger.getCustomLevelCount()});

Color Code Reference:

CodeColorCodeColor
31Red91Bright Red
32Green92Bright Green
33Yellow93Bright Yellow
34Blue94Bright Blue
35Magenta95Bright Magenta
36Cyan96Bright Cyan
37White97Bright White

Modifiers: Add with semicolon: 31;1 (bold), 34;4 (underline), 32;7 (reverse)

removeCustomLevel(name: []const u8) void ​

Removes a previously registered custom level.

custom(level_name: []const u8, message: []const u8, src: ?std.builtin.SourceLocation) !void ​

Logs using a registered custom level. The entire line is colored:

zig
try logger.addCustomLevel("audit", 35, "35;1"); // Bold magenta
try logger.custom("audit", "User login detected", @src());
// Output: [2024-01-15 10:30:45] [AUDIT] myfile.zig:42:0: User login detected (bold magenta)

customf(level_name: []const u8, comptime fmt: []const u8, args: anytype, src: ?std.builtin.SourceLocation) !void ​

Formatted logging with custom levels:

zig
try logger.customf("audit", "User {s} logged in from {s}", .{ "alice", "10.0.0.1" }, @src());

Callbacks ​

setLogCallback(callback: *const fn (*const Record) anyerror!void) void ​

Sets a callback function that is invoked for each log record. Useful for integration with external systems.

setColorCallback(callback: *const fn (Level, []const u8) []const u8) void ​

Sets a custom color callback for overriding default level colors.

Control Methods ​

enable() void ​

Enables the logger (logging is enabled by default).

disable() void ​

Temporarily disables all logging.

flush() !void ​

Flushes all sinks, ensuring all buffered data is written. Note: When config.auto_flush is true (default), this is called automatically after each log operation.

logSystemDiagnostics(src: ?std.builtin.SourceLocation) !void ​

Collects OS/CPU/memory (and optional per-drive storage) and logs them as a single info record. Honors config.include_drive_diagnostics and uses the logger's scratch allocator.

zig
var cfg = logly.Config.default();
cfg.include_drive_diagnostics = true;

const logger = try logly.Logger.initWithConfig(allocator, cfg);
try logger.logSystemDiagnostics(@src());

Logging Methods ​

All logging methods accept an optional source location parameter. Pass @src() to enable clickable file:line:column output in terminal, or null if source location is not needed.

Source Location Parameter ​

The src parameter is optional (type ?std.builtin.SourceLocation):

ValueResult
@src()Includes file:line:column in output (when enabled in config)
nullNo source location in output
zig
// With source location - recommended for debugging
try logger.info("Application started", @src());

// Without source location - useful for high-volume logging
try logger.debug("Processing item", null);

trace(message: []const u8, src: ?std.builtin.SourceLocation) !void ​

Logs a message at the TRACE level (Priority 5).

zig
try logger.trace("Detailed trace info", @src());  // With source location
try logger.trace("Trace without location", null); // Without source location

debug(message: []const u8, src: ?std.builtin.SourceLocation) !void ​

Logs a message at the DEBUG level (Priority 10).

info(message: []const u8, src: ?std.builtin.SourceLocation) !void ​

Logs a message at the INFO level (Priority 20).

notice(message: []const u8, src: ?std.builtin.SourceLocation) !void ​

Logs a message at the NOTICE level (Priority 22).

success(message: []const u8, src: ?std.builtin.SourceLocation) !void ​

Logs a message at the SUCCESS level (Priority 25).

warning(message: []const u8, src: ?std.builtin.SourceLocation) !void ​

Logs a message at the WARNING level (Priority 30).

  • Alias: warn()

err(message: []const u8, src: ?std.builtin.SourceLocation) !void ​

Logs a message at the ERROR level (Priority 40).

  • Alias: @"error"() (Zig keyword escape)

fail(message: []const u8, src: ?std.builtin.SourceLocation) !void ​

Logs a message at the FAIL level (Priority 45).

critical(message: []const u8, src: ?std.builtin.SourceLocation) !void ​

Logs a message at the CRITICAL level (Priority 50).

  • Alias: crit()

fatal(message: []const u8, src: ?std.builtin.SourceLocation) !void ​

Logs a message at the FATAL level (Priority 55). This is the highest severity level with white text on red background.

zig
try logger.fatal("System crash imminent!", @src());

custom(level_name: []const u8, message: []const u8, src: ?std.builtin.SourceLocation) !void ​

Logs a message using a user-defined custom level. The level must be registered first.

zig
try logger.addCustomLevel("audit", 35, "35;1");
try logger.custom("audit", "User login detected", @src());

Formatted Logging ​

Note: The main logging methods (info, debug, etc.) now directly support format strings and arguments. The f suffix variants are maintained for backward compatibility.

All logging methods accept a format string and arguments in the same call:

zig
// Recommended: Use main method with format string
try logger.info("User {s} connected from {s}", .{ "alice", "10.0.0.1" }, @src());
try logger.warn("Request took {d}ms", .{elapsed_ms}, @src());
try logger.crit("Failed after {d} retries: {s}", .{ retry_count, error_msg }, @src());

Legacy Format Methods (f-suffix) ​

These methods are maintained for backward compatibility:

tracef(comptime fmt: []const u8, args: anytype, src: ?std.builtin.SourceLocation) !void ​

debugf(comptime fmt: []const u8, args: anytype, src: ?std.builtin.SourceLocation) !void ​

infof(comptime fmt: []const u8, args: anytype, src: ?std.builtin.SourceLocation) !void ​

zig
// Legacy style (still works)
try logger.infof("User {s} connected from {s}", .{ "alice", "10.0.0.1" }, @src());

successf(comptime fmt: []const u8, args: anytype, src: ?std.builtin.SourceLocation) !void ​

warningf(comptime fmt: []const u8, args: anytype, src: ?std.builtin.SourceLocation) !void ​

  • Alias: warnf()

errf(comptime fmt: []const u8, args: anytype, src: ?std.builtin.SourceLocation) !void ​

  • Alias: errorf()

failf(comptime fmt: []const u8, args: anytype, src: ?std.builtin.SourceLocation) !void ​

criticalf(comptime fmt: []const u8, args: anytype, src: ?std.builtin.SourceLocation) !void ​

  • Alias: critf()

customf(level_name: []const u8, comptime fmt: []const u8, args: anytype, src: ?std.builtin.SourceLocation) !void ​

Source Location Display ​

When you enable show_filename and show_lineno in the configuration and pass @src() to logging calls, the output includes clickable file:line:column information:

zig
var config = logly.Config.default();
config.show_filename = true;
config.show_lineno = true;
logger.configure(config);

try logger.info(@src(), "This message has source location", .{});
// Output: [2024-01-15 10:30:45] [INFO] myfile.zig:42:0: This message has source location

The format file:line:column: is compatible with most terminals and IDEs, allowing you to click on it to jump directly to the source location.

Utility Methods ​

logError(message: []const u8, err_val: anyerror) !void ​

Logs an error with automatic error name resolution.

logTimed(level: Level, message: []const u8, start_time: i128) !i128 ​

Logs a message with elapsed time calculation.

getRecordCount() u64 ​

Returns the total number of log records processed.

getUptime() i64 ​

Returns the logger uptime in seconds.

Released under the MIT License.