Model Observability and Logging
Model Observability and Logging
Learn how to make production Machine Learning systems understandable through structured logs, metrics, distributed traces, request correlation, model-version metadata, feature diagnostics, prediction monitoring, dashboards, alerts, privacy controls, incident response, and OpenTelemetry instrumentation.
Introduction
A deployed Machine Learning model is part of a larger production system. A prediction request may pass through an API gateway, authentication service, feature store, inference service, model runtime, database, message broker, and downstream business application.
When a prediction is slow, incorrect, unavailable, or inconsistent, an operations team needs more than a generic error message. The team must be able to determine which model version ran, which application version handled the request, where time was spent, which dependency failed, whether feature values were missing or stale, and whether similar requests were affected.
Model observability is the ability to understand the internal state and behavior of an ML system by examining the telemetry it produces. Logging is one important part of observability, but complete observability also includes metrics, traces, events, version metadata, model-quality signals, and business outcomes.
Learning Objectives
By the End of This Lesson
- Define model observability
- Distinguish monitoring from observability
- Explain the roles of logs, metrics, and traces
- Understand structured logging
- Create correlation and trace identifiers
- Record model and application versions safely
- Log inference lifecycle events
- Avoid logging sensitive prediction payloads
- Define operational ML metrics
- Define data and feature-quality metrics
- Track prediction distributions
- Track delayed model-quality metrics
- Create distributed traces for inference requests
- Instrument a FastAPI service with OpenTelemetry
- Correlate logs with traces
- Understand telemetry collection and export
- Design observability dashboards
- Create actionable alerts
- Use telemetry during incident investigation
- Create an observability-readiness checklist
Prerequisites
Recommended Knowledge
- Python programming
- FastAPI or Flask fundamentals
- REST APIs and HTTP status codes
- Model deployment
- Model monitoring
- Feature stores
- JSON fundamentals
- Docker fundamentals
- Distributed-system fundamentals
- Basic incident-management concepts
Example Packages
- FastAPI
- Uvicorn
- OpenTelemetry API
- OpenTelemetry SDK
- OpenTelemetry OTLP exporter
- OpenTelemetry FastAPI instrumentation
- Prometheus client
python -m pip install \
fastapi \
uvicorn \
opentelemetry-api \
opentelemetry-sdk \
opentelemetry-exporter-otlp \
opentelemetry-instrumentation-fastapi \
prometheus-client
What Is Observability?
Observability is an engineering property that allows an operator to understand a system's internal behavior using the signals emitted by that system.
| Signal | Main Question |
|---|---|
| Logs | What event occurred? |
| Metrics | How much, how often, or how long? |
| Traces | Where did this request travel and where was time spent? |
| Profiles | Which code paths consumed CPU or memory? |
| Events | What deployment, configuration, or infrastructure change occurred? |
| ML Context | Which model, feature contract, threshold, and data conditions were involved? |
Monitoring vs Observability
| Area | Monitoring | Observability |
|---|---|---|
| Primary Purpose | Tracks known indicators and conditions | Supports investigation of known and unexpected behavior |
| Typical Question | Is the error rate above the defined threshold? | Why did errors increase for this model version? |
| Primary Outputs | Dashboards, thresholds, and alerts | Correlated logs, metrics, traces, events, and context |
| Scope | Predefined health and performance measures | Deep investigation across system components |
| ML Requirement | Tracks data, prediction, quality, and business signals | Explains how those signals relate to requests, models, features, and dependencies |
Layers of ML Observability
| Layer | Examples |
|---|---|
| Infrastructure | CPU, memory, storage, accelerator, network, and restarts |
| Application | Availability, requests, HTTP errors, latency, and exceptions |
| Data | Schema failures, missing values, invalid ranges, and unexpected categories |
| Features | Freshness, retrieval latency, coverage, and offline-online consistency |
| Predictions | Scores, classes, uncertainty, abstention, and threshold rates |
| Model Quality | Precision, recall, calibration, residuals, and drift |
| Responsible Use | Relevant group behavior, overrides, complaints, and harmful outcomes |
| Business | Task completion, value, cost, fallback use, and operational impact |
Logs
A log is a timestamped record describing an event that occurred within an application, service, pipeline, or infrastructure component.
| Log Category | Example Event |
|---|---|
| Lifecycle | Model loaded successfully |
| Request | Prediction request accepted |
| Validation | Unsupported category detected |
| Feature | Required feature value unavailable |
| Inference | Prediction completed |
| Dependency | Feature-store request timed out |
| Security | Unauthorized request rejected |
| Deployment | Application version started |
| Incident | Fallback mode activated |
Structured Logging
Structured logging records events as named fields rather than unstructured text. This makes logs easier to search, filter, aggregate, correlate, validate, and process automatically.
{
"timestamp": "recorded-event-time",
"level": "ERROR",
"event_name": "feature_retrieval_failed",
"service_name": "customer-churn-api",
"application_version": "2.1.0",
"model_name": "customer-churn-classifier",
"model_version": "1.0.0",
"request_id": "generated-request-id",
"trace_id": "generated-trace-id",
"dependency": "feature-store",
"error_type": "timeout",
"duration_ms": 1200,
"fallback_applied": true
}
Log Levels
| Level | General Purpose | ML Example |
|---|---|---|
| DEBUG | Detailed development or diagnostic information | Validated feature names and component transitions |
| INFO | Expected lifecycle and operational events | Model version loaded successfully |
| WARNING | Unexpected condition that did not stop processing | Optional feature missing and approved fallback applied |
| ERROR | Failed operation requiring investigation | Prediction failed because a required dependency was unavailable |
| CRITICAL | Severe condition threatening safe service operation | Model integrity verification failed during startup |
ERROR for expected invalid user input or
INFO for a condition that compromises safe inference.
Create a JSON Log Formatter
import json
import logging
from datetime import datetime
from datetime import timezone
class JsonLogFormatter(
logging.Formatter
):
def format(
self,
record
):
log_record = {
"timestamp": (
datetime.now(
timezone.utc
).isoformat()
),
"level": record.levelname,
"logger": record.name,
"message": (
record.getMessage()
)
}
approved_fields = [
"event_name",
"service_name",
"application_version",
"model_name",
"model_version",
"request_id",
"trace_id",
"span_id",
"schema_version",
"duration_ms",
"status",
"error_type"
]
for field_name in approved_fields:
field_value = getattr(
record,
field_name,
None
)
if field_value is not None:
log_record[
field_name
] = field_value
if record.exc_info:
log_record[
"exception"
] = self.formatException(
record.exc_info
)
return json.dumps(
log_record,
ensure_ascii=False
)
Configure Structured Logging
import logging
import sys
def configure_logging():
log_handler = logging.StreamHandler(
sys.stdout
)
log_handler.setFormatter(
JsonLogFormatter()
)
root_logger = logging.getLogger()
root_logger.handlers.clear()
root_logger.addHandler(
log_handler
)
root_logger.setLevel(
logging.INFO
)
configure_logging()
logger = logging.getLogger(
"customer-churn-api"
)
Log Model Startup
from pathlib import Path
import joblib
def load_model_artifact(
model_path,
model_name,
model_version
):
model_path = Path(
model_path
)
try:
model = joblib.load(
model_path
)
logger.info(
"Model loaded successfully.",
extra={
"event_name": "model_loaded",
"service_name": (
"customer-churn-api"
),
"model_name": model_name,
"model_version": model_version,
"status": "success"
}
)
return model
except Exception:
logger.critical(
"Model loading failed.",
extra={
"event_name": (
"model_loading_failed"
),
"service_name": (
"customer-churn-api"
),
"model_name": model_name,
"model_version": model_version,
"status": "failed"
},
exc_info=True
)
raise
Correlation Identifiers
Correlation identifiers connect telemetry produced by different components while processing one logical request.
| Identifier | Purpose |
|---|---|
| Request ID | Identifies one API or application request |
| Trace ID | Identifies an end-to-end distributed transaction |
| Span ID | Identifies one operation within a trace |
| Prediction ID | Identifies one model prediction for later outcome joining |
| Pipeline Run ID | Identifies one training or monitoring-pipeline execution |
| Experiment Run ID | Links production lineage to the tracked training run |
Create Request Middleware
import time
import uuid
from fastapi import FastAPI
from fastapi import Request
app = FastAPI(
title="Customer Churn API",
version="2.1.0"
)
@app.middleware(
"http"
)
async def request_observability(
request: Request,
call_next
):
request_id = (
request.headers.get(
"X-Request-ID"
)
or str(
uuid.uuid4()
)
)
request.state.request_id = (
request_id
)
started_at = (
time.perf_counter()
)
try:
response = await call_next(
request
)
duration_ms = (
time.perf_counter()
-
started_at
) * 1000
logger.info(
"Request completed.",
extra={
"event_name": (
"request_completed"
),
"service_name": (
"customer-churn-api"
),
"application_version": (
"2.1.0"
),
"request_id": request_id,
"duration_ms": round(
duration_ms,
3
),
"status": str(
response.status_code
)
}
)
response.headers[
"X-Request-ID"
] = request_id
return response
except Exception:
duration_ms = (
time.perf_counter()
-
started_at
) * 1000
logger.error(
"Request failed.",
extra={
"event_name": (
"request_failed"
),
"service_name": (
"customer-churn-api"
),
"application_version": (
"2.1.0"
),
"request_id": request_id,
"duration_ms": round(
duration_ms,
3
),
"status": "failed"
},
exc_info=True
)
raise
What Must Not Be Logged?
Prohibited or High-Risk Content
- Passwords
- Access tokens
- API keys
- Private cryptographic keys
- Session cookies
- Complete authorization headers
- Unnecessary personal information
- Complete confidential prediction payloads
- Raw documents submitted for inference
- Sensitive feature values without approved need
- Database connection strings
- Internal stack details exposed to end users
Redact Sensitive Fields
SENSITIVE_KEYS = {
"password",
"access_token",
"api_key",
"authorization",
"cookie",
"secret",
"private_key"
}
def redact_value(
value
):
if isinstance(
value,
dict
):
return {
key: (
"[REDACTED]"
if key.lower()
in SENSITIVE_KEYS
else redact_value(
nested_value
)
)
for key, nested_value
in value.items()
}
if isinstance(
value,
list
):
return [
redact_value(
item
)
for item in value
]
return value
Metrics
Metrics are numerical measurements aggregated over time. They are efficient for dashboards, thresholds, trend analysis, service-level objectives, and alerts.
| Metric Type | Purpose | Example |
|---|---|---|
| Counter | Tracks an accumulated total | Total prediction requests |
| Gauge | Tracks a value that can increase or decrease | Current queue depth |
| Histogram | Tracks the distribution of observations | Inference latency |
| Summary | Summarizes observations according to implementation behavior | Request duration |
Operational Metrics for ML APIs
| Metric | Purpose |
|---|---|
| Request Count | Tracks inference traffic |
| Success Rate | Tracks completed requests |
| Error Rate | Tracks failed requests |
| Validation-Failure Rate | Tracks rejected input contracts |
| Latency Distribution | Tracks service and inference duration |
| Throughput | Tracks completed predictions per interval |
| Queue Depth | Tracks waiting asynchronous work |
| Fallback Rate | Tracks use of alternate behavior |
| Dependency-Failure Rate | Tracks feature, database, or external-service failures |
ML-Specific Metrics
| Metric Area | Examples |
|---|---|
| Input Quality | Missing values, invalid ranges, and unsupported categories |
| Feature Store | Coverage, freshness, retrieval latency, and stale-value rate |
| Prediction | Mean score, class distribution, selection rate, and uncertainty |
| Drift | Feature, prediction, label, and attribution drift |
| Delayed Quality | Precision, recall, F1, calibration, and regression error |
| Group Behavior | Quality and selection measures across relevant groups |
| Business Outcome | Task completion, intervention success, overrides, and complaints |
| Cost | Compute, storage, request, feature retrieval, and downstream use |
Instrument Metrics with Prometheus
from prometheus_client import Counter
from prometheus_client import Histogram
prediction_requests = Counter(
"ml_prediction_requests_total",
"Total prediction requests",
[
"model_name",
"model_version",
"status"
]
)
prediction_latency = Histogram(
"ml_prediction_duration_seconds",
"Prediction request duration",
[
"model_name",
"model_version"
]
)
validation_failures = Counter(
"ml_validation_failures_total",
"Rejected prediction inputs",
[
"schema_version",
"failure_type"
]
)
Record Prediction Metrics
import time
def run_observed_prediction(
model,
feature_frame,
model_name,
model_version
):
started_at = (
time.perf_counter()
)
try:
probabilities = model.predict_proba(
feature_frame
)[:, 1]
prediction_requests.labels(
model_name=model_name,
model_version=model_version,
status="success"
).inc()
return probabilities
except Exception:
prediction_requests.labels(
model_name=model_name,
model_version=model_version,
status="failed"
).inc()
raise
finally:
duration_seconds = (
time.perf_counter()
-
started_at
)
prediction_latency.labels(
model_name=model_name,
model_version=model_version
).observe(
duration_seconds
)
Traces
A distributed trace represents the path of one request across services and operations. A trace consists of spans. Each span represents one timed operation such as authentication, feature retrieval, preprocessing, model inference, or persistence.
| Trace Concept | Meaning |
|---|---|
| Trace | The complete end-to-end request path |
| Span | One operation inside the trace |
| Parent Span | The operation that initiated another operation |
| Span Attribute | Searchable metadata describing an operation |
| Span Event | A timestamped event occurring within a span |
| Span Status | The completion state of an operation |
Configure OpenTelemetry Tracing
from opentelemetry import trace
from opentelemetry.exporter.otlp.proto.grpc.trace_exporter import OTLPSpanExporter
from opentelemetry.sdk.resources import Resource
from opentelemetry.sdk.trace import TracerProvider
from opentelemetry.sdk.trace.export import BatchSpanProcessor
resource = Resource.create(
{
"service.name": (
"customer-churn-api"
),
"service.version": "2.1.0",
"deployment.environment.name": (
"development"
)
}
)
trace_provider = TracerProvider(
resource=resource
)
trace_exporter = OTLPSpanExporter(
endpoint=(
"replace-with-approved-otlp-endpoint"
),
insecure=False
)
trace_provider.add_span_processor(
BatchSpanProcessor(
trace_exporter
)
)
trace.set_tracer_provider(
trace_provider
)
tracer = trace.get_tracer(
"customer-churn-api"
)
Instrument FastAPI
from opentelemetry.instrumentation.fastapi import FastAPIInstrumentor
FastAPIInstrumentor.instrument_app(
app
)
Create Custom ML Spans
from opentelemetry.trace import Status
from opentelemetry.trace import StatusCode
def traced_prediction(
model,
feature_frame,
model_name,
model_version,
feature_service_version
):
with tracer.start_as_current_span(
"ml.predict"
) as prediction_span:
prediction_span.set_attribute(
"ml.model.name",
model_name
)
prediction_span.set_attribute(
"ml.model.version",
model_version
)
prediction_span.set_attribute(
"ml.feature_service.version",
feature_service_version
)
prediction_span.set_attribute(
"ml.batch_size",
len(
feature_frame
)
)
try:
probabilities = model.predict_proba(
feature_frame
)[:, 1]
prediction_span.set_status(
Status(
StatusCode.OK
)
)
return probabilities
except Exception as error:
prediction_span.record_exception(
error
)
prediction_span.set_status(
Status(
StatusCode.ERROR,
str(
error
)
)
)
raise
Trace Feature Retrieval
def retrieve_features_with_trace(
feature_store,
feature_service,
entity_rows
):
with tracer.start_as_current_span(
"feature_store.get_online_features"
) as feature_span:
feature_span.set_attribute(
"feature_store.service",
feature_service
)
feature_span.set_attribute(
"feature_store.entity_count",
len(
entity_rows
)
)
try:
result = (
feature_store.get_online_features(
features=feature_service,
entity_rows=entity_rows
)
)
response = result.to_dict()
feature_span.set_attribute(
"feature_store.status",
"success"
)
return response
except Exception as error:
feature_span.set_attribute(
"feature_store.status",
"failed"
)
feature_span.record_exception(
error
)
raise
Correlate Logs with Traces
from opentelemetry import trace
def get_trace_context():
current_span = (
trace.get_current_span()
)
span_context = (
current_span.get_span_context()
)
if not span_context.is_valid:
return {
"trace_id": None,
"span_id": None
}
return {
"trace_id": format(
span_context.trace_id,
"032x"
),
"span_id": format(
span_context.span_id,
"016x"
)
}
trace_context = (
get_trace_context()
)
logger.info(
"Prediction completed.",
extra={
"event_name": (
"prediction_completed"
),
"service_name": (
"customer-churn-api"
),
"model_name": (
"customer-churn-classifier"
),
"model_version": "1.0.0",
"trace_id": (
trace_context[
"trace_id"
]
),
"span_id": (
trace_context[
"span_id"
]
),
"status": "success"
}
)
OpenTelemetry Collector
An OpenTelemetry Collector can receive telemetry, process it, and export it to one or more approved observability destinations.
receivers:
otlp:
protocols:
grpc:
http:
processors:
batch:
exporters:
otlp:
endpoint: replace-with-approved-backend
tls:
insecure: false
service:
pipelines:
traces:
receivers:
- otlp
processors:
- batch
exporters:
- otlp
Telemetry Sampling
Sampling controls which trace or event records are retained. It can reduce telemetry volume and cost, but insufficient sampling may remove rare failures or important operational evidence.
| Approach | General Purpose |
|---|---|
| Head Sampling | Decides near the beginning of the request |
| Tail Sampling | Decides after more of the trace is available |
| Probability Sampling | Retains a configured proportion of traces |
| Error-Focused Sampling | Prioritizes failed operations |
| Latency-Focused Sampling | Prioritizes unusually slow traces |
| Version-Aware Sampling | Increases evidence for a newly released candidate |
Metric Cardinality
Metric cardinality is the number of distinct label combinations associated with a metric. Unbounded labels can make monitoring expensive and difficult to operate.
| Suitable Bounded Label | High-Cardinality Label to Avoid |
|---|---|
| Model version | Request ID |
| Environment | Customer ID |
| Response status | Raw error message |
| Error category | Feature vector |
| Endpoint name | Complete URL with unique query values |
| Deployment region | Prediction ID |
Observability Dashboard
| Dashboard Section | Suggested Content |
|---|---|
| Release Context | Model, application, schema, feature-service, and threshold versions |
| Traffic | Requests, throughput, batch size, and endpoint usage |
| Reliability | Availability, errors, retries, fallbacks, and dependency health |
| Latency | P50, P90, P95, and P99 request and inference duration |
| Infrastructure | CPU, memory, accelerators, replicas, and restarts |
| Input Quality | Schema failures, missing values, invalid ranges, and categories |
| Features | Retrieval latency, coverage, freshness, and stale values |
| Predictions | Score distribution, positive rate, uncertainty, and abstention |
| Model Quality | Delayed quality, calibration, drift, and relevant group metrics |
| Business | Task success, override rate, complaints, cost, and fallback use |
| Incidents | Active alerts, owners, severity, response, and recovery status |
Actionable Alerts
Every Important Alert Should Define
- Metric or log condition
- Affected service and model version
- Evaluation window
- Threshold
- Minimum sample size
- Persistence requirement
- Severity
- Responsible owner
- Notification channel
- Dashboard or query link
- Investigation runbook
- Fallback and rollback criteria
Reduce Alert Fatigue
Alert-Quality Controls
- Use appropriate warning and critical severities
- Require sufficient sample size
- Require persistence where appropriate
- Group correlated failures into one incident
- Suppress duplicate notifications
- Account for maintenance and controlled deployments
- Route alerts to responsible owners
- Review false positives and missed incidents
- Retire alerts that do not lead to action
- Increase evidence temporarily during canary releases
Incident Investigation
Validate the Alert
Confirm the signal, calculation, time window, sample size, deployment state, and alert configuration.
Identify the Scope
Determine affected endpoints, model versions, application versions, regions, users, entities, and time periods.
Inspect Representative Traces
Locate slow, failed, or unusual requests and identify the affected span or dependency.
Query Correlated Logs
Use trace, request, prediction, and deployment identifiers to retrieve related events.
Review Metrics
Compare errors, latency, resources, input quality, feature freshness, predictions, and dependencies.
Assess ML Impact
Review prediction distributions, drift, calibration, delayed outcomes, relevant groups, and business effects.
Mitigate and Recover
Correct data, scale resources, disable a dependency, use fallback, restrict traffic, or roll back as required.
Document and Improve
Record root cause, impact, response, recovery evidence, and observability improvements.
Fallback and Rollback Observability
| Signal | Purpose |
|---|---|
| Fallback Activation Count | Shows how often alternate behavior is used |
| Fallback Reason | Separates feature, model, dependency, and policy causes |
| Rollback Initiated | Records the start of restoration |
| Previous Version | Identifies the release being replaced |
| Restored Version | Identifies the approved release restored |
| Recovery Validation | Confirms health, latency, errors, and prediction consistency |
| Post-Rollback Monitoring | Verifies stability after restoration |
Test Logging Behavior
import json
import logging
def test_json_log_formatter():
formatter = JsonLogFormatter()
record = logging.LogRecord(
name="test-logger",
level=logging.INFO,
pathname=__file__,
lineno=1,
msg="Prediction completed.",
args=(),
exc_info=None
)
record.event_name = (
"prediction_completed"
)
record.model_version = "1.0.0"
record.request_id = (
"test-request-id"
)
serialized_log = formatter.format(
record
)
parsed_log = json.loads(
serialized_log
)
assert parsed_log[
"level"
] == "INFO"
assert parsed_log[
"event_name"
] == "prediction_completed"
assert parsed_log[
"model_version"
] == "1.0.0"
assert parsed_log[
"request_id"
] == "test-request-id"
Test Redaction
def test_sensitive_values_are_redacted():
payload = {
"username": "approved-test-user",
"password": "example-password",
"nested": {
"api_key": "example-key",
"status": "active"
}
}
redacted = redact_value(
payload
)
assert redacted[
"password"
] == "[REDACTED]"
assert redacted[
"nested"
][
"api_key"
] == "[REDACTED]"
assert redacted[
"nested"
][
"status"
] == "active"
python -m pytest -v
Telemetry Retention
| Telemetry | Retention Consideration |
|---|---|
| Application Logs | Operational diagnosis, security, privacy, volume, and incident needs |
| Audit Logs | Governance, privileged access, and required audit history |
| Metrics | Trend, capacity, drift, performance, and cost analysis |
| Traces | Sampling strategy, incident analysis, and storage cost |
| Prediction Metadata | Delayed-label joining, model quality, privacy, and investigation |
| Raw Payload Samples | Require explicit authorization, minimization, and restricted retention |
| Incident Evidence | Preserve according to incident and governance requirements |
Observability Cost Management
| Cost Driver | Possible Control |
|---|---|
| Verbose Logs | Use environment-appropriate levels and structured events |
| High-Cardinality Metrics | Use bounded labels and move unique context to traces or logs |
| Full Trace Retention | Apply approved sampling while preserving important failures |
| Long Retention | Apply tiered lifecycle and deletion policies |
| Duplicate Telemetry | Standardize collectors and avoid unnecessary repeated exports |
| Raw Payload Logging | Use safe aggregates and approved diagnostic samples |
| Unused Dashboards | Retire views without owners or operational value |
Responsible Model Observability
Responsible-Use Requirements
- Collect only telemetry required for an approved purpose
- Protect personal and confidential information
- Track model, schema, threshold, and feature versions
- Monitor relevant operating groups appropriately
- Use minimum sample sizes for group-level reporting
- Track complaints, corrections, and overrides
- Preserve human review for consequential actions
- Communicate uncertainty in diagnostic conclusions
- Audit access to sensitive telemetry
- Maintain tested fallback and rollback
- Retire telemetry fields without a valid operational purpose
- Retire models that are no longer appropriate
Common Observability Mistakes
Recommended Observability Workflow
Define Observability Objectives
Identify system risks, operational questions, service objectives, model risks, owners, and incident needs.
Design the Telemetry Schema
Standardize events, fields, model context, identifiers, severity, and redaction requirements.
Instrument Application Boundaries
Instrument HTTP requests, validation, features, inference, dependencies, persistence, and fallback paths.
Generate Structured Logs
Emit machine-readable events containing approved diagnostic context.
Record Metrics
Track traffic, errors, latency, resources, data quality, predictions, delayed quality, and business outcomes.
Create Distributed Traces
Connect service, feature, inference, and dependency spans under one trace context.
Collect and Export Safely
Use approved collectors, exporters, identities, encryption, redaction, sampling, and destinations.
Create Dashboards and Alerts
Present system and ML behavior with defined thresholds, ownership, and investigation links.
Test Incident Response
Verify telemetry discovery, trace correlation, fallback, rollback, escalation, and recovery validation.
Continuously Improve
Review missing evidence, noisy alerts, privacy exposure, cost, retention, and incident learnings.
Hands-On Practice
Create a Telemetry Inventory
List the logs, metrics, traces, events, versions, and business signals required for one deployed model.
Implement Structured Logging
Create a JSON formatter and record model startup, request completion, validation failure, and dependency failure events.
Add Request Correlation
Generate or validate a request ID and return it to the API caller.
Create Metrics
Track prediction requests, errors, validation failures, and inference duration using bounded labels.
Instrument FastAPI
Add OpenTelemetry HTTP instrumentation and confirm that request traces are generated.
Add Custom ML Spans
Trace feature retrieval, preprocessing, inference, and response construction.
Correlate Logs and Traces
Include trace and span identifiers in structured log events.
Run an Incident Exercise
Introduce a safe development-only feature timeout and use metrics, logs, and traces to identify the failing operation.
Knowledge Check
What is model observability?
Explain how telemetry helps operators understand the internal behavior of a production ML system.
How does observability differ from monitoring?
Explain predefined condition tracking compared with deeper investigation using correlated signals.
What is structured logging?
Explain how named fields make events searchable, filterable, aggregatable, and machine-readable.
What is a metric?
Explain a numerical measurement aggregated or observed over time.
What is a distributed trace?
Explain the end-to-end path of one request across multiple operations and services.
What is a span?
Explain one timed operation within a distributed trace.
Why correlate logs and traces?
Explain how trace identifiers connect event details with the complete request path.
Why avoid high-cardinality metric labels?
Explain how unique identifiers create excessive time-series combinations and operational cost.
Why should raw feature vectors not be added to traces?
Explain privacy, security, telemetry volume, and unnecessary exposure.
Why is application observability insufficient for ML quality?
Explain how a reliable API can still serve a degraded, inaccurate, unfair, or inappropriate model.
Observability Readiness Checklist
Confirm Before Production Use
- Observability objectives are documented
- Telemetry owners are identified
- Application logs use a structured format
- Log event names are standardized
- Log levels are used consistently
- Request identifiers are generated or validated
- Distributed trace propagation is configured
- Logs contain trace and span identifiers
- Every prediction identifies its model version
- Application and schema versions are recorded
- Feature-service and threshold versions are recorded
- Sensitive values are not logged unnecessarily
- Credential and token redaction is tested
- Raw payload logging is disabled by default
- Operational metrics are available
- ML-specific metrics are available
- Metric labels have bounded cardinality
- Latency percentiles are monitored
- Feature retrieval is traced and monitored
- Model inference has a dedicated trace span
- Dependency failures are identifiable
- An approved telemetry collector is configured
- Telemetry export uses secure transport
- Sampling preserves important failures
- Dashboards identify their population and owners
- Alerts include actionable context
- Critical alerts have runbooks
- Fallback and rollback events are observable
- Telemetry retention policies are documented
- Access to telemetry is auditable
- Logging and redaction logic is tested
- Incident-response exercises have been completed
- Responsible-use monitoring is included
Lesson Summary
| Observability Area | Main Outcome |
|---|---|
| Structured Logs | Provide searchable event-level diagnostic details |
| Metrics | Provide numerical trends, dashboards, and alert signals |
| Traces | Show the end-to-end path and timing of a request |
| Correlation IDs | Connect telemetry across components |
| Model Context | Identifies the model and serving configuration involved |
| Input Observability | Tracks schema, quality, and validation behavior |
| Feature Observability | Tracks retrieval, coverage, freshness, and consistency |
| Prediction Observability | Tracks score, class, uncertainty, and decision distributions |
| Delayed Quality | Tracks model performance after verified outcomes arrive |
| OpenTelemetry | Provides vendor-neutral telemetry instrumentation and export |
| Collector | Receives, processes, and exports telemetry |
| Dashboards | Present operational and ML behavior clearly |
| Alerts | Direct responsible owners toward defined action |
| Incident Response | Uses correlated evidence for diagnosis and recovery |
| Privacy and Governance | Control telemetry content, access, retention, and use |
Key Takeaway
Model observability and logging make production Machine Learning systems understandable and supportable. Structured logs explain individual events, metrics reveal trends and threshold conditions, and distributed traces show how requests move through validation, feature retrieval, preprocessing, inference, and downstream services. Complete ML observability also records the model, application, schema, feature-service, threshold, and deployment versions associated with each request. OpenTelemetry can provide vendor-neutral instrumentation and telemetry export, but instrumentation must be combined with privacy-safe schemas, bounded metric labels, secure collection, appropriate sampling, actionable alerts, responsible ownership, and tested incident procedures. Observability does not replace model monitoring. It connects operational evidence with data, prediction, delayed-quality, responsible-use, and business signals so teams can understand and correct production behavior safely.
Get Machine Learning updates by email
Short, practical lessons and course updates from Rumman Ansari. No spam.