Add products to the cart
The shopper browses the store and builds an order with the items they want to buy.
This post is a continuation of the Serilog series (previous part: Serilog: Enrichers for Better Context).
In this part, we will:
Let's get started! 🚀
Note: The goal of this post is to showcase the benefits of using logging (Serilog) and observability (OpenTelemetry) and not to be a step-by-step tutorial. Also note that the code is not production-ready and is only meant for demonstration purposes. It is not fully optimized for performance, security, or maintainability (it was written mostly by GPT-5.4 with my review and guidance). If you have any questions about the code, concepts, or implementation details, feel free to ask in the comments or reach out via LinkedIn or GitHub.
The three load-test files enter the story first: one drives public HTTP traffic, while two worker-oriented scripts target fulfillment-heavy flows.
💡 Tap nodes for details and use Back or Next to uncover the story💡 Hover nodes for details and use Back or Next to uncover the story
Before diving into observability details, here is the end-to-end business flow from cart and checkout through payment outcomes, fulfillment, order tracking, and the Grafana view of the whole journey.
The shopper browses the store and builds an order with the items they want to buy.
At checkout, the shopper confirms the basket, picks a delivery option, selects a payment method, and chooses a demo payment scenario (one of: Success, Decline, SlowSuccess, Timeout).
The shopper confirms the purchase and the order request is sent to the checkout flow. This is the moment the order leaves the cart and enters checkout processing.
The payment step decides whether the order can continue immediately, continue after a delay, or stop before fulfillment begins.
When payment succeeds, the shopper gets a confirmation. The warehouse receives a new order to prepare. Notification is sent (email or/and SMS) to the shopper.
Warehouse staff collect the items, pack the order, and move it toward shipment.
The shopper can look up the order to see its current status and the details of what was purchased.
Grafana brings the whole business journey together so you can inspect what happened through logs, traces, and metrics in one place.
The load test is designed to simulate realistic user behavior and exercise the full business workflow. It is implemented with k6 and can be found in the load-test folder. To run it, execute the following command in the terminal:
It is divided into three files:
Open Grafana in the browser and navigate to the Drilldown dashboard (Menu > Drilldown > Logs). You should see the following APIs:
Each of them has its own panel with logs and telemetry. In the filters section, you can select a specific API for further details. Here is what it looks like for the SerilogDemo API:
Any log entry contains trace and span IDs, which allow us to correlate logs with traces and metrics. This is crucial for understanding the context of each log entry and how it fits into the overall workflow.
For a specific log entry, you can click on it to see more details, including the log line, structured metadata, and indexed metadata. This can help you understand what happened at that point in time and why.
Now copy traceId, OrderId, or any other relevant metadata and use it to filter logs in the Logs panel. This allows you to see all logs related to a specific business process (orderId/orderNumber) or trace, which can be incredibly useful for debugging and understanding the flow of a request through the system.
TIP
In this setup, the Drilldown > Logs panel is not practical for searching high-cardinality fields such as traceId or orderId because those fields are not indexed. To search for logs across all APIs, use the Explore panel instead. This way you can see all logs related to a specific traceId or orderId across the entire system. The screenshot below shows how to do it for orderId.
In the Drilldown > Traces panel, you can also see traces related to specific traceId. This allows you to see the entire flow of a request through the system, including all spans and their metadata. This is incredibly useful for understanding the performance and behavior of the system under load.
Let me give you an example. A client calls your service desk with the following problem: "I cannot check out my order; the payment fails." You can ask for the order number and then use it to find all logs and traces related to that order. This way you can see exactly what happened during the checkout process, where it failed, and why. You can see whether the payment gateway was called, whether any errors were returned, and what the response looked like. This can help you quickly identify the root cause of the problem and provide a solution to the customer (let's say, "It seems that the payment gateway is currently experiencing issues. We are working on it and will notify you once it's resolved. In the meantime, you can try a different payment method or place the order later.").
Here is what the trace looks like in Grafana:
Logs empower you to understand what lines of code were executed and what was the context of those executions. Modern web applications are mostly distributed and often involve multiple services, APIs, and databases. Logs alone can be overwhelming and insufficient to understand the full picture of what is happening in the system. This is where OpenTelemetry comes in.
OpenTelemetry provides a standardized way to collect and correlate logs, metrics, and traces. This allows you to have a holistic view of your system's behavior and performance. With OpenTelemetry, you can easily correlate logs with traces and metrics, which can help you understand the context of each log entry and how it fits into the overall workflow. For example, if you see an error log, you can quickly find the corresponding trace and see the full flow of the request that led to that error, including all the spans and their metadata. This can help you quickly identify the root cause of the problem and understand how it affects the overall system.
NOTE
Keep in mind that OpenTelemetry is just a standard. It is up to you to implement it in your application and choose the right tools for collecting, processing, and visualizing your telemetry data. In this post, we used the Grafana stack, but there are many other options available in the ecosystem (e.g., Datadog, Dynatrace, Elastic, New Relic, SigNoz, Dash0, etc.). The key is to choose the tools that best fit your needs and provide the features you require for your observability strategy.
WARNING
It is important to realize! Poorly implemented OpenTelemetry can open security and performance issues. For example, if you are collecting sensitive data in your logs or traces, you need to ensure that it is properly redacted or encrypted. Additionally, if you are collecting too much telemetry data, it can lead to performance degradation and increased costs. It is crucial to implement OpenTelemetry thoughtfully and with consideration for the specific needs and constraints of your application and infrastructure.
You have probably noticed in daily life that visualizing data is often more effective than reading raw data. For example, when you look at a map, you can quickly understand geography and the relationships between different locations, which would be much harder if you were staring at a list of coordinates. Similarly, in software development, having a visual representation of your logs, metrics, and traces can help you quickly understand the behavior and performance of your system, identify patterns and anomalies, and make informed decisions about how to improve it.
By using OpenTelemetry and a visualization tool like Grafana, you can gain deeper insights into your system's behavior and performance, which can help you deliver better software and provide a better experience for your users.
Talking about visualization: in the next episode of the Serilog series, we will introduce a user interface for our demo project, which will allow us to interact with the system in a more intuitive way. Stay tuned!
In the meantime, happy coding! 🚀


