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
Basic Usage ​
zig
const std = @import("std");
const logly = @import("logly");
const Metrics = logly.Metrics;
pub fn main() !void {
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
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.total_records});
std.debug.print("Total bytes: {d}\n", .{snapshot.total_bytes});
std.debug.print("Error count: {d}\n", .{snapshot.error_count});
std.debug.print("Records/sec: {d:.2}\n", .{snapshot.records_per_second});
}Metrics Snapshot ​
The Metrics.Snapshot struct contains:
| Field | Type | Description |
|---|---|---|
total_records | u64 | Total number of log records processed |
total_bytes | u64 | Total bytes logged |
dropped_records | u64 | Records dropped (by sampling/filtering) |
error_count | u64 | Number of errors recorded |
uptime_ms | i64 | Time since metrics started (milliseconds) |
records_per_second | f64 | Current record rate |
bytes_per_second | f64 | Current throughput rate |
level_counts | [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.level_counts[1]; // debug
const info_count = snapshot.level_counts[2]; // info
const warning_count = snapshot.level_counts[4]; // warning
const error_count = snapshot.level_counts[5]; // err
const critical_count = snapshot.level_counts[7]; // criticalRecording 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:1Level 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:1Periodic 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 error_rate = if (snapshot.total_records > 0)
@as(f64, @floatFromInt(snapshot.error_count)) /
@as(f64, @floatFromInt(snapshot.total_records)) * 100.0
else
0.0;
std.debug.print(
"[Metrics] Total: {d} | Errors: {d} ({d:.2}%) | Rate: {d:.1} rec/sec\n",
.{
snapshot.total_records,
snapshot.error_count,
error_rate,
snapshot.records_per_second,
}
);
}
pub fn main() !void {
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
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.enable_metrics = true;
config.metrics = .{
.enabled = true,
.track_levels = true,
.track_sinks = true,
.track_throughput = true,
.track_latency = true,
.snapshot_interval_ms = 60000, // 1 minute
.error_rate_threshold = 0.01, // 1% error rate alert
.drop_rate_threshold = 0.001, // 0.1% drop rate alert
.export_format = .json,
.enable_histogram = true,
.histogram_buckets = 20,
.history_size = 60,
};
const logger = try logly.Logger.initWithConfig(allocator, config);MetricsConfig Options ​
| Option | Type | Default | Description |
|---|---|---|---|
enabled | bool | false | Enable metrics collection |
track_levels | bool | true | Track per-level counts |
track_sinks | bool | true | Track per-sink metrics |
track_throughput | bool | true | Calculate records/bytes per second |
track_latency | bool | false | Track logging latency |
snapshot_interval_ms | u64 | 0 | Auto-snapshot interval (0 = disabled) |
error_rate_threshold | f32 | 0.0 | Error rate alert threshold |
drop_rate_threshold | f32 | 0.0 | Drop rate alert threshold |
max_records_per_second | u64 | 0 | Max rate alert threshold |
export_format | ExportFormat | .text | Export format (text/json/prometheus/statsd) |
enable_histogram | bool | false | Enable latency histogram |
histogram_buckets | u8 | 10 | Number of histogram buckets |
history_size | 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 ​
- Use metrics in production: Metrics have minimal overhead
- Export regularly: Don't wait until shutdown to check metrics
- Alert on error rates: Set up alerts for high error percentages
- Track dropped records: Monitor how many records are sampled/filtered out
- 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.total_records});
}
fn onThresholdExceeded(metric_type: Metrics.MetricType, value: u64, threshold: u64) void {
std.debug.print("Threshold exceeded: {any} = {d} > {d}\n", .{metric_type, 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,
.track_latency = true,
.enable_histogram = 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 sink_idx = try metrics.addSink("file_sink");
// Record flush
metrics.recordSinkFlush(sink_idx);
// Get sink metrics by index or name
if (metrics.getSinkMetrics(sink_idx)) |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|cDirect 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 err_rate = metrics.errorRate(); // 0.0 - 1.0
const drop_rate = metrics.dropRate(); // 0.0 - 1.0
const rps = metrics.rate(); // records per second
const bps = metrics.bytesPerSecond(); // bytes per second
// Uptime
const uptime_ms = 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 - Full API documentation
- Filtering - Rule-based log filtering
- Sampling - Log volume control
- Tracing - Distributed tracing
- Configuration - Global configuration options
