diff --git a/Build.ps1 b/Build.ps1 index 9b2d878..fe080b5 100644 --- a/Build.ps1 +++ b/Build.ps1 @@ -11,7 +11,7 @@ if(Test-Path .\artifacts) { $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 "master" -and $revision -ne "local"] +$suffix = @{ $true = ""; $false = "$($branch.Substring(0, [math]::Min(10,$branch.Length)))-$revision"}[$branch -eq "main" -and $revision -ne "local"] echo "build: Version suffix is $suffix" diff --git a/README.md b/README.md index 7128444..f7b5b93 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ For an example, see [the _dotnetconf_ deep dive session](https://channel9.msdn.com/Events/dotnetConf/2016/ASPNET-Core--deep-dive-on-building-a-real-website-with-todays-bits). -![Screenshot](https://github.com/datalust/seq-extensions-logging/blob/master/asset/screenshot.png?raw=true) +![Screenshot](https://raw.githubusercontent.com/datalust/seq-extensions-logging/dev/asset/screenshot.png) This package makes it a one-liner to configure ASP.NET Core logging with Seq. diff --git a/appveyor.yml b/appveyor.yml index 489e6fb..80928a3 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -9,7 +9,7 @@ artifacts: deploy: - provider: NuGet api_key: - secure: 94298ksNMfJ2LkBV3Lw9o4JVzKvVPPMHF91p0XjsBhNWVeMX2NeROX4fpio6BsQy + secure: QYBIGd/prsJud8qlJati9FdnXbiA0VvyxO1z0TUiQV3svdpbD7ZaykJFTYKyIdQq skip_symbols: true on: - branch: /^(master|dev)$/ + branch: /^(main|dev)$/ diff --git a/asset/icon.png b/asset/icon.png new file mode 100644 index 0000000..ed37092 Binary files /dev/null and b/asset/icon.png differ diff --git a/example/ConsoleApplication/ConsoleApplication.csproj b/example/ConsoleApplication/ConsoleApplication.csproj index 25ecd34..b10ddf3 100644 --- a/example/ConsoleApplication/ConsoleApplication.csproj +++ b/example/ConsoleApplication/ConsoleApplication.csproj @@ -2,7 +2,7 @@ Exe - netcoreapp3.0 + net5.0 diff --git a/example/WebApplication/Startup.cs b/example/WebApplication/Startup.cs index f80fcaa..7645c57 100644 --- a/example/WebApplication/Startup.cs +++ b/example/WebApplication/Startup.cs @@ -1,14 +1,16 @@ using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; +using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Logging; namespace WebApplication { public class Startup { - public Startup(IHostingEnvironment env) + public Startup(IWebHostEnvironment env) { var builder = new ConfigurationBuilder() .SetBasePath(env.ContentRootPath) @@ -23,6 +25,8 @@ public Startup(IHostingEnvironment env) // This method gets called by the runtime. Use this method to add services to the container. public void ConfigureServices(IServiceCollection services) { + services.Configure(Configuration.GetSection("MvcOptions")); + services.AddLogging(loggingBuilder => { // Add the Seq logger, with configuration from appsettings.json @@ -37,12 +41,11 @@ public void ConfigureServices(IServiceCollection services) } // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. - public void Configure(IApplicationBuilder app, IHostingEnvironment env) + public void Configure(IApplicationBuilder app, IWebHostEnvironment env) { if (env.IsDevelopment()) { app.UseDeveloperExceptionPage(); - app.UseBrowserLink(); } else { diff --git a/example/WebApplication/WebApplication.csproj b/example/WebApplication/WebApplication.csproj index 3996676..da94f0a 100644 --- a/example/WebApplication/WebApplication.csproj +++ b/example/WebApplication/WebApplication.csproj @@ -1,12 +1,10 @@ - netcoreapp2.0 + net5.0 true WebApplication Exe - WebApplication - 2.0.0 @@ -19,8 +17,8 @@ - - + + diff --git a/example/WebApplication/appsettings.json b/example/WebApplication/appsettings.json index ba11e4d..60f7e58 100644 --- a/example/WebApplication/appsettings.json +++ b/example/WebApplication/appsettings.json @@ -7,5 +7,8 @@ "ServerUrl": "http://localhost:5341", "ApiKey": "" } + }, + "MvcOptions": { + "EnableEndpointRouting": false } } diff --git a/global.json b/global.json new file mode 100644 index 0000000..52599fd --- /dev/null +++ b/global.json @@ -0,0 +1,6 @@ +{ + "sdk": { + "version": "5.0.100", + "rollForward": "latestFeature" + } +} \ No newline at end of file diff --git a/seq-extensions-logging.sln b/seq-extensions-logging.sln index 1d425a7..2e68bf9 100644 --- a/seq-extensions-logging.sln +++ b/seq-extensions-logging.sln @@ -11,6 +11,9 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "global", "global", "{B0D573 Build.ps1 = Build.ps1 LICENSE = LICENSE README.md = README.md + global.json = global.json + .gitattributes = .gitattributes + .gitignore = .gitignore EndProjectSection EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "test", "test", "{D54DE844-AC36-4872-928F-5F573D41ACAE}" diff --git a/seq-extensions-logging.sln.DotSettings b/seq-extensions-logging.sln.DotSettings index 4927542..1c67b9a 100644 --- a/seq-extensions-logging.sln.DotSettings +++ b/seq-extensions-logging.sln.DotSettings @@ -1,3 +1,6 @@  <Policy Inspect="True" Prefix="_" Suffix="" Style="aaBb"><ExtraRule Prefix="" Suffix="" Style="AaBb" /></Policy> + True + True + True True \ No newline at end of file diff --git a/src/Seq.Extensions.Logging/Seq.Extensions.Logging.csproj b/src/Seq.Extensions.Logging/Seq.Extensions.Logging.csproj index 0ffd3fa..7949ffe 100644 --- a/src/Seq.Extensions.Logging/Seq.Extensions.Logging.csproj +++ b/src/Seq.Extensions.Logging/Seq.Extensions.Logging.csproj @@ -1,19 +1,20 @@ - + Add centralized structured log collection to ASP.NET Core apps with one line of code. 5.0.0 Datalust and Contributors - net462;netstandard2.0 + net462;netstandard2.0;net5.0 true true ../../asset/seqext.snk true true aspnet;logging - https://datalust.co/images/nuget/seq.png + icon.png https://github.com/datalust/seq-extensions-logging Apache-2.0 + @@ -22,4 +23,8 @@ + + + + diff --git a/src/Seq.Extensions.Logging/Seq/Extensions/Logging/ExceptionDataEnricher.cs b/src/Seq.Extensions.Logging/Seq/Extensions/Logging/ExceptionDataEnricher.cs index fe0f039..ef2fcd6 100644 --- a/src/Seq.Extensions.Logging/Seq/Extensions/Logging/ExceptionDataEnricher.cs +++ b/src/Seq.Extensions.Logging/Seq/Extensions/Logging/ExceptionDataEnricher.cs @@ -1,4 +1,4 @@ -// Copyright 2016 Datalust +// Copyright 2016-2020 Datalust and Contributors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -23,15 +23,11 @@ class ExceptionDataEnricher : ILogEventEnricher { public void Enrich(LogEvent logEvent, ILogEventPropertyFactory propertyFactory) { - if (logEvent.Exception == null) + var exceptionData = logEvent.Exception?.GetBaseException().Data; + if (exceptionData == null || exceptionData.Count == 0) return; - var exception = logEvent.Exception.GetBaseException(); - - if (exception.Data == null || exception.Data.Count == 0) - return; - - var data = exception.Data + var data = exceptionData .Cast() .Where(e => e.Key is string) .Select(e => propertyFactory.CreateProperty((string)e.Key, e.Value)); diff --git a/src/Seq.Extensions.Logging/Serilog/Extensions/Logging/SerilogLoggerProvider.cs b/src/Seq.Extensions.Logging/Serilog/Extensions/Logging/SerilogLoggerProvider.cs index 9088b69..4ea3a5f 100644 --- a/src/Seq.Extensions.Logging/Serilog/Extensions/Logging/SerilogLoggerProvider.cs +++ b/src/Seq.Extensions.Logging/Serilog/Extensions/Logging/SerilogLoggerProvider.cs @@ -2,7 +2,6 @@ // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; -using System.Threading; using Microsoft.Extensions.Logging; using Serilog.Core; using Serilog.Events; @@ -15,7 +14,7 @@ namespace Serilog.Extensions.Logging /// An that pipes events through Serilog. /// [ProviderAlias("Seq")] - class SerilogLoggerProvider : ILoggerProvider, ILogEventEnricher + class SerilogLoggerProvider : ILoggerProvider, ILogEventEnricher, ISupportExternalScope { internal const string OriginalFormatPropertyName = "{OriginalFormat}"; internal const string ScopePropertyName = "Scope"; @@ -23,6 +22,8 @@ class SerilogLoggerProvider : ILoggerProvider, ILogEventEnricher readonly Logger _logger; readonly Action _dispose; + IExternalScopeProvider _scopeProvider; + /// /// Construct a . /// @@ -44,44 +45,39 @@ public FrameworkLogger CreateLogger(string name) public IDisposable BeginScope(T state) { - return new SerilogLoggerScope(this, state); + return _scopeProvider?.Push(state); } /// public void Enrich(LogEvent logEvent, ILogEventPropertyFactory propertyFactory) { - List scopeItems = null; - for (var scope = CurrentScope; scope != null; scope = scope.Parent) - { - LogEventPropertyValue scopeItem; - scope.EnrichAndCreateScopeItem(logEvent, propertyFactory, out scopeItem); + var scopeItems = new List(); + _scopeProvider?.ForEachScope((scopeState, state) => + { + SerilogLoggerScope.EnrichAndCreateScopeItem(scopeState, state.logEvent, state.propertyFactory, out var scopeItem); + if (scopeItem != null) { - scopeItems = scopeItems ?? new List(); - scopeItems.Add(scopeItem); + state.scopeItems.Add(scopeItem); } - } + }, (logEvent, propertyFactory, scopeItems)); - if (scopeItems != null) + if (scopeItems.Count > 0) { - scopeItems.Reverse(); logEvent.AddPropertyIfAbsent(new LogEventProperty(ScopePropertyName, new SequenceValue(scopeItems))); } } - readonly AsyncLocal _value = new AsyncLocal(); - - internal SerilogLoggerScope CurrentScope - { - get => _value.Value; - set => _value.Value = value; - } - /// public void Dispose() { _dispose(); } + + public void SetScopeProvider(IExternalScopeProvider scopeProvider) + { + _scopeProvider = scopeProvider; + } } } diff --git a/src/Seq.Extensions.Logging/Serilog/Extensions/Logging/SerilogLoggerScope.cs b/src/Seq.Extensions.Logging/Serilog/Extensions/Logging/SerilogLoggerScope.cs index be36a18..62bbe66 100644 --- a/src/Seq.Extensions.Logging/Serilog/Extensions/Logging/SerilogLoggerScope.cs +++ b/src/Seq.Extensions.Logging/Serilog/Extensions/Logging/SerilogLoggerScope.cs @@ -1,60 +1,25 @@ // 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. +// Modifications Copyright (c) Datalust and Contributors +// Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. -using System; using System.Collections.Generic; using Serilog.Core; using Serilog.Events; namespace Serilog.Extensions.Logging { - class SerilogLoggerScope : IDisposable + static class SerilogLoggerScope { const string NoName = "None"; - - readonly SerilogLoggerProvider _provider; - readonly object _state; - - // An optimization only, no problem if there are data races on this. - bool _disposed; - - public SerilogLoggerScope(SerilogLoggerProvider provider, object state) - { - _provider = provider; - _state = state; - - Parent = _provider.CurrentScope; - _provider.CurrentScope = this; - } - - public SerilogLoggerScope Parent { get; } - - public void Dispose() - { - if (!_disposed) - { - _disposed = true; - - // In case one of the parent scopes has been disposed out-of-order, don't - // just blindly reinstate our own parent. - for (var scan = _provider.CurrentScope; scan != null; scan = scan.Parent) - { - if (ReferenceEquals(scan, this)) - _provider.CurrentScope = Parent; - } - } - } - - public void EnrichAndCreateScopeItem(LogEvent logEvent, ILogEventPropertyFactory propertyFactory, out LogEventPropertyValue scopeItem) + + public static void EnrichAndCreateScopeItem(object state, LogEvent logEvent, ILogEventPropertyFactory propertyFactory, out LogEventPropertyValue scopeItem) { - if (_state == null) + if (state == null) { scopeItem = null; - return; } - var stateProperties = _state as IEnumerable>; - if (stateProperties != null) + if (state is IEnumerable> stateProperties) { scopeItem = null; // Unless it's `FormattedLogValues`, these are treated as property bags rather than scope items. @@ -62,7 +27,7 @@ public void EnrichAndCreateScopeItem(LogEvent logEvent, ILogEventPropertyFactory { if (stateProperty.Key == SerilogLoggerProvider.OriginalFormatPropertyName && stateProperty.Value is string) { - scopeItem = new ScalarValue(_state.ToString()); + scopeItem = new ScalarValue(state.ToString()); continue; } @@ -76,12 +41,12 @@ public void EnrichAndCreateScopeItem(LogEvent logEvent, ILogEventPropertyFactory } var property = propertyFactory.CreateProperty(key, stateProperty.Value, destructureObject); - logEvent.AddPropertyIfAbsent(property); + logEvent.AddOrUpdateProperty(property); } } else { - scopeItem = propertyFactory.CreateProperty(NoName, _state).Value; + scopeItem = propertyFactory.CreateProperty(NoName, state).Value; } } } 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 2643083..05ad0a3 100644 --- a/test/Seq.Extensions.Logging.Tests/Seq.Extensions.Logging.Tests.csproj +++ b/test/Seq.Extensions.Logging.Tests/Seq.Extensions.Logging.Tests.csproj @@ -1,7 +1,7 @@  - net472;netcoreapp3.0 + net472;netcoreapp3.1;net5.0 ../../asset/seqext.snk true true @@ -20,8 +20,4 @@ - - - - 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 73d91ce..a9fd34a 100644 --- a/test/Seq.Extensions.Logging.Tests/Serilog/Extensions/Logging/SerilogLoggerTests.cs +++ b/test/Seq.Extensions.Logging.Tests/Serilog/Extensions/Logging/SerilogLoggerTests.cs @@ -12,7 +12,6 @@ using Serilog.Extensions.Logging; using Seq.Extensions.Logging; using Tests.Serilog.Extensions.Logging.Support; -using Serilog.Core.Enrichers; namespace Tests.Serilog.Extensions.Logging { @@ -28,6 +27,7 @@ private Tuple SetUp(LogLevel logLevel) var l = new global::Serilog.Core.Logger(new global::Serilog.Core.LoggingLevelSwitch(logLevel), sink); var provider = new SerilogLoggerProvider(l); + provider.SetScopeProvider(new LoggerExternalScopeProvider()); var logger = (SerilogLogger)provider.CreateLogger(Name); return new Tuple(logger, sink);