Async API ​
The async module provides non-blocking asynchronous logging with configurable buffering, background processing, and high-throughput capabilities.
Quick Reference: Method Aliases ​
| Full Method | Alias(es) | Description |
|---|---|---|
init() | create() | Initialize async logger |
deinit() | destroy() | Deinitialize async logger |
queue() | enqueue(), push(), logMsg() | Queue log message |
getStats() | statistics() | Get statistics |
resetStats() | clearStats() | Reset statistics |
queueDepth() | depth(), pending() | Get queue depth |
isQueueEmpty() | empty() | Check if queue empty |
availableCapacity() | capacityLeft(), freeSlots() | Get available queue slots |
queueUtilization() | utilization() | Get queue utilization ratio |
isNearCapacity() | nearCapacity(), isBackpressured() | Check backpressure threshold |
waitUntilDrained() | waitForDrain(), drain() | Wait for queue drain with timeout |
setOverflowCallback() | onOverflow() | Set overflow callback |
setFlushCallback() | onFlush() | Set flush callback |
setWorkerStartCallback() | onWorkerStart() | Set worker start callback |
setWorkerStopCallback() | onWorkerStop() | Set worker stop callback |
setBatchProcessedCallback() | onBatchProcessed() | Set batch processed callback |
setLatencyThresholdExceededCallback() | onLatencyThresholdExceeded() | Set latency threshold exceeded callback |
setFullCallback() | onFull() | Set full callback |
setEmptyCallback() | onEmpty() | Set empty callback |
setErrorCallback() | onError() | Set error callback |
isFull() | full() | Check if full |
startWorker() | begin() | Start worker |
stop() | halt(), end() | Stop async logger |
write() | writeData() | Write data (AsyncFileWriter) |
writeLine() | writeLn() | Write line (AsyncFileWriter) |
flushSync() | flush() | Flush synchronously (AsyncFileWriter) |
startAutoFlush() | startFlush() | Start auto flush (AsyncFileWriter) |
bytesWritten() | written() | Get bytes written (AsyncFileWriter) |
Overview ​
const logly = @import("logly");
const AsyncLogger = logly.AsyncLogger;
const AsyncFileWriter = logly.AsyncFileWriter;Centralized Configuration ​
Async logging can be enabled through the central Config struct:
var config = logly.Config.default();
config.async_config = .{
.enabled = true,
.buffer_size = 16384,
.batch_size = 128,
.flush_interval_ms = 50,
};
const logger = try logly.Logger.initWithConfig(allocator, config);Or use the fluent API:
const config = logly.Config.default().withAsync();Types ​
AsyncLogger ​
The main async logging struct with ring buffer and background worker.
pub const AsyncLogger = struct {
allocator: std.mem.Allocator,
config: AsyncConfig,
buffer: RingBuffer,
stats: AsyncStats,
worker_thread: ?std.Thread,
sinks: std.ArrayList(*Sink),
};AsyncConfig ​
Configuration available through Config.AsyncConfig:
pub const AsyncConfig = struct {
/// Enable async logging.
enabled: bool = false,
/// Buffer size for async queue.
buffer_size: usize = 8192,
/// Batch size for flushing.
batch_size: usize = 100,
/// Flush interval in milliseconds.
flush_interval_ms: u64 = 100,
/// Minimum time between flushes to avoid thrashing.
min_flush_interval_ms: u64 = 0,
/// Maximum latency before forcing a flush.
max_latency_ms: u64 = 5000,
/// What to do when buffer is full.
overflow_policy: OverflowPolicy = .drop_oldest,
/// Auto-start worker thread.
background_worker: bool = true,
/// Enable arena allocator for batch processing (reduces malloc overhead).
use_arena: bool = false,
pub const OverflowPolicy = enum {
drop_oldest,
drop_newest,
block,
};
};OverflowPolicy ​
What to do when the buffer is full.
pub const OverflowPolicy = enum {
/// Drop the oldest entries to make room
drop_oldest,
/// Drop new entries (block if blocking enabled)
drop_newest,
/// Block until space is available
block,
/// Expand buffer dynamically
expand,
};WorkerPriority ​
Worker thread priority levels.
pub const WorkerPriority = enum {
low,
normal,
high,
realtime,
};AsyncStats ​
Statistics for async operations.
pub const AsyncStats = struct {
records_queued: std.atomic.Value(u64),
records_written: std.atomic.Value(u64),
records_dropped: std.atomic.Value(u64),
flush_count: std.atomic.Value(u64),
total_latency_ns: std.atomic.Value(u64),
max_latency_ns: std.atomic.Value(u64),
buffer_high_watermark: std.atomic.Value(u64),
};RingBuffer ​
Lock-free ring buffer for async message queuing.
pub const RingBuffer = struct {
entries: []BufferEntry,
head: std.atomic.Value(usize),
tail: std.atomic.Value(usize),
capacity: usize,
};AsyncFileWriter ​
Optimized async file writer with buffering.
pub const AsyncFileWriter = struct {
allocator: std.mem.Allocator,
config: FileWriterConfig,
write_buffer: []u8,
buffer_pos: usize,
stats: FileWriterStats,
background_thread: ?std.Thread,
};FileWriterConfig ​
Configuration for async file writing.
pub const FileWriterConfig = struct {
/// Path to the log file
file_path: []const u8,
/// Write buffer size
buffer_size: usize = 64 * 1024, // 64KB
/// Auto-flush interval in milliseconds
flush_interval_ms: u64 = 1000,
/// Sync to disk on flush
sync_on_flush: bool = false,
/// Enable direct I/O (bypass OS cache)
direct_io: bool = false,
/// Create parent directories if needed
create_dirs: bool = true,
/// Append to existing file
append: bool = true,
};AsyncLogger Methods ​
init ​
Create a new async logger with default configuration.
Alias: create
pub fn init(allocator: std.mem.Allocator) !*AsyncLoggerinitWithConfig ​
Create a new async logger with custom configuration.
pub fn initWithConfig(allocator: std.mem.Allocator, config: AsyncConfig) !*AsyncLoggerdeinit ​
Clean up resources and stop worker thread.
Alias: destroy
pub fn deinit(self: *AsyncLogger) voidstart ​
Start the background worker thread.
pub fn start(self: *AsyncLogger) !voidstop ​
Stop the background worker and flush remaining logs.
pub fn stop(self: *AsyncLogger) voidlog ​
Queue a log record for async processing.
pub fn log(self: *AsyncLogger, record: *const Record) !voidaddSink ​
Add a sink for the async logger to write to.
pub fn addSink(self: *AsyncLogger, sink: *Sink) !voidflush ​
Force flush all pending records.
pub fn flush(self: *AsyncLogger) voidgetStats ​
Get current async statistics.
pub fn getStats(self: *const AsyncLogger) AsyncStatsavailableCapacity ​
Get available slots before the queue reaches capacity.
pub fn availableCapacity(self: *AsyncLogger) usizequeueUtilization ​
Get queue utilization as a ratio in [0.0, 1.0].
pub fn queueUtilization(self: *AsyncLogger) f64isNearCapacity ​
Check whether queue utilization is above a threshold.
pub fn isNearCapacity(self: *AsyncLogger, threshold: f64) boolwaitUntilDrained ​
Wait for the queue to drain until timeout.
pub fn waitUntilDrained(self: *AsyncLogger, timeout_ms: u64) boolscratchAllocator ​
Returns the arena allocator if enabled, otherwise returns the main allocator. Use for temporary allocations that can be batch-freed.
pub fn scratchAllocator(self: *AsyncLogger) std.mem.AllocatorresetArena ​
Resets the arena allocator, freeing all temporary allocations. Call periodically after processing batches.
pub fn resetArena(self: *AsyncLogger) voidAsyncStats Methods ​
Getter Methods ​
getQueued ​
Get total records queued.
pub fn getQueued(self: *const AsyncStats) u64getWritten ​
Get total records written.
pub fn getWritten(self: *const AsyncStats) u64getDropped ​
Get total records dropped.
pub fn getDropped(self: *const AsyncStats) u64getFlushCount ​
Get total flush operations.
pub fn getFlushCount(self: *const AsyncStats) u64inFlight ​
Get queued-but-not-yet-written records.
pub fn inFlight(self: *const AsyncStats) u64getMaxQueueDepth ​
Get maximum queue depth reached.
pub fn getMaxQueueDepth(self: *const AsyncStats) u64getBufferOverflows ​
Get total buffer overflows.
pub fn getBufferOverflows(self: *const AsyncStats) u64Boolean Checks ​
hasDropped ​
Check if any records have been dropped.
pub fn hasDropped(self: *const AsyncStats) boolhasOverflows ​
Check if any buffer overflows occurred.
pub fn hasOverflows(self: *const AsyncStats) boolRate Calculations ​
dropRate ​
Calculate the percentage of dropped records.
pub fn dropRate(self: *const AsyncStats) f64successRate ​
Calculate successful write rate (0.0 - 1.0).
pub fn successRate(self: *const AsyncStats) f64throughputRecordsPerSecond ​
Calculate records throughput per second.
pub fn throughputRecordsPerSecond(self: *const AsyncStats, elapsed_seconds: f64) f64averageLatencyMs ​
Get average latency in milliseconds.
pub fn averageLatencyMs(self: *const AsyncStats) f64averageLatencyNs ​
Get average latency in nanoseconds.
pub fn averageLatencyNs(self: *const AsyncStats) u64Reset ​
reset ​
Reset all statistics to initial state.
pub fn reset(self: *AsyncStats) voidAsyncFileWriter Methods ​
init ​
Create a new async file writer.
pub fn init(allocator: std.mem.Allocator, config: FileWriterConfig) !AsyncFileWriterdeinit ​
Clean up and close file.
pub fn deinit(self: *AsyncFileWriter) voidwrite ​
Write data to the buffer (async).
pub fn write(self: *AsyncFileWriter, data: []const u8) !voidflush ​
Flush buffer to file.
pub fn flush(self: *AsyncFileWriter) !voidPresets ​
highThroughput ​
Optimized for maximum throughput.
pub fn highThroughput() AsyncConfig {
return .{
.buffer_size = 65536,
.flush_interval_ms = 500,
.batch_size = 256,
.overflow_policy = .drop_oldest,
.preallocate_buffers = true,
};
}lowLatency ​
Optimized for minimum latency.
pub fn lowLatency() AsyncConfig {
return .{
.buffer_size = 1024,
.flush_interval_ms = 10,
.batch_size = 16,
.overflow_policy = .block,
};
}balanced ​
Balance between throughput and latency.
pub fn balanced() AsyncConfig {
return .{
.buffer_size = 8192,
.flush_interval_ms = 100,
.batch_size = 64,
};
}noDrop ​
Never drop messages (may block).
pub fn noDrop() AsyncConfig {
return .{
.buffer_size = 16384,
.overflow_policy = .block,
};
}Usage Example ​
const std = @import("std");
const logly = @import("logly");
pub fn main() !void {
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
defer _ = gpa.deinit();
const allocator = gpa.allocator();
// Create async logger with high throughput config
var async_logger = try logly.AsyncLogger.init(
allocator,
logly.AsyncPresets.highThroughput(),
);
defer async_logger.deinit();
// Start the background worker
try async_logger.start();
defer async_logger.stop();
// Log messages (non-blocking)
for (0..1000) |i| {
_ = i;
// async_logger.log(&record);
}
// Check statistics
const stats = async_logger.getStats();
std.debug.print("Queued: {d}, Written: {d}, Dropped: {d}\\n", .{
stats.getQueued(),
stats.getWritten(),
stats.getDropped(),
});
std.debug.print("Drop rate: {d:.2}%\\n", .{stats.dropRate() * 100});
}See Also ​
- Async Logging Guide - In-depth async logging guide
- Thread Pool API - Parallel logging
- Configuration Guide - Full configuration options
Aliases ​
The AsyncLogger provides convenience aliases:
| Alias | Method |
|---|---|
enqueue | queue |
push | queue |
logMsg | queue |
statistics | getStats |
depth | queueDepth |
pending | queueDepth |
capacityLeft | availableCapacity |
freeSlots | availableCapacity |
utilization | queueUtilization |
nearCapacity | isNearCapacity |
waitForDrain | waitUntilDrained |
drain | waitUntilDrained |
begin | startWorker |
halt | stop |
end | stop |
Additional Methods ​
isRunning() bool- Returns true if the async logger worker is runningbufferCapacity() usize- Returns the buffer capacityisFull() bool- Returns true if the buffer is at capacityqueueDepth() usize- Returns current queue depthisQueueEmpty() bool- Returns true if queue is emptyavailableCapacity() usize- Returns remaining queue slotsqueueUtilization() f64- Returns current queue utilization ratioisNearCapacity(threshold: f64) bool- Returns true when utilization exceeds thresholdwaitUntilDrained(timeout_ms: u64) bool- Waits for queue drain until timeoutresetStats() void- Resets all statistics
Callbacks ​
// Set callbacks for various events
logger.setOverflowCallback(onOverflow);
logger.setFlushCallback(onFlush);
logger.setWorkerStartCallback(onWorkerStart);
logger.setWorkerStopCallback(onWorkerStop);
logger.setBatchProcessedCallback(onBatchProcessed);
logger.setLatencyThresholdExceededCallback(onLatencyExceeded);
logger.setFullCallback(onBufferFull);
logger.setEmptyCallback(onBufferEmpty);
logger.setErrorCallback(onError);