Skip to main content
CoreSDK
Observability

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

MetricTypeLabelsDescription
coresdk_auth_requests_totalCountertenant_id, method, resultAuth requests by method (jwt, api_key) and result (allowed, denied)
coresdk_policy_eval_duration_msHistogramtenant_id, action, resultPolicy evaluation latency in milliseconds
coresdk_active_sessionsGaugetenant_idCurrent number of active authenticated sessions
coresdk_tenant_request_rateGaugetenant_idRequests per second, per tenant (1-minute rolling window)
coresdk_auth_latency_msHistogramtenant_id, methodJWT verification and API-key lookup latency
coresdk_errors_totalCountertenant_id, error_typeErrors by type (network, config, policy_engine)
coresdk_jwks_cache_hits_totalCountertenant_idJWKS 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:9090
sdk, 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: instance

You can also export metrics over OTLP alongside traces and logs:

let engine = Engine::from_env().await?;
// OTEL_EXPORTER_OTLP_ENDPOINT=http://otel-collector:4317

Grafana 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.json

Or 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
# 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

On this page