Observability
Observability¶
This guide covers monitoring and observability for self-hosted Huitzo deployments, including Prometheus metrics, logging, and alerting.
Built-in Metrics Endpoint¶
Huitzo exposes metrics in Prometheus format at /metrics:
curl http://localhost:8080/metrics
Example output:
# HELP huitzo_command_duration_seconds Command execution duration
# TYPE huitzo_command_duration_seconds histogram
huitzo_command_duration_seconds_bucket{namespace="analytics",command="analyze",le="0.1"} 12
huitzo_command_duration_seconds_bucket{namespace="analytics",command="analyze",le="0.5"} 45
huitzo_command_duration_seconds_bucket{namespace="analytics",command="analyze",le="1.0"} 78
...
# HELP huitzo_command_total Total commands executed
# TYPE huitzo_command_total counter
huitzo_command_total{namespace="analytics",command="analyze",status="success"} 156
huitzo_command_total{namespace="analytics",command="analyze",status="error"} 3
Key Metrics¶
Command Execution¶
| Metric | Type | Labels | Description |
|---|---|---|---|
huitzo_command_duration_seconds |
histogram | namespace, command | Command execution duration |
huitzo_command_total |
counter | namespace, command, status | Total commands executed |
huitzo_active_tasks |
gauge | queue | Currently running tasks per queue |
huitzo_queued_tasks |
gauge | queue | Tasks waiting in queue |
Storage Operations¶
| Metric | Type | Labels | Description |
|---|---|---|---|
huitzo_storage_operations_total |
counter | operation, status | Storage operations (save, get, delete) |
huitzo_storage_operation_duration_seconds |
histogram | operation | Storage operation latency |
huitzo_storage_bytes_total |
counter | operation | Bytes read/written |
System Health¶
| Metric | Type | Labels | Description |
|---|---|---|---|
huitzo_http_requests_total |
counter | method, endpoint, status | HTTP request count |
huitzo_http_request_duration_seconds |
histogram | method, endpoint | HTTP request latency |
huitzo_worker_status |
gauge | worker_id | Worker health (1=healthy, 0=unhealthy) |
huitzo_database_connections |
gauge | pool | Active database connections |
LLM Usage¶
| Metric | Type | Labels | Description |
|---|---|---|---|
huitzo_llm_requests_total |
counter | provider, model, status | LLM API calls |
huitzo_llm_tokens_total |
counter | provider, model, type | Tokens consumed (input/output) |
huitzo_llm_request_duration_seconds |
histogram | provider, model | LLM request latency |
Prometheus Configuration¶
Basic Setup¶
Add Prometheus to your docker-compose.yml:
# docker-compose.yml
services:
# ... existing services ...
prometheus:
image: prom/prometheus:latest
ports:
- "9090:9090"
volumes:
- ./prometheus.yml:/etc/prometheus/prometheus.yml
- prometheus_data:/prometheus
command:
- '--config.file=/etc/prometheus/prometheus.yml'
- '--storage.tsdb.retention.time=15d'
volumes:
prometheus_data:
Prometheus Configuration¶
# prometheus.yml
global:
scrape_interval: 15s
evaluation_interval: 15s
scrape_configs:
- job_name: 'huitzo'
static_configs:
- targets: ['app:8080']
metrics_path: /metrics
- job_name: 'huitzo-workers'
static_configs:
- targets: ['worker:9100'] # If worker exposes metrics
- job_name: 'redis'
static_configs:
- targets: ['redis:9121'] # Redis exporter
- job_name: 'postgres'
static_configs:
- targets: ['postgres:9187'] # Postgres exporter
Adding Exporters¶
For database and cache metrics, add exporters:
# docker-compose.yml additions
services:
redis-exporter:
image: oliver006/redis_exporter:latest
environment:
- REDIS_ADDR=redis://redis:6379
ports:
- "9121:9121"
postgres-exporter:
image: prometheuscommunity/postgres-exporter:latest
environment:
- DATA_SOURCE_NAME=postgresql://huitzo:${POSTGRES_PASSWORD}@postgres:5432/huitzo?sslmode=disable
ports:
- "9187:9187"
Grafana Dashboard¶
Setup¶
Add Grafana to your stack:
# docker-compose.yml
services:
grafana:
image: grafana/grafana:latest
ports:
- "3000:3000"
environment:
- GF_SECURITY_ADMIN_PASSWORD=admin
- GF_USERS_ALLOW_SIGN_UP=false
volumes:
- grafana_data:/var/lib/grafana
- ./grafana/dashboards:/etc/grafana/provisioning/dashboards
- ./grafana/datasources:/etc/grafana/provisioning/datasources
volumes:
grafana_data:
Datasource Configuration¶
# grafana/datasources/prometheus.yml
apiVersion: 1
datasources:
- name: Prometheus
type: prometheus
access: proxy
url: http://prometheus:9090
isDefault: true
Dashboard JSON¶
Create a Huitzo dashboard at grafana/dashboards/huitzo.json:
{
"dashboard": {
"title": "Huitzo Platform",
"panels": [
{
"title": "Command Execution Rate",
"type": "graph",
"targets": [
{
"expr": "rate(huitzo_command_total[5m])",
"legendFormat": "{{namespace}}.{{command}} - {{status}}"
}
]
},
{
"title": "Command Duration (p95)",
"type": "graph",
"targets": [
{
"expr": "histogram_quantile(0.95, rate(huitzo_command_duration_seconds_bucket[5m]))",
"legendFormat": "{{namespace}}.{{command}}"
}
]
},
{
"title": "Active Tasks",
"type": "gauge",
"targets": [
{
"expr": "sum(huitzo_active_tasks)",
"legendFormat": "Active"
}
]
},
{
"title": "Error Rate",
"type": "graph",
"targets": [
{
"expr": "sum(rate(huitzo_command_total{status=\"error\"}[5m])) / sum(rate(huitzo_command_total[5m])) * 100",
"legendFormat": "Error %"
}
]
},
{
"title": "LLM Token Usage",
"type": "graph",
"targets": [
{
"expr": "sum(rate(huitzo_llm_tokens_total[1h])) by (provider, type)",
"legendFormat": "{{provider}} - {{type}}"
}
]
},
{
"title": "Storage Operations",
"type": "graph",
"targets": [
{
"expr": "rate(huitzo_storage_operations_total[5m])",
"legendFormat": "{{operation}}"
}
]
}
]
}
}
AlertManager Rules¶
Setup¶
Add AlertManager:
# docker-compose.yml
services:
alertmanager:
image: prom/alertmanager:latest
ports:
- "9093:9093"
volumes:
- ./alertmanager.yml:/etc/alertmanager/alertmanager.yml
Alert Rules¶
Create alert rules in prometheus-rules.yml:
# prometheus-rules.yml
groups:
- name: huitzo-alerts
rules:
# High error rate
- alert: HuitzoHighErrorRate
expr: |
sum(rate(huitzo_command_total{status="error"}[5m]))
/ sum(rate(huitzo_command_total[5m])) > 0.05
for: 5m
labels:
severity: warning
annotations:
summary: "High command error rate"
description: "Error rate is {{ $value | humanizePercentage }} (threshold: 5%)"
# Command latency spike
- alert: HuitzoHighLatency
expr: |
histogram_quantile(0.95, rate(huitzo_command_duration_seconds_bucket[5m])) > 30
for: 10m
labels:
severity: warning
annotations:
summary: "High command latency"
description: "p95 latency is {{ $value | humanizeDuration }}"
# Worker down
- alert: HuitzoWorkerDown
expr: huitzo_worker_status == 0
for: 2m
labels:
severity: critical
annotations:
summary: "Huitzo worker is down"
description: "Worker {{ $labels.worker_id }} is unhealthy"
# Queue backup
- alert: HuitzoQueueBacklog
expr: huitzo_queued_tasks > 100
for: 10m
labels:
severity: warning
annotations:
summary: "Task queue backlog"
description: "{{ $value }} tasks waiting in {{ $labels.queue }} queue"
# Database connection exhaustion
- alert: HuitzoDatabaseConnectionsHigh
expr: huitzo_database_connections / huitzo_database_connections_max > 0.8
for: 5m
labels:
severity: warning
annotations:
summary: "Database connection pool nearly exhausted"
description: "{{ $value | humanizePercentage }} of connections in use"
# LLM provider errors
- alert: HuitzoLLMErrors
expr: |
sum(rate(huitzo_llm_requests_total{status="error"}[5m]))
/ sum(rate(huitzo_llm_requests_total[5m])) > 0.1
for: 5m
labels:
severity: warning
annotations:
summary: "High LLM error rate"
description: "LLM error rate is {{ $value | humanizePercentage }}"
AlertManager Configuration¶
# alertmanager.yml
global:
smtp_smarthost: 'smtp.example.com:587'
smtp_from: '[email protected]'
smtp_auth_username: '[email protected]'
smtp_auth_password: 'password'
route:
group_by: ['alertname']
group_wait: 30s
group_interval: 5m
repeat_interval: 1h
receiver: 'email-notifications'
receivers:
- name: 'email-notifications'
email_configs:
- to: '[email protected]'
- name: 'slack-notifications'
slack_configs:
- api_url: 'https://hooks.slack.com/services/xxx/xxx/xxx'
channel: '#alerts'
Structured Logging¶
Log Configuration¶
# Environment variables
LOG_LEVEL=INFO
LOG_FORMAT=json # json | text
LOG_OUTPUT=stdout # stdout | file
LOG_FILE_PATH=/var/log/huitzo/app.log
Log Format¶
JSON logs include correlation IDs for request tracing:
{
"timestamp": "2026-01-22T10:30:00.000Z",
"level": "INFO",
"logger": "huitzo.executor",
"message": "Command completed",
"correlation_id": "abc-123-def",
"tenant_id": "org_456",
"user_id": "user_789",
"command": "analytics.analyze",
"duration_ms": 1234,
"status": "success"
}
Log Aggregation¶
For log aggregation, use Loki or your preferred solution:
# docker-compose.yml
services:
loki:
image: grafana/loki:latest
ports:
- "3100:3100"
volumes:
- loki_data:/loki
promtail:
image: grafana/promtail:latest
volumes:
- /var/log:/var/log
- ./promtail.yml:/etc/promtail/promtail.yml
Health Checks¶
Endpoints¶
| Endpoint | Description |
|---|---|
GET /health |
Overall system health |
GET /health/live |
Liveness probe (is process running?) |
GET /health/ready |
Readiness probe (can accept requests?) |
Response Format¶
{
"status": "healthy",
"version": "2.0.0",
"timestamp": "2026-01-22T10:30:00Z",
"services": {
"database": {"status": "healthy", "latency_ms": 2},
"redis": {"status": "healthy", "latency_ms": 1},
"worker": {"status": "healthy", "active_tasks": 5}
}
}
Kubernetes Probes¶
# kubernetes deployment
spec:
containers:
- name: huitzo
livenessProbe:
httpGet:
path: /health/live
port: 8080
initialDelaySeconds: 10
periodSeconds: 10
readinessProbe:
httpGet:
path: /health/ready
port: 8080
initialDelaySeconds: 5
periodSeconds: 5
Complete Observability Stack¶
Full docker-compose with all observability components:
# docker-compose.observability.yml
services:
prometheus:
image: prom/prometheus:latest
ports:
- "9090:9090"
volumes:
- ./prometheus.yml:/etc/prometheus/prometheus.yml
- ./prometheus-rules.yml:/etc/prometheus/rules.yml
- prometheus_data:/prometheus
grafana:
image: grafana/grafana:latest
ports:
- "3000:3000"
environment:
- GF_SECURITY_ADMIN_PASSWORD=${GRAFANA_PASSWORD:-admin}
volumes:
- grafana_data:/var/lib/grafana
- ./grafana/provisioning:/etc/grafana/provisioning
alertmanager:
image: prom/alertmanager:latest
ports:
- "9093:9093"
volumes:
- ./alertmanager.yml:/etc/alertmanager/alertmanager.yml
loki:
image: grafana/loki:latest
ports:
- "3100:3100"
volumes:
- loki_data:/loki
volumes:
prometheus_data:
grafana_data:
loki_data:
Run with your main stack:
docker compose -f docker-compose.yml -f docker-compose.observability.yml up -d
Related Documentation¶
- Self-Hosted Deployment – Initial deployment setup
- Configuration Reference – Environment variables
- Architecture Overview – System architecture