What Monitoring & Observability Actually Mean
Deconstruct the anatomy of a production AI application and understand why traditional black-box ping monitoring fails to diagnose distributed non-deterministic failures.
In local development or notebook prototyping, an AI application feels like a single synchronous function call: response = llm.generate(prompt). In production, however, that single function call is transformed into an asynchronous, distributed web of microservices, networking boundaries, and third-party rate limits.
When a user complains that "the assistant took 30 seconds and then gave a generic apology", where did the failure occur?
You rely entirely on angry customer support emails, tweets, or executive escalations. You have zero visibility into traffic volume, failure rates, or latencies. Fixing problems requires reproducing them locally by guessing input payloads.
You poll a /healthz endpoint every 30 seconds and check if container CPU is under 80%. You know IF a server crashed, but have zero clues when requests silently degrade or hang in upstream LLM rate-limit retry loops.
Your system emits structured telemetry (metrics, logs, traces) at every hop. You can isolate the exact 14.2s delay to an unindexed vector search query, correlate it with the customer session ID, and inspect the exact prompt token count in seconds.
Monitoring answers: "Is the system broken?" (Tracking predefined failure modes and threshold alerts: "CPU > 90%").
Observability is a property of the system that answers: "Why is it behaving this way?" (The ability to infer the internal state of a distributed system solely by questioning its emitted telemetry, especially for novel, unpredicted failure states).
Production Health Dashboard & Anomaly Simulator
Observe how four core production telemetry signals interact under normal traffic vs simulated catastrophic failure states.
All services operating within optimal parameters. p95 latency is well below the 1.0s SLA, error rate is nominal, and GPU memory has headroom for burst traffic.
Telemetry & The Three Core Signals
Explore the fundamental units of telemetryβMetrics, Logs, and Tracesβand discover how a single AI user request manifests across all three lenses.
Telemetry is the automated process of collecting, formatting, and transmitting operational data from remote software components to monitoring backends. In modern cloud-native observability, telemetry is divided into three primary signals:
Metrics
Numeric measurements aggregated over time. Optimized for mathematical aggregation (rates, averages, percentiles) and high-speed dashboard visualization.
Logs
Timestamped discrete event records. Emitted when specific events take place (e.g. user authentication, vector query execution, or uncaught exceptions).
Traces
End-to-end journey of a single request. Follows a transaction across network boundaries, visualizing the exact parent-child hierarchy and execution duration of each hop.
One Request, Three Telescopic Views
Suppose a user asks: "Summarize the quarterly revenue report." The request executes through the API Gateway, retrieves 5 text chunks from Qdrant, calls the LLM, and streams tokens back to the user. Here is how that identical request appears to your telemetry systems:
| Telemetry Signal | Underlying Representation | Primary Question Answered | Operational Strength |
|---|---|---|---|
| Metric | A counter incremented: ai_token_usage_total{type="prompt"} += 1840 | "How many total tokens did our entire cluster consume over the last 15 minutes?" | Extremely cheap storage, fast mathematical alerting, low storage footprint. |
| Structured Log | A JSON record with timestamp, service name, session ID, chunk count, and model duration. | "What exact parameters and metadata were passed during the failure at 14:22:01?" | Rich context, granular event payload, stack traces. |
| Distributed Trace | A DAG of Spans: HTTP request (2.1s) containing Qdrant search (120ms) and LLM call (1.9s). | "Which specific component in the multi-service dependency chain caused the 2-second delay?" | Reveals latency bottlenecks, concurrency overlap, and downstream cascading stalls. |
Telemetry Signal Classifier
Inspect real production telemetry payloads and classify whether each snippet is a Metric, a Log, or a Trace Span.
Metrics That Matter in Production AI
Learn to differentiate high-signal operational indicators from vanity metrics, and avoid the catastrophic Prometheus cardinality explosion trap.
Novice operations teams make one of two critical mistakes: they either monitor nothing, or they collect thousands of arbitrary metrics that create alert noise and crash their time-series databases. Production metrics must be selected around a single operational question:
1. Application Metrics
- Request Count (RPS): Total traffic throughput.
- HTTP Error Rate (%): 4xx client errors vs 5xx server bugs.
- Request Duration (Latency): Distribution across endpoints.
- Active In-flight Requests: Concurrency load on worker threads.
2. Infrastructure & Hardware
- CPU / Memory (RAM): Host resource exhaustion.
- GPU Tensor Core Util (%): Compute efficiency.
- GPU VRAM Allocation: Model weights + KV-cache headroom.
- PCIe Throughput: Host-to-device tensor transfer stalls.
3. AI & GenAI Specific
- Time to First Token (TTFT): Time to begin streaming.
- Token Velocity (ITL): Output tokens generated per second.
- Token Volumes: Prompt tokens vs completion tokens.
- Provider Error Codes: 429 rate-limits vs 503 capacity outages.
In Prometheus, every unique combination of key-value labels creates a completely separate time-series in memory. Never attach unbounded, high-cardinality values (such as user_id, prompt_id, session_uuid, or raw document_title) to Prometheus metric labels! If you have 100,000 users, that single metric creates 100,000 active time series, causing memory exhaustion and crashing your monitoring cluster. High-cardinality identifiers belong in Traces and Structured Logs, NEVER in metrics.
Metric Selection & Triage Lab
Select the high-signal operational metrics required to diagnose real production customer problems without collecting useless vanity data.
"Enterprise customers report that the streaming AI assistant takes upwards of 15 seconds to begin typing responses during business peak hours."
Choose the metrics that will immediately isolate the root cause:
Latency, Error Rates & Percentiles
Unmask the mathematical fallacy of average latency, master percentiles (p50, p95, p99), and understand tail latency compounding in multi-hop AI pipelines.
The three holy pillars of service health monitoring (often called the RED method: Rate, Errors, and Duration) describe how much traffic your system handles, how many requests fail, and how long each request takes.
Why Averages Lie: The Tale of the Flawed Mean
Imagine you have 100 requests. 95 of those requests hit an in-memory cache and return in 50ms. However, 5 requests execute an intensive 8-tool autonomous agent workflow with web search and vector retrieval, taking 8,000ms (8 seconds).
Looking at the average latency, the engineering manager concludes: "Our API is fast! Less than half a second!" Meanwhile, 1 out of every 20 enterprise users is waiting 8 seconds and abandoning the product in frustration.
Percentiles expose the true distribution: p50 represents the typical median user experience, while p99 exposes the extreme tail latency experienced under load or complex edge cases.
If a single microservice has a 1% tail latency probability (p99 = 4s), a traditional monolithic endpoint rarely trips it. However, if an autonomous AI agent executes 10 sequential tool calls in a loop, the probability that at least one step hits the p99 tail delay is:1 - (1 - 0.01)^10 = 1 - (0.99)^10 β 9.56%
Nearly 10% of all user requests will suffer an excruciating multi-second freeze! Tail latency amplification makes percentile monitoring non-negotiable for AI applications.
Performance Metrics & Percentiles Lab
Manipulate synthetic request latency distributions and calculate real-time Average, p50, p95, and p99 percentiles mathematically.
Structured Logging & Security Hygiene
Move beyond arbitrary string printing to schema-enforced JSON logs, and enforce strict PII sanitization to prevent catastrophic compliance violations.
In early development, developers often rely on unstructured print statements like print(f"Error in model: {err}"). In production with 200 distributed worker instances processing 5,000 requests per second, unstructured logs are completely uselessβthey cannot be indexed, queried, filtered by tenant, or correlated with distributed traces.
Cannot filter by HTTP status code, cannot join with trace ID, cannot isolate tenant, and cannot aggregate by model name in log aggregators (Loki, Elasticsearch).
Β Β "timestamp": "2026-09-20T14:22:01.402Z",
Β Β "level": "ERROR",
Β Β "service": "ai-orchestrator",
Β Β "trace_id": "4bf92f3577b34da6",
Β Β "span_id": "00f067aa0ba902b7",
Β Β "gen_ai.request.model": "gpt-4o",
Β Β "gen_ai.usage.prompt_tokens": 1420,
Β Β "error.type": "UpstreamTimeout",
Β Β "duration_ms": 5002
}
Every key is indexed, queryable via LogQL/Elastic, directly correlates to distributed traces, and enables instant analytical filtering.
Unlike traditional CRUD applications where database fields are known beforehand, GenAI inputs are free-form natural language. Users routinely paste credentials, social security numbers, medical histories, credit cards, and proprietary source code into AI prompts.
STRICT MANDATE:
1. NEVER log Authorization headers, API keys, or raw JWT bearer tokens.
2. NEVER blindly write raw prompt strings or model outputs to standard application logs without regex sanitizers and automated PII scrubbing middlewares.
3. Persist only prompt token counts, prompt hash IDs, or sanitized summaries. If raw inputs must be captured for evaluation (Evals), store them in an encrypted, access-controlled vault separated from operational log streams.
Production Log Stream Debugger & PII Sanitizer
Filter through an asynchronous interleaved multi-service log stream, correlate an incident via Trace ID, and test real-time PII redaction.
Distributed Tracing & Span Waterfalls
Learn how distributed context propagation reconstructs the end-to-end execution path of requests across independent microservices and external AI APIs.
In a microservices architecture, when a user request takes 4.5 seconds to return, looking at metrics only tells you "The overall API is slow". Looking at logs gives you thousands of disconnected text events. Distributed Tracing solves this by tracking the exact trajectory of an individual request through every boundary.
1. Trace
The complete directed acyclic graph (DAG) representing the entire journey of a request from client initiation to final response delivery. Identified by a globally unique 128-bit trace_id.
2. Span
A single contiguous unit of execution within the trace (e.g. executing a SQL query, computing embeddings, or calling an LLM). Has a name, start time, finish time, parent span ID, and status code.
3. Context Propagation
The mechanism of passing trace metadata across network boundaries via standard HTTP headers (W3C Trace Context traceparent) so downstream services attach their spans to the same trace.
Distributed Trace Waterfall Inspector
Inspect an end-to-end multi-tier AI request Gantt chart. Click individual spans to reveal execution durations, parent-child links, and semantic metadata.
openai.chat.completions
openai.chat.completions) accounted for 1,720ms (70.2%) of the total 2,450ms request duration, of which 480ms was Time to First Token (TTFT). The vector DB similarity query took 380ms.Correlating Metrics, Logs & Traces
Learn how high-performing SREs correlate all three telemetry signals into a single unified incident narrative.
Observability is not about having three disconnected tools in three browser tabs (a Grafana chart tab, a Loki logs tab, and a Jaeger traces tab). True observability happens when all three signals are linked by shared correlation keys:
In modern Prometheus and Grafana, you do not have to manually copy-paste trace IDs. Exemplars attach a specific trace_id directly to a histogram metric bucket. When you see a high-latency spike dot on your Grafana latency graph, clicking that exact dot instantly opens the corresponding distributed trace in Grafana Tempo!
OpenTelemetry (OTel) Architecture & Semantic Conventions
Understand the Cloud Native Computing Foundation (CNCF) industry standard for vendor-neutral telemetry collection, processors, and the 2026 GenAI semantic standards.
OpenTelemetry (OTel) is an open-source observability framework providing vendor-neutral APIs, SDKs, and tooling to generate, collect, and export telemetry data.
OTel IS: The universal standard for instrumenting code, generating spans/metrics, and routing telemetry data via the OpenTelemetry Protocol (OTLP).
OTel IS NOT: A visualization dashboard or permanent database backend. OTel generates and forwards data; backends like Prometheus (metrics), Grafana Tempo (traces), and Loki (logs) store and render it.
Official CNCF OpenTelemetry GenAI Semantic Conventions (2026 Standard)
Before 2024, every AI library invented its own inconsistent span names and attribute keys. The CNCF OpenTelemetry GenAI Working Group established standardized semantic attribute conventions:
| Standard OTel Attribute | Type | Example Value | Description |
|---|---|---|---|
gen_ai.system | String | "openai", "anthropic", "vllm" | The model provider or inference runtime engine. |
gen_ai.request.model | String | "gpt-4o", "claude-3-5-sonnet" | The model name requested by the client. |
gen_ai.usage.input_tokens | Int | 1240 | Number of prompt/input tokens processed. |
gen_ai.usage.output_tokens | Int | 312 | Number of generated/completion tokens. |
gen_ai.server.time_to_first_token | Float | 0.482 (seconds) | Time from request dispatch to receipt of the first token. |
# FastAPI OpenTelemetry Production Instrumentation
from fastapi import FastAPI
from opentelemetry import trace
from opentelemetry.sdk.trace import TracerProvider
from opentelemetry.sdk.trace.export import BatchSpanProcessor
from opentelemetry.exporter.otlp.proto.grpc.trace_exporter import OTLPSpanExporter
from opentelemetry.instrumentation.fastapi import FastAPIInstrumentor
from opentelemetry.sdk.resources import Resource
# 1. Define Service Identity Resource
resource = Resource.create({"service.name": "ai-orchestrator", "service.version": "1.4.0"})
provider = TracerProvider(resource=resource)
# 2. Configure OTLP Exporter to OpenTelemetry Collector
otlp_exporter = OTLPSpanExporter(endpoint="http://otel-collector:4317", insecure=True)
provider.add_span_processor(BatchSpanProcessor(otlp_exporter))
trace.set_tracer_provider(provider)
# 3. Auto-instrument FastAPI
app = FastAPI()
FastAPIInstrumentor.instrument_app(app)Telemetry Pipeline Architecture Simulator
Toggle components in the OpenTelemetry collection pipeline to observe how misconfigurations or missing proxies impact production visibility.
Prometheus & Time-Series Metrics Basics
Learn how Prometheus scrapes metric endpoints, understands metric types (Counter, Gauge, Histogram), and evaluates real-time queries with PromQL.
Prometheus is the de-facto cloud-native metrics engine. Unlike logging engines that push unstructured events, Prometheus operates on a pull-based architecture: it periodically scrapes HTTP /metrics endpoints exposed by your microservices, storing the measurements in a high-performance time-series database (TSDB).
A cumulative metric that only ever increases or resets to zero on restart. Ideal for tracking total request counts, errors, or tokens consumed.
A numerical value that can arbitrarily go up and down. Ideal for current active connections, GPU memory utilization, or queue depth.
Samples observations (usually request durations or payload sizes) and counts them into configurable buckets. Enables accurate p50, p95, and p99 percentile calculations.
Calculates configurable quantiles directly on the client side over a sliding time window. More expensive on client CPU, but requires no bucket definitions.
PromQL Primer: Essential Queries for Production AI
PromQL (Prometheus Query Language) allows you to aggregate and filter time series in real time. Here are the three most critical PromQL patterns:
| Operational Goal | PromQL Query | Explanation |
|---|---|---|
| Request Rate (RPS) | sum(rate(http_requests_total[5m])) | Calculates per-second rate of increase across a 5-minute sliding window. |
| Error Rate Percentage | sum(rate(http_requests_total{status=~"5.."}[5m])) / sum(rate(http_requests_total[5m])) * 100 | Divides 5xx error rate by total request rate to compute failure percentage. |
| 95th Percentile Latency | histogram_quantile(0.95, sum(rate(http_request_duration_seconds_bucket[5m])) by (le)) | Interpolates the p95 latency across histogram duration buckets. |
| Token Consumption Velocity | sum by (model) (rate(ai_tokens_consumed_total[5m])) | Tracks the per-second burn rate of tokens categorized by LLM model. |
PromQL Query Simulator & Metrics Lab
Execute synthetic PromQL queries against live simulated time-series data and inspect the calculated metrics and operational decisions.
Steady traffic volume. Current throughput is well within autoscaling limits.
Designing High-Impact Production Dashboards
Learn why a dashboard is not a visual junkyard of 50 graphs, and master the 4-tier layout designed around actionable operational questions.
A bad dashboard has 40 disconnected panels showing raw CPU, random thread counts, and obscure network sockets that take 10 minutes to decipher. A high-impact production dashboard tells a clear story from top to bottom, answering questions in hierarchical priority:
"Are customers experiencing problems right now?"
β’ Inbound RPS
β’ HTTP 5xx Error Rate
β’ p95 / p99 Latency
"How is the generative model performing?"
β’ Time to First Token (TTFT)
β’ Output Tokens / Second
β’ Upstream Provider 429s
"Are downstream backends stalling?"
β’ Qdrant Vector DB Latency
β’ PostgreSQL Pool Saturation
β’ Tool API HTTP Status
"Are compute resources saturated?"
β’ GPU VRAM Allocation
β’ GPU Tensor Core Util
β’ Pod CPU & Host RAM
Actionable Alerting & Alert Fatigue Prevention
Discover the principles of symptom-based alerting and learn how to construct alert rules that wake on-call engineers only for real customer-impacting outages.
Alert fatigue is one of the most dangerous failure modes in engineering organizations. When an on-call engineer receives 50 false-positive notifications per week for transient CPU spikes or brief network retries, they begin ignoring alerts. Eventually, when a catastrophic database outage strikes, the critical alert is buried in the noise.
"Alert if Pod CPU > 80% for 30 seconds."
Why it fails: High CPU is normal during bursty batch processing or JIT warmup. If user requests are completing in 80ms with 0% errors, waking up an engineer is completely counter-productive.
"Alert if HTTP 5xx error rate > 2.5% for 5 continuous minutes."
Why it succeeds: Directly reflects customer pain. A sustained 2.5% failure rate for 5 minutes means users cannot use the application, requiring immediate operational triage.
Alert Rule Builder & Noise Simulator
Configure threshold rules and simulate them against realistic traffic bursts vs sustained outages to detect false alarms and alert fatigue.
AI-Specific Observability Signals
Master the specialized operational metrics unique to modern Large Language Models, Retrieval-Augmented Generation (RAG), and autonomous Agent loops.
While traditional web services only care about request count and status codes, AI applications possess non-deterministic execution times, autoregressive streaming mechanics, and GPU hardware dependencies that require specialized telemetry signals:
1. LLM & Serving Telemetry
- Time to First Token (TTFT): Measures prompt prefill speed and queue latency before the first character streams.
- Inter-Token Latency (ITL): Time required to generate each subsequent token (governed by GPU memory bandwidth).
- Prompt / Completion Token Ratio: Detects prompt bloating or unexpected runaway verbose responses.
- vLLM KV Cache Usage Factor: Percentage of allocated GPU memory occupied by active conversation context blocks.
2. RAG & Vector Search Telemetry
- Vector DB Search Duration: Latency of HNSW similarity queries inside Qdrant / Pinecone / pgvector.
- Top-K Similarity Score Distribution: If average cosine distance drops below 0.65, users are searching for topics absent from your knowledge base.
- Zero-Result Retrieval Count: Spikes indicate broken metadata filters or malformed embedding vectors.
3. AI Agent Observability
- Tool Call Count per Request: Number of external actions taken before reaching a final response.
- Tool Failure Rate: Frequency of third-party API exceptions (e.g. Stripe card errors, SQL syntax errors).
- Agent Runaway Loop Metric: Counts requests hitting max iteration ceiling (circuit breaker trips).
4. Cost & Quota Signals
- Dollar Cost Per 1k Requests: Real-time estimation of external API billing based on token usage metrics.
- Semantic Cache Hit Ratio: Percentage of user queries served from Redis semantic cache without calling external LLMs.
- Upstream Quota Saturation (%): Tier limit utilization to predict and prevent 429 rate limit outages.
Production Incident Response Console
Step into the shoes of an on-call AI SRE. Triaging a live degraded production incident by correlating Metrics, Logs, and Traces to discover the root cause and apply remediation.
Live Production Triage Terminal
Follow the 3-step investigation workflow (Detect β Isolate β Remediate) to resolve an active customer outage.
Multiple corporate clients report that customer support queries are spinning indefinitely. The PagerDuty alert fired 4 minutes ago.
Review the service metrics. Notice where the anomaly exists:
Mini-Project: Production Observability Architecture Plan
Synthesize all concepts into a complete, battle-tested production observability specification for an enterprise AI support application.
As the Lead AI Platform Engineer, you are tasked with designing the monitoring architecture for an enterprise AI chat platform consisting of: a FastAPI Backend, PostgreSQL, Qdrant Vector DB, LiteLLM Provider Router, and background Celery Workers. Below is the production-grade specification:
http_requests_total{route, status, method}(Bounded status codes)http_request_duration_seconds_bucket(Exponential buckets 0.05s to 30s)ai_tokens_consumed_total{model, type="prompt|completion"}ai_upstream_provider_requests_total{provider, status_code}rag_vector_search_duration_seconds_bucket{collection}
- Emits strict JSON to stdout (scraped by Vector / Promtail into Loki).
- Always includes:
timestamp,level,service,trace_id,span_id. - PII Regex sanitization filter intercepts credit cards, SSNs, and bearer tokens.
- Raw prompts are NEVER logged in operational logs; only prompt token counts and hashes.
- W3C
traceparentpropagated from Gateway to Celery via Redis message headers. - Explicit child spans for: Vector search, Re-ranking, Prompt formatting, Upstream LLM streaming, Guardrail scoring.
- Tail sampling: 100% of errors and traces > 3.0s kept; 5% of healthy traces sampled.
- Critical Pager: HTTP 5xx error rate > 2.5% for 5 continuous minutes.
- Critical Pager: Chatbot p95 tail latency > 4.0s for 5 continuous minutes.
- Warning (Slack): Upstream LLM 429 rate limit errors > 10 req/min for 3 minutes.
- Warning (Slack): vLLM GPU KV-cache utilization > 92% for 5 minutes.
# Production OpenTelemetry Collector Configuration
receivers:
otlp:
protocols:
grpc:
endpoint: 0.0.0.0:4317
http:
endpoint: 0.0.0.0:4318
processors:
batch:
send_batch_size: 1024
timeout: 1s
memory_limiter:
check_interval: 1s
limit_percentage: 75
spike_limit_percentage: 20
tail_sampling:
decision_wait: 10s
num_traces: 10000
expected_new_traces_per_sec: 2000
policies:
- name: errors-policy
type: status_code
status_code: { status_codes: [ ERROR ] }
- name: slow-traces-policy
type: latency
latency: { threshold_ms: 3000 }
- name: probabilistic-sample
type: probabilistic
probabilistic: { sampling_percentage: 5.0 }
exporters:
prometheus:
endpoint: "0.0.0.0:8889"
otlp/tempo:
endpoint: "tempo:4317"
tls:
insecure: true
service:
pipelines:
traces:
receivers: [otlp]
processors: [memory_limiter, tail_sampling, batch]
exporters: [otlp/tempo]
metrics:
receivers: [otlp]
processors: [memory_limiter, batch]
exporters: [prometheus]8 Real-World Production Incident Post-Mortems
Deep-dive into actual verified outages suffered by production AI companies, analyzing the exact symptoms, root causes, architectural fixes, and copyable code solutions.
Incident #1: Prometheus Cardinality Explosion Crashing Telemetry Node
critical severityThe central Prometheus instance crashed with Out-Of-Memory (OOMKill) every 3 hours. Metric scraping failed across all production clusters, rendering all operational dashboards blank.
A newly deployed AI routing middleware attached `user_uuid` and `session_id` as Prometheus metric labels to `ai_inference_requests_total`. With 250,000 active daily users, this created over 4 million concurrent active time series in the Prometheus TSDB head chunk.
Strip high-cardinality labels from metrics immediately. Move `user_uuid` to OpenTelemetry trace attributes and structured log metadata where cardinality is unbounded by design.
# BEFORE (FATAL: High-cardinality explosion in metrics)
# ai_requests_counter.labels(model="gpt-4o", user_id=user.id, session_id=session.id).inc()
# AFTER (CORRECT: Keep low-cardinality dimensions in Prometheus)
from prometheus_client import Counter
from opentelemetry import trace
ai_requests_counter = Counter(
"ai_inference_requests_total",
"Total AI inference calls",
["model_name", "status_code", "provider"]
)
# Record metric with bounded label values
ai_requests_counter.labels(
model_name="gpt-4o",
status_code="200",
provider="openai"
).inc()
# Store user_id and session_id inside OpenTelemetry Trace Span attributes instead!
current_span = trace.get_current_span()
if current_span.is_recording():
current_span.set_attribute("user.id", user.id)
current_span.set_attribute("session.id", session.id)Incident #2: Silent Upstream Rate Limiting (HTTP 429) Masquerading as Latency
high severityCustomer chatbot requests exhibited severe p99 tail latency spikes from 800ms to 45,000ms. Frontend users experienced repeated spinner timeouts, but the application error rate metric showed 0% 500 errors.
The backend HTTP client was configured with exponential backoff retry (up to 6 retries) without a circuit breaker. Upstream OpenAI API began returning HTTP 429 'Rate limit reached', causing workers to sleep and retry in a tight loop for 45 seconds before returning a fallback message.
Instrument upstream HTTP client with custom status metrics (`upstream_llm_status_total{code="429"}`), configure circuit breaking, and fail fast to an alternate fallback model when rate limits trigger.
# Production Resilient LLM Client with Telemetry & Circuit Breaking
import time
from opentelemetry import trace
from prometheus_client import Counter, Histogram
LLM_UPSTREAM_REQUESTS = Counter(
"ai_upstream_requests_total",
"Upstream LLM requests by provider and status",
["provider", "model", "http_status"]
)
LLM_RETRY_COUNT = Counter(
"ai_upstream_retries_total",
"Number of retry attempts triggered",
["provider", "reason"]
)
async def call_llm_with_telemetry(prompt: str, model: str, attempt: int = 1):
tracer = trace.get_tracer("ai.llm.client")
with tracer.start_as_current_span("upstream_llm_call") as span:
span.set_attribute("gen_ai.request.model", model)
span.set_attribute("retry.attempt", attempt)
try:
resp = await client.chat.completions.create(model=model, messages=[...])
LLM_UPSTREAM_REQUESTS.labels(provider="openai", model=model, http_status="200").inc()
return resp
except RateLimitError as e:
LLM_UPSTREAM_REQUESTS.labels(provider="openai", model=model, http_status="429").inc()
LLM_RETRY_COUNT.labels(provider="openai", reason="rate_limit_429").inc()
span.record_exception(e)
span.set_status(trace.Status(trace.StatusCode.ERROR, "Rate limited"))
# Fail fast to fallback model if attempt > 2 instead of 45s backoff stall
if attempt >= 2:
return await call_fallback_local_vllm(prompt)Incident #3: OpenTelemetry Span Leak Causing Python Worker Memory Exhaustion
critical severityFastAPI inference workers accumulated 500MB of resident RAM per hour under steady 20 RPS traffic. After 8 hours, Kubernetes OOMKilled worker pods sequentially, causing brief 502 Bad Gateway outages.
Streaming chat endpoints initialized an OpenTelemetry tracer span at the start of the generator, but because client disconnects triggered `CancelledError`, the `span.end()` call was skipped, leaving hundreds of thousands of orphaned span objects in the active context memory pool.
Ensure all manual spans are managed via Python context managers (`with tracer.start_as_current_span(...)`), or wrapped in `try...finally: span.end()` to guarantee cleanup even during connection aborts.
# ANTI-PATTERN: Manual span without exception safety
# span = tracer.start_span("streaming_chat")
# for chunk in stream: yield chunk
# span.end() # NEVER REACHED IF CLIENT DISCONNECTS!
# PRODUCTION FIX: Context manager guarantees span.end() execution
from fastapi import Request
from fastapi.responses import StreamingResponse
from opentelemetry import trace
tracer = trace.get_tracer("ai.chat.service")
@app.post("/v1/chat/stream")
async def chat_stream(request: Request, payload: ChatPayload):
async def token_generator():
# Context manager handles span lifecycle across async cancellation
with tracer.start_as_current_span("llm_stream_generation") as span:
span.set_attribute("gen_ai.system", "openai")
span.set_attribute("gen_ai.request.model", payload.model)
token_count = 0
try:
async for chunk in model_stream(payload.prompt):
token_count += 1
yield f"data: {chunk}\n\n"
except Exception as exc:
span.record_exception(exc)
span.set_status(trace.Status(trace.StatusCode.ERROR))
raise
finally:
span.set_attribute("gen_ai.usage.output_tokens", token_count)
# span context automatically closes here, preventing memory leaks!
return StreamingResponse(token_generator(), media_type="text/event-stream")Incident #4: Vector DB Slow Query Storm Concealed by Average Latency
high severityA medical RAG application reported an average API response time of 320ms, which was considered healthy by the operations team. However, customer support reported that enterprise search was 'completely hanging' for doctors reviewing oncology files.
While 90% of searches were simple 1-sentence queries (taking 40ms in Qdrant), complex queries containing 2,000-word clinical notes hit an unindexed HNSW partition, triggering brute-force vector scans that took 18,500ms. The mean latency was diluted by the 90% fast queries.
Implement histogram buckets tailored for tail latency (`[0.05, 0.1, 0.25, 0.5, 1.0, 2.5, 5.0, 10.0, 20.0]`). Add p95 and p99 alerts on vector retrieval spans.
# Prometheus Histogram bucket configuration in Python
from prometheus_client import Histogram
VECTOR_SEARCH_DURATION = Histogram(
"rag_vector_search_duration_seconds",
"Latency of Qdrant HNSW similarity search",
["collection_name", "index_type"],
# Explicit custom exponential buckets up to 25 seconds
buckets=[0.025, 0.05, 0.1, 0.25, 0.5, 1.0, 2.5, 5.0, 10.0, 20.0, 25.0]
)
# PromQL Alert Rule for Prometheus Alertmanager (alerts.yaml)
"""
groups:
- name: rag_alerts
rules:
- alert: RAGVectorSearchTailLatencySpike
expr: histogram_quantile(0.99, sum(rate(rag_vector_search_duration_seconds_bucket[5m])) by (le)) > 3.0
for: 3m
labels:
severity: warning
annotations:
summary: "Vector DB p99 search latency > 3s (currently {{ $value }}s)"
runbook_url: "https://wiki.corp/ops/rag-tuning"
"""Incident #5: Unmasked PII in Debug Logs Triggering Regulatory Audit
critical severityA security scanner flagged that credit card numbers, social security numbers, and patient names were visible in plain text inside central Elasticsearch / Kibana log clusters, triggering an emergency compliance incident.
Developers enabled `DEBUG` level logging in production to troubleshoot an issue, and logged the entire raw incoming HTTP JSON payload including `messages[0].content`.
Implement an automated OpenTelemetry / logging filter that scrubs PII matching regex patterns (credit cards, SSNs, bearer tokens) before emitting logs. Never log raw prompts directly.
# Production Structured Logging Sanitizer Middleware
import re
import logging
import json
class PIIMaskingFormatter(logging.Formatter):
CC_REGEX = re.compile(r'\b(?:\d[ -]*?){13,16}\b')
SSN_REGEX = re.compile(r'\b\d{3}-\d{2}-\d{4}\b')
BEARER_REGEX = re.compile(r'Bearer\s+[A-Za-z0-9\-\._~\+\/]+=*', re.IGNORECASE)
def format(self, record: logging.LogRecord) -> str:
original = super().format(record)
# Redact credit cards
sanitized = self.CC_REGEX.sub("[REDACTED_CC]", original)
# Redact SSNs
sanitized = self.SSN_REGEX.sub("[REDACTED_SSN]", sanitized)
# Redact Authorization header tokens
sanitized = self.BEARER_REGEX.sub("Bearer [REDACTED_TOKEN]", sanitized)
return sanitized
# Setup safe logger
logger = logging.getLogger("ai.safe.service")
handler = logging.StreamHandler()
handler.setFormatter(PIIMaskingFormatter('{"time":"%(asctime)s", "level":"%(levelname)s", "msg":"%(message)s"}'))
logger.addHandler(handler)
logger.setLevel(logging.INFO)Incident #6: AI Agent Infinite Tool-Calling Loop Starving Worker Pool
critical severityApplication throughput plummeted from 120 RPS to 3 RPS. Worker threads were pegged at 100% CPU. Individual requests took over 120 seconds before timing out.
An autonomous customer support agent entered an infinite tool-calling loop: Tool A failed with a validation warning, which prompted the LLM to call Tool B, which redirected back to Tool A. Because there was no loop detector or max-iteration metric, the agent executed 85 sequential LLM and tool calls per request.
Add span attributes for `agent.iteration_count` and `agent.tool_call_depth`. Enforce a strict max iteration limit (e.g., 6) and emit an alert metric if an agent exceeds 4 tool calls.
# Agent Observability & Circuit Breaking Loop Guard
from opentelemetry import trace
from prometheus_client import Counter, Histogram
AGENT_TOOL_CALLS = Counter(
"ai_agent_tool_calls_total",
"Total tool invocations by agent",
["agent_name", "tool_name", "status"]
)
AGENT_ITERATION_DEPTH = Histogram(
"ai_agent_iterations_per_request",
"Number of iterations before agent resolution",
buckets=[1, 2, 3, 4, 5, 8, 10, 15]
)
MAX_ALLOWED_ITERATIONS = 6
async def run_agent_loop(user_query: str):
tracer = trace.get_tracer("ai.agent")
with tracer.start_as_current_span("agent_execution_cycle") as span:
iteration = 0
while iteration < MAX_ALLOWED_ITERATIONS:
iteration += 1
span.add_event(f"Iteration_{iteration}_start")
tool_decision = await llm_decide_action(user_query)
if tool_decision.is_final_answer:
AGENT_ITERATION_DEPTH.observe(iteration)
span.set_attribute("agent.total_iterations", iteration)
return tool_decision.answer
# Track tool execution
AGENT_TOOL_CALLS.labels(
agent_name="support_v1",
tool_name=tool_decision.tool_name,
status="invoked"
).inc()
await execute_tool(tool_decision.tool_name, tool_decision.args)
# Circuit breaker triggered!
span.set_status(trace.Status(trace.StatusCode.ERROR, "Max iterations exceeded"))
span.set_attribute("agent.loop_broken", True)
return "I apologize, but I was unable to resolve your request in a timely manner."Incident #7: OpenTelemetry Collector Tail-Sampling Dropping 99% of Error Traces
high severityEngineers investigating production 500 errors could not find any corresponding traces in Grafana Tempo. Only successful 200 OK traces were visible.
The OpenTelemetry Collector `tail_sampling` processor was misconfigured with a naive probabilistic sampler (`sampling_percentage: 1%`) placed BEFORE the error status filter, randomly dropping 99% of all traces including the rare fatal error traces.
Reorder tail sampling policies: ALWAYS evaluate status code rules first (`status_code: ERROR` keep 100%), then apply probabilistic sampling (10%) only to successful `OK` spans.
# otel-collector-config.yaml (CORRECT Sampling Pipeline)
processors:
tail_sampling:
decision_wait: 10s
num_traces: 10000
expected_new_traces_per_sec: 2000
policies:
# Policy 1: Always keep 100% of traces that contain errors
- name: keep_all_errors
type: status_code
status_code: { status_codes: [ ERROR ] }
# Policy 2: Keep 100% of slow traces (> 3 seconds)
- name: keep_slow_traces
type: latency
latency: { threshold_ms: 3000 }
# Policy 3: Sample only 5% of healthy, fast traces to save disk space
- name: sample_normal_traffic
type: probabilistic
probabilistic: { sampling_percentage: 5.0 }
service:
pipelines:
traces:
receivers: [otlp]
processors: [memory_limiter, tail_sampling, batch]
exporters: [otlp/tempo]Incident #8: GPU Memory Fragmentation Stall Missing from Host Metrics
high severityModel serving pods running vLLM reported GPU Utilization at only 35% on standard Prometheus node-exporter graphs, yet client inference latency degraded by 400% and requests began queuing.
Standard Linux `node_exporter` only monitors host CPU and RAM. The team had not deployed `dcgm-exporter` (NVIDIA Data Center GPU Manager) or vLLM native metrics. The GPU VRAM was 98% fragmented due to non-continuous KV-cache allocations under extreme prompt length variations, causing the vLLM scheduler to pause token generation.
Deploy NVIDIA `dcgm-exporter` and scrape `/metrics` directly from vLLM model serving pods to track `vllm:num_requests_waiting`, `vllm:gpu_cache_usage_factor`, and `DCGM_FI_DEV_GPU_UTIL`.
# Prometheus Scrape Config for AI Model Serving (prometheus.yml)
scrape_configs:
# 1. Scrape vLLM Native Metrics (KV cache, queue depths)
- job_name: 'vllm-model-server'
metrics_path: '/metrics'
static_configs:
- targets: ['vllm-service.inference.svc:8000']
metric_relabel_configs:
- source_labels: [__name__]
regex: '(vllm:.*)'
action: keep
# 2. Scrape NVIDIA DCGM Exporter for Hardware Accelerators
- job_name: 'nvidia-dcgm'
static_configs:
- targets: ['dcgm-exporter.monitoring.svc:9400']
# Critical Alert in Alertmanager:
# expr: vllm:gpu_cache_usage_factor > 0.95 and vllm:num_requests_waiting > 10
# for: 2m
# summary: "vLLM KV Cache exhausted! Requests are stalling in scheduler queue."What You Should Know Now & Assessment Quiz
Verify your competency across cloud-native observability, OpenTelemetry semantic conventions, Prometheus PromQL, and production AI incident triage.
Production AI Observability Competency Checklist
traceparent headers link parent and child spans across distributed microservices.gen_ai.system, gen_ai.usage.input_tokens, gen_ai.server.time_to_first_token).for: 5m) to eliminate alert fatigue.Production Observability & SRE Knowledge Assessment
Test your mastery of OpenTelemetry standards, Prometheus alerting rules, latency math, and production incident triaging.