---
url: /logly.zig/guide/metrics.md
description: >-
  Monitor logging performance with Logly.zig's built-in metrics. Track record
  counts, error rates, bytes written, per-level statistics, and sink throughput
  with atomic counters.
---

# Metrics

Logly.zig provides built-in metrics collection for monitoring logging performance and behavior. Track record counts, error rates, throughput, and more.

## Overview

The `Metrics` module enables you to:

* Track total log records processed
* Count records by log level
* Monitor error rates and failures
* Measure logging throughput
* Track per-sink statistics

For targeted cleanup, `Metrics.resetLevelMetrics(level)` clears a single log-level counter without resetting the full collector.

## Export Naming and Breakdowns

Prometheus and StatsD exports can be namespaced and shaped from config:

```zig
const metricsCfg = logly.Config.MetricsConfig.production()
    .prometheus()
    .prefix("api.gateway")
    .histogram(20)
    .breakdowns(true, true);

var metrics = logly.Metrics.initWithConfig(allocator, metricsCfg);
```

This produces exporter-safe names like `api_gateway_records_total` for Prometheus. Per-level and per-sink breakdowns are enabled by default and can be disabled when you need smaller exports.

## Basic Usage

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

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

    // Create metrics collector
    var metrics = Metrics.init(allocator);
    defer metrics.deinit();

    // Record log events
    metrics.recordLog(.info, 45);     // info log, 45 bytes
    metrics.recordLog(.debug, 32);    // debug log, 32 bytes
    metrics.recordLog(.warning, 28);  // warning log, 28 bytes
    metrics.recordLog(.err, 52);      // error log, 52 bytes
    metrics.recordError();            // record an error event

    // Get metrics snapshot
    const snapshot = metrics.getSnapshot();
    
    std.debug.print("=== Logging Metrics ===\n", .{});
    std.debug.print("Total records: {d}\n", .{snapshot.totalRecords});
    std.debug.print("Total bytes: {d}\n", .{snapshot.totalBytes});
    std.debug.print("Error count: {d}\n", .{snapshot.errorCount});
    std.debug.print("Records/sec: {d:.2}\n", .{snapshot.recordsPerSecond});
}
```

## Metrics Snapshot

The `Metrics.Snapshot` struct contains:

| Field | Type | Description |
|-------|------|-------------|
| `totalRecords` | `u64` | Total number of log records processed |
| `totalBytes` | `u64` | Total bytes logged |
| `droppedRecords` | `u64` | Records dropped (by sampling/filtering) |
| `errorCount` | `u64` | Number of errors recorded |
| `uptimeMs` | `i64` | Time since metrics started (milliseconds) |
| `recordsPerSecond` | `f64` | Current record rate |
| `bytesPerSecond` | `f64` | Current throughput rate |
| `levelCounts` | `[8]u64` | Per-level record counts |

## Level Counts

Access counts per log level using the level index:

```zig
const snapshot = metrics.getSnapshot();

// Level indices: trace=0, debug=1, info=2, success=3, warning=4, err=5, fail=6, critical=7
const debug_count = snapshot.levelCounts[1];   // debug
const info_count = snapshot.levelCounts[2];    // info
const warning_count = snapshot.levelCounts[4]; // warning
const errorCount = snapshot.levelCounts[5];   // err
const critical_count = snapshot.levelCounts[7]; // critical
```

## Recording Events

```zig
var metrics = Metrics.init(allocator);
defer metrics.deinit();

// Record a log at specific level with byte size
metrics.recordLog(.info, 100);
metrics.recordLog(.warning, 75);

// Record a dropped log (e.g., filtered or sampled out)
metrics.recordDrop();

// Record an error event
metrics.recordError();

// Reset all metrics to zero
metrics.reset();
```

## Per-Sink Metrics

Track metrics per sink:

```zig
var metrics = Metrics.init(allocator);
defer metrics.deinit();

// Add sinks to track
const file_sink_idx = try metrics.addSink("file_sink");
const console_sink_idx = try metrics.addSink("console_sink");

// Record sink-specific writes
metrics.recordSinkWrite(file_sink_idx, 256);      // 256 bytes to file sink
metrics.recordSinkWrite(console_sink_idx, 128);   // 128 bytes to console

// Record sink write errors
metrics.recordSinkError(file_sink_idx);
```

## Formatted Output

Get a human-readable metrics report:

```zig
var metrics = Metrics.init(allocator);
defer metrics.deinit();

// Record some logs...
metrics.recordLog(.info, 100);
metrics.recordLog(.err, 50);

// Get formatted output
const formatted = try metrics.format(allocator);
defer allocator.free(formatted);

std.debug.print("{s}\n", .{formatted});
// Output:
// Logly Metrics
//   Total Records: 2
//   Total Bytes: 150
//   Dropped: 0
//   Errors: 0
//   Uptime: 1234ms
//   Rate: 1.62 records/sec
//   Throughput: 121.55 bytes/sec

// Get level breakdown
const level_breakdown = try metrics.formatLevelBreakdown(allocator);
defer allocator.free(level_breakdown);

std.debug.print("{s}\n", .{level_breakdown});
// Output: Level Breakdown: INFO:1, ERROR:1
```

## Level Breakdown

The `formatLevelBreakdown` method provides a compact view of non-zero level counts:

```zig
var metrics = Metrics.init(allocator);
defer metrics.deinit();

// Log at different levels
metrics.recordLog(.info, 100);
metrics.recordLog(.info, 80);
metrics.recordLog(.info, 90);
metrics.recordLog(.warning, 50);
metrics.recordLog(.err, 60);
metrics.recordLog(.critical, 70);

const breakdown = try metrics.formatLevelBreakdown(allocator);
defer allocator.free(breakdown);

std.debug.print("{s}\n", .{breakdown});
// Output: Level Breakdown: INFO:3, WARNING:1, ERROR:1, CRITICAL:1
```

## Periodic Metrics Reporting

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

pub fn reportMetrics(m: *Metrics) void {
    const snapshot = m.getSnapshot();
    
    const errorRate = if (snapshot.totalRecords > 0)
        @as(f64, @floatFromInt(snapshot.errorCount)) / 
        @as(f64, @floatFromInt(snapshot.totalRecords)) * 100.0
    else
        0.0;

    std.debug.print(
        "[Metrics] Total: {d} | Errors: {d} ({d:.2}%) | Rate: {d:.1} rec/sec\n",
        .{
            snapshot.totalRecords,
            snapshot.errorCount,
            errorRate,
            snapshot.recordsPerSecond,
        }
    );
}

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

    var metrics = Metrics.init(allocator);
    defer metrics.deinit();

    // Simulate logging with periodic reporting
    var i: usize = 0;
    while (i < 1000) : (i += 1) {
        metrics.recordLog(.info, 50);

        // Report every 100 records
        if (i % 100 == 0) {
            reportMetrics(&metrics);
        }
    }

    // Final report
    reportMetrics(&metrics);
}
```

## Global Configuration

Configure metrics globally through `Config.MetricsConfig`:

```zig
var config = logly.Config.default();
config.enableMetrics = true;
config.metrics = .{
    .enabled = true,
    .trackLevels = true,
    .trackSinks = true,
    .trackThroughput = true,
    .trackLatency = true,
    .snapshotIntervalMs = 60000,  // 1 minute
    .errorRateThreshold = 0.01,   // 1% error rate alert
    .dropRateThreshold = 0.001,   // 0.1% drop rate alert
    .exportFormat = .json,
    .enableHistogram = true,
    .histogramBuckets = 20,
    .historySize = 60,
};

const logger = try logly.Logger.initWithConfig(allocator, config);
```

### MetricsConfig Options

| Option | Type | Default | Description |
|--------|------|---------|-------------|
| `enabled` | `bool` | `false` | Enable metrics collection |
| `trackLevels` | `bool` | `true` | Track per-level counts |
| `trackSinks` | `bool` | `true` | Track per-sink metrics |
| `trackThroughput` | `bool` | `true` | Calculate records/bytes per second |
| `trackLatency` | `bool` | `false` | Track logging latency |
| `snapshotIntervalMs` | `u64` | `0` | Auto-snapshot interval (0 = disabled) |
| `errorRateThreshold` | `f32` | `0.0` | Error rate alert threshold |
| `dropRateThreshold` | `f32` | `0.0` | Drop rate alert threshold |
| `maxRecordsPerSecond` | `u64` | `0` | Max rate alert threshold |
| `exportFormat` | `ExportFormat` | `.text` | Export format (text/json/prometheus/statsd) |
| `enableHistogram` | `bool` | `false` | Enable latency histogram |
| `histogramBuckets` | `u8` | `10` | Number of histogram buckets |
| `historySize` | `u16` | `0` | Snapshots to retain in history |

### Configuration Presets

```zig
// Production - comprehensive with alerts
config.metrics = logly.Config.MetricsConfig.production();

// Minimal - basic counts only
config.metrics = logly.Config.MetricsConfig.minimal();

// Detailed - everything including histograms
config.metrics = logly.Config.MetricsConfig.detailed();
```

## Best Practices

1. **Use metrics in production**: Metrics have minimal overhead
2. **Export regularly**: Don't wait until shutdown to check metrics
3. **Alert on error rates**: Set up alerts for high error percentages
4. **Track dropped records**: Monitor how many records are sampled/filtered out
5. **Reset periodically**: Use `reset()` for time-windowed metrics

## Callback Support

Set callbacks to react to metrics events:

```zig
var metrics = Metrics.init(allocator);
defer metrics.deinit();

// Callback when a record is logged
const S = struct {
    fn onRecordLogged(level: logly.Level, bytes: u64) void {
        std.debug.print("Logged {s}: {d} bytes\n", .{level.asString(), bytes});
    }
    
    fn onSnapshotTaken(snapshot: *const Metrics.Snapshot) void {
        std.debug.print("Snapshot: {d} records\n", .{snapshot.totalRecords});
    }
    
    fn onThresholdExceeded(metricType: Metrics.MetricType, value: u64, threshold: u64) void {
        std.debug.print("Threshold exceeded: {any} = {d} > {d}\n", .{metricType, value, threshold});
    }
    
    fn onErrorDetected(event: Metrics.ErrorEvent, count: u64) void {
        std.debug.print("Error event: {any}, count: {d}\n", .{event, count});
    }
};

metrics.setRecordLoggedCallback(S.onRecordLogged);
metrics.setSnapshotCallback(S.onSnapshotTaken);
metrics.setThresholdCallback(S.onThresholdExceeded);
metrics.setErrorCallback(S.onErrorDetected);

// Now callbacks will be invoked when events occur
metrics.recordLog(.info, 100);
```

## Latency Tracking

Track logging latency for performance monitoring:

```zig
var metrics = Metrics.initWithConfig(allocator, .{
    .enabled = true,
    .trackLatency = true,
    .enableHistogram = true,
});
defer metrics.deinit();

// Record with latency (in nanoseconds)
metrics.recordLogWithLatency(.info, 100, 1_500_000); // 1.5ms

// Get latency statistics
const avg = metrics.avgLatencyNs();
const min = metrics.minLatencyNs();
const max = metrics.maxLatencyNs();

// Get histogram buckets for latency distribution
const histogram = metrics.getHistogram();
```

## Sink Flush Tracking

Track flush operations on sinks:

```zig
var metrics = Metrics.init(allocator);
defer metrics.deinit();

const sinkIdx = try metrics.addSink("file_sink");

// Record flush
metrics.recordSinkFlush(sinkIdx);

// Get sink metrics by index or name
if (metrics.getSinkMetrics(sinkIdx)) |sink| {
    std.debug.print("Flushes: {d}\\n", .{sink.getFlushCount()});
}

if (metrics.getSinkMetricsByName("file_sink")) |sink| {
    std.debug.print("Bytes: {d}\\n", .{sink.getBytesWritten()});
}
```

## Export Formats

Export metrics in various formats:

```zig
var metrics = Metrics.init(allocator);
defer metrics.deinit();

metrics.recordLog(.info, 100);

// JSON format
const json = try metrics.exportJson(allocator);
defer allocator.free(json);
// {"total_records":1,"total_bytes":100,...}

// Prometheus format
const prom = try metrics.exportPrometheus(allocator);
defer allocator.free(prom);
// logly_records_total 1
// logly_bytes_total 100
// ...

// StatsD format
const statsd = try metrics.exportStatsd(allocator);
defer allocator.free(statsd);
// logly.records.total:1|c
// logly.bytes.total:100|c
```

## Direct Statistics Access

```zig
var metrics = Metrics.init(allocator);
defer metrics.deinit();

// Record some logs
metrics.recordLog(.info, 100);
metrics.recordError();
metrics.recordDrop();

// Direct access methods
const total = metrics.totalRecordCount();
const bytes = metrics.totalBytesLogged();
const errors = metrics.errorCount();
const drops = metrics.droppedCount();

// Rate calculations
const errRate = metrics.errorRate();   // 0.0 - 1.0
const dropRate = metrics.dropRate();   // 0.0 - 1.0
const rps = metrics.rate();             // records per second
const bps = metrics.bytesPerSecond();   // bytes per second

// Uptime
const uptimeMs = metrics.uptime();
const uptime_sec = metrics.uptimeSeconds();

// Level-specific counts
const info_count = metrics.levelCount(.info);
const err_count = metrics.levelCount(.err);

// Sink count
const sinks = metrics.sinkCount();

// Configuration access
const config = metrics.getConfig();
const enabled = metrics.isEnabled();
```

### Threshold Checks

```zig
// Check for high error rate
if (metrics.hasHighErrorRate(0.01)) {
    std.debug.print("Warning: Error rate exceeds 1%!\n", .{});
}

// Check for high drop rate
if (metrics.hasHighDropRate(0.05)) {
    std.debug.print("Warning: Drop rate exceeds 5%!\n", .{});
}

// Check for any errors or drops
if (metrics.hasErrors()) {
    std.debug.print("Errors detected!\n", .{});
}
if (metrics.hasDropped()) {
    std.debug.print("Records were dropped!\n", .{});
}
```

### State and Control

```zig
// Check if any records have been logged
if (metrics.hasRecords()) {
    // Export metrics
}

// Reset all statistics
metrics.reset();
// Or use alias
metrics.clear();
```

## Aliases

| Alias | Method |
|-------|--------|
| `record` | `recordLog` |
| `log` | `recordLog` |
| `drop` | `recordDrop` |
| `dropped` | `recordDrop` |
| `recordErr` | `recordError` |
| `metricsSnapshot` | `getSnapshot` |
| `levels` | `formatLevelBreakdown` |
| `breakdown` | `formatLevelBreakdown` |
| `clear` | `reset` |
| `uptimeSec` | `uptimeSeconds` |
| `throughput` | `bytesPerSecond` |

## See Also

* [Metrics API Reference](/api/metrics) - Full API documentation
* [Filtering](/guide/filtering) - Rule-based log filtering
* [Sampling](/guide/sampling) - Log volume control
* [Tracing](/guide/tracing) - Distributed tracing
* [Configuration](/guide/configuration) - Global configuration options
