Skip to content
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

8.0.1 Release #256

Draft
wants to merge 25 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
322c5f2
SerilogLoggerScope add support for ValueTuple<string, object?> state.…
jimbojim1997 Nov 8, 2023
d8637c2
Update readme with exaple of BeginScope with a ValueTuple.
jimbojim1997 Nov 12, 2023
85172ad
Changed copyright header from .NET Foundation to Serilog Contributors.
jimbojim1997 Nov 14, 2023
14c2965
Update Serilog.Extensions.Logging.csproj
nblumhardt Nov 14, 2023
7a2fa1d
Minor optimisations
jods4 Feb 12, 2024
76a05ee
Revert String.StartsWith, not present in old .net fx
jods4 Feb 12, 2024
8d20ade
Merge pull request #242 from jods4/patch-1
nblumhardt Feb 14, 2024
099a66a
Document {SourceContext} for MEL log category
DavidHopkinsFbr Feb 21, 2024
7dc58c3
Merge pull request #244 from DavidHopkinsFbr/mention-sourcecontext-in…
nblumhardt Feb 22, 2024
3e5c53c
Add experiement project demonstrating that tags and baggage do not ap…
Mar 13, 2024
2395852
Implement ISupportExternalScope in SerilogLoggerProvider
Mar 13, 2024
e5d72ac
Add test of external scope provider support
Mar 13, 2024
fe4a9b9
Rename proof of concept sample app
Mar 13, 2024
65eabbf
Use all ActivityTrackingOptions in sample app
Mar 13, 2024
49fa3e9
Rename sample to SampleWithExternalScope
Mar 14, 2024
3b56cb4
Merge pull request #246 from david-obee/support-external-scopes
nblumhardt Mar 14, 2024
1e1479e
Merge pull request #232 from jimbojim1997/beginscope-valuetuple
nblumhardt Mar 14, 2024
6fd2e72
Publishing key update
nblumhardt Mar 14, 2024
504fc5c
Support for ITuple
sungam3r Mar 25, 2024
3c32997
Merge pull request #249 from sungam3r/tuple
nblumhardt Apr 2, 2024
849cb02
Make the enrichment from scope state static
pavel-faltynek May 22, 2024
8bcc366
For external scope provider use enrichment with no side effects
pavel-faltynek May 22, 2024
1e9f655
Merge pull request #252 from pavel-faltynek/remove-logger-scope-side-…
nblumhardt May 22, 2024
3dd50b2
Optimization: used StartsWith(char) instead of StartsWith(string) whe…
epeshk Jul 17, 2024
7fc50b0
Merge pull request #255 from epeshk/startswith-char
nblumhardt Jul 23, 2024
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
92 changes: 64 additions & 28 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -28,41 +28,41 @@ using Serilog;

public class Startup
{
public Startup(IHostingEnvironment env)
{
Log.Logger = new LoggerConfiguration()
.Enrich.FromLogContext()
.WriteTo.Console()
.CreateLogger();

// Other startup code
public Startup(IHostingEnvironment env)
{
Log.Logger = new LoggerConfiguration()
.Enrich.FromLogContext()
.WriteTo.Console()
.CreateLogger();

// Other startup code
```

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

```csharp
public void ConfigureServices(IServiceCollection services)
{
services.AddLogging(loggingBuilder =>
loggingBuilder.AddSerilog(dispose: true));
public void ConfigureServices(IServiceCollection services)
{
services.AddLogging(loggingBuilder =>
loggingBuilder.AddSerilog(dispose: true));

// Other services ...
}
// Other services ...
}
```

**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`.

```
public void Configure(IApplicationBuilder app,
IHostingEnvironment env,
ILoggerFactory loggerfactory,
IApplicationLifetime appLifetime)
{
loggerfactory.AddSerilog();

// Ensure any buffered events are sent at shutdown
appLifetime.ApplicationStopped.Register(Log.CloseAndFlush);
public void Configure(IApplicationBuilder app,
IHostingEnvironment env,
ILoggerFactory loggerfactory,
IApplicationLifetime appLifetime)
{
loggerfactory.AddSerilog();

// Ensure any buffered events are sent at shutdown
appLifetime.ApplicationStopped.Register(Log.CloseAndFlush);
```

That's it! With the level bumped up a little you should see log output like:
Expand All @@ -80,6 +80,19 @@ That's it! With the level bumped up a little you should see log output like:
[22:14:45.741 DBG] Handled. Status code: 304 File: /css/site.css
```

### Including the log category in text-format sink output
All _Microsoft.Extensions.Logging.ILogger_ implementations are created with a specified [_log category_](https://learn.microsoft.com/en-us/dotnet/core/extensions/logging?tabs=command-line#log-category) string, which is then attached as structured data to each log message created by that `ILogger` instance. Typically, the log category is the fully-qualified name of the class generating the log messages. This convention is implemented by the [`ILogger<TCategoryName>`](https://learn.microsoft.com/en-us/dotnet/api/microsoft.extensions.logging.ilogger-1) interface, which is commonly used as an injected dependency in frameworks that use _Microsoft.Extensions.Logging_.

_Serilog.Extensions.Logging_ captures the `ILogger`'s log category, but it's not included in the default output templates for text-based sinks, such as [Console](https://github.com/serilog/serilog-sinks-console), [File](https://github.com/serilog/serilog-sinks-file) and [Debug](https://github.com/serilog/serilog-sinks-debug).

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:
```csharp
.WriteTo.Console(
outputTemplate: "[{Timestamp:HH:mm:ss} {Level:u3}] {SourceContext}: {Message:lj}{NewLine}{Exception}")
.WriteTo.File("log.txt",
outputTemplate: "{Timestamp:yyyy-MM-dd HH:mm:ss.fff zzz} [{Level:u3}] {SourceContext}: {Message:lj}{NewLine}{Exception}")
```

### Notes on Log Scopes

_Microsoft.Extensions.Logging_ provides the `BeginScope` API, which can be used to add arbitrary properties to log events within a certain region of code. The API comes in two forms:
Expand All @@ -90,7 +103,8 @@ _Microsoft.Extensions.Logging_ provides the `BeginScope` API, which can be used
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:

```csharp
using (_logger.BeginScope("Transaction")) {
using (_logger.BeginScope("Transaction"))
{
_logger.LogInformation("Beginning...");
_logger.LogInformation("Completed in {DurationMs}ms...", 30);
}
Expand All @@ -102,8 +116,9 @@ using (_logger.BeginScope("Transaction")) {
If you simply want to add a "bag" of additional properties to your log events, however, this extension method approach can be overly verbose. For example, to add `TransactionId` and `ResponseJson` properties to your log events, you would have to do something like the following:

```csharp
// WRONG! Prefer the dictionary approach below instead
using (_logger.BeginScope("TransactionId: {TransactionId}, ResponseJson: {ResponseJson}", 12345, jsonString)) {
// WRONG! Prefer the dictionary or value tuple approach below instead
using (_logger.BeginScope("TransactionId: {TransactionId}, ResponseJson: {ResponseJson}", 12345, jsonString))
{
_logger.LogInformation("Completed in {DurationMs}ms...", 30);
}
// Example JSON output:
Expand All @@ -125,11 +140,13 @@ Moreover, the template string within `BeginScope` is rather arbitrary when all y
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:

```csharp
var scopeProps = new Dictionary<string, object> {
var scopeProps = new Dictionary<string, object>
{
{ "TransactionId", 12345 },
{ "ResponseJson", jsonString },
};
using (_logger.BeginScope(scopeProps) {
using (_logger.BeginScope(scopeProps)
{
_logger.LogInformation("Transaction completed in {DurationMs}ms...", 30);
}
// Example JSON output:
Expand All @@ -144,6 +161,25 @@ using (_logger.BeginScope(scopeProps) {
// }
```

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?` if your target platform is net462 or netstandard2.0.

```csharp
using (_logger.BeginScope(("TransactionId", 12345))
{
_logger.LogInformation("Transaction completed in {DurationMs}ms...", 30);
}
// Example JSON output:
// {
// "@t":"2020-10-29T19:05:56.4176816Z",
// "@m":"Completed in 30ms...",
// "@i":"51812baa",
// "DurationMs":30,
// "SourceContext":"SomeNamespace.SomeService",
// "TransactionId": 12345
// }
```

### Versioning

This package tracks the versioning and target framework support of its [_Microsoft.Extensions.Logging_](https://nuget.org/packages/Microsoft.Extensions.Logging) dependency.
Expand Down
2 changes: 1 addition & 1 deletion appveyor.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ artifacts:
deploy:
- provider: NuGet
api_key:
secure: EN9f+XXE3fW+ebL4wxrIbafdtbNvRfddBN8UUixvctYh4qMBHzr1JdnM83QsM1zo
secure: H96ajkMxwIafhF2vrr+UAUS10bFcAL/1wc3iphidRiYi9WoTc2i8shTLtF+75ODb
skip_symbols: true
on:
branch: /^(main|dev)$/
Expand Down
56 changes: 56 additions & 0 deletions samples/SampleWithExternalScope/Program.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
using System.Diagnostics;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Serilog;
using Serilog.Formatting.Json;

// Configure a JsonFormatter to log out scope to the console
Log.Logger = new LoggerConfiguration()
.MinimumLevel.Debug()
.WriteTo.Console(new JsonFormatter())
.CreateLogger();

// Setup Serilog with M.E.L, and configure the appropriate ActivityTrackingOptions
var services = new ServiceCollection();

services.AddLogging(l => l
.AddSerilog()
.Configure(options =>
{
options.ActivityTrackingOptions =
ActivityTrackingOptions.SpanId
| ActivityTrackingOptions.TraceId
| ActivityTrackingOptions.ParentId
| ActivityTrackingOptions.TraceState
| ActivityTrackingOptions.TraceFlags
| ActivityTrackingOptions.Tags
| ActivityTrackingOptions.Baggage;
}));

// Add an ActivityListener (required, otherwise Activities don't actually get created if nothing is listening to them)
ActivitySource.AddActivityListener(new ActivityListener
{
ShouldListenTo = source => true,
Sample = (ref ActivityCreationOptions<ActivityContext> options) => ActivitySamplingResult.AllDataAndRecorded
});

// Run our test
var activitySource = new ActivitySource("SomeActivitySource");

var serviceProvider = services.BuildServiceProvider();
var logger = serviceProvider.GetRequiredService<ILogger<Program>>();

using var activity = activitySource.StartActivity();

activity?.SetTag("tag.domain.id", 1234);
activity?.SetBaggage("baggage.environment", "uat");

using var scope = logger.BeginScope(new
{
User = "Hugh Mann",
Time = DateTimeOffset.UtcNow
});

logger.LogInformation("Hello world!");

serviceProvider.Dispose();
21 changes: 21 additions & 0 deletions samples/SampleWithExternalScope/SampleWithExternalScope.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<OutputType>Exe</OutputType>
<Nullable>enable</Nullable>
</PropertyGroup>

<ItemGroup>
<ProjectReference Include="..\..\src\Serilog.Extensions.Logging\Serilog.Extensions.Logging.csproj" />
</ItemGroup>

<ItemGroup>
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="8.0.0" />
<PackageReference Include="Microsoft.Extensions.Logging.Console" Version="8.0.0" />
<PackageReference Include="Serilog.Sinks.Console" Version="5.0.0" />
</ItemGroup>

</Project>
7 changes: 7 additions & 0 deletions serilog-extensions-logging.sln
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "assets", "assets", "{9C21B9
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Serilog.Extensions.Logging.Benchmarks", "test\Serilog.Extensions.Logging.Benchmarks\Serilog.Extensions.Logging.Benchmarks.csproj", "{6D5986FF-EECD-4E75-8BC6-A5F78AB549B2}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SampleWithExternalScope", "samples\SampleWithExternalScope\SampleWithExternalScope.csproj", "{653092A8-CBAD-40AA-A4CE-F8B19D6492C2}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand All @@ -53,6 +55,10 @@ Global
{6D5986FF-EECD-4E75-8BC6-A5F78AB549B2}.Debug|Any CPU.Build.0 = Debug|Any CPU
{6D5986FF-EECD-4E75-8BC6-A5F78AB549B2}.Release|Any CPU.ActiveCfg = Release|Any CPU
{6D5986FF-EECD-4E75-8BC6-A5F78AB549B2}.Release|Any CPU.Build.0 = Release|Any CPU
{653092A8-CBAD-40AA-A4CE-F8B19D6492C2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{653092A8-CBAD-40AA-A4CE-F8B19D6492C2}.Debug|Any CPU.Build.0 = Debug|Any CPU
{653092A8-CBAD-40AA-A4CE-F8B19D6492C2}.Release|Any CPU.ActiveCfg = Release|Any CPU
{653092A8-CBAD-40AA-A4CE-F8B19D6492C2}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand All @@ -62,6 +68,7 @@ Global
{37EADF84-5E41-4224-A194-1E3299DCD0B8} = {E30F638E-BBBE-4AD1-93CE-48CC69CFEFE1}
{65357FBC-9BC4-466D-B621-1C3A19BC2A78} = {F2407211-6043-439C-8E06-3641634332E7}
{6D5986FF-EECD-4E75-8BC6-A5F78AB549B2} = {E30F638E-BBBE-4AD1-93CE-48CC69CFEFE1}
{653092A8-CBAD-40AA-A4CE-F8B19D6492C2} = {F2407211-6043-439C-8E06-3641634332E7}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {811E61C5-3871-4633-AFAE-B35B619C8A10}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -103,12 +103,12 @@ LogEvent PrepareWrite<TState>(LogEventLevel level, EventId eventId, TState state
{
messageTemplate = value;
}
else if (property.Key.StartsWith("@"))
else if (property.Key.StartsWith('@'))
{
if (_logger.BindProperty(GetKeyWithoutFirstSymbol(DestructureDictionary, property.Key), property.Value, true, out var destructured))
properties.Add(destructured);
}
else if (property.Key.StartsWith("$"))
else if (property.Key.StartsWith('$'))
{
if (_logger.BindProperty(GetKeyWithoutFirstSymbol(StringifyDictionary, property.Key), property.Value?.ToString(), true, out var stringified))
properties.Add(stringified);
Expand Down Expand Up @@ -167,10 +167,10 @@ LogEvent PrepareWrite<TState>(LogEventLevel level, EventId eventId, TState state

static object? AsLoggableValue<TState>(TState state, Func<TState, Exception?, string>? formatter)
{
object? stateObj = state;
object? stateObj = null;
if (formatter != null)
stateObj = formatter(state, null);
return stateObj;
return stateObj ?? state;
}

internal static LogEventProperty CreateEventIdProperty(EventId eventId)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,14 +13,15 @@ namespace Serilog.Extensions.Logging;
/// An <see cref="ILoggerProvider"/> that pipes events through Serilog.
/// </summary>
[ProviderAlias("Serilog")]
public class SerilogLoggerProvider : ILoggerProvider, ILogEventEnricher
public class SerilogLoggerProvider : ILoggerProvider, ILogEventEnricher, ISupportExternalScope
{
internal const string OriginalFormatPropertyName = "{OriginalFormat}";
internal const string ScopePropertyName = "Scope";

// May be null; if it is, Log.Logger will be lazily used
readonly ILogger? _logger;
readonly Action? _dispose;
private IExternalScopeProvider? _externalScopeProvider;

/// <summary>
/// Construct a <see cref="SerilogLoggerProvider"/>.
Expand Down Expand Up @@ -75,13 +76,30 @@ public void Enrich(LogEvent logEvent, ILogEventPropertyFactory propertyFactory)
}
}

_externalScopeProvider?.ForEachScope((state, accumulatingLogEvent) =>
{
SerilogLoggerScope.EnrichWithStateAndCreateScopeItem(accumulatingLogEvent, propertyFactory, state, out var scopeItem);

if (scopeItem != null)
{
scopeItems ??= new List<LogEventPropertyValue>();
scopeItems.Add(scopeItem);
}
}, logEvent);

if (scopeItems != null)
{
scopeItems.Reverse();
logEvent.AddPropertyIfAbsent(new LogEventProperty(ScopePropertyName, new SequenceValue(scopeItems)));
}
}

/// <inheritdoc />
public void SetScopeProvider(IExternalScopeProvider scopeProvider)
{
_externalScopeProvider = scopeProvider;
}

readonly AsyncLocal<SerilogLoggerScope?> _value = new();

internal SerilogLoggerScope? CurrentScope
Expand Down
Loading