Skip to content

Allow internal types and methods #363

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

Merged
merged 4 commits into from
Mar 21, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -233,7 +233,7 @@ void ApplyFilters(LoggerConfiguration loggerConfiguration)
if (filterDirective.GetChildren().Any())
{
var methodCalls = GetMethodCalls(filterDirective);
CallConfigurationMethods(methodCalls, FindFilterConfigurationMethods(_configurationAssemblies), loggerConfiguration.Filter);
CallConfigurationMethods(methodCalls, FindFilterConfigurationMethods(_configurationAssemblies, _resolutionContext.ReaderOptions.AllowInternalTypes, _resolutionContext.ReaderOptions.AllowInternalMethods), loggerConfiguration.Filter);
}
}

Expand All @@ -243,7 +243,7 @@ void ApplyDestructuring(LoggerConfiguration loggerConfiguration)
if (destructureDirective.GetChildren().Any())
{
var methodCalls = GetMethodCalls(destructureDirective);
CallConfigurationMethods(methodCalls, FindDestructureConfigurationMethods(_configurationAssemblies), loggerConfiguration.Destructure);
CallConfigurationMethods(methodCalls, FindDestructureConfigurationMethods(_configurationAssemblies, _resolutionContext.ReaderOptions.AllowInternalTypes, _resolutionContext.ReaderOptions.AllowInternalMethods), loggerConfiguration.Destructure);
}
}

Expand All @@ -253,7 +253,7 @@ void ApplySinks(LoggerConfiguration loggerConfiguration)
if (writeToDirective.GetChildren().Any())
{
var methodCalls = GetMethodCalls(writeToDirective);
CallConfigurationMethods(methodCalls, FindSinkConfigurationMethods(_configurationAssemblies), loggerConfiguration.WriteTo);
CallConfigurationMethods(methodCalls, FindSinkConfigurationMethods(_configurationAssemblies, _resolutionContext.ReaderOptions.AllowInternalTypes, _resolutionContext.ReaderOptions.AllowInternalMethods), loggerConfiguration.WriteTo);
}
}

Expand All @@ -263,20 +263,20 @@ void ApplyAuditSinks(LoggerConfiguration loggerConfiguration)
if (auditToDirective.GetChildren().Any())
{
var methodCalls = GetMethodCalls(auditToDirective);
CallConfigurationMethods(methodCalls, FindAuditSinkConfigurationMethods(_configurationAssemblies), loggerConfiguration.AuditTo);
CallConfigurationMethods(methodCalls, FindAuditSinkConfigurationMethods(_configurationAssemblies, _resolutionContext.ReaderOptions.AllowInternalTypes, _resolutionContext.ReaderOptions.AllowInternalMethods), loggerConfiguration.AuditTo);
}
}

void IConfigurationReader.ApplySinks(LoggerSinkConfiguration loggerSinkConfiguration)
{
var methodCalls = GetMethodCalls(_section);
CallConfigurationMethods(methodCalls, FindSinkConfigurationMethods(_configurationAssemblies), loggerSinkConfiguration);
CallConfigurationMethods(methodCalls, FindSinkConfigurationMethods(_configurationAssemblies, _resolutionContext.ReaderOptions.AllowInternalTypes, _resolutionContext.ReaderOptions.AllowInternalMethods), loggerSinkConfiguration);
}

void IConfigurationReader.ApplyEnrichment(LoggerEnrichmentConfiguration loggerEnrichmentConfiguration)
{
var methodCalls = GetMethodCalls(_section);
CallConfigurationMethods(methodCalls, FindEventEnricherConfigurationMethods(_configurationAssemblies), loggerEnrichmentConfiguration);
CallConfigurationMethods(methodCalls, FindEventEnricherConfigurationMethods(_configurationAssemblies, _resolutionContext.ReaderOptions.AllowInternalTypes, _resolutionContext.ReaderOptions.AllowInternalMethods), loggerEnrichmentConfiguration);
}

void ApplyEnrichment(LoggerConfiguration loggerConfiguration)
Expand All @@ -285,7 +285,7 @@ void ApplyEnrichment(LoggerConfiguration loggerConfiguration)
if (enrichDirective.GetChildren().Any())
{
var methodCalls = GetMethodCalls(enrichDirective);
CallConfigurationMethods(methodCalls, FindEventEnricherConfigurationMethods(_configurationAssemblies), loggerConfiguration.Enrich);
CallConfigurationMethods(methodCalls, FindEventEnricherConfigurationMethods(_configurationAssemblies, _resolutionContext.ReaderOptions.AllowInternalTypes, _resolutionContext.ReaderOptions.AllowInternalMethods), loggerConfiguration.Enrich);
}

var propertiesDirective = _section.GetSection("Properties");
Expand Down Expand Up @@ -494,51 +494,51 @@ static bool ParameterNameMatches(string actualParameterName, IEnumerable<string>
return suppliedNames.Any(s => ParameterNameMatches(actualParameterName, s));
}

static IReadOnlyCollection<MethodInfo> FindSinkConfigurationMethods(IReadOnlyCollection<Assembly> configurationAssemblies)
static IReadOnlyCollection<MethodInfo> FindSinkConfigurationMethods(IReadOnlyCollection<Assembly> configurationAssemblies, bool allowInternalTypes, bool allowInternalMethods)
{
var found = FindConfigurationExtensionMethods(configurationAssemblies, typeof(LoggerSinkConfiguration));
var found = FindConfigurationExtensionMethods(configurationAssemblies, typeof(LoggerSinkConfiguration), allowInternalTypes, allowInternalMethods);
if (configurationAssemblies.Contains(typeof(LoggerSinkConfiguration).GetTypeInfo().Assembly))
found.AddRange(SurrogateConfigurationMethods.WriteTo);

return found;
}

static IReadOnlyCollection<MethodInfo> FindAuditSinkConfigurationMethods(IReadOnlyCollection<Assembly> configurationAssemblies)
static IReadOnlyCollection<MethodInfo> FindAuditSinkConfigurationMethods(IReadOnlyCollection<Assembly> configurationAssemblies, bool allowInternalTypes, bool allowInternalMethods)
{
var found = FindConfigurationExtensionMethods(configurationAssemblies, typeof(LoggerAuditSinkConfiguration));
var found = FindConfigurationExtensionMethods(configurationAssemblies, typeof(LoggerAuditSinkConfiguration), allowInternalTypes, allowInternalMethods);
if (configurationAssemblies.Contains(typeof(LoggerAuditSinkConfiguration).GetTypeInfo().Assembly))
found.AddRange(SurrogateConfigurationMethods.AuditTo);
return found;
}

static IReadOnlyCollection<MethodInfo> FindFilterConfigurationMethods(IReadOnlyCollection<Assembly> configurationAssemblies)
static IReadOnlyCollection<MethodInfo> FindFilterConfigurationMethods(IReadOnlyCollection<Assembly> configurationAssemblies, bool allowInternalTypes, bool allowInternalMethods)
{
var found = FindConfigurationExtensionMethods(configurationAssemblies, typeof(LoggerFilterConfiguration));
var found = FindConfigurationExtensionMethods(configurationAssemblies, typeof(LoggerFilterConfiguration), allowInternalTypes, allowInternalMethods);
if (configurationAssemblies.Contains(typeof(LoggerFilterConfiguration).GetTypeInfo().Assembly))
found.AddRange(SurrogateConfigurationMethods.Filter);

return found;
}

static IReadOnlyCollection<MethodInfo> FindDestructureConfigurationMethods(IReadOnlyCollection<Assembly> configurationAssemblies)
static IReadOnlyCollection<MethodInfo> FindDestructureConfigurationMethods(IReadOnlyCollection<Assembly> configurationAssemblies, bool allowInternalTypes, bool allowInternalMethods)
{
var found = FindConfigurationExtensionMethods(configurationAssemblies, typeof(LoggerDestructuringConfiguration));
var found = FindConfigurationExtensionMethods(configurationAssemblies, typeof(LoggerDestructuringConfiguration), allowInternalTypes, allowInternalMethods);
if (configurationAssemblies.Contains(typeof(LoggerDestructuringConfiguration).GetTypeInfo().Assembly))
found.AddRange(SurrogateConfigurationMethods.Destructure);

return found;
}

static IReadOnlyCollection<MethodInfo> FindEventEnricherConfigurationMethods(IReadOnlyCollection<Assembly> configurationAssemblies)
static IReadOnlyCollection<MethodInfo> FindEventEnricherConfigurationMethods(IReadOnlyCollection<Assembly> configurationAssemblies, bool allowInternalTypes, bool allowInternalMethods)
{
var found = FindConfigurationExtensionMethods(configurationAssemblies, typeof(LoggerEnrichmentConfiguration));
var found = FindConfigurationExtensionMethods(configurationAssemblies, typeof(LoggerEnrichmentConfiguration), allowInternalTypes, allowInternalMethods);
if (configurationAssemblies.Contains(typeof(LoggerEnrichmentConfiguration).GetTypeInfo().Assembly))
found.AddRange(SurrogateConfigurationMethods.Enrich);

return found;
}

static List<MethodInfo> FindConfigurationExtensionMethods(IReadOnlyCollection<Assembly> configurationAssemblies, Type configType)
static List<MethodInfo> FindConfigurationExtensionMethods(IReadOnlyCollection<Assembly> configurationAssemblies, Type configType, bool allowInternalTypes, bool allowInternalMethods)
{
// ExtensionAttribute can be polyfilled to support extension methods
static bool HasCustomExtensionAttribute(MethodInfo m)
Expand All @@ -554,11 +554,11 @@ static bool HasCustomExtensionAttribute(MethodInfo m)
}

return configurationAssemblies
.SelectMany(a => a.ExportedTypes
.SelectMany(a => (allowInternalTypes ? a.GetTypes() : a.ExportedTypes)
.Select(t => t.GetTypeInfo())
.Where(t => t.IsSealed && t.IsAbstract && !t.IsNested))
.Where(t => t.IsSealed && t.IsAbstract && !t.IsNested && (t.IsPublic || allowInternalTypes && !t.IsVisible)))
.SelectMany(t => t.DeclaredMethods)
.Where(m => m.IsStatic && m.IsPublic && (m.IsDefined(typeof(ExtensionAttribute), false) || HasCustomExtensionAttribute(m)))
.Where(m => m.IsStatic && (m.IsPublic || allowInternalMethods && m.IsAssembly) && (m.IsDefined(typeof(ExtensionAttribute), false) || HasCustomExtensionAttribute(m)))
.Where(m => m.GetParameters()[0].ParameterType == configType)
.ToList();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,16 @@ public ConfigurationReaderOptions() : this(dependencyContext: null)
/// </summary>
public IFormatProvider? FormatProvider { get; init; } = CultureInfo.InvariantCulture;

/// <summary>
/// Allows to use internal types for extension methods for sink configuration. Defaults to <see langword="false"/>.
/// </summary>
public bool AllowInternalTypes { get; init; }

/// <summary>
/// Allows to use internal extension methods for sink configuration. Defaults to <see langword="false"/>.
/// </summary>
public bool AllowInternalMethods { get; init; }

/// <summary>
/// Called when a log level switch is created while reading the configuration.
/// Log level switches are created either from the <c>Serilog:LevelSwitches</c> section (declared switches) or the <c>Serilog:MinimumLevel:Override</c> section (minimum level override switches).
Expand Down
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe also add tests that ensure that internal methods are not found when AllowInternalTypes/AllowInternalMethods are false.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ok

Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,102 @@ public void ConfigurationAssembliesFromDllScanning()
Assert.Single(DummyConsoleSink.Emitted);
}

[Theory]
[InlineData(false)]
[InlineData(true)]
public void ConfigurationAssembliesWithInternalMethodInPublicClass(bool allowInternalMethods)
{
var json = """
{
"Serilog": {
"Using": ["TestDummies"],
"WriteTo": ["DummyConsoleInternal"]
}
}
""";

var builder = new ConfigurationBuilder().AddJsonString(json);
var config = builder.Build();
var log = new LoggerConfiguration()
.ReadFrom.Configuration(
configuration: config,
readerOptions: new ConfigurationReaderOptions(ConfigurationAssemblySource.AlwaysScanDllFiles) { AllowInternalMethods = allowInternalMethods })
.CreateLogger();

DummyConsoleSink.Emitted.Clear();

log.Write(Some.InformationEvent());

if (allowInternalMethods)
Assert.Single(DummyConsoleSink.Emitted);
else
Assert.Empty(DummyConsoleSink.Emitted);
}

[Theory]
[InlineData(false)]
[InlineData(true)]
public void ConfigurationAssembliesWithPublicMethodInInternalClass(bool allowInternalTypes)
{
var json = """
{
"Serilog": {
"Using": ["TestDummies"],
"WriteTo": ["DummyConsolePublicInInternal"]
}
}
""";

var builder = new ConfigurationBuilder().AddJsonString(json);
var config = builder.Build();
var log = new LoggerConfiguration()
.ReadFrom.Configuration(
configuration: config,
readerOptions: new ConfigurationReaderOptions(ConfigurationAssemblySource.AlwaysScanDllFiles) { AllowInternalTypes = allowInternalTypes })
.CreateLogger();

DummyConsoleSink.Emitted.Clear();

log.Write(Some.InformationEvent());

if (allowInternalTypes)
Assert.Single(DummyConsoleSink.Emitted);
else
Assert.Empty(DummyConsoleSink.Emitted);
}

[Theory]
[InlineData(false, false)]
[InlineData(true, true)]
public void ConfigurationAssembliesWithInternalMethodInInternalClass(bool allowInternalTypes, bool allowInternalMethods)
{
var json = """
{
"Serilog": {
"Using": ["TestDummies"],
"WriteTo": ["DummyConsoleInternalInInternal"]
}
}
""";

var builder = new ConfigurationBuilder().AddJsonString(json);
var config = builder.Build();
var log = new LoggerConfiguration()
.ReadFrom.Configuration(
configuration: config,
readerOptions: new ConfigurationReaderOptions(ConfigurationAssemblySource.AlwaysScanDllFiles) { AllowInternalTypes = allowInternalTypes, AllowInternalMethods = allowInternalMethods })
.CreateLogger();

DummyConsoleSink.Emitted.Clear();

log.Write(Some.InformationEvent());

if (allowInternalTypes && allowInternalMethods)
Assert.Single(DummyConsoleSink.Emitted);
else
Assert.Empty(DummyConsoleSink.Emitted);
}

[Fact]
public void SinksAreConfigured()
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,8 @@ namespace Serilog.Settings.Configuration
public ConfigurationReaderOptions(Microsoft.Extensions.DependencyModel.DependencyContext? dependencyContext) { }
public ConfigurationReaderOptions(Serilog.Settings.Configuration.ConfigurationAssemblySource configurationAssemblySource) { }
public ConfigurationReaderOptions(params System.Reflection.Assembly[] assemblies) { }
public bool AllowInternalMethods { get; init; }
public bool AllowInternalTypes { get; init; }
public System.IFormatProvider? FormatProvider { get; init; }
public System.Action<string, Serilog.Core.LoggingLevelSwitch>? OnLevelSwitchCreated { get; init; }
public string? SectionName { get; init; }
Expand Down
30 changes: 30 additions & 0 deletions test/TestDummies/DummyLoggerConfigurationExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,15 @@ public static LoggerConfiguration DummyConsole(
return loggerSinkConfiguration.Sink(new DummyConsoleSink(theme), restrictedToMinimumLevel, levelSwitch);
}

internal static LoggerConfiguration DummyConsoleInternal(
this LoggerSinkConfiguration loggerSinkConfiguration,
LogEventLevel restrictedToMinimumLevel = LevelAlias.Minimum,
LoggingLevelSwitch? levelSwitch = null,
ConsoleTheme? theme = null)
{
return loggerSinkConfiguration.Sink(new DummyConsoleSink(theme), restrictedToMinimumLevel, levelSwitch);
}

public static LoggerConfiguration Dummy(
this LoggerSinkConfiguration loggerSinkConfiguration,
Action<LoggerSinkConfiguration> wrappedSinkAction)
Expand Down Expand Up @@ -170,3 +179,24 @@ public static LoggerConfiguration DummyNumbers(this LoggerDestructuringConfigura
});
}
}

internal static class DummyLoggerConfigurationExtensionsInternal
{
public static LoggerConfiguration DummyConsolePublicInInternal(
this LoggerSinkConfiguration loggerSinkConfiguration,
LogEventLevel restrictedToMinimumLevel = LevelAlias.Minimum,
LoggingLevelSwitch? levelSwitch = null,
ConsoleTheme? theme = null)
{
return loggerSinkConfiguration.Sink(new DummyConsoleSink(theme), restrictedToMinimumLevel, levelSwitch);
}

internal static LoggerConfiguration DummyConsoleInternalInInternal(
this LoggerSinkConfiguration loggerSinkConfiguration,
LogEventLevel restrictedToMinimumLevel = LevelAlias.Minimum,
LoggingLevelSwitch? levelSwitch = null,
ConsoleTheme? theme = null)
{
return loggerSinkConfiguration.Sink(new DummyConsoleSink(theme), restrictedToMinimumLevel, levelSwitch);
}
}