> ## Documentation Index
> Fetch the complete documentation index at: https://docs.bronto.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Send .NET logs, metrics, and traces to Bronto

> Instrument .NET and ASP.NET Core applications with the OpenTelemetry .NET SDK to send logs, metrics, and traces to Bronto over OTLP/HTTP, via an OTel Collector or directly.

One `AddOpenTelemetry()` block sends **logs, metrics, and traces** from your .NET application to Bronto. Existing `ILogger` calls feed the log pipeline, instruments feed the meter provider, and activities feed the tracer provider — all three sharing one resource identity and one OTLP exporter.

The application exports to an [OpenTelemetry Collector](/agent-setup/open-telemetry), which forwards to Bronto. If you don't run a Collector, [export directly to Bronto](#direct-export-to-bronto) instead — only the endpoint and an API key header change.

## Prerequisites

* .NET 8 or later
* An OTel Collector reachable from your application, with `logs`, `metrics`, and `traces` pipelines forwarding to Bronto — see [Connect OpenTelemetry Collector to Bronto](/agent-setup/open-telemetry)

## Install dependencies

```bash theme={"dark"}
dotnet add package OpenTelemetry.Extensions.Hosting
dotnet add package OpenTelemetry.Exporter.OpenTelemetryProtocol
dotnet add package OpenTelemetry.Instrumentation.AspNetCore
dotnet add package OpenTelemetry.Instrumentation.Http
```

The two instrumentation packages produce request and HTTP client spans and metrics automatically. See [.NET instrumentation libraries](https://opentelemetry.io/docs/languages/dotnet/libraries/) for the full list, including Entity Framework Core and gRPC.

## Initialise the SDK

Configure all three signals in `Program.cs`:

```csharp Program.cs theme={"dark"}
using OpenTelemetry.Exporter;
using OpenTelemetry.Logs;
using OpenTelemetry.Metrics;
using OpenTelemetry.Resources;
using OpenTelemetry.Trace;

var builder = WebApplication.CreateBuilder(args);

// Bridge ILogger into the OTel pipeline.
builder.Logging.AddOpenTelemetry(logging =>
{
    logging.IncludeFormattedMessage = true;
    logging.IncludeScopes = true;
});

builder.Services.AddOpenTelemetry()
    .ConfigureResource(resource => resource
        .AddService(serviceName: "my-service", serviceNamespace: "my-team")
        .AddAttributes(new Dictionary<string, object>
        {
            ["deployment.environment"] = "production",
        }))
    .WithMetrics(metrics => metrics
        .AddAspNetCoreInstrumentation()
        .AddHttpClientInstrumentation()
        .AddMeter("my-service"))
    .WithTracing(tracing => tracing
        .AddAspNetCoreInstrumentation()
        .AddHttpClientInstrumentation()
        .AddSource("my-service"))
    .UseOtlpExporter(OtlpExportProtocol.HttpProtobuf, new Uri("http://localhost:4318/"));

var app = builder.Build();
app.Run();
```

`UseOtlpExporter` enables the exporter for logs, metrics, and traces in one call, appending the signal path (`/v1/logs`, `/v1/metrics`, `/v1/traces`) to the base URL. `ConfigureResource` applies across all three signals, so `service.name` and `service.namespace` are shared automatically.

Two resource attributes determine how Bronto organises your data:

| OTel attribute      | Bronto concept | Description                                  |
| ------------------- | -------------- | -------------------------------------------- |
| `service.name`      | Dataset        | Groups telemetry from one service            |
| `service.namespace` | Collection     | Groups related services or a team's services |

<Note>
  `http://localhost:4318` is the standard OTLP/HTTP address for a Collector on the same host. Use the address reachable from your application if the Collector runs in another container, pod, or host. No authentication is needed between the application and the Collector — the Collector holds the Bronto API key.
</Note>

<Note>
  For console applications and workers, use `Host.CreateApplicationBuilder(args)` in place of `WebApplication.CreateBuilder(args)`. The `AddOpenTelemetry()` block is identical.
</Note>

## Instrument your application

`ILogger` calls need no changes. Add a `Meter` and an `ActivitySource` for your own measurements and spans:

```csharp theme={"dark"}
using System.Diagnostics;
using System.Diagnostics.Metrics;

public class PaymentService
{
    private static readonly ActivitySource ActivitySource = new("my-service");
    private static readonly Meter Meter = new("my-service");
    private static readonly Counter<long> Payments = Meter.CreateCounter<long>("app.payments");

    private readonly ILogger<PaymentService> _logger;

    public PaymentService(ILogger<PaymentService> logger) => _logger = logger;

    public void ProcessPayment(decimal amount)
    {
        using var activity = ActivitySource.StartActivity("process-payment");
        activity?.SetTag("payment.amount", amount);
        Payments.Add(1);

        _logger.LogInformation("Processing payment");  // trace_id and span_id attached automatically
    }
}
```

The `AddSource` and `AddMeter` names in `Program.cs` must match the `ActivitySource` and `Meter` names here, or their telemetry is dropped.

Any `ILogger` call made inside an active `Activity` automatically carries that span's `trace_id` and `span_id`, so you can jump from a log line to its trace in Bronto with no manual context propagation.

## Verify

Run your application, then check each signal in Bronto, filtering by the `service.name` you set:

* **Logs** — the [Search](https://app.bronto.io/search) page, in the dataset named after your service
* **Metrics** — the Metric Explorer
* **Traces** — the Explore Traces page

If nothing arrives:

* Confirm the Collector is running and reachable at the configured endpoint, and that its pipelines include an `otlp` receiver and the Bronto exporters.
* Confirm `OtlpExportProtocol.HttpProtobuf` matches the Collector receiver you are pointing at — port `4318` is HTTP, `4317` is gRPC.
* Check that every `ActivitySource` and `Meter` name is registered with `AddSource` / `AddMeter`.

For signal-specific reference material, see [Send Metrics to Bronto](/metrics/send-metrics) and [Send Traces to Bronto](/tracing/send-traces).

## Direct export to Bronto

Without a Collector, the application exports straight to Bronto over OTLP/HTTP. Point the base URL at your Bronto region:

```csharp Program.cs theme={"dark"}
    .UseOtlpExporter(
        OtlpExportProtocol.HttpProtobuf,
        new Uri("https://ingestion.eu.bronto.io/"));   // or ingestion.us.bronto.io
```

and supply your API key through the standard OTLP header variable, which `UseOtlpExporter` reads:

```bash theme={"dark"}
export OTEL_EXPORTER_OTLP_HEADERS="x-bronto-api-key=<YOUR_API_KEY>"
```

| Region | Base endpoint                    |
| ------ | -------------------------------- |
| EU     | `https://ingestion.eu.bronto.io` |
| US     | `https://ingestion.us.bronto.io` |

See [API Keys](/Account-Management/API-Keys) for how to create a key with ingestion permissions.

<Tip>
  Prefer the Collector for multi-service environments — it batches, filters, and enriches telemetry, and keeps the API key out of every application. Direct export suits single services and simple architectures.
</Tip>

## GenAI semantic conventions

If your application calls an LLM, OpenTelemetry's [GenAI semantic conventions](https://github.com/open-telemetry/semantic-conventions-genai/blob/main/docs/gen-ai/gen-ai-spans.md) define `gen_ai.*` span attributes for model, token usage, and prompt and response content.

.NET GenAI telemetry is library-specific — each has its own opt-in, and each emits from its own activity source, which you register with `AddSource`:

| Library                 | Enable with                                                             | Activity source                        |
| ----------------------- | ----------------------------------------------------------------------- | -------------------------------------- |
| Semantic Kernel         | `SEMANTICKERNEL_EXPERIMENTAL_GENAI_ENABLE_OTEL_DIAGNOSTICS=true`        | `Microsoft.SemanticKernel*`            |
| OpenAI .NET SDK         | `AppContext.SetSwitch("OpenAI.Experimental.EnableOpenTelemetry", true)` | `OpenAI.*`                             |
| Microsoft.Extensions.AI | Wrap the `IChatClient` with `UseOpenTelemetry()`                        | `Experimental.Microsoft.Extensions.AI` |
| AWS SDK (Bedrock)       | `OpenTelemetry.Instrumentation.AWS` package                             | added by `AddAWSInstrumentation()`     |

These APIs are experimental and version-sensitive, so verify the attributes emitted by the version you deploy against each library's current documentation.

### Capture prompts and responses

Content capture is controlled by the .NET library rather than the cross-language OTel variables — `OTEL_INSTRUMENTATION_GENAI_CAPTURE_MESSAGE_CONTENT`, used by the Python, JavaScript, and Java instrumentations, is not a .NET-wide switch. Semantic Kernel uses its own sensitive-diagnostics opt-in:

```bash theme={"dark"}
export SEMANTICKERNEL_EXPERIMENTAL_GENAI_ENABLE_OTEL_DIAGNOSTICS_SENSITIVE=true
```

Microsoft.Extensions.AI uses the `EnableSensitiveData` option on `UseOpenTelemetry()` instead. Prompt and response content can contain sensitive data, so enable it deliberately.

### Manual spans

Where no instrumentation exists for your provider, set the same attributes yourself:

```csharp theme={"dark"}
private static readonly ActivitySource ActivitySource = new("my-service");

using var activity = ActivitySource.StartActivity("chat gpt-4o-mini");
activity?.SetTag("gen_ai.provider.name", "openai");
activity?.SetTag("gen_ai.request.model", "gpt-4o-mini");
activity?.SetTag("gen_ai.usage.input_tokens", 33);
activity?.SetTag("gen_ai.usage.output_tokens", 74);
// Bronto flattens array attributes — this is searchable as finish_reasons.0
activity?.SetTag("gen_ai.response.finish_reasons", new[] { "stop" });
```

See the [GenAI span conventions](https://github.com/open-telemetry/semantic-conventions-genai/blob/main/docs/gen-ai/gen-ai-spans.md) for the full attribute list.

For the recommended attribute set and Bronto search queries, see [LLM Observability](/ai-features/llm-observability).
