diff --git a/.gitignore b/.gitignore index fada4b0..2bd57a4 100644 --- a/.gitignore +++ b/.gitignore @@ -251,3 +251,4 @@ paket-files/ .idea/ *.sln.iml src/Seq.Extensions.Logging/project.json.orig +*.orig diff --git a/README.md b/README.md index 1829b2c..7128444 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # Seq.Extensions.Logging [![Build status](https://ci.appveyor.com/api/projects/status/h7r1hv3cpd6e2ou3?svg=true)](https://ci.appveyor.com/project/datalust/seq-extensions-logging) [![NuGet Pre Release](https://img.shields.io/nuget/vpre/Seq.Extensions.Logging.svg)](https://nuget.org/packages/Seq.Extensions.Logging) [![Join the chat at https://gitter.im/datalust/seq](https://img.shields.io/gitter/room/datalust/seq.svg)](https://gitter.im/datalust/seq) -[Seq](https://getseq.net) 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. +[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. 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). @@ -14,39 +14,22 @@ The instructions that follow are for **.NET Core 2.0+**. Add [the NuGet package](https://nuget.org/packages/seq.extensions.logging) to your project either by editing the CSPROJ file, or using the NuGet package manager: -```powershell -Install-Package Seq.Extensions.Logging +``` +dotnet add package Seq.Extensions.Logging ``` -In `Program.cs`, call `AddSeq()` on the provided `ILoggingBuilder`. +In `Startup.cs`, call `AddSeq()` on the provided `ILoggingBuilder`. ```csharp -public static void Main(string[] args) +public class Startup { - var webHost = new WebHostBuilder() - .UseKestrel() - .UseContentRoot(Directory.GetCurrentDirectory()) - .ConfigureAppConfiguration((hostingContext, config) => - { - var env = hostingContext.HostingEnvironment; - config.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true) - .AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true, reloadOnChange: true); - config.AddEnvironmentVariables(); - }) - .ConfigureLogging((hostingContext, logging) => - { - logging.AddConfiguration(hostingContext.Configuration.GetSection("Logging")); - - // Add this line - logging.AddSeq("http://localhost:5341"); - - logging.AddConsole(); - logging.AddDebug(); - }) - .UseStartup() - .Build(); - - webHost.Run(); + public void ConfigureServices(IServiceCollection services) + { + // Added + services.AddLogging(builder => builder.AddSeq()) + + // Other configuration follows... + } } ``` @@ -96,7 +79,7 @@ The `AddSeq()` method exposes some basic options for controlling the connection | Parameter | Description | Example value | | --------- | ----------- | ------------- | -| `apiKey` | A Seq [API key](http://docs.getseq.net/docs/api-keys) to authenticate or tag messages from the logger | `"1234567890"` | +| `apiKey` | A Seq [API key](http://docs.datalust.co/docs/api-keys) to authenticate or tag messages from the logger | `"1234567890"` | | `levelOverrides` | A dictionary mapping logger name prefixes to minimum logging levels | `new Dictionary{ ["Microsoft"] = LogLevel.Warning }` | | `minimumLevel` | The level below which events will be suppressed (the default is `Information`) | `LogLevel.Trace` | @@ -104,40 +87,29 @@ The `AddSeq()` method exposes some basic options for controlling the connection The logging level, Seq server URL, API key and other settings can be read from JSON configuration if desired. -In `appsettings.json` add a `"Seq"` property to `"Logging"` to configure levels for the Seq provider: +In `appsettings.json` add a `"Seq"` property to `"Logging"` to configure the server URL, API key, and levels for the Seq provider: ```json { "Logging": { - "IncludeScopes": false, "LogLevel": { "Default": "Information" }, "Seq": { + "ServerUrl": "http://localhost:5341", + "ApiKey": "1234567890", "LogLevel": { - "Default": "Trace", "System": "Information", "Microsoft": "Warning" } } - }, - "Seq": { - "ServerUrl": "http://localhost:5341", - "ApiKey": "1234567890", } } ``` -An additional root `"Seq"` property is used to configure the server URL and API key. To use this, -pass the configuration section to the `AddSeq()` method: - -```csharp - logging.AddSeq(Configuration.GetSection("Seq")); -``` - ### Dynamic log level control -The logging provider will dynamically adjust the default logging level up or down based on the level associated with an API key in Seq. For further information see the [Seq documentation](http://docs.getseq.net/docs/using-serilog#dynamic-level-control). +The logging provider will dynamically adjust the default logging level up or down based on the level associated with an API key in Seq. For further information see the [Seq documentation](http://docs.datalust.co/docs/using-serilog#dynamic-level-control). ### Troubleshooting @@ -173,7 +145,7 @@ Seq.Extensions.Logging.SelfLog.Enable(message => { * Check the Seq _Ingestion Log_, as described in the _Server-side issues_ section above. * Turn on the `SelfLog` as described above to check for connectivity problems and other issues on the client side. - * [Raise an issue](https://github.com/datalust/seq-extensions-logging/issues), ask for help on the [Seq support forum](http://docs.getseq.net/discuss) or email **support@getseq.net**. + * [Raise an issue](https://github.com/datalust/seq-extensions-logging/issues), ask for help on the [Seq support forum](http://docs.datalust.co/discuss) or email **support@datalust.co**. ### Migrating to Serilog diff --git a/asset/screenshot.png b/asset/screenshot.png index 0c2443b..13f544f 100644 Binary files a/asset/screenshot.png and b/asset/screenshot.png differ diff --git a/example/WebApplication/Startup.cs b/example/WebApplication/Startup.cs index d75b9f4..f80fcaa 100644 --- a/example/WebApplication/Startup.cs +++ b/example/WebApplication/Startup.cs @@ -25,11 +25,8 @@ public void ConfigureServices(IServiceCollection services) { services.AddLogging(loggingBuilder => { - loggingBuilder.ClearProviders(); - loggingBuilder.AddConfiguration(Configuration.GetSection("Logging")); - - // Add a Seq logger. - loggingBuilder.AddSeq(Configuration.GetSection("Seq")); + // Add the Seq logger, with configuration from appsettings.json + loggingBuilder.AddSeq(); loggingBuilder.AddConsole(); loggingBuilder.AddDebug(); diff --git a/example/WebApplication/appsettings.json b/example/WebApplication/appsettings.json index 3ddad78..ba11e4d 100644 --- a/example/WebApplication/appsettings.json +++ b/example/WebApplication/appsettings.json @@ -1,19 +1,11 @@ { "Logging": { - "IncludeScopes": false, "LogLevel": { "Default": "Information" }, "Seq": { - "LogLevel": { - "Default": "Trace", - "System": "Information", - "Microsoft": "Warning" - } + "ServerUrl": "http://localhost:5341", + "ApiKey": "" } - }, - "Seq": { - "ServerUrl": "http://localhost:5341", - "ApiKey": "" } } diff --git a/seq-extensions-logging.sln.DotSettings b/seq-extensions-logging.sln.DotSettings index 06a0197..4927542 100644 --- a/seq-extensions-logging.sln.DotSettings +++ b/seq-extensions-logging.sln.DotSettings @@ -1,2 +1,3 @@  - <Policy Inspect="True" Prefix="_" Suffix="" Style="aaBb"><ExtraRule Prefix="" Suffix="" Style="AaBb" /></Policy> \ No newline at end of file + <Policy Inspect="True" Prefix="_" Suffix="" Style="aaBb"><ExtraRule Prefix="" Suffix="" Style="AaBb" /></Policy> + True \ No newline at end of file diff --git a/src/Seq.Extensions.Logging/Microsoft/Extensions/Logging/SeqLoggerExtensions.cs b/src/Seq.Extensions.Logging/Microsoft/Extensions/Logging/SeqLoggerExtensions.cs index da81889..d901852 100644 --- a/src/Seq.Extensions.Logging/Microsoft/Extensions/Logging/SeqLoggerExtensions.cs +++ b/src/Seq.Extensions.Logging/Microsoft/Extensions/Logging/SeqLoggerExtensions.cs @@ -6,10 +6,8 @@ using Serilog.Events; using Serilog.Extensions.Logging; using Serilog.Sinks.Seq; - -#if LOGGING_BUILDER using Microsoft.Extensions.DependencyInjection; -#endif +using Microsoft.Extensions.Logging.Configuration; namespace Microsoft.Extensions.Logging { @@ -61,9 +59,6 @@ public static ILoggerFactory AddSeq( return loggerFactory; } - -#if LOGGING_BUILDER - /// /// Adds a Seq logger. /// @@ -79,9 +74,12 @@ public static ILoggingBuilder AddSeq( if (loggingBuilder == null) throw new ArgumentNullException(nameof(loggingBuilder)); if (serverUrl == null) throw new ArgumentNullException(nameof(serverUrl)); - var provider = CreateProvider(serverUrl, apiKey, LevelAlias.Minimum, null); - - loggingBuilder.Services.AddSingleton(_ => provider); + loggingBuilder.Services.AddSingleton(s => + { + var opts = s.GetService>(); + var provider = CreateProvider(opts?.Configuration, serverUrl, apiKey); + return provider; + }); return loggingBuilder; } @@ -105,8 +103,6 @@ public static ILoggingBuilder AddSeq( return loggingBuilder; } -#endif - static bool TryCreateProvider( IConfigurationSection configuration, LogLevel defaultMinimumLevel, @@ -152,6 +148,27 @@ static bool TryCreateProvider( return true; } + static SerilogLoggerProvider CreateProvider( + IConfiguration configuration, + string defaultServerUrl, + string defaultApiKey) + { + string serverUrl = null, apiKey = null; + if (configuration != null) + { + serverUrl = configuration["ServerUrl"]; + apiKey = configuration["ApiKey"]; + } + + if (string.IsNullOrWhiteSpace(serverUrl)) + serverUrl = defaultServerUrl; + + if (string.IsNullOrWhiteSpace(apiKey)) + apiKey = defaultApiKey; + + return CreateProvider(serverUrl, apiKey, LevelAlias.Minimum, null); + } + static SerilogLoggerProvider CreateProvider( string serverUrl, string apiKey, diff --git a/src/Seq.Extensions.Logging/Properties/AssemblyInfo.cs b/src/Seq.Extensions.Logging/Properties/AssemblyInfo.cs index 02b4ac5..3c928b8 100644 --- a/src/Seq.Extensions.Logging/Properties/AssemblyInfo.cs +++ b/src/Seq.Extensions.Logging/Properties/AssemblyInfo.cs @@ -1,19 +1,4 @@ -using System.Reflection; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; - -// General Information about an assembly is controlled through the following -// set of attributes. Change these attribute values to modify the information -// associated with an assembly. -[assembly: AssemblyConfiguration("")] -[assembly: AssemblyCompany("")] -[assembly: AssemblyProduct("Seq.Extensions.Logging")] -[assembly: AssemblyTrademark("")] - -// Setting ComVisible to false makes the types in this assembly not visible -// to COM components. If you need to access a type in this assembly from -// COM, set the ComVisible attribute to true on that type. -[assembly: ComVisible(false)] +using System.Runtime.CompilerServices; [assembly: InternalsVisibleTo("Seq.Extensions.Logging.Tests, PublicKey=" + "00240000048000009400000006020000002400005253413100040000010001003181403962b35f" + diff --git a/src/Seq.Extensions.Logging/Seq.Extensions.Logging.csproj b/src/Seq.Extensions.Logging/Seq.Extensions.Logging.csproj index b78b864..0ffd3fa 100644 --- a/src/Seq.Extensions.Logging/Seq.Extensions.Logging.csproj +++ b/src/Seq.Extensions.Logging/Seq.Extensions.Logging.csproj @@ -2,54 +2,24 @@ Add centralized structured log collection to ASP.NET Core apps with one line of code. - 4.0.2 + 5.0.0 Datalust and Contributors - net45;net46;net461;netstandard1.3;netstandard2.0 + net462;netstandard2.0 true true - Seq.Extensions.Logging ../../asset/seqext.snk true true - Seq.Extensions.Logging - asp.net;core;logging + aspnet;logging https://datalust.co/images/nuget/seq.png https://github.com/datalust/seq-extensions-logging - http://www.apache.org/licenses/LICENSE-2.0 - false - false - false + Apache-2.0 - + + + - - - - - - - - - - - - - - - - - $(DefineConstants);REMOTING;THREADING_TIMER;WAITABLE_TIMER;HRESULTS - - - - $(DefineConstants);ASYNCLOCAL - - - - $(DefineConstants);ASYNCLOCAL;LOGGING_BUILDER - - diff --git a/src/Seq.Extensions.Logging/Serilog/Extensions/Logging/SerilogLoggerProvider.cs b/src/Seq.Extensions.Logging/Serilog/Extensions/Logging/SerilogLoggerProvider.cs index 5f010bc..9088b69 100644 --- a/src/Seq.Extensions.Logging/Serilog/Extensions/Logging/SerilogLoggerProvider.cs +++ b/src/Seq.Extensions.Logging/Serilog/Extensions/Logging/SerilogLoggerProvider.cs @@ -2,12 +2,7 @@ // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; -#if ASYNCLOCAL using System.Threading; -#else -using System.Runtime.Remoting; -using System.Runtime.Remoting.Messaging; -#endif using Microsoft.Extensions.Logging; using Serilog.Core; using Serilog.Events; @@ -19,9 +14,7 @@ namespace Serilog.Extensions.Logging /// /// An that pipes events through Serilog. /// -#if LOGGING_BUILDER [ProviderAlias("Seq")] -#endif class SerilogLoggerProvider : ILoggerProvider, ILogEventEnricher { internal const string OriginalFormatPropertyName = "{OriginalFormat}"; @@ -77,7 +70,6 @@ public void Enrich(LogEvent logEvent, ILogEventPropertyFactory propertyFactory) } } -#if ASYNCLOCAL readonly AsyncLocal _value = new AsyncLocal(); internal SerilogLoggerScope CurrentScope @@ -85,19 +77,6 @@ internal SerilogLoggerScope CurrentScope get => _value.Value; set => _value.Value = value; } -#else - readonly string _currentScopeKey = nameof(SerilogLoggerScope) + "#" + Guid.NewGuid().ToString("n"); - - internal SerilogLoggerScope CurrentScope - { - get - { - var objectHandle = CallContext.LogicalGetData(_currentScopeKey) as ObjectHandle; - return objectHandle?.Unwrap() as SerilogLoggerScope; - } - set => CallContext.LogicalSetData(_currentScopeKey, new ObjectHandle(value)); - } -#endif /// public void Dispose() diff --git a/src/Seq.Extensions.Logging/Serilog/Sinks/PeriodicBatching/PortableTimer.cs b/src/Seq.Extensions.Logging/Serilog/Sinks/PeriodicBatching/PortableTimer.cs index 71a8e3a..7c3648b 100644 --- a/src/Seq.Extensions.Logging/Serilog/Sinks/PeriodicBatching/PortableTimer.cs +++ b/src/Seq.Extensions.Logging/Serilog/Sinks/PeriodicBatching/PortableTimer.cs @@ -26,10 +26,6 @@ class PortableTimer : IDisposable readonly Action _onTick; readonly CancellationTokenSource _cancel = new CancellationTokenSource(); -#if THREADING_TIMER - readonly Timer _timer; -#endif - bool _running; bool _disposed; @@ -38,10 +34,6 @@ public PortableTimer(Action onTick) if (onTick == null) throw new ArgumentNullException(nameof(onTick)); _onTick = onTick; - -#if THREADING_TIMER - _timer = new Timer(_ => OnTick(), null, Timeout.Infinite, Timeout.Infinite); -#endif } public void Start(TimeSpan interval) @@ -53,9 +45,6 @@ public void Start(TimeSpan interval) if (_disposed) throw new ObjectDisposedException(nameof(PortableTimer)); -#if THREADING_TIMER - _timer.Change(interval, Timeout.InfiniteTimeSpan); -#else Task.Delay(interval, _cancel.Token) .ContinueWith( _ => OnTick(), @@ -63,7 +52,6 @@ public void Start(TimeSpan interval) TaskContinuationOptions.DenyChildAttach, TaskScheduler.Default) .AsObserved(); -#endif } } @@ -129,10 +117,6 @@ public void Dispose() Monitor.Wait(_stateLock); } -#if THREADING_TIMER - _timer.Dispose(); -#endif - _disposed = true; } } diff --git a/test/Seq.Extensions.Logging.Tests/Properties/AssemblyInfo.cs b/test/Seq.Extensions.Logging.Tests/Properties/AssemblyInfo.cs deleted file mode 100644 index 1b5f26e..0000000 --- a/test/Seq.Extensions.Logging.Tests/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,19 +0,0 @@ -using System.Reflection; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; - -// General Information about an assembly is controlled through the following -// set of attributes. Change these attribute values to modify the information -// associated with an assembly. -[assembly: AssemblyConfiguration("")] -[assembly: AssemblyCompany("")] -[assembly: AssemblyProduct("Seq.Extensions.Logging.Tests")] -[assembly: AssemblyTrademark("")] - -// Setting ComVisible to false makes the types in this assembly not visible -// to COM components. If you need to access a type in this assembly from -// COM, set the ComVisible attribute to true on that type. -[assembly: ComVisible(false)] - -// The following GUID is for the ID of the typelib if this project is exposed to COM -[assembly: Guid("64a3f2c5-d71e-44a6-8dc7-99474765b32e")] 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 2115726..2643083 100644 --- a/test/Seq.Extensions.Logging.Tests/Seq.Extensions.Logging.Tests.csproj +++ b/test/Seq.Extensions.Logging.Tests/Seq.Extensions.Logging.Tests.csproj @@ -1,18 +1,10 @@ - + - net46;netcoreapp1.0;netcoreapp2.0 - Seq.Extensions.Logging.Tests + net472;netcoreapp3.0 ../../asset/seqext.snk true true - Seq.Extensions.Logging.Tests - true - $(PackageTargetFallback);dnxcore50;portable-net45+win8 - 1.0.4 - false - false - false @@ -20,14 +12,16 @@ - - - + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + - - - + + 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 35fafb0..73d91ce 100644 --- a/test/Seq.Extensions.Logging.Tests/Serilog/Extensions/Logging/SerilogLoggerTests.cs +++ b/test/Seq.Extensions.Logging.Tests/Serilog/Extensions/Logging/SerilogLoggerTests.cs @@ -42,7 +42,7 @@ public void LogsWhenNullFilterGiven() logger.Log(LogLevel.Information, 0, TestMessage, null, null); - Assert.Equal(1, sink.Writes.Count); + Assert.Single(sink.Writes); } [Fact] @@ -152,7 +152,7 @@ public void CarriesException() logger.Log(LogLevel.Information, 0, "Test", exception, null); - Assert.Equal(1, sink.Writes.Count); + Assert.Single(sink.Writes); Assert.Same(exception, sink.Writes[0].Exception); } @@ -168,7 +168,7 @@ public void SingleScopeProperty() logger.Log(LogLevel.Information, 0, TestMessage, null, null); } - Assert.Equal(1, sink.Writes.Count); + Assert.Single(sink.Writes); Assert.True(sink.Writes[0].Properties.ContainsKey("Name")); Assert.Equal("\"pizza\"", sink.Writes[0].Properties["Name"].ToString()); } @@ -189,7 +189,7 @@ public void NestedScopeSameProperty() } // Should retain the property of the most specific scope - Assert.Equal(1, sink.Writes.Count); + Assert.Single(sink.Writes); Assert.True(sink.Writes[0].Properties.ContainsKey("Name")); Assert.Equal("\"bacon\"", sink.Writes[0].Properties["Name"].ToString()); } @@ -209,7 +209,7 @@ public void NestedScopesDifferentProperties() } } - Assert.Equal(1, sink.Writes.Count); + Assert.Single(sink.Writes); Assert.True(sink.Writes[0].Properties.ContainsKey("Name")); Assert.Equal("\"spaghetti\"", sink.Writes[0].Properties["Name"].ToString()); Assert.True(sink.Writes[0].Properties.ContainsKey("LuckyNumber")); @@ -247,7 +247,7 @@ public void CarriesEventIdIfNonzero() logger.Log(LogLevel.Information, expected, "Test", null, null); - Assert.Equal(1, sink.Writes.Count); + Assert.Single(sink.Writes); var eventId = (StructureValue) sink.Writes[0].Properties["EventId"]; var id = (ScalarValue) eventId.Properties.Single(p => p.Name == "Id").Value; @@ -266,7 +266,7 @@ public void BeginScopeDestructuresObjectsWhenDestructurerIsUsedInMessageTemplate logger.Log(LogLevel.Information, 0, TestMessage, null, null); } - Assert.Equal(1, sink.Writes.Count); + Assert.Single(sink.Writes); Assert.True(sink.Writes[0].Properties.ContainsKey("Person")); var person = (StructureValue)sink.Writes[0].Properties["Person"]; @@ -288,7 +288,7 @@ public void BeginScopeDestructuresObjectsWhenDestructurerIsUsedInDictionary() logger.Log(LogLevel.Information, 0, TestMessage, null, null); } - Assert.Equal(1, sink.Writes.Count); + Assert.Single(sink.Writes); Assert.True(sink.Writes[0].Properties.ContainsKey("Person")); var person = (StructureValue)sink.Writes[0].Properties["Person"]; @@ -310,7 +310,7 @@ public void BeginScopeDoesNotModifyKeyWhenDestructurerIsNotUsedInMessageTemplate logger.Log(LogLevel.Information, 0, TestMessage, null, null); } - Assert.Equal(1, sink.Writes.Count); + Assert.Single(sink.Writes); Assert.True(sink.Writes[0].Properties.ContainsKey("FirstName")); } @@ -326,7 +326,7 @@ public void BeginScopeDoesNotModifyKeyWhenDestructurerIsNotUsedInDictionary() logger.Log(LogLevel.Information, 0, TestMessage, null, null); } - Assert.Equal(1, sink.Writes.Count); + Assert.Single(sink.Writes); Assert.True(sink.Writes[0].Properties.ContainsKey("FirstName")); } @@ -343,7 +343,7 @@ public void NamedScopesAreCaptured() logger.Log(LogLevel.Information, 0, TestMessage, null, null); } - Assert.Equal(1, sink.Writes.Count); + Assert.Single(sink.Writes); LogEventPropertyValue scopeValue; Assert.True(sink.Writes[0].Properties.TryGetValue(SerilogLoggerProvider.ScopePropertyName, out scopeValue));