Observing Spin Apps with OpenTelemetry and the .NET Aspire Dashboard
Thorsten Hans
otel
tracing
metrics
logs
aspire
spin
Gathering telemetry data is crucial for understanding how your apps behave at runtime and troubleshooting when things go sideways. In this post, we will explore how you can observe Spin apps when running locally using the .NET Aspire Dashboard in standalone mode.
Automatic Instrumentation done by Spin
Spin does automatic instrumentation. Automatic instrumentation means that Spin gathers metrics, traces, and logs at runtime, without you having to do anything. To have Spin export that telemetry data to an OpenTelemetry Collector all you need to do is set the OTEL_EXPORTER_OTLP_ENDPOINT
environment variable before running your Spin apps.
Although there are many different observability stacks available for observing your Spin apps, this article illustrates how you can observe your apps using the .NET Aspire Dashboard in standalone mode.
What is the .NET Aspire Dashboard
The .NET Aspire Dashboard was created by the Microsoft .NET team as part of .NET Aspire. In a nutshell, .NET Aspire is a set of libraries that allows developers to orchestrate, run, and investigate distributed applications on their local machine (inner loop).
The .NET Aspire Dashboard is a sophisticated dashboard for monitoring and inspecting all sorts of applications. It allows you to drill into logs, traces, metrics, and configuration data (environment variables) assigned to the individual components of your app. Check out this link to explore all features of the .NET Aspire Dashboard.
The .NET Aspire Dashboard is also available as a standalone variant, which is a single-container observability stack that you could use for observing your individual applications. Given the fact that we could provide all users of Spin a full-fledged observability stack - by just spawning a single container during development time - made us update the pre-existing otel
plugin and incorporate the standalone .NET Aspire Dashboard.
Meet the otel
Plugin for Spin
The otel
plugin for Spin is designed to assist you in developing and observing Spin applications on your local machine. Relying on Docker Compose, it can deploy and run different observability stacks within seconds and configures your Spin app(s) to send telemetry data to your observability stack of choice. As of today, the otel
plugin for Spin supports two different observability stacks:
- Default: A multi-container observability stack based on Prometheus, Loki, Grafana, and Jaeger
- Aspire: A single-container observability stack using .NET Aspire Standalone Dashboard
Installing the otel
plugin for Spin
Before we dive into observing a Spin app, let’s install the otel
plugin for Spin, which is as easy as executing the following commands:
# Update the Plugin Feed
spin plugins update
# Install the otel Plugin
spin plugins install --yes otel
Keep in mind that the otel
plugin is leveraging Docker Compose under the covers, so ensure that you’ve Docker installed on your machine and verify that the Docker Daemon is running.
The easiest way to verify if your Docker installation works as expected, is to run the official hello-world
image (which will print some text to stdout
) and remove it upon completion:
docker run --rm hello-world
What are we going to observe?
We need some sort of demo application that we can observe. Because implementing a Spin App is not what this article is about, we will use an existing application.
The Spin app we’re going to observe exposes data stored in a SQLite database via HTTP. Every HTTP response created by the Spin app also contains a custom x-spin-runtime
header. Its value is loaded from the Spin app using its wasi-config
implementation provided by the Spin SDK for JavaScript.
The sample application is part of our Enterprise Architectures & Patterns repository on GitHub (see the http-crud-js-sqlite
folder).
Setting up the Observability Stack and Starting the Spin app
Once you have cloned the repository and navigated into the folder of the app (cd http-crud-js-sqlite
), you have to set up the observability stack using spin otel setup --aspire
.
Next, you can build your Spin app - as you would normally do - by executing a simple spin build
. As this app is built with JavaScript, you must have Node.js (version 21 or later) installed on your system.
The otel
plugin also provides an up
command, which you use for starting the Spin app and configuring the previously mentioned OTEL_EXPORTER_OTLP_ENDPOINT
environment variable.
This will instruct Spin to send telemetry data to the OTLP endpoint exposed by the .NET Aspire Dashboard:
# Setup the .NET Aspire Observability Stack
spin otel setup --aspire
# Build the Spin App
spin build
# Start the Spin App through the otel plugin
spin otel up -- --sqlite @migrations.sql
Awesome!
Having the observability stack and the Spin app running on your local machine, we can send requests to the Spin app to generate telemetry data which we will then inspect in the .NET Aspire Dashboard:
# Read All Items
curl -iX GET localhost:3000/items
# Create a new Item
curl -iX POST -H "content-type: application/json" \
-d '{"name": "Pet Mode", "active": true}' \
localhost:3000/items
# Again, Read all Items
curl -iX GET localhost:3000/items
Inspecting Spin Apps using the .NET Aspire Dashboard
The .NET Aspire Dashboard is exposed on port 18888
of your local machine, you can either open the address manually in your browser or use the handy spin otel open aspire
command.
Exploring Logs
Logs that are either sent to stdout
or stderr
are automatically captured and forwarded to the OTLP endpoint by Spin. The .NET Aspire Dashboard visualizes structured logs in the corresponding view of the dashboard UI.
Exploring Metrics
At the point of writing this article, the Spin runtime emits the execution count of your app as a metric. Navigate to the Metrics view of the .NET Aspire dashboard to see those metrics.
Exploring Distributed Traces
Distributed traces are crucial to understanding runtime behavior and dependency invocation for incoming requests handled by your Spin app(s). Spin automatically creates spans when your code interacts with external resources (e.g. interacting with a SQLite database or loading configuration data through the wasi-config
implementation of Spin). Additionally, individual logs created from within your Spin app are treated as events and are embedded in the corresponding spans, which streamlines the investigation process. Navigate to the Traces view of the .NET Aspire Dashboard to drill into traces, and locate custom logs being represented as events of a particular span.
Removing the local observability stack
To remove the local observability stack, you can run spin otel cleanup
, which will terminate and remove the .NET Aspire Dashboard container on your local machine.
Conclusion
Automatic instrumentation by Spin provides essential telemetry data of your apps at runtime allowing you to observe and inspect with ease. In conjunction with the .NET Aspire standalone Dashboard, we can understand how our apps behave at runtime and drill into different telemetry data buckets with ease. The otel
plugin for Spin streamlines the inner-loop experience by deploying, configuring, and integrating the observability stack with your Spin app(s).
Additional Resources
If you want to dive deeper into observing your Spin Apps using the standalone.NET Aspire Dashboard, check out our video on YouTube: