Skip to content

Commit d8d162c

Browse files
committed
Fix auto-discovery of configuration assemblies in bundled mode
By reading the deps json directly from the application host the with the help of the`Microsoft.NET.HostModel` package.
1 parent cad35a0 commit d8d162c

File tree

6 files changed

+54
-107
lines changed

6 files changed

+54
-107
lines changed

README.md

Lines changed: 0 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -115,21 +115,6 @@ For legacy .NET Framework projects it also scans default probing path(s).
115115

116116
For all other cases, as well as in the case of non-conventional configuration assembly names **DO** use [Using](#using-section-and-auto-discovery-of-configuration-assemblies) section.
117117

118-
#### .NET Single File Applications
119-
120-
Currently, auto-discovery of configuration assemblies requires special care when the application is deployed in [bundled mode](https://docs.microsoft.com/en-us/dotnet/core/deploying/single-file/overview).
121-
122-
Starting with version 3.4.0, the configuration assemblies must be explicitly loaded prior to calling `loggerConfiguration.ReadFrom.Configuration(…)`.
123-
124-
```csharp
125-
// Force loading of assemblies that might be used in the configuration (required when published as single file)
126-
_ = typeof(ConsoleLoggerConfigurationExtensions).Assembly;
127-
_ = typeof(SeqLoggerConfigurationExtensions).Assembly;
128-
loggerConfiguration.ReadFrom.Configuration(context.Configuration);
129-
```
130-
131-
For versions older than 3.4.0, the [Using](#using-section-and-auto-discovery-of-configuration-assemblies) section must be used to load configuration assemblies as a workaround.
132-
133118
### MinimumLevel, LevelSwitches, overrides and dynamic reload
134119

135120
The `MinimumLevel` configuration property can be set to a single value as in the sample above, or, levels can be overridden per logging source.

src/Serilog.Settings.Configuration/Serilog.Settings.Configuration.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,5 +41,6 @@
4141

4242
<ItemGroup Condition="('$(TargetFramework)' == 'netstandard2.0') Or ('$(TargetFramework)' == 'net461')">
4343
<PackageReference Include="Microsoft.Extensions.Options.ConfigurationExtensions" Version="2.0.0" />
44+
<PackageReference Include="Microsoft.NET.HostModel" Version="3.1.16" />
4445
</ItemGroup>
4546
</Project>

src/Serilog.Settings.Configuration/Settings/Configuration/Assemblies/AppDomainAssemblyFinder.cs

Lines changed: 0 additions & 20 deletions
This file was deleted.

src/Serilog.Settings.Configuration/Settings/Configuration/Assemblies/AssemblyFinder.cs

Lines changed: 53 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,14 @@
33
using System.Reflection;
44
using Microsoft.Extensions.DependencyModel;
55

6+
#if !NET451
7+
using System.Diagnostics;
8+
using System.IO;
9+
using System.IO.MemoryMappedFiles;
10+
using System.Text;
11+
using Microsoft.NET.HostModel.AppHost;
12+
#endif
13+
614
namespace Serilog.Settings.Configuration.Assemblies
715
{
816
abstract class AssemblyFinder
@@ -16,20 +24,8 @@ protected static bool IsCaseInsensitiveMatch(string text, string textToFind)
1624

1725
public static AssemblyFinder Auto()
1826
{
19-
try
20-
{
21-
// Need to check `Assembly.GetEntryAssembly()` first because
22-
// `DependencyContext.Default` throws an exception when `Assembly.GetEntryAssembly()` returns null
23-
if (Assembly.GetEntryAssembly() != null && DependencyContext.Default != null)
24-
{
25-
return new CompositeAssemblyFinder(new AppDomainAssemblyFinder(), new DependencyContextAssemblyFinder(DependencyContext.Default));
26-
}
27-
}
28-
catch (NotSupportedException) when (typeof(object).Assembly.Location is "") // bundled mode detection
29-
{
30-
}
31-
32-
return new DllScanningAssemblyFinder();
27+
var dependencyContext = GetDependencyContext();
28+
return dependencyContext != null ? new DependencyContextAssemblyFinder(dependencyContext) : new DllScanningAssemblyFinder();
3329
}
3430

3531
public static AssemblyFinder ForSource(ConfigurationAssemblySource configurationAssemblySource)
@@ -46,5 +42,48 @@ public static AssemblyFinder ForDependencyContext(DependencyContext dependencyCo
4642
{
4743
return new DependencyContextAssemblyFinder(dependencyContext);
4844
}
45+
46+
static DependencyContext GetDependencyContext()
47+
{
48+
var isBundled = string.IsNullOrEmpty(Assembly.GetEntryAssembly()?.Location);
49+
if (!isBundled)
50+
{
51+
return DependencyContext.Default;
52+
}
53+
54+
#if !NET451
55+
try
56+
{
57+
var currentProcessPath = Process.GetCurrentProcess().MainModule?.FileName;
58+
if (currentProcessPath != null && HostWriter.IsBundle(currentProcessPath, out var bundleHeaderOffset))
59+
{
60+
using var mappedFile = MemoryMappedFile.CreateFromFile(currentProcessPath, FileMode.Open);
61+
using var currentProcessStream = mappedFile.CreateViewStream(0, new FileInfo(currentProcessPath).Length, MemoryMappedFileAccess.Read);
62+
using var reader = new BinaryReader(currentProcessStream, Encoding.UTF8);
63+
// See https://github.com/dotnet/runtime/blob/v6.0.3/src/installer/managed/Microsoft.NET.HostModel/Bundle/Manifest.cs#L32-L39
64+
// and https://github.com/dotnet/runtime/blob/v6.0.3/src/installer/managed/Microsoft.NET.HostModel/Bundle/Manifest.cs#L144-L155
65+
reader.BaseStream.Position = bundleHeaderOffset;
66+
var majorVersion = reader.ReadUInt32();
67+
_ = reader.ReadUInt32(); // minorVersion
68+
_ = reader.ReadInt32(); // numEmbeddedFiles
69+
_ = reader.ReadString(); // bundleId
70+
if (majorVersion >= 2)
71+
{
72+
var depsJsonOffset = reader.ReadInt64();
73+
var depsJsonSize = reader.ReadInt64();
74+
using var depsJsonStream = mappedFile.CreateViewStream(depsJsonOffset, depsJsonSize, MemoryMappedFileAccess.Read);
75+
using var depsJsonReader = new DependencyContextJsonReader();
76+
return depsJsonReader.Read(depsJsonStream);
77+
}
78+
}
79+
}
80+
catch
81+
{
82+
return null;
83+
}
84+
#endif
85+
86+
return null;
87+
}
4988
}
5089
}

src/Serilog.Settings.Configuration/Settings/Configuration/Assemblies/CompositeAssemblyFinder.cs

Lines changed: 0 additions & 51 deletions
This file was deleted.

test/TestSingleFileApp/Program.cs

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,6 @@
66

77
try
88
{
9-
// Force loading of assemblies could become unnecessary if the [DependencyContextLoader][1]
10-
// starts supporting applications published as single-file in the future.
11-
// Unfortunately, as of .NET 6, loading the DependencyContext from a single-file application is not supported.
12-
// [1]: https://github.com/dotnet/runtime/blob/v6.0.3/src/libraries/Microsoft.Extensions.DependencyModel/src/DependencyContextLoader.cs#L54-L55
13-
_ = typeof(Serilog.Sinks.InMemory.InMemorySinkExtensions).Assembly;
14-
_ = typeof(ConsoleLoggerConfigurationExtensions).Assembly;
15-
169
SelfLog.Enable(text => Console.Error.WriteLine(text));
1710

1811
var configuration = new ConfigurationBuilder().AddInMemoryCollection(new Dictionary<string, string>

0 commit comments

Comments
 (0)