Product release

Elastic APM .NET agent beta released

We are proud to announce the beta release of the Elastic APM .NET agent! The version number of this release is 1.0.0-beta1.

Before you get too excited, a quick reminder that you should not run the beta in production. The agent is now ready for more serious testing, so you should definitely consider giving it a try in your test, pre-prod, and dev environments. If you do so, we'd be thrilled to get some feedback in the discussion forum.

Back in February we announced the alpha release of the .NET APM agent. We received very positive feedback from the community, and we managed to attract lots of users already in this very early stage. Our base NuGet package reached around 20,000 downloads.

In this post I'll summarize what happened since the alpha release, and I'll also give a short summary about what we plan to do in the coming months.

Distributed tracing support

An important new feature of the agent in the beta release is distributed tracing. If you have multiple services that communicate over HTTP with the HttpClient class, the agent will automatically connect these services into a single trace.

Dot Net Distributed Tracing Sample

The agent implements the W3C Trace Context specification. One small caveat to this is that the Trace Context spec is not yet an official standard (although it’s already very close to it — at this moment it's a "Candidate Recommendation"), therefore, in order to avoid breaking tracing in case the W3C Trace Context proposal changes before its finalization, similarly to other Elastic APM agents, we also prefix the added HTTP header with elastic-apm. Everything else is implemented according to the specification, and once the W3C Trace Context specification becomes a standard, we will also rename our HTTP header to completely follow the specification.

In case of HTTP calls, with the HttpClient class you don’t need to do anything; distributed tracing is automatically enabled.

We have also added the ability to serialize and deserialize the trace-parent value manually with the Public Agent API. If your application communicates over a non-HTTP protocol (e.g. gRPC or a direct TCP connection) by using the Public Agent API, you can manually “continue” a distributed trace on the incoming side.

Here is how to serialize the trace parent header:

string outgoingDistributedTracingData = Agent.Tracer.CurrentTransaction
                     .OutgoingDistributedTracingData.SerializeToString();

The outgoingDistributedTracingData variable contains the string representation of the distributed tracing data.

When you call another service, you can transfer this string to the callee service and continue the trace with the following method call:

var transaction2 = Agent.Tracer.StartTransaction("Transaction2", "TestTransaction", DistributedTracingData.TryDeserializeFromString(serializedDistributedTracingData));

where serializedDistributedTracingData is the string value that you serialized in the caller side.

The result is that when you look at transactions from these two services in Kibana they will be connected as a single trace.

Dot Net Distributed Tracing With Agent API

Of course we plan to continuously add support for more and more libraries using non-HTTP protocols out of the box.

Sampling

The alpha releases of the agent recorded every single transaction, which in a production system isn’t necessarily ideal. In production, you usually want to capture only a subset of your transactions.

Therefore we also added sampling to the agent. It works the same as in other Elastic APM agents: With the TransactionSampleRate config you can tell the agent to only record a certain percentage of the transactions. The expected value is between 0 and 1, where 0.4 means 40% of the transactions will be recorded.

Errors and exceptions are always captured no matter whether the corresponding transaction is sampled or not.

Metrics

The beta version out of the box reports the following metrics:

  • System free and total memory (Windows and Linux only)
  • Process CPU usage (every platform where .NET Core is supported)
  • System CPU usage (Windows and Linux only)
  • Process working set and private bytes (every platform where .NET Core is supported)

With this, the first iteration of the metrics work landed in the 1.0.0-beta1 version of the APM .NET agent.

Dot Net Metrics

But we won’t stop here: We plan to add runtime-specific metrics (e.g. GC metrics and more), and we're also considering an API or an integration with an existing metrics library to also send custom metrics to Elastic APM. In case you have any input, feel free to contact us.

One interesting side note: While building this feature we found a small glitch in the CoreFX API: The Process.TotalProcessorTime method reported inaccurate values on macOS. We implemented a fix for this, which we contributed back to the CoreFX codebase.

Additional improvements based on your feedback

The following are a few of the refinements we've made based on your feedback.

ASP.NET Core transaction names based on routing information

We changed how the agent generates transaction names in the case of ASP.NET Core based on your feedback. The new way of generating transaction names is based on routing information, which is also in-sync with other Elastic APM agents.

In the case of ASP.NET Core MVC, the transaction name will contain the name of the Controller class and the name of the action method. Additionally, in case the action method has parameters, those will be shown as a placeholder string.

Let's take an example: We have an MVC controller called User with an action method called Profile that has an integer parameter, which is a user id:

public class UserController : Controller
{
  public IActionResult Profile(int id)
     //... more code
}

In this case all calls that are routed to the Profile method will be mapped to the same transaction name, which is GET User/Profile {id} — regardless of the user id. So every transaction to the same action method will be grouped to the same transaction group.

ASP Net Core APM Transaction Name

This works similarly with Razor Pages.

Docker container ID

If your .NET Core application runs in a Linux container, the agent also reports the container id, following the Elastic Common Schema. With this feature you can jump from APM data directly to container metrics, logs, and more.

APM Dot Net Container Id

This feature is currently Linux only, so we only capture the container id for Linux containers. Of course we plan to have this feature also for Windows containers.

Public Agent API: evolved

The API surface of our Public Agent API has grown significantly. We know that there are lots of libraries out there that we don’t yet cover with auto instrumentation, but we definitely don’t want you to lose visibility because of this.

Therefore, with the Public Agent API you can trace certain activities in your code.

The alpha release already allowed the capturing of spans and transactions, and we now have additional context information that you can attach to your spans and transactions.

For example, if you use a database library that we don’t support with automatic instrumentation, here is how you send database tracing information manually to Elastic APM with the Public Agent API:

Agent.Tracer.CurrentTransaction.CaptureSpan("MyCustomDbWrite", ApiConstants.TypeDb, (span) =>
{
    span.Context.Db = new Database 
        { Statement = dbStatement, Type = dbType, Instance = dbInstance };
    //..code executing the database operation
});

where the string variables dbStatement, dbType, and dbInstance can hold any string value that you would like to capture for your database statements.

You can also manually capture a transaction with incoming HTTP information, in case you’d like to trace a web framework that we don’t support currently out of the box:

Agent.Tracer.CaptureTransaction("MyCustomTransaction",ApiConstants.TypeRequest, (transaction) =>
{
  transaction.Context.Request = new Request(myRequestMethod, myRequestUri);
  //code executing the request
  transaction.Context.Response =
     new Response { StatusCode = myStatusCode, Finished = wasFinished };
});

If you don't want to or aren't able to wrap your code with lambda expressions, you can use the non-lambda pattern of Agent.Tracer.StartTransaction(...) / transaction.StartSpan(...) and transaction.End() / span.End() method pairs.

.NET flavor support, and Elastic APM Server and NuGet packages

The supported .NET flavors and versions haven't changed. What we announced in the previous blog post under "Supported frameworks" still holds: The agent works on every .NET flavor and version that supports .NET Standard 2.0. This means .NET Core 2.0 and newer and .NET Framework 4.6.1. For ASP.NET Core we have automatic instrumentation, and you can use the Public Agent API on every .NET Standard 2.0 compatible .NET version.

The NuGet packages haven't changed either (except the fact that we added new versions). We still have four packages — the Elastic.Apm.All is the default package that references our three other packages. By adding this to an ASP.NET Core application, you are ready to go. Otherwise you can also reference the library specific packages. The Elastic.Apm package contains the base of the agent with the Public Agent API.

The beta agents works with Elastic APM Server version 6.5 and newer. You can keep track of this in the agent/server compatibility page.

What’s next?

Of course our biggest priority for the .NET APM is to reach GA. This will happen based on your feedback — if you have any for us please let us know. If you have a general question, just hop on our Discuss forum. If you have more-detailed feedback where you want to also reference source code, or have a more-detailed feature request, feel free to open an issue in our GitHub repository.

In addition to making existing features production ready, we're also working hard on adding auto instrumentation for ASP.NET on top of .NET Framework.

And of course we're continuing work on closing the gap between the .NET agent and other Elastic APMs.

Thank you!

And finally, I'd like to take this opportunity to say a huge "thank YOU" to all the amazing community members who are already involved in the development of the .NET agent. Thanks for everyone who tried out the agent, and special thanks for community members who opened issues and also implemented features and actively contributed to the agent:

Try out the .NET APM agent beta with your non-production workloads and let us know what you think! You can get started with Elastic APM with a free trial on the Elasticsearch Service, or by downloading the default distribution.