Intro
.NET Aspire is an opinionated, cloud ready stack for building observable, production ready, distributed applications.
In this post we'll look at working with Elasticsearch in .NET Aspire, but firstly - on the observability side - let's learn just how easy it is to instrument our app with OpenTelemetry and see logs, metrics and traces in Elastic.
Observability with OpenTelemetry and Elastic
.NET Aspire integrations automatically set up Logging, Tracing, and Metrics configurations using the .NET OpenTelemetry SDK, and the telemetry data is exported to an endpoint using the OpenTelemetry protocol (OTLP).
Where to send the data, and identifiers such as a service name, are set via environment variables that .NET Aspire projects launch with. For example:
OTEL_SERVICE_NAME = ApiService
OTEL_RESOURCE_ATTRIBUTES = service.instance.id=1a5f9c1e-e5ba-451b-95ee-ced1ee89c168
OTEL_EXPORTER_OTLP_HEADERS, "Authorization=Bearer ABC123456789"
OTEL_EXPORTER_OTLP_ENDPOINT = http://localhost:4318
Elastic Observability
As Elastic APM supports OTEL natively, we can simply set the above environment variables to our specific Elastic APM instance details, as found in the Add data walkthrough for OTEL within Elastic APM:
If we wanted, we can also overwrite the environment variables in C# code within the .NET Aspire AppHost project when setting up other project reference(s):
builder.AddProject<Projects.AspireOtelDefaults_Web>("webfrontend")
.WithExternalHttpEndpoints()
.WithReference(apiService)
.WithEnvironment("OTEL_SERVICE_NAME", "WebFrontEnd")
.WithEnvironment("OTEL_EXPORTER_OTLP_ENDPOINT", "https://myapmserveraddress.apm.cloud.es.io:443")
.WithEnvironment("OTEL_EXPORTER_OTLP_HEADERS", "Authorization=Bearer ABC123456789");
When we start the app, we get our data in Elastic APM:
...and our logs (ingested to a data stream named e.g: "logs-apm.app.yourappname", so are still discoverable under the "logs-*" data view):
The logs are also correlated automatically with our APM data, keeping everything nicely together:
Worth noting that we could have achieved this with no code changes - we'd only need to have set the environment variables necessary for OTEL!
.NET Aspire Elasticsearch Integrations
Moving away from observability, Microsoft provide two integrations as Nuget packages for working with Elasticsearch: Aspire.Hosting.Elasticsearch, which allows you to host your own Elasticsearch instance in a container, and Aspire.Elastic.Clients.Elasticsearch for working with existing Elasticsearch deployments.
We'll have a brief look at the client integration. Once you've added the Aspire.Elastic.Clients.Elasticsearch package in your client-consuming project, within Program.cs, you register the Elasticsearch client instance like this (this example is for Elastic Cloud):
builder.AddElasticsearchClient(
"elasticsearch",
static settings =>
{
settings.ApiKey = "yourKeyHere";
settings.CloudId = "ESS:CloudId";
});
We can then work with the .NET Elasticsearch client as per usual:
app.MapGet("/weatherforecast", async () =>
{
// Retrieve the Elasticsearch client from the container.
var esClient = app.Services.GetRequiredService<ElasticsearchClient>();
// Search for documents in the Elasticsearch index.
var response = await esClient.SearchAsync<MyDoc>(s => s
.Index("my-student-index")
.From(0)
.Size(10));