ops: Add prometheus metrics

Adds a /metrics endpoint in serve mode. Can be disabled via --disable-metrics.

In fetch, dumps the metrics if --metrics is specified (defaults to false).

None of the metrics being collected are on a hot path, so they're just always
collected using atomic operations (i.e. no `if (!enabled) return;`). Because
the operations are cheap and infrequent enough not to matter.

The one place I want to add more metrics is in HttpClient (bytes, status,
counts, ...) but after the pending HttpClient-related PR is merged.

./lightpanda fetch --log-level fatal --metrics "https://lightpanda.io/"

```
build_info{version="1.0.0-dev.7837+70493ce35"} 1
cdp_connections_total 0
cdp_connection_limit_total 0
cdp_active_connections 0
cdp_commands_total 0
cdp_unknown_commands_total 0
js_heap_limits_total 0
script_errors_total 0
arena_hit_total{size="tiny"} 908
arena_hit_total{size="small"} 35
arena_hit_total{size="medium"} 0
arena_hit_total{size="large"} 27
arena_miss_total{size="tiny"} 193
arena_miss_total{size="small"} 31
arena_miss_total{size="medium"} 5
arena_miss_total{size="large"} 23
navigate_total{type="page"} 1
navigate_total{type="iframe"} 0
navigate_total{type="popup"} 0
js_heap_size_bytes_bucket{le="4194304"} 0
js_heap_size_bytes_bucket{le="8388608"} 0
js_heap_size_bytes_bucket{le="16777216"} 1
js_heap_size_bytes_bucket{le="33554432"} 1
js_heap_size_bytes_bucket{le="67108864"} 1
js_heap_size_bytes_bucket{le="134217728"} 1
js_heap_size_bytes_bucket{le="268435456"} 1
js_heap_size_bytes_bucket{le="536870912"} 1
js_heap_size_bytes_bucket{le="+Inf"} 1
js_heap_size_bytes_sum 11223040
js_heap_size_bytes_count 1
```
This commit is contained in:
Karl Seguin committed 2026-07-14 09:17:14 +08:00
1 parent 56c5701b7b
commit 4d81e48e44
14 files changed
+377 -24

No files matched your search

+16 -11
View File
@@ -111,25 +111,27 @@ pub fn deinit(self: *ArenaPool) void {
// - Pass a BucketSize (.tiny, .small, .medium, .large) for explicit bucket selection
// - Pass a usize for automatic bucket selection based on expected size
pub fn acquire(self: *ArenaPool, size_or_bucket: anytype, debug: []const u8) !Allocator {
const bucket = blk: {
const bucket_size: BucketSize = blk: {
const T = @TypeOf(size_or_bucket);
if (T == BucketSize or T == @TypeOf(.enum_literal)) {
break :blk switch (@as(BucketSize, size_or_bucket)) {
.tiny => &self.tiny,
.small => &self.small,
.medium => &self.medium,
.large => &self.large,
};
break :blk @as(BucketSize, size_or_bucket);
}
if (T == usize or T == comptime_int) {
if (size_or_bucket <= self.tiny.retain_bytes) break :blk &self.tiny;
if (size_or_bucket <= self.small.retain_bytes) break :blk &self.small;
if (size_or_bucket <= self.medium.retain_bytes) break :blk &self.medium;
break :blk &self.large;
if (size_or_bucket <= self.tiny.retain_bytes) break :blk .tiny;
if (size_or_bucket <= self.small.retain_bytes) break :blk .small;
if (size_or_bucket <= self.medium.retain_bytes) break :blk .medium;
break :blk .large;
}
@compileError("acquire expects BucketSize or usize, got " ++ @typeName(T));
};
const bucket = switch (bucket_size) {
.tiny => &self.tiny,
.small => &self.small,
.medium => &self.medium,
.large => &self.large,
};
self.mutex.lock();
defer self.mutex.unlock();
@@ -144,9 +146,12 @@ pub fn acquire(self: *ArenaPool, size_or_bucket: anytype, debug: []const u8) !Al
}
gop.value_ptr.* += 1;
}
lp.metrics.arena_hit.incr(bucket_size);
return entry.arena.allocator();
}
lp.metrics.arena_miss.incr(bucket_size);
const entry = try self.entry_pool.create();
entry.* = .{
.next = null,