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 Method | Alias(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 Method | Alias(es) | Level | Description |
|---|---|---|---|
trace() | - | TRACE | Verbose debugging |
debug() | - | DEBUG | Debug information |
info() | - | INFO | General information |
notice() | note() | NOTICE | Important notices |
success() | - | SUCCESS | Success messages |
warning() | warn() | WARNING | Warning messages |
err() | @"error"() | ERROR | Error messages |
fail() | failure() | FAIL | Failure messages |
critical() | crit() | CRITICAL | Critical errors |
fatal() | panic() | FATAL | Fatal errors |
Formatted Logging Aliases ​
| Full Method | Alias(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 Method | Alias(es) | Description |
|---|---|---|
init() | create() | Initialize logger |
deinit() | destroy() | Deinitialize logger |
Other Aliases ​
| Full Method | Alias(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
Loggeror 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
Loggeror 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).
// 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.
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.
// 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:
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:
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()
// 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).
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.
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.
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.
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.
logger.enableMetrics();getMetrics() ?Metrics.Snapshot ​
Returns a snapshot of current logging metrics, or null if metrics are not enabled.
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.
try logger.setTraceContext("trace-abc-123", "span-xyz-789");setCorrelationId(correlation_id: []const u8) !void ​
Sets a correlation ID for request correlation across services.
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.
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.
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.
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.
// 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.
// 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.
if (logger.hasCustomLevel("audit")) {
try logger.custom("audit", "User action", @src());
}getCustomLevelCount() usize ​
Returns the count of registered custom levels.
std.debug.print("Custom levels: {}\n", .{logger.getCustomLevelCount()});Color Code Reference:
| Code | Color | Code | Color |
|---|---|---|---|
31 | Red | 91 | Bright Red |
32 | Green | 92 | Bright Green |
33 | Yellow | 93 | Bright Yellow |
34 | Blue | 94 | Bright Blue |
35 | Magenta | 95 | Bright Magenta |
36 | Cyan | 96 | Bright Cyan |
37 | White | 97 | Bright 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:
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:
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.
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):
| Value | Result |
|---|---|
@src() | Includes file:line:column in output (when enabled in config) |
null | No source location in output |
// 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).
try logger.trace("Detailed trace info", @src()); // With source location
try logger.trace("Trace without location", null); // Without source locationdebug(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.
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.
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. Thefsuffix variants are maintained for backward compatibility.
All logging methods accept a format string and arguments in the same call:
// 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 ​
// 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:
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 locationThe 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.
