Logit.io
OpenTelemetry for .NET on Logit.io
← Back to blog
10/8/2026 · 6 min read

OpenTelemetry for .NET: an appsettings.json that works as pasted

Logit.io Team
Logit.io Team
Technical Content Team

Last updated 10/8/2026

How To GuidesGetting Started

The most common reason a .NET app sends nothing to an OTLP endpoint is one line that isn't in the config. The OpenTelemetry .NET exporter defaults to gRPC, and Logit.io's Hosted OpenTelemetry endpoint takes OTLP over HTTP only. Leave the protocol unset and the exporter tries gRPC, the request fails, and depending on how your app handles exporter errors you may not notice for a while.

The fix is a small appsettings.json block. The dashboard's onboarding now generates that block with your stack's endpoint and credential already filled in, so it works the moment you paste it. Below is the file, the code that reads it, and what to change before the same file goes anywhere near a git repository.

Contents

The file

Open your Logs stack in the dashboard, go to onboarding, choose OpenTelemetry and then Applications. .NET is the default language in the dropdown. The config file it shows looks like this, with your own values in place of the examples:

{
  "OTEL_SERVICE_NAME": "my-app",
  "OTEL_EXPORTER_OTLP_ENDPOINT": "https://your-stack-id-otel.logit.io",
  "OTEL_EXPORTER_OTLP_PROTOCOL": "http/protobuf",
  "OTEL_EXPORTER_OTLP_HEADERS": "Authorization=Basic%20<base64-of-username:api-key>"
}

Those are top-level keys, merged into your existing appsettings.json next to Logging and AllowedHosts. They use the standard OpenTelemetry environment variable names on purpose: the .NET exporter reads them from IConfiguration, so the same names work in the JSON file, in user secrets and as real environment variables.

Each line has a job:

  • OTEL_EXPORTER_OTLP_PROTOCOL is the line that's usually missing. http/protobuf switches the exporter off its gRPC default.
  • OTEL_EXPORTER_OTLP_ENDPOINT is the base URL only: HTTPS, no port, no path. Because it comes from this key, the exporter appends /v1/logs, /v1/traces and /v1/metrics itself.
  • OTEL_EXPORTER_OTLP_HEADERS is the Header value from the onboarding credentials list: Authorization=Basic%20 followed by the base64 of your Hosted OpenTelemetry username and API key. In the dashboard it's already filled in; here it's a placeholder.
  • OTEL_SERVICE_NAME becomes service.name on every log, span and metric, which is what you'll filter on later.

Leave the %20 as it is. The .NET exporter decodes it to a space on the wire. Rewriting it as a literal space is a common "fix" that breaks the header.

The code that reads it

Add the four packages the onboarding page lists:

dotnet add package OpenTelemetry.Extensions.Hosting
dotnet add package OpenTelemetry.Exporter.OpenTelemetryProtocol
dotnet add package OpenTelemetry.Instrumentation.AspNetCore
dotnet add package OpenTelemetry.Instrumentation.Http

Then register OpenTelemetry in Program.cs:

builder.Services.AddOpenTelemetry()
    .WithTracing(tracing => tracing
        .AddAspNetCoreInstrumentation()
        .AddHttpClientInstrumentation())
    .WithMetrics(metrics => metrics
        .AddAspNetCoreInstrumentation()
        .AddHttpClientInstrumentation())
    .UseOtlpExporter();

builder.Logging.AddOpenTelemetry(logging =>
{
    logging.IncludeFormattedMessage = true;
    logging.IncludeScopes = true;
});

UseOtlpExporter() turns on OTLP export for logs, metrics and traces in one call, and with no arguments it takes everything from configuration. There's no endpoint in the code, which is the point: the code is identical for every environment, and only the configuration changes.

IncludeFormattedMessage keeps the rendered log message alongside the template, so a line written as "Order {OrderId} failed" arrives with the order number filled in and is searchable as you'd expect.

Two ways it goes wrong

Setting the endpoint in code. If your app already has something like o.Endpoint = new Uri(config["OpenTelemetry:Uri"]) inside an AddOtlpExporter callback, the exporter uses that URI exactly as written and doesn't append the signal paths. Point it at the bare Logit.io host and every request goes to /. Either remove the assignment and let OTEL_EXPORTER_OTLP_ENDPOINT do the work, or set the full signal URL for that one signal. The overload UseOtlpExporter(protocol, baseUrl) is the exception: it still appends the paths, so give it the base URL and OtlpExportProtocol.HttpProtobuf.

Registering the exporter twice. Calling UseOtlpExporter() a second time throws, and so does combining it with AddOtlpExporter. If the app already registers an OTLP exporter, don't paste the block above. Change the endpoint, protocol and header in the configuration the app already reads, and keep its existing service name.

The same applies to per-signal keys such as OTEL_EXPORTER_OTLP_TRACES_ENDPOINT. Where the exporter reads them, they're used as written, with no /v1 path appended. If one is set somewhere in your environment, remove it or give it the full signal URL.

Start Free Trial

Unlock complete visibility with hosted ELK, Grafana, and Prometheus-backed Observability

Start Free Trial

Before you commit it

The pasted file has a working credential in it. That's what makes it work first time, and it's also why it shouldn't be committed. Keep the endpoint, protocol and service name in appsettings.json and move the header out:

dotnet user-secrets init
dotnet user-secrets set OTEL_EXPORTER_OTLP_HEADERS "Authorization=Basic%20<base64-of-username:api-key>"

For local runs, ASP.NET Core user secrets are loaded in the Development environment and override the JSON file. Where the app is deployed, set OTEL_EXPORTER_OTLP_HEADERS as an environment variable on the process. The default host configuration reads environment variables after the JSON files, so it wins there too. Use appsettings.{Environment}.json if staging and production need a different service name or stack.

If you'd rather keep the username and API key as two separate settings, that works as well, but UseOtlpExporter() won't read them on its own. You'd build the Authorization=Basic%20… string from them in code and assign it to OtlpExporterOptions.Headers. Pasting the Header value is simpler. If you rotate the Hosted OpenTelemetry credentials on your stack's Endpoints page, remember to update whichever one you chose.

Check it worked

Run the app and send it a few requests. Traces leave quickly, logs follow, and metrics go out on the SDK's default export interval of about 60 seconds, so don't judge metrics in the first minute.

If you suspect an auth problem, listen to the exporter's EventSource, OpenTelemetry-Exporter-OpenTelemetryProtocol, with dotnet-trace or your existing self-diagnostics setup, and look for 401 or 403 responses. A 401 almost always means the header was edited, truncated, or copied before a credential rotation.

Then look for the data in the dashboard:

  • Logs land in your Logs stack's OpenSearch. Search for your service.name in OpenSearch Dashboards.
  • Traces go to your APM stack, where the service appears in the trace view.
  • Metrics go to the Metrics stack.

If you don't have an APM or Metrics stack, the endpoint still accepts those signals but has nowhere to store them, so they're dropped and the dashboard shows a count of what arrived. That's a useful prompt rather than an error, and it means you can ship the same configuration everywhere. APM starts at $20/mo and Metrics at $12/mo, both billed annually, and Hosted OpenTelemetry itself is on every plan.

If you can't change the code

For an app you can't recompile, the .NET zero-code instrumentation is the alternative, not an addition: don't run it alongside UseOtlpExporter(). It reads the process environment rather than appsettings.json, so set the same four values as environment variables on that process.

The onboarding page also has Copy AI prompt for .NET, which covers the less common shapes: a console app without IConfiguration, Serilog or NLog sinks (which need a literal space after Basic, not %20), and apps that already send to a Collector. The prompt never includes your key. For wider reference, the OpenTelemetry .NET exporter docs and our OpenTelemetry getting started docs are the places to go next.

Get the latest Elastic Stack & logging resources when you subscribe

Want to see this in action?
Start a free trial and connect logs to your alert workflows.