const std = @import("std"); const http_api = @import("api.zig"); const router = @import("router.zig"); const telemetry = @import("../internal/telemetry.zig"); const http = std.http; const Row = struct { stats: telemetry.EndpointStats, path: []const u8, group: []const u8, }; const TrafficSummary = struct { requests: u64 = 0, failures: u64 = 0, writes: u64 = 0, proxy_count: u64 = 0, proxy_p95_ms: f64 = 0, }; const TROUBLE_US = 1000 * std.time.us_per_ms; const TROUBLE_LIMIT = 12; pub fn serve(request: *http_api.Request) !void { var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator); defer arena.deinit(); const body = try render(arena.allocator()); try http_api.respond(request, .ok, body, &html_headers); } fn render(allocator: std.mem.Allocator) ![]const u8 { const snap = telemetry.snapshot(); const rows = try collectRows(allocator, snap); sortRows(rows); const service_rows = try filterServiceRows(allocator, rows); const summary = summarizeTraffic(service_rows); const appview_rows = try renderRouteRows(allocator, try filterAppviewRows(allocator, service_rows), "no appview proxy samples yet."); const pds_rows = try renderRouteRows(allocator, try filterPdsRows(allocator, service_rows), "no local PDS samples yet."); const write_rows = try renderWriteRows(allocator, try filterWrites(allocator, rows)); const trouble_rows = try renderTroubleRows(allocator, snap); const uptime = try formatDuration(allocator, snap.uptime_s); const failure_rate = if (summary.requests == 0) "0%" else try std.fmt.allocPrint(allocator, "{d:.1}%", .{@as(f64, @floatFromInt(summary.failures)) * 100.0 / @as(f64, @floatFromInt(summary.requests))}); const proxy_p95 = try formatMaybeMs(allocator, summary.proxy_count, summary.proxy_p95_ms); return std.fmt.allocPrint(allocator, \\ \\ \\ \\ \\ \\ zds / health \\ \\ \\ \\
\\
zds
\\

pds health

\\

Live health for this PDS. Counts reset when the service restarts; this page does not count its own refreshes.

\\
\\
status
ok
\\
uptime
{s}
\\
api requests
{d}
\\
writes
{d}
\\
appview p95
{s}
\\
failures
{d}
\\
failure rate
{s}
\\
\\
\\

needs attention

newest first; only 1s+ requests or server failures

\\
{s}
\\
\\
\\

writes

likes usually createRecord; batches use applyWrites

\\
{s}
\\
\\
\\

appview proxy

client reads forwarded to Bluesky/chat appviews

\\
{s}
\\
\\
\\

pds routes

local repository, sync, auth, and preference routes

\\
{s}
\\
\\
\\ \\ , .{ uptime, summary.requests, summary.writes, proxy_p95, if (summary.failures == 0) "good" else "bad", summary.failures, failure_rate, trouble_rows, write_rows, appview_rows, pds_rows, }); } fn collectRows(allocator: std.mem.Allocator, snap: telemetry.Snapshot) ![]Row { var rows: std.ArrayList(Row) = .empty; for (0..@typeInfo(router.Route).@"enum".fields.len) |route_idx| { for (0..@typeInfo(telemetry.Method).@"enum".fields.len) |method_idx| { const stats = snap.routes[route_idx][method_idx]; if (stats.count == 0) continue; const meta = endpointMeta(stats.route, stats.method); try rows.append(allocator, .{ .stats = stats, .path = meta.path, .group = meta.group }); } } return rows.toOwnedSlice(allocator); } fn renderRows(allocator: std.mem.Allocator, rows: []const Row) ![]const u8 { if (rows.len == 0) return allocator.dupe(u8, "
no samples yet.
"); var out: std.Io.Writer.Allocating = .init(allocator); defer out.deinit(); try out.writer.writeAll(""); for (rows) |row| { try out.writer.print( "", .{ @tagName(row.stats.method), row.stats.method.label(), row.path, row.group, row.stats.count, row.stats.p50_ms, row.stats.p95_ms, row.stats.avg_ms, row.stats.max_ms, row.stats.failure_count, }, ); } try out.writer.writeAll("
methodroutegroupcountp50p95avgmaxfail
{s}{s}{s}{d}{d:.1}ms{d:.1}ms{d:.1}ms{d:.1}ms{d}
"); return out.toOwnedSlice(); } fn renderRouteRows(allocator: std.mem.Allocator, rows: []const Row, empty: []const u8) ![]const u8 { if (rows.len == 0) return std.fmt.allocPrint(allocator, "
{s}
", .{empty}); var out: std.Io.Writer.Allocating = .init(allocator); defer out.deinit(); try out.writer.writeAll(""); for (rows) |row| { try out.writer.print( "", .{ @tagName(row.stats.method), row.stats.method.label(), row.path, row.stats.count, row.stats.p50_ms, row.stats.p95_ms, row.stats.max_ms, row.stats.failure_count, }, ); } try out.writer.writeAll("
methodroutecountp50p95maxfail
{s}{s}{d}{d:.1}ms{d:.1}ms{d:.1}ms{d}
"); return out.toOwnedSlice(); } fn renderWriteRows(allocator: std.mem.Allocator, rows: []const Row) ![]const u8 { if (rows.len == 0) return allocator.dupe(u8, "
no write samples yet.
"); var out: std.Io.Writer.Allocating = .init(allocator); defer out.deinit(); try out.writer.writeAll(""); for (rows) |row| { try out.writer.print( "", .{ writeKind(row.stats.route), row.path, row.stats.count, row.stats.p50_ms, row.stats.p95_ms, row.stats.max_ms, row.stats.failure_count, }, ); } try out.writer.writeAll("
kindendpointcountp50p95maxfail
{s}{s}{d}{d:.1}ms{d:.1}ms{d:.1}ms{d}
"); return out.toOwnedSlice(); } fn renderTroubleRows(allocator: std.mem.Allocator, snap: telemetry.Snapshot) ![]const u8 { var out: std.Io.Writer.Allocating = .init(allocator); defer out.deinit(); try out.writer.writeAll(""); var i = snap.slow_len; var rendered: usize = 0; while (i > 0) { i -= 1; const item = snap.slow_requests[i]; if (!needsAttention(item)) continue; try out.writer.print( "", .{ if (item.at_s > 0) @max(0, snap.started_at_s + @as(i64, @intCast(snap.uptime_s)) - item.at_s) else 0, @tagName(item.method), item.method.label(), item.class.label(), item.labelText(), @as(f64, @floatFromInt(item.elapsed_us)) / 1000.0, statusClass(item), item.status, }, ); rendered += 1; if (rendered == TROUBLE_LIMIT) break; } if (rendered == 0) return allocator.dupe(u8, "
nothing needs attention right now.
"); try out.writer.writeAll("
whenmethodclassrouteelapsedstatus
{d}s ago{s}{s}{s}{d:.1}ms{d}
"); return out.toOwnedSlice(); } fn needsAttention(item: telemetry.SlowRequest) bool { return item.failed() or item.elapsed_us >= TROUBLE_US; } fn statusClass(item: telemetry.SlowRequest) []const u8 { if (item.failed()) return "bad"; if (item.status >= 400) return "group"; return "good"; } fn filterWrites(allocator: std.mem.Allocator, rows: []const Row) ![]Row { var filtered: std.ArrayList(Row) = .empty; for (rows) |row| { if (isWriteRoute(row.stats.route, row.stats.method)) try filtered.append(allocator, row); } return filtered.toOwnedSlice(allocator); } fn filterServiceRows(allocator: std.mem.Allocator, rows: []const Row) ![]Row { var filtered: std.ArrayList(Row) = .empty; for (rows) |row| { if (!isNoiseRoute(row.stats.route)) try filtered.append(allocator, row); } return filtered.toOwnedSlice(allocator); } fn filterAppviewRows(allocator: std.mem.Allocator, rows: []const Row) ![]Row { var filtered: std.ArrayList(Row) = .empty; for (rows) |row| { if (row.stats.route == .proxy_xrpc) try filtered.append(allocator, row); } return filtered.toOwnedSlice(allocator); } fn filterPdsRows(allocator: std.mem.Allocator, rows: []const Row) ![]Row { var filtered: std.ArrayList(Row) = .empty; for (rows) |row| { if (row.stats.route != .proxy_xrpc and !isWriteRoute(row.stats.route, row.stats.method)) { try filtered.append(allocator, row); } } return filtered.toOwnedSlice(allocator); } fn summarizeTraffic(rows: []const Row) TrafficSummary { var summary: TrafficSummary = .{}; for (rows) |row| { summary.requests += row.stats.count; summary.failures += row.stats.failure_count; if (isWriteRoute(row.stats.route, row.stats.method)) summary.writes += row.stats.count; if (row.stats.route == .proxy_xrpc and row.stats.method == .get) { summary.proxy_count += row.stats.count; summary.proxy_p95_ms = @max(summary.proxy_p95_ms, row.stats.p95_ms); } } return summary; } fn sortRows(rows: []Row) void { std.mem.sort(Row, rows, {}, struct { fn lessThan(_: void, a: Row, b: Row) bool { if (a.stats.p95_ms != b.stats.p95_ms) return a.stats.p95_ms > b.stats.p95_ms; if (a.stats.max_ms != b.stats.max_ms) return a.stats.max_ms > b.stats.max_ms; return a.stats.count > b.stats.count; } }.lessThan); } fn isNoiseRoute(route: router.Route) bool { return switch (route) { .stats_page, .health, .root, .api_docs, .api_openapi, .favicon, .og_image, .cors_preflight, .not_found, => true, else => false, }; } fn isWriteRoute(route: router.Route, method: telemetry.Method) bool { if (method != .post) return false; return switch (route) { .app_preferences_put, .repo_create_record, .repo_put_record, .repo_delete_record, .repo_apply_writes, .repo_import_repo, .repo_upload_blob, .permissioned_data, => true, else => false, }; } fn writeKind(route: router.Route) []const u8 { return switch (route) { .repo_create_record => "single record create", .repo_put_record => "replace by rkey", .repo_delete_record => "record delete", .repo_apply_writes => "batched repo writes", .repo_import_repo => "repo import", .repo_upload_blob => "blob upload", .app_preferences_put => "preferences update", .permissioned_data => "permissioned-data write", else => "write", }; } const EndpointMeta = struct { path: []const u8, group: []const u8 }; fn endpointMeta(route: router.Route, method: telemetry.Method) EndpointMeta { if (route == .proxy_xrpc) return .{ .path = "proxied XRPC", .group = "proxy" }; if (route == .not_found) return .{ .path = "unmatched request", .group = "unknown" }; for (router.endpoints) |endpoint| { if (endpoint.route == route and std.mem.eql(u8, endpoint.method, method.label())) return .{ .path = endpoint.path, .group = endpoint.group }; } for (router.endpoints) |endpoint| { if (endpoint.route == route) return .{ .path = endpoint.path, .group = endpoint.group }; } return .{ .path = @tagName(route), .group = "internal" }; } fn formatDuration(allocator: std.mem.Allocator, seconds: u64) ![]const u8 { const days = seconds / 86_400; const hours = (seconds % 86_400) / 3_600; const minutes = (seconds % 3_600) / 60; if (days > 0) return std.fmt.allocPrint(allocator, "{d}d {d}h", .{ days, hours }); if (hours > 0) return std.fmt.allocPrint(allocator, "{d}h {d}m", .{ hours, minutes }); return std.fmt.allocPrint(allocator, "{d}m", .{minutes}); } fn formatMaybeMs(allocator: std.mem.Allocator, count: u64, ms: f64) ![]const u8 { if (count == 0) return allocator.dupe(u8, "none"); return std.fmt.allocPrint(allocator, "{d:.1}ms", .{ms}); } const html_headers = [_]http.Header{ .{ .name = "content-type", .value = "text/html; charset=utf-8" }, .{ .name = "cache-control", .value = "no-store" }, .{ .name = "access-control-allow-origin", .value = "*" }, .{ .name = "access-control-allow-private-network", .value = "true" }, .{ .name = "connection", .value = "close" }, };