.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:
| Component | Span Type | Captured Attributes | Auto-Traced? |
|---|---|---|---|
| HTTP Requests | SERVER | Route, method, status, duration, headers | Yes |
| HttpClient Calls | CLIENT | method, url, status_code, duration | Yes |
| Database Queries | DB | db.system, db.statement, db.name, duration | Yes (via Entity Framework / ADO.NET) |
| Exceptions | N/A | exception.type, exception.message, stack_trace | Yes |
| Custom Spans | Custom | user-defined attributes | Manual |
Installation
Install the TraceKit .NET SDK via NuGet:
For ASP.NET Core
dotnet add package TraceKit.AspNetCoreFor Vanilla .NET
dotnet add package TraceKit.CoreASP.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
| Symbol | Type | Registration | Description |
|---|---|---|---|
TracekitMiddleware | IMiddleware | app.UseTracekit() | Automatic SERVER span creation on all incoming requests. |
TracekitOptions | Options Class | AddTracekit(opts) | Options for middleware behavior including RouteFilter and IgnorePatterns. |
AddTracekitInstrumentation() | Extension Method | IHttpClientBuilder | Instruments 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
| Option | Type | Description |
|---|---|---|
ApiKey | string | Your TraceKit API key (required) |
ServiceName | string | Name of your service |
Environment | string | Environment (dev, staging, prod) |
Endpoint | string | TraceKit endpoint URL |
EnableCodeMonitoring | bool | Enable 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
SecurityPatternsdetect 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
| Symbol | Type | Description |
|---|---|---|
SnapshotClient | Class (Internal) | Used internally for breakpoint polling and snapshot transmission. |
SensitiveDataDetector | Class (Advanced) | Detects PII and credentials in snapshot data. Automatically applied. |
SecurityPatterns | Static Class (Advanced) | 13 built-in regex patterns for PII and credential detection. |
CaptureConfig | Class (Advanced) | Configuration for snapshot capture including PII scrubbing and depth limits. |
CircuitBreakerConfig | Class (Advanced) | Circuit breaker settings: failure threshold (3), window (60s), cooldown (5m). |
LocalUIDetector | Class (Internal) | Detects when TraceKit Local UI is running on the configured port. |
End-to-End Workflow
- Enable Code Monitoring -- Defaults to enabled (true) in the .NET SDK.
- 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,
});- Deploy and Verify Traces -- Run
dotnet runor deploy. Check the Traces dashboard. - Navigate to Code Monitoring -- Go to /snapshots and click "Browse Code".
- Set Breakpoints -- Auto-registered on the first
CaptureSnapshot()call, or manually via the UI. - Trigger Snapshot Capture -- Make a request that hits a code path containing
CaptureSnapshot(). - 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
| Method | Parameters | Returns | Description |
|---|---|---|---|
HttpUtilities.ExtractClientIP(ctx) | HttpContext | string? | Extracts client IP. Checks X-Forwarded-For, X-Real-IP, then RemoteIpAddress. |
Dispose() | none | void | IDisposable 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 downLLM 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
| Attribute | Description |
|---|---|
gen_ai.system | Provider name (openai, anthropic) |
gen_ai.request.model | Model name (gpt-4o, claude-sonnet-4-20250514, etc.) |
gen_ai.usage.input_tokens | Prompt token count |
gen_ai.usage.output_tokens | Completion token count |
gen_ai.response.finish_reasons | Finish 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=trueTracekitConfig Class
The TracekitConfig class provides programmatic configuration with a builder pattern:
| Symbol | Type | Description |
|---|---|---|
TracekitConfig | Class | Configuration object with properties: ApiKey, Endpoint, ServiceName, Environment, Enabled, SampleRate, EnableCodeMonitoring, UseSSL, ServiceVersion, LocalUIPort, CodeMonitoringPollIntervalSeconds. |
TracekitConfig.CreateBuilder() | Static Method | Returns a TracekitConfigBuilder for fluent configuration. Use .WithApiKey("...").WithServiceName("...").Build() pattern. |
Environment Variable Reference
| Option | Type | Default | Env Variable | Description |
|---|---|---|---|---|
ApiKey | string | required | TRACEKIT_API_KEY | Your TraceKit API key for authentication |
ServiceName | string | required | TRACEKIT_SERVICE_NAME | Name of your service in the trace dashboard |
Endpoint | string | "app.tracekit.dev" | TRACEKIT_ENDPOINT | TraceKit collector endpoint URL |
UseSSL | bool | true | TRACEKIT_USE_SSL | Enable TLS for the exporter connection |
Environment | string | "production" | TRACEKIT_ENVIRONMENT | Deployment environment |
ServiceVersion | string | "1.0.0" | TRACEKIT_SERVICE_VERSION | Version of your service |
EnableCodeMonitoring | bool | true | TRACEKIT_CODE_MONITORING_ENABLED | Enable live code debugging |
CodeMonitoringPollIntervalSeconds | int | 30 | TRACEKIT_CODE_MONITORING_POLL_INTERVAL | How often to poll for active breakpoints |
LocalUIPort | int | 9999 | TRACEKIT_LOCAL_UI_PORT | Port for the local development UI |
SamplingRate | double | 1.0 | TRACEKIT_SAMPLE_RATE | Trace 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);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)BEFOREvar 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
EnableCodeMonitoringistruein TracekitConfig (it is by default) - Add
sdk.CaptureSnapshot("label")calls in code paths you want to inspect - Check that
CodeMonitoringPollIntervalSecondshas 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
truefor 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
- Install NuGet package:
dotnet add package Tracekit.AspNetCore - Replace AddOpenTelemetry: Replace the
AddOpenTelemetry()chain withAddTracekit() - Add UseTracekit middleware: Add
app.UseTracekit()to your middleware pipeline - Remove OTel NuGet packages:
dotnet remove package OpenTelemetry.Exporter.OpenTelemetryProtocoland related packages - Verify: Run
dotnet runand 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.
| Metric | Impact | Details |
|---|---|---|
| Request Tracing | < 1ms per request | Spans are batched and exported asynchronously |
| Code Monitoring (Idle) | Zero overhead | No performance impact when no active breakpoints |
| Code Monitoring (Capture) | < 5ms per snapshot | Includes variable serialization and security scanning |
| Memory Footprint | ~10-20 MB | SDK runtime and span buffer |
| SDK Initialization | < 200ms one-time | Registered 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_KEYorappsettings.json - Enable code monitoring in staging first before production
- Use sampling in high-traffic services -- set
TRACEKIT_SAMPLING_RATEbelow 1.0 - Set meaningful service names for easy identification in the trace viewer
DON'T:
- Create
TracekitClientmanually -- useAddTracekit()for proper lifecycle management - Skip
app.UseTracekit()afterAddTracekit()-- 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_INSECUREflag 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
Ruby Integration Guide
Add TraceKit to your Ruby applications for automatic distributed tracing. Debug production Ruby services with live breakpoints and Rails/Sinatra support.
Laravel Integration Guide
Integrate TraceKit with Laravel for automatic request tracing, live breakpoints, and production debugging. See database queries, API calls, and exceptions in real-time.