Unveiling the System's Secrets: The Magic of Observability
The goal of this article is to teach the concepts of Observability, Logs, Metrics, and Tracing, based on the literature and on hands-on experience in large companies.
We are going to learn how to apply them in a practical project and understand how they relate to each other.
- Table of Contents
โYou can't manage what you can't measure.โ
-- Peter Drucker
๐ญ So, What Is Observability?โ
The term emerged in 1960 in the field of control engineering, introduced by the engineer Rudolf E. Kรกlmรกn, and was popularized by On The General Theory of Control Systems.
In Control Theory, Observability is a measure of how well the internal states of a system can be inferred from knowledge of its external outputs.
This means that observability proposes to understand how a system reached a state by looking at its outputs.
In this way, it allows us to deal with current problems and even to solve new ones.
But not everything is magic... To be able to identify the state of a system by looking at its outputs, the application must be properly instrumented, that is, it must emit signals that can be captured and analyzed.
We divide these signals into three pillars: traces, metrics, and logs. However, having the signals alone is not enough; it is necessary to collect, transmit, and store this data.
We call the collection, transmission, and storage of this data Telemetry.
Generated by DALL-E
An application is properly instrumented when developers do not need to add more instruments to solve a problem, because they already have all the information they need โ that is, without the need for a new deploy to add more instruments. Think of instruments as: new software, file or configuration changes, etc.
Of course, this maturity is reached over time, and it is a continuous process of improvement. It would be impossible to instrument everything at once, because there will always be problems that have not manifested themselves... yet.
The Three Pillars of Observabilityโ
| Pillar | Description |
|---|---|
| Metrics | Measures quantitatively, commonly used to compare and track performance along a timeline. |
| Logs | A record that an event occurred. |
| Tracing | Tracing is a mechanism for following the flow and progression of data through a piece of software. |
Conclusionโ
Well-instrumented applications are able to answer questions about their behavior by analyzing them from the outside, and that is what we call Observability.
Instrumenting applications is the act of emitting signals; these signals are divided into 3 pillars: traces, metrics, and logs.
Telemetry is the process of collecting, transmitting, and storing these signals.
1. ๐ Logs: Secrets Hidden in the Codeโ
1.1. Introductionโ
Every piece of software runs methods/functions, and most of the time these executions have something to tell us that makes them worth recording. So...
Logs are records of events that occur in the software.
Below I list some types of logs:
- Application Logs
- Audit Logs
- Authorization and Access Logs
- Change Logs
- Event Logs
- Resource Logs
- Server Logs
- System Logs
- Threat Logs
- Transaction Logs
We are going to cover only application logs, which are logs generated by a running application.
Typically, in an application log record we find:
TimestampMessageLevelContext Data
1.2. Log Levelsโ
DEBUG- Detailed information, normally of interest only to developers.INFO- Captures events that occurred but that are not errors.WARN- Indicates that something unexpected happened, but the software is still able to work.ERROR- Indicates that at least one system component failed and may interfere with part of or the overall operation.FATAL- Indicates that the system cannot continue to operate.
1.3. Log Formatsโ
Below we will look at some of the log structures most commonly found in the market. JSON tends to be the most used one because it is easy to parse and because it is structured.
1.3.1. Structuredโ
- JSON
- XML
- CSV
- Logfmt
- Syslog (RFC 5424)

In the example above, note that there are two fields, trace_id and span_id; we will cover them further ahead.
<log>
<timestamp>2024-07-10T14:45:23Z</timestamp>
<level>INFO</level>
<message>User logged in</message>
<user>john.doe</user>
<ip>127.0.0.1</ip>
</log>
timestamp,level,message,user,ip
2024-07-10T14:45:23Z,INFO,User logged in,john.doe,127.0.0.1
timestamp="2024-07-10T14:45:23Z" level=INFO message="User logged in" user="john.doe" ip="127.0.0.1"
<165>1 2003-10-11T22:14:15.003Z mymachine.example.com evntslog - ID47 [exampleSDID@32473 iut="3" eventSource="Application" eventID="1011"][examplePriority@32473 class="high"]
1.3.2. Semi-Structuredโ
- Common Event Format (CEF)
- NCSA Common Log Format (CLF)
CEF:0|Security|ThreatDetection|1.0|100|User logged in|5|msg=User logged in src=127.0.0.1 user=john.doe
192.168.0.1 - jane [08/Jul/2024:10:23:45 +0000] "POST /login HTTP/1.1" 302 512
1.3.3. Unstructuredโ

What is the difference between Semi-Structured, Structured, and Unstructured?
Semi-Structured logs have a fixed structure, but the fields are not fixed.
For example,
Logfmtalways has the samekey=valuestructure with a space separator, but the fields can vary.
Structured logs have a fixed structure and the fields are fixed.
For example,
JSONalways has the same fields (if implemented that way), meaning all logs have the same fields, such astimestamp,level,message, andextra, but the values can vary.
Unstructured logs do not have a fixed structure, meaning the fields and values can vary.
For example, plain-text logs.
1.4. Implementationโ
How do you use logs in an application? Below we see a code snippet from a Flask application in Python that I built to illustrate this article.
@app.route('/')
def hello():
message = f"Hello from {MS_NAME}!"
Logger().info('app.py', {"response": message, "status": "200", "method": "GET", "path": "/", "app": MS_NAME})
return message
The code is emitting a structured log through the Logger abstraction to override the default behavior of Python's logging.
Now let's analyze the JSON output of the log generated by this code.
{
"timestamp": "2024-07-05 19:59:37",
"level": "INFO",
"message": "app.py",
"extra": {
"response": "Hello from ms-one!",
"status": "200",
"method": "GET",
"path": "/",
"app": "ms-one"
}
}
The destination of this output could be a file, a database, a log service, etc. In our case we are just printing the log to the console, stdout and stderr.
1.5. Architectureโ

In the example above, promtail is an agent that collects logs from the containers and sends them to Loki, which is a log storage system, while Grafana is a log visualization tool that consumes directly from Loki.
Storing logs ends up becoming expensive ๐ฐ over time, so it is important to define which events truly have value and to standardize the output structure.
Look for strategic points within the application code to emit logs with relevant context; this will help you track the software's behavior from the outside using some log visualization tool and will allow you to build dashboards with metrics from that data, making it easier to discover problems and supporting decision-making.
Conclusionโ
Structured logging allows you to run more complex queries more easily, and to build metrics from the logs.
With unstructured logs, parsing is harder because the structure is not fixed, and building metrics can become more complex. It can lead to broken dashboards and metrics depending on how the query was built.
This does not mean it is impossible to build metrics from Semi-Structured and Unstructured logs, but it ends up being difficult when there is no standard.
Define the logging best practices for your application and follow them:
- Define the log levels that should be emitted in production; do not emit
debug. - Define the output format (JSON, XML, CSV...).
- Define the fields that should be emitted.
- Define when to use an error log.
- Define when to use an info log.
- Define when to use a warning log.
- Define when to use a fatal log.
- Design the code to emit logs at strategic points.
- Define the destination of the logs (stdout, stderr, file, database, log service, etc.).
- Define the log retention policy (X days).
2. ๐ Metrics: Decoding Software Performanceโ
Metrics are values captured in your systems at a specific point in time.
Examples:
- the number of users who logged into your system in the last 2h
- the number of requests your application received in the last 24h
- the average response time of a request within the last 1h
They can be collected once per second, once per minute, or at another regular interval to monitor a system over time. Their data is commonly stored in a time-series database, such as InfluxDB, Prometheus, Graphite, etc. In Prometheus, collection is done through scraping.
We also build metrics from application logs, filtering and aggregating the data within a time interval. We call this technique White-box monitoring.
For example, from the application logs that have the field extra_path="/health", we can count how many times the /health route was requested within a time interval. See below:

3 requests were made to the /health route within an interval of ~1 minute. In this case we built a Traffic metric.
2.1. Resource Metrics (CPU, Memory, Disk, Network)โ
Resource metrics are metrics that measure a system's resource usage, such as CPU, memory, disk, and network. These metrics are essential for monitoring the health and performance of a system and for identifying bottlenecks and performance problems.
| Signal | Description |
|---|---|
| Utilization | Measures the amount of a resource that is being used. |
| Saturation | Measures the degree to which a resource is near its maximum capacity or beyond. |
| Errors | Measures the number of errors that occurred while using a resource. |
| Jitter | Measures the variation between when a network packet is transmitted and when received. |
| Capacity | Measures the maximum amount of work a system can perform. E.g.: 8GB RAM, 3vCPU |

2.2. Service Metrics (Four Golden Signals)โ
Introduced in Google's SRE book, these 4 metrics are meant to understand the system's usage experience from the users' point of view โ Service-Oriented.
| Signal | Description |
|---|---|
| Latency | Measures the time the system takes to respond to a request. |
| Traffic | Measures the number of requests the system is receiving. |
| Errors | Measures the number of errors the system is returning. |
| Saturation | Measures the degree to which a resource is near its maximum capacity or beyond. |
The most common performance metric is latency, which represents the time needed to complete a unit of work. Latency can be expressed as an average or as a percentile, such as "99% of the requests returned in 0.1s".
The average is widely used to understand the overall performance of a system, but the average is not such a reliable value, because it can be distorted by extreme values; percentiles, on the other hand, are useful to understand performance across different quantiles of the distribution.
Below I show a dashboard to monitor the total CPU usage of a Container. I ran a simulation of 1000 requests and we can see the CPU spike.

