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

9.0.0 Release #256

Merged
merged 57 commits into from
Dec 9, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
57 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
62b775b
Move AddProperty() out to avoid a closure allocation
nblumhardt Mar 15, 2024
f845237
It's not necessary to handle {OriginalFormat} when TState is Dictiona…
nblumhardt Mar 15, 2024
5864a5d
Use LogEvent.UnstableAssembleFromParts() to avoid a dictionary alloc
nblumhardt Mar 15, 2024
1c77935
Update BenchmarkDotNet
nblumhardt Mar 15, 2024
03fcf95
"Improve LogEventBenchmark benchmarks":
nblumhardt Mar 15, 2024
bcc5021
Ordinal string comparisons
nblumhardt Mar 18, 2024
ccae6ff
Remove some accidental usages of throwing Dictionary<,>.Add()
nblumhardt Mar 18, 2024
7bdddec
Roll back EventId comparison change; the overridden != operator does …
nblumhardt Mar 18, 2024
adf275f
Tighter level mapping, though for invalid cases only
nblumhardt Mar 18, 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
51770bd
Merge branch 'dev' into serilog-4x
nblumhardt Jul 23, 2024
7fc50b0
Merge pull request #255 from epeshk/startswith-char
nblumhardt Jul 23, 2024
10afe06
Merge branch 'dev' into serilog-4x
nblumhardt Jul 23, 2024
d0dbbb1
Extend LogEventBenchmark with ILogger.Log that includes EventId.
Oct 31, 2024
0e5fe4c
Implement EventId to LogEventProperty cache.
Oct 31, 2024
4eb8b3c
Use EventIdPropertyCache in SerilogLogger; Create EventIdPropertyCach…
Oct 31, 2024
919b919
Compare both Id and Name for equity.
Oct 31, 2024
b68c884
Remove comment.
Oct 31, 2024
3731bcb
Define EventKey as readonly record struct.
Nov 1, 2024
59165d5
Change copyright text.
Nov 1, 2024
cb77647
Shared static EventIdPropertyCache cache; Use ConcurrentDictionary as…
Nov 1, 2024
29b94f3
Revert sample code changes.
Nov 1, 2024
4f3305a
Add functional tests of AddSerilog() extension method.
agehrke Nov 15, 2024
d5eb130
Make SerilogLoggerProvider implement IAsyncDisposable on .NET 6 or la…
agehrke Nov 15, 2024
2bc0ea5
Make EventIdPropertyCache non-static; Create unit tests.
Nov 15, 2024
5e1527d
Merge pull request #260 from AndreReise/improve-event-id-cache
nblumhardt Nov 15, 2024
03a235a
Address naming
agehrke Nov 16, 2024
ac351f2
Define FEATURE_ASYNCDISPOSABLE and use
agehrke Nov 16, 2024
0584a5b
Merge pull request #262 from agehrke/asyncdispose
nblumhardt Nov 16, 2024
a35b35b
Update to .NET 9, Actions build, revise targets, seal public types no…
nblumhardt Nov 24, 2024
44d0ec9
Ignore .DS_Store
nblumhardt Nov 24, 2024
1549fe5
Merge pull request #264 from nblumhardt/dotnet-9
nblumhardt Nov 26, 2024
e47a264
Merge branch 'dev' of https://github.com/serilog/serilog-extensions-l…
nblumhardt Dec 6, 2024
92751dd
Merge pull request #247 from nblumhardt/serilog-4x
nblumhardt Dec 6, 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
41 changes: 41 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
# If this file is renamed, the incrementing run attempt number will be reset.

name: CI

on:
push:
branches: [ "dev", "main" ]
pull_request:
branches: [ "dev", "main" ]

env:
CI_BUILD_NUMBER_BASE: ${{ github.run_number }}
CI_TARGET_BRANCH: ${{ github.head_ref || github.ref_name }}

jobs:
build:

# The build must run on Windows so that .NET Framework targets can be built and tested.
runs-on: windows-latest

permissions:
contents: write

steps:
- uses: actions/checkout@v4
- name: Setup
uses: actions/setup-dotnet@v4
with:
dotnet-version: 9.0.x
- name: Compute build number
shell: bash
run: |
echo "CI_BUILD_NUMBER=$(($CI_BUILD_NUMBER_BASE+2300))" >> $GITHUB_ENV
- name: Build and Publish
env:
DOTNET_CLI_TELEMETRY_OPTOUT: true
NUGET_API_KEY: ${{ secrets.NUGET_API_KEY }}
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
shell: pwsh
run: |
./Build.ps1
5 changes: 4 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -200,4 +200,7 @@ project.lock.json
# JetBrains Rider
.idea

.vscode
.vscode

.DS_Store

93 changes: 57 additions & 36 deletions Build.ps1
Original file line number Diff line number Diff line change
@@ -1,58 +1,79 @@
echo "build: Build started"
Write-Output "build: Tool versions follow"

$env:Path = "$pwd/.dotnetcli;$env:Path"
dotnet --version
dotnet --list-sdks

Write-Output "build: Build started"

Push-Location $PSScriptRoot
try {
if(Test-Path .\artifacts) {
Write-Output "build: Cleaning ./artifacts"
Remove-Item ./artifacts -Force -Recurse
}

if(Test-Path .\artifacts) {
echo "build: Cleaning .\artifacts"
Remove-Item .\artifacts -Force -Recurse
}
& dotnet restore --no-cache

& dotnet restore --no-cache
$dbp = [Xml] (Get-Content .\Directory.Version.props)
$versionPrefix = $dbp.Project.PropertyGroup.VersionPrefix

$branch = @{ $true = $env:APPVEYOR_REPO_BRANCH; $false = $(git symbolic-ref --short -q HEAD) }[$env:APPVEYOR_REPO_BRANCH -ne $NULL];
$revision = @{ $true = "{0:00000}" -f [convert]::ToInt32("0" + $env:APPVEYOR_BUILD_NUMBER, 10); $false = "local" }[$env:APPVEYOR_BUILD_NUMBER -ne $NULL];
$suffix = @{ $true = ""; $false = "$($branch.Substring(0, [math]::Min(10,$branch.Length)))-$revision"}[$branch -eq "main" -and $revision -ne "local"]
Write-Output "build: Package version prefix is $versionPrefix"

echo "build: Version suffix is $suffix"
$branch = @{ $true = $env:CI_TARGET_BRANCH; $false = $(git symbolic-ref --short -q HEAD) }[$NULL -ne $env:CI_TARGET_BRANCH];
$revision = @{ $true = "{0:00000}" -f [convert]::ToInt32("0" + $env:CI_BUILD_NUMBER, 10); $false = "local" }[$NULL -ne $env:CI_BUILD_NUMBER];
$suffix = @{ $true = ""; $false = "$($branch.Substring(0, [math]::Min(10,$branch.Length)) -replace '([^a-zA-Z0-9\-]*)', '')-$revision"}[$branch -eq "main" -and $revision -ne "local"]
$commitHash = $(git rev-parse --short HEAD)
$buildSuffix = @{ $true = "$($suffix)-$($commitHash)"; $false = "$($branch)-$($commitHash)" }[$suffix -ne ""]

foreach ($src in ls src/*) {
Push-Location $src
Write-Output "build: Package version suffix is $suffix"
Write-Output "build: Build version suffix is $buildSuffix"

echo "build: Packaging project in $src"
& dotnet build -c Release --version-suffix=$buildSuffix /p:ContinuousIntegrationBuild=true
if($LASTEXITCODE -ne 0) { throw "Build failed" }

if($suffix) {
& dotnet pack -c Release --include-source -o ..\..\artifacts --version-suffix=$suffix
} else {
& dotnet pack -c Release --include-source -o ..\..\artifacts
}
foreach ($src in Get-ChildItem src/*) {
Push-Location $src

if($LASTEXITCODE -ne 0) { throw "build failed" }
Write-Output "build: Packaging project in $src"

Pop-Location
}
if ($suffix) {
& dotnet pack -c Release --no-build --no-restore -o ../../artifacts --version-suffix=$suffix
} else {
& dotnet pack -c Release --no-build --no-restore -o ../../artifacts
}
if($LASTEXITCODE -ne 0) { throw "Packaging failed" }

Pop-Location
}

foreach ($test in ls test/*.PerformanceTests) {
Push-Location $test
foreach ($test in Get-ChildItem test/*.Tests) {
Push-Location $test

echo "build: Building performance test project in $test"
Write-Output "build: Testing project in $test"

& dotnet build -c Release
if($LASTEXITCODE -ne 0) { throw "test failed" }
& dotnet test -c Release --no-build --no-restore
if($LASTEXITCODE -ne 0) { throw "Testing failed" }

Pop-Location
}
Pop-Location
}

foreach ($test in ls test/*.Tests) {
Push-Location $test
if ($env:NUGET_API_KEY) {
# GitHub Actions will only supply this to branch builds and not PRs. We publish
# builds from any branch this action targets (i.e. main and dev).

echo "build: Testing project in $test"
Write-Output "build: Publishing NuGet packages"

& dotnet test -c Release
if($LASTEXITCODE -ne 0) { throw "test failed" }
foreach ($nupkg in Get-ChildItem artifacts/*.nupkg) {
& dotnet nuget push -k $env:NUGET_API_KEY -s https://api.nuget.org/v3/index.json "$nupkg"
if($LASTEXITCODE -ne 0) { throw "Publishing failed" }
}

if (!($suffix)) {
Write-Output "build: Creating release for version $versionPrefix"

iex "gh release create v$versionPrefix --title v$versionPrefix --generate-notes $(get-item ./artifacts/*.nupkg) $(get-item ./artifacts/*.snupkg)"
}
}
} finally {
Pop-Location
}

Pop-Location
21 changes: 17 additions & 4 deletions Directory.Build.props
Original file line number Diff line number Diff line change
@@ -1,12 +1,25 @@
<Project>

<!-- Properties in this file are expected to be identical for all Serilog organization projects. If
a property value is project-specific, please record it in the CSPROJ file instead. -->
<Import Project="$(MSBuildThisFileDirectory)Directory.Version.props" />
<PropertyGroup>
<LangVersion>latest</LangVersion>
<TreatWarningsAsErrors>True</TreatWarningsAsErrors>
<SignAssembly>true</SignAssembly>
<!-- The condition is required to support BenchmarkDotNet -->
<SignAssembly Condition="Exists('$(MSBuildThisFileDirectory)assets/Serilog.snk')">true</SignAssembly>
<AssemblyOriginatorKeyFile>$(MSBuildThisFileDirectory)assets/Serilog.snk</AssemblyOriginatorKeyFile>
<PublicSign Condition=" '$(OS)' != 'Windows_NT' ">true</PublicSign>
<CheckEolTargetFramework>false</CheckEolTargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
<PublishRepositoryUrl>true</PublishRepositoryUrl>
<EmbedUntrackedSources>true</EmbedUntrackedSources>
<IncludeSymbols>true</IncludeSymbols>
<SymbolPackageFormat>snupkg</SymbolPackageFormat>
</PropertyGroup>

<ItemGroup Condition="'$(TargetFrameworkIdentifier)' == '.NETFramework'">
<Reference Include="System" />
<Reference Include="System.Core" />
<Reference Include="Microsoft.CSharp" />
</ItemGroup>
</Project>
3 changes: 0 additions & 3 deletions Directory.Build.targets

This file was deleted.

6 changes: 6 additions & 0 deletions Directory.Version.props
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
<Project>
<PropertyGroup>
<!-- This must match the major and minor components of the referenced Microsoft.Extensions.Logging package. -->
<VersionPrefix>9.0.0</VersionPrefix>
</PropertyGroup>
</Project>
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
9 changes: 0 additions & 9 deletions Setup.ps1

This file was deleted.

26 changes: 0 additions & 26 deletions appveyor.yml

This file was deleted.

Loading