TraceKitTraceKit Docs
Backend SDKs

.NET / C# Integration Guide

Learn how to integrate TraceKit with .NET and ASP.NET Core. Automatic distributed tracing with OpenTelemetry for debugging production C# applications.

Learn how to instrument your .NET and ASP.NET Core applications with TraceKit APM for OpenTelemetry-based distributed tracing.

Let AI set up TraceKit for you -- AI assistants can guide you through the entire setup process. Try AI Setup

Prerequisites

  • .NET 8.0 or higher
  • ASP.NET Core 8.0+ (for web applications)
  • A TraceKit account (create one free)
  • A generated API key from the API Keys page

What Gets Traced Automatically?

With TraceKit .NET SDK installed, these operations are traced automatically:

ComponentSpan TypeCaptured AttributesAuto-Traced?
HTTP RequestsSERVERRoute, method, status, duration, headersYes
HttpClient CallsCLIENTmethod, url, status_code, durationYes
Database QueriesDBdb.system, db.statement, db.name, durationYes (via Entity Framework / ADO.NET)
ExceptionsN/Aexception.type, exception.message, stack_traceYes
Custom SpansCustomuser-defined attributesManual

Installation

Install the TraceKit .NET SDK via NuGet:

For ASP.NET Core

dotnet add package TraceKit.AspNetCore

For Vanilla .NET

dotnet add package TraceKit.Core

ASP.NET Core Setup

Program.cs

Add TraceKit to your application in Program.cs:

using TraceKit.AspNetCore;

var builder = WebApplication.CreateBuilder(args);

// Add TraceKit
builder.Services.AddTracekit(options =>
{
    options.ApiKey = Environment.GetEnvironmentVariable("TRACEKIT_API_KEY");
    options.ServiceName = "my-service";
    options.Environment = "production";
    options.EnableCodeMonitoring = true;
});

var app = builder.Build();

// Use TraceKit middleware
app.UseTracekit();

app.MapGet("/api/users", (TracekitSDK sdk) =>
{
    var counter = sdk.Counter("http.requests.total");
    counter.Inc();

    return Results.Ok(new { message = "Hello" });
});

app.Run();

That's It! All HTTP requests and HttpClient calls are now automatically traced with distributed context propagation!

ASP.NET Core API Reference

SymbolTypeRegistrationDescription
TracekitMiddlewareIMiddlewareapp.UseTracekit()Automatic SERVER span creation on all incoming requests.
TracekitOptionsOptions ClassAddTracekit(opts)Options for middleware behavior including RouteFilter and IgnorePatterns.
AddTracekitInstrumentation()Extension MethodIHttpClientBuilderInstruments HttpClient for automatic CLIENT spans on outgoing calls.

HTTP Client Instrumentation

Add tracing to outgoing HttpClient calls:

// Program.cs -- Add HTTP client instrumentation
builder.Services.AddHttpClient("my-api", client => {
    client.BaseAddress = new Uri("https://api.example.com");
}).AddTracekitInstrumentation();

// Or instrument the default HttpClient
builder.Services.AddHttpClient()
    .AddTracekitInstrumentation();

// Now all HttpClient calls create CLIENT spans automatically!

Verify It Works -- Start your application and make a few requests. Then open the Traces page in your TraceKit dashboard. You should see:

  • SERVER spans for each incoming ASP.NET Core request with route and status
  • CLIENT spans for HttpClient calls
  • DB spans for Entity Framework / ADO.NET queries

Traces typically appear within 30 seconds. If you don't see them, check the Troubleshooting section.

Configuration

appsettings.json

Configure TraceKit in your appsettings.json:

{
  "Tracekit": {
    "Enabled": true,
    "ApiKey": "ctxio_abc123...",
    "ServiceName": "my-api",
    "Environment": "production",
    "Endpoint": "https://app.tracekit.dev",
    "EnableCodeMonitoring": true
  }
}

Configuration Options

OptionTypeDescription
ApiKeystringYour TraceKit API key (required)
ServiceNamestringName of your service
EnvironmentstringEnvironment (dev, staging, prod)
EndpointstringTraceKit endpoint URL
EnableCodeMonitoringboolEnable live debugging snapshots

Code Monitoring (Live Debugging)

Production-Safe Live Debugging -- Capture variable state, stack traces, and request context at any point in your code without redeployment. Perfect for debugging production issues! Full Code Monitoring Documentation

Enable Code Monitoring

Code monitoring is enabled by default in the .NET SDK. To configure explicitly:

// Option 1: Builder API
var sdk = TracekitSDK.CreateBuilder()
    .WithApiKey(Environment.GetEnvironmentVariable("TRACEKIT_API_KEY"))
    .WithServiceName("my-dotnet-app")
    .WithEnableCodeMonitoring(true)  // default: true
    .Build();

// Option 2: ASP.NET Core Dependency Injection
builder.Services.AddTracekit(options => {
    options.ApiKey = Environment.GetEnvironmentVariable("TRACEKIT_API_KEY");
    options.ServiceName = "my-dotnet-app";
    options.EnableCodeMonitoring = true;  // default: true
});

Default: true -- code monitoring is enabled by default in the .NET SDK. Set to false to disable.

Capture Snapshots

Use CaptureSnapshot to capture runtime state:

app.MapPost("/api/checkout", (CheckoutRequest request, TracekitSDK sdk) =>
{
    // Capture snapshot with variable state
    sdk.CaptureSnapshot("checkout-start", new()
    {
        ["userId"] = request.UserId,
        ["amount"] = request.Amount,
        ["status"] = "processing"
    });

    // Your business logic here
    var result = ProcessCheckout(request);

    return Results.Ok(result);
});

Automatic Security Scanning -- TraceKit automatically detects and redacts sensitive data like credit cards, SSNs, API keys, and passwords before sending snapshots.

Production Safety

All production safety features are enabled by default -- no configuration required.

  • PII Scrubbing -- 13 built-in patterns via SecurityPatterns detect sensitive data (passwords, tokens, SSNs, credit cards). Enabled by default with typed [REDACTED:type] markers.
  • Crash Isolation -- All entry points use catch(Exception) to isolate failures. Snapshot errors never crash your application.
  • Circuit Breaker -- After 3 failures within 60 seconds, the circuit opens for a 5-minute cooldown. Uses lock-based thread safety. Automatically recovers when the backend is healthy again.
  • Remote Kill Switch -- Toggle code monitoring on or off from the dashboard. Changes propagate via SSE (CancellationToken-aware) in real-time -- no restart required.

Real-Time Updates -- The .NET SDK uses SSE (Server-Sent Events) via StreamReader with CancellationToken for real-time breakpoint changes and kill switch state. This replaces polling, giving you instant control over production instrumentation.

Code Monitoring and Security Types

SymbolTypeDescription
SnapshotClientClass (Internal)Used internally for breakpoint polling and snapshot transmission.
SensitiveDataDetectorClass (Advanced)Detects PII and credentials in snapshot data. Automatically applied.
SecurityPatternsStatic Class (Advanced)13 built-in regex patterns for PII and credential detection.
CaptureConfigClass (Advanced)Configuration for snapshot capture including PII scrubbing and depth limits.
CircuitBreakerConfigClass (Advanced)Circuit breaker settings: failure threshold (3), window (60s), cooldown (5m).
LocalUIDetectorClass (Internal)Detects when TraceKit Local UI is running on the configured port.

End-to-End Workflow

  1. Enable Code Monitoring -- Defaults to enabled (true) in the .NET SDK.
  2. Add Snapshot Capture Points -- Place CaptureSnapshot() calls at points of interest. The .NET SDK uses [CallerFilePath], [CallerLineNumber], and [CallerMemberName] compiler attributes for automatic file and line detection.
sdk.CaptureSnapshot("order-checkout", new Dictionary<string, object>
{
    ["orderId"] = order.Id,
    ["total"] = order.Total,
    ["items"] = order.Items.Count,
    ["customer"] = customer.Email,
});
  1. Deploy and Verify Traces -- Run dotnet run or deploy. Check the Traces dashboard.
  2. Navigate to Code Monitoring -- Go to /snapshots and click "Browse Code".
  3. Set Breakpoints -- Auto-registered on the first CaptureSnapshot() call, or manually via the UI.
  4. Trigger Snapshot Capture -- Make a request that hits a code path containing CaptureSnapshot().
  5. View Captured Snapshot -- Inspect variables, stack trace, request context, and security flags.

Local Development UI

Automatic Local UI Detection -- When developing locally, TraceKit automatically sends traces to localhost:5173 (TraceKit UI) if available. Run the TraceKit UI locally to see traces in real-time without an API key!

Manual Instrumentation

For custom spans, use the injected TracekitSDK:

app.MapGet("/api/products/{id}", async (int id, TracekitSDK sdk) =>
{
    using var span = sdk.StartSpan("fetch-product", new()
    {
        ["product.id"] = id
    });

    try
    {
        var product = await GetProductFromDatabase(id);
        span.SetAttribute("product.price", product.Price);
        return Results.Ok(product);
    }
    catch (Exception ex)
    {
        span.RecordException(ex);
        span.SetStatus("error");
        throw;
    }
});

Automatic Context Propagation -- TraceKit automatically propagates trace context through ASP.NET Core's dependency injection and async/await chains.

Utility API Reference

MethodParametersReturnsDescription
HttpUtilities.ExtractClientIP(ctx)HttpContextstring?Extracts client IP. Checks X-Forwarded-For, X-Real-IP, then RemoteIpAddress.
Dispose()nonevoidIDisposable implementation. Shuts down and flushes remaining data.

SDK Disposal

Properly shut down the SDK to flush pending data:

// Option 1: using statement (recommended)
using var sdk = TracekitSDK.Create(config);
// SDK disposes automatically at end of scope

// Option 2: manual disposal
var sdk = TracekitSDK.Create(config);
// ... use sdk
sdk.Dispose(); // Flushes and shuts down

// Option 3: ASP.NET Core (handled by DI container)
// No manual disposal needed -- the DI container
// calls Dispose() when the application shuts down

LLM Instrumentation

TraceKit can instrument OpenAI, Anthropic, and Azure OpenAI API calls via HttpClient DelegatingHandler. LLM calls appear as spans with OTel GenAI semantic convention attributes.

Manual setup required -- Add the TraceKit LLM handler to your HttpClient to instrument OpenAI and Anthropic API calls. For Azure OpenAI, use the dedicated pipeline policy.

Setup -- OpenAI / Anthropic

using TraceKit.Core.LLM;

// Create HttpClient with TraceKit LLM handler
var handler = new LlmDelegatingHandler(new HttpClientHandler());
var httpClient = new HttpClient(handler);

// Use with OpenAI SDK
var openaiClient = new OpenAIClient(
    new ApiKeyCredential(Environment.GetEnvironmentVariable("OPENAI_API_KEY")),
    new OpenAIClientOptions { Transport = new HttpClientPipelineTransport(httpClient) }
);

Setup -- Azure OpenAI

using TraceKit.Core.LLM;
using Azure.AI.OpenAI;

// Create Azure OpenAI client with TraceKit policy
var client = new AzureOpenAIClient(
    new Uri("https://your-resource.openai.azure.com"),
    new AzureKeyCredential(Environment.GetEnvironmentVariable("AZURE_OPENAI_API_KEY")),
    new AzureOpenAIClientOptions()
);

// Add TraceKit instrumentation
AzureOpenAiInstrumentation.Instrument(client);

Configuration

var handler = new LlmDelegatingHandler(new LlmConfig
{
    CaptureContent = false,  // Capture prompts/completions (off by default)
    OpenAi = true,           // OpenAI instrumentation
    Anthropic = true,        // Anthropic instrumentation
}, new HttpClientHandler());

Captured Attributes

AttributeDescription
gen_ai.systemProvider name (openai, anthropic)
gen_ai.request.modelModel name (gpt-4o, claude-sonnet-4-20250514, etc.)
gen_ai.usage.input_tokensPrompt token count
gen_ai.usage.output_tokensCompletion token count
gen_ai.response.finish_reasonsFinish reason (stop, end_turn, tool_calls)

Environment Variable Override

Use TRACEKIT_LLM_CAPTURE_CONTENT=true to enable prompt/completion capture without code changes. Useful for enabling in staging but not production.

Streaming Support

Streaming responses produce a single span that covers the entire stream. Token counts are accumulated from the final chunk. No special configuration needed.

LLM Dashboard

View LLM cost, token usage, and latency metrics on the dedicated LLM Observability dashboard at /ai/llm in your TraceKit instance.

Environment Variables

You can also configure TraceKit using environment variables:

TRACEKIT_API_KEY=ctxio_your_generated_api_key_here
TRACEKIT_ENDPOINT=https://app.tracekit.dev
TRACEKIT_SERVICE_NAME=my-dotnet-app
TRACEKIT_ENVIRONMENT=production
TRACEKIT_CODE_MONITORING_ENABLED=true

TracekitConfig Class

The TracekitConfig class provides programmatic configuration with a builder pattern:

SymbolTypeDescription
TracekitConfigClassConfiguration object with properties: ApiKey, Endpoint, ServiceName, Environment, Enabled, SampleRate, EnableCodeMonitoring, UseSSL, ServiceVersion, LocalUIPort, CodeMonitoringPollIntervalSeconds.
TracekitConfig.CreateBuilder()Static MethodReturns a TracekitConfigBuilder for fluent configuration. Use .WithApiKey("...").WithServiceName("...").Build() pattern.

Environment Variable Reference

OptionTypeDefaultEnv VariableDescription
ApiKeystringrequiredTRACEKIT_API_KEYYour TraceKit API key for authentication
ServiceNamestringrequiredTRACEKIT_SERVICE_NAMEName of your service in the trace dashboard
Endpointstring"app.tracekit.dev"TRACEKIT_ENDPOINTTraceKit collector endpoint URL
UseSSLbooltrueTRACEKIT_USE_SSLEnable TLS for the exporter connection
Environmentstring"production"TRACEKIT_ENVIRONMENTDeployment environment
ServiceVersionstring"1.0.0"TRACEKIT_SERVICE_VERSIONVersion of your service
EnableCodeMonitoringbooltrueTRACEKIT_CODE_MONITORING_ENABLEDEnable live code debugging
CodeMonitoringPollIntervalSecondsint30TRACEKIT_CODE_MONITORING_POLL_INTERVALHow often to poll for active breakpoints
LocalUIPortint9999TRACEKIT_LOCAL_UI_PORTPort for the local development UI
SamplingRatedouble1.0TRACEKIT_SAMPLE_RATETrace sampling rate (0.0 to 1.0)

The .NET SDK does not auto-read environment variables. Configure via TracekitConfig object or dependency injection.

Complete Example

Here's a complete example with custom spans, metrics, and code monitoring:

using TraceKit.AspNetCore;

var builder = WebApplication.CreateBuilder(args);

// Add TraceKit
builder.Services.AddTracekit(options =>
{
    options.ApiKey = Environment.GetEnvironmentVariable("TRACEKIT_API_KEY");
    options.ServiceName = "express-api";
    options.Environment = "production";
    options.Endpoint = "https://app.tracekit.dev";
    options.EnableCodeMonitoring = true;
});

var app = builder.Build();
app.UseTracekit();

// Routes - automatically traced!
app.MapGet("/api/users", (TracekitSDK sdk) =>
{
    var users = new[]
    {
        new { Id = "1", Name = "Alice", Email = "alice@example.com" },
        new { Id = "2", Name = "Bob", Email = "bob@example.com" }
    };
    return Results.Ok(users);
});

// Manual span example
app.MapPost("/api/users", async (UserRequest request, TracekitSDK sdk) =>
{
    using var span = sdk.StartSpan("create-user", new()
    {
        ["user.email"] = request.Email
    });

    try
    {
        // Simulate user creation
        var user = new
        {
            Id = Guid.NewGuid().ToString(),
            Name = request.Name,
            Email = request.Email
        };

        // Simulate async operation
        await Task.Delay(100);

        span.SetAttribute("user.id", user.Id);
        return Results.Created($"/api/users/{user.Id}", user);
    }
    catch (Exception ex)
    {
        span.RecordException(ex);
        span.SetStatus("error");
        return Results.Problem("Failed to create user");
    }
});

app.Run();

record UserRequest(string Name, string Email);
record CheckoutRequest(string UserId, decimal Amount);

Custom Metrics

TraceKit provides a simple metrics API for tracking application performance:

Counter

For tracking cumulative values that only go up:

var sdk = TracekitSDK.Create(config);

// Create a counter with optional tags
var counter = sdk.Counter("http.requests.total",
    new() { ["service"] = "api" });

// Increment by 1
counter.Inc();

// Add a specific value
counter.Add(5);

Gauge

For tracking values that can go up or down:

// Create a gauge
var gauge = sdk.Gauge("http.connections.active");

// Set to specific value
gauge.Set(42);

// Increment/decrement
gauge.Inc();
gauge.Dec();

Histogram

For tracking distributions of values:

// Create a histogram with tags
var histogram = sdk.Histogram("http.request.duration",
    new() { ["unit"] = "ms" });

// Record values
histogram.Record(45.2);
histogram.Record(123.5);

View Custom Metrics Guide

Troubleshooting

Traces not appearing?

Cause: The TraceKit middleware is not in the ASP.NET Core pipeline or is registered in the wrong order.

Fix:

  • Ensure app.UseTracekit() is called in Program.cs middleware pipeline
  • It should be placed early (before app.MapControllers())
  • Verify API key is correct
  • Check console output for errors

Dependency injection not working?

Cause: AddTracekit() must be called before the app is built.

Fix:

  • Call builder.Services.AddTracekit(config) BEFORE var app = builder.Build()
  • Ensure you are injecting TracekitSDK (not creating a new instance manually)
  • Verify the config object has a valid ApiKey

Code monitoring not working?

Cause: While code monitoring defaults to true in .NET, the CaptureSnapshot calls may be missing.

Fix:

  • Verify EnableCodeMonitoring is true in TracekitConfig (it is by default)
  • Add sdk.CaptureSnapshot("label") calls in code paths you want to inspect
  • Check that CodeMonitoringPollIntervalSeconds has not been set to an extremely high value

Authentication errors (401/403)?

Cause: API key is invalid or the endpoint is wrong.

Fix:

  • Verify the TracekitConfig.ApiKey value
  • Check for whitespace: config.ApiKey = config.ApiKey.Trim()
  • Ensure UseSSL is true for production (it defaults to true)
  • Regenerate key in dashboard if needed

Performance concerns?

Cause: High-traffic services may see overhead from full trace sampling.

Fix:

  • Reduce sampling rate: SamplingRate = 0.1 (10% of requests)
  • TraceKit uses OpenTelemetry's efficient batch exporting -- overhead is typically less than 1ms per request
  • Consider disabling unused features to reduce overhead further

Migrating from OpenTelemetry

TraceKit wraps OpenTelemetry internally, so you get the same standards-based trace data with significantly less setup code.

Before vs After

Before: Raw OpenTelemetry (~20 lines):

using OpenTelemetry;
using OpenTelemetry.Trace;
using OpenTelemetry.Resources;
using OpenTelemetry.Exporter;

builder.Services.AddOpenTelemetry()
    .ConfigureResource(r => r.AddService("my-service"))
    .WithTracing(tracing => tracing
        .AddAspNetCoreInstrumentation()
        .AddHttpClientInstrumentation()
        .AddOtlpExporter(opt => {
            opt.Endpoint = new Uri("https://api.tracekit.io");
            opt.Headers = "Authorization=Bearer " + apiKey;
        }));

After: TraceKit SDK (6 lines):

using Tracekit.AspNetCore;

builder.Services.AddTracekit(options => {
    options.ApiKey = builder.Configuration["TRACEKIT_API_KEY"];
    options.ServiceName = "my-service";
});

var app = builder.Build();
app.UseTracekit();

Migration Steps

  1. Install NuGet package: dotnet add package Tracekit.AspNetCore
  2. Replace AddOpenTelemetry: Replace the AddOpenTelemetry() chain with AddTracekit()
  3. Add UseTracekit middleware: Add app.UseTracekit() to your middleware pipeline
  4. Remove OTel NuGet packages: dotnet remove package OpenTelemetry.Exporter.OpenTelemetryProtocol and related packages
  5. Verify: Run dotnet run and check the Traces page for incoming data

Key Migration Benefits:

  • 20 lines to 6 lines -- no more complex builder chains for exporters and instrumentation
  • No OTel dependency management -- TraceKit handles version pinning internally
  • Built-in code monitoring -- not available with raw OpenTelemetry
  • Built-in security scanning -- automatic variable redaction on snapshots
  • ASP.NET Core DI pattern -- follows standard .NET dependency injection conventions

Performance Overhead

TraceKit is built on OpenTelemetry's efficient batch processing pipeline. The SDK adds minimal overhead to your .NET application.

MetricImpactDetails
Request Tracing< 1ms per requestSpans are batched and exported asynchronously
Code Monitoring (Idle)Zero overheadNo performance impact when no active breakpoints
Code Monitoring (Capture)< 5ms per snapshotIncludes variable serialization and security scanning
Memory Footprint~10-20 MBSDK runtime and span buffer
SDK Initialization< 200ms one-timeRegistered via ASP.NET Core dependency injection

Performance characteristics are typical for production workloads and may vary with application complexity, request volume, and number of instrumented libraries. Use sampling configuration to reduce overhead in high-traffic services.

Best Practices

DO:

  • Register via builder.Services.AddTracekit() -- use the ASP.NET Core DI pattern
  • Use the IDisposable pattern for SDK lifecycle -- DI container handles disposal automatically
  • Place app.UseTracekit() early in the middleware pipeline (before authentication, routing, etc.)
  • Use environment variables for API keys -- store in TRACEKIT_API_KEY or appsettings.json
  • Enable code monitoring in staging first before production
  • Use sampling in high-traffic services -- set TRACEKIT_SAMPLING_RATE below 1.0
  • Set meaningful service names for easy identification in the trace viewer

DON'T:

  • Create TracekitClient manually -- use AddTracekit() for proper lifecycle management
  • Skip app.UseTracekit() after AddTracekit() -- both are required for HTTP request tracing
  • Create spans for every function -- trace boundaries like HTTP handlers, DB calls, and external services
  • Add high-cardinality attributes -- avoid user IDs, request IDs, or session tokens as span attributes
  • Disable TLS in production -- the TRACEKIT_INSECURE flag is for local development only

Next Steps

  • Code Monitoring -- Learn about live debugging with production-safe snapshots
  • Set Up Alerts -- Configure alerts for errors, performance issues, and custom metrics
  • View Your Traces -- Explore distributed traces and find performance bottlenecks
  • Create Dashboards -- Build custom dashboards with metrics and visualizations

On this page