feat: Claude Code Monitor — lanes, pipelines and a merged workspace
Internal SmartGift build of a Claude Code monitoring dashboard. Lanes: a durable unit of parallel agent work, one per working directory, tracked across session restarts. Managed lanes are git worktrees the dashboard provisions and can reset or remove behind a three-check destroy guard and a counted preflight; adopted lanes are directories you already own and are never destroyable. Pipelines: a lane moves through pipeline stages. A stage the agent declares with evidence renders green; a stage inferred from the tool-event stream renders dashed amber and never counts as done. Detection is forward-only within a 30-minute window, and never writes the declared stage. Workspace: one page at /run with a lane grid, the selected lane's pipeline, and a full Claude console behind a disclosure.
This commit is contained in:
@@ -0,0 +1,241 @@
|
||||
# ─────────────────────────────────────────────────────────────────────────────
|
||||
# Alertmanager configuration for Claude Code Agent Monitor
|
||||
#
|
||||
# Replace placeholder values (marked with <PLACEHOLDER>) before deploying.
|
||||
# ─────────────────────────────────────────────────────────────────────────────
|
||||
global:
|
||||
# SMTP defaults for email notifications
|
||||
smtp_smarthost: "<SMTP_HOST>:587"
|
||||
smtp_from: "alertmanager@example.com"
|
||||
smtp_auth_username: "<SMTP_USERNAME>"
|
||||
smtp_auth_password: "<SMTP_PASSWORD>"
|
||||
smtp_require_tls: true
|
||||
|
||||
# Slack API URL (override per-receiver if needed)
|
||||
slack_api_url: "<SLACK_WEBHOOK_URL>"
|
||||
|
||||
# PagerDuty URL
|
||||
pagerduty_url: "https://events.pagerduty.com/v2/enqueue"
|
||||
|
||||
# Global resolve timeout
|
||||
resolve_timeout: 5m
|
||||
|
||||
# ── Templates ───────────────────────────────────────────────────────────────
|
||||
templates:
|
||||
- "/etc/alertmanager/templates/*.tmpl"
|
||||
|
||||
# ── Inhibition rules ───────────────────────────────────────────────────────
|
||||
# Prevent lower-severity alerts from firing when a higher-severity alert
|
||||
# for the same service/alertname is already active.
|
||||
inhibit_rules:
|
||||
# If a critical alert is firing, suppress warning alerts for the same service
|
||||
- source_matchers:
|
||||
- severity = critical
|
||||
target_matchers:
|
||||
- severity = warning
|
||||
equal:
|
||||
- alertname
|
||||
- service
|
||||
- namespace
|
||||
|
||||
# If a critical alert is firing, suppress info alerts for the same service
|
||||
- source_matchers:
|
||||
- severity = critical
|
||||
target_matchers:
|
||||
- severity = info
|
||||
equal:
|
||||
- service
|
||||
- namespace
|
||||
|
||||
# If a warning alert is firing, suppress info alerts for the same service
|
||||
- source_matchers:
|
||||
- severity = warning
|
||||
target_matchers:
|
||||
- severity = info
|
||||
equal:
|
||||
- service
|
||||
- namespace
|
||||
|
||||
# If AgentMonitorDown is firing, suppress all other agent-monitor alerts
|
||||
- source_matchers:
|
||||
- alertname = AgentMonitorDown
|
||||
target_matchers:
|
||||
- service = agent-monitor
|
||||
equal:
|
||||
- namespace
|
||||
|
||||
# ── Route tree ──────────────────────────────────────────────────────────────
|
||||
route:
|
||||
# Default receiver for unmatched alerts
|
||||
receiver: slack-default
|
||||
|
||||
# Group alerts by these labels
|
||||
group_by:
|
||||
- alertname
|
||||
- environment
|
||||
- namespace
|
||||
|
||||
# Wait before sending initial notification (allows grouping)
|
||||
group_wait: 30s
|
||||
|
||||
# Wait before sending updates to an existing group
|
||||
group_interval: 5m
|
||||
|
||||
# Wait before re-sending a resolved notification
|
||||
repeat_interval: 4h
|
||||
|
||||
# Child routes (evaluated top-to-bottom, first match wins)
|
||||
routes:
|
||||
# ── Critical → PagerDuty + Slack ──────────────────────────────────────
|
||||
- matchers:
|
||||
- severity = critical
|
||||
receiver: pagerduty-critical
|
||||
group_wait: 10s
|
||||
repeat_interval: 1h
|
||||
continue: true # Also notify Slack
|
||||
|
||||
- matchers:
|
||||
- severity = critical
|
||||
receiver: slack-critical
|
||||
group_wait: 10s
|
||||
repeat_interval: 1h
|
||||
|
||||
# ── Warning → Slack ───────────────────────────────────────────────────
|
||||
- matchers:
|
||||
- severity = warning
|
||||
receiver: slack-warning
|
||||
group_wait: 30s
|
||||
repeat_interval: 4h
|
||||
|
||||
# ── Info → Email ──────────────────────────────────────────────────────
|
||||
- matchers:
|
||||
- severity = info
|
||||
receiver: email-info
|
||||
group_wait: 1m
|
||||
repeat_interval: 12h
|
||||
|
||||
# ── Watchdog (deadman's switch) ───────────────────────────────────────
|
||||
- matchers:
|
||||
- alertname = Watchdog
|
||||
receiver: "null"
|
||||
repeat_interval: 5m
|
||||
|
||||
# ── Receivers ───────────────────────────────────────────────────────────────
|
||||
receivers:
|
||||
# ── Null receiver (discard alerts) ────────────────────────────────────────
|
||||
- name: "null"
|
||||
|
||||
# ── Slack: Default channel ────────────────────────────────────────────────
|
||||
- name: slack-default
|
||||
slack_configs:
|
||||
- channel: "#agent-monitor-alerts"
|
||||
send_resolved: true
|
||||
username: "AlertManager"
|
||||
icon_emoji: ":bell:"
|
||||
title: >-
|
||||
[{{ .Status | toUpper }}{{ if eq .Status "firing" }}:{{ .Alerts.Firing | len }}{{ end }}]
|
||||
{{ .CommonLabels.alertname }}
|
||||
text: >-
|
||||
{{ range .Alerts }}
|
||||
*Alert:* {{ .Labels.alertname }} - `{{ .Labels.severity }}`
|
||||
*Environment:* {{ .Labels.namespace }}
|
||||
*Description:* {{ .Annotations.description }}
|
||||
*Details:*
|
||||
{{ range .Labels.SortedPairs }} • *{{ .Name }}:* `{{ .Value }}`
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
actions:
|
||||
- type: button
|
||||
text: "Dashboard :grafana:"
|
||||
url: "{{ (index .Alerts 0).Annotations.dashboard_url }}"
|
||||
- type: button
|
||||
text: "Runbook :book:"
|
||||
url: "{{ (index .Alerts 0).Annotations.runbook_url }}"
|
||||
|
||||
# ── Slack: Critical alerts ────────────────────────────────────────────────
|
||||
- name: slack-critical
|
||||
slack_configs:
|
||||
- channel: "#agent-monitor-critical"
|
||||
send_resolved: true
|
||||
username: "AlertManager"
|
||||
icon_emoji: ":rotating_light:"
|
||||
color: >-
|
||||
{{ if eq .Status "firing" }}danger{{ else }}good{{ end }}
|
||||
title: >-
|
||||
:rotating_light: [{{ .Status | toUpper }}]
|
||||
{{ .CommonLabels.alertname }}
|
||||
text: >-
|
||||
{{ range .Alerts }}
|
||||
*CRITICAL Alert:* {{ .Labels.alertname }}
|
||||
*Environment:* {{ .Labels.namespace }}
|
||||
*Description:* {{ .Annotations.description }}
|
||||
*Summary:* {{ .Annotations.summary }}
|
||||
*Started:* {{ .StartsAt.Format "2006-01-02 15:04:05 UTC" }}
|
||||
{{ end }}
|
||||
actions:
|
||||
- type: button
|
||||
text: "Dashboard"
|
||||
url: "{{ (index .Alerts 0).Annotations.dashboard_url }}"
|
||||
- type: button
|
||||
text: "Runbook"
|
||||
url: "{{ (index .Alerts 0).Annotations.runbook_url }}"
|
||||
|
||||
# ── Slack: Warning alerts ─────────────────────────────────────────────────
|
||||
- name: slack-warning
|
||||
slack_configs:
|
||||
- channel: "#agent-monitor-alerts"
|
||||
send_resolved: true
|
||||
username: "AlertManager"
|
||||
icon_emoji: ":warning:"
|
||||
color: >-
|
||||
{{ if eq .Status "firing" }}warning{{ else }}good{{ end }}
|
||||
title: >-
|
||||
:warning: [{{ .Status | toUpper }}]
|
||||
{{ .CommonLabels.alertname }}
|
||||
text: >-
|
||||
{{ range .Alerts }}
|
||||
*Warning:* {{ .Labels.alertname }}
|
||||
*Environment:* {{ .Labels.namespace }}
|
||||
*Description:* {{ .Annotations.description }}
|
||||
{{ end }}
|
||||
|
||||
# ── PagerDuty: Critical alerts ───────────────────────────────────────────
|
||||
- name: pagerduty-critical
|
||||
pagerduty_configs:
|
||||
- routing_key: "<PAGERDUTY_ROUTING_KEY>"
|
||||
severity: >-
|
||||
{{ if eq .CommonLabels.severity "critical" }}critical{{ else }}warning{{ end }}
|
||||
description: >-
|
||||
{{ .CommonAnnotations.summary }}
|
||||
details:
|
||||
environment: "{{ .CommonLabels.namespace }}"
|
||||
alertname: "{{ .CommonLabels.alertname }}"
|
||||
service: "{{ .CommonLabels.service }}"
|
||||
description: "{{ .CommonAnnotations.description }}"
|
||||
num_firing: "{{ .Alerts.Firing | len }}"
|
||||
|
||||
# ── Email: Info-level alerts ──────────────────────────────────────────────
|
||||
- name: email-info
|
||||
email_configs:
|
||||
- to: "<ALERT_EMAIL_RECIPIENTS>"
|
||||
send_resolved: true
|
||||
headers:
|
||||
Subject: >-
|
||||
[Agent Monitor {{ .Status | toUpper }}]
|
||||
{{ .CommonLabels.alertname }}
|
||||
({{ .CommonLabels.namespace }})
|
||||
html: |
|
||||
<h2>{{ .CommonLabels.alertname }}</h2>
|
||||
<p><b>Status:</b> {{ .Status }}</p>
|
||||
<p><b>Environment:</b> {{ .CommonLabels.namespace }}</p>
|
||||
<table border="1" cellpadding="5">
|
||||
<tr><th>Alert</th><th>Severity</th><th>Description</th><th>Started</th></tr>
|
||||
{{ range .Alerts }}
|
||||
<tr>
|
||||
<td>{{ .Labels.alertname }}</td>
|
||||
<td>{{ .Labels.severity }}</td>
|
||||
<td>{{ .Annotations.description }}</td>
|
||||
<td>{{ .StartsAt.Format "2006-01-02 15:04:05 UTC" }}</td>
|
||||
</tr>
|
||||
{{ end }}
|
||||
</table>
|
||||
@@ -0,0 +1,140 @@
|
||||
# Coralogix Integration
|
||||
|
||||
Full-stack observability for Claude Code Agent Monitor via [Coralogix](https://coralogix.com) — logs, metrics, traces, and SLO tracking through a single platform.
|
||||
|
||||
## Architecture
|
||||
|
||||
```mermaid
|
||||
graph TB
|
||||
subgraph "Kubernetes Cluster"
|
||||
APP["Agent Monitor Pods"]
|
||||
MCP["MCP Sidecar"]
|
||||
OTEL["OTel Collector<br/>(DaemonSet)"]
|
||||
end
|
||||
|
||||
APP -->|"metrics + logs"| OTEL
|
||||
MCP -->|"metrics + logs"| OTEL
|
||||
|
||||
OTEL -->|"OTLP (gRPC)"| CX["Coralogix Platform"]
|
||||
|
||||
subgraph "Coralogix"
|
||||
CX --> LOGS["Log Analytics<br/>DataPrime Queries"]
|
||||
CX --> MET["Metrics<br/>PromQL + Recording Rules"]
|
||||
CX --> TRACE["Distributed Tracing"]
|
||||
CX --> ALERT["Alert Engine"]
|
||||
CX --> DASH["Custom Dashboards"]
|
||||
CX --> SLO["SLO Management"]
|
||||
end
|
||||
|
||||
ALERT -->|"Critical"| PD["PagerDuty"]
|
||||
ALERT -->|"Warning"| SLACK["Slack"]
|
||||
|
||||
style OTEL fill:#4f46e5,color:#fff
|
||||
style CX fill:#1a1a2e,color:#fff
|
||||
style LOGS fill:#7c3aed,color:#fff
|
||||
style MET fill:#e6522c,color:#fff
|
||||
style TRACE fill:#059669,color:#fff
|
||||
style ALERT fill:#dc2626,color:#fff
|
||||
style DASH fill:#f46800,color:#fff
|
||||
style SLO fill:#0ea5e9,color:#fff
|
||||
```
|
||||
|
||||
## Files
|
||||
|
||||
| File | Purpose |
|
||||
|------|---------|
|
||||
| `values.yaml` | Helm values for Coralogix OpenTelemetry Collector |
|
||||
| `alerts.yaml` | Alert definitions (mirrors Prometheus/Alertmanager rules) |
|
||||
| `dashboards.yaml` | Custom dashboard with 6 rows, 18 panels, SLO tracking |
|
||||
| `coralogix-terraform.tf` | Terraform-managed alerts, parsing rules, recording rules |
|
||||
|
||||
## Quick Start
|
||||
|
||||
### 1. Add the Helm Repository
|
||||
|
||||
```bash
|
||||
helm repo add coralogix https://cgx.jfrog.io/artifactory/coralogix-charts-virtual
|
||||
helm repo update
|
||||
```
|
||||
|
||||
### 2. Create the API Key Secret
|
||||
|
||||
```bash
|
||||
kubectl create secret generic coralogix-keys \
|
||||
--namespace agent-monitor \
|
||||
--from-literal=PRIVATE_KEY=<YOUR_CORALOGIX_SEND_YOUR_DATA_KEY>
|
||||
```
|
||||
|
||||
### 3. Deploy the OTel Collector
|
||||
|
||||
```bash
|
||||
helm install coralogix-otel coralogix/opentelemetry \
|
||||
--namespace agent-monitor \
|
||||
-f deployments/monitoring/coralogix/values.yaml
|
||||
```
|
||||
|
||||
### 4. Import the Dashboard
|
||||
|
||||
Upload `dashboards.yaml` via the Coralogix UI:
|
||||
|
||||
**Dashboards → Custom Dashboards → Import**
|
||||
|
||||
### 5. (Optional) Terraform-managed Alerts
|
||||
|
||||
```bash
|
||||
cd deployments/monitoring/coralogix
|
||||
export CORALOGIX_API_KEY="<your-key>"
|
||||
export CORALOGIX_ENV="coralogix.com"
|
||||
terraform init
|
||||
terraform apply
|
||||
```
|
||||
|
||||
## What Gets Collected
|
||||
|
||||
| Signal | Source | Destination |
|
||||
|--------|--------|-------------|
|
||||
| **Logs** | Pod stdout/stderr (JSON structured) | Coralogix Log Analytics |
|
||||
| **Metrics** | Prometheus scrape (`/api/health`) | Coralogix Metrics |
|
||||
| **K8s Metrics** | kubelet, cAdvisor, host metrics | Coralogix Metrics |
|
||||
| **Traces** | OTLP from application (if instrumented) | Coralogix Tracing |
|
||||
|
||||
## Alert Parity
|
||||
|
||||
All 10 Prometheus/Alertmanager rules are replicated in Coralogix:
|
||||
|
||||
| Alert | Severity | Prometheus | Coralogix |
|
||||
|-------|----------|:----------:|:---------:|
|
||||
| Instance Down | Critical | ✓ | ✓ |
|
||||
| High Error Rate | Critical | ✓ | ✓ |
|
||||
| Pod Restart Loop | Critical | ✓ | ✓ |
|
||||
| PV Nearly Full | Critical | ✓ | ✓ |
|
||||
| High Latency | Warning | ✓ | ✓ |
|
||||
| WebSocket Spike | Warning | ✓ | ✓ |
|
||||
| High Memory | Warning | ✓ | ✓ |
|
||||
| High CPU | Warning | ✓ | ✓ |
|
||||
| HPA Maxed Out | Warning | ✓ | ✓ |
|
||||
| Slow DB Queries | Warning | ✓ | ✓ |
|
||||
|
||||
## Dashboard Panels
|
||||
|
||||
The custom dashboard provides 18 panels across 6 rows:
|
||||
|
||||
1. **Overview** — Active sessions, request rate, WebSocket connections
|
||||
2. **HTTP Performance** — Latency distribution, error rate, status codes
|
||||
3. **Application Logs** — Error log stream (DataPrime), log volume by severity, hook throughput
|
||||
4. **Infrastructure** — CPU, memory, pod status
|
||||
5. **Database & Storage** — SQLite query duration, PV usage, network I/O
|
||||
6. **SLO Tracking** — Availability SLO (99.9%), latency SLO (P95 < 500ms), error budget burn
|
||||
|
||||
## Coralogix Regions
|
||||
|
||||
Set `global.domain` in `values.yaml` to match your Coralogix region:
|
||||
|
||||
| Region | Domain |
|
||||
|--------|--------|
|
||||
| US1 | `coralogix.us` |
|
||||
| US2 | `cx498.coralogix.com` |
|
||||
| EU1 | `coralogix.com` |
|
||||
| EU2 | `eu2.coralogix.com` |
|
||||
| AP1 (India) | `coralogix.in` |
|
||||
| AP2 (Singapore) | `coralogix.sg` |
|
||||
@@ -0,0 +1,194 @@
|
||||
# ─────────────────────────────────────────────────────────────────────────────
|
||||
# Coralogix Alert Definitions for Claude Code Agent Monitor
|
||||
#
|
||||
# These alerts can be imported via the Coralogix Alerts API or Terraform
|
||||
# provider (coralogix/coralogix). They mirror the Prometheus/Alertmanager
|
||||
# rules in ../prometheus/rules/ for consistency across observability stacks.
|
||||
#
|
||||
# API import:
|
||||
# curl -X POST "https://api.coralogix.com/api/v1/external/alerts" \
|
||||
# -H "Authorization: Bearer $CORALOGIX_API_KEY" \
|
||||
# -H "Content-Type: application/json" \
|
||||
# -d @deployments/monitoring/coralogix/alerts.yaml
|
||||
#
|
||||
# Terraform:
|
||||
# See the coralogix_alert resources in coralogix-terraform.tf
|
||||
# ─────────────────────────────────────────────────────────────────────────────
|
||||
|
||||
alerts:
|
||||
# ── Critical ───────────────────────────────────────────────────────────────
|
||||
|
||||
- name: "Agent Monitor Down"
|
||||
description: "No metrics received from agent-monitor pods for > 2 minutes"
|
||||
severity: critical
|
||||
type: metric
|
||||
condition:
|
||||
metric_name: "up"
|
||||
filter:
|
||||
job: "agent-monitor"
|
||||
threshold: 1
|
||||
comparison: less_than
|
||||
for_duration: "2m"
|
||||
of_last: "5m"
|
||||
notifications:
|
||||
- integration: pagerduty
|
||||
- integration: slack
|
||||
channel: "#agent-monitor-critical"
|
||||
labels:
|
||||
service: agent-monitor
|
||||
team: platform
|
||||
|
||||
- name: "High Error Rate"
|
||||
description: "5xx error rate exceeds 5% of total requests for 5 minutes"
|
||||
severity: critical
|
||||
type: ratio
|
||||
condition:
|
||||
numerator:
|
||||
query: 'http_requests_total{job="agent-monitor", status=~"5.."}'
|
||||
denominator:
|
||||
query: 'http_requests_total{job="agent-monitor"}'
|
||||
threshold: 0.05
|
||||
comparison: greater_than
|
||||
for_duration: "5m"
|
||||
notifications:
|
||||
- integration: pagerduty
|
||||
- integration: slack
|
||||
channel: "#agent-monitor-critical"
|
||||
labels:
|
||||
service: agent-monitor
|
||||
|
||||
- name: "Pod Restart Loop"
|
||||
description: "Agent Monitor pod has restarted > 5 times in 15 minutes"
|
||||
severity: critical
|
||||
type: metric
|
||||
condition:
|
||||
query: 'increase(kube_pod_container_status_restarts_total{namespace=~"agent-monitor.*", container="agent-monitor"}[15m])'
|
||||
threshold: 5
|
||||
comparison: greater_than
|
||||
for_duration: "1m"
|
||||
notifications:
|
||||
- integration: pagerduty
|
||||
- integration: slack
|
||||
channel: "#agent-monitor-critical"
|
||||
labels:
|
||||
service: agent-monitor
|
||||
|
||||
- name: "Persistent Volume Nearly Full"
|
||||
description: "SQLite persistent volume is > 90% full"
|
||||
severity: critical
|
||||
type: metric
|
||||
condition:
|
||||
query: '(kubelet_volume_stats_used_bytes{namespace=~"agent-monitor.*"} / kubelet_volume_stats_capacity_bytes{namespace=~"agent-monitor.*"}) * 100'
|
||||
threshold: 90
|
||||
comparison: greater_than
|
||||
for_duration: "5m"
|
||||
notifications:
|
||||
- integration: pagerduty
|
||||
- integration: slack
|
||||
channel: "#agent-monitor-critical"
|
||||
labels:
|
||||
service: agent-monitor
|
||||
|
||||
# ── Warning ────────────────────────────────────────────────────────────────
|
||||
|
||||
- name: "High Latency"
|
||||
description: "P95 request latency exceeds 2 seconds for 5 minutes"
|
||||
severity: warning
|
||||
type: metric
|
||||
condition:
|
||||
query: 'histogram_quantile(0.95, rate(http_request_duration_seconds_bucket{job="agent-monitor"}[5m]))'
|
||||
threshold: 2
|
||||
comparison: greater_than
|
||||
for_duration: "5m"
|
||||
notifications:
|
||||
- integration: slack
|
||||
channel: "#agent-monitor-alerts"
|
||||
labels:
|
||||
service: agent-monitor
|
||||
|
||||
- name: "WebSocket Connection Spike"
|
||||
description: "Active WebSocket connections exceed 1000"
|
||||
severity: warning
|
||||
type: metric
|
||||
condition:
|
||||
metric_name: "websocket_connections_active"
|
||||
filter:
|
||||
job: "agent-monitor"
|
||||
threshold: 1000
|
||||
comparison: greater_than
|
||||
for_duration: "2m"
|
||||
notifications:
|
||||
- integration: slack
|
||||
channel: "#agent-monitor-alerts"
|
||||
labels:
|
||||
service: agent-monitor
|
||||
|
||||
- name: "High Memory Usage"
|
||||
description: "Container memory usage exceeds 85% of limit"
|
||||
severity: warning
|
||||
type: metric
|
||||
condition:
|
||||
query: '(container_memory_working_set_bytes{namespace=~"agent-monitor.*", container="agent-monitor"} / container_spec_memory_limit_bytes{namespace=~"agent-monitor.*", container="agent-monitor"}) * 100'
|
||||
threshold: 85
|
||||
comparison: greater_than
|
||||
for_duration: "5m"
|
||||
notifications:
|
||||
- integration: slack
|
||||
channel: "#agent-monitor-alerts"
|
||||
labels:
|
||||
service: agent-monitor
|
||||
|
||||
- name: "High CPU Usage"
|
||||
description: "Container CPU usage exceeds 80% for 10 minutes"
|
||||
severity: warning
|
||||
type: metric
|
||||
condition:
|
||||
query: '(rate(container_cpu_usage_seconds_total{namespace=~"agent-monitor.*", container="agent-monitor"}[5m]) / container_spec_cpu_quota{namespace=~"agent-monitor.*", container="agent-monitor"} * 100000)'
|
||||
threshold: 80
|
||||
comparison: greater_than
|
||||
for_duration: "10m"
|
||||
notifications:
|
||||
- integration: slack
|
||||
channel: "#agent-monitor-alerts"
|
||||
labels:
|
||||
service: agent-monitor
|
||||
|
||||
- name: "HPA Maxed Out"
|
||||
description: "HPA replicas at max for 15 minutes — may need capacity increase"
|
||||
severity: warning
|
||||
type: metric
|
||||
condition:
|
||||
query: 'kube_horizontalpodautoscaler_status_current_replicas{namespace=~"agent-monitor.*"} == kube_horizontalpodautoscaler_spec_max_replicas{namespace=~"agent-monitor.*"}'
|
||||
threshold: 1
|
||||
comparison: greater_than_or_equal
|
||||
for_duration: "15m"
|
||||
notifications:
|
||||
- integration: slack
|
||||
channel: "#agent-monitor-alerts"
|
||||
labels:
|
||||
service: agent-monitor
|
||||
|
||||
- name: "Slow Database Queries"
|
||||
description: "SQLite query duration exceeds 1 second"
|
||||
severity: warning
|
||||
type: metric
|
||||
condition:
|
||||
metric_name: "sqlite_query_duration_seconds"
|
||||
filter:
|
||||
job: "agent-monitor"
|
||||
threshold: 1
|
||||
comparison: greater_than
|
||||
for_duration: "5m"
|
||||
notifications:
|
||||
- integration: slack
|
||||
channel: "#agent-monitor-alerts"
|
||||
labels:
|
||||
service: agent-monitor
|
||||
|
||||
# ── Notification integrations ────────────────────────────────────────────────
|
||||
# Configure these in Coralogix UI: Settings → Integrations → Outbound Webhooks
|
||||
#
|
||||
# Required integrations:
|
||||
# - pagerduty: PagerDuty Events API v2 routing key
|
||||
# - slack: Slack webhook for #agent-monitor-critical and #agent-monitor-alerts
|
||||
# - email: (optional) Email notification group
|
||||
@@ -0,0 +1,353 @@
|
||||
# ─────────────────────────────────────────────────────────────────────────────
|
||||
# Coralogix Terraform Integration for Claude Code Agent Monitor
|
||||
#
|
||||
# Provisions Coralogix resources via the official Terraform provider:
|
||||
# - Alert rules (mirroring Prometheus/Alertmanager rules)
|
||||
# - Log parsing rules for structured JSON ingestion
|
||||
# - Recording rules for pre-aggregated SLO metrics
|
||||
# - Dashboard provisioning
|
||||
#
|
||||
# Usage:
|
||||
# export CORALOGIX_API_KEY="<your-send-your-data-key>"
|
||||
# export CORALOGIX_ENV="<your-coralogix-domain>" # e.g. coralogix.com
|
||||
# terraform init
|
||||
# terraform plan
|
||||
# terraform apply
|
||||
#
|
||||
# Requires: hashicorp/terraform >= 1.5, coralogix/coralogix >= 1.10
|
||||
# ─────────────────────────────────────────────────────────────────────────────
|
||||
|
||||
terraform {
|
||||
required_version = ">= 1.5"
|
||||
required_providers {
|
||||
coralogix = {
|
||||
source = "coralogix/coralogix"
|
||||
version = "~> 1.10"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
provider "coralogix" {
|
||||
# API key and environment are sourced from:
|
||||
# CORALOGIX_API_KEY – Send-Your-Data API key
|
||||
# CORALOGIX_ENV – Domain (e.g. coralogix.com, eu2.coralogix.com)
|
||||
}
|
||||
|
||||
# ── Variables ────────────────────────────────────────────────────────────────
|
||||
|
||||
variable "environment" {
|
||||
description = "Deployment environment (dev, staging, production)"
|
||||
type = string
|
||||
default = "production"
|
||||
validation {
|
||||
condition = contains(["dev", "staging", "production"], var.environment)
|
||||
error_message = "environment must be one of: dev, staging, production."
|
||||
}
|
||||
}
|
||||
|
||||
variable "notification_group_id" {
|
||||
description = "Coralogix notification group ID for alert routing"
|
||||
type = string
|
||||
default = ""
|
||||
}
|
||||
|
||||
variable "pagerduty_webhook_id" {
|
||||
description = "Coralogix outbound webhook ID for PagerDuty integration"
|
||||
type = string
|
||||
default = ""
|
||||
}
|
||||
|
||||
variable "slack_webhook_id" {
|
||||
description = "Coralogix outbound webhook ID for Slack integration"
|
||||
type = string
|
||||
default = ""
|
||||
}
|
||||
|
||||
locals {
|
||||
app_name = "agent-monitor"
|
||||
subsystem = "kubernetes"
|
||||
alert_prefix = "[Agent Monitor]"
|
||||
}
|
||||
|
||||
# ── Parsing Rules ────────────────────────────────────────────────────────────
|
||||
# Structured JSON log parsing for agent-monitor application logs
|
||||
|
||||
resource "coralogix_rules_group" "agent_monitor_parsing" {
|
||||
name = "${local.alert_prefix} Log Parsing"
|
||||
description = "Parse structured JSON logs from Agent Monitor pods"
|
||||
enabled = true
|
||||
order = 1
|
||||
|
||||
rule_subgroups {
|
||||
rules {
|
||||
name = "JSON Extract"
|
||||
description = "Extract structured fields from JSON application logs"
|
||||
source_field = "text"
|
||||
enabled = true
|
||||
|
||||
parse_json_field {
|
||||
destination_field = "json"
|
||||
keep_source_field = false
|
||||
keep_destination_field = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
rule_subgroups {
|
||||
rules {
|
||||
name = "Severity Mapping"
|
||||
description = "Map log level field to Coralogix severity"
|
||||
source_field = "json.level"
|
||||
enabled = true
|
||||
|
||||
extract {
|
||||
regexp = "(?P<severity>debug|info|warn|error|fatal)"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# ── Recording Rules ──────────────────────────────────────────────────────────
|
||||
# Pre-aggregate SLO metrics for efficient dashboard queries
|
||||
|
||||
resource "coralogix_recording_rule_group_set" "slo_metrics" {
|
||||
name = "${local.alert_prefix} SLO Recording Rules"
|
||||
|
||||
groups {
|
||||
name = "agent_monitor_slo"
|
||||
interval = 60 # seconds
|
||||
|
||||
rules {
|
||||
record = "agent_monitor:http_availability:ratio_rate5m"
|
||||
expr = <<-EOT
|
||||
1 - (
|
||||
sum(rate(http_requests_total{job="agent-monitor", status=~"5.."}[5m]))
|
||||
/
|
||||
sum(rate(http_requests_total{job="agent-monitor"}[5m]))
|
||||
)
|
||||
EOT
|
||||
labels = {
|
||||
service = local.app_name
|
||||
environment = var.environment
|
||||
}
|
||||
}
|
||||
|
||||
rules {
|
||||
record = "agent_monitor:http_latency_p95:seconds_rate5m"
|
||||
expr = <<-EOT
|
||||
histogram_quantile(0.95,
|
||||
sum(rate(http_request_duration_seconds_bucket{job="agent-monitor"}[5m])) by (le)
|
||||
)
|
||||
EOT
|
||||
labels = {
|
||||
service = local.app_name
|
||||
environment = var.environment
|
||||
}
|
||||
}
|
||||
|
||||
rules {
|
||||
record = "agent_monitor:websocket_connections:total"
|
||||
expr = <<-EOT
|
||||
sum(websocket_connections_active{job="agent-monitor"})
|
||||
EOT
|
||||
labels = {
|
||||
service = local.app_name
|
||||
environment = var.environment
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# ── Alert Rules ──────────────────────────────────────────────────────────────
|
||||
|
||||
resource "coralogix_alert" "instance_down" {
|
||||
name = "${local.alert_prefix} Instance Down"
|
||||
description = "No metrics received from agent-monitor pods for > 2 minutes"
|
||||
severity = "Critical"
|
||||
enabled = true
|
||||
|
||||
metric {
|
||||
promql {
|
||||
text = "up{job=\"agent-monitor\"} == 0"
|
||||
condition = "more_than"
|
||||
threshold = 0
|
||||
}
|
||||
duration = "2m"
|
||||
}
|
||||
|
||||
notifications_group {
|
||||
dynamic "notification" {
|
||||
for_each = var.pagerduty_webhook_id != "" ? [1] : []
|
||||
content {
|
||||
integration_id = var.pagerduty_webhook_id
|
||||
}
|
||||
}
|
||||
dynamic "notification" {
|
||||
for_each = var.slack_webhook_id != "" ? [1] : []
|
||||
content {
|
||||
integration_id = var.slack_webhook_id
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
labels = {
|
||||
service = local.app_name
|
||||
environment = var.environment
|
||||
team = "platform"
|
||||
}
|
||||
}
|
||||
|
||||
resource "coralogix_alert" "high_error_rate" {
|
||||
name = "${local.alert_prefix} High Error Rate"
|
||||
description = "5xx error rate exceeds 5% of total requests for 5 minutes"
|
||||
severity = "Critical"
|
||||
enabled = true
|
||||
|
||||
metric {
|
||||
promql {
|
||||
text = <<-EOT
|
||||
(
|
||||
sum(rate(http_requests_total{job="agent-monitor", status=~"5.."}[5m]))
|
||||
/
|
||||
sum(rate(http_requests_total{job="agent-monitor"}[5m]))
|
||||
) * 100 > 5
|
||||
EOT
|
||||
condition = "more_than"
|
||||
threshold = 5
|
||||
}
|
||||
duration = "5m"
|
||||
}
|
||||
|
||||
notifications_group {
|
||||
dynamic "notification" {
|
||||
for_each = var.pagerduty_webhook_id != "" ? [1] : []
|
||||
content {
|
||||
integration_id = var.pagerduty_webhook_id
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
labels = {
|
||||
service = local.app_name
|
||||
environment = var.environment
|
||||
}
|
||||
}
|
||||
|
||||
resource "coralogix_alert" "high_latency" {
|
||||
name = "${local.alert_prefix} High Latency"
|
||||
description = "P95 request latency exceeds 2 seconds for 5 minutes"
|
||||
severity = "Warning"
|
||||
enabled = true
|
||||
|
||||
metric {
|
||||
promql {
|
||||
text = "histogram_quantile(0.95, sum(rate(http_request_duration_seconds_bucket{job=\"agent-monitor\"}[5m])) by (le)) > 2"
|
||||
condition = "more_than"
|
||||
threshold = 2
|
||||
}
|
||||
duration = "5m"
|
||||
}
|
||||
|
||||
notifications_group {
|
||||
dynamic "notification" {
|
||||
for_each = var.slack_webhook_id != "" ? [1] : []
|
||||
content {
|
||||
integration_id = var.slack_webhook_id
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
labels = {
|
||||
service = local.app_name
|
||||
environment = var.environment
|
||||
}
|
||||
}
|
||||
|
||||
resource "coralogix_alert" "high_memory" {
|
||||
name = "${local.alert_prefix} High Memory Usage"
|
||||
description = "Container memory usage exceeds 85% of limit"
|
||||
severity = "Warning"
|
||||
enabled = true
|
||||
|
||||
metric {
|
||||
promql {
|
||||
text = <<-EOT
|
||||
(
|
||||
container_memory_working_set_bytes{namespace=~"agent-monitor.*", container="agent-monitor"}
|
||||
/
|
||||
container_spec_memory_limit_bytes{namespace=~"agent-monitor.*", container="agent-monitor"}
|
||||
) * 100 > 85
|
||||
EOT
|
||||
condition = "more_than"
|
||||
threshold = 85
|
||||
}
|
||||
duration = "5m"
|
||||
}
|
||||
|
||||
notifications_group {
|
||||
dynamic "notification" {
|
||||
for_each = var.slack_webhook_id != "" ? [1] : []
|
||||
content {
|
||||
integration_id = var.slack_webhook_id
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
labels = {
|
||||
service = local.app_name
|
||||
environment = var.environment
|
||||
}
|
||||
}
|
||||
|
||||
resource "coralogix_alert" "pod_restart_loop" {
|
||||
name = "${local.alert_prefix} Pod Restart Loop"
|
||||
description = "Agent Monitor pod has restarted > 5 times in 15 minutes"
|
||||
severity = "Critical"
|
||||
enabled = true
|
||||
|
||||
metric {
|
||||
promql {
|
||||
text = "increase(kube_pod_container_status_restarts_total{namespace=~\"agent-monitor.*\", container=\"agent-monitor\"}[15m]) > 5"
|
||||
condition = "more_than"
|
||||
threshold = 5
|
||||
}
|
||||
duration = "1m"
|
||||
}
|
||||
|
||||
notifications_group {
|
||||
dynamic "notification" {
|
||||
for_each = var.pagerduty_webhook_id != "" ? [1] : []
|
||||
content {
|
||||
integration_id = var.pagerduty_webhook_id
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
labels = {
|
||||
service = local.app_name
|
||||
environment = var.environment
|
||||
}
|
||||
}
|
||||
|
||||
# ── Outputs ──────────────────────────────────────────────────────────────────
|
||||
|
||||
output "parsing_rule_group_id" {
|
||||
description = "ID of the Coralogix parsing rule group"
|
||||
value = coralogix_rules_group.agent_monitor_parsing.id
|
||||
}
|
||||
|
||||
output "recording_rule_set_id" {
|
||||
description = "ID of the Coralogix recording rule group set"
|
||||
value = coralogix_recording_rule_group_set.slo_metrics.id
|
||||
}
|
||||
|
||||
output "alert_ids" {
|
||||
description = "IDs of all provisioned Coralogix alerts"
|
||||
value = {
|
||||
instance_down = coralogix_alert.instance_down.id
|
||||
high_error_rate = coralogix_alert.high_error_rate.id
|
||||
high_latency = coralogix_alert.high_latency.id
|
||||
high_memory = coralogix_alert.high_memory.id
|
||||
pod_restart = coralogix_alert.pod_restart_loop.id
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,245 @@
|
||||
# ─────────────────────────────────────────────────────────────────────────────
|
||||
# Coralogix Custom Dashboard – Agent Monitor
|
||||
#
|
||||
# Import via Coralogix UI: Dashboards → Custom Dashboards → Import
|
||||
# Or via API:
|
||||
# curl -X POST "https://api.coralogix.com/api/v1/external/grafana/api/dashboards/db" \
|
||||
# -H "Authorization: Bearer $CORALOGIX_API_KEY" \
|
||||
# -H "Content-Type: application/json" \
|
||||
# -d @dashboards.yaml
|
||||
#
|
||||
# This dashboard mirrors the Grafana dashboard (../grafana/dashboards/) while
|
||||
# leveraging Coralogix-native features: DataPrime queries, log correlation,
|
||||
# distributed tracing waterfall, and Apdex scoring.
|
||||
# ─────────────────────────────────────────────────────────────────────────────
|
||||
|
||||
dashboard:
|
||||
name: "Agent Monitor – Operations"
|
||||
description: "Claude Code Agent Monitor: real-time operations, SLOs, and infrastructure health"
|
||||
folder: "Agent Monitor"
|
||||
tags:
|
||||
- agent-monitor
|
||||
- operations
|
||||
- sre
|
||||
|
||||
# ── Row 1: Overview ──────────────────────────────────────────────────────
|
||||
rows:
|
||||
- name: "Overview"
|
||||
panels:
|
||||
- title: "Active Sessions"
|
||||
type: line-chart
|
||||
query:
|
||||
type: metrics
|
||||
promql: 'agent_monitor_active_sessions'
|
||||
legend: "{{namespace}}"
|
||||
span: 4
|
||||
|
||||
- title: "Request Rate (req/s)"
|
||||
type: line-chart
|
||||
query:
|
||||
type: metrics
|
||||
promql: 'sum(rate(http_requests_total{job="agent-monitor"}[5m]))'
|
||||
legend: "Requests/sec"
|
||||
span: 4
|
||||
|
||||
- title: "WebSocket Connections"
|
||||
type: line-chart
|
||||
query:
|
||||
type: metrics
|
||||
promql: 'websocket_connections_active{job="agent-monitor"}'
|
||||
legend: "{{pod}}"
|
||||
span: 4
|
||||
|
||||
# ── Row 2: HTTP Performance ────────────────────────────────────────────
|
||||
- name: "HTTP Performance"
|
||||
panels:
|
||||
- title: "Latency Distribution (P50 / P95 / P99)"
|
||||
type: line-chart
|
||||
query:
|
||||
type: metrics
|
||||
promql: |
|
||||
histogram_quantile(0.50, sum(rate(http_request_duration_seconds_bucket{job="agent-monitor"}[5m])) by (le))
|
||||
histogram_quantile(0.95, sum(rate(http_request_duration_seconds_bucket{job="agent-monitor"}[5m])) by (le))
|
||||
histogram_quantile(0.99, sum(rate(http_request_duration_seconds_bucket{job="agent-monitor"}[5m])) by (le))
|
||||
span: 6
|
||||
|
||||
- title: "Error Rate (%)"
|
||||
type: line-chart
|
||||
query:
|
||||
type: metrics
|
||||
promql: |
|
||||
sum(rate(http_requests_total{job="agent-monitor", status=~"5.."}[5m]))
|
||||
/
|
||||
sum(rate(http_requests_total{job="agent-monitor"}[5m])) * 100
|
||||
legend: "5xx %"
|
||||
thresholds:
|
||||
- value: 1
|
||||
color: yellow
|
||||
- value: 5
|
||||
color: red
|
||||
span: 3
|
||||
|
||||
- title: "Status Code Distribution"
|
||||
type: bar-chart
|
||||
query:
|
||||
type: metrics
|
||||
promql: 'sum by (status) (increase(http_requests_total{job="agent-monitor"}[1h]))'
|
||||
span: 3
|
||||
|
||||
# ── Row 3: Logs (DataPrime) ────────────────────────────────────────────
|
||||
- name: "Application Logs"
|
||||
panels:
|
||||
- title: "Error Logs"
|
||||
type: dataprime
|
||||
query: |
|
||||
source logs
|
||||
| filter $d.cx.application.name == 'agent-monitor'
|
||||
| filter $d.severity == 'ERROR' || $d.severity == 'FATAL'
|
||||
| select $m.timestamp, $d.k8s.pod.name, $d.message
|
||||
| order by $m.timestamp desc
|
||||
| limit 100
|
||||
span: 6
|
||||
|
||||
- title: "Log Volume by Severity"
|
||||
type: bar-chart
|
||||
query:
|
||||
type: dataprime
|
||||
expression: |
|
||||
source logs
|
||||
| filter $d.cx.application.name == 'agent-monitor'
|
||||
| count_group_by $d.severity as count
|
||||
span: 3
|
||||
|
||||
- title: "Hook Event Throughput"
|
||||
type: line-chart
|
||||
query:
|
||||
type: dataprime
|
||||
expression: |
|
||||
source logs
|
||||
| filter $d.cx.application.name == 'agent-monitor'
|
||||
| filter $d.message matches 'hook.*event'
|
||||
| count_per_time 1m as throughput
|
||||
span: 3
|
||||
|
||||
# ── Row 4: Infrastructure ──────────────────────────────────────────────
|
||||
- name: "Infrastructure"
|
||||
panels:
|
||||
- title: "CPU Usage (%)"
|
||||
type: line-chart
|
||||
query:
|
||||
type: metrics
|
||||
promql: |
|
||||
rate(container_cpu_usage_seconds_total{
|
||||
namespace=~"agent-monitor.*",
|
||||
container="agent-monitor"
|
||||
}[5m]) * 100
|
||||
legend: "{{pod}}"
|
||||
span: 4
|
||||
|
||||
- title: "Memory Usage (MiB)"
|
||||
type: line-chart
|
||||
query:
|
||||
type: metrics
|
||||
promql: |
|
||||
container_memory_working_set_bytes{
|
||||
namespace=~"agent-monitor.*",
|
||||
container="agent-monitor"
|
||||
} / 1024 / 1024
|
||||
legend: "{{pod}}"
|
||||
span: 4
|
||||
|
||||
- title: "Pod Status"
|
||||
type: gauge
|
||||
query:
|
||||
type: metrics
|
||||
promql: |
|
||||
count by (phase) (
|
||||
kube_pod_status_phase{namespace=~"agent-monitor.*"}
|
||||
)
|
||||
span: 4
|
||||
|
||||
# ── Row 5: Database & Storage ──────────────────────────────────────────
|
||||
- name: "Database & Storage"
|
||||
panels:
|
||||
- title: "SQLite Query Duration (ms)"
|
||||
type: line-chart
|
||||
query:
|
||||
type: metrics
|
||||
promql: 'sqlite_query_duration_seconds{job="agent-monitor"} * 1000'
|
||||
span: 4
|
||||
|
||||
- title: "PV Usage (%)"
|
||||
type: gauge
|
||||
query:
|
||||
type: metrics
|
||||
promql: |
|
||||
(kubelet_volume_stats_used_bytes{namespace=~"agent-monitor.*"}
|
||||
/ kubelet_volume_stats_capacity_bytes{namespace=~"agent-monitor.*"}) * 100
|
||||
thresholds:
|
||||
- value: 70
|
||||
color: yellow
|
||||
- value: 90
|
||||
color: red
|
||||
span: 4
|
||||
|
||||
- title: "Network I/O (bytes/s)"
|
||||
type: line-chart
|
||||
query:
|
||||
type: metrics
|
||||
promql: |
|
||||
sum by (pod) (rate(container_network_receive_bytes_total{namespace=~"agent-monitor.*"}[5m]))
|
||||
sum by (pod) (rate(container_network_transmit_bytes_total{namespace=~"agent-monitor.*"}[5m]))
|
||||
span: 4
|
||||
|
||||
# ── Row 6: SLO Tracking ────────────────────────────────────────────────
|
||||
- name: "SLO Tracking"
|
||||
panels:
|
||||
- title: "Availability SLO (99.9% target)"
|
||||
type: gauge
|
||||
query:
|
||||
type: metrics
|
||||
promql: |
|
||||
(1 - sum(rate(http_requests_total{job="agent-monitor", status=~"5.."}[30d]))
|
||||
/ sum(rate(http_requests_total{job="agent-monitor"}[30d]))) * 100
|
||||
thresholds:
|
||||
- value: 99.9
|
||||
color: green
|
||||
- value: 99.5
|
||||
color: yellow
|
||||
- value: 99.0
|
||||
color: red
|
||||
span: 4
|
||||
|
||||
- title: "Latency SLO (P95 < 500ms)"
|
||||
type: gauge
|
||||
query:
|
||||
type: metrics
|
||||
promql: |
|
||||
histogram_quantile(0.95,
|
||||
sum(rate(http_request_duration_seconds_bucket{job="agent-monitor"}[30d])) by (le)
|
||||
) * 1000
|
||||
thresholds:
|
||||
- value: 300
|
||||
color: green
|
||||
- value: 500
|
||||
color: yellow
|
||||
- value: 1000
|
||||
color: red
|
||||
span: 4
|
||||
|
||||
- title: "Error Budget Remaining"
|
||||
type: gauge
|
||||
query:
|
||||
type: metrics
|
||||
promql: |
|
||||
(0.001 - sum(rate(http_requests_total{job="agent-monitor", status=~"5.."}[30d]))
|
||||
/ sum(rate(http_requests_total{job="agent-monitor"}[30d])))
|
||||
/ 0.001 * 100
|
||||
thresholds:
|
||||
- value: 50
|
||||
color: green
|
||||
- value: 25
|
||||
color: yellow
|
||||
- value: 0
|
||||
color: red
|
||||
span: 4
|
||||
@@ -0,0 +1,180 @@
|
||||
# ─────────────────────────────────────────────────────────────────────────────
|
||||
# Coralogix OpenTelemetry Collector – Helm Values
|
||||
#
|
||||
# Deploys the Coralogix OTel collector as a DaemonSet + Gateway for shipping
|
||||
# logs, metrics, and traces from the Agent Monitor cluster.
|
||||
#
|
||||
# Prerequisites:
|
||||
# 1. Add Coralogix Helm repo:
|
||||
# helm repo add coralogix https://cgx.jfrog.io/artifactory/coralogix-charts-virtual
|
||||
# helm repo update
|
||||
# 2. Create the API key secret:
|
||||
# kubectl create secret generic coralogix-keys \
|
||||
# --namespace agent-monitor \
|
||||
# --from-literal=PRIVATE_KEY=<YOUR_CORALOGIX_PRIVATE_KEY>
|
||||
#
|
||||
# Install:
|
||||
# helm install coralogix-otel coralogix/opentelemetry \
|
||||
# --namespace agent-monitor \
|
||||
# -f deployments/monitoring/coralogix/values.yaml
|
||||
# ─────────────────────────────────────────────────────────────────────────────
|
||||
|
||||
global:
|
||||
# Coralogix domain – set to your region's endpoint
|
||||
# Options: coralogix.com | eu2.coralogix.com | coralogix.in | coralogix.us |
|
||||
# cx498.coralogix.com | coralogix.eu | coralogix.sg
|
||||
domain: "coralogix.com"
|
||||
|
||||
# Reference the API key from the pre-created K8s secret
|
||||
clusterName: "agent-monitor"
|
||||
|
||||
# ── Secret reference ─────────────────────────────────────────────────────────
|
||||
secret:
|
||||
enabled: true
|
||||
name: "coralogix-keys"
|
||||
# Key in the secret containing the Coralogix Send-Your-Data API key
|
||||
privateKeySecretRef:
|
||||
key: "PRIVATE_KEY"
|
||||
|
||||
# ── Collector – DaemonSet mode (node-level collection) ───────────────────────
|
||||
opentelemetry-collector:
|
||||
mode: daemonset
|
||||
|
||||
presets:
|
||||
# Collect Kubernetes pod/container logs
|
||||
logsCollection:
|
||||
enabled: true
|
||||
includeCollectorLogs: false
|
||||
|
||||
# Enrich telemetry with Kubernetes metadata
|
||||
kubernetesAttributes:
|
||||
enabled: true
|
||||
extractAllPodLabels: true
|
||||
extractAllPodAnnotations: false
|
||||
|
||||
# Collect host-level metrics (CPU, memory, disk, network)
|
||||
hostMetrics:
|
||||
enabled: true
|
||||
|
||||
# Collect kubelet/cAdvisor metrics
|
||||
kubeletMetrics:
|
||||
enabled: true
|
||||
|
||||
config:
|
||||
receivers:
|
||||
# Scrape Prometheus metrics from agent-monitor pods
|
||||
prometheus:
|
||||
config:
|
||||
scrape_configs:
|
||||
- job_name: "agent-monitor"
|
||||
scrape_interval: 15s
|
||||
kubernetes_sd_configs:
|
||||
- role: pod
|
||||
namespaces:
|
||||
names:
|
||||
- agent-monitor
|
||||
- agent-monitor-staging
|
||||
- agent-monitor-production
|
||||
relabel_configs:
|
||||
- source_labels: [__meta_kubernetes_pod_annotation_prometheus_io_scrape]
|
||||
action: keep
|
||||
regex: "true"
|
||||
- source_labels: [__meta_kubernetes_pod_annotation_prometheus_io_path]
|
||||
action: replace
|
||||
target_label: __metrics_path__
|
||||
regex: (.+)
|
||||
- source_labels: [__address__, __meta_kubernetes_pod_annotation_prometheus_io_port]
|
||||
action: replace
|
||||
regex: ([^:]+)(?::\d+)?;(\d+)
|
||||
replacement: $$1:$$2
|
||||
target_label: __address__
|
||||
|
||||
# Receive OTLP from in-cluster services (gRPC + HTTP)
|
||||
otlp:
|
||||
protocols:
|
||||
grpc:
|
||||
endpoint: "0.0.0.0:4317"
|
||||
http:
|
||||
endpoint: "0.0.0.0:4318"
|
||||
|
||||
processors:
|
||||
# Batch telemetry for efficient export
|
||||
batch:
|
||||
send_batch_size: 1024
|
||||
send_batch_max_size: 2048
|
||||
timeout: 5s
|
||||
|
||||
# Enrich with resource attributes
|
||||
resource:
|
||||
attributes:
|
||||
- key: cx.application.name
|
||||
value: "agent-monitor"
|
||||
action: upsert
|
||||
- key: cx.subsystem.name
|
||||
from_attribute: k8s.container.name
|
||||
action: upsert
|
||||
- key: k8s.cluster.name
|
||||
value: "agent-monitor"
|
||||
action: upsert
|
||||
|
||||
# Memory limiter to prevent OOM
|
||||
memory_limiter:
|
||||
check_interval: 5s
|
||||
limit_percentage: 80
|
||||
spike_limit_percentage: 25
|
||||
|
||||
# Filter out noisy internal logs
|
||||
filter/drop-internal:
|
||||
logs:
|
||||
exclude:
|
||||
match_type: regexp
|
||||
bodies:
|
||||
- ".*kube-probe.*"
|
||||
- ".*healthz.*"
|
||||
|
||||
exporters:
|
||||
coralogix:
|
||||
domain: "${CORALOGIX_DOMAIN}"
|
||||
private_key: "${PRIVATE_KEY}"
|
||||
application_name: "agent-monitor"
|
||||
subsystem_name: "kubernetes"
|
||||
timeout: 30s
|
||||
|
||||
service:
|
||||
pipelines:
|
||||
logs:
|
||||
receivers: [otlp]
|
||||
processors: [memory_limiter, resource, filter/drop-internal, batch]
|
||||
exporters: [coralogix]
|
||||
metrics:
|
||||
receivers: [otlp, prometheus]
|
||||
processors: [memory_limiter, resource, batch]
|
||||
exporters: [coralogix]
|
||||
traces:
|
||||
receivers: [otlp]
|
||||
processors: [memory_limiter, resource, batch]
|
||||
exporters: [coralogix]
|
||||
|
||||
resources:
|
||||
requests:
|
||||
cpu: 100m
|
||||
memory: 256Mi
|
||||
limits:
|
||||
cpu: 500m
|
||||
memory: 512Mi
|
||||
|
||||
tolerations:
|
||||
- operator: Exists
|
||||
effect: NoSchedule
|
||||
|
||||
# ── Gateway mode (optional – for centralized export) ─────────────────────────
|
||||
opentelemetry-gateway:
|
||||
enabled: false
|
||||
replicaCount: 2
|
||||
resources:
|
||||
requests:
|
||||
cpu: 250m
|
||||
memory: 512Mi
|
||||
limits:
|
||||
cpu: "1"
|
||||
memory: "1Gi"
|
||||
@@ -0,0 +1,707 @@
|
||||
{
|
||||
"annotations": {
|
||||
"list": [
|
||||
{
|
||||
"builtIn": 1,
|
||||
"datasource": { "type": "grafana", "uid": "-- Grafana --" },
|
||||
"enable": true,
|
||||
"hide": true,
|
||||
"iconColor": "rgba(0, 211, 255, 1)",
|
||||
"name": "Annotations & Alerts",
|
||||
"type": "dashboard"
|
||||
},
|
||||
{
|
||||
"datasource": { "type": "prometheus", "uid": "prometheus" },
|
||||
"enable": true,
|
||||
"expr": "changes(kube_deployment_status_observed_generation{namespace=~\"$namespace\", deployment=~\"agent-monitor.*\"}[2m]) > 0",
|
||||
"iconColor": "#6ED0E0",
|
||||
"name": "Deployments",
|
||||
"titleFormat": "Deployment updated"
|
||||
}
|
||||
]
|
||||
},
|
||||
"description": "Comprehensive monitoring dashboard for Claude Code Agent Monitor",
|
||||
"editable": true,
|
||||
"fiscalYearStartMonth": 0,
|
||||
"graphTooltip": 1,
|
||||
"id": null,
|
||||
"links": [],
|
||||
"panels": [
|
||||
{
|
||||
"collapsed": false,
|
||||
"gridPos": { "h": 1, "w": 24, "x": 0, "y": 0 },
|
||||
"id": 100,
|
||||
"title": "Overview",
|
||||
"type": "row"
|
||||
},
|
||||
{
|
||||
"datasource": { "type": "prometheus", "uid": "prometheus" },
|
||||
"fieldConfig": {
|
||||
"defaults": {
|
||||
"color": { "mode": "thresholds" },
|
||||
"mappings": [
|
||||
{
|
||||
"options": {
|
||||
"0": { "color": "red", "text": "DOWN" },
|
||||
"1": { "color": "green", "text": "UP" }
|
||||
},
|
||||
"type": "value"
|
||||
}
|
||||
],
|
||||
"thresholds": {
|
||||
"steps": [
|
||||
{ "color": "red", "value": null },
|
||||
{ "color": "green", "value": 1 }
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"gridPos": { "h": 4, "w": 4, "x": 0, "y": 1 },
|
||||
"id": 1,
|
||||
"options": {
|
||||
"colorMode": "background",
|
||||
"graphMode": "none",
|
||||
"justifyMode": "center",
|
||||
"textMode": "value",
|
||||
"reduceOptions": { "calcs": ["lastNotNull"] }
|
||||
},
|
||||
"title": "Uptime Status",
|
||||
"type": "stat",
|
||||
"targets": [
|
||||
{
|
||||
"expr": "up{job=\"agent-monitor\", namespace=~\"$namespace\"}",
|
||||
"legendFormat": "{{instance}}",
|
||||
"refId": "A"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"datasource": { "type": "prometheus", "uid": "prometheus" },
|
||||
"fieldConfig": {
|
||||
"defaults": {
|
||||
"color": { "mode": "thresholds" },
|
||||
"unit": "dtdurations",
|
||||
"thresholds": { "steps": [{ "color": "green", "value": null }] }
|
||||
}
|
||||
},
|
||||
"gridPos": { "h": 4, "w": 4, "x": 4, "y": 1 },
|
||||
"id": 2,
|
||||
"options": {
|
||||
"colorMode": "value",
|
||||
"graphMode": "none",
|
||||
"justifyMode": "center",
|
||||
"textMode": "value",
|
||||
"reduceOptions": { "calcs": ["lastNotNull"] }
|
||||
},
|
||||
"title": "Uptime Duration",
|
||||
"type": "stat",
|
||||
"targets": [
|
||||
{
|
||||
"expr": "time() - process_start_time_seconds{job=\"agent-monitor\", namespace=~\"$namespace\"}",
|
||||
"legendFormat": "",
|
||||
"refId": "A"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"datasource": { "type": "prometheus", "uid": "prometheus" },
|
||||
"fieldConfig": {
|
||||
"defaults": {
|
||||
"color": { "mode": "thresholds" },
|
||||
"unit": "short",
|
||||
"thresholds": {
|
||||
"steps": [
|
||||
{ "color": "green", "value": null },
|
||||
{ "color": "yellow", "value": 2 },
|
||||
{ "color": "red", "value": 0 }
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"gridPos": { "h": 4, "w": 4, "x": 8, "y": 1 },
|
||||
"id": 3,
|
||||
"options": {
|
||||
"colorMode": "value",
|
||||
"graphMode": "area",
|
||||
"justifyMode": "center",
|
||||
"textMode": "value",
|
||||
"reduceOptions": { "calcs": ["lastNotNull"] }
|
||||
},
|
||||
"title": "Pod Count",
|
||||
"type": "stat",
|
||||
"targets": [
|
||||
{
|
||||
"expr": "count(kube_pod_status_ready{namespace=~\"$namespace\", condition=\"true\"} == 1)",
|
||||
"legendFormat": "Ready",
|
||||
"refId": "A"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"datasource": { "type": "prometheus", "uid": "prometheus" },
|
||||
"fieldConfig": {
|
||||
"defaults": {
|
||||
"color": { "mode": "thresholds" },
|
||||
"unit": "short",
|
||||
"thresholds": {
|
||||
"steps": [
|
||||
{ "color": "green", "value": null },
|
||||
{ "color": "yellow", "value": 1 },
|
||||
{ "color": "red", "value": 3 }
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"gridPos": { "h": 4, "w": 4, "x": 12, "y": 1 },
|
||||
"id": 4,
|
||||
"options": {
|
||||
"colorMode": "value",
|
||||
"graphMode": "area",
|
||||
"justifyMode": "center",
|
||||
"textMode": "value",
|
||||
"reduceOptions": { "calcs": ["lastNotNull"] }
|
||||
},
|
||||
"title": "Pod Restarts (1h)",
|
||||
"type": "stat",
|
||||
"targets": [
|
||||
{
|
||||
"expr": "sum(increase(kube_pod_container_status_restarts_total{namespace=~\"$namespace\", container=\"agent-monitor\"}[1h]))",
|
||||
"legendFormat": "",
|
||||
"refId": "A"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"datasource": { "type": "prometheus", "uid": "prometheus" },
|
||||
"fieldConfig": {
|
||||
"defaults": {
|
||||
"color": { "mode": "thresholds" },
|
||||
"unit": "short",
|
||||
"thresholds": { "steps": [{ "color": "blue", "value": null }] }
|
||||
}
|
||||
},
|
||||
"gridPos": { "h": 4, "w": 4, "x": 16, "y": 1 },
|
||||
"id": 5,
|
||||
"options": {
|
||||
"colorMode": "value",
|
||||
"graphMode": "area",
|
||||
"justifyMode": "center",
|
||||
"textMode": "value",
|
||||
"reduceOptions": { "calcs": ["lastNotNull"] }
|
||||
},
|
||||
"title": "Active WebSocket Connections",
|
||||
"type": "stat",
|
||||
"targets": [
|
||||
{
|
||||
"expr": "sum(websocket_connections_active{job=\"agent-monitor\", namespace=~\"$namespace\"})",
|
||||
"legendFormat": "",
|
||||
"refId": "A"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"datasource": { "type": "prometheus", "uid": "prometheus" },
|
||||
"fieldConfig": {
|
||||
"defaults": {
|
||||
"color": { "mode": "thresholds" },
|
||||
"unit": "decbytes",
|
||||
"thresholds": {
|
||||
"steps": [
|
||||
{ "color": "green", "value": null },
|
||||
{ "color": "yellow", "value": 5368709120 },
|
||||
{ "color": "red", "value": 8589934592 }
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"gridPos": { "h": 4, "w": 4, "x": 20, "y": 1 },
|
||||
"id": 6,
|
||||
"options": {
|
||||
"colorMode": "value",
|
||||
"graphMode": "area",
|
||||
"justifyMode": "center",
|
||||
"textMode": "value",
|
||||
"reduceOptions": { "calcs": ["lastNotNull"] }
|
||||
},
|
||||
"title": "SQLite DB Size",
|
||||
"type": "stat",
|
||||
"targets": [
|
||||
{
|
||||
"expr": "kubelet_volume_stats_used_bytes{namespace=~\"$namespace\", persistentvolumeclaim=~\"agent-monitor.*\"}",
|
||||
"legendFormat": "",
|
||||
"refId": "A"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"collapsed": false,
|
||||
"gridPos": { "h": 1, "w": 24, "x": 0, "y": 5 },
|
||||
"id": 101,
|
||||
"title": "HTTP Traffic",
|
||||
"type": "row"
|
||||
},
|
||||
{
|
||||
"datasource": { "type": "prometheus", "uid": "prometheus" },
|
||||
"fieldConfig": {
|
||||
"defaults": {
|
||||
"color": { "mode": "palette-classic" },
|
||||
"custom": {
|
||||
"axisLabel": "req/s",
|
||||
"drawStyle": "line",
|
||||
"fillOpacity": 15,
|
||||
"lineWidth": 2,
|
||||
"pointSize": 5,
|
||||
"spanNulls": false,
|
||||
"stacking": { "mode": "none" }
|
||||
},
|
||||
"unit": "reqps"
|
||||
},
|
||||
"overrides": [
|
||||
{
|
||||
"matcher": { "id": "byRegexp", "options": "5.." },
|
||||
"properties": [{ "id": "color", "value": { "fixedColor": "red", "mode": "fixed" } }]
|
||||
},
|
||||
{
|
||||
"matcher": { "id": "byRegexp", "options": "4.." },
|
||||
"properties": [{ "id": "color", "value": { "fixedColor": "yellow", "mode": "fixed" } }]
|
||||
},
|
||||
{
|
||||
"matcher": { "id": "byRegexp", "options": "2.." },
|
||||
"properties": [{ "id": "color", "value": { "fixedColor": "green", "mode": "fixed" } }]
|
||||
}
|
||||
]
|
||||
},
|
||||
"gridPos": { "h": 8, "w": 12, "x": 0, "y": 6 },
|
||||
"id": 10,
|
||||
"options": {
|
||||
"legend": { "calcs": ["mean", "max"], "displayMode": "table", "placement": "bottom" },
|
||||
"tooltip": { "mode": "multi", "sort": "desc" }
|
||||
},
|
||||
"title": "Request Rate (RPS)",
|
||||
"type": "timeseries",
|
||||
"targets": [
|
||||
{
|
||||
"expr": "sum(rate(http_requests_total{job=\"agent-monitor\", namespace=~\"$namespace\"}[5m])) by (status)",
|
||||
"legendFormat": "{{ status }}",
|
||||
"refId": "A"
|
||||
},
|
||||
{
|
||||
"expr": "sum(rate(http_requests_total{job=\"agent-monitor\", namespace=~\"$namespace\"}[5m]))",
|
||||
"legendFormat": "Total",
|
||||
"refId": "B"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"datasource": { "type": "prometheus", "uid": "prometheus" },
|
||||
"fieldConfig": {
|
||||
"defaults": {
|
||||
"color": { "mode": "palette-classic" },
|
||||
"custom": {
|
||||
"axisLabel": "%",
|
||||
"drawStyle": "line",
|
||||
"fillOpacity": 20,
|
||||
"lineWidth": 2,
|
||||
"thresholdsStyle": { "mode": "line" }
|
||||
},
|
||||
"max": 100,
|
||||
"min": 0,
|
||||
"unit": "percent",
|
||||
"thresholds": {
|
||||
"steps": [
|
||||
{ "color": "green", "value": null },
|
||||
{ "color": "yellow", "value": 2 },
|
||||
{ "color": "red", "value": 5 }
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"gridPos": { "h": 8, "w": 12, "x": 12, "y": 6 },
|
||||
"id": 11,
|
||||
"options": {
|
||||
"legend": { "calcs": ["mean", "max"], "displayMode": "table", "placement": "bottom" },
|
||||
"tooltip": { "mode": "multi" }
|
||||
},
|
||||
"title": "Error Rate (%)",
|
||||
"type": "timeseries",
|
||||
"targets": [
|
||||
{
|
||||
"expr": "100 * sum(rate(http_requests_total{job=\"agent-monitor\", namespace=~\"$namespace\", status=~\"5..\"}[5m])) / sum(rate(http_requests_total{job=\"agent-monitor\", namespace=~\"$namespace\"}[5m]))",
|
||||
"legendFormat": "5xx Error Rate",
|
||||
"refId": "A"
|
||||
},
|
||||
{
|
||||
"expr": "100 * sum(rate(http_requests_total{job=\"agent-monitor\", namespace=~\"$namespace\", status=~\"4..\"}[5m])) / sum(rate(http_requests_total{job=\"agent-monitor\", namespace=~\"$namespace\"}[5m]))",
|
||||
"legendFormat": "4xx Error Rate",
|
||||
"refId": "B"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"collapsed": false,
|
||||
"gridPos": { "h": 1, "w": 24, "x": 0, "y": 14 },
|
||||
"id": 102,
|
||||
"title": "Latency",
|
||||
"type": "row"
|
||||
},
|
||||
{
|
||||
"datasource": { "type": "prometheus", "uid": "prometheus" },
|
||||
"fieldConfig": {
|
||||
"defaults": {
|
||||
"color": { "mode": "palette-classic" },
|
||||
"custom": {
|
||||
"axisLabel": "seconds",
|
||||
"drawStyle": "line",
|
||||
"fillOpacity": 10,
|
||||
"lineWidth": 2,
|
||||
"spanNulls": false
|
||||
},
|
||||
"unit": "s",
|
||||
"thresholds": {
|
||||
"steps": [
|
||||
{ "color": "green", "value": null },
|
||||
{ "color": "yellow", "value": 0.5 },
|
||||
{ "color": "red", "value": 1 }
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"gridPos": { "h": 8, "w": 12, "x": 0, "y": 15 },
|
||||
"id": 20,
|
||||
"options": {
|
||||
"legend": { "calcs": ["mean", "max"], "displayMode": "table", "placement": "bottom" },
|
||||
"tooltip": { "mode": "multi", "sort": "desc" }
|
||||
},
|
||||
"title": "Response Latency (p50 / p90 / p99)",
|
||||
"type": "timeseries",
|
||||
"targets": [
|
||||
{
|
||||
"expr": "histogram_quantile(0.50, sum(rate(http_request_duration_seconds_bucket{job=\"agent-monitor\", namespace=~\"$namespace\"}[5m])) by (le))",
|
||||
"legendFormat": "p50",
|
||||
"refId": "A"
|
||||
},
|
||||
{
|
||||
"expr": "histogram_quantile(0.90, sum(rate(http_request_duration_seconds_bucket{job=\"agent-monitor\", namespace=~\"$namespace\"}[5m])) by (le))",
|
||||
"legendFormat": "p90",
|
||||
"refId": "B"
|
||||
},
|
||||
{
|
||||
"expr": "histogram_quantile(0.99, sum(rate(http_request_duration_seconds_bucket{job=\"agent-monitor\", namespace=~\"$namespace\"}[5m])) by (le))",
|
||||
"legendFormat": "p99",
|
||||
"refId": "C"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"datasource": { "type": "prometheus", "uid": "prometheus" },
|
||||
"fieldConfig": {
|
||||
"defaults": {
|
||||
"color": { "mode": "palette-classic" },
|
||||
"custom": {
|
||||
"drawStyle": "bars",
|
||||
"fillOpacity": 80,
|
||||
"lineWidth": 1,
|
||||
"stacking": { "mode": "normal" }
|
||||
},
|
||||
"unit": "reqps"
|
||||
}
|
||||
},
|
||||
"gridPos": { "h": 8, "w": 12, "x": 12, "y": 15 },
|
||||
"id": 21,
|
||||
"options": {
|
||||
"legend": { "calcs": ["mean"], "displayMode": "table", "placement": "bottom" },
|
||||
"tooltip": { "mode": "multi" }
|
||||
},
|
||||
"title": "Request Rate by Endpoint",
|
||||
"type": "timeseries",
|
||||
"targets": [
|
||||
{
|
||||
"expr": "sum(rate(http_requests_total{job=\"agent-monitor\", namespace=~\"$namespace\"}[5m])) by (handler)",
|
||||
"legendFormat": "{{ handler }}",
|
||||
"refId": "A"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"collapsed": false,
|
||||
"gridPos": { "h": 1, "w": 24, "x": 0, "y": 23 },
|
||||
"id": 103,
|
||||
"title": "WebSocket & Connections",
|
||||
"type": "row"
|
||||
},
|
||||
{
|
||||
"datasource": { "type": "prometheus", "uid": "prometheus" },
|
||||
"fieldConfig": {
|
||||
"defaults": {
|
||||
"color": { "mode": "palette-classic" },
|
||||
"custom": { "drawStyle": "line", "fillOpacity": 25, "lineWidth": 2, "spanNulls": false },
|
||||
"unit": "short"
|
||||
}
|
||||
},
|
||||
"gridPos": { "h": 8, "w": 12, "x": 0, "y": 24 },
|
||||
"id": 30,
|
||||
"options": {
|
||||
"legend": {
|
||||
"calcs": ["mean", "max", "last"],
|
||||
"displayMode": "table",
|
||||
"placement": "bottom"
|
||||
},
|
||||
"tooltip": { "mode": "multi" }
|
||||
},
|
||||
"title": "Active WebSocket Connections",
|
||||
"type": "timeseries",
|
||||
"targets": [
|
||||
{
|
||||
"expr": "websocket_connections_active{job=\"agent-monitor\", namespace=~\"$namespace\"}",
|
||||
"legendFormat": "{{ pod }}",
|
||||
"refId": "A"
|
||||
},
|
||||
{
|
||||
"expr": "sum(websocket_connections_active{job=\"agent-monitor\", namespace=~\"$namespace\"})",
|
||||
"legendFormat": "Total",
|
||||
"refId": "B"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"datasource": { "type": "prometheus", "uid": "prometheus" },
|
||||
"fieldConfig": {
|
||||
"defaults": {
|
||||
"color": { "mode": "palette-classic" },
|
||||
"custom": { "drawStyle": "line", "fillOpacity": 10, "lineWidth": 2 },
|
||||
"unit": "short"
|
||||
}
|
||||
},
|
||||
"gridPos": { "h": 8, "w": 12, "x": 12, "y": 24 },
|
||||
"id": 31,
|
||||
"options": {
|
||||
"legend": { "calcs": ["sum"], "displayMode": "table", "placement": "bottom" },
|
||||
"tooltip": { "mode": "multi" }
|
||||
},
|
||||
"title": "WebSocket Messages (rate/s)",
|
||||
"type": "timeseries",
|
||||
"targets": [
|
||||
{
|
||||
"expr": "sum(rate(websocket_messages_sent_total{job=\"agent-monitor\", namespace=~\"$namespace\"}[5m]))",
|
||||
"legendFormat": "Sent",
|
||||
"refId": "A"
|
||||
},
|
||||
{
|
||||
"expr": "sum(rate(websocket_messages_received_total{job=\"agent-monitor\", namespace=~\"$namespace\"}[5m]))",
|
||||
"legendFormat": "Received",
|
||||
"refId": "B"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"collapsed": false,
|
||||
"gridPos": { "h": 1, "w": 24, "x": 0, "y": 32 },
|
||||
"id": 104,
|
||||
"title": "Resource Usage",
|
||||
"type": "row"
|
||||
},
|
||||
{
|
||||
"datasource": { "type": "prometheus", "uid": "prometheus" },
|
||||
"fieldConfig": {
|
||||
"defaults": {
|
||||
"color": { "mode": "palette-classic" },
|
||||
"custom": {
|
||||
"drawStyle": "line",
|
||||
"fillOpacity": 20,
|
||||
"lineWidth": 2,
|
||||
"thresholdsStyle": { "mode": "line+area" }
|
||||
},
|
||||
"unit": "bytes",
|
||||
"thresholds": {
|
||||
"steps": [
|
||||
{ "color": "transparent", "value": null },
|
||||
{ "color": "red", "value": 536870912 }
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"gridPos": { "h": 8, "w": 12, "x": 0, "y": 33 },
|
||||
"id": 40,
|
||||
"options": {
|
||||
"legend": { "calcs": ["mean", "max"], "displayMode": "table", "placement": "bottom" },
|
||||
"tooltip": { "mode": "multi" }
|
||||
},
|
||||
"title": "Memory Usage",
|
||||
"type": "timeseries",
|
||||
"targets": [
|
||||
{
|
||||
"expr": "container_memory_working_set_bytes{namespace=~\"$namespace\", container=\"agent-monitor\"}",
|
||||
"legendFormat": "{{ pod }} (working set)",
|
||||
"refId": "A"
|
||||
},
|
||||
{
|
||||
"expr": "container_spec_memory_limit_bytes{namespace=~\"$namespace\", container=\"agent-monitor\"}",
|
||||
"legendFormat": "{{ pod }} (limit)",
|
||||
"refId": "B"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"datasource": { "type": "prometheus", "uid": "prometheus" },
|
||||
"fieldConfig": {
|
||||
"defaults": {
|
||||
"color": { "mode": "palette-classic" },
|
||||
"custom": {
|
||||
"drawStyle": "line",
|
||||
"fillOpacity": 20,
|
||||
"lineWidth": 2,
|
||||
"thresholdsStyle": { "mode": "line" }
|
||||
},
|
||||
"unit": "percentunit",
|
||||
"max": 1,
|
||||
"min": 0,
|
||||
"thresholds": {
|
||||
"steps": [
|
||||
{ "color": "green", "value": null },
|
||||
{ "color": "yellow", "value": 0.7 },
|
||||
{ "color": "red", "value": 0.9 }
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"gridPos": { "h": 8, "w": 12, "x": 12, "y": 33 },
|
||||
"id": 41,
|
||||
"options": {
|
||||
"legend": { "calcs": ["mean", "max"], "displayMode": "table", "placement": "bottom" },
|
||||
"tooltip": { "mode": "multi" }
|
||||
},
|
||||
"title": "CPU Usage",
|
||||
"type": "timeseries",
|
||||
"targets": [
|
||||
{
|
||||
"expr": "rate(container_cpu_usage_seconds_total{namespace=~\"$namespace\", container=\"agent-monitor\"}[5m])",
|
||||
"legendFormat": "{{ pod }}",
|
||||
"refId": "A"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"collapsed": false,
|
||||
"gridPos": { "h": 1, "w": 24, "x": 0, "y": 41 },
|
||||
"id": 105,
|
||||
"title": "Storage",
|
||||
"type": "row"
|
||||
},
|
||||
{
|
||||
"datasource": { "type": "prometheus", "uid": "prometheus" },
|
||||
"fieldConfig": {
|
||||
"defaults": {
|
||||
"color": { "mode": "palette-classic" },
|
||||
"custom": {
|
||||
"drawStyle": "line",
|
||||
"fillOpacity": 20,
|
||||
"lineWidth": 2,
|
||||
"thresholdsStyle": { "mode": "line+area" }
|
||||
},
|
||||
"unit": "decbytes",
|
||||
"thresholds": {
|
||||
"steps": [
|
||||
{ "color": "transparent", "value": null },
|
||||
{ "color": "yellow", "value": 8589934592 },
|
||||
{ "color": "red", "value": 9663676416 }
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"gridPos": { "h": 8, "w": 12, "x": 0, "y": 42 },
|
||||
"id": 50,
|
||||
"options": {
|
||||
"legend": { "calcs": ["last", "max"], "displayMode": "table", "placement": "bottom" },
|
||||
"tooltip": { "mode": "multi" }
|
||||
},
|
||||
"title": "SQLite DB Size (PV Usage)",
|
||||
"type": "timeseries",
|
||||
"targets": [
|
||||
{
|
||||
"expr": "kubelet_volume_stats_used_bytes{namespace=~\"$namespace\", persistentvolumeclaim=~\"agent-monitor.*\"}",
|
||||
"legendFormat": "Used ({{ persistentvolumeclaim }})",
|
||||
"refId": "A"
|
||||
},
|
||||
{
|
||||
"expr": "kubelet_volume_stats_capacity_bytes{namespace=~\"$namespace\", persistentvolumeclaim=~\"agent-monitor.*\"}",
|
||||
"legendFormat": "Capacity ({{ persistentvolumeclaim }})",
|
||||
"refId": "B"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"datasource": { "type": "prometheus", "uid": "prometheus" },
|
||||
"fieldConfig": {
|
||||
"defaults": {
|
||||
"color": { "mode": "thresholds" },
|
||||
"max": 100,
|
||||
"min": 0,
|
||||
"unit": "percent",
|
||||
"thresholds": {
|
||||
"steps": [
|
||||
{ "color": "green", "value": null },
|
||||
{ "color": "yellow", "value": 75 },
|
||||
{ "color": "red", "value": 90 }
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"gridPos": { "h": 8, "w": 12, "x": 12, "y": 42 },
|
||||
"id": 51,
|
||||
"options": {
|
||||
"orientation": "horizontal",
|
||||
"reduceOptions": { "calcs": ["lastNotNull"] },
|
||||
"showThresholdLabels": false,
|
||||
"showThresholdMarkers": true
|
||||
},
|
||||
"title": "PV Usage (%)",
|
||||
"type": "gauge",
|
||||
"targets": [
|
||||
{
|
||||
"expr": "100 * kubelet_volume_stats_used_bytes{namespace=~\"$namespace\", persistentvolumeclaim=~\"agent-monitor.*\"} / kubelet_volume_stats_capacity_bytes{namespace=~\"$namespace\", persistentvolumeclaim=~\"agent-monitor.*\"}",
|
||||
"legendFormat": "{{ persistentvolumeclaim }}",
|
||||
"refId": "A"
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"refresh": "30s",
|
||||
"schemaVersion": 39,
|
||||
"tags": ["agent-monitor", "nodejs", "websocket"],
|
||||
"templating": {
|
||||
"list": [
|
||||
{
|
||||
"current": { "selected": false, "text": "Prometheus", "value": "prometheus" },
|
||||
"hide": 0,
|
||||
"includeAll": false,
|
||||
"label": "Data Source",
|
||||
"name": "datasource",
|
||||
"options": [],
|
||||
"query": "prometheus",
|
||||
"type": "datasource"
|
||||
},
|
||||
{
|
||||
"current": {},
|
||||
"datasource": { "type": "prometheus", "uid": "${datasource}" },
|
||||
"definition": "label_values(up{job=\"agent-monitor\"}, namespace)",
|
||||
"hide": 0,
|
||||
"includeAll": true,
|
||||
"label": "Namespace",
|
||||
"multi": false,
|
||||
"name": "namespace",
|
||||
"query": { "qryType": 1, "query": "label_values(up{job=\"agent-monitor\"}, namespace)" },
|
||||
"refresh": 2,
|
||||
"type": "query"
|
||||
}
|
||||
]
|
||||
},
|
||||
"time": { "from": "now-6h", "to": "now" },
|
||||
"timepicker": {
|
||||
"refresh_intervals": ["10s", "30s", "1m", "5m", "15m"],
|
||||
"time_options": ["5m", "15m", "1h", "6h", "12h", "24h", "2d", "7d", "30d"]
|
||||
},
|
||||
"timezone": "browser",
|
||||
"title": "Claude Code Agent Monitor",
|
||||
"uid": "agent-monitor-main",
|
||||
"version": 1
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
# ─────────────────────────────────────────────────────────────────────────────
|
||||
# Grafana datasource provisioning for Claude Code Agent Monitor
|
||||
# ─────────────────────────────────────────────────────────────────────────────
|
||||
apiVersion: 1
|
||||
|
||||
# Prevent users from deleting provisioned data sources in the UI
|
||||
deleteDatasources:
|
||||
- name: Prometheus
|
||||
orgId: 1
|
||||
|
||||
datasources:
|
||||
# ── Primary Prometheus datasource ─────────────────────────────────────────
|
||||
- name: Prometheus
|
||||
type: prometheus
|
||||
uid: prometheus
|
||||
access: proxy
|
||||
url: http://prometheus-server:9090
|
||||
isDefault: true
|
||||
editable: false
|
||||
jsonData:
|
||||
# Scrape interval matches prometheus.yaml global setting
|
||||
timeInterval: "15s"
|
||||
# Query timeout
|
||||
queryTimeout: "30s"
|
||||
# HTTP method for queries
|
||||
httpMethod: POST
|
||||
# Manage alerts via Prometheus
|
||||
manageAlerts: true
|
||||
# Alerting rule settings
|
||||
prometheusType: Prometheus
|
||||
prometheusVersion: ">=2.45.0"
|
||||
# Incremental querying for better performance
|
||||
incrementalQuerying: true
|
||||
incrementalQueryOverlapWindow: "10m"
|
||||
# Exemplar trace support (uncomment if using tracing)
|
||||
# exemplarTraceIdDestinations:
|
||||
# - name: traceId
|
||||
# datasourceUid: tempo
|
||||
version: 1
|
||||
|
||||
# ── Alertmanager datasource ───────────────────────────────────────────────
|
||||
- name: Alertmanager
|
||||
type: alertmanager
|
||||
uid: alertmanager
|
||||
access: proxy
|
||||
url: http://alertmanager:9093
|
||||
editable: false
|
||||
jsonData:
|
||||
implementation: prometheus
|
||||
handleGrafanaManagedAlerts: false
|
||||
version: 1
|
||||
@@ -0,0 +1,226 @@
|
||||
# ─────────────────────────────────────────────────────────────────────────────
|
||||
# Prometheus configuration for Claude Code Agent Monitor
|
||||
# ─────────────────────────────────────────────────────────────────────────────
|
||||
global:
|
||||
scrape_interval: 15s
|
||||
scrape_timeout: 10s
|
||||
evaluation_interval: 15s
|
||||
|
||||
external_labels:
|
||||
cluster: "${CLUSTER_NAME:agent-monitor}"
|
||||
environment: "${ENVIRONMENT:production}"
|
||||
|
||||
# ── Rule files ──────────────────────────────────────────────────────────────
|
||||
rule_files:
|
||||
- /etc/prometheus/rules/*.rules.yaml
|
||||
|
||||
# ── Alertmanager ────────────────────────────────────────────────────────────
|
||||
alerting:
|
||||
alertmanagers:
|
||||
- static_configs:
|
||||
- targets:
|
||||
- alertmanager:9093
|
||||
scheme: http
|
||||
timeout: 10s
|
||||
api_version: v2
|
||||
|
||||
# ── Scrape configs ──────────────────────────────────────────────────────────
|
||||
scrape_configs:
|
||||
# ── Agent Monitor application ─────────────────────────────────────────────
|
||||
- job_name: "agent-monitor"
|
||||
metrics_path: /api/health
|
||||
scrape_interval: 15s
|
||||
scrape_timeout: 5s
|
||||
scheme: http
|
||||
|
||||
# Static target for standalone deployments
|
||||
static_configs:
|
||||
- targets:
|
||||
- "agent-monitor:4820"
|
||||
labels:
|
||||
app: agent-monitor
|
||||
component: server
|
||||
|
||||
# Relabeling to add standard labels
|
||||
relabel_configs:
|
||||
- source_labels: [__address__]
|
||||
target_label: instance
|
||||
- target_label: __metrics_path__
|
||||
replacement: /api/health
|
||||
|
||||
metric_relabel_configs:
|
||||
- source_labels: [__name__]
|
||||
regex: "go_.*"
|
||||
action: drop
|
||||
|
||||
# ── Agent Monitor MCP sidecar ─────────────────────────────────────────────
|
||||
- job_name: "agent-monitor-mcp"
|
||||
scrape_interval: 30s
|
||||
static_configs:
|
||||
- targets:
|
||||
- "agent-monitor-mcp:8819"
|
||||
labels:
|
||||
app: agent-monitor
|
||||
component: mcp
|
||||
|
||||
# ── Kubernetes service discovery (pods) ───────────────────────────────────
|
||||
- job_name: "kubernetes-pods"
|
||||
scrape_interval: 15s
|
||||
|
||||
kubernetes_sd_configs:
|
||||
- role: pod
|
||||
namespaces:
|
||||
own_namespace: false
|
||||
names:
|
||||
- agent-monitor
|
||||
- agent-monitor-dev
|
||||
- agent-monitor-staging
|
||||
- agent-monitor-production
|
||||
|
||||
relabel_configs:
|
||||
# Only scrape pods with annotation prometheus.io/scrape=true
|
||||
- source_labels: [__meta_kubernetes_pod_annotation_prometheus_io_scrape]
|
||||
action: keep
|
||||
regex: true
|
||||
|
||||
# Use custom metrics path if annotated
|
||||
- source_labels: [__meta_kubernetes_pod_annotation_prometheus_io_path]
|
||||
action: replace
|
||||
target_label: __metrics_path__
|
||||
regex: (.+)
|
||||
|
||||
# Use custom port if annotated
|
||||
- source_labels: [__address__, __meta_kubernetes_pod_annotation_prometheus_io_port]
|
||||
action: replace
|
||||
regex: ([^:]+)(?::\d+)?;(\d+)
|
||||
replacement: $1:$2
|
||||
target_label: __address__
|
||||
|
||||
# Use custom scheme if annotated
|
||||
- source_labels: [__meta_kubernetes_pod_annotation_prometheus_io_scheme]
|
||||
action: replace
|
||||
target_label: __scheme__
|
||||
regex: (.+)
|
||||
|
||||
# Map pod labels to Prometheus labels
|
||||
- action: labelmap
|
||||
regex: __meta_kubernetes_pod_label_(.+)
|
||||
|
||||
# Add namespace label
|
||||
- source_labels: [__meta_kubernetes_namespace]
|
||||
action: replace
|
||||
target_label: namespace
|
||||
|
||||
# Add pod name label
|
||||
- source_labels: [__meta_kubernetes_pod_name]
|
||||
action: replace
|
||||
target_label: pod
|
||||
|
||||
# Add node name label
|
||||
- source_labels: [__meta_kubernetes_pod_node_name]
|
||||
action: replace
|
||||
target_label: node
|
||||
|
||||
# ── Kubernetes service discovery (services) ──────────────────────────────
|
||||
- job_name: "kubernetes-services"
|
||||
scrape_interval: 15s
|
||||
|
||||
kubernetes_sd_configs:
|
||||
- role: service
|
||||
namespaces:
|
||||
names:
|
||||
- agent-monitor
|
||||
- agent-monitor-dev
|
||||
- agent-monitor-staging
|
||||
- agent-monitor-production
|
||||
|
||||
relabel_configs:
|
||||
- source_labels: [__meta_kubernetes_service_annotation_prometheus_io_scrape]
|
||||
action: keep
|
||||
regex: true
|
||||
|
||||
- source_labels: [__meta_kubernetes_service_annotation_prometheus_io_path]
|
||||
action: replace
|
||||
target_label: __metrics_path__
|
||||
regex: (.+)
|
||||
|
||||
- source_labels: [__address__, __meta_kubernetes_service_annotation_prometheus_io_port]
|
||||
action: replace
|
||||
regex: ([^:]+)(?::\d+)?;(\d+)
|
||||
replacement: $1:$2
|
||||
target_label: __address__
|
||||
|
||||
- action: labelmap
|
||||
regex: __meta_kubernetes_service_label_(.+)
|
||||
|
||||
- source_labels: [__meta_kubernetes_namespace]
|
||||
action: replace
|
||||
target_label: namespace
|
||||
|
||||
- source_labels: [__meta_kubernetes_service_name]
|
||||
action: replace
|
||||
target_label: service
|
||||
|
||||
# ── Kubernetes nodes ──────────────────────────────────────────────────────
|
||||
- job_name: "kubernetes-nodes"
|
||||
scrape_interval: 30s
|
||||
scheme: https
|
||||
|
||||
kubernetes_sd_configs:
|
||||
- role: node
|
||||
|
||||
tls_config:
|
||||
ca_file: /var/run/secrets/kubernetes.io/serviceaccount/ca.crt
|
||||
insecure_skip_verify: false
|
||||
|
||||
bearer_token_file: /var/run/secrets/kubernetes.io/serviceaccount/token
|
||||
|
||||
relabel_configs:
|
||||
- action: labelmap
|
||||
regex: __meta_kubernetes_node_label_(.+)
|
||||
|
||||
- target_label: __address__
|
||||
replacement: kubernetes.default.svc:443
|
||||
|
||||
- source_labels: [__meta_kubernetes_node_name]
|
||||
regex: (.+)
|
||||
target_label: __metrics_path__
|
||||
replacement: /api/v1/nodes/$1/proxy/metrics
|
||||
|
||||
# ── Kubernetes cadvisor (container metrics) ───────────────────────────────
|
||||
- job_name: "kubernetes-cadvisor"
|
||||
scrape_interval: 15s
|
||||
scheme: https
|
||||
|
||||
kubernetes_sd_configs:
|
||||
- role: node
|
||||
|
||||
tls_config:
|
||||
ca_file: /var/run/secrets/kubernetes.io/serviceaccount/ca.crt
|
||||
insecure_skip_verify: false
|
||||
|
||||
bearer_token_file: /var/run/secrets/kubernetes.io/serviceaccount/token
|
||||
|
||||
relabel_configs:
|
||||
- action: labelmap
|
||||
regex: __meta_kubernetes_node_label_(.+)
|
||||
|
||||
- target_label: __address__
|
||||
replacement: kubernetes.default.svc:443
|
||||
|
||||
- source_labels: [__meta_kubernetes_node_name]
|
||||
regex: (.+)
|
||||
target_label: __metrics_path__
|
||||
replacement: /api/v1/nodes/$1/proxy/metrics/cadvisor
|
||||
|
||||
metric_relabel_configs:
|
||||
# Keep only container metrics for agent-monitor pods
|
||||
- source_labels: [container]
|
||||
regex: "agent-monitor.*"
|
||||
action: keep
|
||||
|
||||
# ── Prometheus self-monitoring ────────────────────────────────────────────
|
||||
- job_name: "prometheus"
|
||||
static_configs:
|
||||
- targets:
|
||||
- "localhost:9090"
|
||||
@@ -0,0 +1,310 @@
|
||||
# ─────────────────────────────────────────────────────────────────────────────
|
||||
# Prometheus alerting rules for Claude Code Agent Monitor
|
||||
# ─────────────────────────────────────────────────────────────────────────────
|
||||
apiVersion: monitoring.coreos.com/v1
|
||||
kind: PrometheusRule
|
||||
metadata:
|
||||
name: agent-monitor-alerts
|
||||
namespace: agent-monitor
|
||||
labels:
|
||||
app.kubernetes.io/name: agent-monitor
|
||||
app.kubernetes.io/component: monitoring
|
||||
prometheus: kube-prometheus
|
||||
role: alert-rules
|
||||
spec:
|
||||
groups:
|
||||
# ── Availability alerts ─────────────────────────────────────────────────
|
||||
- name: agent-monitor.availability
|
||||
rules:
|
||||
- alert: AgentMonitorDown
|
||||
expr: up{job="agent-monitor"} == 0
|
||||
for: 2m
|
||||
labels:
|
||||
severity: critical
|
||||
service: agent-monitor
|
||||
team: platform
|
||||
annotations:
|
||||
summary: "Agent Monitor is down"
|
||||
description: >-
|
||||
The Agent Monitor instance {{ $labels.instance }} has been
|
||||
unreachable for more than 2 minutes. Immediate investigation
|
||||
required.
|
||||
runbook_url: "https://wiki.example.com/runbooks/agent-monitor-down"
|
||||
dashboard_url: "https://grafana.example.com/d/agent-monitor"
|
||||
|
||||
- alert: AgentMonitorHighAvailabilityDegraded
|
||||
expr: |
|
||||
count(up{job="agent-monitor"} == 1)
|
||||
< count(up{job="agent-monitor"})
|
||||
for: 5m
|
||||
labels:
|
||||
severity: warning
|
||||
service: agent-monitor
|
||||
annotations:
|
||||
summary: "Agent Monitor partial outage"
|
||||
description: >-
|
||||
Not all Agent Monitor replicas are healthy. Only
|
||||
{{ $value }} of expected replicas are up.
|
||||
|
||||
# ── Error rate alerts ───────────────────────────────────────────────────
|
||||
- name: agent-monitor.errors
|
||||
rules:
|
||||
- alert: HighErrorRate
|
||||
expr: |
|
||||
(
|
||||
sum(rate(http_requests_total{job="agent-monitor", status=~"5.."}[5m]))
|
||||
/
|
||||
sum(rate(http_requests_total{job="agent-monitor"}[5m]))
|
||||
) > 0.05
|
||||
for: 5m
|
||||
labels:
|
||||
severity: warning
|
||||
service: agent-monitor
|
||||
annotations:
|
||||
summary: "High HTTP error rate (>5%)"
|
||||
description: >-
|
||||
The Agent Monitor is returning 5xx errors at a rate of
|
||||
{{ $value | humanizePercentage }} over the last 5 minutes.
|
||||
dashboard_url: "https://grafana.example.com/d/agent-monitor?tab=errors"
|
||||
|
||||
- alert: CriticalErrorRate
|
||||
expr: |
|
||||
(
|
||||
sum(rate(http_requests_total{job="agent-monitor", status=~"5.."}[5m]))
|
||||
/
|
||||
sum(rate(http_requests_total{job="agent-monitor"}[5m]))
|
||||
) > 0.25
|
||||
for: 2m
|
||||
labels:
|
||||
severity: critical
|
||||
service: agent-monitor
|
||||
annotations:
|
||||
summary: "Critical HTTP error rate (>25%)"
|
||||
description: >-
|
||||
The Agent Monitor is returning 5xx errors at a rate of
|
||||
{{ $value | humanizePercentage }}. Service may be severely degraded.
|
||||
|
||||
# ── Latency alerts ──────────────────────────────────────────────────────
|
||||
- name: agent-monitor.latency
|
||||
rules:
|
||||
- alert: HighLatency
|
||||
expr: |
|
||||
histogram_quantile(0.99,
|
||||
sum(rate(http_request_duration_seconds_bucket{job="agent-monitor"}[5m])) by (le)
|
||||
) > 1
|
||||
for: 5m
|
||||
labels:
|
||||
severity: warning
|
||||
service: agent-monitor
|
||||
annotations:
|
||||
summary: "High p99 response latency (>1s)"
|
||||
description: >-
|
||||
The 99th percentile response time for Agent Monitor is
|
||||
{{ $value | humanizeDuration }}. Users may experience slow page loads.
|
||||
|
||||
- alert: HighMedianLatency
|
||||
expr: |
|
||||
histogram_quantile(0.50,
|
||||
sum(rate(http_request_duration_seconds_bucket{job="agent-monitor"}[5m])) by (le)
|
||||
) > 0.5
|
||||
for: 5m
|
||||
labels:
|
||||
severity: warning
|
||||
service: agent-monitor
|
||||
annotations:
|
||||
summary: "High median response latency (>500ms)"
|
||||
description: >-
|
||||
The median response time is {{ $value | humanizeDuration }}.
|
||||
This affects the majority of requests.
|
||||
|
||||
# ── Resource alerts ─────────────────────────────────────────────────────
|
||||
- name: agent-monitor.resources
|
||||
rules:
|
||||
- alert: HighMemoryUsage
|
||||
expr: |
|
||||
(
|
||||
container_memory_working_set_bytes{
|
||||
namespace=~"agent-monitor.*",
|
||||
container="agent-monitor"
|
||||
}
|
||||
/
|
||||
container_spec_memory_limit_bytes{
|
||||
namespace=~"agent-monitor.*",
|
||||
container="agent-monitor"
|
||||
}
|
||||
) > 0.90
|
||||
for: 10m
|
||||
labels:
|
||||
severity: warning
|
||||
service: agent-monitor
|
||||
annotations:
|
||||
summary: "High memory usage (>90% of limit)"
|
||||
description: >-
|
||||
Pod {{ $labels.pod }} is using {{ $value | humanizePercentage }}
|
||||
of its memory limit. OOMKill risk is elevated.
|
||||
|
||||
- alert: HighCPUUsage
|
||||
expr: |
|
||||
(
|
||||
rate(container_cpu_usage_seconds_total{
|
||||
namespace=~"agent-monitor.*",
|
||||
container="agent-monitor"
|
||||
}[5m])
|
||||
/
|
||||
container_spec_cpu_quota{
|
||||
namespace=~"agent-monitor.*",
|
||||
container="agent-monitor"
|
||||
}
|
||||
* 100000
|
||||
) > 0.85
|
||||
for: 10m
|
||||
labels:
|
||||
severity: warning
|
||||
service: agent-monitor
|
||||
annotations:
|
||||
summary: "High CPU usage (>85% of limit)"
|
||||
description: >-
|
||||
Pod {{ $labels.pod }} is using {{ $value | humanizePercentage }}
|
||||
of its CPU limit. Consider scaling up.
|
||||
|
||||
- alert: PersistentVolumeNearlyFull
|
||||
expr: |
|
||||
(
|
||||
kubelet_volume_stats_used_bytes{
|
||||
namespace=~"agent-monitor.*",
|
||||
persistentvolumeclaim=~"agent-monitor.*"
|
||||
}
|
||||
/
|
||||
kubelet_volume_stats_capacity_bytes{
|
||||
namespace=~"agent-monitor.*",
|
||||
persistentvolumeclaim=~"agent-monitor.*"
|
||||
}
|
||||
) > 0.85
|
||||
for: 15m
|
||||
labels:
|
||||
severity: warning
|
||||
service: agent-monitor
|
||||
annotations:
|
||||
summary: "Persistent volume nearly full (>85%)"
|
||||
description: >-
|
||||
PVC {{ $labels.persistentvolumeclaim }} in namespace
|
||||
{{ $labels.namespace }} is {{ $value | humanizePercentage }}
|
||||
full. SQLite writes may fail when volume is exhausted.
|
||||
|
||||
- alert: PersistentVolumeCriticallyFull
|
||||
expr: |
|
||||
(
|
||||
kubelet_volume_stats_used_bytes{
|
||||
namespace=~"agent-monitor.*",
|
||||
persistentvolumeclaim=~"agent-monitor.*"
|
||||
}
|
||||
/
|
||||
kubelet_volume_stats_capacity_bytes{
|
||||
namespace=~"agent-monitor.*",
|
||||
persistentvolumeclaim=~"agent-monitor.*"
|
||||
}
|
||||
) > 0.95
|
||||
for: 5m
|
||||
labels:
|
||||
severity: critical
|
||||
service: agent-monitor
|
||||
annotations:
|
||||
summary: "Persistent volume critically full (>95%)"
|
||||
description: >-
|
||||
PVC {{ $labels.persistentvolumeclaim }} is at
|
||||
{{ $value | humanizePercentage }} capacity.
|
||||
Immediate action required to prevent data loss.
|
||||
|
||||
# ── WebSocket alerts ────────────────────────────────────────────────────
|
||||
- name: agent-monitor.websocket
|
||||
rules:
|
||||
- alert: WebSocketConnectionsDrop
|
||||
expr: |
|
||||
(
|
||||
max_over_time(websocket_connections_active{job="agent-monitor"}[10m])
|
||||
- websocket_connections_active{job="agent-monitor"}
|
||||
)
|
||||
/
|
||||
max_over_time(websocket_connections_active{job="agent-monitor"}[10m])
|
||||
> 0.50
|
||||
for: 5m
|
||||
labels:
|
||||
severity: warning
|
||||
service: agent-monitor
|
||||
annotations:
|
||||
summary: "WebSocket connections dropped >50%"
|
||||
description: >-
|
||||
Active WebSocket connections have dropped by more than 50%
|
||||
in the last 5 minutes (from
|
||||
{{ with printf `max_over_time(websocket_connections_active{instance="%s"}[10m])` .Labels.instance | query }}{{ . | first | value }}{{ end }}
|
||||
to {{ $value }}). This may indicate connectivity issues.
|
||||
|
||||
- alert: NoWebSocketConnections
|
||||
expr: |
|
||||
websocket_connections_active{job="agent-monitor"} == 0
|
||||
and on() hour() >= 8 <= 20
|
||||
for: 15m
|
||||
labels:
|
||||
severity: info
|
||||
service: agent-monitor
|
||||
annotations:
|
||||
summary: "No active WebSocket connections during business hours"
|
||||
description: >-
|
||||
There are no active WebSocket connections to Agent Monitor
|
||||
during expected business hours.
|
||||
|
||||
# ── Pod stability alerts ────────────────────────────────────────────────
|
||||
- name: agent-monitor.stability
|
||||
rules:
|
||||
- alert: PodRestarting
|
||||
expr: |
|
||||
increase(
|
||||
kube_pod_container_status_restarts_total{
|
||||
namespace=~"agent-monitor.*",
|
||||
container="agent-monitor"
|
||||
}[15m]
|
||||
) > 3
|
||||
for: 0m
|
||||
labels:
|
||||
severity: critical
|
||||
service: agent-monitor
|
||||
annotations:
|
||||
summary: "Pod restarting frequently (>3 in 15m)"
|
||||
description: >-
|
||||
Pod {{ $labels.pod }} in namespace {{ $labels.namespace }}
|
||||
has restarted {{ $value }} times in the last 15 minutes.
|
||||
Check logs: kubectl logs {{ $labels.pod }} -n {{ $labels.namespace }} --previous
|
||||
|
||||
- alert: PodNotReady
|
||||
expr: |
|
||||
kube_pod_status_ready{
|
||||
namespace=~"agent-monitor.*",
|
||||
condition="true"
|
||||
} == 0
|
||||
for: 5m
|
||||
labels:
|
||||
severity: warning
|
||||
service: agent-monitor
|
||||
annotations:
|
||||
summary: "Pod not ready for >5 minutes"
|
||||
description: >-
|
||||
Pod {{ $labels.pod }} has been in a not-ready state for
|
||||
more than 5 minutes.
|
||||
|
||||
- alert: DeploymentReplicasMismatch
|
||||
expr: |
|
||||
kube_deployment_spec_replicas{namespace=~"agent-monitor.*"}
|
||||
!=
|
||||
kube_deployment_status_ready_replicas{namespace=~"agent-monitor.*"}
|
||||
for: 10m
|
||||
labels:
|
||||
severity: warning
|
||||
service: agent-monitor
|
||||
annotations:
|
||||
summary: "Deployment replicas mismatch"
|
||||
description: >-
|
||||
Deployment {{ $labels.deployment }} has
|
||||
{{ with printf `kube_deployment_status_ready_replicas{deployment="%s",namespace="%s"}` .Labels.deployment .Labels.namespace | query }}{{ . | first | value }}{{ end }}
|
||||
ready replicas but
|
||||
{{ with printf `kube_deployment_spec_replicas{deployment="%s",namespace="%s"}` .Labels.deployment .Labels.namespace | query }}{{ . | first | value }}{{ end }}
|
||||
desired.
|
||||
Reference in New Issue
Block a user