Prometheus Metrics
PRISM exports metrics in Prometheus text exposition format on the admin API at GET /metrics.
Request Metrics
| Metric | Type | Description |
|---|---|---|
prism_requests_total{status="hit|miss|bypass|stale|error"} | counter | Total requests by cache status |
prism_inflight_requests | gauge | Currently processing requests |
prism_rate_limit_rejections_total | counter | Requests rejected by rate limiter (429) |
Render Metrics
| Metric | Type | Description |
|---|---|---|
prism_render_active | gauge | Currently active renders |
prism_render_queue_depth | gauge | Requests waiting for a Chrome tab |
prism_render_total | counter | Total renders completed |
prism_render_duration_ms | histogram | Render duration in milliseconds |
prism_shadow_renders_total | counter | Shadow renders completed (shadow mode) |
prism_content_validation_failures_total | counter | Renders that failed content validation |
prism_render_duration_ms histogram buckets: 100, 250, 500, 1000, 2500, 5000, 10000, 30000
Cache Metrics
| Metric | Type | Description |
|---|---|---|
prism_cache_entries | gauge | Number of entries in the render cache |
prism_cache_memory_bytes | gauge | Memory used by cached entries |
prism_cache_evictions_total | counter | Cache entries evicted (LRU or TTL) |
Chrome Tab Pool Metrics
| Metric | Type | Description |
|---|---|---|
prism_tab_pool_size | gauge | Total tabs in the pool |
prism_tab_pool_available | gauge | Idle tabs available for rendering |
prism_tab_crashes_total | counter | Tab crashes detected |
prism_tab_creates_total | counter | New tabs created |
prism_tab_recycles_total | counter | Tabs recycled after max renders |
prism_chrome_restarts_total | counter | Full Chrome process restarts |
Origin Metrics
| Metric | Type | Description |
|---|---|---|
prism_origin_requests_total | counter | Requests forwarded to the origin server |
prism_origin_duration_sum_ms | counter | Cumulative origin request duration (ms) |
Warmup Metrics
| Metric | Type | Description |
|---|---|---|
prism_warmup_urls_processed | counter | URLs successfully warmed up |
prism_warmup_urls_failed | counter | URLs that failed during warmup |
Circuit Breaker
| Metric | Type | Description |
|---|---|---|
prism_circuit_breaker_state | gauge | 0 = closed (healthy), 1 = open (tripped), 2 = half-open (probing) |
System Metrics
| Metric | Type | Description |
|---|---|---|
prism_uptime_seconds | gauge | Seconds since PRISM started |
process_resident_memory_bytes | gauge | Resident memory (RSS) of the PRISM process |
process_virtual_memory_bytes | gauge | Virtual memory of the PRISM process |
process_cpu_seconds_total | counter | Total CPU time consumed |
process_open_fds | gauge | Open file descriptors |
process_max_fds | gauge | Maximum file descriptors (ulimit) |
process_threads | gauge | Number of OS threads |
Scrape Configuration
Add PRISM to your Prometheus scrape_configs:
scrape_configs:
- job_name: prism
scrape_interval: 15s
static_configs:
- targets: ['127.0.0.1:4001']
# If bearer_token is configured:
authorization:
type: Bearer
credentials: your-secret-token
Grafana Dashboard
Import this JSON snippet as a Grafana dashboard to get started with key panels:
{
"title": "Trident PRISM",
"panels": [
{
"title": "Cache Hit Rate",
"type": "stat",
"targets": [
{
"expr": "rate(prism_requests_total{status=\"hit\"}[5m]) / (rate(prism_requests_total{status=\"hit\"}[5m]) + rate(prism_requests_total{status=\"miss\"}[5m]))",
"legendFormat": "Hit Rate"
}
],
"fieldConfig": {
"defaults": { "unit": "percentunit", "thresholds": { "steps": [
{ "value": 0, "color": "red" },
{ "value": 0.7, "color": "yellow" },
{ "value": 0.9, "color": "green" }
]}}
}
},
{
"title": "Render Duration (p50 / p95 / p99)",
"type": "timeseries",
"targets": [
{
"expr": "histogram_quantile(0.50, rate(prism_render_duration_ms_bucket[5m]))",
"legendFormat": "p50"
},
{
"expr": "histogram_quantile(0.95, rate(prism_render_duration_ms_bucket[5m]))",
"legendFormat": "p95"
},
{
"expr": "histogram_quantile(0.99, rate(prism_render_duration_ms_bucket[5m]))",
"legendFormat": "p99"
}
],
"fieldConfig": { "defaults": { "unit": "ms" } }
},
{
"title": "Active Renders & Queue Depth",
"type": "timeseries",
"targets": [
{ "expr": "prism_render_active", "legendFormat": "Active" },
{ "expr": "prism_render_queue_depth", "legendFormat": "Queued" }
]
},
{
"title": "Tab Pool",
"type": "timeseries",
"targets": [
{ "expr": "prism_tab_pool_size", "legendFormat": "Total" },
{ "expr": "prism_tab_pool_available", "legendFormat": "Available" }
]
},
{
"title": "Cache Entries & Memory",
"type": "timeseries",
"targets": [
{ "expr": "prism_cache_entries", "legendFormat": "Entries" },
{ "expr": "prism_cache_memory_bytes", "legendFormat": "Memory (bytes)" }
]
},
{
"title": "Circuit Breaker State",
"type": "stat",
"targets": [
{ "expr": "prism_circuit_breaker_state", "legendFormat": "State" }
],
"fieldConfig": {
"defaults": {
"mappings": [
{ "type": "value", "options": { "0": { "text": "CLOSED", "color": "green" } } },
{ "type": "value", "options": { "1": { "text": "OPEN", "color": "red" } } },
{ "type": "value", "options": { "2": { "text": "HALF-OPEN", "color": "yellow" } } }
]
}
}
},
{
"title": "Origin Latency (avg)",
"type": "timeseries",
"targets": [
{
"expr": "rate(prism_origin_duration_sum_ms[5m]) / rate(prism_origin_requests_total[5m])",
"legendFormat": "Avg Origin Latency (ms)"
}
],
"fieldConfig": { "defaults": { "unit": "ms" } }
},
{
"title": "Process Memory",
"type": "timeseries",
"targets": [
{ "expr": "process_resident_memory_bytes", "legendFormat": "RSS" },
{ "expr": "process_virtual_memory_bytes", "legendFormat": "Virtual" }
],
"fieldConfig": { "defaults": { "unit": "bytes" } }
}
]
}
Key Alerts
groups:
- name: prism
rules:
- alert: PrismCircuitBreakerOpen
expr: prism_circuit_breaker_state == 1
for: 1m
labels: { severity: critical }
annotations:
summary: "PRISM circuit breaker is open — renders disabled"
- alert: PrismHighRenderLatency
expr: histogram_quantile(0.95, rate(prism_render_duration_ms_bucket[5m])) > 10000
for: 5m
labels: { severity: warning }
annotations:
summary: "PRISM p95 render latency > 10s"
- alert: PrismTabPoolExhausted
expr: prism_tab_pool_available == 0
for: 2m
labels: { severity: warning }
annotations:
summary: "No available Chrome tabs in the pool"