From d66061568c1cf4d4bb3150c766cb3747c707dbf7 Mon Sep 17 00:00:00 2001 From: Nicholas Blumhardt Date: Mon, 28 Aug 2017 08:30:39 +1000 Subject: [PATCH 1/7] Initial version --- .gitattributes | 3 + Build.ps1 | 56 +++ CHANGES.md | 4 + README.md | 90 ++++- appveyor.yml | 29 ++ assets/Serilog.snk | Bin 0 -> 596 bytes build.sh | 11 + global.json | 5 + .../Controllers/ScopesController.cs | 37 ++ .../Controllers/ValuesController.cs | 19 + samples/SimpleWebSample/Program.cs | 53 +++ .../SimpleWebSample/SimpleWebSample.csproj | 20 + samples/SimpleWebSample/Startup.cs | 40 ++ .../appsettings.Development.json | 7 + samples/SimpleWebSample/appsettings.json | 11 + serilog-aspnetcore.sln | 57 +++ .../AspNetCore/SerilogLoggerFactory.cs | 45 +++ .../Properties/AssemblyInfo.cs | 11 + .../Serilog.AspNetCore.csproj | 31 ++ .../SerilogWebHostBuilderExtensions.cs | 45 +++ .../Serilog.AspNetCore.Tests.csproj | 22 ++ .../SerilogWebHostBuilderExtensionsTests.cs | 16 + .../Support/DisposeTrackingLogger.cs | 354 ++++++++++++++++++ .../Support/SerilogSink.cs | 19 + 24 files changed, 983 insertions(+), 2 deletions(-) create mode 100644 .gitattributes create mode 100644 Build.ps1 create mode 100644 CHANGES.md create mode 100644 appveyor.yml create mode 100644 assets/Serilog.snk create mode 100644 build.sh create mode 100644 global.json create mode 100644 samples/SimpleWebSample/Controllers/ScopesController.cs create mode 100644 samples/SimpleWebSample/Controllers/ValuesController.cs create mode 100644 samples/SimpleWebSample/Program.cs create mode 100644 samples/SimpleWebSample/SimpleWebSample.csproj create mode 100644 samples/SimpleWebSample/Startup.cs create mode 100644 samples/SimpleWebSample/appsettings.Development.json create mode 100644 samples/SimpleWebSample/appsettings.json create mode 100644 serilog-aspnetcore.sln create mode 100644 src/Serilog.AspNetCore/AspNetCore/SerilogLoggerFactory.cs create mode 100644 src/Serilog.AspNetCore/Properties/AssemblyInfo.cs create mode 100644 src/Serilog.AspNetCore/Serilog.AspNetCore.csproj create mode 100644 src/Serilog.AspNetCore/SerilogWebHostBuilderExtensions.cs create mode 100644 test/Serilog.AspNetCore.Tests/Serilog.AspNetCore.Tests.csproj create mode 100644 test/Serilog.AspNetCore.Tests/SerilogWebHostBuilderExtensionsTests.cs create mode 100644 test/Serilog.AspNetCore.Tests/Support/DisposeTrackingLogger.cs create mode 100644 test/Serilog.AspNetCore.Tests/Support/SerilogSink.cs diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..b84a08f --- /dev/null +++ b/.gitattributes @@ -0,0 +1,3 @@ +# Auto detect text files and perform LF normalization + +* text=auto diff --git a/Build.ps1 b/Build.ps1 new file mode 100644 index 0000000..41fbf5d --- /dev/null +++ b/Build.ps1 @@ -0,0 +1,56 @@ +echo "build: Build started" + +Push-Location $PSScriptRoot + +if(Test-Path .\artifacts) { + echo "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 "master" -and $revision -ne "local"] + +echo "build: Version suffix is $suffix" + +foreach ($src in ls src/*) { + Push-Location $src + + echo "build: Packaging project in $src" + + if($suffix) { + & dotnet pack -c Release --include-source -o ..\..\artifacts --version-suffix=$suffix + } else { + & dotnet pack -c Release --include-source -o ..\..\artifacts + } + + if($LASTEXITCODE -ne 0) { exit 1 } + + Pop-Location +} + +foreach ($test in ls test/*.PerformanceTests) { + Push-Location $test + + echo "build: Building performance test project in $test" + + & dotnet build -c Release + if($LASTEXITCODE -ne 0) { exit 2 } + + Pop-Location +} + +foreach ($test in ls test/*.Tests) { + Push-Location $test + + echo "build: Testing project in $test" + + & dotnet test -c Release + if($LASTEXITCODE -ne 0) { exit 3 } + + Pop-Location +} + +Pop-Location diff --git a/CHANGES.md b/CHANGES.md new file mode 100644 index 0000000..be62715 --- /dev/null +++ b/CHANGES.md @@ -0,0 +1,4 @@ +2.0.0 + + * Initial version for ASP.NET Core 2. + diff --git a/README.md b/README.md index 09d256e..e46c493 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,88 @@ -# serilog-aspnetcore -Serilog integration for ASP.NET Core 2+ +# Serilog.AspNetCore [![Build status](https://ci.appveyor.com/api/projects/status/865nohxfiq1rnby0/branch/master?svg=true)](https://ci.appveyor.com/project/serilog/serilog-framework-logging/history) [![NuGet Version](http://img.shields.io/nuget/v/Serilog.Extensions.Logging.svg?style=flat)](https://www.nuget.org/packages/Serilog.Extensions.Logging/) + + +Serilog logging for ASP.NET Core. This package routes ASP.NET Core log messages through Serilog, so you can get information about ASP.NET's internal operations logged to the same Serilog sinks as your application events. + +### Instructions + +**First**, install the _Serilog.AspNetCore_ [NuGet package](https://www.nuget.org/packages/Serilog.AspNetCore) into your app. You will need a way to view the log messages - _Serilog.Sinks.Console_ writes these to the console, and _Serilog.Sinks.Debug_ writes to the Visual Studio output window; there are [many more sinks available](https://www.nuget.org/packages?q=Tags%3A%22serilog%22) on NuGet. + +```powershell +Install-Package Serilog.AspNetCore -DependencyVersion Highest +Install-Package Serilog.Sinks.Console +Install-Package Serilog.Sinks.Debug +``` + +**Next**, in your application's _Program.cs_ file, configure Serilog first: + +```csharp +public class Program +{ + public static int Main(string[] args) + { + Log.Logger = new LoggerConfiguration() + .MinimumLevel.Debug() + .MinimumLevel.Override("Microsoft", LogEventLevel.Information) + .Enrich.FromLogContext() + .WriteTo.Console() + .WriteTo.Debug() + .CreateLogger(); +``` + +Then, add `UseSerilog()` to the web host builder. A `try`/`catch` block will ensure any configuration issues are appropriately logged: + +```csharp + try + { + Log.Information("Starting web host"); + + var host = new WebHostBuilder() + .UseKestrel() + .UseContentRoot(Directory.GetCurrentDirectory()) + .UseIISIntegration() + .UseStartup() + .UseSerilog() // <-- Add this line + .Build(); + + host.Run(); + + return 0; + } + catch (Exception ex) + { + Log.Fatal(ex, "Host terminated unexpectedly"); + return 1; + } + finally + { + Log.CloseAndFlush(); + } + } +} +``` + +**Finally**, clean up by removing the remaining configuration for the default logger: + + * Remove calls to `AddLogging()` + * Remove the `"Logging"` section from _appsettings.json_ files (this can be replaced with [Serilog configuration](https://github.com/serilog/serilog-settings-configuration) as shown in [this example](), if required) + * Remove `ILoggerFactory` parameters and any `Add*()` calls on the logger factory in _Startup.cs_ + * Remove `UseApplicationInsights()` (this can be replaced with the [Serilog AI sink](https://github.com/serilog/serilog-sinks-applicationinsights), if required) + +That's it! With the level bumped up a little you will see log output like: + +``` +[22:14:44.646 DBG] RouteCollection.RouteAsync + Routes: + Microsoft.AspNet.Mvc.Routing.AttributeRoute + {controller=Home}/{action=Index}/{id?} + Handled? True +[22:14:44.647 DBG] RouterMiddleware.Invoke + Handled? True +[22:14:45.706 DBG] /lib/jquery/jquery.js not modified +[22:14:45.706 DBG] /css/site.css not modified +[22:14:45.741 DBG] Handled. Status code: 304 File: /css/site.css +``` + +### Using the package + +With _Serilog.AspNetCore_ installed and configured, you can write log messages directly through Serilog or any `ILogger` interface injected by ASP.NET. All loggers will use the same underlying implementation, levels, and destinations. diff --git a/appveyor.yml b/appveyor.yml new file mode 100644 index 0000000..c13bd52 --- /dev/null +++ b/appveyor.yml @@ -0,0 +1,29 @@ +version: '{build}' +skip_tags: true +image: Visual Studio 2017 Preview +configuration: Release +install: + - ps: mkdir -Force ".\build\" | Out-Null + #- ps: Invoke-WebRequest "https://raw.githubusercontent.com/dotnet/cli/release/2.0.0/scripts/obtain/dotnet-install.ps1" -OutFile ".\build\installcli.ps1" + #- ps: $env:DOTNET_INSTALL_DIR = "$pwd\.dotnetcli" + #- ps: '& .\build\installcli.ps1 -InstallDir "$env:DOTNET_INSTALL_DIR" -NoPath -Version 2.0.0-preview2-006497' + #- ps: $env:Path = "$env:DOTNET_INSTALL_DIR;$env:Path" +build_script: +- ps: ./Build.ps1 +test: off +artifacts: +- path: artifacts/Serilog.*.nupkg +deploy: +- provider: NuGet + api_key: + secure: nvZ/z+pMS91b3kG4DgfES5AcmwwGoBYQxr9kp4XiJHj25SAlgdIxFx++1N0lFH2x + skip_symbols: true + on: + branch: /^(master|dev)$/ +- provider: GitHub + auth_token: + secure: p4LpVhBKxGS5WqucHxFQ5c7C8cP74kbNB0Z8k9Oxx/PMaDQ1+ibmoexNqVU5ZlmX + artifact: /Serilog.*\.nupkg/ + tag: v$(appveyor_build_version) + on: + branch: master diff --git a/assets/Serilog.snk b/assets/Serilog.snk new file mode 100644 index 0000000000000000000000000000000000000000..6d662e5cdc929e43c71cfc64514bb410395bec97 GIT binary patch literal 596 zcmV-a0;~N80ssI2Bme+XQ$aES1ONa50098|jT8MeN*r(C{)6u`!3YcU29S1J#_J98 zZol7;A!iI^CR;Gu3HLe;>RUau$-lzl!fdY z>2jw{n`-|ngZlxMYM3RQIgrl=dHgwzRv}V<($tqHW?{E_ze}8XxNvMr z&?hS-?&$$V zPxG|}g1dD39L|vaU#iGcUO>V2&Px7{=>1LMo{+W_ver%m`aj>a*1HO=G|4%DK3B=b zh;TEEIK?K|8ga>Uo|MZ{F9m9NuxU|sMDznV;6Nc&)i}b9LJi7*5_8I%Ab;f%q+Sy^ zwDuyUa%vfY_n3f>uW(Zg?}T8oU?rJ86(lCUdtE(PfZ{~;I%;58Ya)4rtN!UtGb%RC z!}E8`V-Ntz;J1zlQQ4^m?}wkPqpqmKx=2JhX<&!sRqGNxz<=O=v#_jgUB!ciUBUL| i!(?$$p+ZI+LCT_)i`wqHx9}7zTDk9 _logger; + + public ScopesController(ILogger logger) + { + _logger = logger; + } + + // GET api/scopes + [HttpGet] + public IEnumerable Get() + { + _logger.LogInformation("Before"); + + using (_logger.BeginScope("Some name")) + using (_logger.BeginScope(42)) + using (_logger.BeginScope("Formatted {WithValue}", 12345)) + using (_logger.BeginScope(new Dictionary { ["ViaDictionary"] = 100 })) + { + _logger.LogInformation("Hello from the Index!"); + _logger.LogDebug("Hello is done"); + } + + _logger.LogInformation("After"); + + return new string[] { "value1", "value2" }; + } + } +} diff --git a/samples/SimpleWebSample/Controllers/ValuesController.cs b/samples/SimpleWebSample/Controllers/ValuesController.cs new file mode 100644 index 0000000..f14dafc --- /dev/null +++ b/samples/SimpleWebSample/Controllers/ValuesController.cs @@ -0,0 +1,19 @@ +using System.Collections.Generic; +using Microsoft.AspNetCore.Mvc; +using Serilog; + +namespace SimpleWebSample.Controllers +{ + [Route("api/[controller]")] + public class ValuesController : Controller + { + // GET api/values + [HttpGet] + public IEnumerable Get() + { + // Directly through Serilog + Log.Information("This is a handler for {Path}", Request.Path); + return new string[] { "value1", "value2" }; + } + } +} diff --git a/samples/SimpleWebSample/Program.cs b/samples/SimpleWebSample/Program.cs new file mode 100644 index 0000000..fc4d20a --- /dev/null +++ b/samples/SimpleWebSample/Program.cs @@ -0,0 +1,53 @@ +using System; +using System.IO; +using Microsoft.AspNetCore.Hosting; +using Microsoft.Extensions.Configuration; +using Serilog; + +namespace SimpleWebSample +{ + public class Program + { + public static int Main(string[] args) + { + var configuration = new ConfigurationBuilder() + .SetBasePath(Directory.GetCurrentDirectory()) + .AddJsonFile("appsettings.json", optional: false, reloadOnChange: true) + .AddJsonFile($"appsettings.{Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT")}.json", optional: true) + .Build(); + + Log.Logger = new LoggerConfiguration() + .ReadFrom.Configuration(configuration) + .Enrich.FromLogContext() + .WriteTo.Console() + .CreateLogger(); + + try + { + Log.Information("Getting the motors running..."); + + var host = new WebHostBuilder() + .UseKestrel() + .UseContentRoot(Directory.GetCurrentDirectory()) + .UseIISIntegration() + .UseStartup() + .UseConfiguration(configuration) + .UseSerilog() + .Build(); + + host.Run(); + + return 0; + } + catch (Exception ex) + { + Log.Fatal(ex, "Host terminated unexpectedly"); + return 1; + } + finally + { + Log.CloseAndFlush(); + } + } + } +} diff --git a/samples/SimpleWebSample/SimpleWebSample.csproj b/samples/SimpleWebSample/SimpleWebSample.csproj new file mode 100644 index 0000000..9977d79 --- /dev/null +++ b/samples/SimpleWebSample/SimpleWebSample.csproj @@ -0,0 +1,20 @@ + + + netcoreapp2.0 + + + + + + + + + + + + + + + + + diff --git a/samples/SimpleWebSample/Startup.cs b/samples/SimpleWebSample/Startup.cs new file mode 100644 index 0000000..84b2abc --- /dev/null +++ b/samples/SimpleWebSample/Startup.cs @@ -0,0 +1,40 @@ +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Hosting; +using Microsoft.AspNetCore.Http; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; + +namespace SimpleWebSample +{ + public class Startup + { + public Startup(IConfiguration configuration) + { + Configuration = configuration; + } + + public IConfiguration Configuration { get; } + + // This method gets called by the runtime. Use this method to add services to the container. + public void ConfigureServices(IServiceCollection services) + { + services.AddMvc(); + } + + // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. + public void Configure(IApplicationBuilder app, IHostingEnvironment env) + { + if (env.IsDevelopment()) + { + app.UseDeveloperExceptionPage(); + } + + app.UseMvc(); + + app.Run(async (context) => + { + await context.Response.WriteAsync("Hello World!"); + }); + } + } +} diff --git a/samples/SimpleWebSample/appsettings.Development.json b/samples/SimpleWebSample/appsettings.Development.json new file mode 100644 index 0000000..a96c927 --- /dev/null +++ b/samples/SimpleWebSample/appsettings.Development.json @@ -0,0 +1,7 @@ +{ + "Serilog": { + "MinimumLevel": { + "Default": "Debug" + } + } +} diff --git a/samples/SimpleWebSample/appsettings.json b/samples/SimpleWebSample/appsettings.json new file mode 100644 index 0000000..4478f5c --- /dev/null +++ b/samples/SimpleWebSample/appsettings.json @@ -0,0 +1,11 @@ +{ + "Serilog": { + "MinimumLevel": { + "Default": "Debug", + "Override": { + "Microsoft": "Warning", + "System": "Warning" + } + } + } +} diff --git a/serilog-aspnetcore.sln b/serilog-aspnetcore.sln new file mode 100644 index 0000000..3957472 --- /dev/null +++ b/serilog-aspnetcore.sln @@ -0,0 +1,57 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 15 +VisualStudioVersion = 15.0.26730.10 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{A1893BD1-333D-4DFE-A0F0-DDBB2FE526E0}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "test", "test", "{E30F638E-BBBE-4AD1-93CE-48CC69CFEFE1}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "samples", "samples", "{F2407211-6043-439C-8E06-3641634332E7}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "assets", "assets", "{9C21B9DF-AEDD-4AA6-BEA4-912DEF3E5B8E}" + ProjectSection(SolutionItems) = preProject + appveyor.yml = appveyor.yml + Build.ps1 = Build.ps1 + global.json = global.json + README.md = README.md + assets\Serilog.snk = assets\Serilog.snk + EndProjectSection +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SimpleWebSample", "samples\SimpleWebSample\SimpleWebSample.csproj", "{69F9A0ED-7910-4F33-8919-28BB05376FBC}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Serilog.AspNetCore", "src\Serilog.AspNetCore\Serilog.AspNetCore.csproj", "{0549D23F-986B-4FB2-BACE-16FD7A7BC9EF}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Serilog.AspNetCore.Tests", "test\Serilog.AspNetCore.Tests\Serilog.AspNetCore.Tests.csproj", "{AD51759B-CD58-473F-9620-0B0E56A123A1}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {69F9A0ED-7910-4F33-8919-28BB05376FBC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {69F9A0ED-7910-4F33-8919-28BB05376FBC}.Debug|Any CPU.Build.0 = Debug|Any CPU + {69F9A0ED-7910-4F33-8919-28BB05376FBC}.Release|Any CPU.ActiveCfg = Release|Any CPU + {69F9A0ED-7910-4F33-8919-28BB05376FBC}.Release|Any CPU.Build.0 = Release|Any CPU + {0549D23F-986B-4FB2-BACE-16FD7A7BC9EF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {0549D23F-986B-4FB2-BACE-16FD7A7BC9EF}.Debug|Any CPU.Build.0 = Debug|Any CPU + {0549D23F-986B-4FB2-BACE-16FD7A7BC9EF}.Release|Any CPU.ActiveCfg = Release|Any CPU + {0549D23F-986B-4FB2-BACE-16FD7A7BC9EF}.Release|Any CPU.Build.0 = Release|Any CPU + {AD51759B-CD58-473F-9620-0B0E56A123A1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {AD51759B-CD58-473F-9620-0B0E56A123A1}.Debug|Any CPU.Build.0 = Debug|Any CPU + {AD51759B-CD58-473F-9620-0B0E56A123A1}.Release|Any CPU.ActiveCfg = Release|Any CPU + {AD51759B-CD58-473F-9620-0B0E56A123A1}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {69F9A0ED-7910-4F33-8919-28BB05376FBC} = {F2407211-6043-439C-8E06-3641634332E7} + {0549D23F-986B-4FB2-BACE-16FD7A7BC9EF} = {A1893BD1-333D-4DFE-A0F0-DDBB2FE526E0} + {AD51759B-CD58-473F-9620-0B0E56A123A1} = {E30F638E-BBBE-4AD1-93CE-48CC69CFEFE1} + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {811E61C5-3871-4633-AFAE-B35B619C8A10} + EndGlobalSection +EndGlobal diff --git a/src/Serilog.AspNetCore/AspNetCore/SerilogLoggerFactory.cs b/src/Serilog.AspNetCore/AspNetCore/SerilogLoggerFactory.cs new file mode 100644 index 0000000..ad5af8c --- /dev/null +++ b/src/Serilog.AspNetCore/AspNetCore/SerilogLoggerFactory.cs @@ -0,0 +1,45 @@ +// Copyright 2017 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. + +using Microsoft.Extensions.Logging; +using Serilog.Debugging; +using Serilog.Extensions.Logging; + +namespace Serilog.AspNetCore +{ + class SerilogLoggerFactory : ILoggerFactory + { + readonly SerilogLoggerProvider _provider; + + public SerilogLoggerFactory(Serilog.ILogger logger = null, bool dispose = false) + { + _provider = new SerilogLoggerProvider(logger, dispose); + } + + public void Dispose() + { + _provider.Dispose(); + } + + public Microsoft.Extensions.Logging.ILogger CreateLogger(string categoryName) + { + return _provider.CreateLogger(categoryName); + } + + public void AddProvider(ILoggerProvider provider) + { + SelfLog.WriteLine("Ignoring added logger provider {0}", provider); + } + } +} diff --git a/src/Serilog.AspNetCore/Properties/AssemblyInfo.cs b/src/Serilog.AspNetCore/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..9e85a56 --- /dev/null +++ b/src/Serilog.AspNetCore/Properties/AssemblyInfo.cs @@ -0,0 +1,11 @@ +using System.Reflection; +using System.Runtime.CompilerServices; + +[assembly: AssemblyVersion("2.0.0.0")] + +[assembly: InternalsVisibleTo("Serilog.AspNetCore.Tests, PublicKey=" + + "0024000004800000940000000602000000240000525341310004000001000100fb8d13fd344a1c" + + "6fe0fe83ef33c1080bf30690765bc6eb0df26ebfdf8f21670c64265b30db09f73a0dea5b3db4c9" + + "d18dbf6d5a25af5ce9016f281014d79dc3b4201ac646c451830fc7e61a2dfd633d34c39f87b818" + + "94191652df5ac63cc40c77f3542f702bda692e6e8a9158353df189007a49da0f3cfd55eb250066" + + "b19485ec")] diff --git a/src/Serilog.AspNetCore/Serilog.AspNetCore.csproj b/src/Serilog.AspNetCore/Serilog.AspNetCore.csproj new file mode 100644 index 0000000..f029249 --- /dev/null +++ b/src/Serilog.AspNetCore/Serilog.AspNetCore.csproj @@ -0,0 +1,31 @@ + + + + Serilog support for ASP.NET Core logging + 2.0.0 + Microsoft;Serilog Contributors + netstandard2.0 + true + true + Serilog.AspNetCore + ../../assets/Serilog.snk + true + true + Serilog.AspNetCore + serilog;aspnet;aspnetcore + http://serilog.net/images/serilog-extension-nuget.png + https://github.com/serilog/serilog-aspnetcore + http://www.apache.org/licenses/LICENSE-2.0 + false + Serilog + + + + + + + + + + + diff --git a/src/Serilog.AspNetCore/SerilogWebHostBuilderExtensions.cs b/src/Serilog.AspNetCore/SerilogWebHostBuilderExtensions.cs new file mode 100644 index 0000000..c7a1df1 --- /dev/null +++ b/src/Serilog.AspNetCore/SerilogWebHostBuilderExtensions.cs @@ -0,0 +1,45 @@ +// Copyright 2017 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. + +using System; +using Microsoft.AspNetCore.Hosting; +using Microsoft.Extensions.Logging; +using Serilog.AspNetCore; +using Microsoft.Extensions.DependencyInjection; + +namespace Serilog +{ + /// + /// Extends with Serilog configuration methods. + /// + public static class SerilogWebHostBuilderExtensions + { + /// + /// Sets Serilog as the logging provider. + /// + /// The web host builder to configure. + /// The Serilog logger; if not supplied, the static will be used. + /// When true, dispose when the framework disposes the provider. If the + /// logger is not specified but is true, the method will be + /// called on the static class instead. + /// The web host builder. + public static IWebHostBuilder UseSerilog(this IWebHostBuilder builder, Serilog.ILogger logger = null, bool dispose = false) + { + if (builder == null) throw new ArgumentNullException(nameof(builder)); + builder.ConfigureServices(collection => + collection.AddSingleton(new SerilogLoggerFactory(logger, dispose))); + return builder; + } + } +} diff --git a/test/Serilog.AspNetCore.Tests/Serilog.AspNetCore.Tests.csproj b/test/Serilog.AspNetCore.Tests/Serilog.AspNetCore.Tests.csproj new file mode 100644 index 0000000..71b2335 --- /dev/null +++ b/test/Serilog.AspNetCore.Tests/Serilog.AspNetCore.Tests.csproj @@ -0,0 +1,22 @@ + + + + netcoreapp2.0 + Serilog.AspNetCore.Tests + ../../assets/Serilog.snk + true + true + true + + + + + + + + + + + + + diff --git a/test/Serilog.AspNetCore.Tests/SerilogWebHostBuilderExtensionsTests.cs b/test/Serilog.AspNetCore.Tests/SerilogWebHostBuilderExtensionsTests.cs new file mode 100644 index 0000000..6aa9b1f --- /dev/null +++ b/test/Serilog.AspNetCore.Tests/SerilogWebHostBuilderExtensionsTests.cs @@ -0,0 +1,16 @@ +// 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 Xunit; + +namespace Serilog.AspNetCore.Tests +{ + public class SerilogWebHostBuilderExtensionsTests + { + [Fact] + public void Todo() + { + + } + } +} \ No newline at end of file diff --git a/test/Serilog.AspNetCore.Tests/Support/DisposeTrackingLogger.cs b/test/Serilog.AspNetCore.Tests/Support/DisposeTrackingLogger.cs new file mode 100644 index 0000000..70d3ef0 --- /dev/null +++ b/test/Serilog.AspNetCore.Tests/Support/DisposeTrackingLogger.cs @@ -0,0 +1,354 @@ +using System; +using System.Collections.Generic; +using Serilog.Core; +using Serilog.Events; + +namespace Serilog.AspNetCore.Tests.Support +{ + public class DisposeTrackingLogger : ILogger, IDisposable + { + public bool IsDisposed { get; set; } + + public ILogger ForContext(ILogEventEnricher enricher) + { + return new LoggerConfiguration().CreateLogger(); + } + + public ILogger ForContext(IEnumerable enrichers) + { + return new LoggerConfiguration().CreateLogger(); + } + + public ILogger ForContext(string propertyName, object value, bool destructureObjects = false) + { + return new LoggerConfiguration().CreateLogger(); + } + + public ILogger ForContext() + { + return new LoggerConfiguration().CreateLogger(); + } + + public ILogger ForContext(Type source) + { + return new LoggerConfiguration().CreateLogger(); + } + + public void Write(LogEvent logEvent) + { + } + + public void Write(LogEventLevel level, string messageTemplate) + { + } + + public void Write(LogEventLevel level, string messageTemplate, T propertyValue) + { + } + + public void Write(LogEventLevel level, string messageTemplate, T0 propertyValue0, T1 propertyValue1) + { + } + + public void Write(LogEventLevel level, string messageTemplate, T0 propertyValue0, T1 propertyValue1, + T2 propertyValue2) + { + } + + public void Write(LogEventLevel level, string messageTemplate, params object[] propertyValues) + { + } + + public void Write(LogEventLevel level, Exception exception, string messageTemplate) + { + } + + public void Write(LogEventLevel level, Exception exception, string messageTemplate, T propertyValue) + { + } + + public void Write(LogEventLevel level, Exception exception, string messageTemplate, T0 propertyValue0, + T1 propertyValue1) + { + } + + public void Write(LogEventLevel level, Exception exception, string messageTemplate, T0 propertyValue0, + T1 propertyValue1, T2 propertyValue2) + { + } + + public void Write(LogEventLevel level, Exception exception, string messageTemplate, params object[] propertyValues) + { + } + + public bool IsEnabled(LogEventLevel level) + { + return false; + } + + public void Verbose(string messageTemplate) + { + } + + public void Verbose(string messageTemplate, T propertyValue) + { + } + + public void Verbose(string messageTemplate, T0 propertyValue0, T1 propertyValue1) + { + } + + public void Verbose(string messageTemplate, T0 propertyValue0, T1 propertyValue1, T2 propertyValue2) + { + } + + public void Verbose(string messageTemplate, params object[] propertyValues) + { + } + + public void Verbose(Exception exception, string messageTemplate) + { + } + + public void Verbose(Exception exception, string messageTemplate, T propertyValue) + { + } + + public void Verbose(Exception exception, string messageTemplate, T0 propertyValue0, T1 propertyValue1) + { + } + + public void Verbose(Exception exception, string messageTemplate, T0 propertyValue0, T1 propertyValue1, + T2 propertyValue2) + { + } + + public void Verbose(Exception exception, string messageTemplate, params object[] propertyValues) + { + } + + public void Debug(string messageTemplate) + { + } + + public void Debug(string messageTemplate, T propertyValue) + { + } + + public void Debug(string messageTemplate, T0 propertyValue0, T1 propertyValue1) + { + } + + public void Debug(string messageTemplate, T0 propertyValue0, T1 propertyValue1, T2 propertyValue2) + { + } + + public void Debug(string messageTemplate, params object[] propertyValues) + { + } + + public void Debug(Exception exception, string messageTemplate) + { + } + + public void Debug(Exception exception, string messageTemplate, T propertyValue) + { + } + + public void Debug(Exception exception, string messageTemplate, T0 propertyValue0, T1 propertyValue1) + { + } + + public void Debug(Exception exception, string messageTemplate, T0 propertyValue0, T1 propertyValue1, + T2 propertyValue2) + { + } + + public void Debug(Exception exception, string messageTemplate, params object[] propertyValues) + { + } + + public void Information(string messageTemplate) + { + } + + public void Information(string messageTemplate, T propertyValue) + { + } + + public void Information(string messageTemplate, T0 propertyValue0, T1 propertyValue1) + { + } + + public void Information(string messageTemplate, T0 propertyValue0, T1 propertyValue1, T2 propertyValue2) + { + } + + public void Information(string messageTemplate, params object[] propertyValues) + { + } + + public void Information(Exception exception, string messageTemplate) + { + } + + public void Information(Exception exception, string messageTemplate, T propertyValue) + { + } + + public void Information(Exception exception, string messageTemplate, T0 propertyValue0, T1 propertyValue1) + { + } + + public void Information(Exception exception, string messageTemplate, T0 propertyValue0, T1 propertyValue1, + T2 propertyValue2) + { + } + + public void Information(Exception exception, string messageTemplate, params object[] propertyValues) + { + } + + public void Warning(string messageTemplate) + { + } + + public void Warning(string messageTemplate, T propertyValue) + { + } + + public void Warning(string messageTemplate, T0 propertyValue0, T1 propertyValue1) + { + } + + public void Warning(string messageTemplate, T0 propertyValue0, T1 propertyValue1, T2 propertyValue2) + { + } + + public void Warning(string messageTemplate, params object[] propertyValues) + { + } + + public void Warning(Exception exception, string messageTemplate) + { + } + + public void Warning(Exception exception, string messageTemplate, T propertyValue) + { + } + + public void Warning(Exception exception, string messageTemplate, T0 propertyValue0, T1 propertyValue1) + { + } + + public void Warning(Exception exception, string messageTemplate, T0 propertyValue0, T1 propertyValue1, + T2 propertyValue2) + { + } + + public void Warning(Exception exception, string messageTemplate, params object[] propertyValues) + { + } + + public void Error(string messageTemplate) + { + } + + public void Error(string messageTemplate, T propertyValue) + { + } + + public void Error(string messageTemplate, T0 propertyValue0, T1 propertyValue1) + { + } + + public void Error(string messageTemplate, T0 propertyValue0, T1 propertyValue1, T2 propertyValue2) + { + } + + public void Error(string messageTemplate, params object[] propertyValues) + { + } + + public void Error(Exception exception, string messageTemplate) + { + } + + public void Error(Exception exception, string messageTemplate, T propertyValue) + { + } + + public void Error(Exception exception, string messageTemplate, T0 propertyValue0, T1 propertyValue1) + { + } + + public void Error(Exception exception, string messageTemplate, T0 propertyValue0, T1 propertyValue1, + T2 propertyValue2) + { + } + + public void Error(Exception exception, string messageTemplate, params object[] propertyValues) + { + } + + public void Fatal(string messageTemplate) + { + } + + public void Fatal(string messageTemplate, T propertyValue) + { + } + + public void Fatal(string messageTemplate, T0 propertyValue0, T1 propertyValue1) + { + } + + public void Fatal(string messageTemplate, T0 propertyValue0, T1 propertyValue1, T2 propertyValue2) + { + } + + public void Fatal(string messageTemplate, params object[] propertyValues) + { + } + + public void Fatal(Exception exception, string messageTemplate) + { + } + + public void Fatal(Exception exception, string messageTemplate, T propertyValue) + { + } + + public void Fatal(Exception exception, string messageTemplate, T0 propertyValue0, T1 propertyValue1) + { + } + + public void Fatal(Exception exception, string messageTemplate, T0 propertyValue0, T1 propertyValue1, + T2 propertyValue2) + { + } + + public void Fatal(Exception exception, string messageTemplate, params object[] propertyValues) + { + } + + public bool BindMessageTemplate(string messageTemplate, object[] propertyValues, out MessageTemplate parsedTemplate, + out IEnumerable boundProperties) + { + parsedTemplate = null; + boundProperties = null; + return false; + } + + public bool BindProperty(string propertyName, object value, bool destructureObjects, out LogEventProperty property) + { + property = null; + return false; + } + + public void Dispose() + { + IsDisposed = true; + } + } +} diff --git a/test/Serilog.AspNetCore.Tests/Support/SerilogSink.cs b/test/Serilog.AspNetCore.Tests/Support/SerilogSink.cs new file mode 100644 index 0000000..5af3d36 --- /dev/null +++ b/test/Serilog.AspNetCore.Tests/Support/SerilogSink.cs @@ -0,0 +1,19 @@ +// 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; + +namespace Serilog.AspNetCore.Tests.Support +{ + public class SerilogSink : ILogEventSink + { + public List Writes { get; set; } = new List(); + + public void Emit(LogEvent logEvent) + { + Writes.Add(logEvent); + } + } +} \ No newline at end of file From 900f35fed225b08d5516c1e83d30f265144c5c39 Mon Sep 17 00:00:00 2001 From: Nicholas Blumhardt Date: Mon, 28 Aug 2017 08:51:07 +1000 Subject: [PATCH 2/7] Badge updates [Skip CI] --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index e46c493..f90497e 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# Serilog.AspNetCore [![Build status](https://ci.appveyor.com/api/projects/status/865nohxfiq1rnby0/branch/master?svg=true)](https://ci.appveyor.com/project/serilog/serilog-framework-logging/history) [![NuGet Version](http://img.shields.io/nuget/v/Serilog.Extensions.Logging.svg?style=flat)](https://www.nuget.org/packages/Serilog.Extensions.Logging/) +# Serilog.AspNetCore [![Build status](https://ci.appveyor.com/api/projects/status/4rscdto23ik6vm2r?svg=true)](https://ci.appveyor.com/project/serilog/serilog-aspnetcore) [![NuGet Version](http://img.shields.io/nuget/v/Serilog.AspNetCore.svg?style=flat)](https://www.nuget.org/packages/Serilog.AspNetCore/) Serilog logging for ASP.NET Core. This package routes ASP.NET Core log messages through Serilog, so you can get information about ASP.NET's internal operations logged to the same Serilog sinks as your application events. From bef046dbd00433808a5692a3d93e3308a556941e Mon Sep 17 00:00:00 2001 From: Nicholas Blumhardt Date: Mon, 28 Aug 2017 09:54:45 +1000 Subject: [PATCH 3/7] Remove `Debug()` sink example from README - redundant with the new VS output window category [Skip CI] --- README.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/README.md b/README.md index f90497e..1c2cb9d 100644 --- a/README.md +++ b/README.md @@ -5,12 +5,11 @@ Serilog logging for ASP.NET Core. This package routes ASP.NET Core log messages ### Instructions -**First**, install the _Serilog.AspNetCore_ [NuGet package](https://www.nuget.org/packages/Serilog.AspNetCore) into your app. You will need a way to view the log messages - _Serilog.Sinks.Console_ writes these to the console, and _Serilog.Sinks.Debug_ writes to the Visual Studio output window; there are [many more sinks available](https://www.nuget.org/packages?q=Tags%3A%22serilog%22) on NuGet. +**First**, install the _Serilog.AspNetCore_ [NuGet package](https://www.nuget.org/packages/Serilog.AspNetCore) into your app. You will need a way to view the log messages - _Serilog.Sinks.Console_ writes these to the console; there are [many more sinks available](https://www.nuget.org/packages?q=Tags%3A%22serilog%22) on NuGet. ```powershell Install-Package Serilog.AspNetCore -DependencyVersion Highest Install-Package Serilog.Sinks.Console -Install-Package Serilog.Sinks.Debug ``` **Next**, in your application's _Program.cs_ file, configure Serilog first: From c7be61732ca26326a670cbd719ac904dbb7f0b48 Mon Sep 17 00:00:00 2001 From: Nicholas Blumhardt Date: Mon, 28 Aug 2017 09:57:32 +1000 Subject: [PATCH 4/7] Remove dangling line of `Debug()` example [Skip CI] --- README.md | 1 - 1 file changed, 1 deletion(-) diff --git a/README.md b/README.md index 1c2cb9d..29ec63b 100644 --- a/README.md +++ b/README.md @@ -24,7 +24,6 @@ public class Program .MinimumLevel.Override("Microsoft", LogEventLevel.Information) .Enrich.FromLogContext() .WriteTo.Console() - .WriteTo.Debug() .CreateLogger(); ``` From 4bc97e56123069d9abd7d04c6878154c9dcd6f14 Mon Sep 17 00:00:00 2001 From: Nicholas Blumhardt Date: Mon, 28 Aug 2017 09:59:25 +1000 Subject: [PATCH 5/7] Add a note about seeing output in VS [Skip CI] --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 29ec63b..5139458 100644 --- a/README.md +++ b/README.md @@ -81,6 +81,8 @@ That's it! With the level bumped up a little you will see log output like: [22:14:45.741 DBG] Handled. Status code: 304 File: /css/site.css ``` +Tip: to see Serilog output in the Visual Studio output window when running under IIS, select _ASP.NET Core Web Server_ from the _Show output from_ drop-down list. + ### Using the package With _Serilog.AspNetCore_ installed and configured, you can write log messages directly through Serilog or any `ILogger` interface injected by ASP.NET. All loggers will use the same underlying implementation, levels, and destinations. From 788e0f04fc31e77f279db43e22a0f1cfa5bfc04f Mon Sep 17 00:00:00 2001 From: Nicholas Blumhardt Date: Mon, 28 Aug 2017 14:58:46 +1000 Subject: [PATCH 6/7] Add link to configuration sample [Skip CI] --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 5139458..f14822a 100644 --- a/README.md +++ b/README.md @@ -62,7 +62,7 @@ Then, add `UseSerilog()` to the web host builder. A `try`/`catch` block will ens **Finally**, clean up by removing the remaining configuration for the default logger: * Remove calls to `AddLogging()` - * Remove the `"Logging"` section from _appsettings.json_ files (this can be replaced with [Serilog configuration](https://github.com/serilog/serilog-settings-configuration) as shown in [this example](), if required) + * Remove the `"Logging"` section from _appsettings.json_ files (this can be replaced with [Serilog configuration](https://github.com/serilog/serilog-settings-configuration) as shown in [this example](https://github.com/serilog/serilog-aspnetcore/blob/dev/samples/SimpleWebSample/Program.cs), if required) * Remove `ILoggerFactory` parameters and any `Add*()` calls on the logger factory in _Startup.cs_ * Remove `UseApplicationInsights()` (this can be replaced with the [Serilog AI sink](https://github.com/serilog/serilog-sinks-applicationinsights), if required) From b8a3708f1e5f268b3eb84e85f031eb13a655eac9 Mon Sep 17 00:00:00 2001 From: Nicholas Blumhardt Date: Wed, 30 Aug 2017 06:51:45 +1000 Subject: [PATCH 7/7] Include the "Production" environment fallback in sample config loading --- samples/SimpleWebSample/Program.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/samples/SimpleWebSample/Program.cs b/samples/SimpleWebSample/Program.cs index fc4d20a..e080844 100644 --- a/samples/SimpleWebSample/Program.cs +++ b/samples/SimpleWebSample/Program.cs @@ -13,7 +13,7 @@ public static int Main(string[] args) var configuration = new ConfigurationBuilder() .SetBasePath(Directory.GetCurrentDirectory()) .AddJsonFile("appsettings.json", optional: false, reloadOnChange: true) - .AddJsonFile($"appsettings.{Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT")}.json", optional: true) + .AddJsonFile($"appsettings.{Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT") ?? "Production"}.json", optional: true) .Build(); Log.Logger = new LoggerConfiguration()