Azure / Azure/data-api-builder
[Enh]: Support OpenTelemetry logs export (OTLP)
- Dominant language
- C#
- Stars
- 1.5k
- Forks
- 370
- Avg merge
- 3d 17h
- Merged PRs (30d)
- 8
Description
## Summary
Add an optional path for DAB's ILogger output to flow through the .NET OpenTelemetry SDK's logs pipeline and ship via OTLP, alongside the existing `application-insights`, `azure-log-analytics`, `file`, and console destinations.
## Context
Per #2397, `runtime.telemetry.open-telemetry` was intentionally scoped to traces (`ActivitySource`) and metrics (`Meter`). The issue's framing *"Serilog handles logging, while OpenTelemetry focuses on tracing and metrics"* was accurate at the time: the .NET OpenTelemetry **Logs** API was experimental.
That's no longer the case. `OpenTelemetry.Extensions.Logging` and the `OtlpLogExporter` are stable in the .NET OpenTelemetry SDK (graduated in 1.5+). The `ILoggingBuilder.AddOpenTelemetry()` extension method is production-ready and is the canonical way modern .NET services emit logs to OTLP collectors. ASP.NET Core's own host builder supports it out of the box.
The result is a gap: DAB users who self-host on Kubernetes with a Grafana / Loki / Alloy / Tempo stack (or any non-Azure OTLP collector) have no first-class log destination. Stdout scraping by an external collector works but loses the structured ILogger scope / category metadata that flows naturally through the OTel logs pipeline.
## Proposal
Add a `logs-enabled` sub-flag under the existing `open-telemetry` block:
``` json
{
"runtime": {
"telemetry": {
"open-telemetry": {
"enabled": true,
"endpoint": "http://otel-collector:4317",
"service-name": "dab",
"exporter-protocol": "grpc",
"logs-enabled": true // new, defaults to false
}
}
}
}
```
When `logs-enabled: true`, register an OpenTelemetry logs provider on `builder.Logging` that reuses the existing `endpoint`, `headers`, and `exporter-protocol` values. No new config fields, no schema disruption, no behavior change for existing users (default off).
Pseudocode in `src/Service/Program.cs`:
```csharp
if (otelConfig.Enabled && otelConfig.LogsEnabled)
{
builder.Logging.AddOpenTelemetry(options =>
{
options.SetResourceBuilder(sharedResourceBuilder);
options.IncludeFormattedMessage = true;
options.IncludeScopes = true;
options.AddOtlpExporter(opt =>
{
opt.Endpoint = new Uri(otelConfig.Endpoint);
opt.Protocol = otelConfig.ExporterProtocol == "grpc"
? OtlpExportProtocol.Grpc
: OtlpExportProtocol.HttpProtobuf;
opt.Headers = otelConfig.Headers;
});
});
}
```
CLI flag in `dab add-telemetry`:
- `--otel-logs-enabled true|false`
## Why this is low-risk
- **Additive.** No change to traces / metrics / existing logger providers. Existing users see no behavior difference.
- **Default off.** Zero impact on the install-base until explicitly opted in.
- **Reuses existing config surface.** No new endpoint / headers / protocol fields — same connection params, three pipelines.
- **Small footprint.** ~30 lines of production code plus schema update plus a CLI option.
- **Mirrors a pattern .NET teams ship in production today.** The OTel SDK exposes traces / metrics / logs as a coherent three-signal builder; the missing logs piece is the only deviation here.
## Validation
I run DAB on AKS behind a Grafana Alloy DaemonSet. I'm happy to test against a real OTLP gRPC receiver and post screenshots of DAB logs landing in Loki with full structured fields (scope_name, category, trace_id correlation with the existing OTel traces).
## Offer
Happy to PR this if the team is open to the addition!
Closes the gap noted (implicitly) by the increasing number of users self-hosting DAB on non-Azure observability stacks; explicit framing of "Serilog vs OpenTelemetry" in #2397 predates the .NET Logs API going stable.
Contributor guide
Assessment
This issue has not been assessed yet.