Skip to content

Commit

Permalink
Annotate Serilog.Settings.Configuration as not trim-compatible
Browse files Browse the repository at this point in the history
Unfortunately I don't have any good news for this library. It looks like the fundamental
concept, loading arbitrary types by name from arbitrary assemblies, configured by the user
at run-time, is fundamentally incompatible with trimming and AOT.

This at least marks it as such. Supporting a feature set like the one provided
here would likely require a source generator, or build-time code generation phase.
  • Loading branch information
agocke committed Mar 31, 2023
1 parent eae1ded commit c395276
Show file tree
Hide file tree
Showing 9 changed files with 183 additions and 8 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.

using System.Diagnostics.CodeAnalysis;
using System.Reflection;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyModel;
Expand Down Expand Up @@ -43,6 +44,8 @@ public static class ConfigurationLoggerConfigurationExtensions
/// default will be used.</param>
/// <returns>An object allowing configuration to continue.</returns>
[Obsolete("Use ReadFrom.Configuration(IConfiguration configuration, ConfigurationReaderOptions readerOptions) instead.")]
[RequiresUnreferencedCode(TrimWarningMessages.NotSupportedWhenTrimming)]
[RequiresDynamicCode(TrimWarningMessages.NotSupportedInAot)]
public static LoggerConfiguration Configuration(
this LoggerSettingsConfiguration settingConfiguration,
IConfiguration configuration,
Expand All @@ -68,6 +71,8 @@ public static LoggerConfiguration Configuration(
/// default will be used.</param>
/// <returns>An object allowing configuration to continue.</returns>
[Obsolete("Use ReadFrom.Configuration(IConfiguration configuration, ConfigurationReaderOptions readerOptions) instead.")]
[RequiresUnreferencedCode(TrimWarningMessages.NotSupportedWhenTrimming)]
[RequiresDynamicCode(TrimWarningMessages.NotSupportedInAot)]
public static LoggerConfiguration Configuration(
this LoggerSettingsConfiguration settingConfiguration,
IConfiguration configuration,
Expand All @@ -84,6 +89,8 @@ public static LoggerConfiguration Configuration(
/// default will be used.</param>
/// <returns>An object allowing configuration to continue.</returns>
[Obsolete("Use ReadFrom.Configuration(IConfiguration configuration, string sectionName, DependencyContext dependencyContext) instead.")]
[RequiresUnreferencedCode(TrimWarningMessages.NotSupportedWhenTrimming)]
[RequiresDynamicCode(TrimWarningMessages.NotSupportedInAot)]
public static LoggerConfiguration ConfigurationSection(
this LoggerSettingsConfiguration settingConfiguration,
IConfigurationSection configSection,
Expand Down Expand Up @@ -115,6 +122,8 @@ public static LoggerConfiguration ConfigurationSection(
/// <param name="configurationAssemblySource">Defines how the package identifies assemblies to scan for sinks and other types.</param>
/// <returns>An object allowing configuration to continue.</returns>
[Obsolete("Use ReadFrom.Configuration(IConfiguration configuration, ConfigurationReaderOptions readerOptions) instead.")]
[RequiresUnreferencedCode(TrimWarningMessages.NotSupportedWhenTrimming)]
[RequiresDynamicCode(TrimWarningMessages.NotSupportedInAot)]
public static LoggerConfiguration Configuration(
this LoggerSettingsConfiguration settingConfiguration,
IConfiguration configuration,
Expand All @@ -139,6 +148,8 @@ public static LoggerConfiguration Configuration(
/// <param name="configurationAssemblySource">Defines how the package identifies assemblies to scan for sinks and other types.</param>
/// <returns>An object allowing configuration to continue.</returns>
[Obsolete("Use ReadFrom.Configuration(IConfiguration configuration, ConfigurationReaderOptions readerOptions) instead.")]
[RequiresUnreferencedCode(TrimWarningMessages.NotSupportedWhenTrimming)]
[RequiresDynamicCode(TrimWarningMessages.NotSupportedInAot)]
public static LoggerConfiguration Configuration(
this LoggerSettingsConfiguration settingConfiguration,
IConfiguration configuration,
Expand All @@ -154,6 +165,8 @@ public static LoggerConfiguration Configuration(
/// <param name="configurationAssemblySource">Defines how the package identifies assemblies to scan for sinks and other types.</param>
/// <returns>An object allowing configuration to continue.</returns>
[Obsolete("Use ReadFrom.Configuration(IConfiguration configuration, string sectionName, ConfigurationAssemblySource configurationAssemblySource) instead.")]
[RequiresUnreferencedCode(TrimWarningMessages.NotSupportedWhenTrimming)]
[RequiresDynamicCode(TrimWarningMessages.NotSupportedInAot)]
public static LoggerConfiguration ConfigurationSection(
this LoggerSettingsConfiguration settingConfiguration,
IConfigurationSection configSection,
Expand All @@ -176,6 +189,8 @@ public static LoggerConfiguration ConfigurationSection(
/// <param name="assemblies">A collection of assemblies that contains sinks and other types.</param>
/// <returns>An object allowing configuration to continue.</returns>
[Obsolete("Use ReadFrom.Configuration(IConfiguration configuration, ConfigurationReaderOptions readerOptions) instead.")]
[RequiresUnreferencedCode(TrimWarningMessages.NotSupportedWhenTrimming)]
[RequiresDynamicCode(TrimWarningMessages.NotSupportedInAot)]
public static LoggerConfiguration Configuration(
this LoggerSettingsConfiguration settingConfiguration,
IConfiguration configuration,
Expand All @@ -198,6 +213,8 @@ public static LoggerConfiguration Configuration(
/// <param name="assemblies">A collection of assemblies that contains sinks and other types.</param>
/// <returns>An object allowing configuration to continue.</returns>
[Obsolete("Use ReadFrom.Configuration(IConfiguration configuration, ConfigurationReaderOptions readerOptions) instead.")]
[RequiresUnreferencedCode(TrimWarningMessages.NotSupportedWhenTrimming)]
[RequiresDynamicCode(TrimWarningMessages.NotSupportedInAot)]
public static LoggerConfiguration Configuration(
this LoggerSettingsConfiguration settingConfiguration,
IConfiguration configuration,
Expand All @@ -211,6 +228,8 @@ public static LoggerConfiguration Configuration(
/// <param name="configuration">A configuration object which contains a Serilog section.</param>
/// <param name="readerOptions">Options to adjust how the configuration object is processed.</param>
/// <returns>An object allowing configuration to continue.</returns>
[RequiresUnreferencedCode(TrimWarningMessages.NotSupportedWhenTrimming)]
[RequiresDynamicCode(TrimWarningMessages.NotSupportedInAot)]
public static LoggerConfiguration Configuration(
this LoggerSettingsConfiguration settingConfiguration,
IConfiguration configuration,
Expand All @@ -225,20 +244,26 @@ public static LoggerConfiguration Configuration(
return settingConfiguration.Settings(configurationReader);
}

[RequiresUnreferencedCode(TrimWarningMessages.UnboundedReflection)]
[RequiresDynamicCode(TrimWarningMessages.CreatesArraysOfArbitraryTypes)]
static ConfigurationReader GetConfigurationReader(IConfiguration configuration, ConfigurationReaderOptions readerOptions, DependencyContext? dependencyContext)
{
var assemblyFinder = dependencyContext == null ? AssemblyFinder.Auto() : AssemblyFinder.ForDependencyContext(dependencyContext);
var section = string.IsNullOrWhiteSpace(readerOptions.SectionName) ? configuration : configuration.GetSection(readerOptions.SectionName);
return new ConfigurationReader(section, assemblyFinder, readerOptions, configuration);
}

[RequiresUnreferencedCode(TrimWarningMessages.UnboundedReflection)]
[RequiresDynamicCode(TrimWarningMessages.CreatesArraysOfArbitraryTypes)]
static ConfigurationReader GetConfigurationReader(IConfiguration configuration, ConfigurationReaderOptions readerOptions, ConfigurationAssemblySource source)
{
var assemblyFinder = AssemblyFinder.ForSource(source);
var section = string.IsNullOrWhiteSpace(readerOptions.SectionName) ? configuration : configuration.GetSection(readerOptions.SectionName);
return new ConfigurationReader(section, assemblyFinder, readerOptions, configuration);
}

[RequiresUnreferencedCode(TrimWarningMessages.UnboundedReflection)]
[RequiresDynamicCode(TrimWarningMessages.CreatesArraysOfArbitraryTypes)]
static ConfigurationReader GetConfigurationReader(IConfiguration configuration, ConfigurationReaderOptions readerOptions, IReadOnlyCollection<Assembly> assemblies)
{
var section = string.IsNullOrWhiteSpace(readerOptions.SectionName) ? configuration : configuration.GetSection(readerOptions.SectionName);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
<Description>Microsoft.Extensions.Configuration (appsettings.json) support for Serilog.</Description>
<VersionPrefix>4.0.0</VersionPrefix>
<Authors>Serilog Contributors</Authors>
<TargetFrameworks>netstandard2.0;net461</TargetFrameworks>
<TargetFrameworks>net7.0;netstandard2.0;net461</TargetFrameworks>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
<AssemblyName>Serilog.Settings.Configuration</AssemblyName>
<PackageTags>serilog;json</PackageTags>
Expand All @@ -15,6 +15,8 @@
<EmbedUntrackedSources>true</EmbedUntrackedSources>
<IncludeSymbols>true</IncludeSymbols>
<SymbolPackageFormat>snupkg</SymbolPackageFormat>
<IsTrimmable>true</IsTrimmable>
<EnableAotAnalyzer>true</EnableAotAnalyzer>
</PropertyGroup>

<ItemGroup>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
using System.Diagnostics.CodeAnalysis;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Text.RegularExpressions;
Expand All @@ -13,6 +14,8 @@

namespace Serilog.Settings.Configuration;

[RequiresUnreferencedCode(TrimWarningMessages.UnboundedReflection)]
[RequiresDynamicCode(TrimWarningMessages.CreatesArraysOfArbitraryTypes)]
class ConfigurationReader : IConfigurationReader
{
const string LevelSwitchNameRegex = @"^\${0,1}[A-Za-z]+[A-Za-z0-9]*$";
Expand Down Expand Up @@ -298,6 +301,7 @@ void ApplyEnrichment(LoggerConfiguration loggerConfiguration)
}
}

[RequiresDynamicCode(TrimWarningMessages.CreatesArraysOfArbitraryTypes)]
internal ILookup<string, Dictionary<string, IConfigurationArgumentValue>> GetMethodCalls(IConfiguration directive)
{
var children = directive.GetChildren().ToList();
Expand Down Expand Up @@ -331,6 +335,7 @@ static string GetSectionName(IConfigurationSection s)
}
}

[RequiresDynamicCode(TrimWarningMessages.CreatesArraysOfArbitraryTypes)]
internal static IConfigurationArgumentValue GetArgumentValue(IConfigurationSection argumentSection, IReadOnlyCollection<Assembly> configurationAssemblies)
{
IConfigurationArgumentValue argumentValue;
Expand Down Expand Up @@ -359,7 +364,7 @@ internal static IConfigurationArgumentValue GetArgumentValue(IConfigurationSecti
static IReadOnlyCollection<Assembly> LoadConfigurationAssemblies(IConfiguration section, AssemblyFinder assemblyFinder)
{
var serilogAssembly = typeof(ILogger).Assembly;
var assemblies = new Dictionary<string, Assembly> { [serilogAssembly.FullName] = serilogAssembly };
var assemblies = new Dictionary<string, Assembly> { [serilogAssembly.FullName!] = serilogAssembly };

var usingSection = section.GetSection("Using");
if (usingSection.GetChildren().Any())
Expand All @@ -371,16 +376,16 @@ static IReadOnlyCollection<Assembly> LoadConfigurationAssemblies(IConfiguration
"A zero-length or whitespace assembly name was supplied to a Serilog.Using configuration statement.");

var assembly = Assembly.Load(new AssemblyName(simpleName));
if (!assemblies.ContainsKey(assembly.FullName))
assemblies.Add(assembly.FullName, assembly);
if (!assemblies.ContainsKey(assembly.FullName!))
assemblies.Add(assembly.FullName!, assembly);
}
}

foreach (var assemblyName in assemblyFinder.FindAssembliesContainingName("serilog"))
{
var assumed = Assembly.Load(assemblyName);
if (assumed != null && !assemblies.ContainsKey(assumed.FullName))
assemblies.Add(assumed.FullName, assumed);
if (assumed != null && !assemblies.ContainsKey(assumed.FullName!))
assemblies.Add(assumed.FullName!, assumed);
}

return assemblies.Values.ToList().AsReadOnly();
Expand Down Expand Up @@ -504,6 +509,7 @@ static bool ParameterNameMatches(string? actualParameterName, IEnumerable<string
return suppliedNames.Any(s => ParameterNameMatches(actualParameterName, s));
}

[RequiresUnreferencedCode(TrimWarningMessages.UnboundedReflection)]
static IReadOnlyCollection<MethodInfo> FindSinkConfigurationMethods(IReadOnlyCollection<Assembly> configurationAssemblies, bool allowInternalTypes, bool allowInternalMethods)
{
var found = FindConfigurationExtensionMethods(configurationAssemblies, typeof(LoggerSinkConfiguration), allowInternalTypes, allowInternalMethods);
Expand All @@ -513,6 +519,7 @@ static IReadOnlyCollection<MethodInfo> FindSinkConfigurationMethods(IReadOnlyCol
return found;
}

[RequiresUnreferencedCode(TrimWarningMessages.UnboundedReflection)]
static IReadOnlyCollection<MethodInfo> FindAuditSinkConfigurationMethods(IReadOnlyCollection<Assembly> configurationAssemblies, bool allowInternalTypes, bool allowInternalMethods)
{
var found = FindConfigurationExtensionMethods(configurationAssemblies, typeof(LoggerAuditSinkConfiguration), allowInternalTypes, allowInternalMethods);
Expand All @@ -521,6 +528,7 @@ static IReadOnlyCollection<MethodInfo> FindAuditSinkConfigurationMethods(IReadOn
return found;
}

[RequiresUnreferencedCode(TrimWarningMessages.UnboundedReflection)]
static IReadOnlyCollection<MethodInfo> FindFilterConfigurationMethods(IReadOnlyCollection<Assembly> configurationAssemblies, bool allowInternalTypes, bool allowInternalMethods)
{
var found = FindConfigurationExtensionMethods(configurationAssemblies, typeof(LoggerFilterConfiguration), allowInternalTypes, allowInternalMethods);
Expand All @@ -530,6 +538,7 @@ static IReadOnlyCollection<MethodInfo> FindFilterConfigurationMethods(IReadOnlyC
return found;
}

[RequiresUnreferencedCode(TrimWarningMessages.UnboundedReflection)]
static IReadOnlyCollection<MethodInfo> FindDestructureConfigurationMethods(IReadOnlyCollection<Assembly> configurationAssemblies, bool allowInternalTypes, bool allowInternalMethods)
{
var found = FindConfigurationExtensionMethods(configurationAssemblies, typeof(LoggerDestructuringConfiguration), allowInternalTypes, allowInternalMethods);
Expand All @@ -539,6 +548,7 @@ static IReadOnlyCollection<MethodInfo> FindDestructureConfigurationMethods(IRead
return found;
}

[RequiresUnreferencedCode(TrimWarningMessages.UnboundedReflection)]
static IReadOnlyCollection<MethodInfo> FindEventEnricherConfigurationMethods(IReadOnlyCollection<Assembly> configurationAssemblies, bool allowInternalTypes, bool allowInternalMethods)
{
var found = FindConfigurationExtensionMethods(configurationAssemblies, typeof(LoggerEnrichmentConfiguration), allowInternalTypes, allowInternalMethods);
Expand All @@ -548,6 +558,7 @@ static IReadOnlyCollection<MethodInfo> FindEventEnricherConfigurationMethods(IRe
return found;
}

[RequiresUnreferencedCode(TrimWarningMessages.UnboundedReflection)]
static List<MethodInfo> FindConfigurationExtensionMethods(IReadOnlyCollection<Assembly> configurationAssemblies, Type configType, bool allowInternalTypes, bool allowInternalMethods)
{
// ExtensionAttribute can be polyfilled to support extension methods
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@

#if !NET6_0_OR_GREATER
namespace System.Diagnostics.CodeAnalysis
{
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct | AttributeTargets.Method | AttributeTargets.Property | AttributeTargets.Field | AttributeTargets.Interface | AttributeTargets.Parameter | AttributeTargets.ReturnValue | AttributeTargets.GenericParameter, Inherited = false)]
internal sealed class DynamicallyAccessedMembersAttribute : Attribute
{
public DynamicallyAccessedMembersAttribute(DynamicallyAccessedMemberTypes memberTypes)
{
MemberTypes = memberTypes;
}

public DynamicallyAccessedMemberTypes MemberTypes { get; }
}

[Flags]
internal enum DynamicallyAccessedMemberTypes
{
//
// Summary:
// Specifies all members.
All = -1,
//
// Summary:
// Specifies no members.
None = 0,
//
// Summary:
// Specifies the default, parameterless public constructor.
PublicParameterlessConstructor = 1,
//
// Summary:
// Specifies all public constructors.
PublicConstructors = 3,
//
// Summary:
// Specifies all non-public constructors.
NonPublicConstructors = 4,
//
// Summary:
// Specifies all public methods.
PublicMethods = 8,
//
// Summary:
// Specifies all non-public methods.
NonPublicMethods = 16,
//
// Summary:
// Specifies all public fields.
PublicFields = 32,
//
// Summary:
// Specifies all non-public fields.
NonPublicFields = 64,
//
// Summary:
// Specifies all public nested types.
PublicNestedTypes = 128,
//
// Summary:
// Specifies all non-public nested types.
NonPublicNestedTypes = 256,
//
// Summary:
// Specifies all public properties.
PublicProperties = 512,
//
// Summary:
// Specifies all non-public properties.
NonPublicProperties = 1024,
//
// Summary:
// Specifies all public events.
PublicEvents = 2048,
//
// Summary:
// Specifies all non-public events.
NonPublicEvents = 4096,
//
// Summary:
// Specifies all interfaces implemented by the type.
Interfaces = 8192
}
}
#endif
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@

namespace Serilog.Settings.Configuration;

[RequiresUnreferencedCode(TrimWarningMessages.UnboundedReflection)]
[RequiresDynamicCode(TrimWarningMessages.CreatesArraysOfArbitraryTypes)]
class ObjectArgumentValue : IConfigurationArgumentValue
{
readonly IConfigurationSection _section;
Expand Down Expand Up @@ -98,6 +100,7 @@ bool TryCreateContainer([NotNullWhen(true)] out object? result)
}
}

[RequiresUnreferencedCode(TrimWarningMessages.UnboundedReflection)]
internal static bool TryBuildCtorExpression(
IConfigurationSection section, Type parameterType, ResolutionContext resolutionContext, [NotNullWhen(true)] out NewExpression? ctorExpression)
{
Expand Down Expand Up @@ -218,7 +221,9 @@ static bool TryBindToCtorArgument(object value, Type type, ResolutionContext res
}
}

static bool IsContainer(Type type, [NotNullWhen(true)] out Type? elementType)
static bool IsContainer(
[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.Interfaces)] Type type,
[NotNullWhen(true)] out Type? elementType)
{
elementType = null;
foreach (var iface in type.GetInterfaces())
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
#if !NET6_0_OR_GREATER

namespace System.Diagnostics.CodeAnalysis
{
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Constructor | AttributeTargets.Method, Inherited = false)]
internal sealed class RequiresDynamicCodeAttribute : Attribute
{
public RequiresDynamicCodeAttribute(string message)
{
Message = message;
}

public string Message { get; }
}
}
#endif
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@

#if !NET5_0_OR_GREATER
namespace System.Diagnostics.CodeAnalysis
{
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Constructor | AttributeTargets.Method, Inherited = false)]
internal sealed class RequiresUnreferencedCodeAttribute : Attribute
{
public RequiresUnreferencedCodeAttribute(string message)
{
Message = message;
}

public string Message { get; }
}
}
#endif
Loading

0 comments on commit c395276

Please sign in to comment.