# OpenTelemetry Tracing API for .NET

> Instrument .NET services with the OpenTelemetry Tracing API, create spans, record errors, and send distributed traces to Uptrace.

![undefined](/devicon/dot-net-original.svg)## Prerequisites

<partial path="otel-prereq-dotnet">



</partial>

## OpenTelemetry-DotNet

[OpenTelemetry-DotNet](https://github.com/open-telemetry/opentelemetry-dotnet) is the .NET implementation of OpenTelemetry. It provides the OpenTelemetry Tracing API which you can use to instrument your application with [traces](/opentelemetry/distributed-tracing).

## Activity API

The OpenTelemetry API reuses the .NET [Activity API](https://docs.microsoft.com/en-us/dotnet/api/system.diagnostics.activity?view=net-5.0), which existed long before OpenTelemetry was created. The Activity API covers all OpenTelemetry requirements but uses slightly different API and terminology.

<table>
<thead>
  <tr>
    <th>
      OpenTelemetry API
    </th>
    
    <th>
      .NET Activity API
    </th>
  </tr>
</thead>

<tbody>
  <tr>
    <td>
      Tracer
    </td>
    
    <td>
      ActivitySource
    </td>
  </tr>
  
  <tr>
    <td>
      Span
    </td>
    
    <td>
      Activity
    </td>
  </tr>
  
  <tr>
    <td>
      NoopSpan
    </td>
    
    <td>
      <code>
        null
      </code>
      
       activity
    </td>
  </tr>
  
  <tr>
    <td>
      SpanContext
    </td>
    
    <td>
      ActivityContext
    </td>
  </tr>
  
  <tr>
    <td>
      Span kind
    </td>
    
    <td>
      ActivityKind
    </td>
  </tr>
  
  <tr>
    <td>
      Attribute
    </td>
    
    <td>
      Tag
    </td>
  </tr>
  
  <tr>
    <td>
      SpanLink
    </td>
    
    <td>
      ActivityLink
    </td>
  </tr>
  
  <tr>
    <td>
      SpanEvent
    </td>
    
    <td>
      ActivityEvent
    </td>
  </tr>
  
  <tr>
    <td>
      span.IsRecording()
    </td>
    
    <td>
      activity.IsAllDataRequested
    </td>
  </tr>
</tbody>
</table>

To install the Activity API, add the following dependency to your project:

```xml
<ItemGroup>
  <PackageReference Include="System.Diagnostics.DiagnosticSource" Version="5.0.1" />
</ItemGroup>
```

## Quickstart

**Step 1: Start with Your Function**

Let's instrument the following function:

```cs
public string RedisGet(string key) {
    return this.redisDb.StringGet("mykey");
}
```

**Step 2: Add ActivitySource and Create a Span**

Wrap the operation with a [span](/opentelemetry/distributed-tracing#spans):

```cs
using System.Diagnostics;

var activitySource = new ActivitySource("app_or_lib_name");

public string RedisGet(string key) {
    using var activity = this.activitySource.StartActivity("RedisGet");

    return this.redisDb.StringGet("mykey");
}
```

**Step 3: Handle Errors and Set Status**

Record errors and set [status code](/opentelemetry/distributed-tracing#status-code):

```cs
public string RedisGet(string key) {
    using var activity = this.activitySource.StartActivity("RedisGet");

    try {
        return this.redisDb.StringGet("mykey");
    } catch (Exception ex) {
        activity?.RecordException(ex);
        activity?.SetStatus(Status.Error.WithDescription(ex.Message));
        throw; // Re-throw the exception
    }
}
```

**Step 4: Add Contextual Information**

Record contextual information with [attributes](/opentelemetry/distributed-tracing#attributes):

```cs
public string RedisGet(string key) {
    using var activity = this.activitySource.StartActivity("RedisGet");

    if (activity != null && activity.IsAllDataRequested) {
        activity.SetTag("redis.key", key);
    }

    try {
        return this.redisDb.StringGet("mykey");
    } catch (Exception ex) {
        activity?.RecordException(ex);
        activity?.SetStatus(Status.Error.WithDescription(ex.Message));
        throw;
    }
}
```

And that's it! The operation is fully instrumented.

## Creating a Tracer

To start creating [spans](/opentelemetry/distributed-tracing#spans), you need a tracer (`ActivitySource`). You can create a tracer by providing the name and version of the library/application doing the instrumentation:

```cs
using System.Diagnostics;

static ActivitySource activitySource = new ActivitySource(
    "app_or_package_name",
    "semver1.0.0");
```

You can have as many tracers as you want, but usually you need only one tracer per application or library. Later, you can use tracer names to identify the instrumentation that produces the spans.

## Creating Spans

You can create [spans](/opentelemetry/distributed-tracing#spans) using `StartActivity`. Because `StartActivity` returns a `null` activity when OpenTelemetry is not enabled or the span was not sampled, you need to check for `null` when working with activities:

```cs
var activity = activitySource.StartActivity("operation-name", ActivityKind.Server);

// Perform your operation

// End the span when the operation we are measuring is done
activity?.Stop();
```

You can also create spans in `using` blocks to automatically stop the activity at the end of the block:

```cs
using (var activity = activitySource.StartActivity("operation-name"))
{
    activity?.SetTag("http.method", "GET");
} // Activity is automatically stopped during block disposal
```

## Current Span

OpenTelemetry stores the active span in a [context](/opentelemetry/distributed-tracing#context) and saves the context in a pluggable context storage. You can nest contexts inside each other, and OpenTelemetry will automatically activate the parent span context when you end the span.

To get the active span from the current context:

```cs
// May be null if there is no active span
var activity = Activity.Current;
```

## Adding Span Attributes

To record contextual information, you can annotate spans with [attributes](/opentelemetry/distributed-tracing#attributes). For example, an HTTP endpoint may have attributes such as `http.method = GET` and `http.route = /projects/:id`.

```cs
// To avoid expensive computations, check that the span was sampled
// before setting any attributes
if (activity != null && activity.IsAllDataRequested)
{
    activity.SetTag("http.method", "GET");
    activity.SetTag("http.route", "/projects/:id");
}
```

You can name attributes as you want, but for common operations you should use the [semantic attributes convention](https://opentelemetry.io/docs/reference/specification/trace/semantic_conventions/).

## Adding Span Events

You can annotate spans with [events](/opentelemetry/distributed-tracing#events). For example, you can use events to record log messages:

```cs
activity?.AddEvent(
    new ActivityEvent(
        "log",
        DateTime.UtcNow,
        new ActivityTagsCollection(
            new Dictionary<string, object>
            {
                { "log.severity", "error" },
                { "log.message", "User not found" },
                { "enduser.id", 123 },
            }
        )
    )
);
```

## Setting Status Code

You can set an `error` [status code](/opentelemetry/distributed-tracing#status-code) to indicate that the operation contains an error:

```cs
catch (Exception ex)
{
    activity?.SetStatus(Status.Error.WithDescription(ex.Message));
}
```

## Recording Exceptions

OpenTelemetry provides a shortcut to [record exceptions](https://github.com/open-telemetry/opentelemetry-dotnet/blob/main/docs/trace/reporting-exceptions/README.md), which is usually used together with `SetStatus`:

```cs
using (var activity = activitySource.StartActivity("operation-name"))
{
    try
    {
        Func();
    }
    catch (Exception ex)
    {
        activity?.RecordException(ex);
        activity?.SetStatus(Status.Error.WithDescription(ex.Message));
        throw; // Re-throw if needed
    }
}
```

## What's next?

- [Install and configure OpenTelemetry .NET SDK](/get/opentelemetry-dotnet)
- [Browse more examples](https://github.com/uptrace/uptrace-dotnet/tree/master/example)
- [Learn about OpenTelemetry .NET Sampling](/get/opentelemetry-dotnet/sampling)
- [Learn about OpenTelemetry .NET Metrics API](/get/opentelemetry-dotnet/metrics)
- [Learn about OpenTelemetry .NET Logs](/get/opentelemetry-dotnet/logs)
