From fc16200fadbb80b312f7d29dc445ee6778c26c9e Mon Sep 17 00:00:00 2001 From: Abhijeet Date: Wed, 15 Jul 2026 00:07:09 +0530 Subject: [PATCH 1/3] feat: add cache stats --- apps/guardrails-service/container/src/go.mod | 4 +-- apps/guardrails-service/container/src/go.sum | 4 +-- apps/guardrails-service/container/src/main.go | 16 +++++++---- .../src/pkg/metrics/system_metrics.go | 28 +++++++++++++++++-- 4 files changed, 40 insertions(+), 12 deletions(-) diff --git a/apps/guardrails-service/container/src/go.mod b/apps/guardrails-service/container/src/go.mod index 7064c17dbd..f565cdbca9 100644 --- a/apps/guardrails-service/container/src/go.mod +++ b/apps/guardrails-service/container/src/go.mod @@ -3,7 +3,7 @@ module github.com/akto-api-security/guardrails-service go 1.25.0 require ( - github.com/akto-api-security/akto-endpoint-shield v0.0.0-20260713165715-0741a43011a6 + github.com/akto-api-security/akto-endpoint-shield v0.0.0-20260714114404-dadbd4118ccb github.com/gin-gonic/gin v1.12.0 github.com/golang-jwt/jwt/v5 v5.3.0 github.com/redis/go-redis/v9 v9.20.0 @@ -132,6 +132,6 @@ require ( ) replace ( - github.com/akto-api-security/akto-endpoint-shield => github.com/akto-api-security/akto-gateway/mcp-endpoint-shield v0.0.0-20260713165715-0741a43011a6 + github.com/akto-api-security/akto-endpoint-shield => github.com/akto-api-security/akto-gateway/mcp-endpoint-shield v0.0.0-20260714114404-dadbd4118ccb github.com/shoenig/go-m1cpu => ./internal/stub/go-m1cpu ) diff --git a/apps/guardrails-service/container/src/go.sum b/apps/guardrails-service/container/src/go.sum index afc6ebe194..b4b2759382 100644 --- a/apps/guardrails-service/container/src/go.sum +++ b/apps/guardrails-service/container/src/go.sum @@ -29,8 +29,8 @@ github.com/Masterminds/sprig/v3 v3.3.0 h1:mQh0Yrg1XPo6vjYXgtf5OtijNAKJRNcTdOOGZe github.com/Masterminds/sprig/v3 v3.3.0/go.mod h1:Zy1iXRYNqNLUolqCpL4uhk6SHUMAOSCzdgBfDb35Lz0= github.com/STARRY-S/zip v0.2.1 h1:pWBd4tuSGm3wtpoqRZZ2EAwOmcHK6XFf7bU9qcJXyFg= github.com/STARRY-S/zip v0.2.1/go.mod h1:xNvshLODWtC4EJ702g7cTYn13G53o1+X9BWnPFpcWV4= -github.com/akto-api-security/akto-gateway/mcp-endpoint-shield v0.0.0-20260713165715-0741a43011a6 h1:FlVQxBopt3lJhPXw19h2cTHvMG5jCx2VAIxMwt2Fd5A= -github.com/akto-api-security/akto-gateway/mcp-endpoint-shield v0.0.0-20260713165715-0741a43011a6/go.mod h1:xygh4yYStZhELBqnSAgC/eflshcDFacvKQz2ZF5bFwY= +github.com/akto-api-security/akto-gateway/mcp-endpoint-shield v0.0.0-20260714114404-dadbd4118ccb h1:nEDsZm5EnOJvHPJnEplYS93E+O1R8N5k69lhUwgiOCg= +github.com/akto-api-security/akto-gateway/mcp-endpoint-shield v0.0.0-20260714114404-dadbd4118ccb/go.mod h1:xygh4yYStZhELBqnSAgC/eflshcDFacvKQz2ZF5bFwY= github.com/andybalholm/brotli v1.2.0 h1:ukwgCxwYrmACq68yiUqwIWnGY0cTPox/M94sVwToPjQ= github.com/andybalholm/brotli v1.2.0/go.mod h1:rzTDkvFWvIrjDXZHkuS16NPggd91W3kUSvPlQ1pLaKY= github.com/aymanbagabas/go-osc52/v2 v2.0.1 h1:HwpRHbFMcZLEVr42D4p7XBqjyuxQH5SMiErDT4WkJ2k= diff --git a/apps/guardrails-service/container/src/main.go b/apps/guardrails-service/container/src/main.go index ea4daa758e..88b03bbff8 100644 --- a/apps/guardrails-service/container/src/main.go +++ b/apps/guardrails-service/container/src/main.go @@ -10,6 +10,7 @@ import ( "time" + "github.com/akto-api-security/akto-endpoint-shield/mcp/guardcache" "github.com/akto-api-security/akto-endpoint-shield/utils" "github.com/akto-api-security/guardrails-service/handlers" "github.com/akto-api-security/guardrails-service/pkg/auth" @@ -75,12 +76,13 @@ func main() { // metricsFlushLoop periodically drains guardrail metrics and pushes them to the // db-abstractor every 60s: per-account latency plus instance-level -// CPU/memory/goroutine gauges. It blocks forever, so start it with `go`. +// CPU/memory/goroutine gauges and full-text semantic-cache hit/miss counts. +// It blocks forever, so start it with `go`. func metricsFlushLoop(dbClient *dbabstractor.Client, logger *zap.Logger, acc *metrics.Accumulator) { sysSampler, err := metrics.NewSystemSampler() if err != nil { // Non-fatal: the service still validates without instance metrics. - logger.Warn("System metrics sampler unavailable; CPU/memory gauges disabled", zap.Error(err)) + logger.Warn("System metrics sampler unavailable; CPU/memory/cache gauges disabled", zap.Error(err)) } ticker := time.NewTicker(60 * time.Second) @@ -89,10 +91,14 @@ func metricsFlushLoop(dbClient *dbabstractor.Client, logger *zap.Logger, acc *me for accountId, batch := range acc.DrainAll() { flushMetrics(dbClient, logger, accountId, batch) } - // Instance-level CPU/memory/goroutine gauges, attributed to the - // service token's account (see metrics.SystemSampler). + // Instance-level CPU/memory/goroutine gauges plus cache hit/miss + // counts, attributed to the service token's account (see + // metrics.SystemSampler). if sysSampler != nil { - if sysBatch := sysSampler.Sample(); len(sysBatch) > 0 { + sysBatch := sysSampler.Sample() + cacheStats := guardcache.DrainStats() + sysBatch = append(sysBatch, sysSampler.CacheStats(cacheStats.HitsExact, cacheStats.HitsFuzzy, cacheStats.Misses)...) + if len(sysBatch) > 0 { flushMetrics(dbClient, logger, strconv.FormatInt(sysBatch[0].AccountId, 10), sysBatch) } } diff --git a/apps/guardrails-service/container/src/pkg/metrics/system_metrics.go b/apps/guardrails-service/container/src/pkg/metrics/system_metrics.go index 3e9a924b23..2d93b4711b 100644 --- a/apps/guardrails-service/container/src/pkg/metrics/system_metrics.go +++ b/apps/guardrails-service/container/src/pkg/metrics/system_metrics.go @@ -41,19 +41,27 @@ func NewSystemSampler() (*SystemSampler, error) { return &SystemSampler{proc: p, accountId: accID, instanceId: host}, nil } -// gauge builds a MetricData for one instance-level reading. -func (s *SystemSampler) gauge(metricId string, value float64, now int64) MetricData { +// metric builds a MetricData for one instance-level reading of the given type. +func (s *SystemSampler) metric(metricId, metricType string, value float64, now int64) MetricData { return MetricData{ MetricId: metricId, Value: value, Timestamp: now, - MetricType: "GAUGE", + MetricType: metricType, ModuleType: moduleTypeAgentGateway, AccountId: s.accountId, InstanceId: s.instanceId, } } +func (s *SystemSampler) gauge(metricId string, value float64, now int64) MetricData { + return s.metric(metricId, "GAUGE", value, now) +} + +func (s *SystemSampler) sum(metricId string, value float64, now int64) MetricData { + return s.metric(metricId, "SUM", value, now) +} + // Sample reads current CPU%, resident memory and goroutine count. It is meant to // be called once per flush tick; the long-lived process handle lets Percent // report usage over the interval since the previous call. @@ -75,3 +83,17 @@ func (s *SystemSampler) Sample() []MetricData { return out } + +// CacheStats builds SUM metrics for the full-text semantic-cache hit/miss +// counts accumulated since the last call — a delta (e.g. from +// guardcache.DrainStats), not a running total, matching MetricType SUM. +// Hits are split by match type: exact (literal repeat) vs fuzzy (embedding +// similarity match), since they represent different cache behavior. +func (s *SystemSampler) CacheStats(hitsExact, hitsFuzzy, misses uint64) []MetricData { + now := time.Now().Unix() + return []MetricData{ + s.sum("GUARDRAIL_CACHE_HIT_EXACT", float64(hitsExact), now), + s.sum("GUARDRAIL_CACHE_HIT_FUZZY", float64(hitsFuzzy), now), + s.sum("GUARDRAIL_CACHE_MISS", float64(misses), now), + } +} From 96c0585e0f94c34fb5e5c5474dc792792d8c8005 Mon Sep 17 00:00:00 2001 From: Abhijeet Date: Wed, 15 Jul 2026 15:32:04 +0530 Subject: [PATCH 2/3] feat: add metrics related to downstream services --- apps/guardrails-service/container/src/main.go | 22 +++++++++++++------ .../src/pkg/metrics/system_metrics.go | 15 +++++++++++++ 2 files changed, 30 insertions(+), 7 deletions(-) diff --git a/apps/guardrails-service/container/src/main.go b/apps/guardrails-service/container/src/main.go index 88b03bbff8..8078ff1c58 100644 --- a/apps/guardrails-service/container/src/main.go +++ b/apps/guardrails-service/container/src/main.go @@ -10,6 +10,7 @@ import ( "time" + "github.com/akto-api-security/akto-endpoint-shield/mcp/depstats" "github.com/akto-api-security/akto-endpoint-shield/mcp/guardcache" "github.com/akto-api-security/akto-endpoint-shield/utils" "github.com/akto-api-security/guardrails-service/handlers" @@ -75,14 +76,17 @@ func main() { } // metricsFlushLoop periodically drains guardrail metrics and pushes them to the -// db-abstractor every 60s: per-account latency plus instance-level -// CPU/memory/goroutine gauges and full-text semantic-cache hit/miss counts. -// It blocks forever, so start it with `go`. +// db-abstractor every 60s: per-account latency, instance-level CPU/memory/ +// goroutine gauges, full-text semantic-cache hit/miss counts, and client-observed +// call health (errors, latency) for the two downstream dependencies — agent-guard +// (includes the Anonymize scanner) and the embedder (combined across the cache's +// single-text lookup and intentguard's batched calls). It blocks forever, so +// start it with `go`. func metricsFlushLoop(dbClient *dbabstractor.Client, logger *zap.Logger, acc *metrics.Accumulator) { sysSampler, err := metrics.NewSystemSampler() if err != nil { // Non-fatal: the service still validates without instance metrics. - logger.Warn("System metrics sampler unavailable; CPU/memory/cache gauges disabled", zap.Error(err)) + logger.Warn("System metrics sampler unavailable; CPU/memory/cache/dependency gauges disabled", zap.Error(err)) } ticker := time.NewTicker(60 * time.Second) @@ -91,13 +95,17 @@ func metricsFlushLoop(dbClient *dbabstractor.Client, logger *zap.Logger, acc *me for accountId, batch := range acc.DrainAll() { flushMetrics(dbClient, logger, accountId, batch) } - // Instance-level CPU/memory/goroutine gauges plus cache hit/miss - // counts, attributed to the service token's account (see - // metrics.SystemSampler). + // Instance-level CPU/memory/goroutine gauges, cache hit/miss counts, + // and downstream-dependency call health, attributed to the service + // token's account (see metrics.SystemSampler). if sysSampler != nil { sysBatch := sysSampler.Sample() cacheStats := guardcache.DrainStats() sysBatch = append(sysBatch, sysSampler.CacheStats(cacheStats.HitsExact, cacheStats.HitsFuzzy, cacheStats.Misses)...) + agStats := depstats.AgentGuard.Drain() + sysBatch = append(sysBatch, sysSampler.DependencyStats("GUARDRAIL_AGENT_GUARD", agStats.Calls, agStats.Errors, agStats.AvgLatencyMs)...) + embStats := depstats.Embedder.Drain() + sysBatch = append(sysBatch, sysSampler.DependencyStats("GUARDRAIL_EMBEDDER", embStats.Calls, embStats.Errors, embStats.AvgLatencyMs)...) if len(sysBatch) > 0 { flushMetrics(dbClient, logger, strconv.FormatInt(sysBatch[0].AccountId, 10), sysBatch) } diff --git a/apps/guardrails-service/container/src/pkg/metrics/system_metrics.go b/apps/guardrails-service/container/src/pkg/metrics/system_metrics.go index 2d93b4711b..09a0e932b1 100644 --- a/apps/guardrails-service/container/src/pkg/metrics/system_metrics.go +++ b/apps/guardrails-service/container/src/pkg/metrics/system_metrics.go @@ -97,3 +97,18 @@ func (s *SystemSampler) CacheStats(hitsExact, hitsFuzzy, misses uint64) []Metric s.sum("GUARDRAIL_CACHE_MISS", float64(misses), now), } } + +// DependencyStats builds call-health metrics for one downstream dependency +// (agent-guard or the embedder), as observed by this client — network transit +// time and connection-level failures included, which the dependency's own +// server-side metrics can't see. calls/errors are deltas since the last call +// (e.g. from a depstats.Tracker.Drain), matching MetricType SUM; avgLatencyMs +// is this window's mean, matching MetricType GAUGE. +func (s *SystemSampler) DependencyStats(prefix string, calls, errors uint64, avgLatencyMs float64) []MetricData { + now := time.Now().Unix() + return []MetricData{ + s.sum(prefix+"_CALLS", float64(calls), now), + s.sum(prefix+"_ERRORS", float64(errors), now), + s.gauge(prefix+"_LATENCY", avgLatencyMs, now), + } +} From cd8009f50cf858b1bed21367933fbf2362467e1a Mon Sep 17 00:00:00 2001 From: Abhijeet Date: Wed, 15 Jul 2026 15:34:53 +0530 Subject: [PATCH 3/3] feat: add metrics related to downstream services --- apps/guardrails-service/container/src/go.mod | 4 ++-- apps/guardrails-service/container/src/go.sum | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/apps/guardrails-service/container/src/go.mod b/apps/guardrails-service/container/src/go.mod index f565cdbca9..71b800f6fc 100644 --- a/apps/guardrails-service/container/src/go.mod +++ b/apps/guardrails-service/container/src/go.mod @@ -3,7 +3,7 @@ module github.com/akto-api-security/guardrails-service go 1.25.0 require ( - github.com/akto-api-security/akto-endpoint-shield v0.0.0-20260714114404-dadbd4118ccb + github.com/akto-api-security/akto-endpoint-shield v0.0.0-20260715100314-2175c200e52e github.com/gin-gonic/gin v1.12.0 github.com/golang-jwt/jwt/v5 v5.3.0 github.com/redis/go-redis/v9 v9.20.0 @@ -132,6 +132,6 @@ require ( ) replace ( - github.com/akto-api-security/akto-endpoint-shield => github.com/akto-api-security/akto-gateway/mcp-endpoint-shield v0.0.0-20260714114404-dadbd4118ccb + github.com/akto-api-security/akto-endpoint-shield => github.com/akto-api-security/akto-gateway/mcp-endpoint-shield v0.0.0-20260715100314-2175c200e52e github.com/shoenig/go-m1cpu => ./internal/stub/go-m1cpu ) diff --git a/apps/guardrails-service/container/src/go.sum b/apps/guardrails-service/container/src/go.sum index b4b2759382..4124fb1a36 100644 --- a/apps/guardrails-service/container/src/go.sum +++ b/apps/guardrails-service/container/src/go.sum @@ -29,8 +29,8 @@ github.com/Masterminds/sprig/v3 v3.3.0 h1:mQh0Yrg1XPo6vjYXgtf5OtijNAKJRNcTdOOGZe github.com/Masterminds/sprig/v3 v3.3.0/go.mod h1:Zy1iXRYNqNLUolqCpL4uhk6SHUMAOSCzdgBfDb35Lz0= github.com/STARRY-S/zip v0.2.1 h1:pWBd4tuSGm3wtpoqRZZ2EAwOmcHK6XFf7bU9qcJXyFg= github.com/STARRY-S/zip v0.2.1/go.mod h1:xNvshLODWtC4EJ702g7cTYn13G53o1+X9BWnPFpcWV4= -github.com/akto-api-security/akto-gateway/mcp-endpoint-shield v0.0.0-20260714114404-dadbd4118ccb h1:nEDsZm5EnOJvHPJnEplYS93E+O1R8N5k69lhUwgiOCg= -github.com/akto-api-security/akto-gateway/mcp-endpoint-shield v0.0.0-20260714114404-dadbd4118ccb/go.mod h1:xygh4yYStZhELBqnSAgC/eflshcDFacvKQz2ZF5bFwY= +github.com/akto-api-security/akto-gateway/mcp-endpoint-shield v0.0.0-20260715100314-2175c200e52e h1:bHBIYh0EDH3depP0elagmoiiE21P3hqDn9HKmeBzvBo= +github.com/akto-api-security/akto-gateway/mcp-endpoint-shield v0.0.0-20260715100314-2175c200e52e/go.mod h1:xygh4yYStZhELBqnSAgC/eflshcDFacvKQz2ZF5bFwY= github.com/andybalholm/brotli v1.2.0 h1:ukwgCxwYrmACq68yiUqwIWnGY0cTPox/M94sVwToPjQ= github.com/andybalholm/brotli v1.2.0/go.mod h1:rzTDkvFWvIrjDXZHkuS16NPggd91W3kUSvPlQ1pLaKY= github.com/aymanbagabas/go-osc52/v2 v2.0.1 h1:HwpRHbFMcZLEVr42D4p7XBqjyuxQH5SMiErDT4WkJ2k=