Skip to content

Commit 51770bd

Browse files
authored
Merge branch 'dev' into serilog-4x
2 parents adf275f + 1e9f655 commit 51770bd

File tree

5 files changed

+78
-45
lines changed

5 files changed

+78
-45
lines changed

README.md

Lines changed: 39 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -28,41 +28,41 @@ using Serilog;
2828

2929
public class Startup
3030
{
31-
public Startup(IHostingEnvironment env)
32-
{
33-
Log.Logger = new LoggerConfiguration()
34-
.Enrich.FromLogContext()
35-
.WriteTo.Console()
36-
.CreateLogger();
37-
38-
// Other startup code
31+
public Startup(IHostingEnvironment env)
32+
{
33+
Log.Logger = new LoggerConfiguration()
34+
.Enrich.FromLogContext()
35+
.WriteTo.Console()
36+
.CreateLogger();
37+
38+
// Other startup code
3939
```
4040

4141
**Finally, for .NET Core 2.0+**, in your `Startup` class's `Configure()` method, remove the existing logger configuration entries and
4242
call `AddSerilog()` on the provided `loggingBuilder`.
4343

4444
```csharp
45-
public void ConfigureServices(IServiceCollection services)
46-
{
47-
services.AddLogging(loggingBuilder =>
48-
loggingBuilder.AddSerilog(dispose: true));
45+
public void ConfigureServices(IServiceCollection services)
46+
{
47+
services.AddLogging(loggingBuilder =>
48+
loggingBuilder.AddSerilog(dispose: true));
4949

50-
// Other services ...
51-
}
50+
// Other services ...
51+
}
5252
```
5353

5454
**For .NET Core 1.0 or 1.1**, in your `Startup` class's `Configure()` method, remove the existing logger configuration entries and call `AddSerilog()` on the provided `loggerFactory`.
5555

5656
```
57-
public void Configure(IApplicationBuilder app,
58-
IHostingEnvironment env,
59-
ILoggerFactory loggerfactory,
60-
IApplicationLifetime appLifetime)
61-
{
62-
loggerfactory.AddSerilog();
63-
64-
// Ensure any buffered events are sent at shutdown
65-
appLifetime.ApplicationStopped.Register(Log.CloseAndFlush);
57+
public void Configure(IApplicationBuilder app,
58+
IHostingEnvironment env,
59+
ILoggerFactory loggerfactory,
60+
IApplicationLifetime appLifetime)
61+
{
62+
loggerfactory.AddSerilog();
63+
64+
// Ensure any buffered events are sent at shutdown
65+
appLifetime.ApplicationStopped.Register(Log.CloseAndFlush);
6666
```
6767

6868
That's it! With the level bumped up a little you should see log output like:
@@ -87,10 +87,10 @@ _Serilog.Extensions.Logging_ captures the `ILogger`'s log category, but it's not
8787
8888
To include the log category in the final written messages, add the `{SourceContext}` named hole to a customised `outputTemplate` parameter value when configuring the relevant sink(s). For example:
8989
```csharp
90-
.WriteTo.Console(
91-
outputTemplate: "[{Timestamp:HH:mm:ss} {Level:u3}] {SourceContext}: {Message:lj}{NewLine}{Exception}")
92-
.WriteTo.File("log.txt",
93-
outputTemplate: "{Timestamp:yyyy-MM-dd HH:mm:ss.fff zzz} [{Level:u3}] {SourceContext}: {Message:lj}{NewLine}{Exception}")
90+
.WriteTo.Console(
91+
outputTemplate: "[{Timestamp:HH:mm:ss} {Level:u3}] {SourceContext}: {Message:lj}{NewLine}{Exception}")
92+
.WriteTo.File("log.txt",
93+
outputTemplate: "{Timestamp:yyyy-MM-dd HH:mm:ss.fff zzz} [{Level:u3}] {SourceContext}: {Message:lj}{NewLine}{Exception}")
9494
```
9595

9696
### Notes on Log Scopes
@@ -103,7 +103,8 @@ _Microsoft.Extensions.Logging_ provides the `BeginScope` API, which can be used
103103
Using the extension method will add a `Scope` property to your log events. This is most useful for adding simple "scope strings" to your events, as in the following code:
104104

105105
```csharp
106-
using (_logger.BeginScope("Transaction")) {
106+
using (_logger.BeginScope("Transaction"))
107+
{
107108
_logger.LogInformation("Beginning...");
108109
_logger.LogInformation("Completed in {DurationMs}ms...", 30);
109110
}
@@ -116,7 +117,8 @@ If you simply want to add a "bag" of additional properties to your log events, h
116117

117118
```csharp
118119
// WRONG! Prefer the dictionary or value tuple approach below instead
119-
using (_logger.BeginScope("TransactionId: {TransactionId}, ResponseJson: {ResponseJson}", 12345, jsonString)) {
120+
using (_logger.BeginScope("TransactionId: {TransactionId}, ResponseJson: {ResponseJson}", 12345, jsonString))
121+
{
120122
_logger.LogInformation("Completed in {DurationMs}ms...", 30);
121123
}
122124
// Example JSON output:
@@ -138,11 +140,13 @@ Moreover, the template string within `BeginScope` is rather arbitrary when all y
138140
A far better alternative is to use the `BeginScope<TState>(TState state)` method. If you provide any `IEnumerable<KeyValuePair<string, object>>` to this method, then Serilog will output the key/value pairs as structured properties _without_ the `Scope` property, as in this example:
139141

140142
```csharp
141-
var scopeProps = new Dictionary<string, object> {
143+
var scopeProps = new Dictionary<string, object>
144+
{
142145
{ "TransactionId", 12345 },
143146
{ "ResponseJson", jsonString },
144147
};
145-
using (_logger.BeginScope(scopeProps) {
148+
using (_logger.BeginScope(scopeProps)
149+
{
146150
_logger.LogInformation("Transaction completed in {DurationMs}ms...", 30);
147151
}
148152
// Example JSON output:
@@ -157,10 +161,12 @@ using (_logger.BeginScope(scopeProps) {
157161
// }
158162
```
159163

160-
Alternatively provide a `ValueTuple<string, object?>` to this method, where `Item1` is the property name and `Item2` is the property value. Note that `T2` _must_ be `object?`.
164+
Alternatively provide a `ValueTuple<string, object?>` to this method, where `Item1` is the property name and `Item2` is the property value.
165+
Note that `T2` _must_ be `object?` if your target platform is net462 or netstandard2.0.
161166

162167
```csharp
163-
using (_logger.BeginScope(("TransactionId", (object?)12345)) {
168+
using (_logger.BeginScope(("TransactionId", 12345))
169+
{
164170
_logger.LogInformation("Transaction completed in {DurationMs}ms...", 30);
165171
}
166172
// Example JSON output:

src/Serilog.Extensions.Logging/Extensions/Logging/SerilogLoggerProvider.cs

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -78,9 +78,7 @@ public void Enrich(LogEvent logEvent, ILogEventPropertyFactory propertyFactory)
7878

7979
_externalScopeProvider?.ForEachScope((state, accumulatingLogEvent) =>
8080
{
81-
var scope = new SerilogLoggerScope(this, state);
82-
83-
scope.EnrichAndCreateScopeItem(accumulatingLogEvent, propertyFactory, out var scopeItem);
81+
SerilogLoggerScope.EnrichWithStateAndCreateScopeItem(accumulatingLogEvent, propertyFactory, state, out var scopeItem);
8482

8583
if (scopeItem != null)
8684
{

src/Serilog.Extensions.Logging/Extensions/Logging/SerilogLoggerScope.cs

Lines changed: 19 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -47,47 +47,58 @@ public void Dispose()
4747
}
4848
}
4949

50-
public void EnrichAndCreateScopeItem(LogEvent logEvent, ILogEventPropertyFactory propertyFactory, out LogEventPropertyValue? scopeItem)
50+
public void EnrichAndCreateScopeItem(LogEvent logEvent, ILogEventPropertyFactory propertyFactory, out LogEventPropertyValue? scopeItem) => EnrichWithStateAndCreateScopeItem(logEvent, propertyFactory, _state, out scopeItem);
51+
52+
public static void EnrichWithStateAndCreateScopeItem(LogEvent logEvent, ILogEventPropertyFactory propertyFactory, object? state, out LogEventPropertyValue? scopeItem)
5153
{
52-
if (_state == null)
54+
if (state == null)
5355
{
5456
scopeItem = null;
5557
return;
5658
}
5759

58-
if (_state is Dictionary<string, object> dictionary)
60+
// Eliminates boxing of Dictionary<TKey, TValue>.Enumerator for the most common use case
61+
if (state is Dictionary<string, object> dictionary)
5962
{
6063
// Separate handling of this case eliminates boxing of Dictionary<TKey, TValue>.Enumerator.
6164
scopeItem = null;
6265
foreach (var stateProperty in dictionary)
63-
{
66+
{
6467
AddProperty(logEvent, propertyFactory, stateProperty.Key, stateProperty.Value);
6568
}
6669
}
67-
else if (_state is IEnumerable<KeyValuePair<string, object>> stateProperties)
70+
else if (state is IEnumerable<KeyValuePair<string, object>> stateProperties)
6871
{
6972
scopeItem = null;
7073
foreach (var stateProperty in stateProperties)
7174
{
7275
if (stateProperty is { Key: SerilogLoggerProvider.OriginalFormatPropertyName, Value: string })
7376
{
7477
// `_state` is most likely `FormattedLogValues` (a MEL internal type).
75-
scopeItem = new ScalarValue(_state.ToString());
78+
scopeItem = new ScalarValue(state.ToString());
7679
}
7780
else
7881
{
7982
AddProperty(logEvent, propertyFactory, stateProperty.Key, stateProperty.Value);
8083
}
8184
}
8285
}
83-
else if (_state is ValueTuple<string, object?> tuple)
86+
#if FEATURE_ITUPLE
87+
else if (state is System.Runtime.CompilerServices.ITuple tuple && tuple.Length == 2 && tuple[0] is string s)
88+
{
89+
scopeItem = null; // Unless it's `FormattedLogValues`, these are treated as property bags rather than scope items.
90+
AddProperty(logEvent, propertyFactory, s, tuple[1]);
91+
}
92+
#else
93+
else if (state is ValueTuple<string, object?> tuple)
8494
{
8595
scopeItem = null;
8696
AddProperty(logEvent, propertyFactory, tuple.Item1, tuple.Item2);
8797
}
98+
#endif
8899
else
89100
{
90-
scopeItem = propertyFactory.CreateProperty(NoName, _state).Value;
101+
scopeItem = propertyFactory.CreateProperty(NoName, state).Value;
91102
}
92103
}
93104

src/Serilog.Extensions.Logging/Serilog.Extensions.Logging.csproj

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,4 +35,19 @@
3535
<PackageReference Include="Nullable" Version="1.3.1" PrivateAssets="All" />
3636
</ItemGroup>
3737

38+
<PropertyGroup Condition=" '$(TargetFramework)' == 'netstandard2.1' ">
39+
<DefineConstants>$(DefineConstants);FEATURE_ITUPLE</DefineConstants>
40+
</PropertyGroup>
41+
42+
<PropertyGroup Condition=" '$(TargetFramework)' == 'net6.0' ">
43+
<DefineConstants>$(DefineConstants);FEATURE_ITUPLE</DefineConstants>
44+
</PropertyGroup>
45+
46+
<PropertyGroup Condition=" '$(TargetFramework)' == 'net7.0' ">
47+
<DefineConstants>$(DefineConstants);FEATURE_ITUPLE</DefineConstants>
48+
</PropertyGroup>
49+
50+
<PropertyGroup Condition=" '$(TargetFramework)' == 'net8.0' ">
51+
<DefineConstants>$(DefineConstants);FEATURE_ITUPLE</DefineConstants>
52+
</PropertyGroup>
3853
</Project>

test/Serilog.Extensions.Logging.Tests/SerilogLoggerScopeTests.cs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -81,8 +81,11 @@ public void EnrichWithTupleStringObject()
8181

8282
var (loggerProvider, logEventPropertyFactory, logEvent) = SetUp();
8383

84-
84+
#if NET48
8585
var state = (propertyName, (object)expectedValue);
86+
#else
87+
var state = (propertyName, expectedValue);
88+
#endif
8689

8790
var loggerScope = new SerilogLoggerScope(loggerProvider, state);
8891

0 commit comments

Comments
 (0)