Skip to content

[feature request] AddOtlpExporter and "WithLog/Traces/Metrics should probably add 2 overload (only named options / one with IServiceProvider) #6537

@tebeco

Description

@tebeco

Package

OpenTelemetry.Exporter.OpenTelemetryProtocol

Is your feature request related to a problem?

I've tried to integrate with Datadog using their OTEL ingestions API.
For now, datadog have decided to have 3 differents Endpoints (Logs / Metrics / Traces)
They also for now have different "setup"

All that together imply that I need 3 different OTEL exporter
here's my repo:
https://github.com/tebeco/AgentLessOtelDataDog/

inside you'll find the following file doing the exported setup:
https://github.com/tebeco/AgentLessOtelDataDog/blob/main/AgentLessOtelDataDog/OpenTelemetryExtensions.cs

here's some pain point + possible way to address it:

AddOtlpExporter should have an overload without configure even if that's nullable to reduce noise in client code, just call with null internally in the SDK

  loggerProviderBuilder =>
  {
      loggerProviderBuilder.ConfigureResource(resource =>
      {
          resource.AddDetector(sp => sp.GetRequiredService<DatadogResourceDetector>());
      });
-     loggerProviderBuilder.AddOtlpExporter(NamedOptions, configureExporter: null);
+     loggerProviderBuilder.AddOtlpExporter(NamedOptions);
  },

==============================================================================
Configuring OTEL Exporter should pass-in `IServiceProvider` so we can avoid bouncing back to `AddOptions` manually:

```diff
- builder.Services
-     .AddOptions<OtlpExporterOptions>(NamedOptions)
-     .Configure<IOptions<DatadogOptions>>((options, datadogOptions) =>
-     {
-         options.Headers = $"dd-api-key={datadogOptions.Value.ApiKey}";
-         options.Protocol = OtlpExportProtocol.HttpProtobuf;
-         options.Endpoint = new Uri("https://http-intake.logs.datadoghq.eu/v1/logs");
-     });

  (loggerProviderBuilder) =>
  {
      loggerProviderBuilder.ConfigureResource(resource =>
      {
          resource.AddDetector(sp => sp.GetRequiredService<DatadogResourceDetector>());
      });
-     loggerProviderBuilder.AddOtlpExporter(NamedOptions, configureExporter: null);
+     loggerProviderBuilder.AddOtlpExporter((sp, options) =>
+     {
+         // this would allow me to avoid named options
+         var dataDogOptions = sp.GetRequiredService<IOptions<DatadogOptions>>().Value;
+         options.Endpoint = dataDogOptions.LogEndpoint;
+         options.Protocol = OtlpExportProtocol.HttpProtobuf;
+         options.Headers = $"dd-api-key={datadogOptions.Value.ApiKey}";
+         // etc ...
+     });
  },

=================================================
Resource / ResourceDetector should pass in the `IServiceProvider`
```diff
- public class DatadogResourceDetector(IOptions<DatadogOptions> options) : IResourceDetector
- {
-     public const string AttributeTeam = "team";
-     public const string AttributeEnvironment = "deployment.environment.name";
-     public const string AttributeServiceName = "service.name";
-     public const string AttributeServiceVersion = "service.version";

-     public Resource Detect()
-         => new Resource([
-             new (AttributeTeam, options.Value.Team ),
-             new (AttributeEnvironment, options.Value.Environment),
-             new (AttributeServiceName, options.Value.Service),
-             new (AttributeServiceVersion, options.Value.Version),
-         ]);
- }
- 
- builder.Services.AddSingleton<DatadogResourceDetector>();
- 
- loggerProviderBuilder.ConfigureResource(resource =>
- {
-     resource.AddDetector(sp => sp.GetRequiredService<DatadogResourceDetector>());
- });
+ loggerProviderBuilder.ConfigureResource((resource, sp) =>
+ {
+     var dataDogOptions = sp.GetRequiredService<IOptions<DatadogOptions>>().Value;
+     resource.Attributes.AddRange([
+         new (AttributeTeam, options.Value.Team ),
+         new (AttributeEnvironment, options.Value.Environment),
+         new (AttributeServiceName, options.Value.Service),
+         new (AttributeServiceVersion, options.Value.Version),
+       ]);
+ });

What is the expected behavior?

I simpler integration experience, giving the same "functionally" as today, more direct and less verbose in client code.

DI friendly method should probably be documented on the Resource docs as well, as I had to Debug an AspNetCore app with SourceLink to find that the idea of Detector was existing.

Which alternative solutions or features have you considered?

I couldn't find any, but i might just have missed it

Additional context

No response

Tip

React with 👍 to help prioritize this issue. Please use comments to provide useful context, avoiding +1 or me too, to help us triage it. Learn more here.

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions