Custom Instrumentation

To instrument certain regions of your code, you can create transactions to capture them.

Copied
// Transaction can be started by providing, at minimum, the name and the operation
var transaction = SentrySdk.StartTransaction(
  "test-transaction-name",
  "test-transaction-operation"
);

// Transactions can have child spans (and those spans can have child spans as well)
var span = transaction.StartChild("test-child-operation");

// ...
// (Perform the operation represented by the span/transaction)
// ...

span.Finish(); // Mark the span as finished
transaction.Finish(); // Mark the transaction as finished and send it to Sentry

For example, if you want to create a transaction for a user interaction in your application:

Copied
// Let's say this method is invoked when a user clicks on the checkout button of your shop
public async Task PerformCheckoutAsync()
{
  // This will create a new Transaction for you
  var transaction = SentrySdk.StartTransaction(
      "checkout", // name
      "perform-checkout" // operation
  );
  
  // Set transaction on scope to associate with errors and get included span instrumentation
  // If there's currently an unfinished transaction, it may be dropped
  SentrySdk.ConfigureScope(scope => scope.Transaction = transaction);

  // Validate the cart
  var validationSpan = transaction.StartChild(
      "validation", // operation
      "validating shopping cart" // description
  );

  await ValidateShoppingCartAsync();

  validationSpan.Finish();

  // Process the oder
  var processSpan = transaction.StartChild(
      "process", // operation
      "processing shopping cart" // description
  )

  await ProcessShoppingCartAsync();

  processSpan.Finish();

  transaction.Finish();
}

This example will send a transaction checkout to Sentry. The transaction will contain a validation span that measures how long ValidateShoppingCartAsync took and a process span that measures ProcessShoppingCartAsync. Finally, the call to transaction.Finish() will finish the transaction and send it to Sentry.

Retrieve a Transaction

In cases where you want to attach Spans to an already ongoing Transaction you can use Sentry#GetSpan. This method will return a SentryTransaction in case there is a running Transaction or a Span in case there is already a running Span, otherwise it returns null.

Copied
using Sentry;

var span = SentrySdk.GetSpan();

if (span == null)
{
    span = SentrySdk.StartTransaction("task", "op");
}
else
{
    span = span.StartChild("subtask");
}

Distributed Tracing

Traces can bridge across multiple software services. Each span in a trace can be represented as a sentry-trace header, containing the trace id, span id, and sampling details. This sentry-trace header can be passed to downstream services so that they can create spans that are a continuation of the trace started in the originating service.

To obtain a trace header from the span, use ISpan.GetTraceHeader() method, then pass it to the downstream service.

The code snippet below demonstrates propagating the trace programatically. Even though SentryHttpMessageHandler does that automatically for you, this illustrates the mechanism so it can be replicated in other protocols and technologies such a messaging systems.

Copied
using System;
using System.Net.Http;
using Sentry;

var transaction = SentrySdk.StartTransaction("name", "op");
var sentryTraceHeader = transaction.GetTraceHeader();

using var request = new HttpRequestMessage(HttpMethod.Get, "https://example.com");
request.Headers.Add("sentry-trace", sentryTraceHeader.ToString());

To create a span as a continuation of the trace retrieved from the upstream service, pass the sentry-trace header value to the StartTransaction(...) method:

Copied
using System;
using Sentry;

var httpContext = ...;

if (httpContext.Request.Headers.TryGetValue("sentry-trace", out var sentryTraceHeaderValue))
{
    try
    {
        var sentryTraceHeader = SentryTraceHeader.Parse(sentryTraceHeaderValue);
        var transaction = SentrySdk.StartTransaction("name", "op", sentryTraceHeader);
    }
    catch (FormatException e)
    {
        // Handle invalid trace header
        e.Data["bad-sentry-header"] = sentryTraceHeaderValue;
        SentrySdk.CaptureException(e);
    }
}