In this article we are going to cover How to Integrate OpenTelemetry for .NET Application | instrument a simple .NET application with OpenTelemetry for Traces, logs and metrics.

Prerequisites

  • .NET SDK installed on your machine.
  • A basic understanding of .NET development.
  • Access to a tracing backend like Jaeger, Zipkin, or any other OpenTelemetry-compatible service for exporting telemetry data.

Step#1:Install .NET SDK and .NET Runtime on Ubuntu

Please follow below article to Install .NET SDK and .NET Runtime on Ubuntu

How to Install .NET 8.0 SDK and .NET Runtime on Ubuntu 24.04 LTS

Step #2:Create a simple .NET Application with HTTP Server

To begin, set up an environment in a new directory called RollDiceApp.

sudo mkdir RollDiceApp: This command creates a new folder named “RollDiceApp”. The sudo part gives you the necessary permissions to make changes, and mkdir stands for “make directory.”

cd RollDiceApp: This changes your current working location to the “RollDiceApp” folder so you can start working inside it. The cd stands for “change directory.”

sudo mkdir RollDiceApp
cd RollDiceApp

Within that directory, execute following command

sudo dotnet new web

In the same directory we found a file, replace the content of Program.cs with the following code.

using System.Globalization;

using Microsoft.AspNetCore.Mvc;

var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();

string HandleRollDice([FromServices]ILogger<Program> logger, string? player)
{
    var result = RollDice();

    if (string.IsNullOrEmpty(player))
    {
        logger.LogInformation("Anonymous player is rolling the dice: {result}", result);
    }
    else
    {
        logger.LogInformation("{player} is rolling the dice: {result}", player, result);
    }

    return result.ToString(CultureInfo.InvariantCulture);
}

int RollDice()
{
    return Random.Shared.Next(1, 7);
}

app.MapGet("/rolldice/{player?}", HandleRollDice);

app.Run();

In the Properties subdirectory, replace the content of launchSettings.json with the below code.

{
  "$schema": "http://json.schemastore.org/launchsettings.json",
  "profiles": {
    "http": {
      "commandName": "Project",
      "dotnetRunMessages": true,
      "launchBrowser": true,
      "applicationUrl": "http://localhost:8080",
      "environmentVariables": {
        "ASPNETCORE_ENVIRONMENT": "Development"
      }
    }
  }
}

Build and run the application with the below command.

sudo dotnet build
sudo dotnet run

Output:

ubuntu@ip-172-31-44-31:~/dotnet-simple$ sudo dotnet build
MSBuild version 17.8.5+b5265ef37 for .NET
  Determining projects to restore...
  All projects are up-to-date for restore.
  dotnet-simple -> /home/ubuntu/dotnet-simple/bin/Debug/net8.0/dotnet-simple.dll

Build succeeded.
    0 Warning(s)
    0 Error(s)

Time Elapsed 00:00:01.84
ubuntu@ip-172-31-44-31:~/dotnet-simple$ dotnet run
Building...
info: Microsoft.Hosting.Lifetime[14]
      Now listening on: http://localhost:5277
info: Microsoft.Hosting.Lifetime[0]
      Application started. Press Ctrl+C to shut down.
info: Microsoft.Hosting.Lifetime[0]
      Hosting environment: Development
info: Microsoft.Hosting.Lifetime[0]
      Content root path: /home/ubuntu/dotnet-simple
^Cinfo: Microsoft.Hosting.Lifetime[0]
      Application is shutting down...

Enter the below URL in your web browser to verify it is working.

http://localhost:8080/rolldice OR http://server_IP:8080/rolldice
Integrate OpenTelemetry for .NET Application 1
Integrate OpenTelemetry for .NET Application 2

Number of times if you click on refresh button it gives roll dice the number, as you have seen in above output.

Output:

ubuntu@ip-172-31-35-192:~/dotnet-simple$ sudo dotnet build
MSBuild version 17.8.5+b5265ef37 for .NET
  Determining projects to restore...
  All projects are up-to-date for restore.
  dotnet-simple -> /home/ubuntu/dotnet-simple/bin/Debug/net8.0/dotnet-simple.dll

Build succeeded.
    0 Warning(s)
    0 Error(s)

Time Elapsed 00:00:01.92
ubuntu@ip-172-31-35-192:~/dotnet-simple$ dotnet run
Building...
info: Microsoft.Hosting.Lifetime[14]
      Now listening on: http://0.0.0.0:8080
info: Microsoft.Hosting.Lifetime[0]
      Application started. Press Ctrl+C to shut down.
info: Microsoft.Hosting.Lifetime[0]
      Hosting environment: Development
info: Microsoft.Hosting.Lifetime[0]
      Content root path: /home/ubuntu/dotnet-simple
info: Program[0]
      Anonymous player is rolling the dice: 6
info: Program[0]
      Anonymous player is rolling the dice: 3
info: Program[0]
      Anonymous player is rolling the dice: 4
info: Program[0]
      Anonymous player is rolling the dice: 5
info: Program[0]
      Anonymous player is rolling the dice: 6
info: Program[0]
      Anonymous player is rolling the dice: 3
info: Program[0]
      Anonymous player is rolling the dice: 5
info: Program[0]
      Anonymous player is rolling the dice: 5
info: Program[0]
      Anonymous player is rolling the dice: 3
info: Program[0]
      Anonymous player is rolling the dice: 2

Step#3:Integrate OpenTelemetry for .NET Application

To install the necessary OpenTelemetry instrumentation NuGet packages for your .NET application, you can use the following packages depending on the specific technologies and components you want to instrument. Below are common packages used for ASP.NET Core and general .NET applications.

Run the following commands to install packages that provide automatic instrumentation for ASP.NET Core applications.

Add the packages.

sudo dotnet add package OpenTelemetry.Extensions.Hosting
sudo dotnet add package OpenTelemetry.Instrumentation.AspNetCore
sudo dotnet add package OpenTelemetry.Exporter.Console

Setup the OpenTelemetry code In Program.cs, replace the following lines

var builder = WebApplication.CreateBuilder(args);
var app = builder.Build();

with below code

using OpenTelemetry.Logs;
using OpenTelemetry.Metrics;
using OpenTelemetry.Resources;
using OpenTelemetry.Trace;

var builder = WebApplication.CreateBuilder(args);

const string serviceName = "roll-dice";

builder.Logging.AddOpenTelemetry(options =>
{
    options
        .SetResourceBuilder(
            ResourceBuilder.CreateDefault()
                .AddService(serviceName))
        .AddConsoleExporter();
});
builder.Services.AddOpenTelemetry()
      .ConfigureResource(resource => resource.AddService(serviceName))
      .WithTracing(tracing => tracing
          .AddAspNetCoreInstrumentation()
          .AddConsoleExporter())
      .WithMetrics(metrics => metrics
          .AddAspNetCoreInstrumentation()
          .AddConsoleExporter());

var app = builder.Build();

Run your application once again.

sudo dotnet build
sudo dotnet run

Step#4:Capture Traces, Logs and Metrics for .NET Application by OpenTelemetry

Open in another terminal, send a request using curl or you can access using browser

curl localhost:8080/rolldice
Integrate OpenTelemetry for .NET Application 3

After about 30 sec, stop the server process.

At this point, you should see trace and log output from the server and client that looks something like this (output is line-wrapped for readability)

Resource associated with LogRecord:
service.name: roll-dice
service.instance.id: a6c600b1-5c24-4468-8b7f-e27deed442a2
telemetry.sdk.name: opentelemetry
telemetry.sdk.language: dotnet
telemetry.sdk.version: 1.9.0

Resource associated with Metric:
    service.name: roll-dice
    service.instance.id: a6c600b1-5c24-4468-8b7f-e27deed442a2
    telemetry.sdk.name: opentelemetry
    telemetry.sdk.language: dotnet
    telemetry.sdk.version: 1.9.0
Unhandled exception. System.IO.IOException: Failed to bind to address http://0.0.0.0:8080: address already in use.
 ---> Microsoft.AspNetCore.Connections.AddressInUseException: Address already in use
 ---> System.Net.Sockets.SocketException (98): Address already in use
   at System.Net.Sockets.Socket.UpdateStatusAfterSocketErrorAndThrowException(SocketError error, Boolean disconnectOnFailure, String callerName)
   at System.Net.Sockets.Socket.DoBind(EndPoint endPointSnapshot, SocketAddress socketAddress)
   at System.Net.Sockets.Socket.Bind(EndPoint localEP)
   at Microsoft.AspNetCore.Server.Kestrel.Transport.Sockets.SocketTransportOptions.CreateDefaultBoundListenSocket(EndPoint endpoint)
   at Microsoft.AspNetCore.Server.Kestrel.Transport.Sockets.SocketConnectionListener.Bind()
   --- End of inner exception stack trace ---
   at Microsoft.AspNetCore.Server.Kestrel.Transport.Sockets.SocketConnectionListener.Bind()
   at Microsoft.AspNetCore.Server.Kestrel.Transport.Sockets.SocketTransportFactory.BindAsync(EndPoint endpoint, CancellationToken cancellationToken)
   at Microsoft.AspNetCore.Server.Kestrel.Core.Internal.Infrastructure.TransportManager.BindAsync(EndPoint endPoint, ConnectionDelegate connectionDelegate, EndpointConfig endpointConfig, CancellationToken cancellationToken)
   at Microsoft.AspNetCore.Server.Kestrel.Core.KestrelServerImpl.<>c__DisplayClass28_0`1.<<StartAsync>g__OnBind|0>d.MoveNext()
--- End of stack trace from previous location ---
   at Microsoft.AspNetCore.Server.Kestrel.Core.Internal.AddressBinder.BindEndpointAsync(ListenOptions endpoint, AddressBindContext context, CancellationToken cancellationToken)
   --- End of inner exception stack trace ---
   at Microsoft.AspNetCore.Server.Kestrel.Core.Internal.AddressBinder.BindEndpointAsync(ListenOptions endpoint, AddressBindContext context, CancellationToken cancellationToken)
   at Microsoft.AspNetCore.Server.Kestrel.Core.ListenOptions.BindAsync(AddressBindContext context, CancellationToken cancellationToken)
   at Microsoft.AspNetCore.Server.Kestrel.Core.Internal.AddressBinder.AddressesStrategy.BindAsync(AddressBindContext context, CancellationToken cancellationToken)
   at Microsoft.AspNetCore.Server.Kestrel.Core.Internal.AddressBinder.BindAsync(ListenOptions[] listenOptions, AddressBindContext context, Func`2 useHttps, CancellationToken cancellationToken)
   at Microsoft.AspNetCore.Server.Kestrel.Core.KestrelServerImpl.BindAsync(CancellationToken cancellationToken)
   at Microsoft.AspNetCore.Server.Kestrel.Core.KestrelServerImpl.StartAsync[TContext](IHttpApplication`1 application, CancellationToken cancellationToken)
   at Microsoft.AspNetCore.Hosting.GenericWebHostService.StartAsync(CancellationToken cancellationToken)
   at Microsoft.Extensions.Hosting.Internal.Host.<StartAsync>b__15_1(IHostedService service, CancellationToken token)
   at Microsoft.Extensions.Hosting.Internal.Host.ForeachService[T](IEnumerable`1 services, CancellationToken token, Boolean concurrent, Boolean abortOnFirstException, List`1 exceptions, Func`3 operation)
   at Microsoft.Extensions.Hosting.Internal.Host.StartAsync(CancellationToken cancellationToken)
   at Microsoft.Extensions.Hosting.HostingAbstractionsHostExtensions.RunAsync(IHost host, CancellationToken token)
   at Microsoft.Extensions.Hosting.HostingAbstractionsHostExtensions.RunAsync(IHost host, CancellationToken token)
   at Microsoft.Extensions.Hosting.HostingAbstractionsHostExtensions.Run(IHost host)
   at Program.<Main>$(String[] args) in /home/ubuntu/dotnet-simple/Program.cs:line 55

Here, we have wrapped up with How to Integrate OpenTelemetry for .NET Application | instrument a simple .NET application with OpenTelemetry for Traces, logs and metrics.

Reference:

OpenTelemetry for .NET official page

Similar Posts

Leave a Reply

Your email address will not be published. Required fields are marked *