metrics: Prometheus text exposition for /metrics
/metrics was Prometheus-ish name/value text with a custom banner and text/plain Content-Type, so strict scrapers could fail. Emit Prometheus text 0.0.4 (# HELP, # TYPE, counter|gauge) with stable metric names, and set Content-Type to text/plain; version=0.0.4; charset=utf-8.
This commit is contained in:
@@ -344,42 +344,59 @@ type Stats struct {
|
||||
LastResetTime time.Time
|
||||
}
|
||||
|
||||
// WriteText emits the Prometheus-style text metrics to the ResponseWriter.
|
||||
// Promoted from internal handler per Phase 3 for better package ownership.
|
||||
// WriteText emits Prometheus text exposition format 0.0.4 to the ResponseWriter.
|
||||
// Each metric family is # HELP, then # TYPE, then one or more sample lines.
|
||||
// Metric names are stable; labeled series keep service=%q (Prometheus-valid quotes).
|
||||
// All fmt.Fprintf errors are intentionally discarded via _ = : this is a best-effort
|
||||
// read-only debug endpoint; client disconnects or write errors during metrics dump
|
||||
// are not actionable (do not affect cache correctness or require retries).
|
||||
func WriteText(w http.ResponseWriter, stats *Stats) {
|
||||
_, _ = fmt.Fprintf(w, "# SteamCache2 Metrics\n")
|
||||
_, _ = fmt.Fprintf(w, "total_requests %d\n", stats.TotalRequests)
|
||||
_, _ = fmt.Fprintf(w, "cache_hits %d\n", stats.CacheHits)
|
||||
_, _ = fmt.Fprintf(w, "cache_misses %d\n", stats.CacheMisses)
|
||||
_, _ = fmt.Fprintf(w, "negative_cache_hits %d\n", stats.NegativeCacheHits)
|
||||
_, _ = fmt.Fprintf(w, "cache_coalesced %d\n", stats.CacheCoalesced)
|
||||
_, _ = fmt.Fprintf(w, "range_cache %d\n", stats.RangeCache)
|
||||
_, _ = fmt.Fprintf(w, "range_upstream %d\n", stats.RangeUpstream)
|
||||
_, _ = fmt.Fprintf(w, "errors %d\n", stats.Errors)
|
||||
_, _ = fmt.Fprintf(w, "rate_limited %d\n", stats.RateLimited)
|
||||
_, _ = fmt.Fprintf(w, "upstream_errors %d\n", stats.UpstreamErrors)
|
||||
_, _ = fmt.Fprintf(w, "cache_write_failures %d\n", stats.CacheWriteFailures)
|
||||
_, _ = fmt.Fprintf(w, "memory_cache_hits %d\n", stats.MemoryCacheHits)
|
||||
_, _ = fmt.Fprintf(w, "disk_cache_hits %d\n", stats.DiskCacheHits)
|
||||
_, _ = fmt.Fprintf(w, "promotions %d\n", stats.Promotions)
|
||||
_, _ = fmt.Fprintf(w, "evictions %d\n", stats.Evictions)
|
||||
_, _ = fmt.Fprintf(w, "capacity_pressure_events %d\n", stats.CapacityPressureEvents)
|
||||
writeInt(w, "total_requests", "Total HTTP requests handled.", "counter", stats.TotalRequests)
|
||||
writeInt(w, "cache_hits", "Requests served from cache.", "counter", stats.CacheHits)
|
||||
writeInt(w, "cache_misses", "Requests not found in cache.", "counter", stats.CacheMisses)
|
||||
writeInt(w, "negative_cache_hits", "404/410 served from a still-valid negative cache entry.", "counter", stats.NegativeCacheHits)
|
||||
writeInt(w, "cache_coalesced", "Requests coalesced onto an in-flight upstream fetch.", "counter", stats.CacheCoalesced)
|
||||
writeInt(w, "range_cache", "Range requests served as 206 from an already-cached object.", "counter", stats.RangeCache)
|
||||
writeInt(w, "range_upstream", "Range requests that required an upstream fetch, served as 206.", "counter", stats.RangeUpstream)
|
||||
writeInt(w, "errors", "Request errors.", "counter", stats.Errors)
|
||||
writeInt(w, "rate_limited", "Requests rejected by rate limiting.", "counter", stats.RateLimited)
|
||||
writeInt(w, "upstream_errors", "Errors talking to upstream.", "counter", stats.UpstreamErrors)
|
||||
writeInt(w, "cache_write_failures", "Failures writing objects into cache.", "counter", stats.CacheWriteFailures)
|
||||
writeInt(w, "memory_cache_hits", "Hits served from the memory tier.", "counter", stats.MemoryCacheHits)
|
||||
writeInt(w, "disk_cache_hits", "Hits served from the disk tier.", "counter", stats.DiskCacheHits)
|
||||
writeInt(w, "promotions", "Objects promoted from disk to memory.", "counter", stats.Promotions)
|
||||
writeInt(w, "evictions", "Objects evicted from cache.", "counter", stats.Evictions)
|
||||
writeInt(w, "capacity_pressure_events", "Soft eviction under the memory or disk cap, and/or disk ENOSPC.", "counter", stats.CapacityPressureEvents)
|
||||
|
||||
writeHelpType(w, "service_errors", "Errors attributed to a named service.", "counter")
|
||||
for svc, cnt := range stats.ServiceErrors {
|
||||
_, _ = fmt.Fprintf(w, "service_errors{service=%q} %d\n", svc, cnt)
|
||||
}
|
||||
writeHelpType(w, "service_requests", "Requests attributed to a named service.", "counter")
|
||||
for svc, cnt := range stats.ServiceRequests {
|
||||
_, _ = fmt.Fprintf(w, "service_requests{service=%q} %d\n", svc, cnt)
|
||||
}
|
||||
_, _ = fmt.Fprintf(w, "hit_rate %.4f\n", stats.HitRate)
|
||||
_, _ = fmt.Fprintf(w, "avg_response_time_ms %.2f\n", float64(stats.AvgResponseTime.Nanoseconds())/1e6)
|
||||
_, _ = fmt.Fprintf(w, "total_bytes_served %d\n", stats.TotalBytesServed)
|
||||
_, _ = fmt.Fprintf(w, "total_bytes_saved %d\n", stats.TotalBytesSaved)
|
||||
|
||||
_, _ = fmt.Fprintf(w, "memory_cache_size %d\n", stats.MemoryCacheSize)
|
||||
_, _ = fmt.Fprintf(w, "disk_cache_size %d\n", stats.DiskCacheSize)
|
||||
_, _ = fmt.Fprintf(w, "disk_tier_ready %d\n", stats.DiskTierReady)
|
||||
_, _ = fmt.Fprintf(w, "uptime_seconds %.2f\n", stats.Uptime.Seconds())
|
||||
writeFloat(w, "hit_rate", "Cache hits divided by total requests.", "gauge", "%.4f", stats.HitRate)
|
||||
writeFloat(w, "avg_response_time_ms", "Average response time in milliseconds.", "gauge", "%.2f", float64(stats.AvgResponseTime.Nanoseconds())/1e6)
|
||||
writeInt(w, "total_bytes_served", "Total bytes sent to clients.", "counter", stats.TotalBytesServed)
|
||||
writeInt(w, "total_bytes_saved", "Bytes served from cache instead of being re-downloaded from upstream.", "counter", stats.TotalBytesSaved)
|
||||
writeInt(w, "memory_cache_size", "Current memory cache size in bytes.", "gauge", stats.MemoryCacheSize)
|
||||
writeInt(w, "disk_cache_size", "Current disk cache size in bytes.", "gauge", stats.DiskCacheSize)
|
||||
writeInt(w, "disk_tier_ready", "1 if the disk tier is attached or no disk is configured; 0 while attach is pending.", "gauge", stats.DiskTierReady)
|
||||
writeFloat(w, "uptime_seconds", "Process uptime in seconds.", "gauge", "%.2f", stats.Uptime.Seconds())
|
||||
}
|
||||
|
||||
func writeHelpType(w http.ResponseWriter, name, help, typ string) {
|
||||
_, _ = fmt.Fprintf(w, "# HELP %s %s\n# TYPE %s %s\n", name, help, name, typ)
|
||||
}
|
||||
|
||||
func writeInt(w http.ResponseWriter, name, help, typ string, v int64) {
|
||||
writeHelpType(w, name, help, typ)
|
||||
_, _ = fmt.Fprintf(w, "%s %d\n", name, v)
|
||||
}
|
||||
|
||||
func writeFloat(w http.ResponseWriter, name, help, typ, valFmt string, v float64) {
|
||||
writeHelpType(w, name, help, typ)
|
||||
_, _ = fmt.Fprintf(w, "%s "+valFmt+"\n", name, v)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user