[main-1.8.0] Backport fixes for 1.8.1 core release (#5543)

Co-authored-by: Martin Costello <martin@martincostello.com>
Co-authored-by: Eric Erhardt <eric.erhardt@microsoft.com>
Co-authored-by: Reiley Yang <reyang@microsoft.com>
This commit is contained in:
Mikel Blanchard 2024-04-17 14:17:49 -07:00 committed by GitHub
parent fb74013d64
commit 34daa1f96f
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
14 changed files with 217 additions and 16 deletions

View File

@ -11,7 +11,7 @@ jobs:
strategy:
fail-fast: false # ensures the entire test matrix is run, even if one permutation fails
matrix:
os: [ ubuntu-latest ]
os: [ ubuntu-latest, windows-latest ]
version: [ net8.0 ]
runs-on: ${{ matrix.os }}
@ -24,4 +24,3 @@ jobs:
- name: publish AOT testApp, assert static analysis warning count, and run the app
shell: pwsh
run: .\build\test-aot-compatibility.ps1 ${{ matrix.version }}

View File

@ -28,6 +28,7 @@
-->
<PackageVersion Include="Microsoft.Extensions.Configuration" Version="8.0.0" />
<PackageVersion Include="Microsoft.Extensions.Configuration.Binder" Version="8.0.1" />
<PackageVersion Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="8.0.0" />
<PackageVersion Include="Microsoft.Extensions.Diagnostics.Abstractions" Version="8.0.0" />
<PackageVersion Include="Microsoft.Extensions.Hosting.Abstractions" Version="8.0.0" />

View File

@ -299,6 +299,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Options", "Options", "{4949
ProjectSection(SolutionItems) = preProject
src\Shared\Options\DelegatingOptionsFactory.cs = src\Shared\Options\DelegatingOptionsFactory.cs
src\Shared\Options\DelegatingOptionsFactoryServiceCollectionExtensions.cs = src\Shared\Options\DelegatingOptionsFactoryServiceCollectionExtensions.cs
src\Shared\Options\SingletonOptionsManager.cs = src\Shared\Options\SingletonOptionsManager.cs
EndProjectSection
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Shims", "Shims", "{A0CB9A10-F22D-4E66-A449-74B3D0361A9C}"

View File

@ -1,24 +1,35 @@
param([string]$targetNetFramework)
$rootDirectory = Split-Path $PSScriptRoot -Parent
$publishOutput = dotnet publish $rootDirectory/test/OpenTelemetry.AotCompatibility.TestApp/OpenTelemetry.AotCompatibility.TestApp.csproj -nodeReuse:false /p:UseSharedCompilation=false /p:ExposeExperimentalFeatures=true
$publishOutput = dotnet publish $rootDirectory/test/OpenTelemetry.AotCompatibility.TestApp/OpenTelemetry.AotCompatibility.TestApp.csproj --framework $targetNetFramework -nodeReuse:false /p:UseSharedCompilation=false /p:ExposeExperimentalFeatures=true
$actualWarningCount = 0
foreach ($line in $($publishOutput -split "`r`n"))
{
if ($line -like "*analysis warning IL*")
if (($line -like "*analysis warning IL*") -or ($line -like "*analysis error IL*"))
{
Write-Host $line
$actualWarningCount += 1
}
}
pushd $rootDirectory/test/OpenTelemetry.AotCompatibility.TestApp/bin/Release/$targetNetFramework/linux-x64
Write-Host "Actual warning count is:", $actualWarningCount
$expectedWarningCount = 0
if ($LastExitCode -ne 0)
{
Write-Host "There was an error while publishing AotCompatibility Test App. LastExitCode is:", $LastExitCode
Write-Host $publishOutput
}
$runtime = $IsWindows ? "win-x64" : ($IsMacOS ? "macos-x64" : "linux-x64")
$app = $IsWindows ? "./OpenTelemetry.AotCompatibility.TestApp.exe" : "./OpenTelemetry.AotCompatibility.TestApp"
Push-Location $rootDirectory/test/OpenTelemetry.AotCompatibility.TestApp/bin/Release/$targetNetFramework/$runtime
Write-Host "Executing test App..."
./OpenTelemetry.AotCompatibility.TestApp
$app
Write-Host "Finished executing test App"
if ($LastExitCode -ne 0)
@ -26,10 +37,7 @@ if ($LastExitCode -ne 0)
Write-Host "There was an error while executing AotCompatibility Test App. LastExitCode is:", $LastExitCode
}
popd
Write-Host "Actual warning count is:", $actualWarningCount
$expectedWarningCount = 0
Pop-Location
$testPassed = 0
if ($actualWarningCount -ne $expectedWarningCount)

View File

@ -2,6 +2,9 @@
## Unreleased
* Fix native AoT warnings in `OpenTelemetry.Exporter.OpenTelemetryProtocol`.
([#5520](https://github.com/open-telemetry/opentelemetry-dotnet/pull/5520))
## 1.8.0
Released 2024-Apr-02

View File

@ -8,6 +8,9 @@
<!-- this is temporary. will remove in future PR. -->
<Nullable>disable</Nullable>
<DefineConstants>BUILDING_INTERNAL_PERSISTENT_STORAGE;$(DefineConstants)</DefineConstants>
<EnableConfigurationBindingGenerator>true</EnableConfigurationBindingGenerator>
<!-- SYSLIB1100;SYSLIB1101 - Configuration.Binder: can't create instance and unsupported type -->
<NoWarn>$(NoWarn);SYSLIB1100;SYSLIB1101</NoWarn>
</PropertyGroup>
<ItemGroup>
@ -19,6 +22,7 @@
<PackageReference Include="Grpc" Condition="'$(TargetFramework)' == 'netstandard2.0' OR '$(TargetFramework)' == '$(NetFrameworkMinimumSupportedVersion)'" />
<PackageReference Include="Google.Protobuf" />
<PackageReference Include="Grpc.Tools" PrivateAssets="All" />
<PackageReference Include="Microsoft.Extensions.Configuration.Binder" />
</ItemGroup>
<ItemGroup>

View File

@ -2,6 +2,11 @@
## Unreleased
* Fixed an issue in Logging where unwanted objects (processors, exporters, etc.)
could be created inside delegates automatically executed by the Options API
during configuration reload.
([#5514](https://github.com/open-telemetry/opentelemetry-dotnet/pull/5514))
## 1.8.0
Released 2024-Apr-02

View File

@ -117,6 +117,7 @@ public class OpenTelemetryLoggerOptions
Guard.ThrowIfNull(resourceBuilder);
this.ResourceBuilder = resourceBuilder;
return this;
}

View File

@ -173,6 +173,13 @@ public static class OpenTelemetryLoggingExtensions
// Note: This will bind logger options element (e.g., "Logging:OpenTelemetry") to OpenTelemetryLoggerOptions
RegisterLoggerProviderOptions(services);
// Note: We disable built-in IOptionsMonitor and IOptionsSnapshot
// features for OpenTelemetryLoggerOptions as a workaround to prevent
// unwanted objects (processors, exporters, etc.) being created by
// configuration delegates being re-run during reload of IConfiguration
// or from options created while under scopes.
services.DisableOptionsReloading<OpenTelemetryLoggerOptions>();
/* Note: This ensures IConfiguration is available when using
* IServiceCollections NOT attached to a host. For example when
* performing:
@ -192,7 +199,7 @@ public static class OpenTelemetryLoggingExtensions
var loggingBuilder = new LoggerProviderBuilderBase(services).ConfigureBuilder(
(sp, logging) =>
{
var options = sp.GetRequiredService<IOptionsMonitor<OpenTelemetryLoggerOptions>>().CurrentValue;
var options = sp.GetRequiredService<IOptions<OpenTelemetryLoggerOptions>>().Value;
if (options.ResourceBuilder != null)
{
@ -249,7 +256,7 @@ public static class OpenTelemetryLoggingExtensions
return new OpenTelemetryLoggerProvider(
provider,
sp.GetRequiredService<IOptionsMonitor<OpenTelemetryLoggerOptions>>().CurrentValue,
sp.GetRequiredService<IOptions<OpenTelemetryLoggerOptions>>().Value,
disposeProvider: false);
}));

View File

@ -16,6 +16,9 @@
#nullable enable
using System.Diagnostics;
#if NET6_0_OR_GREATER
using System.Diagnostics.CodeAnalysis;
#endif
using Microsoft.Extensions.Configuration;
namespace Microsoft.Extensions.Options;
@ -24,7 +27,11 @@ namespace Microsoft.Extensions.Options;
/// Implementation of <see cref="IOptionsFactory{TOptions}"/>.
/// </summary>
/// <typeparam name="TOptions">The type of options being requested.</typeparam>
#if NET6_0_OR_GREATER
internal sealed class DelegatingOptionsFactory<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)] TOptions> :
#else
internal sealed class DelegatingOptionsFactory<TOptions> :
#endif
IOptionsFactory<TOptions>
where TOptions : class
{
@ -74,6 +81,7 @@ internal sealed class DelegatingOptionsFactory<TOptions> :
public TOptions Create(string name)
{
TOptions options = this.optionsFactoryFunc(this.configuration, name);
foreach (IConfigureOptions<TOptions> setup in _setups)
{
if (setup is IConfigureNamedOptions<TOptions> namedSetup)

View File

@ -4,6 +4,9 @@
#nullable enable
using System.Diagnostics;
#if NET6_0_OR_GREATER
using System.Diagnostics.CodeAnalysis;
#endif
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection.Extensions;
using Microsoft.Extensions.Options;
@ -12,7 +15,11 @@ namespace Microsoft.Extensions.DependencyInjection;
internal static class DelegatingOptionsFactoryServiceCollectionExtensions
{
#if NET6_0_OR_GREATER
public static IServiceCollection RegisterOptionsFactory<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] T>(
#else
public static IServiceCollection RegisterOptionsFactory<T>(
#endif
this IServiceCollection services,
Func<IConfiguration, T> optionsFactoryFunc)
where T : class
@ -33,7 +40,11 @@ internal static class DelegatingOptionsFactoryServiceCollectionExtensions
return services!;
}
#if NET6_0_OR_GREATER
public static IServiceCollection RegisterOptionsFactory<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] T>(
#else
public static IServiceCollection RegisterOptionsFactory<T>(
#endif
this IServiceCollection services,
Func<IServiceProvider, IConfiguration, string, T> optionsFactoryFunc)
where T : class
@ -51,6 +62,22 @@ internal static class DelegatingOptionsFactoryServiceCollectionExtensions
sp.GetServices<IValidateOptions<T>>());
});
return services!;
}
#if NET6_0_OR_GREATER
public static IServiceCollection DisableOptionsReloading<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] T>(
#else
public static IServiceCollection DisableOptionsReloading<T>(
#endif
this IServiceCollection services)
where T : class
{
Debug.Assert(services != null, "services was null");
services!.TryAddSingleton<IOptionsMonitor<T>, SingletonOptionsManager<T>>();
services!.TryAddScoped<IOptionsSnapshot<T>, SingletonOptionsManager<T>>();
return services!;
}
}

View File

@ -0,0 +1,47 @@
// Copyright The OpenTelemetry Authors
// SPDX-License-Identifier: Apache-2.0
#nullable enable
#if NET6_0_OR_GREATER
using System.Diagnostics.CodeAnalysis;
#endif
namespace Microsoft.Extensions.Options;
#if NET6_0_OR_GREATER
internal sealed class SingletonOptionsManager<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicParameterlessConstructor)] TOptions> : IOptionsMonitor<TOptions>, IOptionsSnapshot<TOptions>
#else
internal sealed class SingletonOptionsManager<TOptions> : IOptionsMonitor<TOptions>, IOptionsSnapshot<TOptions>
#endif
where TOptions : class
{
private readonly TOptions instance;
public SingletonOptionsManager(IOptions<TOptions> options)
{
this.instance = options.Value;
}
public TOptions CurrentValue => this.instance;
public TOptions Value => this.instance;
public TOptions Get(string? name) => this.instance;
public IDisposable? OnChange(Action<TOptions, string?> listener)
=> NoopChangeNotification.Instance;
private sealed class NoopChangeNotification : IDisposable
{
private NoopChangeNotification()
{
}
public static NoopChangeNotification Instance { get; } = new();
public void Dispose()
{
}
}
}

View File

@ -2,7 +2,7 @@
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>$(TargetFrameworksForAotCompatibilityTests)</TargetFramework>
<TargetFrameworks>$(TargetFrameworksForAotCompatibilityTests)</TargetFrameworks>
<PublishAot>true</PublishAot>
<TrimmerSingleWarn>false</TrimmerSingleWarn>
<SelfContained>true</SelfContained>

View File

@ -7,6 +7,7 @@ using System.Reflection;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using Xunit;
namespace OpenTelemetry.Logs.Tests;
@ -297,11 +298,100 @@ public sealed class OpenTelemetryLoggingExtensionsTests
Assert.True(loggerProvider.Processor is TestLogProcessorWithILoggerFactoryDependency);
}
private class TestLogProcessor : BaseProcessor<LogRecord>
[Theory]
[InlineData(true, false)]
[InlineData(false, true)]
[InlineData(false, false)]
public void OptionReloadingTest(bool useOptionsMonitor, bool useOptionsSnapshot)
{
var delegateInvocationCount = 0;
var root = new ConfigurationBuilder().Build();
var services = new ServiceCollection();
services.AddSingleton<IConfiguration>(root);
services.AddLogging(logging => logging
.AddConfiguration(root.GetSection("logging"))
.AddOpenTelemetry(options =>
{
delegateInvocationCount++;
options.AddProcessor(new TestLogProcessor());
}));
using var sp = services.BuildServiceProvider();
if (useOptionsMonitor)
{
var optionsMonitor = sp.GetRequiredService<IOptionsMonitor<OpenTelemetryLoggerOptions>>();
Assert.NotNull(optionsMonitor.CurrentValue);
}
if (useOptionsSnapshot)
{
using var scope = sp.CreateScope();
var optionsSnapshot = scope.ServiceProvider.GetRequiredService<IOptionsSnapshot<OpenTelemetryLoggerOptions>>();
Assert.NotNull(optionsSnapshot.Value);
}
var loggerFactory = sp.GetRequiredService<ILoggerFactory>();
Assert.Equal(1, delegateInvocationCount);
root.Reload();
Assert.Equal(1, delegateInvocationCount);
}
private class TestLogProcessorWithILoggerFactoryDependency : BaseProcessor<LogRecord>
[Fact]
public void MixedOptionsUsageTest()
{
var root = new ConfigurationBuilder().Build();
var services = new ServiceCollection();
services.AddSingleton<IConfiguration>(root);
services.AddLogging(logging => logging
.AddConfiguration(root.GetSection("logging"))
.AddOpenTelemetry(options =>
{
options.AddProcessor(new TestLogProcessor());
}));
using var sp = services.BuildServiceProvider();
var loggerFactory = sp.GetRequiredService<ILoggerFactory>();
var optionsMonitor = sp.GetRequiredService<IOptionsMonitor<OpenTelemetryLoggerOptions>>().CurrentValue;
var options = sp.GetRequiredService<IOptions<OpenTelemetryLoggerOptions>>().Value;
Assert.True(ReferenceEquals(options, optionsMonitor));
using var scope = sp.CreateScope();
var optionsSnapshot = scope.ServiceProvider.GetRequiredService<IOptionsSnapshot<OpenTelemetryLoggerOptions>>().Value;
Assert.True(ReferenceEquals(options, optionsSnapshot));
}
private sealed class TestLogProcessor : BaseProcessor<LogRecord>
{
public bool Disposed;
protected override void Dispose(bool disposing)
{
this.Disposed = true;
base.Dispose(disposing);
}
}
private sealed class TestLogProcessorWithILoggerFactoryDependency : BaseProcessor<LogRecord>
{
private readonly ILogger logger;