feat(ops): export Prometheus metrics (system + game KPIs) via OpenTelemetry #159
No reviewers
Labels
No labels
alpha:wave-0
alpha:wave-1
alpha:wave-2
alpha:wave-3
area:assets
area:combat
area:ecology
area:infra
area:render
area:scripting
area:ui
area:world
enhancement
epic
migration
post-alpha
roadmap
tech-debt
type:bug
type:chore
type:design
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set
Reference
marco/IsoMmo!159
Loading…
Reference in a new issue
No description provided.
Delete branch "feat/prometheus-metrics"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Summary
Export Prometheus metrics from both servers for the homelab Grafana/Prometheus stack, next to the structured logs (#156). Observability only — read-only, no gameplay/wire surface. Closes #157.
Program.cs): runtime + ASP.NET instrumentation, Prometheus exporter, aGET /metricsscrape endpoint. Auth uses only the free instrumentation (login rate/latency viahttp_server_request_duration_*, kestrel connections). Same "one format everywhere" spirit as the logs — env is a label, never a different shape.GameMetricsserver-infra singleton owns oneMeter+ all custom instruments and is injected intoWorldTick,WorldPersister,GameSessionHandler,CommandRegistry.World/Gameplaynever reference it.isommo_tick_duration_milliseconds(histogram),isommo_tick_overruns_total.isommo_players_connected,isommo_player_connects_total,isommo_player_disconnects_total,isommo_player_session_seconds.isommo_save_duration_milliseconds,isommo_last_save_age_seconds,isommo_saves_total,isommo_save_failures_total,isommo_save_size_bytes.isommo_creatures_count,isommo_items_count(total items the server tracks).isommo_malformed_messages_total,isommo_chat_rate_limited_total,isommo_intents_rejected_total{reason},isommo_gm_commands_total{command,authorized}.Design notes (from the critical-design-review, persisted in #157)
ObservableGauges read a sim-thread-updated snapshot (GameMetrics.UpdateWorldCounts, called fromWorldTick), neverWorldoff the scrape thread.players_connectedreads the thread-safeConnectionManager.gm_commandsis tagged with the canonical command name (bounded), never the raw client string;intents_rejected{reason}is a fixed reason set./metricsis unauthenticated → internal-only: documented indocs/deploy.md(the reverse proxy must not route it; Prometheus scrapes over the internal net). The Caddy sample only routes/auth/*and/ws.World.ItemCount,ItemRegistry.Count) mirroring the existingCreatureCount/PlacedItemCount. (Follow-up #158 will unify these intoworld.Items/world.Mobilesquery views.)How it was tested
GameMetricsTests, viaMeterListener): every instrument — tick duration + overrun threshold, connect/disconnect + session seconds, save duration/count/failure/bytes,last_save_age(0 before any save, ~0 right after), the four security counters (incl. the boundedreason/command/authorizedtags), and the world-count gauges.GameServerIntegrationTests):GET /metricsreturns Prometheus text exposition with the custom meter present; after a real WebSocket client connects,isommo_players_connected≥ 1 andisommo_player_connects_totalis emitted.just dev, real client via the debug harness) — scraped/metricsand sanity-checked the numbers:isommo_items_count 5(the 5 seeded items),creatures_count 0,players_connected 0, tick histogram averaging <1 ms with 0 overruns (100 ms budget).player_connects_total 1,players_connected 1,saves_total 2,save_size_bytes 98(one player blob),save_durationavg ~3 ms,last_save_age_seconds 12.8(into a 15 s cycle). Auth/metricsshows kestrel/aspnetcore series._ms_millisecondsdouble suffixes).just lint(0 warnings) +just test(all 7 projects green) + whole solution builds.Checklist
just lintpasses (zero warnings)just testis green (whole solution builds — client + tools included)docs/deploy.md) +## Definition of Donein #157