Monitoring
Comprehensive monitoring is essential for running STELLA in production. This guide covers metrics, logging, tracing, and alerting.
Overview
┌─────────────────────────────────────────────────────────────┐
│ Monitoring Stack │
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
│ │ Prometheus │ │ Loki │ │ Jaeger │ │
│ │ (Metrics) │ │ (Logging) │ │ (Tracing) │ │
│ └─────────────┘ └─────────────┘ └─────────────┘ │
│ │ │ │ │
│ └────────────────┼────────────────┘ │
│ ▼ │
│ ┌─────────────┐ │
│ │ Grafana │ │
│ │ (Dashboards)│ │
│ └─────────────┘ │
└─────────────────────────────────────────────────────────────┘
Metrics (Prometheus)
Backend Metrics
STELLA exposes Prometheus metrics at /metrics:
// Key metrics exposed
stella_http_requests_total // Total HTTP requests
stella_http_request_duration // Request latency histogram
stella_sessions_active // Current active sessions
stella_sessions_created_total // Total sessions created
stella_agent_pods_active // Running agent pods
stella_database_connections // DB connection pool status
Agent Metrics
# Agent metrics (Python)
from prometheus_client import Counter, Histogram, Gauge
messages_processed = Counter(
'stella_agent_messages_total',
'Total messages processed',
['agent_type', 'direction']
)
response_latency = Histogram(
'stella_agent_response_seconds',
'Response generation latency',
['agent_type'],
buckets=[0.1, 0.5, 1.0, 2.0, 5.0, 10.0]
)
active_conversations = Gauge(
'stella_agent_conversations_active',
'Active conversations',
['agent_type']
)
Prometheus Configuration
# prometheus.yml
global:
scrape_interval: 15s
scrape_configs:
# Backend API
- job_name: 'stella-backend'
kubernetes_sd_configs:
- role: pod
namespaces:
names: ['ai-agents']
relabel_configs:
- source_labels: [__meta_kubernetes_pod_label_app]
regex: session-management-server
action: keep
# Agent pods
- job_name: 'stella-agents'
kubernetes_sd_configs:
- role: pod
namespaces:
names: ['ai-agents']
relabel_configs:
- source_labels: [__meta_kubernetes_pod_label_app]
regex: stella-agent
action: keep
Key Dashboards
System Overview:
- Active sessions
- Request rate
- Error rate
- Average latency
Agent Performance:
- Response latency distribution
- Messages per minute
- Tool execution time
- STT/TTS latency
Infrastructure:
- Pod resource usage
- Database connections
- Node resource utilization
Logging (Structured)
Backend Logging
// Structured JSON logging
import { Logger } from '@nestjs/common';
this.logger.log({
message: 'Session created',
sessionId: session.id,
projectId: session.projectId,
agentType: session.agentType,
duration_ms: Date.now() - startTime
});
Agent Logging
import structlog
logger = structlog.get_logger()
logger.info(
"message_processed",
session_id=self.session_id,
speaker="user",
text_length=len(text),
processing_time_ms=processing_time * 1000
)
Log Format
{
"timestamp": "2024-01-15T10:30:00.000Z",
"level": "info",
"message": "Session created",
"service": "stella-backend",
"session_id": "abc123",
"project_id": "proj456",
"agent_type": "stella-agent",
"duration_ms": 150,
"trace_id": "trace789"
}
Loki Configuration
# Promtail config for Kubernetes
scrape_configs:
- job_name: kubernetes-pods
kubernetes_sd_configs:
- role: pod
pipeline_stages:
- json:
expressions:
level: level
session_id: session_id
trace_id: trace_id
- labels:
level:
session_id:
Tracing (OpenTelemetry)
Backend Tracing
// Instrument with OpenTelemetry
import { trace } from '@opentelemetry/api';
const tracer = trace.getTracer('stella-backend');
async function createSession(dto: CreateSessionDto) {
const span = tracer.startSpan('session.create');
try {
span.setAttribute('project_id', dto.projectId);
span.setAttribute('agent_type', dto.agentType);
const session = await this.prisma.session.create({ ... });
span.setAttribute('session_id', session.id);
return session;
} catch (error) {
span.recordException(error);
throw error;
} finally {
span.end();
}
}
Agent Tracing
from opentelemetry import trace
tracer = trace.get_tracer(__name__)
async def generate_response(self, text: str):
with tracer.start_as_current_span("generate_response") as span:
span.set_attribute("input_length", len(text))
# LLM call
with tracer.start_span("llm_call"):
response = await self.openai.chat.completions.create(...)
span.set_attribute("output_length", len(response))
return response
Jaeger Deployment
apiVersion: apps/v1
kind: Deployment
metadata:
name: jaeger
namespace: monitoring
spec:
replicas: 1
selector:
matchLabels:
app: jaeger
template:
spec:
containers:
- name: jaeger
image: jaegertracing/all-in-one:latest
ports:
- containerPort: 16686 # UI
- containerPort: 4318 # OTLP HTTP
env:
- name: COLLECTOR_OTLP_ENABLED
value: "true"
Alerting
Alert Rules
# prometheus-rules.yml
groups:
- name: stella-alerts
rules:
# High error rate
- alert: HighErrorRate
expr: |
sum(rate(stella_http_requests_total{status=~"5.."}[5m]))
/ sum(rate(stella_http_requests_total[5m])) > 0.05
for: 5m
labels:
severity: critical
annotations:
summary: High error rate detected
description: Error rate is above 5%
# Slow responses
- alert: HighLatency
expr: |
histogram_quantile(0.95,
rate(stella_http_request_duration_bucket[5m])
) > 2
for: 5m
labels:
severity: warning
annotations:
summary: High latency detected
description: P95 latency is above 2 seconds
# Too many active sessions
- alert: HighSessionCount
expr: stella_sessions_active > 100
for: 10m
labels:
severity: warning
annotations:
summary: High number of active sessions
# Agent pod failures
- alert: AgentPodFailures
expr: |
increase(kube_pod_container_status_restarts_total{
namespace="ai-agents",
container="agent"
}[1h]) > 5
for: 5m
labels:
severity: critical
annotations:
summary: Agent pods restarting frequently
Alertmanager Configuration
# alertmanager.yml
route:
receiver: 'slack'
group_wait: 30s
group_interval: 5m
repeat_interval: 4h
routes:
- match:
severity: critical
receiver: 'pagerduty'
receivers:
- name: 'slack'
slack_configs:
- api_url: 'https://hooks.slack.com/...'
channel: '#stella-alerts'
- name: 'pagerduty'
pagerduty_configs:
- service_key: '...'
Grafana Dashboards
Dashboard JSON
{
"dashboard": {
"title": "STELLA Overview",
"panels": [
{
"title": "Active Sessions",
"type": "stat",
"targets": [{
"expr": "stella_sessions_active"
}]
},
{
"title": "Request Rate",
"type": "graph",
"targets": [{
"expr": "rate(stella_http_requests_total[5m])"
}]
},
{
"title": "Error Rate",
"type": "graph",
"targets": [{
"expr": "sum(rate(stella_http_requests_total{status=~\"5..\"}[5m])) / sum(rate(stella_http_requests_total[5m]))"
}]
},
{
"title": "Response Latency",
"type": "heatmap",
"targets": [{
"expr": "rate(stella_http_request_duration_bucket[5m])"
}]
}
]
}
}
Health Checks
Kubernetes Probes
spec:
containers:
- name: backend
livenessProbe:
httpGet:
path: /health
port: 3000
initialDelaySeconds: 30
periodSeconds: 10
readinessProbe:
httpGet:
path: /health/ready
port: 3000
initialDelaySeconds: 5
periodSeconds: 5
Health Endpoints
// Backend health endpoints
@Controller('health')
export class HealthController {
@Get()
health() {
return { status: 'healthy', timestamp: new Date() };
}
@Get('ready')
async ready() {
// Check database connection
await this.prisma.$queryRaw`SELECT 1`;
return { status: 'ready' };
}
}
Next Steps
- Production Checklist - Go-live checklist
- Kubernetes Deployment - Deployment guide