AI Observability: Seeing Inside the Black Box
Back to all articles
Infrastructure
20 min read11 min read

AI Observability: Seeing Inside the Black Box

Comprehensive monitoring for AI systems. Covers tracing, metrics, logging, alerting, and debugging strategies for production LLM applications.

Debasish Maji
Debasish Maji
AI Engineering Lead
May 5, 2026
ObservabilityMonitoringTracingDebuggingProduction

Why AI Observability is Different

Traditional observability tells you what happened. AI observability tells you why it happened and whether it was good.

LLMs are non-deterministic, expensive, and opaque. You need to see not just errors and latency, but quality, cost, and behavior patterns. This guide covers comprehensive observability for AI systems.

•••

The Three Pillars + One

Diagram
flowchart TB subgraph Pillars["Observability Pillars"] L[Logs<br/>What happened] M[Metrics<br/>How much] T[Traces<br/>Where it flowed] E[Evaluations<br/>How good] end subgraph Sources["Data Sources"] API[API Gateway] LLM[LLM Calls] DB[Database] User[User Actions] end subgraph Outputs["Outputs"] D[Dashboards] A[Alerts] R[Reports] I[Investigations] end API --> L & M & T LLM --> L & M & T & E DB --> L & M & T User --> L & E L & M & T & E --> D M --> A E --> R L & T --> I style E fill:#14b8a6,color:#fff style A fill:#ef4444,color:#fff

Traditional Pillars

PillarWhat It CapturesAI Application
LogsEvents, errorsPrompts, responses, errors
MetricsAggregated measurementsLatency, cost, throughput
TracesRequest flowEnd-to-end AI pipeline

The Fourth Pillar: Evaluations

ComponentPurposeExample
Quality metricsOutput correctnessAccuracy, relevance
User feedbackActual satisfactionThumbs up/down
Automated evalsContinuous testingLLM-as-judge
•••

Logging for AI

What to Log

LevelDataWhen
AlwaysRequest ID, timestamp, model, tokens, latencyEvery request
DebugFull prompt, full responseDevelopment
AuditUser input, output, decisionsCompliance
ErrorStack trace, contextFailures

Log Schema

FieldTypePurpose
request_idstringTrace correlation
timestampISO 8601Timeline
user_idstringUser attribution
session_idstringSession grouping
modelstringModel used
prompt_hashstringPrompt version
tokens_inintegerInput size
tokens_outintegerOutput size
latency_msintegerPerformance
statusenumSuccess/failure
errorobjectError details

Sensitive Data Handling

Data TypeStrategyImplementation
PII in promptsRedactionRegex patterns
CredentialsNever logPre-filter
User contentHash or encryptAt-rest encryption
Model outputsConditionalBased on sensitivity

Log Retention

Log TypeRetentionReasoning
Operational30 daysDebugging
Audit1-7 yearsCompliance
Quality90 daysAnalysis
Error90 daysPatterns
Typescript
interface LLMRequestLog {
  request_id: string;
  timestamp: string;
  user_id: string;
  session_id: string;
  model: string;
  prompt_hash: string;
  tokens_in: number;
  tokens_out: number;
  latency_ms: number;
  ttft_ms: number;
  status: 'success' | 'error' | 'timeout' | 'rate_limited';
  cost_usd: number;
  error?: {
    code: string;
    message: string;
    retryable: boolean;
  };
  metadata: Record<string, any>;
}

class AILogger {
  private sensitivePatterns = [
    /\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Z|a-z]{2,}\b/g, // Email
    /\b\d{3}-\d{2}-\d{4}\b/g, // SSN
    /\b\d{16}\b/g, // Credit card
  ];
  
  async logRequest(log: LLMRequestLog, prompt?: string, response?: string) {
    // Redact sensitive data
    const safePrompt = prompt ? this.redact(prompt) : undefined;
    const safeResponse = response ? this.redact(response) : undefined;
    
    // Hash prompt for versioning without storing full text
    const promptHash = await this.hashContent(prompt || '');
    
    const enrichedLog: LLMRequestLog = {
      ...log,
      prompt_hash: promptHash,
    };
    
    // Log to appropriate destinations
    await Promise.all([
      this.logToOperational(enrichedLog),
      log.status === 'error' && this.logToErrors(enrichedLog, safePrompt, safeResponse),
      this.shouldAudit(log) && this.logToAudit(enrichedLog, safePrompt, safeResponse),
    ]);
    
    // Update metrics
    this.metrics.recordRequest(enrichedLog);
  }
  
  private redact(text: string): string {
    let redacted = text;
    for (const pattern of this.sensitivePatterns) {
      redacted = redacted.replace(pattern, '[REDACTED]');
    }
    return redacted;
  }
  
  private shouldAudit(log: LLMRequestLog): boolean {
    // Audit high-risk operations
    return log.metadata?.is_decision === true || 
           log.metadata?.affects_user_data === true;
  }
}
•••

Metrics for AI

Core Metrics

MetricDescriptionAlert Threshold
Request rateRequests per secondAnomaly detection
Error rateFailures / totalOver 2%
Latency p50Median response timeOver 2x baseline
Latency p95Tail latencyOver 3x baseline
Latency p99Worst caseOver 5x baseline

AI-Specific Metrics

MetricDescriptionTarget
Token usageTokens per requestBudget limit
Cost per requestDollar amountBudget limit
TTFTTime to first tokenUnder 500ms
Model distributionRequests by modelExpected ratio
Cache hit rateCached / totalOver 30%

Quality Metrics

MetricDescriptionTarget
User ratingThumbs up ratioOver 85%
Regeneration rateRetry requestsUnder 15%
Edit rateUser modificationsUnder 30%
Task completionSuccessful flowsOver 90%
Hallucination rateFactual errorsUnder 5%

Business Metrics

MetricDescriptionTarget
Cost per userTotal cost / usersBudget
Value deliveredTasks completedGrowing
EngagementSessions per userGrowing
ConversionFeature adoptionTarget %
•••

Tracing AI Pipelines

Trace Components

ComponentWhat to CapturePurpose
SpanSingle operationIndividual steps
TraceFull request flowEnd-to-end view
ContextMetadataCorrelation
EventsNotable occurrencesDebugging

AI Pipeline Spans

SpanDurationKey Attributes
Request receivedStartUser ID, input
Preprocessing10-50msTransformations
Retrieval50-200msQuery, results count
Prompt construction5-20msTemplate, tokens
LLM call500-5000msModel, tokens, cost
Post-processing10-50msTransformations
Response sentEndOutput size
Typescript
import { trace, context, SpanStatusCode } from '@opentelemetry/api';

const tracer = trace.getTracer('ai-service');

async function handleAIRequest(request: AIRequest) {
  return tracer.startActiveSpan('ai.request', async (span) => {
    try {
      span.setAttributes({
        'user.id': request.userId,
        'session.id': request.sessionId,
        'input.length': request.input.length,
      });
      
      // Preprocessing span
      const processed = await tracer.startActiveSpan('ai.preprocess', async (prepSpan) => {
        const result = await preprocess(request.input);
        prepSpan.setAttributes({ 'tokens.count': result.tokenCount });
        return result;
      });
      
      // Retrieval span
      const docs = await tracer.startActiveSpan('ai.retrieval', async (retSpan) => {
        const results = await vectorSearch(processed.embedding);
        retSpan.setAttributes({
          'retrieval.query': processed.query,
          'retrieval.results_count': results.length,
          'retrieval.top_score': results[0]?.score || 0,
        });
        return results;
      });
      
      // LLM call span - the most important one
      const response = await tracer.startActiveSpan('ai.llm_call', async (llmSpan) => {
        const startTime = Date.now();
        
        const result = await callLLM({
          model: request.model,
          prompt: buildPrompt(processed, docs),
        });
        
        const latency = Date.now() - startTime;
        
        llmSpan.setAttributes({
          'llm.model': request.model,
          'llm.tokens_in': result.usage.promptTokens,
          'llm.tokens_out': result.usage.completionTokens,
          'llm.ttft_ms': result.timeToFirstToken,
          'llm.latency_ms': latency,
          'llm.cost_usd': calculateCost(request.model, result.usage),
          'llm.finish_reason': result.finishReason,
        });
        
        return result;
      });
      
      span.setStatus({ code: SpanStatusCode.OK });
      return response;
      
    } catch (error) {
      span.setStatus({ code: SpanStatusCode.ERROR, message: error.message });
      span.recordException(error);
      throw error;
    } finally {
      span.end();
    }
  });
}

Trace Sampling

StrategyRateUse Case
All traces100%Development
Head sampling10-50%Low volume
Tail samplingErrors + slowHigh volume
AdaptiveDynamicProduction

Distributed Tracing

ChallengeSolution
Cross-service correlationTrace context propagation
Async operationsParent span linking
External API callsSpan wrapping
Background jobsJob ID correlation
•••

Alerting Strategy

Alert Categories

CategoryUrgencyResponse Time
CriticalPage immediatelyUnder 5 min
HighPage during hoursUnder 1 hour
MediumTicketUnder 24 hours
LowReview weeklyUnder 1 week

Alert Definitions

AlertConditionSeverity
Error rate spikeOver 5% for 5 minCritical
Latency spikep95 over 5x for 10 minHigh
Cost anomalyOver 2x daily averageHigh
Model unavailableProvider errors over 50%Critical
Quality dropUser rating under 70%Medium

Alert Best Practices

PracticeWhyImplementation
Actionable alertsReduce fatigueClear runbook
Grouped alertsReduce noiseCorrelation rules
Severity levelsPrioritizationTiered routing
Auto-resolutionReduce toilSelf-healing

On-Call Runbooks

AlertFirst ResponseEscalation
Error spikeCheck logs, recent deploysRollback
Latency spikeCheck model providerFallback model
Cost spikeCheck usage patternsRate limit
Quality dropCheck prompt changesRevert
•••

Debugging AI Issues

Debugging Workflow

StepActionTools
1. IdentifyFind the problemAlerts, user reports
2. ReproduceRecreate consistentlyRequest replay
3. IsolateFind the componentTracing
4. AnalyzeUnderstand root causeLogs, context
5. FixImplement solutionCode change
6. VerifyConfirm resolutionTesting

Common Issues

IssueSymptomsInvestigation
HallucinationWrong factsCheck retrieval, prompt
Slow responseHigh latencyCheck model, tokens
Format errorInvalid outputCheck prompt, examples
RefusalModel won't answerCheck safety filters
InconsistencyVariable outputCheck temperature, prompt

Debugging Tools

ToolPurposeWhen to Use
Request replayReproduce issuesSpecific failures
Prompt playgroundTest variationsPrompt debugging
A/B comparisonCompare approachesOptimization
Shadow modeSafe testingNew changes
•••

Observability Stack

Components

ComponentPurposeExamples
Log aggregationCollect, searchDatadog, Splunk
MetricsTime-series dataPrometheus, Datadog
TracingRequest flowJaeger, Honeycomb
AI-specificLLM monitoringLangSmith, Helicone
DashboardsVisualizationGrafana, Datadog

AI-Specific Tools

ToolSpecialtyBest For
LangSmithLangChain tracingLangChain apps
HeliconeLLM gatewayCost tracking
Weights & BiasesML trackingModel training
ArizeModel monitoringDrift detection

Build vs Buy

ComponentBuildBuyRecommendation
Basic loggingEasyCheapBuy
MetricsMediumCheapBuy
TracingHardMediumBuy
AI-specificHardMediumEvaluate
Custom dashboardsMediumIncludedCustomize
•••

Dashboard Design

Executive Dashboard

PanelMetricsAudience
HealthError rate, uptimeLeadership
CostDaily/monthly spendFinance
UsageActive users, requestsProduct
QualityUser satisfactionLeadership

Engineering Dashboard

PanelMetricsPurpose
Latencyp50, p95, p99Performance
ErrorsRate, types, top errorsDebugging
ModelsDistribution, performanceOptimization
RetrievalHit rate, relevanceRAG health

On-Call Dashboard

PanelMetricsPurpose
Active alertsCurrent issuesTriage
Recent changesDeploys, configCorrelation
Error trendsLast hourPatterns
Quick actionsRollback, restartResponse
•••

Key Takeaways

  1. 1Log everything, sample wisely - Capture full context for debugging, but sample for storage.
  1. 2AI needs quality metrics - Error rate is not enough. Track ratings, regenerations, and edits.
  1. 3Trace the full pipeline - Every step from input to output should be traceable.
  1. 4Alert on business impact - Cost spikes and quality drops matter as much as errors.
  1. 5Invest in debugging tools - Request replay and prompt playgrounds save hours.
  1. 6Dashboards for each audience - Executives, engineers, and on-call need different views.

You cannot improve what you cannot measure. In AI systems, measurement is especially hard because quality is subjective. Build observability that captures not just what happened, but whether it was good.

Found this helpful?

Share it with others who might benefit

TweetShare

Related articles