Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[sdk-logs] Support builder behavior on OpenTelemetryLoggerOptions (proposal 3) #4502

Conversation

CodeBlanch
Copy link
Member

@CodeBlanch CodeBlanch commented May 17, 2023

Relates to #4433
Relates to #4501

/cc @noahfalk

Changes

  • Introduce OpenTelemetryLoggerOptions.ConfigureOpenTelemetry method for configuring LoggerProviderBuilder
  • Introduce LoggerProviderBuilder.ConfigureLoggerOptions extension for configuring OpenTelemetryLoggerOptions

Overview

Some have already asked and I'm sure others will ask in the future: Why can't OpenTelemetryLoggerOptions just be a LoggerProviderBuilder?

Builders and options are fundamentally different things. Builders (typically) operate on the IServiceCollection. Options are (typically) requested through the IServiceProvider.

What this PR does is hack it to work. When AddOpenTelemetry is called we create an OpenTelemetryLoggerOptions instance bound to the IServiceCollection and invoke the configuration delegate immediately. Later on we retrieve a different OpenTelemetryLoggerOptions instance once the IServiceProvider is available.

Details

Today we configure logging like this:

appBuilder.Logging.AddOpenTelemetry(options =>
{
   options.IncludeFormattedMessage = true;
   options.AddConsoleExporter();
});

"options" is OpenTelemetryLoggerOptions class.

Now the OpenTelemetry Specification has LoggerProvider and we have a LoggerProviderBuilder API. The SDK LoggerProvider is fed into the ILogger integration (OpenTelemetryLoggerProvider).

There are different ways we could approach this. Today we have extensions for logging that target OpenTelemetryLoggerOptions. We will need to target LoggerProviderBuilder now. We could forever have two versions of every extension, but that seems lame.

This PR is adding a new method ConfigureOpenTelemetry on OpenTelemetryLoggerOptions to try and bridge the two worlds. OpenTelemetryLoggerOptions.AddProcessor and OpenTelemetryLoggerOptions.SetResourceBuilder methods would be obsoleted as would our existing extensions on OpenTelemetryLoggerOptions.

All of these styles can be interchanged.

// Configure OTel via ILoggingBuilder
appBuilder.Logging
   .AddOpenTelemetryLogging(options=> {
      options.IncludeFormattedMessage = true;
      options.ConfigureOpenTelemetry(builder => builder.AddConsoleExporter());
   });
// Configure OTel via OpenTelemetryBuilder
appBuilder.Services
   .AddOpenTelemetry()
   .WithLogging(builder => builder
      .ConfigureLoggerOptions(options => options.IncludeFormattedMessage = true)
      .AddConsoleExporter());
// Configure OTel via OpenTelemetryBuilder
appBuilder.Services
   .AddOpenTelemetry()
   .WithLogging(builder => builder.AddConsoleExporter());

// Configure ILogger options via Options API
appBuilder.Services.Configure<OpenTelemetryLoggerOptions>(options => options.IncludeFormattedMessage = true);
// Configure OTel via ILoggingBuilder
appBuilder.Logging
   .AddOpenTelemetryLogging(builder => builder.AddConsoleExporter());

// Configure ILogger options via Options API
appBuilder.Services.Configure<OpenTelemetryLoggerOptions>(options => options.IncludeFormattedMessage = true);
// Configure OTel via ILoggingBuilder
appBuilder.Logging.AddOpenTelemetryLogging();

// Configure ILogger options via Options API
appBuilder.Services.Configure<OpenTelemetryLoggerOptions>(options => {
   options.IncludeFormattedMessage = true;
   options.ConfigureOpenTelemetry(builder => builder.AddConsoleExporter()); // This isn't really desired but is now allowed
});
// Configure OTel via OpenTelemetryBuilder
appBuilder.Services
   .AddOpenTelemetry()
   .WithLogging();

// Configure ILogger options via Options API
appBuilder.Services.Configure<OpenTelemetryLoggerOptions>(options => {
   options.IncludeFormattedMessage = true;
   options.ConfigureOpenTelemetry(builder => builder.AddConsoleExporter()); // This isn't really desired but is now allowed
});

Breaking changes

I couldn't make this work breaking changes 😭

This works today...

appBuilder.Services.Configure<OpenTelemetryLoggerOptions>(options => options.IncludeFormattedMessage = true);
appBuilder.Logging.AddOpenTelemetryLogging(options => {
   if (options.IncludeFormattedMessage)
   {
       // Do something if IncludeFormattedMessage is turned on
   }
});

What will happen now is...

appBuilder.Services.Configure<OpenTelemetryLoggerOptions>(options => options.IncludeFormattedMessage = true);
appBuilder.Logging.AddOpenTelemetryLogging(options => {
   if (options.IncludeFormattedMessage) // <- NotSupportedException is thrown here
   {
       // Code is unreachable
   }
});

This is because before the delegate passed to AddOpenTelemetry is executed on the final OpenTelemetryLoggerOptions instance being built by Options API. This PR needs to make a different OpenTelemetryLoggerOptions instance and invoke the callback immediately. What that means is the getters on that OpenTelemetryLoggerOptions really have no idea what the state will be of the actual instance when created through Options API. They could just return lies, I decided to have them throw.

Merge requirement checklist

  • CONTRIBUTING guidelines followed (nullable enabled, static analysis, etc.)
  • Unit tests added/updated

@codecov
Copy link

codecov bot commented May 17, 2023

Codecov Report

Merging #4502 (70c724f) into main (d63c32f) will decrease coverage by 0.01%.
The diff coverage is 84.90%.

Additional details and impacted files

Impacted file tree graph

@@            Coverage Diff             @@
##             main    #4502      +/-   ##
==========================================
- Coverage   85.21%   85.20%   -0.01%     
==========================================
  Files         315      316       +1     
  Lines       12549    12588      +39     
==========================================
+ Hits        10694    10726      +32     
- Misses       1855     1862       +7     
Impacted Files Coverage Δ
...ry/Logs/Builder/LoggerProviderBuilderExtensions.cs 92.15% <0.00%> (-5.76%) ⬇️
...lemetry/Logs/ILogger/OpenTelemetryLoggerOptions.cs 90.56% <87.80%> (+5.56%) ⬆️
...lder/ProviderBuilderServiceCollectionExtensions.cs 100.00% <100.00%> (ø)
...emetry/Logs/ILogger/OpenTelemetryLoggerProvider.cs 93.75% <100.00%> (-1.42%) ⬇️
...try/Logs/ILogger/OpenTelemetryLoggingExtensions.cs 100.00% <100.00%> (ø)
src/OpenTelemetry/Logs/LoggerProviderSdk.cs 92.85% <100.00%> (+0.07%) ⬆️

... and 10 files with indirect coverage changes

@github-actions
Copy link
Contributor

This PR was marked stale due to lack of activity and will be closed in 7 days. Commenting or Pushing will instruct the bot to automatically remove the label. This bot runs once per day.

@github-actions github-actions bot added the Stale Issues and pull requests which have been flagged for closing due to inactivity label May 25, 2023
@github-actions
Copy link
Contributor

github-actions bot commented Jun 1, 2023

Closed as inactive. Feel free to reopen if this PR is still being worked on.

@github-actions github-actions bot closed this Jun 1, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Stale Issues and pull requests which have been flagged for closing due to inactivity
Projects
None yet
Development

Successfully merging this pull request may close these issues.

1 participant