# .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.

- Canonical: https://tracekit.dev/docs/languages/dotnet

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](/docs/ai)

## Prerequisites

- .NET 8.0 or higher
- ASP.NET Core 8.0+ (for web applications)
- A TraceKit account ([create one free](https://app.tracekit.dev/register))
- A generated API key from the [API Keys](https://app.tracekit.dev/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

```bash
dotnet add package TraceKit.AspNetCore
```

### For Vanilla .NET

```bash
dotnet add package TraceKit.Core
```

## ASP.NET Core Setup

### Program.cs

Add TraceKit to your application in `Program.cs`:

```csharp
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:

```csharp
// 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](https://app.tracekit.dev/traces) 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](#troubleshooting) section.

## Configuration

### appsettings.json

Configure TraceKit in your `appsettings.json`:

```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 dynamic logs snapshots |

## Code Monitoring (Dynamic Logs)

**Production-Safe Dynamic Logs** -- 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](/docs/code-monitoring)

### Enable Code Monitoring

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

```csharp
// 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:

```csharp
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

| 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

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.

```csharp
sdk.CaptureSnapshot("order-checkout", new Dictionary<string, object>
{
    ["orderId"] = order.Id,
    ["total"] = order.Total,
    ["items"] = order.Items.Count,
    ["customer"] = customer.Email,
});
```

3. **Deploy and Verify Traces** -- Run `dotnet run` or deploy. Check the [Traces dashboard](https://app.tracekit.dev/traces).
4. **Navigate to Code Monitoring** -- Go to [/snapshots](https://app.tracekit.dev/snapshots) and click "Browse Code".
5. **Set Breakpoints** -- Auto-registered on the first `CaptureSnapshot()` call, or manually via the UI.
6. **Trigger Snapshot Capture** -- Make a request that hits a code path containing `CaptureSnapshot()`.
7. **View Captured Snapshot** -- Inspect variables, stack trace, request context, and security flags.

### v25: Enhanced Code Monitoring

#### Log-Point Mode

When a breakpoint is set to **log-point mode** in the dashboard, the SDK captures only the specified expressions -- no local variables, stack traces, or request context. This dramatically reduces overhead for high-frequency code paths.

No code changes needed. The SDK reads the breakpoint's `mode` field from the server and adjusts capture behavior automatically.

#### SDK-Side Condition Evaluation

Breakpoint conditions (e.g., `status > 200`) are now evaluated **locally in the SDK** without a server round-trip. The server classifies each condition as `sdk-evaluable` or `server-only` at creation time. Simple expressions (comparisons, boolean logic, property access) are evaluated in-process.

No code changes needed. The SDK checks the `condition_eval` field and evaluates locally when possible.

#### Per-Breakpoint Capture Limits

New breakpoints default to:
- **max_depth: 5** -- variables nested deeper than 5 levels are truncated
- **max_payload_bytes: 128KB** -- snapshots larger than 128KB are truncated

These limits are configurable per breakpoint in the dashboard. The SDK respects per-breakpoint overrides automatically.

#### Auto-Expire

New breakpoints **auto-expire after 48 hours of inactivity**. Each capture resets the timer. Pin a breakpoint in the dashboard to prevent auto-expiry.

Existing breakpoints are not affected -- auto-expire only applies to breakpoints created after the v25 update.

## 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`:

```csharp
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:

```csharp
// 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](https://opentelemetry.io/docs/specs/semconv/gen-ai/) 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

```csharp
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

```csharp
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

```csharp
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:

```bash
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:

| 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:

```csharp
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:

```csharp
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:

```csharp
// 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:

```csharp
// 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](/docs/metrics)

## 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):

```csharp
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):

```csharp
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](https://app.tracekit.dev/traces) 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](#environment-variables) 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](/docs/code-monitoring) -- Learn about dynamic logs with production-safe snapshots
- [Set Up Alerts](/docs/alerts) -- Configure alerts for errors, performance issues, and custom metrics
- [View Your Traces](https://app.tracekit.dev/traces) -- Explore distributed traces and find performance bottlenecks
- [Create Dashboards](https://app.tracekit.dev/dashboards) -- Build custom dashboards with metrics and visualizations
