20.13. Observability Basics
Table of Contents
Why Observability Matters
When your backend runs on your laptop, you can watch the console, restart quickly, and debug by trial and error. In production, your app runs on remote servers, often in multiple containers, behind load balancers, with many concurrent users.
You cannot just “ssh in and print stuff” whenever something goes wrong. You need a way to understand what is happening inside your system from the outside.
That is what observability is about: designing systems so that you can ask questions about their behavior and get useful answers, even for problems you did not predict in advance.
Key idea:
Observability is the ability to understand the internal state of a system by looking at its outputs, without changing the running system.
Good observability lets you:
- Detect problems early.
- Debug incidents quickly.
- Understand performance and bottlenecks.
- Validate that new deployments behave as expected.
- Make data‑driven decisions about scaling and architecture.
You will use three main types of data, often called the three pillars of observability: logs, metrics, and traces.
The Three Pillars of Observability
Logs
Logs are timestamped text records that describe events that happened in your application or infrastructure.
Examples:
- “User 123 requested /api/orders”
- “Order 456 created successfully”
- “Database timeout when calling SELECT …”
- “Payment provider responded with 502 Bad Gateway”
Typical log fields:
| Field | Example |
|---|---|
| Timestamp | 2026-08-28T09:32:15.123Z |
| Level | INFO, WARNING, ERROR |
| Service | orders-service |
| Message | Order created |
| Context | {"order_id": 456, "user_id": 123} |
Logs are great for:
- Debugging: seeing the detailed story of what happened.
- Auditing: who did what and when.
- Error analysis: stack traces and error messages.
However, raw logs can be large and hard to search. This is why we also use metrics.
Metrics
Metrics are numeric measurements over time.
Examples:
- Requests per second (RPS)
- Average response time in milliseconds
- Error rate percentage
- CPU usage, memory usage
- Number of active database connections
- Queue length in a message broker
Metrics are usually stored as time series:
| Time | Value (Requests/sec) |
|---|---|
| 10:00:00 | 120 |
| 10:00:10 | 135 |
| 10:00:20 | 160 |
Metrics are great for:
- Monitoring health: is the service up and fast?
- Alerting: send alerts when error rate or latency crosses a threshold.
- Capacity planning: see trends in CPU, memory, traffic.
Metrics are compact and easy to visualize, but they do not show detailed “per request” information. That is where traces help.
Traces
Traces track the path of a single request as it flows through your system.
Consider a user calling /api/create-order. Internally this may call:
- Orders service
- User service
- Inventory service
- Payments service
- Database
A trace links all these operations together. Each step in the trace is a span with information like:
- Name, for example
GET /api/orders/{id} - Start time and duration
- Status (success or error)
- Extra attributes, for example
user_id=123
A simple trace structure:
| Span | Parent | Duration (ms) |
|---|---|---|
| HTTP request | (root) | 300 |
| Orders service | HTTP request | 200 |
| DB query | Orders service | 50 |
Traces are great for:
- Finding performance bottlenecks: which step is slow?
- Understanding system behavior: what services are involved?
- Debugging distributed systems: where did the error start?
Together, logs, metrics, and traces give you different views of the same reality.
Important:
- Logs answer: “What happened?”
- Metrics answer: “How often and how much?”
- Traces answer: “Where in the flow did it happen?”
Observability vs Monitoring
The terms monitoring and observability are related but not the same.
- Monitoring is about:
- Collecting known metrics and logs.
- Checking predefined conditions.
- Triggering alerts when something looks wrong.
- Example: “Alert if error rate > 5 percent for 5 minutes.”
- Observability is about:
- Designing systems so you can ask new questions.
- Having rich data that lets you explore unknown issues.
- Example: “Why did order creation slow down only for EU users after the last deployment?”
You can think of it like this:
| Concept | Focus |
|---|---|
| Monitoring | Known problems, predefined checks |
| Observability | Unknown problems, open‑ended questions |
In practice:
- Monitoring tools use metrics and logs to show dashboards and fire alerts.
- Observability platforms combine logs, metrics, and traces so you can drill down from a high‑level metric to a specific request and its logs.
You will often see teams say “we are improving observability” when they:
- Add structured logging and trace IDs.
- Introduce distributed tracing.
- Add more detailed metrics for critical parts of the system.
Core Concepts: Signals, Context, and Correlation
To make observability useful, three ideas are important: signals, context, and correlation.
Signals
A signal is any piece of data that describes your system’s behavior. The main signals are:
- Logs
- Metrics
- Traces
Some people also include:
- Events: one‑time occurrences like deployments or configuration changes.
- Profiles: how CPU or memory is used inside your code.
As a beginner backend developer you should focus on the three main signals first.
Context
Context is extra information that makes a signal meaningful.
A log message "Payment failed" is not very helpful alone. With context it becomes usable:
{
"timestamp": "2026-08-28T10:00:00Z",
"level": "ERROR",
"message": "Payment failed",
"user_id": 123,
"order_id": 456,
"payment_provider": "stripe",
"error_code": "card_declined",
"trace_id": "abc-123"
}You will often add context like:
- User ID, tenant ID, or organization ID
- Request path and HTTP method
- Correlation ID or trace ID
- Environment (dev, staging, production)
- Version or git commit of the deployment
Context is what turns a pile of data into something you can reason about.
Correlation
Correlation means linking different signals that refer to the same thing.
Example questions:
- “Show me all logs for the request that had this 500 error.”
- “Show me the trace for the HTTP request that triggered this slow database query.”
- “Show me metrics, logs, and traces around the time this alert fired.”
To do this you usually:
- Generate a correlation ID or trace ID for each incoming request.
- Include this ID in:
- HTTP headers, for example
X-Request-IDor trace headers. - Application logs, as a field.
- Spans in your trace.
- Let your observability tools use that ID to link data.
This is one of the simplest but most powerful practices for observability.
Rule:
Always include a request / trace ID in logs and propagate it across services.
This makes it possible to follow a single request across the whole system.
Basic Observability Setup for a Backend
You do not need a complex stack on day one. Start small and grow over time.
A typical beginner‑friendly setup looks like this:
| Layer | Example Tool | Purpose |
|---|---|---|
| Logs | Application logs to stdout | See what the app is doing |
| Log collection | Docker logging / simple agent | Send logs to a file or service |
| Metrics | Prometheus + client library | Collect app and system metrics |
| Dashboards | Grafana | Visualize metrics over time |
| Traces | OpenTelemetry + Jaeger | Trace requests across services |
You might not use all of these at first, but you can still follow the ideas:
- Add structured logs (for example JSON) with consistent fields.
- Expose basic metrics like:
- HTTP request count and latency
- Error count
- Database query duration
- Introduce tracing when you start having multiple services or complex flows.
Designing for Observability
Good observability is not something you “bolt on at the end.” It is part of your design.
Here are simple practices you can apply as a beginner.
Log Important Events, Not Everything
Do not log every tiny detail such as every loop iteration. Focus on:
- Start and end of important operations.
- External calls, such as database or third‑party APIs.
- User‑visible failures and unexpected states.
Example for an order creation flow:
- Receive request to create order.
- Validate input.
- Check inventory.
- Charge payment.
- Save order.
- Return success.
You might log:
- At step 1:
"Create order requested", with user ID and cart size. - At step 3:
"Inventory check failed"with item IDs. - At step 4:
"Payment failed"with provider and error code. - At step 5:
"Order saved"with order ID and total amount.
This gives you a clear story when something goes wrong.
Use Log Levels Wisely
Common levels:
| Level | When to use |
|---|---|
| DEBUG | Detailed information for local debugging |
| INFO | Normal events in the app’s lifecycle |
| WARNING | Something unusual, but the app can still continue |
| ERROR | An operation failed, but the app is still running |
| CRITICAL | The application or a major part is unusable |
Example:
INFOwhen an order is created successfully.WARNINGwhen payment succeeds after a retry.ERRORwhen payment fails and the order cannot be created.
You will often configure production to ignore DEBUG logs, but still store INFO and above.
Add Metrics to Critical Paths
Identify your most important features and add metrics, for example:
- For an API:
http_requests_totallabeled by path and method.http_request_duration_seconds(histogram).http_requests_errors_totallabeled by status code.- For a database:
db_query_duration_seconds.db_connections_in_use.
These metrics help answer questions like:
- “Did the new release increase the average latency for
/api/orders?” - “Is the error rate higher for POST requests than GET requests?”
- “Are we running out of database connections under load?”
Trace Requests Across Services
If your application is small and monolithic, you might delay full tracing. As soon as you have:
- More than one service, or
- Multiple external dependencies,
it becomes worth introducing distributed tracing.
The basic idea:
- Generate a trace ID when a request enters the system.
- Attach it to all outgoing calls as headers.
- Use a tracing library to automatically create spans for:
- Incoming HTTP requests.
- Outgoing HTTP calls.
- Database queries.
- View the trace in a UI to see the timeline.
Even simple traces can tell you things like, “The slowdown came from the payments service, not the database.”
From Symptoms to Root Cause
Observability is most useful when something breaks. Here is how the three pillars help you go from symptom to root cause.
Imagine you receive an alert: “Error rate > 5 percent on /api/orders for 10 minutes”.
- Start with metrics
- Check the error rate and latency metrics for
/api/orders. - Look for patterns:
- Does it affect all regions or only one?
- Did traffic suddenly increase?
- Did it start after a deployment?
- Drill down into traces
- Open traces for failed
/api/ordersrequests. - Compare traces that succeeded vs failed.
- Look for:
- Spans that have errors.
- Spans that take much longer than usual.
- You might see that:
- The
payments-servicespan fails with HTTP 502. - The
db.queryspan suddenly takes 2 seconds. - Inspect logs for context
- Filter logs by trace ID from a failing trace.
- Read what the services logged during that request.
- Look for stack traces, timeouts, or configuration issues.
- Example findings:
- Logs show that a new API key is invalid.
- Logs show timeouts only in one region.
This flow is typical:
Common investigation flow:
Metric alert → Traces for failing requests → Logs for a specific trace → Root cause
As a backend developer, your job is to make sure the data you emit makes this flow possible.
Measuring and Improving Observability
You cannot directly measure “observability,” but you can watch outcomes related to it.
Common indicators:
- MTTD (Mean Time To Detect): average time to notice a problem.
- MTTR (Mean Time To Recover): average time to fix the problem.
- Number of incidents where the cause is “unknown.”
- How often you need to reproduce a bug locally because production data is unclear.
When observability improves:
- Alerts are more accurate, with less noise.
- You spend less time guessing and more time fixing.
- You rarely say “we have no idea what happened.”
For your own practice on projects:
- Start by logging clearly with context.
- Add a few key metrics for each new feature.
- When you hit a tricky bug, ask:
“What data would have made this easy to diagnose?”
Then add that data for next time.
How This Connects to the Rest of the Course
In this course you will see observability ideas show up in many places:
- Logging chapter: how to structure logs, set levels, and integrate with log collectors.
- Metrics & monitoring chapters: how to expose and visualize metrics.
- FastAPI and production chapters: how to add middleware for request IDs, integrate with APM / tracing tools, and configure observability in Docker and deployment setups.
For now, remember the fundamental mindset:
- Treat logs, metrics, and traces as first‑class features, not afterthoughts.
- Design your backend so that someone who was not there when you wrote it can still understand what it is doing in production.
Views: 7
KAHIBARO