diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
new file mode 100644
index 0000000..e482c23
--- /dev/null
+++ b/.github/workflows/ci.yml
@@ -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:
+
+ 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+130))" >> $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
+
diff --git a/Build.ps1 b/Build.ps1
index d8abfad..2e09e11 100644
--- a/Build.ps1
+++ b/Build.ps1
@@ -1,56 +1,79 @@
-echo "build: Build started"
+Write-Output "build: Build started"
Push-Location $PSScriptRoot
+Write-Output "build: Tool versions follow"
+
+dotnet --version
+dotnet --list-sdks
+
if(Test-Path .\artifacts) {
- echo "build: Cleaning .\artifacts"
- Remove-Item .\artifacts -Force -Recurse
+ Write-Output "build: Cleaning ./artifacts"
+ Remove-Item ./artifacts -Force -Recurse
}
& dotnet restore --no-cache
-$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"]
+$dbp = [Xml] (Get-Content .\Directory.Build.props)
+$versionPrefix = $dbp.Project.PropertyGroup.VersionPrefix
-echo "build: Version suffix is $suffix"
+Write-Output "build: Package version prefix is $versionPrefix"
-foreach ($src in ls src/*) {
- Push-Location $src
+$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"]
- echo "build: Packaging project in $src"
+Write-Output "build: Package version suffix is $suffix"
- if ($suffix) {
- & dotnet pack -c Release -o ..\..\artifacts --version-suffix=$suffix
- } else {
- & dotnet pack -c Release -o ..\..\artifacts
- }
-
- if($LASTEXITCODE -ne 0) { exit 1 }
-
- Pop-Location
+if ($suffix) {
+ & dotnet build -c Release --version-suffix=$suffix /p:ContinuousIntegrationBuild=true
+} else {
+ & dotnet build -c Release /p:ContinuousIntegrationBuild=true
}
-foreach ($test in ls test/*.PerformanceTests) {
- Push-Location $test
+if($LASTEXITCODE -ne 0) { throw "Build failed" }
- echo "build: Building performance test project in $test"
+foreach ($src in Get-ChildItem src/*) {
+ Push-Location $src
- & dotnet build -c Release
- if($LASTEXITCODE -ne 0) { exit 2 }
+ Write-Output "build: Packaging project in $src"
+ 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/*.Tests) {
+foreach ($test in Get-ChildItem test/*.Tests) {
Push-Location $test
- echo "build: Testing project in $test"
+ Write-Output "build: Testing project in $test"
& dotnet test -c Release
- if($LASTEXITCODE -ne 0) { exit 3 }
+ if($LASTEXITCODE -ne 0) { throw "Testing failed" }
Pop-Location
}
Pop-Location
+
+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).
+
+ Write-Output "build: Publishing NuGet packages"
+
+ 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)"
+ }
+}
diff --git a/Directory.Build.props b/Directory.Build.props
new file mode 100644
index 0000000..b6d027b
--- /dev/null
+++ b/Directory.Build.props
@@ -0,0 +1,8 @@
+
+
+ 9.0.0
+ latest
+ enable
+ enable
+
+
diff --git a/README.md b/README.md
index 970535f..ba65a83 100644
--- a/README.md
+++ b/README.md
@@ -1,4 +1,4 @@
-# Seq.Extensions.Logging [](https://ci.appveyor.com/project/datalust/seq-extensions-logging) [](https://nuget.org/packages/Seq.Extensions.Logging)
+# Seq.Extensions.Logging [](https://nuget.org/packages/Seq.Extensions.Logging)
[Seq](https://datalust.co/seq) is a flexible self-hosted back-end for the ASP.NET Core logging subsystem (_Microsoft.Extensions.Logging_). Log events generated by the framework and application code are sent over HTTP to a Seq server, where the structured data associated with each event is used for powerful filtering, correlation, and analysis.
@@ -126,6 +126,17 @@ builder.Logging.Configure(opts => {
});
```
+### Enrichment
+
+You can add additional structured data to events being sent to Seq by specifying _enricher_ callbacks in the `AddSeq()` method:
+
+```csharp
+ .AddSeq(enrichers: [evt => evt.AddOrUpdateProperty(
+ "ThreadId",
+ Environment.CurrentManagedThreadId)
+ ]))
+```
+
### Troubleshooting
> Nothing showed up, what can I do?
@@ -170,5 +181,5 @@ you're targeting `net8.0`, target a 8.* version of _Seq.Extensions.Logging_ for
### Credits
-This package is based on a subset of the powerful [Serilog](https://serilog.net) library. Not all of the options supported by the Serilog and Seq client libraries are present in
-the _Seq.Extensions.Logging_ package.
+This package is based on a subset of [Serilog](https://serilog.net). Not all of the options supported by the Serilog and Seq client
+libraries are present in the _Seq.Extensions.Logging_ package.
diff --git a/appveyor.yml b/appveyor.yml
deleted file mode 100644
index 05aacc5..0000000
--- a/appveyor.yml
+++ /dev/null
@@ -1,15 +0,0 @@
-version: '{build}'
-skip_tags: true
-image: Visual Studio 2022
-build_script:
-- pwsh: ./Build.ps1
-test: off
-artifacts:
-- path: artifacts/Seq.Extensions.Logging.*.nupkg
-deploy:
-- provider: NuGet
- api_key:
- secure: Urj/cvXeFTl4NjNLrRJUZ2EpSak7Jq9JfswqrNBeDpFiTjkpjzsm3CgoOr8gRSBU
- skip_symbols: true
- on:
- branch: /^(main|dev)$/
diff --git a/example/ConsoleExample/ConsoleExample.csproj b/example/ConsoleExample/ConsoleExample.csproj
index dacb017..b9f28d4 100644
--- a/example/ConsoleExample/ConsoleExample.csproj
+++ b/example/ConsoleExample/ConsoleExample.csproj
@@ -2,15 +2,15 @@
Exe
- net8.0
+ net9.0
enable
enable
-
-
-
+
+
+
diff --git a/example/WebExample/WebExample.csproj b/example/WebExample/WebExample.csproj
index 3fecad7..525bd2e 100644
--- a/example/WebExample/WebExample.csproj
+++ b/example/WebExample/WebExample.csproj
@@ -1,7 +1,7 @@
- net8.0
+ net9.0
enable
enable
diff --git a/global.json b/global.json
index 501e79a..d5bf446 100644
--- a/global.json
+++ b/global.json
@@ -1,6 +1,6 @@
{
"sdk": {
- "version": "8.0.100",
+ "version": "9.0.100",
"rollForward": "latestFeature"
}
}
\ No newline at end of file
diff --git a/seq-extensions-logging.sln b/seq-extensions-logging.sln
index 846b6fb..096dfc5 100644
--- a/seq-extensions-logging.sln
+++ b/seq-extensions-logging.sln
@@ -6,13 +6,13 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{0069BFD6-B0E
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "global", "global", "{B0D573D8-1AE2-4C5C-817A-95815067452E}"
ProjectSection(SolutionItems) = preProject
- appveyor.yml = appveyor.yml
- Build.ps1 = Build.ps1
LICENSE = LICENSE
README.md = README.md
global.json = global.json
.gitattributes = .gitattributes
.gitignore = .gitignore
+ Directory.Build.props = Directory.Build.props
+ Build.ps1 = Build.ps1
EndProjectSection
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "test", "test", "{D54DE844-AC36-4872-928F-5F573D41ACAE}"
@@ -27,6 +27,13 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WebExample", "example\WebEx
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ConsoleExample", "example\ConsoleExample\ConsoleExample.csproj", "{3C7C4F1B-7BB1-48F6-BC9E-0A81F632458B}"
EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".github", ".github", "{98FB80CE-7AEF-4FA3-88D0-9812529BC15F}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "workflows", "workflows", "{8BCBFBF7-24E1-491A-956E-1AC7E1183270}"
+ ProjectSection(SolutionItems) = preProject
+ .github\workflows\ci.yml = .github\workflows\ci.yml
+ EndProjectSection
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -58,6 +65,7 @@ Global
{64A3F2C5-D71E-44A6-8DC7-99474765B32E} = {D54DE844-AC36-4872-928F-5F573D41ACAE}
{FC44EC1F-AD83-4522-99B4-4B309B8CC78A} = {1C72E771-7C72-4A30-A408-5CE66E792ADF}
{3C7C4F1B-7BB1-48F6-BC9E-0A81F632458B} = {1C72E771-7C72-4A30-A408-5CE66E792ADF}
+ {8BCBFBF7-24E1-491A-956E-1AC7E1183270} = {98FB80CE-7AEF-4FA3-88D0-9812529BC15F}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {A6DD7789-D2A5-43A4-A4FE-E138166E15C7}
diff --git a/seq-extensions-logging.sln.DotSettings b/seq-extensions-logging.sln.DotSettings
index 6942af0..989d8b8 100644
--- a/seq-extensions-logging.sln.DotSettings
+++ b/seq-extensions-logging.sln.DotSettings
@@ -1,5 +1,7 @@
<Policy Inspect="True" Prefix="_" Suffix="" Style="aaBb"><ExtraRule Prefix="" Suffix="" Style="AaBb" /></Policy>
+ <Policy><Descriptor Staticness="Instance" AccessRightKinds="Private" Description="Instance fields (private)"><ElementKinds><Kind Name="FIELD" /><Kind Name="READONLY_FIELD" /></ElementKinds></Descriptor><Policy Inspect="True" Prefix="_" Suffix="" Style="aaBb"><ExtraRule Prefix="" Suffix="" Style="AaBb" /></Policy></Policy>
+ True
True
True
True
diff --git a/src/Seq.Extensions.Logging/Microsoft/Extensions/Logging/SeqLoggerExtensions.cs b/src/Seq.Extensions.Logging/Microsoft/Extensions/Logging/SeqLoggerExtensions.cs
index 13a2922..afdbca0 100644
--- a/src/Seq.Extensions.Logging/Microsoft/Extensions/Logging/SeqLoggerExtensions.cs
+++ b/src/Seq.Extensions.Logging/Microsoft/Extensions/Logging/SeqLoggerExtensions.cs
@@ -1,4 +1,5 @@
-using Microsoft.Extensions.Configuration;
+using System.Diagnostics.CodeAnalysis;
+using Microsoft.Extensions.Configuration;
using Serilog.Core;
using Seq.Extensions.Logging;
using Serilog.Events;
@@ -7,6 +8,7 @@
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging.Configuration;
using Serilog.Sinks.PeriodicBatching;
+// ReSharper disable UnusedMember.Global
namespace Microsoft.Extensions.Logging;
@@ -28,7 +30,7 @@ public static ILoggerFactory AddSeq(this ILoggerFactory loggerFactory, IConfigur
if (loggerFactory == null) throw new ArgumentNullException(nameof(loggerFactory));
if (configuration == null) throw new ArgumentNullException(nameof(configuration));
- if (TryCreateProvider(configuration, LogLevel.Information, out var provider))
+ if (TryCreateProvider(configuration, LogLevel.Information, [], out var provider))
loggerFactory.AddProvider(provider);
return loggerFactory;
@@ -42,62 +44,66 @@ public static ILoggerFactory AddSeq(this ILoggerFactory loggerFactory, IConfigur
/// A Seq API key to authenticate or tag messages from the logger.
/// The level below which events will be suppressed (the default is ).
/// A dictionary mapping logger name prefixes to minimum logging levels.
+ /// A collection of enrichers to apply.
/// A logger factory to allow further configuration.
public static ILoggerFactory AddSeq(
this ILoggerFactory loggerFactory,
string serverUrl = LocalServerUrl,
- string apiKey = null,
+ string? apiKey = null,
LogLevel minimumLevel = LogLevel.Information,
- IDictionary levelOverrides = null)
+ IDictionary? levelOverrides = null,
+ IEnumerable>? enrichers = null)
{
if (loggerFactory == null) throw new ArgumentNullException(nameof(loggerFactory));
if (serverUrl == null) throw new ArgumentNullException(nameof(serverUrl));
- var provider = CreateProvider(serverUrl, apiKey, minimumLevel, levelOverrides);
+ var provider = CreateProvider(serverUrl, apiKey, minimumLevel, levelOverrides, enrichers);
loggerFactory.AddProvider(provider);
return loggerFactory;
}
///
- /// Adds a Seq logger.
+ /// Adds a Seq logger configured from the supplied configuration section.
///
/// The logging builder.
- /// The Seq server URL; the default is http://localhost:5341.
- /// A Seq API key to authenticate or tag messages from the logger.
+ /// A configuration section with details of the Seq server connection.
/// A logging builder to allow further configuration.
public static ILoggingBuilder AddSeq(
this ILoggingBuilder loggingBuilder,
- string serverUrl = LocalServerUrl,
- string apiKey = null)
+ IConfigurationSection configuration)
{
if (loggingBuilder == null) throw new ArgumentNullException(nameof(loggingBuilder));
- if (serverUrl == null) throw new ArgumentNullException(nameof(serverUrl));
+ if (configuration == null) throw new ArgumentNullException(nameof(configuration));
- loggingBuilder.Services.AddSingleton(s =>
- {
- var opts = s.GetService>();
- var provider = CreateProvider(opts?.Configuration, serverUrl, apiKey);
- return provider;
- });
+ if (TryCreateProvider(configuration, LevelAlias.Minimum, Array.Empty>(), out var provider))
+ loggingBuilder.Services.AddSingleton(_ => provider);
return loggingBuilder;
}
///
- /// Adds a Seq logger configured from the supplied configuration section.
+ /// Adds a Seq logger.
///
/// The logging builder.
- /// A configuration section with details of the Seq server connection.
+ /// The Seq server URL; the default is http://localhost:5341.
+ /// A Seq API key to authenticate or tag messages from the logger.
+ /// A collection of enrichers to apply.
/// A logging builder to allow further configuration.
public static ILoggingBuilder AddSeq(
this ILoggingBuilder loggingBuilder,
- IConfigurationSection configuration)
+ string serverUrl = LocalServerUrl,
+ string? apiKey = null,
+ IEnumerable>? enrichers = null)
{
if (loggingBuilder == null) throw new ArgumentNullException(nameof(loggingBuilder));
- if (configuration == null) throw new ArgumentNullException(nameof(configuration));
+ if (serverUrl == null) throw new ArgumentNullException(nameof(serverUrl));
- if (TryCreateProvider(configuration, LevelAlias.Minimum, out var provider))
- loggingBuilder.Services.AddSingleton(_ => provider);
+ loggingBuilder.Services.AddSingleton(s =>
+ {
+ var opts = s.GetService>();
+ var provider = CreateProvider(opts?.Configuration, serverUrl, apiKey, enrichers);
+ return provider;
+ });
return loggingBuilder;
}
@@ -105,7 +111,8 @@ public static ILoggingBuilder AddSeq(
static bool TryCreateProvider(
IConfigurationSection configuration,
LogLevel defaultMinimumLevel,
- out SerilogLoggerProvider provider)
+ IEnumerable> enrichers,
+ [NotNullWhen(true)] out SerilogLoggerProvider? provider)
{
var serverUrl = configuration["ServerUrl"];
if (string.IsNullOrWhiteSpace(serverUrl))
@@ -131,28 +138,28 @@ static bool TryCreateProvider(
}
var levelOverrides = new Dictionary();
- foreach (var overr in configuration.GetSection("LevelOverride").GetChildren())
+ foreach (var levelOverride in configuration.GetSection("LevelOverride").GetChildren())
{
- LogLevel value;
- if (!Enum.TryParse(overr.Value, out value))
+ if (!Enum.TryParse(levelOverride.Value, out LogLevel value))
{
- SelfLog.WriteLine("The level override setting `{0}` for `{1}` is invalid", overr.Value, overr.Key);
+ SelfLog.WriteLine("The level override setting `{0}` for `{1}` is invalid", levelOverride.Value, levelOverride.Key);
continue;
}
- levelOverrides[overr.Key] = value;
+ levelOverrides[levelOverride.Key] = value;
}
- provider = CreateProvider(serverUrl, apiKey, minimumLevel, levelOverrides);
+ provider = CreateProvider(serverUrl, apiKey, minimumLevel, levelOverrides, enrichers);
return true;
}
static SerilogLoggerProvider CreateProvider(
- IConfiguration configuration,
- string defaultServerUrl,
- string defaultApiKey)
+ IConfiguration? configuration,
+ string? defaultServerUrl,
+ string? defaultApiKey,
+ IEnumerable>? enrichers)
{
- string serverUrl = null, apiKey = null;
+ string? serverUrl = null, apiKey = null;
if (configuration != null)
{
serverUrl = configuration["ServerUrl"];
@@ -165,31 +172,32 @@ static SerilogLoggerProvider CreateProvider(
if (string.IsNullOrWhiteSpace(apiKey))
apiKey = defaultApiKey;
- return CreateProvider(serverUrl, apiKey, LevelAlias.Minimum, null);
+ return CreateProvider(serverUrl, apiKey, LevelAlias.Minimum, null, enrichers);
}
static SerilogLoggerProvider CreateProvider(
- string serverUrl,
- string apiKey,
+ string? serverUrl,
+ string? apiKey,
LogLevel minimumLevel,
- IDictionary levelOverrides)
+ IDictionary? levelOverrides,
+ IEnumerable>? enrichers)
{
var levelSwitch = new LoggingLevelSwitch(minimumLevel);
var sink = new SeqSink(
- serverUrl,
+ serverUrl!,
apiKey,
256 * 1024,
new ControlledLevelSwitch(levelSwitch),
null);
- LevelOverrideMap overrideMap = null;
+ LevelOverrideMap? overrideMap = null;
if (levelOverrides != null && levelOverrides.Count != 0)
{
var overrides = new Dictionary();
foreach (var levelOverride in levelOverrides)
{
- overrides.Add(levelOverride.Key, new LoggingLevelSwitch(levelOverride.Value));
+ overrides[levelOverride.Key] = new LoggingLevelSwitch(levelOverride.Value);
}
overrideMap = new LevelOverrideMap(overrides, levelSwitch);
@@ -201,7 +209,7 @@ static SerilogLoggerProvider CreateProvider(
Period = TimeSpan.FromSeconds(2),
});
- var logger = new Logger(levelSwitch, batchingSink, batchingSink.Dispose, overrideMap);
+ var logger = new Logger(batchingSink, new Enricher(enrichers ?? []), batchingSink.Dispose, levelSwitch, overrideMap);
var provider = new SerilogLoggerProvider(logger);
return provider;
}
diff --git a/src/Seq.Extensions.Logging/Seq.Extensions.Logging.csproj b/src/Seq.Extensions.Logging/Seq.Extensions.Logging.csproj
index 7e42e7b..bdf66ca 100644
--- a/src/Seq.Extensions.Logging/Seq.Extensions.Logging.csproj
+++ b/src/Seq.Extensions.Logging/Seq.Extensions.Logging.csproj
@@ -2,9 +2,8 @@
Add centralized structured log collection to ASP.NET Core apps with one line of code.
- 8.0.0
Datalust and Contributors
- netstandard2.0;net6.0;net8.0
+ netstandard2.0;net8.0;net9.0
true
true
../../asset/seqext.snk
@@ -14,17 +13,15 @@
icon.png
https://github.com/datalust/seq-extensions-logging
Apache-2.0
- latest
README.md
- enable
-
-
-
+
+
+
diff --git a/src/Seq.Extensions.Logging/Seq/Extensions/Logging/Enricher.cs b/src/Seq.Extensions.Logging/Seq/Extensions/Logging/Enricher.cs
new file mode 100644
index 0000000..5254031
--- /dev/null
+++ b/src/Seq.Extensions.Logging/Seq/Extensions/Logging/Enricher.cs
@@ -0,0 +1,36 @@
+using Serilog.Core;
+using Serilog.Core.Enrichers;
+using Serilog.Events;
+
+namespace Seq.Extensions.Logging;
+
+class Enricher: ILogEventEnricher
+{
+ readonly ILogEventEnricher _builtIn = new SafeAggregateEnricher([new ExceptionDataEnricher()]);
+
+ public Enricher(IEnumerable>? enrichers = null)
+ {
+ _enrichers = (enrichers ?? Array.Empty>()).ToArray();
+ }
+
+ readonly Action[] _enrichers;
+
+ public void Enrich(LogEvent logEvent, ILogEventPropertyValueFactory propertyFactory)
+ {
+ _builtIn.Enrich(logEvent, propertyFactory);
+
+ var enriching = new EnrichingEvent(logEvent, propertyFactory);
+
+ foreach (var enricher in _enrichers)
+ {
+ try
+ {
+ enricher(enriching);
+ }
+ catch (Exception ex)
+ {
+ SelfLog.WriteLine("Exception {0} caught while enriching {1}.", ex, logEvent);
+ }
+ }
+ }
+}
diff --git a/src/Seq.Extensions.Logging/Seq/Extensions/Logging/EnrichingEvent.cs b/src/Seq.Extensions.Logging/Seq/Extensions/Logging/EnrichingEvent.cs
new file mode 100644
index 0000000..d94050d
--- /dev/null
+++ b/src/Seq.Extensions.Logging/Seq/Extensions/Logging/EnrichingEvent.cs
@@ -0,0 +1,39 @@
+using Serilog.Core;
+using Serilog.Events;
+
+namespace Seq.Extensions.Logging;
+
+///
+/// The input to an enricher.
+///
+public readonly struct EnrichingEvent
+{
+ internal EnrichingEvent(LogEvent logEvent, ILogEventPropertyValueFactory propertyFactory)
+ {
+ _propertyFactory = propertyFactory;
+ LogEvent = logEvent;
+ }
+
+ readonly ILogEventPropertyValueFactory _propertyFactory;
+
+ internal LogEvent LogEvent { get; }
+
+ ///
+ /// Add a property to the event if not already present, otherwise, update its value.
+ ///
+ public void AddOrUpdateProperty(string propertyName, object propertyValue, bool serialize = false)
+ {
+ LogEvent.AddOrUpdateProperty(propertyName, _propertyFactory.CreatePropertyValue(propertyValue, serialize));
+ }
+
+ ///
+ /// Add a property to the event if not already present.
+ ///
+ public void AddPropertyIfAbsent(string propertyName, object propertyValue, bool serialize = false)
+ {
+ if (LogEvent.Properties.ContainsKey(propertyName))
+ return;
+
+ AddOrUpdateProperty(propertyName, propertyValue, serialize);
+ }
+}
diff --git a/src/Seq.Extensions.Logging/Seq/Extensions/Logging/ExceptionDataEnricher.cs b/src/Seq.Extensions.Logging/Seq/Extensions/Logging/ExceptionDataEnricher.cs
index f748c31..a38f7f2 100644
--- a/src/Seq.Extensions.Logging/Seq/Extensions/Logging/ExceptionDataEnricher.cs
+++ b/src/Seq.Extensions.Logging/Seq/Extensions/Logging/ExceptionDataEnricher.cs
@@ -20,7 +20,7 @@ namespace Seq.Extensions.Logging;
class ExceptionDataEnricher : ILogEventEnricher
{
- public void Enrich(LogEvent logEvent, ILogEventPropertyFactory propertyFactory)
+ public void Enrich(LogEvent logEvent, ILogEventPropertyValueFactory propertyFactory)
{
var exceptionData = logEvent.Exception?.GetBaseException().Data;
if (exceptionData == null || exceptionData.Count == 0)
@@ -29,10 +29,8 @@ public void Enrich(LogEvent logEvent, ILogEventPropertyFactory propertyFactory)
var data = exceptionData
.Cast()
.Where(e => e.Key is string)
- .Select(e => propertyFactory.CreateProperty((string)e.Key, e.Value));
+ .Select(e => new LogEventProperty((string)e.Key, propertyFactory.CreatePropertyValue(e.Value)));
- var property = new LogEventProperty("ExceptionData", new StructureValue(data));
-
- logEvent.AddPropertyIfAbsent(property);
+ logEvent.AddPropertyIfAbsent("ExceptionData", new StructureValue(data));
}
-}
\ No newline at end of file
+}
diff --git a/src/Seq.Extensions.Logging/Seq/Extensions/Logging/JsonSafeString.cs b/src/Seq.Extensions.Logging/Seq/Extensions/Logging/JsonSafeString.cs
index 1ea9bbd..9f70290 100644
--- a/src/Seq.Extensions.Logging/Seq/Extensions/Logging/JsonSafeString.cs
+++ b/src/Seq.Extensions.Logging/Seq/Extensions/Logging/JsonSafeString.cs
@@ -1,6 +1,4 @@
-#nullable enable
-
-namespace Seq.Extensions.Logging;
+namespace Seq.Extensions.Logging;
///
/// A wrapper type that marks a string as containing valid JSON that's safe to include as-is in log events.
diff --git a/src/Seq.Extensions.Logging/Seq/Extensions/Logging/SelfLog.cs b/src/Seq.Extensions.Logging/Seq/Extensions/Logging/SelfLog.cs
index 4209e39..b5549e5 100644
--- a/src/Seq.Extensions.Logging/Seq/Extensions/Logging/SelfLog.cs
+++ b/src/Seq.Extensions.Logging/Seq/Extensions/Logging/SelfLog.cs
@@ -20,7 +20,7 @@ namespace Seq.Extensions.Logging;
///
public static class SelfLog
{
- static Action _output;
+ static Action? _output;
///
/// Set the output mechanism for self-log messages.
@@ -69,7 +69,7 @@ public static void Disable()
/// The name is historical; because this is used from third-party sink packages, removing the "Line"
/// suffix as would seem sensible isn't worth the breakage.
///
- public static void WriteLine(string format, object arg0 = null, object arg1 = null, object arg2 = null)
+ public static void WriteLine(string format, object? arg0 = null, object? arg1 = null, object? arg2 = null)
{
var o = _output;
diff --git a/src/Seq.Extensions.Logging/Serilog/Core/Enrichers/FixedPropertyEnricher.cs b/src/Seq.Extensions.Logging/Serilog/Core/Enrichers/FixedPropertyEnricher.cs
index d0e7e85..6141447 100644
--- a/src/Seq.Extensions.Logging/Serilog/Core/Enrichers/FixedPropertyEnricher.cs
+++ b/src/Seq.Extensions.Logging/Serilog/Core/Enrichers/FixedPropertyEnricher.cs
@@ -22,13 +22,12 @@ class FixedPropertyEnricher : ILogEventEnricher
public FixedPropertyEnricher(LogEventProperty logEventProperty)
{
- if (logEventProperty == null) throw new ArgumentNullException(nameof(logEventProperty));
_logEventProperty = logEventProperty;
}
- public void Enrich(LogEvent logEvent, ILogEventPropertyFactory propertyFactory)
+ public void Enrich(LogEvent logEvent, ILogEventPropertyValueFactory propertyFactory)
{
if (logEvent == null) throw new ArgumentNullException(nameof(logEvent));
- logEvent.AddPropertyIfAbsent(_logEventProperty);
+ logEvent.AddPropertyIfAbsent(_logEventProperty.Name, _logEventProperty.Value);
}
}
\ No newline at end of file
diff --git a/src/Seq.Extensions.Logging/Serilog/Core/Enrichers/PropertyEnricher.cs b/src/Seq.Extensions.Logging/Serilog/Core/Enrichers/PropertyEnricher.cs
index e42e217..ba2e6dd 100644
--- a/src/Seq.Extensions.Logging/Serilog/Core/Enrichers/PropertyEnricher.cs
+++ b/src/Seq.Extensions.Logging/Serilog/Core/Enrichers/PropertyEnricher.cs
@@ -49,11 +49,11 @@ public PropertyEnricher(string name, object value, bool destructureObjects = fal
///
/// The log event to enrich.
/// Factory for creating new properties to add to the event.
- public void Enrich(LogEvent logEvent, ILogEventPropertyFactory propertyFactory)
+ public void Enrich(LogEvent logEvent, ILogEventPropertyValueFactory propertyFactory)
{
if (logEvent == null) throw new ArgumentNullException(nameof(logEvent));
if (propertyFactory == null) throw new ArgumentNullException(nameof(propertyFactory));
- var property = propertyFactory.CreateProperty(_name, _value, _destructureObjects);
- logEvent.AddPropertyIfAbsent(property);
+ var propertyValue = propertyFactory.CreatePropertyValue(_value, _destructureObjects);
+ logEvent.AddPropertyIfAbsent(_name, propertyValue);
}
}
\ No newline at end of file
diff --git a/src/Seq.Extensions.Logging/Serilog/Core/Enrichers/SafeAggregateEnricher.cs b/src/Seq.Extensions.Logging/Serilog/Core/Enrichers/SafeAggregateEnricher.cs
index fd8e497..8fe2098 100644
--- a/src/Seq.Extensions.Logging/Serilog/Core/Enrichers/SafeAggregateEnricher.cs
+++ b/src/Seq.Extensions.Logging/Serilog/Core/Enrichers/SafeAggregateEnricher.cs
@@ -27,7 +27,7 @@ public SafeAggregateEnricher(IEnumerable enrichers)
_enrichers = enrichers.ToArray();
}
- public void Enrich(LogEvent logEvent, ILogEventPropertyFactory propertyFactory)
+ public void Enrich(LogEvent logEvent, ILogEventPropertyValueFactory propertyFactory)
{
foreach (var enricher in _enrichers)
{
diff --git a/src/Seq.Extensions.Logging/Serilog/Core/IDestructuringPolicy.cs b/src/Seq.Extensions.Logging/Serilog/Core/IDestructuringPolicy.cs
index 8f96760..4e418f7 100644
--- a/src/Seq.Extensions.Logging/Serilog/Core/IDestructuringPolicy.cs
+++ b/src/Seq.Extensions.Logging/Serilog/Core/IDestructuringPolicy.cs
@@ -12,6 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+using System.Diagnostics.CodeAnalysis;
using Serilog.Events;
namespace Serilog.Core;
@@ -29,5 +30,5 @@ interface IDestructuringPolicy
/// Recursively apply policies to destructure additional values.
/// The destructured value, or null.
/// True if the value could be destructured under this policy.
- bool TryDestructure(object value, ILogEventPropertyValueFactory propertyValueFactory, out LogEventPropertyValue result);
+ bool TryDestructure(object value, ILogEventPropertyValueFactory propertyValueFactory, [NotNullWhen(true)] out LogEventPropertyValue? result);
}
\ No newline at end of file
diff --git a/src/Seq.Extensions.Logging/Serilog/Core/ILogEventEnricher.cs b/src/Seq.Extensions.Logging/Serilog/Core/ILogEventEnricher.cs
index 334d404..54b1af0 100644
--- a/src/Seq.Extensions.Logging/Serilog/Core/ILogEventEnricher.cs
+++ b/src/Seq.Extensions.Logging/Serilog/Core/ILogEventEnricher.cs
@@ -26,5 +26,5 @@ interface ILogEventEnricher
///
/// The log event to enrich.
/// Factory for creating new properties to add to the event.
- void Enrich(LogEvent logEvent, ILogEventPropertyFactory propertyFactory);
+ void Enrich(LogEvent logEvent, ILogEventPropertyValueFactory propertyFactory);
}
\ No newline at end of file
diff --git a/src/Seq.Extensions.Logging/Serilog/Core/ILogEventPropertyValueFactory.cs b/src/Seq.Extensions.Logging/Serilog/Core/ILogEventPropertyValueFactory.cs
index f662122..2051ed7 100644
--- a/src/Seq.Extensions.Logging/Serilog/Core/ILogEventPropertyValueFactory.cs
+++ b/src/Seq.Extensions.Logging/Serilog/Core/ILogEventPropertyValueFactory.cs
@@ -17,5 +17,5 @@ interface ILogEventPropertyValueFactory
/// then the value will be converted to a structure; otherwise, unknown types will
/// be converted to scalars, which are generally stored as strings.
/// The value.
- LogEventPropertyValue CreatePropertyValue(object value, bool destructureObjects = false);
+ LogEventPropertyValue CreatePropertyValue(object? value, bool destructureObjects = false);
}
\ No newline at end of file
diff --git a/src/Seq.Extensions.Logging/Serilog/Core/IScalarConversionPolicy.cs b/src/Seq.Extensions.Logging/Serilog/Core/IScalarConversionPolicy.cs
index 819925b..947fbce 100644
--- a/src/Seq.Extensions.Logging/Serilog/Core/IScalarConversionPolicy.cs
+++ b/src/Seq.Extensions.Logging/Serilog/Core/IScalarConversionPolicy.cs
@@ -1,4 +1,5 @@
-using Serilog.Events;
+using System.Diagnostics.CodeAnalysis;
+using Serilog.Events;
namespace Serilog.Core;
@@ -15,5 +16,5 @@ interface IScalarConversionPolicy
/// Recursively apply policies to convert additional values.
/// The converted value, or null.
/// True if the value could be converted under this policy.
- bool TryConvertToScalar(object value, ILogEventPropertyValueFactory propertyValueFactory, out ScalarValue result);
+ bool TryConvertToScalar(object value, ILogEventPropertyValueFactory propertyValueFactory, [NotNullWhen(true)] out ScalarValue? result);
}
\ No newline at end of file
diff --git a/src/Seq.Extensions.Logging/Serilog/Core/Logger.cs b/src/Seq.Extensions.Logging/Serilog/Core/Logger.cs
index 0c64b5c..cd42280 100644
--- a/src/Seq.Extensions.Logging/Serilog/Core/Logger.cs
+++ b/src/Seq.Extensions.Logging/Serilog/Core/Logger.cs
@@ -12,6 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+using System.Diagnostics.CodeAnalysis;
using Serilog.Core.Enrichers;
using Seq.Extensions.Logging;
using Serilog.Events;
@@ -29,33 +30,22 @@ namespace Serilog.Core;
///
sealed class Logger : ILogEventSink, IDisposable
{
- static readonly object[] NoPropertyValues = new object[0];
-
- readonly MessageTemplateProcessor _messageTemplateProcessor = new MessageTemplateProcessor(new PropertyValueConverter(10, int.MaxValue));
+ readonly MessageTemplateProcessor _messageTemplateProcessor = new(new PropertyValueConverter(10, int.MaxValue));
readonly ILogEventSink _sink;
- readonly Action _dispose;
+ readonly Action? _dispose;
readonly ILogEventEnricher _enricher;
- readonly LoggingLevelSwitch _levelSwitch;
- readonly LevelOverrideMap _overrideMap;
-
- internal Logger(
- LoggingLevelSwitch levelSwitch,
- ILogEventSink sink,
- Action dispose = null,
- LevelOverrideMap overrideMap = null)
- : this(sink, new ExceptionDataEnricher(), dispose, levelSwitch, overrideMap)
- {
- }
+ readonly LoggingLevelSwitch? _levelSwitch;
+ readonly LevelOverrideMap? _overrideMap;
// The messageTemplateProcessor, sink and enricher are required. Argument checks are dropped because
// throwing from here breaks the logger's no-throw contract, and callers are all in this file anyway.
- Logger(
+ internal Logger(
ILogEventSink sink,
ILogEventEnricher enricher,
- Action dispose = null,
- LoggingLevelSwitch levelSwitch = null,
- LevelOverrideMap overrideMap = null)
+ Action? dispose = null,
+ LoggingLevelSwitch? levelSwitch = null,
+ LevelOverrideMap? overrideMap = null)
{
_sink = sink;
_dispose = dispose;
@@ -71,7 +61,7 @@ internal Logger(
/// A logger that will enrich log events as specified.
public Logger ForContext(ILogEventEnricher enricher)
{
- if (enricher == null)
+ if (enricher == null!)
return this; // No context here, so little point writing to SelfLog.
return new Logger(
@@ -89,7 +79,7 @@ public Logger ForContext(ILogEventEnricher enricher)
/// A logger that will enrich log events as specified.
public Logger ForContext(IEnumerable enrichers)
{
- if (enrichers == null)
+ if (enrichers == null!)
return this; // No context here, so little point writing to SelfLog.
return ForContext(new SafeAggregateEnricher(enrichers));
@@ -103,7 +93,7 @@ public Logger ForContext(IEnumerable enrichers)
/// If true, the value will be serialized as a structured
/// object if possible; if false, the object will be recorded as a scalar or simple array.
/// A logger that will enrich log events as specified.
- public Logger ForContext(string propertyName, object value, bool destructureObjects = false)
+ public Logger ForContext(string propertyName, object? value, bool destructureObjects = false)
{
if (!LogEventProperty.IsValidName(propertyName))
{
@@ -120,8 +110,7 @@ public Logger ForContext(string propertyName, object value, bool destructureObje
var levelSwitch = _levelSwitch;
if (_overrideMap != null && propertyName == Constants.SourceContextPropertyName)
{
- var context = value as string;
- if (context != null)
+ if (value is string context)
_overrideMap.GetEffectiveLevel(context, out levelSwitch);
}
@@ -141,23 +130,12 @@ public Logger ForContext(string propertyName, object value, bool destructureObje
/// A logger that will enrich log events as specified.
public Logger ForContext(Type source)
{
- if (source == null)
+ if (source == null!)
return this; // Little point in writing to SelfLog here because we don't have any contextual information
return ForContext(Constants.SourceContextPropertyName, source.FullName);
}
- ///
- /// Create a logger that marks log events as being from the specified
- /// source type.
- ///
- /// Type generating log messages in the context.
- /// A logger that will enrich log events as specified.
- public Logger ForContext()
- {
- return ForContext(typeof(TSource));
- }
-
///
/// Determine if events at the specified level will be passed through
/// to the log sinks.
@@ -166,9 +144,7 @@ public Logger ForContext()
/// True if the level is enabled; otherwise, false.
public bool IsEnabled(LogLevel level)
{
-
- return _levelSwitch == null ||
- (int)level >= (int)_levelSwitch.MinimumLevel;
+ return level != LogLevel.None && (_levelSwitch == null || level >= _levelSwitch.MinimumLevel);
}
///
@@ -177,7 +153,7 @@ public bool IsEnabled(LogLevel level)
/// The event to write.
public void Write(LogEvent logEvent)
{
- if (logEvent == null) return;
+ if (logEvent == null!) return;
if (!IsEnabled(logEvent.Level)) return;
Dispatch(logEvent);
}
@@ -227,9 +203,9 @@ void Dispatch(LogEvent logEvent)
/// // -> "Hello, World!"
/// }
///
- public bool BindMessageTemplate(string messageTemplate, object[] propertyValues, out MessageTemplate parsedTemplate, out IEnumerable boundProperties)
+ public bool BindMessageTemplate(string messageTemplate, object?[]? propertyValues, [NotNullWhen(true)] out MessageTemplate? parsedTemplate, [NotNullWhen(true)] out IEnumerable? boundProperties)
{
- if (messageTemplate == null)
+ if (messageTemplate == null!)
{
parsedTemplate = null;
boundProperties = null;
@@ -251,7 +227,7 @@ public bool BindMessageTemplate(string messageTemplate, object[] propertyValues,
/// object if possible; if false, the object will be recorded as a scalar or simple array.
/// The resulting property.
/// methods never throw exceptions).
- public bool BindProperty(string propertyName, object value, bool destructureObjects, out LogEventProperty property)
+ public bool BindProperty(string propertyName, object? value, bool destructureObjects, [NotNullWhen(true)] out LogEventProperty? property)
{
if (!LogEventProperty.IsValidName(propertyName))
{
diff --git a/src/Seq.Extensions.Logging/Serilog/Core/LoggingLevelSwitch.cs b/src/Seq.Extensions.Logging/Serilog/Core/LoggingLevelSwitch.cs
index 06ca227..1075298 100644
--- a/src/Seq.Extensions.Logging/Serilog/Core/LoggingLevelSwitch.cs
+++ b/src/Seq.Extensions.Logging/Serilog/Core/LoggingLevelSwitch.cs
@@ -41,7 +41,7 @@ public LoggingLevelSwitch(LogLevel initialMinimumLevel = LogLevel.Information)
// so needs to be used judiciously in the logging pipeline.
public LogLevel MinimumLevel
{
- get { return _minimumLevel; }
- set { _minimumLevel = value; }
+ get => _minimumLevel;
+ set => _minimumLevel = value;
}
}
\ No newline at end of file
diff --git a/src/Seq.Extensions.Logging/Serilog/Core/Pipeline/MessageTemplateCache.cs b/src/Seq.Extensions.Logging/Serilog/Core/Pipeline/MessageTemplateCache.cs
index 743fa7d..0dd6e02 100644
--- a/src/Seq.Extensions.Logging/Serilog/Core/Pipeline/MessageTemplateCache.cs
+++ b/src/Seq.Extensions.Logging/Serilog/Core/Pipeline/MessageTemplateCache.cs
@@ -19,32 +19,27 @@ namespace Serilog.Core.Pipeline;
class MessageTemplateCache
{
- readonly MessageTemplateParser _innerParser;
- readonly Dictionary _templates = new Dictionary();
- readonly object _templatesLock = new object();
+ readonly Dictionary _templates = new();
+ readonly object _templatesLock = new();
const int MaxCacheItems = 1000;
const int MaxCachedTemplateLength = 1024;
- public MessageTemplateCache(MessageTemplateParser innerParser)
- {
- if (innerParser == null) throw new ArgumentNullException(nameof(innerParser));
- _innerParser = innerParser;
- }
public MessageTemplate Parse(string messageTemplate)
{
if (messageTemplate == null) throw new ArgumentNullException(nameof(messageTemplate));
if (messageTemplate.Length > MaxCachedTemplateLength)
- return _innerParser.Parse(messageTemplate);
+ return MessageTemplateParser.Parse(messageTemplate);
- MessageTemplate result;
lock (_templatesLock)
- if (_templates.TryGetValue(messageTemplate, out result))
- return result;
+ {
+ if (_templates.TryGetValue(messageTemplate, out var found))
+ return found;
+ }
- result = _innerParser.Parse(messageTemplate);
+ var result = MessageTemplateParser.Parse(messageTemplate);
lock (_templatesLock)
{
diff --git a/src/Seq.Extensions.Logging/Serilog/Events/DictionaryValue.cs b/src/Seq.Extensions.Logging/Serilog/Events/DictionaryValue.cs
index be73fd6..3edd6fa 100644
--- a/src/Seq.Extensions.Logging/Serilog/Events/DictionaryValue.cs
+++ b/src/Seq.Extensions.Logging/Serilog/Events/DictionaryValue.cs
@@ -42,7 +42,7 @@ public DictionaryValue(IEnumerableA format string applied to the value, or null.
/// A format provider to apply to the value, or null to use the default.
/// .
- public override void Render(TextWriter output, string format = null, IFormatProvider formatProvider = null)
+ public override void Render(TextWriter output, string? format = null, IFormatProvider? formatProvider = null)
{
if (output == null) throw new ArgumentNullException(nameof(output));
diff --git a/src/Seq.Extensions.Logging/Serilog/Events/LogEvent.cs b/src/Seq.Extensions.Logging/Serilog/Events/LogEvent.cs
index c65b50d..42f7685 100644
--- a/src/Seq.Extensions.Logging/Serilog/Events/LogEvent.cs
+++ b/src/Seq.Extensions.Logging/Serilog/Events/LogEvent.cs
@@ -23,8 +23,8 @@ namespace Serilog.Events;
class LogEvent
{
readonly Dictionary _properties;
- ActivityTraceId _traceId;
- ActivitySpanId _spanId;
+ readonly ActivityTraceId _traceId;
+ readonly ActivitySpanId _spanId;
///
/// Construct a new .
@@ -39,22 +39,19 @@ class LogEvent
public LogEvent(
DateTimeOffset timestamp,
LogLevel level,
- Exception exception,
+ Exception? exception,
MessageTemplate messageTemplate,
- IEnumerable properties,
+ Dictionary properties,
ActivityTraceId traceId,
ActivitySpanId spanId)
{
- if (properties == null) throw new ArgumentNullException(nameof(properties));
_traceId = traceId;
_spanId = spanId;
Timestamp = timestamp;
Level = level;
Exception = exception;
- MessageTemplate = messageTemplate ?? throw new ArgumentNullException(nameof(messageTemplate));
- _properties = new Dictionary();
- foreach (var p in properties)
- AddOrUpdateProperty(p);
+ MessageTemplate = messageTemplate;
+ _properties = properties;
}
///
@@ -88,7 +85,7 @@ public LogEvent(
///
/// The output.
/// Supplies culture-specific formatting information, or null.
- public void RenderMessage(TextWriter output, IFormatProvider formatProvider = null)
+ public void RenderMessage(TextWriter output, IFormatProvider? formatProvider = null)
{
MessageTemplate.Render(Properties, output, formatProvider);
}
@@ -98,7 +95,7 @@ public void RenderMessage(TextWriter output, IFormatProvider formatProvider = nu
/// with the event, and return the result.
///
/// Supplies culture-specific formatting information, or null.
- public string RenderMessage(IFormatProvider formatProvider = null)
+ public string RenderMessage(IFormatProvider? formatProvider = null)
{
return MessageTemplate.Render(Properties, formatProvider);
}
@@ -111,38 +108,24 @@ public string RenderMessage(IFormatProvider formatProvider = null)
///
/// An exception associated with the event, or null.
///
- public Exception Exception { get; }
+ public Exception? Exception { get; }
///
/// Add a property to the event if not already present, otherwise, update its value.
///
- /// The property to add or update.
///
- public void AddOrUpdateProperty(LogEventProperty property)
+ public void AddOrUpdateProperty(string propertyName, LogEventPropertyValue propertyValue)
{
- if (property == null) throw new ArgumentNullException(nameof(property));
- _properties[property.Name] = property.Value;
+ _properties[propertyName] = propertyValue;
}
///
/// Add a property to the event if not already present.
///
- /// The property to add.
///
- public void AddPropertyIfAbsent(LogEventProperty property)
+ public void AddPropertyIfAbsent(string propertyName, LogEventPropertyValue propertyValue)
{
- if (property == null) throw new ArgumentNullException(nameof(property));
- if (!_properties.ContainsKey(property.Name))
- _properties.Add(property.Name, property.Value);
- }
-
- ///
- /// Remove a property from the event, if present. Otherwise no action
- /// is performed.
- ///
- /// The name of the property to remove.
- public void RemovePropertyIfPresent(string propertyName)
- {
- _properties.Remove(propertyName);
+ if (!_properties.ContainsKey(propertyName))
+ _properties.Add(propertyName, propertyValue);
}
}
diff --git a/src/Seq.Extensions.Logging/Serilog/Events/LogEventPropertyValue.cs b/src/Seq.Extensions.Logging/Serilog/Events/LogEventPropertyValue.cs
index 98c9e51..17e9df5 100644
--- a/src/Seq.Extensions.Logging/Serilog/Events/LogEventPropertyValue.cs
+++ b/src/Seq.Extensions.Logging/Serilog/Events/LogEventPropertyValue.cs
@@ -27,7 +27,7 @@ abstract class LogEventPropertyValue : IFormattable
/// A format string applied to the value, or null.
/// A format provider to apply to the value, or null to use the default.
/// .
- public abstract void Render(TextWriter output, string format = null, IFormatProvider formatProvider = null);
+ public abstract void Render(TextWriter output, string? format = null, IFormatProvider? formatProvider = null);
///
/// Returns a string that represents the current object.
@@ -52,7 +52,7 @@ public override string ToString()
/// The provider to use to format the value.-or- A null reference
/// (Nothing in Visual Basic) to obtain the numeric format information from the current locale
/// setting of the operating system. 2
- public string ToString(string format, IFormatProvider formatProvider)
+ public string ToString(string? format, IFormatProvider? formatProvider)
{
var output = new StringWriter();
Render(output, format, formatProvider);
diff --git a/src/Seq.Extensions.Logging/Serilog/Events/MessageTemplate.cs b/src/Seq.Extensions.Logging/Serilog/Events/MessageTemplate.cs
index 6c73bf3..68fcb0e 100644
--- a/src/Seq.Extensions.Logging/Serilog/Events/MessageTemplate.cs
+++ b/src/Seq.Extensions.Logging/Serilog/Events/MessageTemplate.cs
@@ -29,15 +29,6 @@ class MessageTemplate
// Optimisation for when the template is bound to
// property values.
- ///
- /// Construct a message template using manually-defined text and property tokens.
- ///
- /// The text and property tokens defining the template.
- public MessageTemplate(IEnumerable tokens)
- : this(string.Join("", tokens), tokens)
- {
- }
-
///
/// Construct a message template using manually-defined text and property tokens.
///
@@ -46,10 +37,9 @@ public MessageTemplate(IEnumerable tokens)
/// The text and property tokens defining the template.
public MessageTemplate(string text, IEnumerable tokens)
{
- if (text == null) throw new ArgumentNullException(nameof(text));
if (tokens == null) throw new ArgumentNullException(nameof(tokens));
- Text = text;
+ Text = text ?? throw new ArgumentNullException(nameof(text));
_tokens = tokens.ToArray();
var propertyTokens = GetElementsOfTypeToArray(_tokens);
@@ -82,14 +72,13 @@ public MessageTemplate(string text, IEnumerable tokens)
///
/// Similar to , but faster.
///
- static TResult[] GetElementsOfTypeToArray(object[] tokens)
+ static TResult[] GetElementsOfTypeToArray(MessageTemplateToken[] tokens)
where TResult : class
{
var result = new List(tokens.Length / 2);
for (var i = 0; i < tokens.Length; i++)
{
- var token = tokens[i] as TResult;
- if (token != null)
+ if (tokens[i] is TResult token)
{
result.Add(token);
}
@@ -116,9 +105,9 @@ public override string ToString()
///
public IEnumerable Tokens => _tokens;
- internal PropertyToken[] NamedProperties { get; }
+ internal PropertyToken[]? NamedProperties { get; }
- internal PropertyToken[] PositionalProperties { get; }
+ internal PropertyToken[]? PositionalProperties { get; }
///
/// Convert the message template into a textual message, given the
@@ -129,7 +118,7 @@ public override string ToString()
/// The message created from the template and properties. If the
/// properties are mismatched with the template, the template will be
/// returned with incomplete substitution.
- public string Render(IReadOnlyDictionary properties, IFormatProvider formatProvider = null)
+ public string Render(IReadOnlyDictionary properties, IFormatProvider? formatProvider = null)
{
var writer = new StringWriter(formatProvider);
Render(properties, writer, formatProvider);
@@ -145,7 +134,7 @@ public string Render(IReadOnlyDictionary properti
/// properties are mismatched with the template, the template will be
/// returned with incomplete substitution.
/// Supplies culture-specific formatting information, or null.
- public void Render(IReadOnlyDictionary properties, TextWriter output, IFormatProvider formatProvider = null)
+ public void Render(IReadOnlyDictionary properties, TextWriter output, IFormatProvider? formatProvider = null)
{
foreach (var token in _tokens)
{
diff --git a/src/Seq.Extensions.Logging/Serilog/Events/ScalarValue.cs b/src/Seq.Extensions.Logging/Serilog/Events/ScalarValue.cs
index a67285f..679504e 100644
--- a/src/Seq.Extensions.Logging/Serilog/Events/ScalarValue.cs
+++ b/src/Seq.Extensions.Logging/Serilog/Events/ScalarValue.cs
@@ -26,7 +26,7 @@ class ScalarValue : LogEventPropertyValue
/// value.
///
/// The value, which may be null.
- public ScalarValue(object value)
+ public ScalarValue(object? value)
{
Value = value;
}
@@ -34,7 +34,7 @@ public ScalarValue(object value)
///
/// The value, which may be null.
///
- public object Value { get; }
+ public object? Value { get; }
///
/// Render the value to the output.
@@ -43,7 +43,7 @@ public ScalarValue(object value)
/// A format string applied to the value, or null.
/// A format provider to apply to the value, or null to use the default.
/// .
- public override void Render(TextWriter output, string format = null, IFormatProvider formatProvider = null)
+ public override void Render(TextWriter output, string? format = null, IFormatProvider? formatProvider = null)
{
if (output == null) throw new ArgumentNullException(nameof(output));
@@ -53,8 +53,7 @@ public override void Render(TextWriter output, string format = null, IFormatProv
return;
}
- var s = Value as string;
- if (s != null)
+ if (Value is string s)
{
if (format != "l")
{
@@ -69,18 +68,14 @@ public override void Render(TextWriter output, string format = null, IFormatProv
return;
}
- if (formatProvider != null)
+ var custom = (ICustomFormatter?)formatProvider?.GetFormat(typeof(ICustomFormatter));
+ if (custom != null)
{
- var custom = (ICustomFormatter)formatProvider.GetFormat(typeof(ICustomFormatter));
- if (custom != null)
- {
- output.Write(custom.Format(format, Value, formatProvider));
- return;
- }
+ output.Write(custom.Format(format, Value, formatProvider));
+ return;
}
- var f = Value as IFormattable;
- if (f != null)
+ if (Value is IFormattable f)
{
output.Write(f.ToString(format, formatProvider ?? CultureInfo.InvariantCulture));
}
@@ -95,7 +90,7 @@ public override void Render(TextWriter output, string format = null, IFormatProv
///
/// The instance to compare with.
/// True if the instances are equal; otherwise, false.
- public override bool Equals(object obj)
+ public override bool Equals(object? obj)
{
var sv = obj as ScalarValue;
if (sv == null) return false;
diff --git a/src/Seq.Extensions.Logging/Serilog/Events/SequenceValue.cs b/src/Seq.Extensions.Logging/Serilog/Events/SequenceValue.cs
index 94cfdde..f066928 100644
--- a/src/Seq.Extensions.Logging/Serilog/Events/SequenceValue.cs
+++ b/src/Seq.Extensions.Logging/Serilog/Events/SequenceValue.cs
@@ -44,7 +44,7 @@ public SequenceValue(IEnumerable elements)
/// A format string applied to the value, or null.
/// A format provider to apply to the value, or null to use the default.
/// .
- public override void Render(TextWriter output, string format = null, IFormatProvider formatProvider = null)
+ public override void Render(TextWriter output, string? format = null, IFormatProvider? formatProvider = null)
{
if (output == null) throw new ArgumentNullException(nameof(output));
diff --git a/src/Seq.Extensions.Logging/Serilog/Events/StructureValue.cs b/src/Seq.Extensions.Logging/Serilog/Events/StructureValue.cs
index 978874c..f56211b 100644
--- a/src/Seq.Extensions.Logging/Serilog/Events/StructureValue.cs
+++ b/src/Seq.Extensions.Logging/Serilog/Events/StructureValue.cs
@@ -28,7 +28,7 @@ class StructureValue : LogEventPropertyValue
/// structure.
/// The properties of the structure.
///
- public StructureValue(IEnumerable properties, string typeTag = null)
+ public StructureValue(IEnumerable properties, string? typeTag = null)
{
if (properties == null) throw new ArgumentNullException(nameof(properties));
TypeTag = typeTag;
@@ -39,7 +39,7 @@ public StructureValue(IEnumerable properties, string typeTag =
/// A piece of metadata describing the "type" of the
/// structure, or null.
///
- public string TypeTag { get; }
+ public string? TypeTag { get; }
///
/// The properties of the structure.
@@ -56,7 +56,7 @@ public StructureValue(IEnumerable properties, string typeTag =
/// A format string applied to the value, or null.
/// A format provider to apply to the value, or null to use the default.
/// .
- public override void Render(TextWriter output, string format = null, IFormatProvider formatProvider = null)
+ public override void Render(TextWriter output, string? format = null, IFormatProvider? formatProvider = null)
{
if (output == null) throw new ArgumentNullException(nameof(output));
@@ -83,7 +83,7 @@ public override void Render(TextWriter output, string format = null, IFormatProv
output.Write(" }");
}
- static void Render(TextWriter output, LogEventProperty property, IFormatProvider formatProvider = null)
+ static void Render(TextWriter output, LogEventProperty property, IFormatProvider? formatProvider = null)
{
output.Write(property.Name);
output.Write(": ");
diff --git a/src/Seq.Extensions.Logging/Serilog/Extensions/Logging/SerilogLogger.cs b/src/Seq.Extensions.Logging/Serilog/Extensions/Logging/SerilogLogger.cs
index 4405aee..a43a449 100644
--- a/src/Seq.Extensions.Logging/Serilog/Extensions/Logging/SerilogLogger.cs
+++ b/src/Seq.Extensions.Logging/Serilog/Extensions/Logging/SerilogLogger.cs
@@ -9,7 +9,6 @@
using System.Reflection;
using Serilog.Parsing;
-#nullable enable
// ReSharper disable ConditionIsAlwaysTrueOrFalse
// ReSharper disable ConditionIsAlwaysTrueOrFalseAccordingToNullableAPIContract
// ReSharper disable ConstantNullCoalescingCondition
@@ -21,8 +20,6 @@ class SerilogLogger : FrameworkLogger
readonly SerilogLoggerProvider _provider;
readonly Logger _logger;
- static readonly MessageTemplateParser MessageTemplateParser = new();
-
public SerilogLogger(
SerilogLoggerProvider provider,
Logger logger,
@@ -31,7 +28,7 @@ public SerilogLogger(
if (logger == null) throw new ArgumentNullException(nameof(logger));
_provider = provider ?? throw new ArgumentNullException(nameof(provider));
- _logger = logger.ForContext(new[] { provider });
+ _logger = logger.ForContext([provider]);
if (name != null)
{
@@ -56,10 +53,9 @@ public void Log(LogLevel logLevel, EventId eventId, TState state, Except
return;
}
- var logger = _logger;
string? messageTemplate = null;
- var properties = new List();
+ var properties = new Dictionary();
if (state is IEnumerable> structure)
{
@@ -71,13 +67,13 @@ public void Log(LogLevel logLevel, EventId eventId, TState state, Except
}
else if (property.Key.StartsWith("@"))
{
- if (logger.BindProperty(property.Key.Substring(1), property.Value, true, out var destructured))
- properties.Add(destructured);
+ if (_logger.BindProperty(property.Key.Substring(1), property.Value, true, out var destructured))
+ properties.Add(destructured.Name, destructured.Value);
}
else
{
- if (logger.BindProperty(property.Key, property.Value, false, out var bound))
- properties.Add(bound);
+ if (_logger.BindProperty(property.Key, property.Value, false, out var bound))
+ properties.Add(bound.Name, bound.Value);
}
}
@@ -87,8 +83,8 @@ public void Log(LogLevel logLevel, EventId eventId, TState state, Except
if (messageTemplate == null && !stateTypeInfo.IsGenericType)
{
messageTemplate = "{" + stateType.Name + ":l}";
- if (logger.BindProperty(stateType.Name, AsLoggableValue(state, formatter), false, out var stateTypeProperty))
- properties.Add(stateTypeProperty);
+ if (_logger.BindProperty(stateType.Name, AsLoggableValue(state, formatter), false, out var stateTypeProperty))
+ properties.Add(stateTypeProperty.Name, stateTypeProperty.Value);
}
}
@@ -108,18 +104,18 @@ public void Log(LogLevel logLevel, EventId eventId, TState state, Except
if (propertyName != null)
{
- if (logger.BindProperty(propertyName, AsLoggableValue(state, formatter!), false, out var property))
- properties.Add(property);
+ if (_logger.BindProperty(propertyName, AsLoggableValue(state, formatter!), false, out var property))
+ properties.Add(property.Name, property.Value);
}
}
if (eventId.Id != 0 || eventId.Name != null)
- properties.Add(CreateEventIdProperty(eventId));
+ properties["EventId"] = CreateEventIdPropertyValue(eventId);
var parsedTemplate = MessageTemplateParser.Parse(messageTemplate ?? "");
var currentActivity = Activity.Current;
var evt = new LogEvent(DateTimeOffset.Now, logLevel, exception, parsedTemplate, properties, currentActivity?.TraceId ?? default, currentActivity?.SpanId ?? default);
- logger.Write(evt);
+ _logger.Write(evt);
}
static object? AsLoggableValue(TState state, Func formatter)
@@ -130,7 +126,7 @@ public void Log(LogLevel logLevel, EventId eventId, TState state, Except
return stateObject;
}
- static LogEventProperty CreateEventIdProperty(EventId eventId)
+ static LogEventPropertyValue CreateEventIdPropertyValue(EventId eventId)
{
var properties = new List(2);
@@ -144,6 +140,6 @@ static LogEventProperty CreateEventIdProperty(EventId eventId)
properties.Add(new LogEventProperty("Name", new ScalarValue(eventId.Name)));
}
- return new LogEventProperty("EventId", new StructureValue(properties));
+ return new StructureValue(properties);
}
}
\ No newline at end of file
diff --git a/src/Seq.Extensions.Logging/Serilog/Extensions/Logging/SerilogLoggerProvider.cs b/src/Seq.Extensions.Logging/Serilog/Extensions/Logging/SerilogLoggerProvider.cs
index 851f263..13a3080 100644
--- a/src/Seq.Extensions.Logging/Serilog/Extensions/Logging/SerilogLoggerProvider.cs
+++ b/src/Seq.Extensions.Logging/Serilog/Extensions/Logging/SerilogLoggerProvider.cs
@@ -20,7 +20,7 @@ class SerilogLoggerProvider : ILoggerProvider, ILogEventEnricher, ISupportExtern
readonly Logger _logger;
readonly Action _dispose;
- IExternalScopeProvider _scopeProvider;
+ IExternalScopeProvider? _scopeProvider;
///
/// Construct a .
@@ -32,7 +32,7 @@ public SerilogLoggerProvider(Logger logger)
throw new ArgumentNullException(nameof(logger));
_dispose = logger.Dispose;
- _logger = logger.ForContext(new[] { this });
+ _logger = logger.ForContext([this]);
}
///
@@ -41,13 +41,13 @@ public FrameworkLogger CreateLogger(string name)
return new SerilogLogger(this, _logger, name);
}
- public IDisposable BeginScope(T state)
+ public IDisposable? BeginScope(T state)
{
return _scopeProvider?.Push(state);
}
///
- public void Enrich(LogEvent logEvent, ILogEventPropertyFactory propertyFactory)
+ public void Enrich(LogEvent logEvent, ILogEventPropertyValueFactory propertyFactory)
{
var scopeItems = new List();
@@ -63,7 +63,7 @@ public void Enrich(LogEvent logEvent, ILogEventPropertyFactory propertyFactory)
if (scopeItems.Count > 0)
{
- logEvent.AddPropertyIfAbsent(new LogEventProperty(ScopePropertyName, new SequenceValue(scopeItems)));
+ logEvent.AddPropertyIfAbsent(ScopePropertyName, new SequenceValue(scopeItems));
}
}
diff --git a/src/Seq.Extensions.Logging/Serilog/Extensions/Logging/SerilogLoggerScope.cs b/src/Seq.Extensions.Logging/Serilog/Extensions/Logging/SerilogLoggerScope.cs
index 17133d9..3cb5a64 100644
--- a/src/Seq.Extensions.Logging/Serilog/Extensions/Logging/SerilogLoggerScope.cs
+++ b/src/Seq.Extensions.Logging/Serilog/Extensions/Logging/SerilogLoggerScope.cs
@@ -9,9 +9,7 @@ namespace Serilog.Extensions.Logging;
static class SerilogLoggerScope
{
- const string NoName = "None";
-
- public static void EnrichAndCreateScopeItem(object state, LogEvent logEvent, ILogEventPropertyFactory propertyFactory, out LogEventPropertyValue scopeItem)
+ public static void EnrichAndCreateScopeItem(object? state, LogEvent logEvent, ILogEventPropertyValueFactory propertyFactory, out LogEventPropertyValue? scopeItem)
{
if (state == null)
{
@@ -39,13 +37,13 @@ public static void EnrichAndCreateScopeItem(object state, LogEvent logEvent, ILo
destructureObject = true;
}
- var property = propertyFactory.CreateProperty(key, stateProperty.Value, destructureObject);
- logEvent.AddOrUpdateProperty(property);
+ var property = propertyFactory.CreatePropertyValue(stateProperty.Value, destructureObject);
+ logEvent.AddOrUpdateProperty(key, property);
}
}
else
{
- scopeItem = propertyFactory.CreateProperty(NoName, state).Value;
+ scopeItem = propertyFactory.CreatePropertyValue(state);
}
}
}
\ No newline at end of file
diff --git a/src/Seq.Extensions.Logging/Serilog/Formatting/Json/JsonValueFormatter.cs b/src/Seq.Extensions.Logging/Serilog/Formatting/Json/JsonValueFormatter.cs
index a5ea602..baaf155 100644
--- a/src/Seq.Extensions.Logging/Serilog/Formatting/Json/JsonValueFormatter.cs
+++ b/src/Seq.Extensions.Logging/Serilog/Formatting/Json/JsonValueFormatter.cs
@@ -25,7 +25,7 @@ namespace Serilog.Formatting.Json;
///
class JsonValueFormatter : LogEventPropertyValueVisitor
{
- readonly string _typeTagName;
+ readonly string? _typeTagName;
const string DefaultTypeTagName = "_typeTag";
@@ -36,7 +36,7 @@ class JsonValueFormatter : LogEventPropertyValueVisitor
/// the property name to use for the Serilog field
/// in the resulting JSON. If null, no type tag field will be written. The default is
/// "_typeTag".
- public JsonValueFormatter(string typeTagName = DefaultTypeTagName)
+ public JsonValueFormatter(string? typeTagName = DefaultTypeTagName)
{
_typeTagName = typeTagName;
}
@@ -136,7 +136,7 @@ protected override bool VisitDictionaryValue(TextWriter state, DictionaryValue d
{
state.Write(delim);
delim = ",";
- WriteQuotedJsonString((element.Key.Value ?? "null").ToString(), state);
+ WriteQuotedJsonString((element.Key.Value ?? "null").ToString()!, state);
state.Write(':');
Visit(state, element.Value);
}
@@ -152,7 +152,7 @@ protected override bool VisitDictionaryValue(TextWriter state, DictionaryValue d
///
/// The value to write.
/// The output
- static void FormatLiteralValue(object value, TextWriter output)
+ static void FormatLiteralValue(object? value, TextWriter output)
{
if (value == null)
{
@@ -200,7 +200,7 @@ static void FormatLiteralValue(object value, TextWriter output)
if (value is char)
{
- FormatStringValue(value.ToString(), output);
+ FormatStringValue(value.ToString()!, output);
return;
}
@@ -274,7 +274,7 @@ static void FormatTimeSpanValue(TimeSpan value, TextWriter output)
static void FormatLiteralObjectValue(object value, TextWriter output)
{
if (value == null) throw new ArgumentNullException(nameof(value));
- FormatStringValue(value.ToString(), output);
+ FormatStringValue(value.ToString()!, output);
}
static void FormatStringValue(string str, TextWriter output)
diff --git a/src/Seq.Extensions.Logging/Serilog/Parameters/DepthLimiter.cs b/src/Seq.Extensions.Logging/Serilog/Parameters/DepthLimiter.cs
index a51eeda..c11788d 100644
--- a/src/Seq.Extensions.Logging/Serilog/Parameters/DepthLimiter.cs
+++ b/src/Seq.Extensions.Logging/Serilog/Parameters/DepthLimiter.cs
@@ -34,19 +34,19 @@ public DepthLimiter(int currentDepth, int maximumDepth, PropertyValueConverter p
_propertyValueConverter = propertyValueConverter;
}
- public LogEventPropertyValue CreatePropertyValue(object value, Destructuring destructuring)
+ public LogEventPropertyValue CreatePropertyValue(object? value, Destructuring destructuring)
{
return DefaultIfMaximumDepth() ??
_propertyValueConverter.CreatePropertyValue(value, destructuring, _currentDepth + 1);
}
- public LogEventPropertyValue CreatePropertyValue(object value, bool destructureObjects = false)
+ public LogEventPropertyValue CreatePropertyValue(object? value, bool destructureObjects = false)
{
return DefaultIfMaximumDepth() ??
_propertyValueConverter.CreatePropertyValue(value, destructureObjects, _currentDepth + 1);
}
- LogEventPropertyValue DefaultIfMaximumDepth()
+ LogEventPropertyValue? DefaultIfMaximumDepth()
{
if (_currentDepth == _maximumDestructuringDepth)
{
diff --git a/src/Seq.Extensions.Logging/Serilog/Parameters/GetablePropertyFinder.cs b/src/Seq.Extensions.Logging/Serilog/Parameters/GettablePropertyFinder.cs
similarity index 88%
rename from src/Seq.Extensions.Logging/Serilog/Parameters/GetablePropertyFinder.cs
rename to src/Seq.Extensions.Logging/Serilog/Parameters/GettablePropertyFinder.cs
index d192f0a..d392a05 100644
--- a/src/Seq.Extensions.Logging/Serilog/Parameters/GetablePropertyFinder.cs
+++ b/src/Seq.Extensions.Logging/Serilog/Parameters/GettablePropertyFinder.cs
@@ -16,7 +16,7 @@
namespace Serilog.Parameters;
-static class GetablePropertyFinder
+static class GettablePropertyFinder
{
internal static IEnumerable GetPropertiesRecursive(this Type type)
{
@@ -27,7 +27,7 @@ internal static IEnumerable GetPropertiesRecursive(this Type type)
while (currentTypeInfo.AsType() != typeof(object))
{
var unseenProperties = currentTypeInfo.DeclaredProperties.Where(p => p.CanRead &&
- p.GetMethod.IsPublic && !p.GetMethod.IsStatic &&
+ p.GetMethod!.IsPublic && !p.GetMethod.IsStatic &&
(p.Name != "Item" || p.GetIndexParameters().Length == 0) && !seenNames.Contains(p.Name));
foreach (var propertyInfo in unseenProperties)
@@ -36,7 +36,7 @@ internal static IEnumerable GetPropertiesRecursive(this Type type)
yield return propertyInfo;
}
- currentTypeInfo = currentTypeInfo.BaseType.GetTypeInfo();
+ currentTypeInfo = currentTypeInfo.BaseType!.GetTypeInfo();
}
}
}
\ No newline at end of file
diff --git a/src/Seq.Extensions.Logging/Serilog/Parameters/MessageTemplateProcessor.cs b/src/Seq.Extensions.Logging/Serilog/Parameters/MessageTemplateProcessor.cs
index cd37220..f982ebd 100644
--- a/src/Seq.Extensions.Logging/Serilog/Parameters/MessageTemplateProcessor.cs
+++ b/src/Seq.Extensions.Logging/Serilog/Parameters/MessageTemplateProcessor.cs
@@ -15,13 +15,12 @@
using Serilog.Core;
using Serilog.Core.Pipeline;
using Serilog.Events;
-using Serilog.Parsing;
namespace Serilog.Parameters;
-class MessageTemplateProcessor : ILogEventPropertyFactory
+class MessageTemplateProcessor : ILogEventPropertyFactory, ILogEventPropertyValueFactory
{
- readonly MessageTemplateCache _parser = new MessageTemplateCache(new MessageTemplateParser());
+ readonly MessageTemplateCache _parser = new();
readonly PropertyBinder _propertyBinder;
readonly PropertyValueConverter _propertyValueConverter;
@@ -31,13 +30,22 @@ public MessageTemplateProcessor(PropertyValueConverter propertyValueConverter)
_propertyBinder = new PropertyBinder(_propertyValueConverter);
}
- public void Process(string messageTemplate, object[] messageTemplateParameters, out MessageTemplate parsedTemplate, out IEnumerable properties)
+ public void Process(
+ string messageTemplate,
+ object?[]? messageTemplateParameters,
+ out MessageTemplate parsedTemplate,
+ out IEnumerable properties)
{
parsedTemplate = _parser.Parse(messageTemplate);
properties = _propertyBinder.ConstructProperties(parsedTemplate, messageTemplateParameters);
}
- public LogEventProperty CreateProperty(string name, object value, bool destructureObjects = false)
+ public LogEventPropertyValue CreatePropertyValue(object? value, bool destructureObjects = false)
+ {
+ return _propertyValueConverter.CreatePropertyValue(value, destructureObjects);
+ }
+
+ public LogEventProperty CreateProperty(string name, object? value, bool destructureObjects = false)
{
return _propertyValueConverter.CreateProperty(name, value, destructureObjects);
}
diff --git a/src/Seq.Extensions.Logging/Serilog/Parameters/PropertyBinder.cs b/src/Seq.Extensions.Logging/Serilog/Parameters/PropertyBinder.cs
index 2d74cbb..aa99d49 100644
--- a/src/Seq.Extensions.Logging/Serilog/Parameters/PropertyBinder.cs
+++ b/src/Seq.Extensions.Logging/Serilog/Parameters/PropertyBinder.cs
@@ -23,7 +23,7 @@ class PropertyBinder
{
readonly PropertyValueConverter _valueConverter;
- static readonly LogEventProperty[] NoProperties = new LogEventProperty[0];
+ static readonly LogEventProperty[] NoProperties = [];
public PropertyBinder(PropertyValueConverter valueConverter)
{
@@ -38,7 +38,7 @@ public PropertyBinder(PropertyValueConverter valueConverter)
/// represented in the message template.
/// A list of properties; if the template is malformed then
/// this will be empty.
- public IEnumerable ConstructProperties(MessageTemplate messageTemplate, object[] messageTemplateParameters)
+ public IEnumerable ConstructProperties(MessageTemplate messageTemplate, object?[]? messageTemplateParameters)
{
if (messageTemplateParameters == null || messageTemplateParameters.Length == 0)
{
@@ -54,18 +54,17 @@ public IEnumerable ConstructProperties(MessageTemplate message
return ConstructNamedProperties(messageTemplate, messageTemplateParameters);
}
- IEnumerable ConstructPositionalProperties(MessageTemplate template, object[] messageTemplateParameters)
+ IEnumerable ConstructPositionalProperties(MessageTemplate template, object?[] messageTemplateParameters)
{
var positionalProperties = template.PositionalProperties;
- if (positionalProperties.Length != messageTemplateParameters.Length)
+ if (positionalProperties!.Length != messageTemplateParameters.Length)
SelfLog.WriteLine("Positional property count does not match parameter count: {0}", template);
- var result = new LogEventProperty[messageTemplateParameters.Length];
+ var result = new LogEventProperty?[messageTemplateParameters.Length];
foreach (var property in positionalProperties)
{
- int position;
- if (property.TryGetPositionalValue(out position))
+ if (property.TryGetPositionalValue(out var position))
{
if (position < 0 || position >= messageTemplateParameters.Length)
SelfLog.WriteLine("Unassigned positional value {0} in: {1}", position, template);
@@ -87,14 +86,14 @@ IEnumerable ConstructPositionalProperties(MessageTemplate temp
if (next != result.Length)
Array.Resize(ref result, next);
- return result;
+ return result!;
}
- IEnumerable ConstructNamedProperties(MessageTemplate template, object[] messageTemplateParameters)
+ IEnumerable ConstructNamedProperties(MessageTemplate template, object?[] messageTemplateParameters)
{
var namedProperties = template.NamedProperties;
if (namedProperties == null)
- return Enumerable.Empty();
+ return [];
var matchedRun = namedProperties.Length;
if (namedProperties.Length != messageTemplateParameters.Length)
@@ -106,7 +105,7 @@ IEnumerable ConstructNamedProperties(MessageTemplate template,
var result = new LogEventProperty[messageTemplateParameters.Length];
for (var i = 0; i < matchedRun; ++i)
{
- var property = template.NamedProperties[i];
+ var property = template.NamedProperties![i];
var value = messageTemplateParameters[i];
result[i] = ConstructProperty(property, value);
}
@@ -119,7 +118,7 @@ IEnumerable ConstructNamedProperties(MessageTemplate template,
return result;
}
- LogEventProperty ConstructProperty(PropertyToken propertyToken, object value)
+ LogEventProperty ConstructProperty(PropertyToken propertyToken, object? value)
{
return new LogEventProperty(
propertyToken.PropertyName,
diff --git a/src/Seq.Extensions.Logging/Serilog/Parameters/PropertyValueConverter.cs b/src/Seq.Extensions.Logging/Serilog/Parameters/PropertyValueConverter.cs
index cc59350..069afab 100644
--- a/src/Seq.Extensions.Logging/Serilog/Parameters/PropertyValueConverter.cs
+++ b/src/Seq.Extensions.Logging/Serilog/Parameters/PropertyValueConverter.cs
@@ -29,8 +29,8 @@ namespace Serilog.Parameters;
// writing a log event (roughly) in control of the cost of recording that event.
partial class PropertyValueConverter : ILogEventPropertyFactory, ILogEventPropertyValueFactory
{
- readonly HashSet _builtInScalarTypes = new()
- {
+ readonly HashSet _builtInScalarTypes =
+ [
typeof(bool),
typeof(char),
typeof(byte), typeof(short), typeof(ushort), typeof(int), typeof(uint),
@@ -38,12 +38,12 @@ partial class PropertyValueConverter : ILogEventPropertyFactory, ILogEventProper
typeof(string),
typeof(DateTime), typeof(DateTimeOffset), typeof(TimeSpan),
typeof(Guid), typeof(Uri)
- };
+ ];
- static IEnumerable SeqExtensionsLoggingScalarTypes() => new[]
- {
+ static IEnumerable SeqExtensionsLoggingScalarTypes() =>
+ [
typeof(JsonSafeString)
- };
+ ];
readonly IDestructuringPolicy[] _destructuringPolicies;
readonly IScalarConversionPolicy[] _scalarConversionPolicies;
@@ -62,32 +62,32 @@ public PropertyValueConverter(
_propagateExceptions = false;
_maximumStringLength = maximumStringLength;
- _scalarConversionPolicies = new IScalarConversionPolicy[]
- {
+ _scalarConversionPolicies =
+ [
new SimpleScalarConversionPolicy(_builtInScalarTypes.Concat(SeqExtensionsLoggingScalarTypes())),
new NullableScalarConversionPolicy(),
new EnumScalarConversionPolicy(),
- new ByteArrayScalarConversionPolicy(),
- };
+ new ByteArrayScalarConversionPolicy()
+ ];
- _destructuringPolicies = new IDestructuringPolicy[]
- {
+ _destructuringPolicies =
+ [
new DelegateDestructuringPolicy(),
new ReflectionTypesScalarDestructuringPolicy()
- };
+ ];
}
- public LogEventProperty CreateProperty(string name, object value, bool destructureObjects = false)
+ public LogEventProperty CreateProperty(string name, object? value, bool destructureObjects = false)
{
return new LogEventProperty(name, CreatePropertyValue(value, destructureObjects));
}
- public LogEventPropertyValue CreatePropertyValue(object value, bool destructureObjects = false)
+ public LogEventPropertyValue CreatePropertyValue(object? value, bool destructureObjects = false)
{
return CreatePropertyValue(value, destructureObjects, 1);
}
- public LogEventPropertyValue CreatePropertyValue(object value, Destructuring destructuring)
+ public LogEventPropertyValue CreatePropertyValue(object? value, Destructuring destructuring)
{
try
{
@@ -104,7 +104,7 @@ public LogEventPropertyValue CreatePropertyValue(object value, Destructuring des
}
}
- LogEventPropertyValue CreatePropertyValue(object value, bool destructureObjects, int depth)
+ LogEventPropertyValue CreatePropertyValue(object? value, bool destructureObjects, int depth)
{
return CreatePropertyValue(
value,
@@ -114,7 +114,7 @@ LogEventPropertyValue CreatePropertyValue(object value, bool destructureObjects,
depth);
}
- LogEventPropertyValue CreatePropertyValue(object value, Destructuring destructuring, int depth)
+ LogEventPropertyValue CreatePropertyValue(object? value, Destructuring destructuring, int depth)
{
if (value == null)
return new ScalarValue(null);
@@ -129,8 +129,7 @@ LogEventPropertyValue CreatePropertyValue(object value, Destructuring destructur
if (destructuring == Destructuring.Destructure)
{
- var stringValue = value as string;
- if (stringValue != null)
+ if (value is string stringValue)
{
value = TruncateIfNecessary(stringValue);
}
@@ -138,8 +137,7 @@ LogEventPropertyValue CreatePropertyValue(object value, Destructuring destructur
foreach (var scalarConversionPolicy in _scalarConversionPolicies)
{
- ScalarValue converted;
- if (scalarConversionPolicy.TryConvertToScalar(value, limiter, out converted))
+ if (scalarConversionPolicy.TryConvertToScalar(value, limiter, out var converted))
return converted;
}
@@ -147,14 +145,12 @@ LogEventPropertyValue CreatePropertyValue(object value, Destructuring destructur
{
foreach (var destructuringPolicy in _destructuringPolicies)
{
- LogEventPropertyValue result;
- if (destructuringPolicy.TryDestructure(value, limiter, out result))
+ if (destructuringPolicy.TryDestructure(value, limiter, out var result))
return result;
}
}
- var enumerable = value as IEnumerable;
- if (enumerable != null)
+ if (value is IEnumerable enumerable)
{
// Only dictionaries with 'scalar' keys are permitted, as
// more complex keys may not serialize to unique values for
@@ -167,10 +163,10 @@ LogEventPropertyValue CreatePropertyValue(object value, Destructuring destructur
if (IsValueTypeDictionary(valueType))
{
var typeInfo = typeof(KeyValuePair<,>).MakeGenericType(valueType.GenericTypeArguments).GetTypeInfo();
- var keyProperty = typeInfo.GetDeclaredProperty("Key");
- var valueProperty = typeInfo.GetDeclaredProperty("Value");
+ var keyProperty = typeInfo.GetDeclaredProperty("Key")!;
+ var valueProperty = typeInfo.GetDeclaredProperty("Value")!;
- return new DictionaryValue(enumerable.Cast
-class MessageTemplateParser
+static class MessageTemplateParser
{
///
/// Parse the supplied message template.
@@ -31,7 +32,7 @@ class MessageTemplateParser
/// is not syntactically valid, text tokens will be returned. The parser
/// will make a best effort to extract valid property tokens even in the
/// presence of parsing issues.
- public MessageTemplate Parse(string messageTemplate)
+ public static MessageTemplate Parse(string messageTemplate)
{
if (messageTemplate == null)
throw new ArgumentNullException(nameof(messageTemplate));
@@ -42,7 +43,7 @@ static IEnumerable Tokenize(string messageTemplate)
{
if (messageTemplate.Length == 0)
{
- yield return new TextToken("", 0);
+ yield return new TextToken("");
yield break;
}
@@ -77,7 +78,7 @@ static MessageTemplateToken ParsePropertyToken(int startAt, string messageTempla
if (startAt == messageTemplate.Length || messageTemplate[startAt] != '}')
{
next = startAt;
- return new TextToken(messageTemplate.Substring(first, next - first), first);
+ return new TextToken(messageTemplate.Substring(first, next - first));
}
next = startAt + 1;
@@ -85,25 +86,23 @@ static MessageTemplateToken ParsePropertyToken(int startAt, string messageTempla
var rawText = messageTemplate.Substring(first, next - first);
var tagContent = rawText.Substring(1, next - (first + 2));
if (tagContent.Length == 0)
- return new TextToken(rawText, first);
+ return new TextToken(rawText);
- string propertyNameAndDestructuring, format, alignment;
- if (!TrySplitTagContent(tagContent, out propertyNameAndDestructuring, out format, out alignment))
- return new TextToken(rawText, first);
+ if (!TrySplitTagContent(tagContent, out var propertyNameAndDestructuring, out var format, out var alignment))
+ return new TextToken(rawText);
var propertyName = propertyNameAndDestructuring;
- Destructuring destructuring;
- if (TryGetDestructuringHint(propertyName[0], out destructuring))
+ if (TryGetDestructuringHint(propertyName[0], out var destructuring))
propertyName = propertyName.Substring(1);
if (propertyName.Length == 0)
- return new TextToken(rawText, first);
+ return new TextToken(rawText);
for (var i = 0; i < propertyName.Length; ++i)
{
var c = propertyName[i];
if (!IsValidInPropertyName(c))
- return new TextToken(rawText, first);
+ return new TextToken(rawText);
}
if (format != null)
@@ -112,7 +111,7 @@ static MessageTemplateToken ParsePropertyToken(int startAt, string messageTempla
{
var c = format[i];
if (!IsValidInFormat(c))
- return new TextToken(rawText, first);
+ return new TextToken(rawText);
}
}
@@ -123,19 +122,19 @@ static MessageTemplateToken ParsePropertyToken(int startAt, string messageTempla
{
var c = alignment[i];
if (!IsValidInAlignment(c))
- return new TextToken(rawText, first);
+ return new TextToken(rawText);
}
var lastDash = alignment.LastIndexOf('-');
if (lastDash > 0)
- return new TextToken(rawText, first);
+ return new TextToken(rawText);
var width = lastDash == -1 ?
int.Parse(alignment) :
int.Parse(alignment.Substring(1));
if (width == 0)
- return new TextToken(rawText, first);
+ return new TextToken(rawText);
var direction = lastDash == -1 ?
AlignmentDirection.Right :
@@ -149,11 +148,10 @@ static MessageTemplateToken ParsePropertyToken(int startAt, string messageTempla
rawText,
format,
alignmentValue,
- destructuring,
- first);
+ destructuring);
}
- static bool TrySplitTagContent(string tagContent, out string propertyNameAndDestructuring, out string format, out string alignment)
+ static bool TrySplitTagContent(string tagContent, [NotNullWhen(true)] out string? propertyNameAndDestructuring, out string? format, out string? alignment)
{
var formatDelim = tagContent.IndexOf(':');
var alignmentDelim = tagContent.IndexOf(',');
@@ -299,6 +297,6 @@ static TextToken ParseTextToken(int startAt, string messageTemplate, out int nex
} while (startAt < messageTemplate.Length);
next = startAt;
- return new TextToken(accum.ToString(), first);
+ return new TextToken(accum.ToString());
}
}
\ No newline at end of file
diff --git a/src/Seq.Extensions.Logging/Serilog/Parsing/MessageTemplateToken.cs b/src/Seq.Extensions.Logging/Serilog/Parsing/MessageTemplateToken.cs
index 9b5e8c5..661cba4 100644
--- a/src/Seq.Extensions.Logging/Serilog/Parsing/MessageTemplateToken.cs
+++ b/src/Seq.Extensions.Logging/Serilog/Parsing/MessageTemplateToken.cs
@@ -21,31 +21,11 @@ namespace Serilog.Parsing;
///
abstract class MessageTemplateToken
{
- ///
- /// Construct a .
- ///
- /// The token's start index in the template.
- protected MessageTemplateToken(int startIndex)
- {
- StartIndex = startIndex;
- }
-
- ///
- /// The token's start index in the template.
- ///
- // ReSharper disable once UnusedAutoPropertyAccessor.Global
- public int StartIndex { get; }
-
- ///
- /// The token's length.
- ///
- public abstract int Length { get; }
-
///
/// Render the token to the output.
///
/// Properties that may be represented by the token.
/// Output for the rendered string.
/// Supplies culture-specific formatting information, or null.
- public abstract void Render(IReadOnlyDictionary properties, TextWriter output, IFormatProvider formatProvider = null);
+ public abstract void Render(IReadOnlyDictionary properties, TextWriter output, IFormatProvider? formatProvider = null);
}
\ No newline at end of file
diff --git a/src/Seq.Extensions.Logging/Serilog/Parsing/PropertyToken.cs b/src/Seq.Extensions.Logging/Serilog/Parsing/PropertyToken.cs
index 94a1322..ce6df4a 100644
--- a/src/Seq.Extensions.Logging/Serilog/Parsing/PropertyToken.cs
+++ b/src/Seq.Extensions.Logging/Serilog/Parsing/PropertyToken.cs
@@ -34,10 +34,8 @@ class PropertyToken : MessageTemplateToken
/// The format applied to the property, if any.
/// The alignment applied to the property, if any.
/// The destructuring strategy applied to the property, if any.
- /// The token's start index in the template.
///
- public PropertyToken(string propertyName, string rawText, string format = null, Alignment? alignment = null, Destructuring destructuring = Destructuring.Default, int startIndex = -1)
- : base(startIndex)
+ public PropertyToken(string propertyName, string rawText, string? format = null, Alignment? alignment = null, Destructuring destructuring = Destructuring.Default)
{
PropertyName = propertyName ?? throw new ArgumentNullException(nameof(propertyName));
Format = format;
@@ -52,18 +50,13 @@ public PropertyToken(string propertyName, string rawText, string format = null,
}
}
- ///
- /// The token's length.
- ///
- public override int Length => _rawText.Length;
-
///
/// Render the token to the output.
///
/// Properties that may be represented by the token.
/// Output for the rendered string.
/// Supplies culture-specific formatting information, or null.
- public override void Render(IReadOnlyDictionary properties, TextWriter output, IFormatProvider formatProvider = null)
+ public override void Render(IReadOnlyDictionary properties, TextWriter output, IFormatProvider? formatProvider = null)
{
if (properties == null) throw new ArgumentNullException(nameof(properties));
if (output == null) throw new ArgumentNullException(nameof(output));
@@ -106,7 +99,7 @@ public override void Render(IReadOnlyDictionary p
///
/// Format applied to the property.
///
- public string Format { get; }
+ public string? Format { get; }
///
/// Alignment applied to the property.
@@ -142,7 +135,7 @@ public bool TryGetPositionalValue(out int position)
/// true if the specified object is equal to the current object; otherwise, false.
///
/// The object to compare with the current object. 2
- public override bool Equals(object obj)
+ public override bool Equals(object? obj)
{
return obj is PropertyToken pt &&
pt.Destructuring == Destructuring &&
diff --git a/src/Seq.Extensions.Logging/Serilog/Parsing/TextToken.cs b/src/Seq.Extensions.Logging/Serilog/Parsing/TextToken.cs
index d1d5722..996f804 100644
--- a/src/Seq.Extensions.Logging/Serilog/Parsing/TextToken.cs
+++ b/src/Seq.Extensions.Logging/Serilog/Parsing/TextToken.cs
@@ -25,26 +25,18 @@ class TextToken : MessageTemplateToken
/// Construct a .
///
/// The text of the token.
- /// The token's start index in the template.
- ///
- public TextToken(string text, int startIndex = -1) : base(startIndex)
+ public TextToken(string text)
{
- if (text == null) throw new ArgumentNullException(nameof(text));
Text = text;
}
- ///
- /// The token's length.
- ///
- public override int Length => Text.Length;
-
///
/// Render the token to the output.
///
/// Properties that may be represented by the token.
/// Output for the rendered string.
/// Supplies culture-specific formatting information, or null.
- public override void Render(IReadOnlyDictionary properties, TextWriter output, IFormatProvider formatProvider = null)
+ public override void Render(IReadOnlyDictionary properties, TextWriter output, IFormatProvider? formatProvider = null)
{
if (output == null) throw new ArgumentNullException(nameof(output));
output.Write(Text);
@@ -57,7 +49,7 @@ public override void Render(IReadOnlyDictionary p
/// true if the specified object is equal to the current object; otherwise, false.
///
/// The object to compare with the current object. 2
- public override bool Equals(object obj)
+ public override bool Equals(object? obj)
{
var tt = obj as TextToken;
return tt != null && tt.Text == Text;
diff --git a/src/Seq.Extensions.Logging/Serilog/Policies/ByteArrayScalarConversionPolicy.cs b/src/Seq.Extensions.Logging/Serilog/Policies/ByteArrayScalarConversionPolicy.cs
index 65d7412..ecf679a 100644
--- a/src/Seq.Extensions.Logging/Serilog/Policies/ByteArrayScalarConversionPolicy.cs
+++ b/src/Seq.Extensions.Logging/Serilog/Policies/ByteArrayScalarConversionPolicy.cs
@@ -12,6 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+using System.Diagnostics.CodeAnalysis;
using Serilog.Core;
using Serilog.Events;
@@ -24,7 +25,7 @@ class ByteArrayScalarConversionPolicy : IScalarConversionPolicy
{
const int MaximumByteArrayLength = 1024;
- public bool TryConvertToScalar(object value, ILogEventPropertyValueFactory propertyValueFactory, out ScalarValue result)
+ public bool TryConvertToScalar(object value, ILogEventPropertyValueFactory propertyValueFactory, [NotNullWhen(true)] out ScalarValue? result)
{
var bytes = value as byte[];
if (bytes == null)
diff --git a/src/Seq.Extensions.Logging/Serilog/Policies/DelegateDestructuringPolicy.cs b/src/Seq.Extensions.Logging/Serilog/Policies/DelegateDestructuringPolicy.cs
index d629ce3..72f69f9 100644
--- a/src/Seq.Extensions.Logging/Serilog/Policies/DelegateDestructuringPolicy.cs
+++ b/src/Seq.Extensions.Logging/Serilog/Policies/DelegateDestructuringPolicy.cs
@@ -12,6 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+using System.Diagnostics.CodeAnalysis;
using Serilog.Core;
using Serilog.Events;
@@ -19,10 +20,9 @@ namespace Serilog.Policies;
class DelegateDestructuringPolicy : IDestructuringPolicy
{
- public bool TryDestructure(object value, ILogEventPropertyValueFactory propertyValueFactory, out LogEventPropertyValue result)
+ public bool TryDestructure(object value, ILogEventPropertyValueFactory propertyValueFactory, [NotNullWhen(true)] out LogEventPropertyValue? result)
{
- var del = value as Delegate;
- if (del != null)
+ if (value is Delegate del)
{
result = new ScalarValue(del.ToString());
return true;
diff --git a/src/Seq.Extensions.Logging/Serilog/Policies/EnumScalarConversionPolicy.cs b/src/Seq.Extensions.Logging/Serilog/Policies/EnumScalarConversionPolicy.cs
index caa99dd..72c175d 100644
--- a/src/Seq.Extensions.Logging/Serilog/Policies/EnumScalarConversionPolicy.cs
+++ b/src/Seq.Extensions.Logging/Serilog/Policies/EnumScalarConversionPolicy.cs
@@ -12,6 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+using System.Diagnostics.CodeAnalysis;
using Serilog.Core;
using Serilog.Events;
using System.Reflection;
@@ -20,7 +21,7 @@ namespace Serilog.Policies;
class EnumScalarConversionPolicy : IScalarConversionPolicy
{
- public bool TryConvertToScalar(object value, ILogEventPropertyValueFactory propertyValueFactory, out ScalarValue result)
+ public bool TryConvertToScalar(object value, ILogEventPropertyValueFactory propertyValueFactory, [NotNullWhen(true)] out ScalarValue? result)
{
if (value.GetType().GetTypeInfo().IsEnum)
{
diff --git a/src/Seq.Extensions.Logging/Serilog/Policies/NullableScalarConversionPolicy.cs b/src/Seq.Extensions.Logging/Serilog/Policies/NullableScalarConversionPolicy.cs
index 352eef8..b22c4ea 100644
--- a/src/Seq.Extensions.Logging/Serilog/Policies/NullableScalarConversionPolicy.cs
+++ b/src/Seq.Extensions.Logging/Serilog/Policies/NullableScalarConversionPolicy.cs
@@ -12,6 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+using System.Diagnostics.CodeAnalysis;
using Serilog.Core;
using Serilog.Events;
@@ -19,7 +20,7 @@ namespace Serilog.Policies;
class NullableScalarConversionPolicy : IScalarConversionPolicy
{
- public bool TryConvertToScalar(object value, ILogEventPropertyValueFactory propertyValueFactory, out ScalarValue result)
+ public bool TryConvertToScalar(object value, ILogEventPropertyValueFactory propertyValueFactory, [NotNullWhen(true)] out ScalarValue? result)
{
var type = value.GetType();
if (!type.IsConstructedGenericType || type.GetGenericTypeDefinition() != typeof(Nullable<>))
diff --git a/src/Seq.Extensions.Logging/Serilog/Policies/ReflectionTypesScalarDestructuringPolicy.cs b/src/Seq.Extensions.Logging/Serilog/Policies/ReflectionTypesScalarDestructuringPolicy.cs
index 4cd3d2b..ea9c36d 100644
--- a/src/Seq.Extensions.Logging/Serilog/Policies/ReflectionTypesScalarDestructuringPolicy.cs
+++ b/src/Seq.Extensions.Logging/Serilog/Policies/ReflectionTypesScalarDestructuringPolicy.cs
@@ -12,6 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+using System.Diagnostics.CodeAnalysis;
using System.Reflection;
using Serilog.Core;
using Serilog.Events;
@@ -20,11 +21,11 @@ namespace Serilog.Policies;
class ReflectionTypesScalarDestructuringPolicy : IDestructuringPolicy
{
- public bool TryDestructure(object value, ILogEventPropertyValueFactory propertyValueFactory, out LogEventPropertyValue result)
+ public bool TryDestructure(object value, ILogEventPropertyValueFactory propertyValueFactory, [NotNullWhen(true)] out LogEventPropertyValue? result)
{
// These types and their subclasses are property-laden and deep;
// most sinks will convert them to strings.
- if (value is Type || value is MemberInfo)
+ if (value is Type or MemberInfo)
{
result = new ScalarValue(value);
return true;
diff --git a/src/Seq.Extensions.Logging/Serilog/Policies/SimpleScalarConversionPolicy.cs b/src/Seq.Extensions.Logging/Serilog/Policies/SimpleScalarConversionPolicy.cs
index e216ff4..b94020c 100644
--- a/src/Seq.Extensions.Logging/Serilog/Policies/SimpleScalarConversionPolicy.cs
+++ b/src/Seq.Extensions.Logging/Serilog/Policies/SimpleScalarConversionPolicy.cs
@@ -12,6 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
+using System.Diagnostics.CodeAnalysis;
using Serilog.Core;
using Serilog.Events;
@@ -23,10 +24,10 @@ class SimpleScalarConversionPolicy : IScalarConversionPolicy
public SimpleScalarConversionPolicy(IEnumerable scalarTypes)
{
- _scalarTypes = new HashSet(scalarTypes);
+ _scalarTypes = [..scalarTypes];
}
- public bool TryConvertToScalar(object value, ILogEventPropertyValueFactory propertyValueFactory, out ScalarValue result)
+ public bool TryConvertToScalar(object value, ILogEventPropertyValueFactory propertyValueFactory, [NotNullWhen(true)] out ScalarValue? result)
{
if (_scalarTypes.Contains(value.GetType()))
{
diff --git a/src/Seq.Extensions.Logging/Serilog/Sinks/PeriodicBatching/BoundedConcurrentQueue.cs b/src/Seq.Extensions.Logging/Serilog/Sinks/PeriodicBatching/BoundedConcurrentQueue.cs
index 6b28e73..6b11352 100644
--- a/src/Seq.Extensions.Logging/Serilog/Sinks/PeriodicBatching/BoundedConcurrentQueue.cs
+++ b/src/Seq.Extensions.Logging/Serilog/Sinks/PeriodicBatching/BoundedConcurrentQueue.cs
@@ -13,6 +13,7 @@
// limitations under the License.
using System.Collections.Concurrent;
+using System.Diagnostics.CodeAnalysis;
namespace Serilog.Sinks.PeriodicBatching;
@@ -20,7 +21,7 @@ class BoundedConcurrentQueue
{
const int Unbounded = -1;
- readonly ConcurrentQueue _queue = new ConcurrentQueue();
+ readonly ConcurrentQueue _queue = new();
readonly int _queueLimit;
int _counter;
@@ -33,9 +34,7 @@ public BoundedConcurrentQueue(int? queueLimit = null)
_queueLimit = queueLimit ?? Unbounded;
}
- public int Count => _queue.Count;
-
- public bool TryDequeue(out T item)
+ public bool TryDequeue([MaybeNullWhen(false)] out T item)
{
if (_queueLimit == Unbounded)
return _queue.TryDequeue(out item);
diff --git a/src/Seq.Extensions.Logging/Serilog/Sinks/PeriodicBatching/PeriodicBatchingSink.cs b/src/Seq.Extensions.Logging/Serilog/Sinks/PeriodicBatching/PeriodicBatchingSink.cs
index 5bd84e0..a3e33b9 100644
--- a/src/Seq.Extensions.Logging/Serilog/Sinks/PeriodicBatching/PeriodicBatchingSink.cs
+++ b/src/Seq.Extensions.Logging/Serilog/Sinks/PeriodicBatching/PeriodicBatchingSink.cs
@@ -30,7 +30,7 @@ namespace Serilog.Sinks.PeriodicBatching;
/// that want to change this behavior need to either implement from scratch, or
/// embed retry logic in the batch emitting functions.
///
-class PeriodicBatchingSink : ILogEventSink, IDisposable, IBatchedLogEventSink
+sealed class PeriodicBatchingSink : ILogEventSink, IDisposable
{
///
/// Constant used with legacy constructor to indicate that the internal queue shouldn't be limited.
@@ -43,9 +43,9 @@ class PeriodicBatchingSink : ILogEventSink, IDisposable, IBatchedLogEventSink
readonly bool _eagerlyEmitFirstEvent;
readonly BoundedConcurrentQueue _queue;
readonly BatchedConnectionStatus _status;
- readonly Queue _waitingBatch = new Queue();
+ readonly Queue _waitingBatch = new();
- readonly object _stateLock = new object();
+ readonly object _stateLock = new();
readonly PortableTimer _timer;
@@ -60,25 +60,18 @@ class PeriodicBatchingSink : ILogEventSink, IDisposable, IBatchedLogEventSink
/// it will dispose this object if possible.
/// Options controlling behavior of the sink.
public PeriodicBatchingSink(IBatchedLogEventSink batchedSink, PeriodicBatchingSinkOptions options)
- : this(options)
{
- _batchedLogEventSink = batchedSink ?? throw new ArgumentNullException(nameof(batchedSink));
- }
-
- PeriodicBatchingSink(PeriodicBatchingSinkOptions options)
- {
- if (options == null) throw new ArgumentNullException(nameof(options));
-
if (options.BatchSizeLimit <= 0)
throw new ArgumentOutOfRangeException(nameof(options), "The batch size limit must be greater than zero.");
if (options.Period <= TimeSpan.Zero)
throw new ArgumentOutOfRangeException(nameof(options), "The period must be greater than zero.");
_batchSizeLimit = options.BatchSizeLimit;
+ _batchedLogEventSink = batchedSink ?? throw new ArgumentNullException(nameof(batchedSink));
_queue = new BoundedConcurrentQueue(options.QueueLimit);
_status = new BatchedConnectionStatus(options.Period);
_eagerlyEmitFirstEvent = options.EagerlyEmitFirstEvent;
- _timer = new PortableTimer(cancel => OnTick());
+ _timer = new PortableTimer(_ => OnTick());
}
void CloseAndFlush()
@@ -97,8 +90,7 @@ void CloseAndFlush()
// so we prevent possible deadlocks here for sync-over-async downstream implementations
ResetSyncContextAndWait(OnTick);
- if (_batchedLogEventSink != this)
- (_batchedLogEventSink as IDisposable)?.Dispose();
+ (_batchedLogEventSink as IDisposable)?.Dispose();
}
static void ResetSyncContextAndWait(Func taskFactory)
@@ -121,44 +113,9 @@ static void ResetSyncContextAndWait(Func taskFactory)
/// 2
public void Dispose()
{
- Dispose(true);
- }
-
- ///
- /// Free resources held by the sink.
- ///
- /// If true, called because the object is being disposed; if false,
- /// the object is being disposed from the finalizer.
- protected virtual void Dispose(bool disposing)
- {
- if (!disposing) return;
CloseAndFlush();
}
- ///
- /// Emit a batch of log events, running to completion synchronously.
- ///
- /// The events to emit.
- /// Override either or ,
- /// not both.
- protected virtual void EmitBatch(IEnumerable events)
- {
- }
-
- ///
- /// Emit a batch of log events, running asynchronously.
- ///
- /// The events to emit.
- /// Override either or ,
- /// not both.
-#pragma warning disable 1998
- protected virtual async Task EmitBatchAsync(IEnumerable events)
-#pragma warning restore 1998
- {
- // ReSharper disable once MethodHasAsyncOverload
- EmitBatch(events);
- }
-
async Task OnTick()
{
try
@@ -169,8 +126,7 @@ async Task OnTick()
while (_waitingBatch.Count < _batchSizeLimit &&
_queue.TryDequeue(out var next))
{
- if (CanInclude(next))
- _waitingBatch.Enqueue(next);
+ _waitingBatch.Enqueue(next);
}
if (_waitingBatch.Count == 0)
@@ -261,43 +217,4 @@ public void Emit(LogEvent logEvent)
_queue.TryEnqueue(logEvent);
}
-
- ///
- /// Determine whether a queued log event should be included in the batch. If
- /// an override returns false, the event will be dropped.
- ///
- /// An event to test for inclusion.
- /// True if the event should be included in the batch; otherwise, false.
- // ReSharper disable once UnusedParameter.Global
- protected virtual bool CanInclude(LogEvent logEvent)
- {
- return true;
- }
-
- ///
- /// Allows derived sinks to perform periodic work without requiring additional threads
- /// or timers (thus avoiding additional flush/shut-down complexity).
- ///
- /// Override either or ,
- /// not both.
- protected virtual void OnEmptyBatch()
- {
- }
-
- ///
- /// Allows derived sinks to perform periodic work without requiring additional threads
- /// or timers (thus avoiding additional flush/shut-down complexity).
- ///
- /// Override either or ,
- /// not both.
-#pragma warning disable 1998
- protected virtual async Task OnEmptyBatchAsync()
-#pragma warning restore 1998
- {
- // ReSharper disable once MethodHasAsyncOverload
- OnEmptyBatch();
- }
-
- Task IBatchedLogEventSink.EmitBatchAsync(IEnumerable batch) => EmitBatchAsync(batch);
- Task IBatchedLogEventSink.OnEmptyBatchAsync() => OnEmptyBatchAsync();
}
\ No newline at end of file
diff --git a/src/Seq.Extensions.Logging/Serilog/Sinks/PeriodicBatching/PortableTimer.cs b/src/Seq.Extensions.Logging/Serilog/Sinks/PeriodicBatching/PortableTimer.cs
index c9a3211..ab2c498 100644
--- a/src/Seq.Extensions.Logging/Serilog/Sinks/PeriodicBatching/PortableTimer.cs
+++ b/src/Seq.Extensions.Logging/Serilog/Sinks/PeriodicBatching/PortableTimer.cs
@@ -18,10 +18,10 @@ namespace Serilog.Sinks.PeriodicBatching;
class PortableTimer : IDisposable
{
- readonly object _stateLock = new object();
+ readonly object _stateLock = new();
readonly Func _onTick;
- readonly CancellationTokenSource _cancel = new CancellationTokenSource();
+ readonly CancellationTokenSource _cancel = new();
readonly Timer _timer;
diff --git a/src/Seq.Extensions.Logging/Serilog/Sinks/Seq/ControlledLevelSwitch.cs b/src/Seq.Extensions.Logging/Serilog/Sinks/Seq/ControlledLevelSwitch.cs
index 1096f8d..612b27b 100644
--- a/src/Seq.Extensions.Logging/Serilog/Sinks/Seq/ControlledLevelSwitch.cs
+++ b/src/Seq.Extensions.Logging/Serilog/Sinks/Seq/ControlledLevelSwitch.cs
@@ -27,10 +27,10 @@ class ControlledLevelSwitch
{
// If non-null, then background level checks will be performed; set either through the constructor
// or in response to a level specification from the server. Never set to null after being made non-null.
- LoggingLevelSwitch _controlledSwitch;
+ LoggingLevelSwitch? _controlledSwitch;
LogLevel? _originalLevel;
- public ControlledLevelSwitch(LoggingLevelSwitch controlledSwitch = null)
+ public ControlledLevelSwitch(LoggingLevelSwitch? controlledSwitch = null)
{
_controlledSwitch = controlledSwitch;
}
diff --git a/src/Seq.Extensions.Logging/Serilog/Sinks/Seq/ExponentialBackoffConnectionSchedule.cs b/src/Seq.Extensions.Logging/Serilog/Sinks/Seq/ExponentialBackoffConnectionSchedule.cs
deleted file mode 100644
index ec9322a..0000000
--- a/src/Seq.Extensions.Logging/Serilog/Sinks/Seq/ExponentialBackoffConnectionSchedule.cs
+++ /dev/null
@@ -1,72 +0,0 @@
-// Serilog.Sinks.Seq Copyright 2016 Serilog Contributors
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-namespace Serilog.Sinks.Seq;
-
-///
-/// Based on the BatchedConnectionStatus class from .
-///
-class ExponentialBackoffConnectionSchedule
-{
- static readonly TimeSpan MinimumBackoffPeriod = TimeSpan.FromSeconds(5);
- static readonly TimeSpan MaximumBackoffInterval = TimeSpan.FromMinutes(10);
-
- readonly TimeSpan _period;
-
- int _failuresSinceSuccessfulConnection;
-
- public ExponentialBackoffConnectionSchedule(TimeSpan period)
- {
- if (period < TimeSpan.Zero) throw new ArgumentOutOfRangeException(nameof(period), "The connection retry period must be a positive timespan");
-
- _period = period;
- }
-
- public void MarkSuccess()
- {
- _failuresSinceSuccessfulConnection = 0;
- }
-
- public void MarkFailure()
- {
- ++_failuresSinceSuccessfulConnection;
- }
-
- public TimeSpan NextInterval
- {
- get
- {
- // Available, and first failure, just try the batch interval
- if (_failuresSinceSuccessfulConnection <= 1) return _period;
-
- // Second failure, start ramping up the interval - first 2x, then 4x, ...
- var backoffFactor = Math.Pow(2, (_failuresSinceSuccessfulConnection - 1));
-
- // If the period is ridiculously short, give it a boost so we get some
- // visible backoff.
- var backoffPeriod = Math.Max(_period.Ticks, MinimumBackoffPeriod.Ticks);
-
- // The "ideal" interval
- var backedOff = (long)(backoffPeriod * backoffFactor);
-
- // Capped to the maximum interval
- var cappedBackoff = Math.Min(MaximumBackoffInterval.Ticks, backedOff);
-
- // Unless that's shorter than the base interval, in which case we'll just apply the period
- var actual = Math.Max(_period.Ticks, cappedBackoff);
-
- return TimeSpan.FromTicks(actual);
- }
- }
-}
\ No newline at end of file
diff --git a/src/Seq.Extensions.Logging/Serilog/Sinks/Seq/SeqSink.cs b/src/Seq.Extensions.Logging/Serilog/Sinks/Seq/SeqSink.cs
index e049d89..ed6993a 100644
--- a/src/Seq.Extensions.Logging/Serilog/Sinks/Seq/SeqSink.cs
+++ b/src/Seq.Extensions.Logging/Serilog/Sinks/Seq/SeqSink.cs
@@ -26,7 +26,7 @@ class SeqSink : IBatchedLogEventSink, IDisposable
static readonly TimeSpan RequiredLevelCheckInterval = TimeSpan.FromMinutes(2);
- readonly string _apiKey;
+ readonly string? _apiKey;
readonly long? _eventBodyLimitBytes;
readonly HttpClient _httpClient;
@@ -35,10 +35,10 @@ class SeqSink : IBatchedLogEventSink, IDisposable
public SeqSink(
string serverUrl,
- string apiKey,
+ string? apiKey,
long? eventBodyLimitBytes,
- ControlledLevelSwitch controlledSwitch,
- HttpMessageHandler messageHandler)
+ ControlledLevelSwitch? controlledSwitch,
+ HttpMessageHandler? messageHandler)
{
if (serverUrl == null) throw new ArgumentNullException(nameof(serverUrl));
_controlledSwitch = controlledSwitch ?? throw new ArgumentNullException(nameof(controlledSwitch));
@@ -60,7 +60,7 @@ public async Task OnEmptyBatchAsync()
if (_controlledSwitch.IsActive &&
_nextRequiredLevelCheckUtc < DateTime.UtcNow)
{
- await EmitBatchAsync(Enumerable.Empty());
+ await EmitBatchAsync([]);
}
}
diff --git a/test/Seq.Extensions.Logging.Tests/Seq.Extensions.Logging.Tests.csproj b/test/Seq.Extensions.Logging.Tests/Seq.Extensions.Logging.Tests.csproj
index 238fe34..267380a 100644
--- a/test/Seq.Extensions.Logging.Tests/Seq.Extensions.Logging.Tests.csproj
+++ b/test/Seq.Extensions.Logging.Tests/Seq.Extensions.Logging.Tests.csproj
@@ -1,12 +1,11 @@
- net48;net8.0
+ net48;net8.0;net9.0
../../asset/seqext.snk
true
true
Tests
- latest
@@ -14,12 +13,13 @@
-
-
+
+
+
all
runtime; build; native; contentfiles; analyzers; buildtransitive
-
+
diff --git a/test/Seq.Extensions.Logging.Tests/Seq/Extensions/Logging/EnricherTests.cs b/test/Seq.Extensions.Logging.Tests/Seq/Extensions/Logging/EnricherTests.cs
new file mode 100644
index 0000000..18e64fd
--- /dev/null
+++ b/test/Seq.Extensions.Logging.Tests/Seq/Extensions/Logging/EnricherTests.cs
@@ -0,0 +1,48 @@
+using Serilog.Parameters;
+using Serilog.Events;
+using Seq.Extensions.Logging;
+using Tests.Support;
+using Xunit;
+
+namespace Tests.Seq.Extensions.Logging;
+
+public class EnricherTests
+{
+ [Fact]
+ public void EnrichersAreAppliedInOrder()
+ {
+ var evt = Some.EmptyLogEvent();
+
+ new Enricher([
+ enrichingEvent => enrichingEvent.AddPropertyIfAbsent("A", 1),
+ enrichingEvent => enrichingEvent.AddPropertyIfAbsent("A", 2),
+ enrichingEvent => enrichingEvent.AddOrUpdateProperty("B", 1),
+ enrichingEvent => enrichingEvent.AddOrUpdateProperty("B", 2),
+ ])
+ .Enrich(
+ evt,
+ new PropertyValueConverter(int.MaxValue, int.MaxValue)
+ );
+
+ Assert.Equal(1, ((ScalarValue)evt.Properties["A"]).Value);
+ Assert.Equal(2, ((ScalarValue)evt.Properties["B"]).Value);
+ }
+
+ [Fact]
+ public void FailingEnricherIsHandled()
+ {
+ var evt = Some.EmptyLogEvent();
+
+ new Enricher([
+ enrichingEvent => enrichingEvent.AddOrUpdateProperty("A", 1),
+ _ => throw new Exception("Enricher Failed"),
+ enrichingEvent => enrichingEvent.AddOrUpdateProperty("A", 2),
+ ])
+ .Enrich(
+ evt,
+ new PropertyValueConverter(int.MaxValue, int.MaxValue)
+ );
+
+ Assert.Equal(2, ((ScalarValue)evt.Properties["A"]).Value);
+ }
+}
\ No newline at end of file
diff --git a/test/Seq.Extensions.Logging.Tests/Seq/Extensions/Logging/EnrichingEventTests.cs b/test/Seq.Extensions.Logging.Tests/Seq/Extensions/Logging/EnrichingEventTests.cs
new file mode 100644
index 0000000..5e05746
--- /dev/null
+++ b/test/Seq.Extensions.Logging.Tests/Seq/Extensions/Logging/EnrichingEventTests.cs
@@ -0,0 +1,38 @@
+using Xunit;
+using Serilog.Parameters;
+using Serilog.Events;
+using Seq.Extensions.Logging;
+using Tests.Support;
+
+namespace Tests.Seq.Extensions.Logging;
+
+public class EnrichingEventTests
+{
+ [Fact]
+ public void AddPropertyIfAbsentAddsProperties()
+ {
+ var enriching = new EnrichingEvent(
+ Some.EmptyLogEvent(),
+ new PropertyValueConverter(int.MaxValue, int.MaxValue)
+ );
+
+ enriching.AddPropertyIfAbsent("A", false);
+ enriching.AddPropertyIfAbsent("A", true);
+
+ Assert.Equal(false, ((ScalarValue)enriching.LogEvent.Properties["A"]).Value);
+ }
+
+ [Fact]
+ public void AddOrUpdatePropertyAddsProperties()
+ {
+ var enriching = new EnrichingEvent(
+ Some.EmptyLogEvent(),
+ new PropertyValueConverter(int.MaxValue, int.MaxValue)
+ );
+
+ enriching.AddOrUpdateProperty("A", false);
+ enriching.AddOrUpdateProperty("A", true);
+
+ Assert.Equal(true, ((ScalarValue)enriching.LogEvent.Properties["A"]).Value);
+ }
+}
diff --git a/test/Seq.Extensions.Logging.Tests/Seq/Extensions/Logging/ExceptionDataEnricherTests.cs b/test/Seq.Extensions.Logging.Tests/Seq/Extensions/Logging/ExceptionDataEnricherTests.cs
index 0d1676b..e598ca9 100644
--- a/test/Seq.Extensions.Logging.Tests/Seq/Extensions/Logging/ExceptionDataEnricherTests.cs
+++ b/test/Seq.Extensions.Logging.Tests/Seq/Extensions/Logging/ExceptionDataEnricherTests.cs
@@ -1,7 +1,5 @@
using Seq.Extensions.Logging;
using Serilog.Events;
-using System;
-using System.Linq;
using Tests.Support;
using Xunit;
@@ -16,16 +14,16 @@ public void WhenNoDataIsPresentNoPropertyIsAdded()
var exception = new Exception();
var evt = Some.ErrorEvent(exception);
- enricher.Enrich(evt, Some.PropertyFactory());
+ enricher.Enrich(evt, Some.PropertyValueFactory());
- Assert.Equal(0, evt.Properties.Count);
+ Assert.Empty(evt.Properties);
}
[Fact]
public void WhenDataIsPresentThePropertyIsAdded()
{
var enricher = new ExceptionDataEnricher();
- var exception = new Exception()
+ var exception = new Exception
{
Data =
{
@@ -35,9 +33,9 @@ public void WhenDataIsPresentThePropertyIsAdded()
};
var evt = Some.ErrorEvent(exception);
- enricher.Enrich(evt, Some.PropertyFactory());
+ enricher.Enrich(evt, Some.PropertyValueFactory());
- Assert.Equal(1, evt.Properties.Count);
+ Assert.Single(evt.Properties);
var data = evt.Properties["ExceptionData"];
var value = Assert.IsType(data);
Assert.Equal(2, value.Properties.Count);
diff --git a/test/Seq.Extensions.Logging.Tests/Serilog/Extensions/Logging/SerilogLoggerTests.cs b/test/Seq.Extensions.Logging.Tests/Serilog/Extensions/Logging/SerilogLoggerTests.cs
index 1df84d1..fe7ac69 100644
--- a/test/Seq.Extensions.Logging.Tests/Serilog/Extensions/Logging/SerilogLoggerTests.cs
+++ b/test/Seq.Extensions.Logging.Tests/Serilog/Extensions/Logging/SerilogLoggerTests.cs
@@ -1,14 +1,10 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
-using System;
using System.Collections;
using Serilog.Events;
using Microsoft.Extensions.Logging;
-using System.Collections.Generic;
using System.Diagnostics;
-using System.IO;
-using System.Linq;
using Xunit;
using Serilog.Extensions.Logging;
using Seq.Extensions.Logging;
@@ -30,11 +26,11 @@ static SerilogLoggerTests()
const string Name = "test";
const string TestMessage = "This is a test";
- static (SerilogLogger logger, SerilogSink sink) SetUp(LogLevel logLevel)
+ static (SerilogLogger logger, SerilogSink sink) SetUp(LogLevel logLevel, params Action[] enrichers)
{
var sink = new SerilogSink();
- var l = new global::Serilog.Core.Logger(new global::Serilog.Core.LoggingLevelSwitch(logLevel), sink);
+ var l = new global::Serilog.Core.Logger(sink, new Enricher(enrichers), null, new global::Serilog.Core.LoggingLevelSwitch(logLevel));
var provider = new SerilogLoggerProvider(l);
provider.SetScopeProvider(new LoggerExternalScopeProvider());
@@ -111,6 +107,19 @@ public void LogsCorrectLevel()
[InlineData(LogLevel.Critical, LogLevel.Warning, 0)]
[InlineData(LogLevel.Critical, LogLevel.Error, 0)]
[InlineData(LogLevel.Critical, LogLevel.Critical, 1)]
+ [InlineData(LogLevel.None, LogLevel.Trace, 0)]
+ [InlineData(LogLevel.None, LogLevel.Debug, 0)]
+ [InlineData(LogLevel.None, LogLevel.Information, 0)]
+ [InlineData(LogLevel.None, LogLevel.Warning, 0)]
+ [InlineData(LogLevel.None, LogLevel.Error, 0)]
+ [InlineData(LogLevel.None, LogLevel.Critical, 0)]
+ [InlineData(LogLevel.None, LogLevel.None, 0)]
+ [InlineData(LogLevel.Critical, LogLevel.None, 0)]
+ [InlineData(LogLevel.Error, LogLevel.None, 0)]
+ [InlineData(LogLevel.Warning, LogLevel.None, 0)]
+ [InlineData(LogLevel.Information, LogLevel.None, 0)]
+ [InlineData(LogLevel.Debug, LogLevel.None, 0)]
+ [InlineData(LogLevel.Trace, LogLevel.None, 0)]
public void LogsWhenEnabled(LogLevel minLevel, LogLevel logLevel, int expected)
{
var (logger, sink) = SetUp(minLevel);
@@ -125,13 +134,13 @@ public void LogsCorrectMessage()
{
var (logger, sink) = SetUp(LogLevel.Trace);
- logger.Log(LogLevel.Information, 0, null, null, null!);
+ logger.Log(LogLevel.Information, 0, null, null, null!);
logger.Log(LogLevel.Information, 0, TestMessage, null, null!);
- logger.Log(LogLevel.Information, 0, null, null, (_, _) => TestMessage);
+ logger.Log(LogLevel.Information, 0, null, null, (_, _) => TestMessage);
Assert.Equal(3, sink.Writes.Count);
- Assert.Equal(1, sink.Writes[0].Properties.Count);
+ Assert.Single(sink.Writes[0].Properties);
Assert.Empty(sink.Writes[0].RenderMessage());
Assert.Equal(2, sink.Writes[1].Properties.Count);
@@ -225,7 +234,13 @@ public void CarriesMessageTemplateProperties()
Assert.Equal("Hello, {Recipient}", sink.Writes[0].MessageTemplate.Text);
SelfLog.Disable();
- Assert.Empty(selfLog.ToString());
+
+ var selfLogContent = selfLog.ToString();
+ if (!string.IsNullOrEmpty(selfLogContent))
+ {
+ // Test failures are hard to diagnose without the full SelfLog entry.
+ throw new Exception(selfLogContent);
+ }
}
[Fact]
@@ -244,6 +259,22 @@ public void CarriesEventIdIfNonzero()
Assert.Equal(42, id.Value);
}
+ [Fact]
+ public void OverridesStateEventIdIfSpecified()
+ {
+ var (logger, sink) = SetUp(LogLevel.Trace);
+
+ const int expected = 3;
+
+ logger.Log[]>(LogLevel.Information, expected, state: [new("EventId", "Something")], exception: null, formatter: (_, _) => "");
+
+ Assert.Single(sink.Writes);
+
+ var eventId = (StructureValue)sink.Writes[0].Properties["EventId"];
+ var id = (ScalarValue)eventId.Properties.Single(p => p.Name == "Id").Value;
+ Assert.Equal(expected, id.Value);
+ }
+
[Fact]
public void BeginScopeDestructuresObjectsWhenDestructurerIsUsedInMessageTemplate()
{
@@ -357,6 +388,21 @@ public void CurrentActivityIsCapturedAtLogEventCreation()
Assert.Equal(activity.SpanId, single.SpanId);
}
+ [Fact]
+ public void EnrichersAreApplied()
+ {
+ var (logger, sink) = SetUp(
+ LogLevel.Trace,
+ evt => evt.AddPropertyIfAbsent("EnrichedScalar", true),
+ evt => evt.AddPropertyIfAbsent("EnrichedObject", new { a = 1 }, true)
+ );
+
+ logger.Log(LogLevel.Information, 0, TestMessage, null, null!);
+
+ Assert.Equal(true, ((ScalarValue)sink.Writes[0].Properties["EnrichedScalar"]).Value);
+ Assert.Equal(1, ((ScalarValue)((StructureValue)sink.Writes[0].Properties["EnrichedObject"]).Properties[0].Value).Value);
+ }
+
class FoodScope : IEnumerable>
{
readonly string _name;
@@ -400,8 +446,8 @@ IEnumerator IEnumerable.GetEnumerator()
class Person
{
// ReSharper disable once UnusedAutoPropertyAccessor.Local
- public string FirstName { get; set; }
+ public string? FirstName { get; set; }
// ReSharper disable once UnusedAutoPropertyAccessor.Local
- public string LastName { get; set; }
+ public string? LastName { get; set; }
}
}
\ No newline at end of file
diff --git a/test/Seq.Extensions.Logging.Tests/Serilog/Extensions/Logging/Support/SerilogSink.cs b/test/Seq.Extensions.Logging.Tests/Serilog/Extensions/Logging/Support/SerilogSink.cs
index baa8cb8..3205cc7 100644
--- a/test/Seq.Extensions.Logging.Tests/Serilog/Extensions/Logging/Support/SerilogSink.cs
+++ b/test/Seq.Extensions.Logging.Tests/Serilog/Extensions/Logging/Support/SerilogSink.cs
@@ -1,7 +1,6 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
-using System.Collections.Generic;
using Serilog.Core;
using Serilog.Events;
using Xunit;
diff --git a/test/Seq.Extensions.Logging.Tests/Serilog/Sinks/BatchedConnectionStatusTests.cs b/test/Seq.Extensions.Logging.Tests/Serilog/Sinks/BatchedConnectionStatusTests.cs
index 366dd4d..29fe89d 100644
--- a/test/Seq.Extensions.Logging.Tests/Serilog/Sinks/BatchedConnectionStatusTests.cs
+++ b/test/Seq.Extensions.Logging.Tests/Serilog/Sinks/BatchedConnectionStatusTests.cs
@@ -12,12 +12,11 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-using System;
using System.Globalization;
-using Xunit;
using Serilog.Sinks.PeriodicBatching;
+using Xunit;
-namespace Seq.Extensions.Logging.Tests.Serilog.Sinks;
+namespace Tests.Serilog.Sinks;
public class BatchedConnectionStatusTests
{
diff --git a/test/Seq.Extensions.Logging.Tests/Serilog/Sinks/Seq/ControlledLevelSwitchTests.cs b/test/Seq.Extensions.Logging.Tests/Serilog/Sinks/Seq/ControlledLevelSwitchTests.cs
index ace60a5..4243d88 100644
--- a/test/Seq.Extensions.Logging.Tests/Serilog/Sinks/Seq/ControlledLevelSwitchTests.cs
+++ b/test/Seq.Extensions.Logging.Tests/Serilog/Sinks/Seq/ControlledLevelSwitchTests.cs
@@ -20,8 +20,7 @@ public void WhenTheServerSendsALevelTheSwitchIsAdjusted()
[Fact]
public void WhenTheServerSendsNoLevelTheSwitchIsNotInitiallyAdjusted()
{
- var lls = new LoggingLevelSwitch(LogLevel.Warning);
- lls.MinimumLevel = LogLevel.Critical;
+ var lls = new LoggingLevelSwitch(LogLevel.Critical);
var cls = new ControlledLevelSwitch(lls);
cls.Update(null);
Assert.Equal(LogLevel.Critical, lls.MinimumLevel);
@@ -40,14 +39,14 @@ public void WhenTheServerSendsNoLevelTheSwitchIsResetIfPreviouslyAdjusted()
[Fact]
public void WithNoSwitchToControlAllEventsAreIncluded()
{
- var cls = new ControlledLevelSwitch(null);
+ var cls = new ControlledLevelSwitch();
Assert.True(cls.IsIncluded(Some.DebugEvent()));
}
[Fact]
public void WithNoSwitchToControlEventsAreStillFiltered()
{
- var cls = new ControlledLevelSwitch(null);
+ var cls = new ControlledLevelSwitch();
cls.Update(LogLevel.Warning);
Assert.True(cls.IsIncluded(Some.ErrorEvent()));
Assert.False(cls.IsIncluded(Some.InformationEvent()));
@@ -56,7 +55,7 @@ public void WithNoSwitchToControlEventsAreStillFiltered()
[Fact]
public void WithNoSwitchToControlAllEventsAreIncludedAfterReset()
{
- var cls = new ControlledLevelSwitch(null);
+ var cls = new ControlledLevelSwitch();
cls.Update(LogLevel.Warning);
cls.Update(null);
Assert.True(cls.IsIncluded(Some.DebugEvent()));
@@ -77,7 +76,7 @@ public void WhenNotControllingASwitchTheControllerIsNotActive()
}
[Fact]
- public void AfterServerControlhTheControllerIsAlwaysActive()
+ public void AfterServerControlTheControllerIsAlwaysActive()
{
var cls = new ControlledLevelSwitch();
diff --git a/test/Seq.Extensions.Logging.Tests/Serilog/Sinks/Seq/SeqPayloadFormatterTests.cs b/test/Seq.Extensions.Logging.Tests/Serilog/Sinks/Seq/SeqPayloadFormatterTests.cs
index 1157956..85f521d 100644
--- a/test/Seq.Extensions.Logging.Tests/Serilog/Sinks/Seq/SeqPayloadFormatterTests.cs
+++ b/test/Seq.Extensions.Logging.Tests/Serilog/Sinks/Seq/SeqPayloadFormatterTests.cs
@@ -1,8 +1,9 @@
-using Serilog.Sinks.Seq;
+using Seq.Extensions.Logging;
+using Serilog.Sinks.Seq;
using Tests.Support;
using Xunit;
-namespace Seq.Extensions.Logging.Tests.Serilog.Sinks.Seq;
+namespace Tests.Serilog.Sinks.Seq;
public class SeqPayloadFormatterTests
{
@@ -11,7 +12,7 @@ public void JsonSafeStringPropertiesAreIncludedAsIs()
{
const string json = "{\"A\": 42}";
var evt = Some.LogEvent("The answer is {Answer}", new JsonSafeString(json));
- var payload = SeqPayloadFormatter.FormatCompactPayload(new[] { evt }, null);
+ var payload = SeqPayloadFormatter.FormatCompactPayload([evt], null);
Assert.Contains("\"Answer\":{\"A\": 42}", payload);
}
@@ -19,7 +20,7 @@ public void JsonSafeStringPropertiesAreIncludedAsIs()
public void DefaultJsonSafeStringsDoNotCorruptPayload()
{
var evt = Some.LogEvent("The answer is {Answer}", (JsonSafeString)default);
- var payload = SeqPayloadFormatter.FormatCompactPayload(new[] { evt }, null);
+ var payload = SeqPayloadFormatter.FormatCompactPayload([evt], null);
Assert.Contains("\"Answer\":\"\"", payload);
}
}
\ No newline at end of file
diff --git a/test/Seq.Extensions.Logging.Tests/Support/Some.cs b/test/Seq.Extensions.Logging.Tests/Support/Some.cs
index b26719d..5f6147a 100644
--- a/test/Seq.Extensions.Logging.Tests/Support/Some.cs
+++ b/test/Seq.Extensions.Logging.Tests/Support/Some.cs
@@ -1,7 +1,4 @@
-using System;
-using System.Collections.Generic;
-using System.Threading;
-using Serilog.Events;
+using Serilog.Events;
using Xunit.Sdk;
using Serilog.Core;
using Microsoft.Extensions.Logging;
@@ -12,33 +9,45 @@ namespace Tests.Support;
static class Some
{
- public static LogEvent LogEvent(string messageTemplate, params object[] propertyValues)
+ public static LogEvent EmptyLogEvent()
+ {
+ return new LogEvent(
+ default,
+ default,
+ null,
+ new MessageTemplate("", []),
+ new(),
+ default,
+ default
+ );
+ }
+
+ public static LogEvent LogEvent(string messageTemplate, params object?[] propertyValues)
{
return LogEvent(null, messageTemplate, propertyValues);
}
- public static LogEvent LogEvent(Exception exception, string messageTemplate, params object[] propertyValues)
+ public static LogEvent LogEvent(Exception? exception, string messageTemplate, params object?[] propertyValues)
{
return LogEvent(LogLevel.Information, exception, messageTemplate, propertyValues);
}
- public static ILogEventPropertyFactory PropertyFactory()
+ public static ILogEventPropertyValueFactory PropertyValueFactory()
{
return new PropertyValueConverter(10, 1024);
}
- public static LogEvent LogEvent(LogLevel level, Exception exception, string messageTemplate, params object[] propertyValues)
+ public static LogEvent LogEvent(LogLevel level, Exception? exception, string messageTemplate, params object?[] propertyValues)
{
- var log = new Logger(null, null, null);
- MessageTemplate template;
- IEnumerable properties;
+ var log = new Logger(null!, null!);
#pragma warning disable Serilog004 // Constant MessageTemplate verifier
- if (!log.BindMessageTemplate(messageTemplate, propertyValues, out template, out properties))
+ // ReSharper disable once ConvertIfStatementToReturnStatement
+ if (!log.BindMessageTemplate(messageTemplate, propertyValues, out var template, out var properties))
#pragma warning restore Serilog004 // Constant MessageTemplate verifier
{
throw new XunitException("Template could not be bound.");
}
- return new LogEvent(DateTimeOffset.Now, level, exception, template, properties, default, default);
+ return new LogEvent(DateTimeOffset.Now, level, exception, template, properties.ToDictionary(p => p.Name, p => p.Value), default, default);
}
public static LogEvent DebugEvent()
@@ -51,7 +60,7 @@ public static LogEvent InformationEvent()
return LogEvent(LogLevel.Information, null, "Information event");
}
- public static LogEvent ErrorEvent(Exception exception = null)
+ public static LogEvent ErrorEvent(Exception? exception = null)
{
return LogEvent(LogLevel.Error, exception, "Error event");
}