---
url: /logly.zig/api/async.md
description: >-
  API reference for Logly.zig AsyncLogger and ring buffer. Non-blocking logging
  with configurable buffering, batch processing, and up to 36M ops/sec
  throughput.
---

# 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

```zig
const logly = @import("logly");
const AsyncLogger = logly.AsyncLogger;
const AsyncFileWriter = logly.AsyncFileWriter;
```

## Centralized Configuration

Async logging can be enabled through the central `Config` struct:

```zig
var config = logly.Config.default();
config.asyncConfig = .{
    .enabled = true,
    .bufferSize = 16384,
    .batchSize = 128,
    .flushIntervalMs = 50,
};
const logger = try logly.Logger.initWithConfig(allocator, config);
```

Or use the fluent API:

```zig
const config = logly.Config.default().withAsync();
```

## Types

### AsyncLogger

The main async logging struct with ring buffer and background worker.

```zig
pub const AsyncLogger = struct {
    allocator: std.mem.Allocator,
    config: AsyncConfig,
    buffer: RingBuffer,
    stats: AsyncStats,
    workerThread: ?std.Thread,
    sinks: std.ArrayList(*Sink),
};
```

### AsyncConfig

Configuration available through `Config.AsyncConfig`:

```zig
pub const AsyncConfig = struct {
    /// Enable async logging.
    enabled: bool = false,
    /// Buffer size for async queue.
    bufferSize: usize = 8192,
    /// Batch size for flushing.
    batchSize: usize = 100,
    /// Flush interval in milliseconds.
    flushIntervalMs: u64 = 100,
    /// Minimum time between flushes to avoid thrashing.
    minFlushIntervalMs: u64 = 0,
    /// Maximum latency before forcing a flush.
    maxLatencyMs: u64 = 5000,
    /// What to do when buffer is full.
    overflowPolicy: OverflowPolicy = .dropOldest,
    /// Auto-start worker thread.
    backgroundWorker: bool = true,
    /// Shutdown grace period in milliseconds to allow remaining records to flush.
    shutdownTimeoutMs: u64 = 1000,
    /// Enable priority queue logic to handle high-priority levels first.
    enable_priority_queue: bool = false,

    pub const OverflowPolicy = enum {
        dropOldest,
        dropNewest,
        block,
    };
};
```

### OverflowPolicy

What to do when the buffer is full.

```zig
pub const OverflowPolicy = enum {
    /// Drop the oldest entries to make room
    dropOldest,
    /// Drop new entries (block if blocking enabled)
    dropNewest,
    /// Block until space is available
    block,
    /// Expand buffer dynamically
    expand,
};
```

### WorkerPriority

Worker thread priority levels.

```zig
pub const WorkerPriority = enum {
    low,
    normal,
    high,
    realtime,
};
```

### AsyncStats

Statistics for async operations.

```zig
pub const AsyncStats = struct {
    recordsQueued: std.atomic.Value(u64),
    recordsWritten: std.atomic.Value(u64),
    recordsDropped: std.atomic.Value(u64),
    flushCount: std.atomic.Value(u64),
    totalLatencyNs: std.atomic.Value(u64),
    maxLatencyNs: std.atomic.Value(u64),
    buffer_high_watermark: std.atomic.Value(u64),
};
```

### RingBuffer

Lock-free ring buffer for async message queuing.

```zig
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.

```zig
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.

```zig
pub const FileWriterConfig = struct {
    /// Path to the log file
    filePath: []const u8,
    /// Write buffer size
    bufferSize: usize = 64 * 1024, // 64KB
    /// Auto-flush interval in milliseconds
    flushIntervalMs: u64 = 1000,
    /// Sync to disk on flush
    syncOnFlush: 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`

```zig
pub fn init(allocator: std.mem.Allocator) !*AsyncLogger
```

### initWithConfig

Create a new async logger with custom configuration. Uses `config.io` if set, otherwise defaults to `logly.defaultIo()`.

```zig
pub fn initWithConfig(allocator: std.mem.Allocator, config: AsyncConfig) !*AsyncLogger
```

### initWithIo

Create a new async logger with an explicit `std.Io` handle and custom configuration.

```zig
pub fn initWithIo(allocator: std.mem.Allocator, io_handle: std.Io, config: AsyncConfig) !*AsyncLogger
```

### deinit

Clean up resources and stop worker thread.

**Alias:** `destroy`

```zig
pub fn deinit(self: *AsyncLogger) void
```

### start

Start the background worker thread.

```zig
pub fn start(self: *AsyncLogger) !void
```

### stop

Stop the background worker and flush remaining logs.

```zig
pub fn stop(self: *AsyncLogger) void
```

### log

Queue a log record for async processing.

```zig
pub fn log(self: *AsyncLogger, record: *const Record) !void
```

### addSink

Add a sink for the async logger to write to.

```zig
pub fn addSink(self: *AsyncLogger, sink: *Sink) !void
```

### flush

Force flush all pending records.

```zig
pub fn flush(self: *AsyncLogger) void
```

### drainAndFlush

Wait up to `timeoutMs` for the queue to drain completely, then flush the buffer to ensure durability before shutting down.

```zig
pub fn drainAndFlush(self: *AsyncLogger, timeoutMs: u64) !void
```

### isBackpressured

Alias for `isNearCapacity(0.9)`. Signals if the async system is struggling to keep up with the log volume.

```zig
pub fn isBackpressured(self: *AsyncLogger) bool
```

### getStats

Get current async statistics.

```zig
pub fn getStats(self: *const AsyncLogger) AsyncStats
```

### availableCapacity

Get available slots before the queue reaches capacity.

```zig
pub fn availableCapacity(self: *AsyncLogger) usize
```

### queueUtilization

Get queue utilization as a ratio in `[0.0, 1.0]`.

```zig
pub fn queueUtilization(self: *AsyncLogger) f64
```

### isNearCapacity

Check whether queue utilization is above a threshold.

```zig
pub fn isNearCapacity(self: *AsyncLogger, threshold: f64) bool
```

### waitUntilDrained

Wait for the queue to drain until timeout.

```zig
pub fn waitUntilDrained(self: *AsyncLogger, timeoutMs: u64) bool
```

## AsyncStats Methods

### Getter Methods

#### getQueued

Get total records queued.

```zig
pub fn getQueued(self: *const AsyncStats) u64
```

#### getWritten

Get total records written.

```zig
pub fn getWritten(self: *const AsyncStats) u64
```

#### getDropped

Get total records dropped.

```zig
pub fn getDropped(self: *const AsyncStats) u64
```

#### getFlushCount

Get total flush operations.

```zig
pub fn getFlushCount(self: *const AsyncStats) u64
```

#### inFlight

Get queued-but-not-yet-written records.

```zig
pub fn inFlight(self: *const AsyncStats) u64
```

#### getMaxQueueDepth

Get maximum queue depth reached.

```zig
pub fn getMaxQueueDepth(self: *const AsyncStats) u64
```

#### getBufferOverflows

Get total buffer overflows.

```zig
pub fn getBufferOverflows(self: *const AsyncStats) u64
```

### Boolean Checks

#### hasDropped

Check if any records have been dropped.

```zig
pub fn hasDropped(self: *const AsyncStats) bool
```

#### hasOverflows

Check if any buffer overflows occurred.

```zig
pub fn hasOverflows(self: *const AsyncStats) bool
```

### Rate Calculations

#### dropRate

Calculate the percentage of dropped records.

```zig
pub fn dropRate(self: *const AsyncStats) f64
```

#### successRate

Calculate successful write rate (0.0 - 1.0).

```zig
pub fn successRate(self: *const AsyncStats) f64
```

#### throughputRecordsPerSecond

Calculate records throughput per second.

```zig
pub fn throughputRecordsPerSecond(self: *const AsyncStats, elapsedSeconds: f64) f64
```

#### averageLatencyMs

Get average latency in milliseconds.

```zig
pub fn averageLatencyMs(self: *const AsyncStats) f64
```

#### averageLatencyNs

Get average latency in nanoseconds.

```zig
pub fn averageLatencyNs(self: *const AsyncStats) u64
```

### Reset

#### reset

Reset all statistics to initial state.

```zig
pub fn reset(self: *AsyncStats) void
```

## AsyncFileWriter Methods

### init

Create a new async file writer.

```zig
pub fn init(allocator: std.mem.Allocator, config: FileWriterConfig) !AsyncFileWriter
```

### deinit

Clean up and close file.

```zig
pub fn deinit(self: *AsyncFileWriter) void
```

### write

Write data to the buffer (async).

```zig
pub fn write(self: *AsyncFileWriter, data: []const u8) !void
```

### flush

Flush buffer to file.

```zig
pub fn flush(self: *AsyncFileWriter) !void
```

## Presets

### highThroughput

Optimized for maximum throughput.

```zig
pub fn highThroughput() AsyncConfig {
    return .{
        .bufferSize = 65536,
        .flushIntervalMs = 500,
        .batchSize = 256,
        .overflowPolicy = .dropOldest,
        .preallocate_buffers = true,
    };
}
```

### lowLatency

Optimized for minimum latency.

```zig
pub fn lowLatency() AsyncConfig {
    return .{
        .bufferSize = 1024,
        .flushIntervalMs = 10,
        .batchSize = 16,
        .overflowPolicy = .block,
    };
}
```

### balanced

Balance between throughput and latency.

```zig
pub fn balanced() AsyncConfig {
    return .{
        .bufferSize = 8192,
        .flushIntervalMs = 100,
        .batchSize = 64,
    };
}
```

### noDrop

Never drop messages (may block).

```zig
pub fn noDrop() AsyncConfig {
    return .{
        .bufferSize = 16384,
        .overflowPolicy = .block,
    };
}
```

## Usage Example

```zig
const std = @import("std");
const logly = @import("logly");

pub fn main() !void {
    var gpa = std.heap.DebugAllocator(.{}){};
    defer _ = gpa.deinit();
    const allocator = gpa.allocator();

    // Create async logger with high throughput config
    var asyncLogger = try logly.AsyncLogger.initWithConfig(
        allocator,
        logly.AsyncPresets.highThroughput(),
    );
    defer asyncLogger.deinit();

    // Start the background worker
    try asyncLogger.start();
    defer asyncLogger.stop();

    // Log messages (non-blocking)
    for (0..1000) |i| {
         _ = i;
        // async_logger.log(&record);
    }

    // Check statistics
    const stats = asyncLogger.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});
}
```

> \[!IMPORTANT]
> **Async logging vs `autoFlush`**: These are independent concepts that are often confused:
>
> * **Async logging** (`asyncConfig.enabled`): Records are queued in a lock-free ring buffer and processed by background worker threads. This controls *when* and *how* log records are dispatched.
> * **`autoFlush`**: Controls whether sinks are flushed after each record (sync path) or after each batch (async path). This controls *durability guarantees*.
> * **Thread pool**: Records are submitted as tasks to a worker pool. No flush occurs at submission time — the task is still queued and not yet written to any sink.
>
> In practice: if you enable async logging with `autoFlush = false`, records are batched and flushed on the configured interval or when the batch size is reached. If you enable async logging with `autoFlush = true`, sinks are flushed after every batch, which can reduce throughput.

## See Also

* [Async Logging Guide](../guide/async.md) - In-depth async logging guide
* [Thread Pool API](thread-pool.md) - Parallel logging
* [Configuration Guide](../guide/configuration.md) - 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 running
* `bufferCapacity() usize` - Returns the buffer capacity
* `isFull() bool` - Returns true if the buffer is at capacity
* `queueDepth() usize` - Returns current queue depth
* `isQueueEmpty() bool` - Returns true if queue is empty
* `availableCapacity() usize` - Returns remaining queue slots
* `queueUtilization() f64` - Returns current queue utilization ratio
* `isNearCapacity(threshold: f64) bool` - Returns true when utilization exceeds threshold
* `waitUntilDrained(timeoutMs: u64) bool` - Waits for queue drain until timeout
* `resetStats() void` - Resets all statistics

## Callbacks

```zig
// 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);
```
