Metrics
Built-in Prometheus metrics, Grafana dashboard setup, and custom metric instrumentation for CoreSDK.
Available in Phase 1b. This feature ships with the sidecar daemon and wrapper SDKs. Phase 1a (Rust crate only) users access this via the core engine directly.
Metrics
CoreSDK exposes a Prometheus-compatible /metrics endpoint and exports metrics over OTLP.
Every metric is labeled with tenant_id and service_name so you can slice dashboards per tenant
without additional instrumentation.
Built-in metrics
| Metric | Type | Labels | Description |
|---|---|---|---|
coresdk_auth_requests_total | Counter | tenant_id, method, result | Auth requests by method (jwt, api_key) and result (allowed, denied) |
coresdk_policy_eval_duration_ms | Histogram | tenant_id, action, result | Policy evaluation latency in milliseconds |
coresdk_active_sessions | Gauge | tenant_id | Current number of active authenticated sessions |
coresdk_tenant_request_rate | Gauge | tenant_id | Requests per second, per tenant (1-minute rolling window) |
coresdk_auth_latency_ms | Histogram | tenant_id, method | JWT verification and API-key lookup latency |
coresdk_errors_total | Counter | tenant_id, error_type | Errors by type (network, config, policy_engine) |
coresdk_jwks_cache_hits_total | Counter | tenant_id | JWKS cache hit rate for JWT verification |
Prometheus export
Enable the Prometheus scrape endpoint in your SDK configuration:
let engine = Engine::from_env().await?;
// CORESDK_METRICS_PROMETHEUS_ADDR=0.0.0.0:9090# CORESDK_METRICS_PROMETHEUS_ADDR=0.0.0.0:9090sdk, err := coresdk.New(coresdk.Config{
Tenant: "acme",
MetricsPrometheusAddr: "0.0.0.0:9090", // scrape at /metrics
})const sdk = await CoreSDK.create({
tenant: "acme",
metricsPrometheusAddr: "0.0.0.0:9090", // scrape at /metrics
});Then add a scrape target to your Prometheus config:
# prometheus.yml
scrape_configs:
- job_name: coresdk
static_configs:
- targets: ["orders-service:9090"]
relabel_configs:
- source_labels: [__address__]
target_label: instanceYou can also export metrics over OTLP alongside traces and logs:
let engine = Engine::from_env().await?;
// OTEL_EXPORTER_OTLP_ENDPOINT=http://otel-collector:4317Grafana dashboard setup
CoreSDK ships a pre-built Grafana dashboard JSON. Import it in three steps:
# Download the dashboard
curl -O https://coresdk.dev/grafana/coresdk-dashboard.json
# Import via Grafana API
curl -X POST http://grafana:3000/api/dashboards/import \
-H "Content-Type: application/json" \
-d @coresdk-dashboard.jsonOr import manually: Dashboards → Import → Upload JSON file.
The dashboard includes:
- Auth request rate and error rate by tenant
- Policy evaluation latency (p50, p95, p99)
- Active sessions heatmap
- Per-tenant request rate over time
- Top denied actions by policy
Recommended alert rules (PromQL)
# High denied auth rate
- alert: CoreSDKHighDeniedRate
expr: |
rate(coresdk_auth_requests_total{result="denied"}[5m])
/ rate(coresdk_auth_requests_total[5m]) > 0.05
for: 2m
labels:
severity: warning
annotations:
summary: "Denied auth rate above 5% for {{ $labels.tenant_id }}"
# Policy eval latency spike
- alert: CoreSDKPolicyLatencyHigh
expr: |
histogram_quantile(0.95,
rate(coresdk_policy_eval_duration_ms_bucket[5m])
) > 200
for: 5m
labels:
severity: warning
annotations:
summary: "p95 policy eval latency above 200ms for {{ $labels.tenant_id }}"Custom metrics
Phase note. Ships Phase 2.
Register your own counters, histograms, and gauges that are co-exported with CoreSDK's metrics.
use coresdk_engine::metrics::{counter, histogram};
// Define once at startup
static ORDERS_CREATED: counter!("orders_created_total", "tenant_id", "region");
static ORDER_VALUE: histogram!("order_value_usd", "tenant_id");
async fn create_order(order: &Order, tenant: &str) {
ORDERS_CREATED.inc(&[tenant, &order.region]);
ORDER_VALUE.observe(order.value_usd, &[tenant]);
}from coresdk.tracing import counter, histogram # Phase 2
orders_created = counter("orders_created_total", labels=["tenant_id", "region"])
order_value = histogram("order_value_usd", labels=["tenant_id"])
async def create_order(order, tenant: str):
orders_created.inc(tenant_id=tenant, region=order.region)
order_value.observe(order.value_usd, tenant_id=tenant)import "github.com/coresdk/sdk-go/metrics"
var (
ordersCreated = metrics.NewCounter("orders_created_total",
metrics.WithLabels("tenant_id", "region"))
orderValue = metrics.NewHistogram("order_value_usd",
metrics.WithLabels("tenant_id"))
)
func createOrder(ctx context.Context, order Order, tenant string) {
ordersCreated.Inc(tenant, order.Region)
orderValue.Observe(order.ValueUSD, tenant)
}import { counter, histogram } from "@coresdk/sdk/metrics";
const ordersCreated = counter("orders_created_total", ["tenant_id", "region"]);
const orderValue = histogram("order_value_usd", ["tenant_id"]);
async function createOrder(order: Order, tenant: string) {
ordersCreated.inc({ tenant_id: tenant, region: order.region });
orderValue.observe(order.valueUsd, { tenant_id: tenant });
}Custom metrics appear automatically on the /metrics endpoint and in the OTLP export stream.
Next steps
- OpenTelemetry — trace context for every metric data point
- Structured Logging — correlate log lines with metric spikes
- Alerts & Anomaly Detection — fire alerts from these metrics