.NET OpenTelemetry workshop

Course

This lesson is a part of our OpenTelemetry masterclass. If you haven't already, checkout the chapter introduction.

Each lesson in this lab builds on the last one, so make sure you read the workshop introduction before proceeding with this one.

In this workshop, you instrument a .NET web application with OpenTelemetry using the fundamentals you learned in the previous chapters of this masterclass! You also send your telemetry data to your New Relic account and see how useful the data is for monitoring your system and observing its behaviors.

If you haven’t already, sign up for a free New Relic account. You need one to complete this workshop.

Set up your environment

Step 1 of 2

Clone our demo repository:

bash
$
git clone https://github.com/newrelic-experimental/mcv3-apps/
Step 2 of 2

Change to the Uninstrumented/dotnet directory:

bash
$
cd mcv3-apps/Uninstrumented/dotnet

Next, you familiarize yourself with the app logic.

Familiarize yourself with the application

This demo service uses ASP.NET Core, a framework for building web applications.

Controllers/FibonacciController.cs has a single endpoint, called /fibonacci, that takes an argument, n, calculates the nth fibonacci number, and returns the results in a JSON-serialized format:

using System.Diagnostics;
using Microsoft.AspNetCore.Mvc;
namespace dotnet.Controllers;
[ApiController]
[Route("[controller]")]
public class FibonacciController : ControllerBase
{
[HttpGet]
public IActionResult Get(long n)
{
try
{
return Ok(new
{
n = n,
result = Fibonacci(n)
});
}
catch (ArgumentOutOfRangeException ex)
{
return BadRequest(new { message = ex.Message });
}
}
private long Fibonacci(long n)
{
var result = 0L;
if (n <= 2)
{
result = 1;
}
else
{
var a = 0L;
var b = 1L;
for (var i = 1; i < n; i++)
{
result = checked(a + b);
a = b;
b = result;
}
}
return result;
}
private void ThrowIfOutOfRange(long n)
{
if (n < 1 || n > 90)
{
throw new ArgumentOutOfRangeException(nameof(n), n, "n must be between 1 and 90");
}
}
}
Controllers/FibonacciController.cs

The logic for calculating the nth fibonacci number is contained within a function called Fibonacci():

using System.Diagnostics;
using Microsoft.AspNetCore.Mvc;
namespace dotnet.Controllers;
[ApiController]
[Route("[controller]")]
public class FibonacciController : ControllerBase
{
[HttpGet]
public IActionResult Get(long n)
{
try
{
return Ok(new
{
n = n,
result = Fibonacci(n)
});
}
catch (ArgumentOutOfRangeException ex)
{
return BadRequest(new { message = ex.Message });
}
}
private long Fibonacci(long n)
{
var result = 0L;
if (n <= 2)
{
result = 1;
}
else
{
var a = 0L;
var b = 1L;
for (var i = 1; i < n; i++)
{
result = checked(a + b);
a = b;
b = result;
}
}
return result;
}
private void ThrowIfOutOfRange(long n)
{
if (n < 1 || n > 90)
{
throw new ArgumentOutOfRangeException(nameof(n), n, "n must be between 1 and 90");
}
}
}
Controllers/FibonacciController.cs

This function takes the argument, n, and checks if it’s between 1 and 90. If it’s not, the function throws an ArgumentOutOfRangeException and rejects the request. Otherwise, it computes and returns the nth fibonacci number.

In Program.cs, you configure and build your ASP.NET Core app:

using dotnet.Controllers;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddControllers();
var app = builder.Build();
app.MapControllers();
app.Run();
Program.cs

You use this configuration to instrument your application in the next section.

Those are the most important functions you need to know about this .NET application before you instrument it. Next, you install your OpenTelemetry dependencies.

Install dependencies

Before you can instrument your application, you need to add some OpenTelemetry dependencies to your project. In the dotnet directory, you’ll find a dotnet.csproj file. This holds the dependency requirements for the demo application.

Step 1 of 2

From your shell, add the OpenTelemetry SDK and supporting packages (please note that these versions are necessary to run this tutorial):

bash
$
dotnet add package OpenTelemetry --version 1.2.0-rc1
$
dotnet add package OpenTelemetry.Api --version 1.2.0-rc1
$
dotnet add package OpenTelemetry.Exporter.OpenTelemetryProtocol --version 1.2.0-rc1
$
dotnet add package OpenTelemetry.Extensions.Hosting --version 1.0.0-rc8
$
dotnet add package OpenTelemetry.Instrumentation.AspNetCore --version 1.0.0-rc8

Here, you added several OpenTelemetry packages to your application:

  • OpenTelemetry: Provides the .NET OpenTelemetry SDK that implements the API
  • OpenTelemetry.Api: Provides the API for managing your telemetry data
  • OpenTelemetry.Exporter.OpenTelemetryProtocol: Provides the OTLP exporter
  • OpenTelemetry.Extensions.Hosting: Provides extensions to register OpenTelemetry with your application
  • OpenTelemetry.Instrumentation.AspNetCore: Provides automatic instrumentation for ASP.NET Core

Tip

These dependencies are sufficient for this workshop. In your applications, you might need other packages.

Step 2 of 2

See your dependencies in dotnet.csproj:

<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="OpenTelemetry" Version="1.2.0-rc2" />
<PackageReference Include="OpenTelemetry.Api" Version="1.2.0-rc2" />
<PackageReference Include="OpenTelemetry.Exporter.OpenTelemetryProtocol" Version="1.2.0-rc2" />
<PackageReference Include="OpenTelemetry.Extensions.Hosting" Version="1.0.0-rc9" />
<PackageReference Include="OpenTelemetry.Instrumentation.AspNetCore" Version="1.0.0-rc9" />
</ItemGroup>
</Project>
dotnet.csproj

Now, you’re ready to instrument your app.

Instrument your application

You’ve set up your environment, read the code, and spun up your app and a load generator. Now, it’s time to instrument your app with OpenTelemetry!

Step 1 of 9

In Program.cs, create a ResourceBuilder:

1
using dotnet.Controllers;
2
using OpenTelemetry.Resources;
3
4
var builder = WebApplication.CreateBuilder(args);
5
6
builder.Services.AddControllers();
7
8
var resourceBuilder = ResourceBuilder
9
.CreateDefault()
10
.AddService("fibonacci")
11
.AddTelemetrySdk();
12
13
var app = builder.Build();
14
15
app.MapControllers();
16
17
app.Run();
Program.cs
1
using Microsoft.AspNetCore.Mvc;
2
3
namespace dotnet.Controllers;
4
5
[ApiController]
6
[Route("[controller]")]
7
public class FibonacciController : ControllerBase
8
{
9
10
[HttpGet]
11
public IActionResult Get(long n)
12
{
13
try
14
{
15
return Ok(new
16
{
17
n = n,
18
result = Fibonacci(n)
19
});
20
}
21
catch (ArgumentOutOfRangeException ex)
22
{
23
return BadRequest(new { message = ex.Message });
24
}
25
}
26
27
private long Fibonacci(long n)
28
{
29
ThrowIfOutOfRange(n);
30
var result = 0L;
31
if (n <= 2)
32
{
33
result = 1;
34
}
35
else
36
{
37
var a = 0L;
38
var b = 1L;
39
40
for (var i = 1; i < n; i++)
41
{
42
result = checked(a + b);
43
a = b;
44
b = result;
45
}
46
}
47
48
return result;
49
}
50
51
private void ThrowIfOutOfRange(long n)
52
{
53
if (n < 1 || n > 90)
54
{
55
throw new ArgumentOutOfRangeException(nameof(n), n, "n must be between 1 and 90");
56
}
57
}
58
}
Controllers/FibonacciController.cs
1
version: '3'
2
services:
3
fibonacci:
4
build: ./
5
ports:
6
- "8080:80"
7
load-generator:
8
build: ./load-generator
dotnet/docker-compose.yaml

Here, you use the ResourceBuilder from the OpenTelemetry.Resources namespace to create a resource. With it, you can compose resource attributes from different semantic convention categories.

Specifically, you use:

  1. .CreateDefault(), which adds some attributes about the current process.

  2. .AddService() with the service name "fibonacci". This sets the conventional attribute service.name.

  3. .AddTelemetrySdk() to set some conventional telemetry SDK attributes. These are:

    • telemetry.sdk.name
    • telemetry.sdk.language
    • telemetry.sdk.version
Step 2 of 9

Configure OpenTelemetry tracing:

1
using dotnet.Controllers;
2
using OpenTelemetry.Resources;
3
using OpenTelemetry.Trace;
4
5
var builder = WebApplication.CreateBuilder(args);
6
7
builder.Services.AddControllers();
8
9
var resourceBuilder = ResourceBuilder
10
.CreateDefault()
11
.AddService("fibonacci")
12
.AddTelemetrySdk();
13
14
builder.Services.AddOpenTelemetryTracing(tracerProviderBuilder =>
15
{
16
tracerProviderBuilder
17
.SetResourceBuilder(resourceBuilder)
18
.AddSource(FibonacciController.ActivitySourceName)
19
.AddAspNetCoreInstrumentation()
20
.AddOtlpExporter();
21
});
22
23
var app = builder.Build();
24
25
app.MapControllers();
26
27
app.Run();
Program.cs
1
using Microsoft.AspNetCore.Mvc;
2
3
namespace dotnet.Controllers;
4
5
[ApiController]
6
[Route("[controller]")]
7
public class FibonacciController : ControllerBase
8
{
9
10
[HttpGet]
11
public IActionResult Get(long n)
12
{
13
try
14
{
15
return Ok(new
16
{
17
n = n,
18
result = Fibonacci(n)
19
});
20
}
21
catch (ArgumentOutOfRangeException ex)
22
{
23
return BadRequest(new { message = ex.Message });
24
}
25
}
26
27
private long Fibonacci(long n)
28
{
29
ThrowIfOutOfRange(n);
30
var result = 0L;
31
if (n <= 2)
32
{
33
result = 1;
34
}
35
else
36
{
37
var a = 0L;
38
var b = 1L;
39
40
for (var i = 1; i < n; i++)
41
{
42
result = checked(a + b);
43
a = b;
44
b = result;
45
}
46
}
47
48
return result;
49
}
50
51
private void ThrowIfOutOfRange(long n)
52
{
53
if (n < 1 || n > 90)
54
{
55
throw new ArgumentOutOfRangeException(nameof(n), n, "n must be between 1 and 90");
56
}
57
}
58
}
Controllers/FibonacciController.cs
1
version: '3'
2
services:
3
fibonacci:
4
build: ./
5
ports:
6
- "8080:80"
7
load-generator:
8
build: ./load-generator
dotnet/docker-compose.yaml

Here, you use .AddOpenTelemetryTracing() to inject a new TracerProviderBuilder into your WebApplicationBuilder. You configure your TracerProviderBuilder with:

  • The resource builder you created in the last step
  • A tracer for your application
  • Automatic instrumentation for ASP.NET Core
  • An OTLP exporter

You named the tracer that you created with .AddSource() using a constant, called ActivitySourceName. You’ll create this in a later step. Before you do, you need to configure your OTLP exporter.

Step 3 of 9

The exporter you configured in the last step needs two important values:

  • A location where you want to send the data (New Relic, in this case)
  • An API key so that New Relic can accept your data and associate it to your account

In Uninstrumented/dotnet/docker-compose.yaml, configure your OTLP exporter:

1
using dotnet.Controllers;
2
using OpenTelemetry.Resources;
3
using OpenTelemetry.Trace;
4
5
var builder = WebApplication.CreateBuilder(args);
6
7
builder.Services.AddControllers();
8
9
var resourceBuilder = ResourceBuilder
10
.CreateDefault()
11
.AddService("fibonacci")
12
.AddTelemetrySdk();
13
14
builder.Services.AddOpenTelemetryTracing(tracerProviderBuilder =>
15
{
16
tracerProviderBuilder
17
.SetResourceBuilder(resourceBuilder)
18
.AddSource(FibonacciController.ActivitySourceName)
19
.AddAspNetCoreInstrumentation()
20
.AddOtlpExporter();
21
});
22
23
var app = builder.Build();
24
25
app.MapControllers();
26
27
app.Run();
Program.cs
1
using Microsoft.AspNetCore.Mvc;
2
3
namespace dotnet.Controllers;
4
5
[ApiController]
6
[Route("[controller]")]
7
public class FibonacciController : ControllerBase
8
{
9
10
[HttpGet]
11
public IActionResult Get(long n)
12
{
13
try
14
{
15
return Ok(new
16
{
17
n = n,
18
result = Fibonacci(n)
19
});
20
}
21
catch (ArgumentOutOfRangeException ex)
22
{
23
return BadRequest(new { message = ex.Message });
24
}
25
}
26
27
private long Fibonacci(long n)
28
{
29
ThrowIfOutOfRange(n);
30
var result = 0L;
31
if (n <= 2)
32
{
33
result = 1;
34
}
35
else
36
{
37
var a = 0L;
38
var b = 1L;
39
40
for (var i = 1; i < n; i++)
41
{
42
result = checked(a + b);
43
a = b;
44
b = result;
45
}
46
}
47
48
return result;
49
}
50
51
private void ThrowIfOutOfRange(long n)
52
{
53
if (n < 1 || n > 90)
54
{
55
throw new ArgumentOutOfRangeException(nameof(n), n, "n must be between 1 and 90");
56
}
57
}
58
}
Controllers/FibonacciController.cs
1
version: '3'
2
services:
3
fibonacci:
4
build: ./
5
ports:
6
- "8080:80"
7
environment:
8
OTEL_EXPORTER_OTLP_ENDPOINT: https://otlp.nr-data.net:4317
9
OTEL_EXPORTER_OTLP_HEADERS: "api-key=${NEW_RELIC_API_KEY}"
10
load-generator:
11
build: ./load-generator
dotnet/docker-compose.yaml

The first variable you set configures the exporter to send telemetry data to https://otlp.nr-data.net:4317. This is our US-based OTLP endpoint. If you’re in the EU, use https://otlp.eu01.nr-data.net:4317 instead.

The second variable passes the NEW_RELIC_API_KEY from your local machine in the api-key header of your OTLP requests. You set this environment variable in the next step.

The OTLP exporter looks for these variables in the Docker container’s environment and applies them at runtime.

Step 4 of 9

Get or create a New Relic license key for your account, and set it in an environment variable called NEW_RELIC_API_KEY:

bash
$
export NEW_RELIC_API_KEY=<YOUR_LICENSE_KEY>

Important

Don’t forget to replace <YOUR_LICENSE_KEY> with your real license key!

Step 5 of 9

In Controller/FibonacciController.cs, create a tracer:

1
using dotnet.Controllers;
2
using OpenTelemetry.Resources;
3
using OpenTelemetry.Trace;
4
5
var builder = WebApplication.CreateBuilder(args);
6
7
builder.Services.AddControllers();
8
9
var resourceBuilder = ResourceBuilder
10
.CreateDefault()
11
.AddService("fibonacci")
12
.AddTelemetrySdk();
13
14
builder.Services.AddOpenTelemetryTracing(tracerProviderBuilder =>
15
{
16
tracerProviderBuilder
17
.SetResourceBuilder(resourceBuilder)
18
.AddSource(FibonacciController.ActivitySourceName)
19
.AddAspNetCoreInstrumentation()
20
.AddOtlpExporter();
21
});
22
23
var app = builder.Build();
24
25
app.MapControllers();
26
27
app.Run();
Program.cs
1
using System.Diagnostics;
2
using Microsoft.AspNetCore.Mvc;
3
4
namespace dotnet.Controllers;
5
6
[ApiController]
7
[Route("[controller]")]
8
public class FibonacciController : ControllerBase
9
{
10
public const string ActivitySourceName = "FibonacciService";
11
private ActivitySource activitySource = new ActivitySource(ActivitySourceName);
12
13
[HttpGet]
14
public IActionResult Get(long n)
15
{
16
try
17
{
18
return Ok(new
19
{
20
n = n,
21
result = Fibonacci(n)
22
});
23
}
24
catch (ArgumentOutOfRangeException ex)
25
{
26
return BadRequest(new { message = ex.Message });
27
}
28
}
29
30
private long Fibonacci(long n)
31
{
32
ThrowIfOutOfRange(n);
33
var result = 0L;
34
if (n <= 2)
35
{
36
result = 1;
37
}
38
else
39
{
40
var a = 0L;
41
var b = 1L;
42
43
for (var i = 1; i < n; i++)
44
{
45
result = checked(a + b);
46
a = b;
47
b = result;
48
}
49
}
50
51
return result;
52
}
53
54
private void ThrowIfOutOfRange(long n)
55
{
56
if (n < 1 || n > 90)
57
{
58
throw new ArgumentOutOfRangeException(nameof(n), n, "n must be between 1 and 90");
59
}
60
}
61
}
Controllers/FibonacciController.cs
1
version: '3'
2
services:
3
fibonacci:
4
build: ./
5
ports:
6
- "8080:80"
7
environment:
8
OTEL_EXPORTER_OTLP_ENDPOINT: https://otlp.nr-data.net:4317
9
OTEL_EXPORTER_OTLP_HEADERS: "api-key=${NEW_RELIC_API_KEY}"
10
load-generator:
11
build: ./load-generator
dotnet/docker-compose.yaml

In .NET, the OpenTelemetry tracer and span APIs make use of existing interfaces from the System.Diagnostics namespace, called ActivitySource and Activity, respectively. They both adhere to the specification.

Step 6 of 9

At the top of Fibonacci(), use your tracer to start a new span:

1
using dotnet.Controllers;
2
using OpenTelemetry.Resources;
3
using OpenTelemetry.Trace;
4
5
var builder = WebApplication.CreateBuilder(args);
6
7
builder.Services.AddControllers();
8
9
var resourceBuilder = ResourceBuilder
10
.CreateDefault()
11
.AddService("fibonacci")
12
.AddTelemetrySdk();
13
14
builder.Services.AddOpenTelemetryTracing(tracerProviderBuilder =>
15
{
16
tracerProviderBuilder
17
.SetResourceBuilder(resourceBuilder)
18
.AddSource(FibonacciController.ActivitySourceName)
19
.AddAspNetCoreInstrumentation()
20
.AddOtlpExporter();
21
});
22
23
var app = builder.Build();
24
25
app.MapControllers();
26
27
app.Run();
Program.cs
1
using System.Diagnostics;
2
using Microsoft.AspNetCore.Mvc;
3
4
namespace dotnet.Controllers;
5
6
[ApiController]
7
[Route("[controller]")]
8
public class FibonacciController : ControllerBase
9
{
10
public const string ActivitySourceName = "FibonacciService";
11
private ActivitySource activitySource = new ActivitySource(ActivitySourceName);
12
13
[HttpGet]
14
public IActionResult Get(long n)
15
{
16
try
17
{
18
return Ok(new
19
{
20
n = n,
21
result = Fibonacci(n)
22
});
23
}
24
catch (ArgumentOutOfRangeException ex)
25
{
26
return BadRequest(new { message = ex.Message });
27
}
28
}
29
30
private long Fibonacci(long n)
31
{
32
using var activity = activitySource.StartActivity(nameof(Fibonacci));
33
34
ThrowIfOutOfRange(n);
35
var result = 0L;
36
if (n <= 2)
37
{
38
result = 1;
39
}
40
else
41
{
42
var a = 0L;
43
var b = 1L;
44
45
for (var i = 1; i < n; i++)
46
{
47
result = checked(a + b);
48
a = b;
49
b = result;
50
}
51
}
52
53
return result;
54
}
55
56
private void ThrowIfOutOfRange(long n)
57
{
58
if (n < 1 || n > 90)
59
{
60
throw new ArgumentOutOfRangeException(nameof(n), n, "n must be between 1 and 90");
61
}
62
}
63
}
Controllers/FibonacciController.cs
1
version: '3'
2
services:
3
fibonacci:
4
build: ./
5
ports:
6
- "8080:80"
7
environment:
8
OTEL_EXPORTER_OTLP_ENDPOINT: https://otlp.nr-data.net:4317
9
OTEL_EXPORTER_OTLP_HEADERS: "api-key=${NEW_RELIC_API_KEY}"
10
load-generator:
11
build: ./load-generator
dotnet/docker-compose.yaml

activitySource.StartActivity() starts a new span (called an Activity in .NET). With this call, you pass the name of the Fibonacci() function.

Step 7 of 9

Add some attributes to capture important values in the execution:

1
using dotnet.Controllers;
2
using OpenTelemetry.Resources;
3
using OpenTelemetry.Trace;
4
5
var builder = WebApplication.CreateBuilder(args);
6
7
builder.Services.AddControllers();
8
9
var resourceBuilder = ResourceBuilder
10
.CreateDefault()
11
.AddService("fibonacci")
12
.AddTelemetrySdk();
13
14
builder.Services.AddOpenTelemetryTracing(tracerProviderBuilder =>
15
{
16
tracerProviderBuilder
17
.SetResourceBuilder(resourceBuilder)
18
.AddSource(FibonacciController.ActivitySourceName)
19
.AddAspNetCoreInstrumentation()
20
.AddOtlpExporter();
21
});
22
23
var app = builder.Build();
24
25
app.MapControllers();
26
27
app.Run();
Program.cs
1
using System.Diagnostics;
2
using Microsoft.AspNetCore.Mvc;
3
4
namespace dotnet.Controllers;
5
6
[ApiController]
7
[Route("[controller]")]
8
public class FibonacciController : ControllerBase
9
{
10
public const string ActivitySourceName = "FibonacciService";
11
private ActivitySource activitySource = new ActivitySource(ActivitySourceName);
12
13
[HttpGet]
14
public IActionResult Get(long n)
15
{
16
try
17
{
18
return Ok(new
19
{
20
n = n,
21
result = Fibonacci(n)
22
});
23
}
24
catch (ArgumentOutOfRangeException ex)
25
{
26
return BadRequest(new { message = ex.Message });
27
}
28
}
29
30
private long Fibonacci(long n)
31
{
32
using var activity = activitySource.StartActivity(nameof(Fibonacci));
33
activity?.SetTag("fibonacci.n", n);
34
35
ThrowIfOutOfRange(n);
36
var result = 0L;
37
if (n <= 2)
38
{
39
result = 1;
40
}
41
else
42
{
43
var a = 0L;
44
var b = 1L;
45
46
for (var i = 1; i < n; i++)
47
{
48
result = checked(a + b);
49
a = b;
50
b = result;
51
}
52
}
53
54
activity?.SetTag("fibonacci.result", result);
55
return result;
56
}
57
58
private void ThrowIfOutOfRange(long n)
59
{
60
if (n < 1 || n > 90)
61
{
62
throw new ArgumentOutOfRangeException(nameof(n), n, "n must be between 1 and 90");
63
}
64
}
65
}
Controllers/FibonacciController.cs
1
version: '3'
2
services:
3
fibonacci:
4
build: ./
5
ports:
6
- "8080:80"
7
environment:
8
OTEL_EXPORTER_OTLP_ENDPOINT: https://otlp.nr-data.net:4317
9
OTEL_EXPORTER_OTLP_HEADERS: "api-key=${NEW_RELIC_API_KEY}"
10
load-generator:
11
build: ./load-generator
dotnet/docker-compose.yaml

Because .NET implements the span specification with its Activity class, some of the API looks slightly different. Here you see that attributes are called "tags" in the .NET implementation.

The first attribute you set is called “fibonacci.n”, which stores the value of n from the user’s request. If the computation was successful, you set “fibonacci.result”, which captures the result.

Step 8 of 9

Record an exception span event if n is invalid:

1
using dotnet.Controllers;
2
using OpenTelemetry.Resources;
3
using OpenTelemetry.Trace;
4
5
var builder = WebApplication.CreateBuilder(args);
6
7
builder.Services.AddControllers();
8
9
var resourceBuilder = ResourceBuilder
10
.CreateDefault()
11
.AddService("fibonacci")
12
.AddTelemetrySdk();
13
14
builder.Services.AddOpenTelemetryTracing(tracerProviderBuilder =>
15
{
16
tracerProviderBuilder
17
.SetResourceBuilder(resourceBuilder)
18
.AddSource(FibonacciController.ActivitySourceName)
19
.AddAspNetCoreInstrumentation()
20
.AddOtlpExporter();
21
});
22
23
var app = builder.Build();
24
25
app.MapControllers();
26
27
app.Run();
Program.cs
1
using System.Diagnostics;
2
using Microsoft.AspNetCore.Mvc;
3
using OpenTelemetry.Trace;
4
5
namespace dotnet.Controllers;
6
7
[ApiController]
8
[Route("[controller]")]
9
public class FibonacciController : ControllerBase
10
{
11
public const string ActivitySourceName = "FibonacciService";
12
private ActivitySource activitySource = new ActivitySource(ActivitySourceName);
13
14
[HttpGet]
15
public IActionResult Get(long n)
16
{
17
try
18
{
19
return Ok(new
20
{
21
n = n,
22
result = Fibonacci(n)
23
});
24
}
25
catch (ArgumentOutOfRangeException ex)
26
{
27
return BadRequest(new { message = ex.Message });
28
}
29
}
30
31
private long Fibonacci(long n)
32
{
33
using var activity = activitySource.StartActivity(nameof(Fibonacci));
34
activity?.SetTag("fibonacci.n", n);
35
36
try
37
{
38
ThrowIfOutOfRange(n);
39
}
40
catch (ArgumentOutOfRangeException ex)
41
{
42
activity?.SetStatus(Status.Error.WithDescription(ex.Message));
43
activity?.RecordException(ex);
44
throw;
45
}
46
47
var result = 0L;
48
if (n <= 2)
49
{
50
result = 1;
51
}
52
else
53
{
54
var a = 0L;
55
var b = 1L;
56
57
for (var i = 1; i < n; i++)
58
{
59
result = checked(a + b);
60
a = b;
61
b = result;
62
}
63
}
64
65
activity?.SetTag("fibonacci.result", result);
66
return result;
67
}
68
69
private void ThrowIfOutOfRange(long n)
70
{
71
if (n < 1 || n > 90)
72
{
73
throw new ArgumentOutOfRangeException(nameof(n), n, "n must be between 1 and 90");
74
}
75
}
76
}
Controllers/FibonacciController.cs
1
version: '3'
2
services:
3
fibonacci:
4
build: ./
5
ports:
6
- "8080:80"
7
environment:
8
OTEL_EXPORTER_OTLP_ENDPOINT: https://otlp.nr-data.net:4317
9
OTEL_EXPORTER_OTLP_HEADERS: "api-key=${NEW_RELIC_API_KEY}"
10
load-generator:
11
build: ./load-generator
dotnet/docker-compose.yaml

If n is invalid, you throw an ArgumentOutofRangeException, record an exception span event on the span, and set the span’s status to Status.Error. You also use the exception message for the status description.

Step 9 of 9

In the same shell you used to export your environment variable, navigate to Uninstrumented/dotnet, then spin up the project's containers with Docker Compose:

bash
$
docker-compose up --build

This runs two docker services:

  • fibonacci: Your app service
  • load-generator: A service that simulates traffic to your app

The load generator makes periodic requests to your application. It sends a mixture of requests that you expect to succeed and ones that you expect to fail. Looking at the Docker Compose log stream, you should see that both your application and load generator are running.

You’re now ready to view your data in New Relic.

Course

This lesson is a part of our OpenTelemetry masterclass. Continue on to the next lesson: View a summary of your data.