Skip to content

Commit

Permalink
Make sure that single-file apps can find assemblies that contains sinks
Browse files Browse the repository at this point in the history
Before this commit, when single-file app was detected, the behaviour was to fallback on DLL scanning. But DLL scanning would not find anything for an app published as a single-file, by sheer definition of single-file app!

After this commit, an exception is thrown if the app is published as a single-file AND no `Serilog:Using` section is defined in the configuration. The error message explains that either a `Serilog:Using` section must be added or show how to explicitly configure assemblies through the `ConfigurationReaderOptions`.
  • Loading branch information
0xced committed Mar 10, 2023
1 parent 0fc8b57 commit 561e41e
Show file tree
Hide file tree
Showing 4 changed files with 22 additions and 38 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -92,14 +92,10 @@ public static LoggerConfiguration ConfigurationSection(
if (settingConfiguration == null) throw new ArgumentNullException(nameof(settingConfiguration));
if (configSection == null) throw new ArgumentNullException(nameof(configSection));

var assemblyFinder = dependencyContext == null
? AssemblyFinder.Auto()
: AssemblyFinder.ForDependencyContext(dependencyContext);

return settingConfiguration.Settings(
new ConfigurationReader(
configSection,
assemblyFinder,
AssemblyFinder.ForDependencyContext(dependencyContext),
configuration: null,
formatProvider: null));
}
Expand Down Expand Up @@ -218,23 +214,15 @@ public static LoggerConfiguration Configuration(
{
var configurationReader = readerOptions switch
{
{ ConfigurationAssemblySource: {} } => GetConfigurationReader(configuration, readerOptions, readerOptions.ConfigurationAssemblySource.Value),
{ ConfigurationAssemblySource: {} } => GetConfigurationReader(configuration, readerOptions, AssemblyFinder.ForSource(readerOptions.ConfigurationAssemblySource.Value)),
{ Assemblies: {} } => GetConfigurationReader(configuration, readerOptions, readerOptions.Assemblies),
_ => GetConfigurationReader(configuration, readerOptions ?? new ConfigurationReaderOptions(), readerOptions?.DependencyContext),
_ => GetConfigurationReader(configuration, readerOptions ?? new ConfigurationReaderOptions(), AssemblyFinder.ForDependencyContext(readerOptions?.DependencyContext)),
};
return settingConfiguration.Settings(configurationReader);
}

static ConfigurationReader GetConfigurationReader(IConfiguration configuration, ConfigurationReaderOptions readerOptions, DependencyContext dependencyContext)
{
var assemblyFinder = dependencyContext == null ? AssemblyFinder.Auto() : AssemblyFinder.ForDependencyContext(dependencyContext);
var section = configuration.GetSection(readerOptions.SectionName);
return new ConfigurationReader(section, assemblyFinder, readerOptions.FormatProvider, configuration);
}

static ConfigurationReader GetConfigurationReader(IConfiguration configuration, ConfigurationReaderOptions readerOptions, ConfigurationAssemblySource source)
static ConfigurationReader GetConfigurationReader(IConfiguration configuration, ConfigurationReaderOptions readerOptions, AssemblyFinder assemblyFinder)
{
var assemblyFinder = AssemblyFinder.ForSource(source);
var section = configuration.GetSection(readerOptions.SectionName);
return new ConfigurationReader(section, assemblyFinder, readerOptions.FormatProvider, configuration);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,43 +5,26 @@ namespace Serilog.Settings.Configuration.Assemblies;

abstract class AssemblyFinder
{
public virtual bool CanFindAssemblies => true;
public abstract IReadOnlyList<AssemblyName> FindAssembliesContainingName(string nameToFind);

protected static bool IsCaseInsensitiveMatch(string text, string textToFind)
{
return text != null && text.ToLowerInvariant().Contains(textToFind.ToLowerInvariant());
}

public static AssemblyFinder Auto()
{
try
{
// Need to check `Assembly.GetEntryAssembly()` first because
// `DependencyContext.Default` throws an exception when `Assembly.GetEntryAssembly()` returns null
if (Assembly.GetEntryAssembly() != null && DependencyContext.Default != null)
{
return new DependencyContextAssemblyFinder(DependencyContext.Default);
}
}
catch (NotSupportedException) when (typeof(object).Assembly.Location is "") // bundled mode detection
{
}

return new DllScanningAssemblyFinder();
}

public static AssemblyFinder ForSource(ConfigurationAssemblySource configurationAssemblySource)
{
return configurationAssemblySource switch
{
ConfigurationAssemblySource.UseLoadedAssemblies => Auto(),
ConfigurationAssemblySource.UseLoadedAssemblies => new DependencyContextAssemblyFinder(DependencyContext.Default),
ConfigurationAssemblySource.AlwaysScanDllFiles => new DllScanningAssemblyFinder(),
_ => throw new ArgumentOutOfRangeException(nameof(configurationAssemblySource), configurationAssemblySource, null),
};
}

public static AssemblyFinder ForDependencyContext(DependencyContext dependencyContext)
{
return new DependencyContextAssemblyFinder(dependencyContext);
return new DependencyContextAssemblyFinder(dependencyContext ?? DependencyContext.Default);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,16 @@ sealed class DependencyContextAssemblyFinder : AssemblyFinder

public DependencyContextAssemblyFinder(DependencyContext dependencyContext)
{
_dependencyContext = dependencyContext ?? throw new ArgumentNullException(nameof(dependencyContext));
_dependencyContext = dependencyContext;
}

public override bool CanFindAssemblies => _dependencyContext != null;

public override IReadOnlyList<AssemblyName> FindAssembliesContainingName(string nameToFind)
{
if (_dependencyContext == null)
return Array.Empty<AssemblyName>();

var query = from library in _dependencyContext.RuntimeLibraries
where IsReferencingSerilog(library)
from assemblyName in library.GetDefaultAssemblyNames(_dependencyContext)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -363,13 +363,21 @@ static IReadOnlyCollection<Assembly> LoadConfigurationAssemblies(IConfigurationS
{
if (string.IsNullOrWhiteSpace(simpleName))
throw new InvalidOperationException(
"A zero-length or whitespace assembly name was supplied to a Serilog.Using configuration statement.");
$"A zero-length or whitespace assembly name was supplied to a {usingSection.Path} configuration statement.");

var assembly = Assembly.Load(new AssemblyName(simpleName));
if (!assemblies.ContainsKey(assembly.FullName))
assemblies.Add(assembly.FullName, assembly);
}
}
else if (!assemblyFinder.CanFindAssemblies)
{
var message = $"The application is published as single-file and no {usingSection.Path} configuration section is defined.{Environment.NewLine}" +
$"Either add a {usingSection.Path} section or explicitly specify assemblies that contains sinks and other types through the reader options. For example:{Environment.NewLine}" +
$"var options = new ConfigurationReaderOptions(typeof(ConsoleLoggerConfigurationExtensions).Assembly, typeof(SerilogExpression).Assembly);{Environment.NewLine}" +
"new LoggerConfiguration().ReadFrom.Configuration(configuration, options);";
throw new InvalidOperationException(message);
}

foreach (var assemblyName in assemblyFinder.FindAssembliesContainingName("serilog"))
{
Expand Down

0 comments on commit 561e41e

Please sign in to comment.