зеркало из https://github.com/SteeltoeOSS/Logging.git
Родитель
0f958ce840
Коммит
5e51521a93
14
Logging.sln
14
Logging.sln
|
@ -27,6 +27,10 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution
|
||||||
stylecop.json = stylecop.json
|
stylecop.json = stylecop.json
|
||||||
EndProjectSection
|
EndProjectSection
|
||||||
EndProject
|
EndProject
|
||||||
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Steeltoe.Extensions.Logging.SerilogDynamicLogger", "src\Steeltoe.Extensions.Logging.SerilogDynamicLogger\Steeltoe.Extensions.Logging.SerilogDynamicLogger.csproj", "{C4F8B0B3-86C8-45AA-88EB-A18570B2754E}"
|
||||||
|
EndProject
|
||||||
|
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Steeltoe.Extensions.Logging.SerilogDynamicLogger.Test", "test\Steeltoe.Extensions.Logging.SerilogDynamicLogger.Test\Steeltoe.Extensions.Logging.SerilogDynamicLogger.Test.csproj", "{DF66D833-EAC4-4C84-A80F-74CFBD2BF78F}"
|
||||||
|
EndProject
|
||||||
Global
|
Global
|
||||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
Debug|Any CPU = Debug|Any CPU
|
Debug|Any CPU = Debug|Any CPU
|
||||||
|
@ -41,6 +45,14 @@ Global
|
||||||
{CBD7C37D-6CDC-4058-9B68-68EDC87837FD}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
{CBD7C37D-6CDC-4058-9B68-68EDC87837FD}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
{CBD7C37D-6CDC-4058-9B68-68EDC87837FD}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
{CBD7C37D-6CDC-4058-9B68-68EDC87837FD}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
{CBD7C37D-6CDC-4058-9B68-68EDC87837FD}.Release|Any CPU.Build.0 = Release|Any CPU
|
{CBD7C37D-6CDC-4058-9B68-68EDC87837FD}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{C4F8B0B3-86C8-45AA-88EB-A18570B2754E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{C4F8B0B3-86C8-45AA-88EB-A18570B2754E}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{C4F8B0B3-86C8-45AA-88EB-A18570B2754E}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{C4F8B0B3-86C8-45AA-88EB-A18570B2754E}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{DF66D833-EAC4-4C84-A80F-74CFBD2BF78F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{DF66D833-EAC4-4C84-A80F-74CFBD2BF78F}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{DF66D833-EAC4-4C84-A80F-74CFBD2BF78F}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{DF66D833-EAC4-4C84-A80F-74CFBD2BF78F}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
GlobalSection(SolutionProperties) = preSolution
|
GlobalSection(SolutionProperties) = preSolution
|
||||||
HideSolutionNode = FALSE
|
HideSolutionNode = FALSE
|
||||||
|
@ -48,6 +60,8 @@ Global
|
||||||
GlobalSection(NestedProjects) = preSolution
|
GlobalSection(NestedProjects) = preSolution
|
||||||
{BCF90E1F-ADD2-42A2-8601-AB7A5C9FAFC4} = {26E1A61B-FA79-41BF-9C3B-5A04299F356A}
|
{BCF90E1F-ADD2-42A2-8601-AB7A5C9FAFC4} = {26E1A61B-FA79-41BF-9C3B-5A04299F356A}
|
||||||
{CBD7C37D-6CDC-4058-9B68-68EDC87837FD} = {EEF15943-96DB-44B5-BA4A-1D11171AD74F}
|
{CBD7C37D-6CDC-4058-9B68-68EDC87837FD} = {EEF15943-96DB-44B5-BA4A-1D11171AD74F}
|
||||||
|
{C4F8B0B3-86C8-45AA-88EB-A18570B2754E} = {26E1A61B-FA79-41BF-9C3B-5A04299F356A}
|
||||||
|
{DF66D833-EAC4-4C84-A80F-74CFBD2BF78F} = {EEF15943-96DB-44B5-BA4A-1D11171AD74F}
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||||
SolutionGuid = {F03D9805-6C7E-411E-A8D5-191ABE8D2F66}
|
SolutionGuid = {F03D9805-6C7E-411E-A8D5-191ABE8D2F66}
|
||||||
|
|
|
@ -0,0 +1,21 @@
|
||||||
|
// Copyright 2017 the original author or authors.
|
||||||
|
//
|
||||||
|
// 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 Steeltoe.Extensions.Logging.SerilogDynamicLogger
|
||||||
|
{
|
||||||
|
public interface ISerilogOptions
|
||||||
|
{
|
||||||
|
MinimumLevel MinimumLevel { get; set; }
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,44 @@
|
||||||
|
// Copyright 2017 the original author or authors.
|
||||||
|
//
|
||||||
|
// 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.DependencyInjection;
|
||||||
|
using Microsoft.Extensions.DependencyInjection.Extensions;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
using Microsoft.Extensions.Logging.Configuration;
|
||||||
|
using Microsoft.Extensions.Logging.Console;
|
||||||
|
using Microsoft.Extensions.Options;
|
||||||
|
using System.Linq;
|
||||||
|
|
||||||
|
namespace Steeltoe.Extensions.Logging.SerilogDynamicLogger
|
||||||
|
{
|
||||||
|
public static class SerilogBuilderExtensions
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Add Steeltoe logger wrapped in a <see cref="IDynamicLoggerProvider"/> that supports
|
||||||
|
/// dynamically controlling the minimum log level via management endpoints
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="builder">The <see cref="ILoggingBuilder"/> for configuring the LoggerFactory </param>
|
||||||
|
/// <returns>The configured <see cref="ILoggingBuilder"/></returns>
|
||||||
|
public static ILoggingBuilder AddSerilogDynamicConsole(this ILoggingBuilder builder)
|
||||||
|
{
|
||||||
|
builder.AddFilter<DynamicLoggerProvider>(null, LogLevel.Trace);
|
||||||
|
builder.Services.AddSingleton<ISerilogOptions, SerilogOptions>();
|
||||||
|
builder.Services.TryAddEnumerable(ServiceDescriptor.Singleton<ILoggerProvider, SerilogDynamicProvider>());
|
||||||
|
|
||||||
|
builder.Services.TryAddEnumerable(ServiceDescriptor.Singleton<IOptionsChangeTokenSource<ConsoleLoggerOptions>, LoggerProviderOptionsChangeTokenSource<ConsoleLoggerOptions, ConsoleLoggerProvider>>());
|
||||||
|
builder.Services.AddSingleton<IDynamicLoggerProvider>((p) => p.GetServices<ILoggerProvider>().OfType<IDynamicLoggerProvider>().SingleOrDefault());
|
||||||
|
return builder;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,199 @@
|
||||||
|
// Copyright 2017 the original author or authors.
|
||||||
|
//
|
||||||
|
// 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.Configuration;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
using Serilog.AspNetCore;
|
||||||
|
using Serilog.Core;
|
||||||
|
using Serilog.Events;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Concurrent;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using static Serilog.ConfigurationLoggerConfigurationExtensions;
|
||||||
|
|
||||||
|
namespace Steeltoe.Extensions.Logging.SerilogDynamicLogger
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// A wrapper for the <see cref="Serilog.Logger"/> to dynamically set log levels
|
||||||
|
/// </summary>
|
||||||
|
public class SerilogDynamicProvider : IDynamicLoggerProvider
|
||||||
|
{
|
||||||
|
private Logger _globalLogger;
|
||||||
|
private ISerilogOptions _serilogOptions;
|
||||||
|
|
||||||
|
private ConcurrentDictionary<string, ILogger> _loggers = new ConcurrentDictionary<string, ILogger>();
|
||||||
|
private ConcurrentDictionary<string, LoggingLevelSwitch> _loggerSwitches = new ConcurrentDictionary<string, LoggingLevelSwitch>();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Initializes a new instance of the <see cref="SerilogDynamicProvider"/> class.
|
||||||
|
/// Any Serilog settings can be passed in the IConfiguration as needed.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="configuration">Serilog readable <see cref="IConfiguration"/></param>
|
||||||
|
/// <param name="options">Subset of Serilog options managed by wrapper<see cref="ISerilogOptions"/></param>
|
||||||
|
public SerilogDynamicProvider(IConfiguration configuration, ISerilogOptions options = null)
|
||||||
|
{
|
||||||
|
if (configuration == null)
|
||||||
|
{
|
||||||
|
throw new ArgumentNullException(nameof(configuration));
|
||||||
|
}
|
||||||
|
|
||||||
|
_serilogOptions = options ?? new SerilogOptions(configuration);
|
||||||
|
|
||||||
|
// Add a level switch that controls the "Default" level at the root
|
||||||
|
var levelSwitch = new LoggingLevelSwitch(_serilogOptions.MinimumLevel.Default);
|
||||||
|
_loggerSwitches.GetOrAdd("Default", levelSwitch);
|
||||||
|
|
||||||
|
// Add a global logger that will be the root of all other added loggers
|
||||||
|
_globalLogger = new Serilog.LoggerConfiguration()
|
||||||
|
.MinimumLevel.ControlledBy(levelSwitch)
|
||||||
|
.ReadFrom.Configuration(configuration)
|
||||||
|
.CreateLogger();
|
||||||
|
}
|
||||||
|
|
||||||
|
public ILogger CreateLogger(string categoryName)
|
||||||
|
{
|
||||||
|
LogEventLevel eventLevel = _serilogOptions.MinimumLevel.Default;
|
||||||
|
|
||||||
|
foreach (var overrideOption in _serilogOptions.MinimumLevel.Override)
|
||||||
|
{
|
||||||
|
if (categoryName.StartsWith(overrideOption.Key))
|
||||||
|
{
|
||||||
|
eventLevel = overrideOption.Value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Chain new loggers to the global loggers with its own switch
|
||||||
|
// taking into accound any "Overrides"
|
||||||
|
var levelSwitch = new LoggingLevelSwitch(eventLevel);
|
||||||
|
_loggerSwitches.GetOrAdd(categoryName, levelSwitch);
|
||||||
|
var serilogger = new Serilog.LoggerConfiguration()
|
||||||
|
.MinimumLevel.ControlledBy(levelSwitch)
|
||||||
|
.WriteTo.Logger(_globalLogger)
|
||||||
|
.CreateLogger();
|
||||||
|
var factory = new SerilogLoggerFactory(serilogger, true);
|
||||||
|
|
||||||
|
return _loggers.GetOrAdd(categoryName, factory.CreateLogger(categoryName));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
_globalLogger.Dispose();
|
||||||
|
_loggerSwitches = null;
|
||||||
|
_loggers = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ICollection<ILoggerConfiguration> GetLoggerConfigurations()
|
||||||
|
{
|
||||||
|
var results = new Dictionary<string, ILoggerConfiguration>();
|
||||||
|
|
||||||
|
// get the default first
|
||||||
|
LogLevel configuredDefault = GetConfiguredLevel("Default") ?? LogLevel.None;
|
||||||
|
LogLevel effectiveDefault = GetEffectiveLevel("Default");
|
||||||
|
results.Add("Default", new LoggerConfiguration("Default", configuredDefault, effectiveDefault));
|
||||||
|
|
||||||
|
// then get all running loggers
|
||||||
|
foreach (var logger in _loggers)
|
||||||
|
{
|
||||||
|
foreach (var name in GetKeyPrefixes(logger.Key))
|
||||||
|
{
|
||||||
|
if (name != "Default")
|
||||||
|
{
|
||||||
|
LogLevel? configured = GetConfiguredLevel(name);
|
||||||
|
LogLevel effective = GetEffectiveLevel(logger.Key);
|
||||||
|
var config = new LoggerConfiguration(name, configured, effective);
|
||||||
|
if (results.ContainsKey(name))
|
||||||
|
{
|
||||||
|
if (!results[name].Equals(config))
|
||||||
|
{
|
||||||
|
throw new InvalidProgramException("Shouldn't happen");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
results[name] = config;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return results.Values;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetLogLevel(string category, LogLevel? level)
|
||||||
|
{
|
||||||
|
var filteredPairs = _loggerSwitches.Where(kvp => kvp.Key.StartsWith(category));
|
||||||
|
foreach (var kvp in filteredPairs)
|
||||||
|
{
|
||||||
|
var currentLevel = level ?? GetConfiguredLevel(kvp.Key);
|
||||||
|
if (currentLevel != null)
|
||||||
|
{
|
||||||
|
kvp.Value.MinimumLevel = ToSerilogLevel(currentLevel);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private LogLevel? GetConfiguredLevel(string name)
|
||||||
|
{
|
||||||
|
LogLevel? returnValue = null;
|
||||||
|
if (name == "Default")
|
||||||
|
{
|
||||||
|
returnValue = (LogLevel)_serilogOptions.MinimumLevel.Default;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var overrides = _serilogOptions.MinimumLevel.Override;
|
||||||
|
if (overrides != null
|
||||||
|
&& overrides.ContainsKey(name)
|
||||||
|
&& overrides.TryGetValue(name, out LogEventLevel configuredLevel))
|
||||||
|
{
|
||||||
|
returnValue = (LogLevel)configuredLevel;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return returnValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
private LogLevel GetEffectiveLevel(string name)
|
||||||
|
{
|
||||||
|
LoggingLevelSwitch levelSwitch;
|
||||||
|
_loggerSwitches.TryGetValue(name, out levelSwitch);
|
||||||
|
return (LogLevel)levelSwitch.MinimumLevel;
|
||||||
|
}
|
||||||
|
|
||||||
|
private IEnumerable<string> GetKeyPrefixes(string name)
|
||||||
|
{
|
||||||
|
while (!string.IsNullOrEmpty(name))
|
||||||
|
{
|
||||||
|
yield return name;
|
||||||
|
var lastIndexOfDot = name.LastIndexOf('.');
|
||||||
|
if (lastIndexOfDot == -1)
|
||||||
|
{
|
||||||
|
yield return "Default";
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
name = name.Substring(0, lastIndexOfDot);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private LogEventLevel ToSerilogLevel(LogLevel? level)
|
||||||
|
{
|
||||||
|
if (level == null || level == LogLevel.None)
|
||||||
|
{
|
||||||
|
return LogEventLevel.Fatal;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (LogEventLevel)level;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,57 @@
|
||||||
|
// Copyright 2017 the original author or authors.
|
||||||
|
//
|
||||||
|
// 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.Configuration;
|
||||||
|
using Serilog.Events;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace Steeltoe.Extensions.Logging.SerilogDynamicLogger
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Implements a subset of the Serilog Options needed for SerilogDynamicProvider
|
||||||
|
/// </summary>
|
||||||
|
public class SerilogOptions : ISerilogOptions
|
||||||
|
{
|
||||||
|
private const string CONFIG_PATH = "Serilog";
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// This controls the root logger (and the "Default") and
|
||||||
|
/// limits the verbosity of all other overrides to this setting
|
||||||
|
/// </summary>
|
||||||
|
public MinimumLevel MinimumLevel { get; set; }
|
||||||
|
|
||||||
|
public SerilogOptions(IConfiguration configuration)
|
||||||
|
{
|
||||||
|
var section = configuration.GetSection(CONFIG_PATH);
|
||||||
|
section.Bind(this);
|
||||||
|
if (MinimumLevel == null)
|
||||||
|
{
|
||||||
|
MinimumLevel = new MinimumLevel()
|
||||||
|
{
|
||||||
|
Default = LogEventLevel.Verbose, // Set root to verbose to have sub loggers work at all levels
|
||||||
|
Override = new Dictionary<string, LogEventLevel>()
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#pragma warning disable SA1402 // File may only contain a single class
|
||||||
|
public class MinimumLevel
|
||||||
|
#pragma warning restore SA1402 // File may only contain a single class
|
||||||
|
{
|
||||||
|
public LogEventLevel Default { get; set; }
|
||||||
|
|
||||||
|
public Dictionary<string, LogEventLevel> Override { get; set; }
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,44 @@
|
||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
<Import Project="..\..\config\versions.props" />
|
||||||
|
<PropertyGroup>
|
||||||
|
<Description>Steeltoe Console Logger</Description>
|
||||||
|
<Authors>Pivotal;hsarella</Authors>
|
||||||
|
<VersionPrefix>$(SteeltoeVersion)</VersionPrefix>
|
||||||
|
<VersionSuffix>$(VersionSuffix)</VersionSuffix>
|
||||||
|
<TargetFramework>netstandard2.0</TargetFramework>
|
||||||
|
<AssemblyName>Steeltoe.Extensions.Logging.SerilogDynamicLogger</AssemblyName>
|
||||||
|
<PackageId>Steeltoe.Extensions.Logging.SerilogDynamicLogger</PackageId>
|
||||||
|
<PackageTags>Spring Cloud;Logging;Management</PackageTags>
|
||||||
|
<PackageIconUrl>https://steeltoe.io/images/transparent.png</PackageIconUrl>
|
||||||
|
<PackageProjectUrl>https://steeltoe.io</PackageProjectUrl>
|
||||||
|
<PackageLicenseUrl>https://www.apache.org/licenses/LICENSE-2.0</PackageLicenseUrl>
|
||||||
|
<GenerateAssemblyConfigurationAttribute>false</GenerateAssemblyConfigurationAttribute>
|
||||||
|
<GenerateAssemblyCompanyAttribute>false</GenerateAssemblyCompanyAttribute>
|
||||||
|
<GenerateAssemblyProductAttribute>false</GenerateAssemblyProductAttribute>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
|
||||||
|
<NoWarn>SA1101;SA1124;SA1201;SA1309;SA1310;SA1401;SA1600;SA1652;1591</NoWarn>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="Microsoft.Extensions.Logging" Version="2.2.0" />
|
||||||
|
<PackageReference Include="Serilog.AspNetCore" Version="2.1.1" />
|
||||||
|
<PackageReference Include="Serilog.Filters.Expressions" Version="2.0.0" />
|
||||||
|
<PackageReference Include="Serilog.Settings.Configuration" Version="3.0.1" />
|
||||||
|
<PackageReference Include="Serilog.Sinks.Console" Version="3.1.1" />
|
||||||
|
<PackageReference Include="StyleCop.Analyzers" Version="1.0.2">
|
||||||
|
<PrivateAssets>all</PrivateAssets>
|
||||||
|
<IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets>
|
||||||
|
</PackageReference>
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<AdditionalFiles Include="..\..\stylecop.json">
|
||||||
|
<Link>stylecop.json</Link>
|
||||||
|
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||||
|
</AdditionalFiles>
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\Steeltoe.Extensions.Logging.DynamicLogger\Steeltoe.Extensions.Logging.DynamicLogger.csproj" />
|
||||||
|
</ItemGroup>
|
||||||
|
</Project>
|
|
@ -0,0 +1,43 @@
|
||||||
|
// Copyright 2017 the original author or authors.
|
||||||
|
//
|
||||||
|
// 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 System.IO;
|
||||||
|
|
||||||
|
namespace Steeltoe.Extensions.Logging.SerilogDynamicLogger.Test
|
||||||
|
{
|
||||||
|
internal class ConsoleOutputBorrower : IDisposable
|
||||||
|
{
|
||||||
|
private StringWriter _borrowedOutput;
|
||||||
|
private TextWriter _originalOutput;
|
||||||
|
|
||||||
|
public ConsoleOutputBorrower()
|
||||||
|
{
|
||||||
|
_borrowedOutput = new StringWriter();
|
||||||
|
_originalOutput = Console.Out;
|
||||||
|
Console.SetOut(_borrowedOutput);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override string ToString()
|
||||||
|
{
|
||||||
|
return _borrowedOutput.ToString();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
Console.SetOut(_originalOutput);
|
||||||
|
_borrowedOutput.Dispose();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,63 @@
|
||||||
|
// Copyright 2017 the original author or authors.
|
||||||
|
//
|
||||||
|
// 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.Configuration;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
using Microsoft.Extensions.Logging.Console;
|
||||||
|
using Serilog.Events;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using Xunit;
|
||||||
|
|
||||||
|
namespace Steeltoe.Extensions.Logging.SerilogDynamicLogger.Test
|
||||||
|
{
|
||||||
|
public class SerilogDynamicLoggerConfigurationExtensionsTest
|
||||||
|
{
|
||||||
|
[Fact]
|
||||||
|
public void SerilogOptions_Set_Correctly()
|
||||||
|
{
|
||||||
|
// arrange
|
||||||
|
var appSettings = new Dictionary<string, string>
|
||||||
|
{
|
||||||
|
{ "Serilog:MinimumLevel:Default", "Error" },
|
||||||
|
{ "Serilog:MinimumLevel:Override:Microsoft", "Warning" },
|
||||||
|
{ "Serilog:MinimumLevel:Override:Steeltoe.Extensions", "Verbose" },
|
||||||
|
{ "Serilog:MinimumLevel:Override:Steeltoe", "Information" }
|
||||||
|
};
|
||||||
|
var builder = new ConfigurationBuilder().AddInMemoryCollection(appSettings);
|
||||||
|
|
||||||
|
// act
|
||||||
|
var serilogOptions = new SerilogOptions(builder.Build());
|
||||||
|
|
||||||
|
// assert
|
||||||
|
Assert.Equal(LogEventLevel.Error, serilogOptions.MinimumLevel.Default);
|
||||||
|
Assert.Equal(LogEventLevel.Warning, serilogOptions.MinimumLevel.Override["Microsoft"]);
|
||||||
|
Assert.Equal(LogEventLevel.Information, serilogOptions.MinimumLevel.Override["Steeltoe"]);
|
||||||
|
Assert.Equal(LogEventLevel.Verbose, serilogOptions.MinimumLevel.Override["Steeltoe.Extensions"]);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void SerilogOptions_NoError_When_NotConfigured()
|
||||||
|
{
|
||||||
|
// arrange
|
||||||
|
var appSettings = new Dictionary<string, string>();
|
||||||
|
var builder = new ConfigurationBuilder().AddInMemoryCollection(appSettings);
|
||||||
|
|
||||||
|
// act
|
||||||
|
var serilogOptions = new SerilogOptions(builder.Build());
|
||||||
|
|
||||||
|
// assert
|
||||||
|
Assert.Equal(LogEventLevel.Verbose, serilogOptions.MinimumLevel.Default);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,375 @@
|
||||||
|
// Copyright 2017 the original author or authors.
|
||||||
|
//
|
||||||
|
// 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.Configuration;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
using Microsoft.Extensions.Logging.Console;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Threading;
|
||||||
|
using Xunit;
|
||||||
|
|
||||||
|
namespace Steeltoe.Extensions.Logging.SerilogDynamicLogger.Test
|
||||||
|
{
|
||||||
|
public class SerilogDynamicLoggerProviderTest
|
||||||
|
{
|
||||||
|
[Fact]
|
||||||
|
public void Create_CreatesCorrectLogger()
|
||||||
|
{
|
||||||
|
var provider = new SerilogDynamicProvider(GetConfiguration());
|
||||||
|
LoggerFactory fac = new LoggerFactory();
|
||||||
|
fac.AddProvider(provider);
|
||||||
|
|
||||||
|
ILogger logger = fac.CreateLogger(typeof(A.B.C.D.TestClass));
|
||||||
|
Assert.NotNull(logger);
|
||||||
|
Assert.True(logger.IsEnabled(LogLevel.Information));
|
||||||
|
Assert.False(logger.IsEnabled(LogLevel.Debug));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void SetLogLevel_UpdatesLogger()
|
||||||
|
{
|
||||||
|
var provider = new SerilogDynamicProvider(GetConfiguration());
|
||||||
|
LoggerFactory fac = new LoggerFactory();
|
||||||
|
fac.AddProvider(provider);
|
||||||
|
|
||||||
|
ILogger logger = fac.CreateLogger(typeof(A.B.C.D.TestClass));
|
||||||
|
Assert.NotNull(logger);
|
||||||
|
Assert.True(logger.IsEnabled(LogLevel.Critical));
|
||||||
|
Assert.True(logger.IsEnabled(LogLevel.Error));
|
||||||
|
Assert.True(logger.IsEnabled(LogLevel.Warning));
|
||||||
|
Assert.True(logger.IsEnabled(LogLevel.Information));
|
||||||
|
Assert.False(logger.IsEnabled(LogLevel.Debug));
|
||||||
|
|
||||||
|
provider.SetLogLevel("A", LogLevel.Debug);
|
||||||
|
Assert.True(logger.IsEnabled(LogLevel.Information));
|
||||||
|
Assert.True(logger.IsEnabled(LogLevel.Debug));
|
||||||
|
|
||||||
|
provider.SetLogLevel("A", LogLevel.Information);
|
||||||
|
Assert.True(logger.IsEnabled(LogLevel.Information));
|
||||||
|
Assert.False(logger.IsEnabled(LogLevel.Debug));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void SetLogLevel_UpdatesNamespaceDescendants()
|
||||||
|
{
|
||||||
|
// arrange (A* should log at Information)
|
||||||
|
var provider = new SerilogDynamicProvider(GetConfiguration());
|
||||||
|
|
||||||
|
// act I: with original setup
|
||||||
|
var childLogger = provider.CreateLogger("A.B.C");
|
||||||
|
var configurations = provider.GetLoggerConfigurations();
|
||||||
|
var tierOneNamespace = configurations.First(n => n.Name == "A");
|
||||||
|
|
||||||
|
// assert I: base namespace is in the response, correctly
|
||||||
|
Assert.Equal(LogLevel.Information, tierOneNamespace.EffectiveLevel);
|
||||||
|
|
||||||
|
configurations = provider.GetLoggerConfigurations();
|
||||||
|
|
||||||
|
// act II: set A.B* to log at Trace
|
||||||
|
provider.SetLogLevel("A.B", LogLevel.Trace);
|
||||||
|
configurations = provider.GetLoggerConfigurations();
|
||||||
|
|
||||||
|
tierOneNamespace = configurations.First(n => n.Name == "A");
|
||||||
|
var tierTwoNamespace = configurations.First(n => n.Name == "A.B");
|
||||||
|
|
||||||
|
// assert II: since Serilog doesnt expose filters, we can only change at the granularity of configured filters
|
||||||
|
Assert.Equal(LogLevel.Trace, tierOneNamespace.EffectiveLevel);
|
||||||
|
Assert.Equal(LogLevel.Trace, tierTwoNamespace.EffectiveLevel);
|
||||||
|
Assert.True(childLogger.IsEnabled(LogLevel.Trace));
|
||||||
|
|
||||||
|
// act III: set A to something else, make sure it inherits down
|
||||||
|
provider.SetLogLevel("A", LogLevel.Error);
|
||||||
|
configurations = provider.GetLoggerConfigurations();
|
||||||
|
tierOneNamespace = configurations.First(n => n.Name == "A");
|
||||||
|
tierTwoNamespace = configurations.First(n => n.Name == "A.B");
|
||||||
|
var grandchildLogger = provider.CreateLogger("A.B.C.D");
|
||||||
|
|
||||||
|
// assert again
|
||||||
|
Assert.Equal(LogLevel.Error, tierOneNamespace.EffectiveLevel);
|
||||||
|
Assert.Equal(LogLevel.Error, tierTwoNamespace.EffectiveLevel);
|
||||||
|
Assert.False(childLogger.IsEnabled(LogLevel.Warning));
|
||||||
|
Assert.False(grandchildLogger.IsEnabled(LogLevel.Debug));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void SetLogLevel_Can_Reset_to_Default()
|
||||||
|
{
|
||||||
|
// arrange (A* should log at Information)
|
||||||
|
var provider = new SerilogDynamicProvider(GetConfiguration());
|
||||||
|
|
||||||
|
// act I: with original setup
|
||||||
|
var firstLogger = provider.CreateLogger("A.B.C");
|
||||||
|
var configurations = provider.GetLoggerConfigurations();
|
||||||
|
var tierOneNamespace = configurations.First(n => n.Name == "A");
|
||||||
|
|
||||||
|
// assert I: base namespace is in the response, correctly
|
||||||
|
Assert.Equal(LogLevel.Information, tierOneNamespace.EffectiveLevel);
|
||||||
|
|
||||||
|
// act II: set A.B* to log at Trace
|
||||||
|
provider.SetLogLevel("A.B", LogLevel.Trace);
|
||||||
|
configurations = provider.GetLoggerConfigurations();
|
||||||
|
tierOneNamespace = configurations.First(n => n.Name == "A");
|
||||||
|
var tierTwoNamespace = configurations.First(n => n.Name == "A.B");
|
||||||
|
|
||||||
|
// assert II: since Serilog doesnt expose filters, we can only change at the granularity of configured overrides
|
||||||
|
Assert.Equal(LogLevel.Trace, tierOneNamespace.EffectiveLevel);
|
||||||
|
Assert.Equal(LogLevel.Trace, tierTwoNamespace.EffectiveLevel);
|
||||||
|
Assert.True(firstLogger.IsEnabled(LogLevel.Trace));
|
||||||
|
|
||||||
|
// act III: reset A.B
|
||||||
|
provider.SetLogLevel("A.B", null);
|
||||||
|
configurations = provider.GetLoggerConfigurations();
|
||||||
|
tierOneNamespace = configurations.First(n => n.Name == "A");
|
||||||
|
tierTwoNamespace = configurations.First(n => n.Name == "A.B");
|
||||||
|
var secondLogger = provider.CreateLogger("A.B.C.D");
|
||||||
|
|
||||||
|
// assert again
|
||||||
|
Assert.Equal(LogLevel.Information, tierOneNamespace.EffectiveLevel);
|
||||||
|
Assert.Equal(LogLevel.Information, tierTwoNamespace.EffectiveLevel);
|
||||||
|
Assert.True(firstLogger.IsEnabled(LogLevel.Information));
|
||||||
|
Assert.True(secondLogger.IsEnabled(LogLevel.Information));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void GetLoggerConfigurations_ReturnsExpected()
|
||||||
|
{
|
||||||
|
var provider = new SerilogDynamicProvider(GetConfiguration());
|
||||||
|
LoggerFactory fac = new LoggerFactory();
|
||||||
|
fac.AddProvider(provider);
|
||||||
|
|
||||||
|
ILogger logger = fac.CreateLogger(typeof(A.B.C.D.TestClass));
|
||||||
|
|
||||||
|
var logConfig = provider.GetLoggerConfigurations();
|
||||||
|
Assert.Equal(6, logConfig.Count);
|
||||||
|
Assert.Contains(new LoggerConfiguration("Default", LogLevel.Trace, LogLevel.Trace), logConfig);
|
||||||
|
Assert.Contains(new LoggerConfiguration("A.B.C.D.TestClass", null, LogLevel.Information), logConfig);
|
||||||
|
Assert.Contains(new LoggerConfiguration("A.B.C.D", null, LogLevel.Information), logConfig);
|
||||||
|
Assert.Contains(new LoggerConfiguration("A.B.C", LogLevel.Information, LogLevel.Information), logConfig);
|
||||||
|
Assert.Contains(new LoggerConfiguration("A.B", null, LogLevel.Information), logConfig);
|
||||||
|
Assert.Contains(new LoggerConfiguration("A", LogLevel.Information, LogLevel.Information), logConfig);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void GetLoggerConfigurations_ReturnsExpected_After_SetLogLevel()
|
||||||
|
{
|
||||||
|
// arrange
|
||||||
|
var provider = new SerilogDynamicProvider(GetConfiguration());
|
||||||
|
LoggerFactory fac = new LoggerFactory();
|
||||||
|
fac.AddProvider(provider);
|
||||||
|
|
||||||
|
// act I
|
||||||
|
ILogger logger = fac.CreateLogger(typeof(A.B.C.D.TestClass));
|
||||||
|
var logConfig = provider.GetLoggerConfigurations();
|
||||||
|
|
||||||
|
// assert I
|
||||||
|
Assert.Equal(6, logConfig.Count);
|
||||||
|
Assert.Contains(new LoggerConfiguration("Default", LogLevel.Trace, LogLevel.Trace), logConfig);
|
||||||
|
Assert.Contains(new LoggerConfiguration("A.B.C.D.TestClass", null, LogLevel.Information), logConfig);
|
||||||
|
Assert.Contains(new LoggerConfiguration("A.B.C.D", null, LogLevel.Information), logConfig);
|
||||||
|
Assert.Contains(new LoggerConfiguration("A.B.C", LogLevel.Information, LogLevel.Information), logConfig);
|
||||||
|
Assert.Contains(new LoggerConfiguration("A.B", null, LogLevel.Information), logConfig);
|
||||||
|
Assert.Contains(new LoggerConfiguration("A", LogLevel.Information, LogLevel.Information), logConfig);
|
||||||
|
|
||||||
|
// act II
|
||||||
|
provider.SetLogLevel("A.B", LogLevel.Trace);
|
||||||
|
logConfig = provider.GetLoggerConfigurations();
|
||||||
|
|
||||||
|
// assert II
|
||||||
|
Assert.Equal(6, logConfig.Count);
|
||||||
|
Assert.Contains(new LoggerConfiguration("Default", LogLevel.Trace, LogLevel.Trace), logConfig);
|
||||||
|
Assert.Contains(new LoggerConfiguration("A.B.C.D.TestClass", null, LogLevel.Trace), logConfig);
|
||||||
|
Assert.Contains(new LoggerConfiguration("A.B.C.D", null, LogLevel.Trace), logConfig);
|
||||||
|
Assert.Contains(new LoggerConfiguration("A.B.C", LogLevel.Information, LogLevel.Trace), logConfig);
|
||||||
|
Assert.Contains(new LoggerConfiguration("A.B", null, LogLevel.Trace), logConfig);
|
||||||
|
Assert.Contains(new LoggerConfiguration("A", LogLevel.Information, LogLevel.Trace), logConfig);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void SetLogLevel_Works_OnDefault()
|
||||||
|
{
|
||||||
|
// arrange
|
||||||
|
var provider = new SerilogDynamicProvider(GetConfiguration());
|
||||||
|
LoggerFactory fac = new LoggerFactory();
|
||||||
|
fac.AddProvider(provider);
|
||||||
|
var originalLogConfig = provider.GetLoggerConfigurations();
|
||||||
|
|
||||||
|
// act
|
||||||
|
provider.SetLogLevel("Default", LogLevel.Information);
|
||||||
|
var updatedLogConfig = provider.GetLoggerConfigurations();
|
||||||
|
|
||||||
|
// assert
|
||||||
|
Assert.Contains(new LoggerConfiguration("Default", LogLevel.Trace, LogLevel.Trace), originalLogConfig);
|
||||||
|
Assert.Contains(new LoggerConfiguration("Default", LogLevel.Trace, LogLevel.Information), updatedLogConfig);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void ResetLogLevel_Works_OnDefault()
|
||||||
|
{
|
||||||
|
// arrange
|
||||||
|
var provider = new SerilogDynamicProvider(GetConfiguration());
|
||||||
|
LoggerFactory fac = new LoggerFactory();
|
||||||
|
fac.AddProvider(provider);
|
||||||
|
var originalLogConfig = provider.GetLoggerConfigurations();
|
||||||
|
|
||||||
|
// act
|
||||||
|
provider.SetLogLevel("Default", LogLevel.Information);
|
||||||
|
var updatedLogConfig = provider.GetLoggerConfigurations();
|
||||||
|
provider.SetLogLevel("Default", null);
|
||||||
|
var resetConfig = provider.GetLoggerConfigurations();
|
||||||
|
|
||||||
|
// assert
|
||||||
|
Assert.Contains(new LoggerConfiguration("Default", LogLevel.Trace, LogLevel.Trace), originalLogConfig);
|
||||||
|
Assert.Contains(new LoggerConfiguration("Default", LogLevel.Trace, LogLevel.Information), updatedLogConfig);
|
||||||
|
Assert.Contains(new LoggerConfiguration("Default", LogLevel.Trace, LogLevel.Trace), resetConfig);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void LoggerLogs_At_Configured_Setting()
|
||||||
|
{
|
||||||
|
// arrange
|
||||||
|
var provider = new SerilogDynamicProvider(GetConfiguration());
|
||||||
|
LoggerFactory fac = new LoggerFactory();
|
||||||
|
fac.AddProvider(provider);
|
||||||
|
ILogger logger = fac.CreateLogger(typeof(A.B.C.D.TestClass));
|
||||||
|
|
||||||
|
// act I - log at all levels, expect Info and above to work
|
||||||
|
using (var unConsole = new ConsoleOutputBorrower())
|
||||||
|
{
|
||||||
|
WriteLogEntries(logger);
|
||||||
|
|
||||||
|
// pause the thread to allow the logging to happen
|
||||||
|
Thread.Sleep(100);
|
||||||
|
|
||||||
|
var logged = unConsole.ToString();
|
||||||
|
|
||||||
|
// assert I
|
||||||
|
Assert.Contains("Critical message", logged);
|
||||||
|
Assert.Contains("Error message", logged);
|
||||||
|
Assert.Contains("Warning message", logged);
|
||||||
|
Assert.Contains("Informational message", logged);
|
||||||
|
Assert.DoesNotContain("Debug message", logged);
|
||||||
|
Assert.DoesNotContain("Trace message", logged);
|
||||||
|
}
|
||||||
|
|
||||||
|
// act II - adjust rules, expect Error and above to work
|
||||||
|
provider.SetLogLevel("A.B.C.D", LogLevel.Error);
|
||||||
|
using (var unConsole = new ConsoleOutputBorrower())
|
||||||
|
{
|
||||||
|
WriteLogEntries(logger);
|
||||||
|
|
||||||
|
// pause the thread to allow the logging to happen
|
||||||
|
Thread.Sleep(100);
|
||||||
|
|
||||||
|
var logged2 = unConsole.ToString();
|
||||||
|
|
||||||
|
// assert II
|
||||||
|
Assert.Contains("Critical message", logged2);
|
||||||
|
Assert.Contains("Error message", logged2);
|
||||||
|
Assert.DoesNotContain("Warning message", logged2);
|
||||||
|
Assert.DoesNotContain("Informational message", logged2);
|
||||||
|
Assert.DoesNotContain("Debug message", logged2);
|
||||||
|
Assert.DoesNotContain("Trace message", logged2);
|
||||||
|
}
|
||||||
|
|
||||||
|
// act III - adjust rules, expect Trace and above to work
|
||||||
|
provider.SetLogLevel("A", LogLevel.Trace);
|
||||||
|
using (var unConsole = new ConsoleOutputBorrower())
|
||||||
|
{
|
||||||
|
WriteLogEntries(logger);
|
||||||
|
|
||||||
|
// pause the thread to allow the logging to happen
|
||||||
|
Thread.Sleep(100);
|
||||||
|
|
||||||
|
var logged3 = unConsole.ToString();
|
||||||
|
|
||||||
|
// assert III
|
||||||
|
Assert.Contains("Critical message", logged3);
|
||||||
|
Assert.Contains("Error message", logged3);
|
||||||
|
Assert.Contains("Warning message", logged3);
|
||||||
|
Assert.Contains("Informational message", logged3);
|
||||||
|
Assert.Contains("Debug message", logged3);
|
||||||
|
Assert.Contains("Trace message", logged3);
|
||||||
|
}
|
||||||
|
|
||||||
|
// act IV - adjust rules, expect nothing to work
|
||||||
|
provider.SetLogLevel("A", LogLevel.None);
|
||||||
|
using (var unConsole = new ConsoleOutputBorrower())
|
||||||
|
{
|
||||||
|
WriteLogEntries(logger);
|
||||||
|
|
||||||
|
// pause the thread to allow the logging to happen
|
||||||
|
Thread.Sleep(100);
|
||||||
|
|
||||||
|
var logged4 = unConsole.ToString();
|
||||||
|
|
||||||
|
// assert IV
|
||||||
|
Assert.Contains("Critical message", logged4); // Serilog lowest level is Fatal == Critical
|
||||||
|
Assert.DoesNotContain("Error message", logged4);
|
||||||
|
Assert.DoesNotContain("Warning message", logged4);
|
||||||
|
Assert.DoesNotContain("Informational message", logged4);
|
||||||
|
Assert.DoesNotContain("Debug message", logged4);
|
||||||
|
Assert.DoesNotContain("Trace message", logged4);
|
||||||
|
}
|
||||||
|
|
||||||
|
// act V - reset the rules, expect Info and above to work
|
||||||
|
provider.SetLogLevel("A", LogLevel.Information); // Only works with serilog for configured values
|
||||||
|
using (var unConsole = new ConsoleOutputBorrower())
|
||||||
|
{
|
||||||
|
WriteLogEntries(logger);
|
||||||
|
|
||||||
|
// pause the thread to allow the logging to happen
|
||||||
|
Thread.Sleep(100);
|
||||||
|
|
||||||
|
var logged5 = unConsole.ToString();
|
||||||
|
|
||||||
|
// assert V
|
||||||
|
Assert.NotNull(provider.GetLoggerConfigurations().First(c => c.Name == "A"));
|
||||||
|
Assert.Contains("Critical message", logged5);
|
||||||
|
Assert.Contains("Error message", logged5);
|
||||||
|
Assert.Contains("Warning message", logged5);
|
||||||
|
Assert.Contains("Informational message", logged5);
|
||||||
|
Assert.DoesNotContain("Debug message", logged5);
|
||||||
|
Assert.DoesNotContain("Trace message", logged5);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void WriteLogEntries(ILogger logger)
|
||||||
|
{
|
||||||
|
logger.LogCritical("Critical message");
|
||||||
|
logger.LogError("Error message");
|
||||||
|
logger.LogWarning("Warning message");
|
||||||
|
logger.LogInformation("Informational message");
|
||||||
|
logger.LogDebug("Debug message");
|
||||||
|
logger.LogTrace("Trace message");
|
||||||
|
}
|
||||||
|
|
||||||
|
private IConfiguration GetConfiguration()
|
||||||
|
{
|
||||||
|
var appSettings = new Dictionary<string, string>
|
||||||
|
{
|
||||||
|
{ "Serilog:MinimumLevel:Default", "Verbose" }, // Sets level of root logger so has to be higher than any sub logger
|
||||||
|
{ "Serilog:MinimumLevel:Override:Microsoft", "Warning" },
|
||||||
|
{ "Serilog:MinimumLevel:Override:Steeltoe.Extensions", "Verbose" },
|
||||||
|
{ "Serilog:MinimumLevel:Override:Steeltoe", "Information" },
|
||||||
|
{ "Serilog:MinimumLevel:Override:A", "Information" },
|
||||||
|
{ "Serilog:MinimumLevel:Override:A.B.C", "Information" },
|
||||||
|
{ "Serilog:WriteTo:Name", "Console" },
|
||||||
|
};
|
||||||
|
|
||||||
|
var builder = new ConfigurationBuilder().AddInMemoryCollection(appSettings);
|
||||||
|
return builder.Build();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,166 @@
|
||||||
|
// Copyright 2017 the original author or authors.
|
||||||
|
//
|
||||||
|
// 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.Configuration;
|
||||||
|
using Microsoft.Extensions.DependencyInjection;
|
||||||
|
using Microsoft.Extensions.Logging;
|
||||||
|
using Microsoft.Extensions.Logging.Console;
|
||||||
|
using Microsoft.Extensions.Options;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using Xunit;
|
||||||
|
|
||||||
|
namespace Steeltoe.Extensions.Logging.SerilogDynamicLogger.Test
|
||||||
|
{
|
||||||
|
public class SerilogDynamicLoggingBuilderTest
|
||||||
|
{
|
||||||
|
private static Dictionary<string, string> appsettings = new Dictionary<string, string>()
|
||||||
|
{
|
||||||
|
{ "Serilog:MinimumLevel:Default", "Verbose" }, // Sets level of root logger so has to be higher than any sub logger
|
||||||
|
{ "Serilog:MinimumLevel:Override:Microsoft", "Warning" },
|
||||||
|
{ "Serilog:MinimumLevel:Override:Steeltoe.Extensions", "Verbose" },
|
||||||
|
{ "Serilog:MinimumLevel:Override:Steeltoe", "Information" },
|
||||||
|
{ "Serilog:MinimumLevel:Override:A", "Information" },
|
||||||
|
{ "Serilog:MinimumLevel:Override:A.B.C.D", "Fatal" },
|
||||||
|
{ "Serilog:WriteTo:Name", "Console" },
|
||||||
|
};
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void OnlyApplicableFilters_AreApplied()
|
||||||
|
{
|
||||||
|
// arrange
|
||||||
|
var appsettings = new Dictionary<string, string>()
|
||||||
|
{
|
||||||
|
["Logging:IncludeScopes"] = "false",
|
||||||
|
["Logging:LogLevel:Default"] = "Information",
|
||||||
|
["Logging:foo:LogLevel:A.B.C.D.TestClass"] = "None"
|
||||||
|
};
|
||||||
|
var configuration = new ConfigurationBuilder().AddInMemoryCollection(appsettings).Build();
|
||||||
|
var services = new ServiceCollection()
|
||||||
|
.AddSingleton<IConfiguration>(configuration)
|
||||||
|
.AddLogging(builder =>
|
||||||
|
{
|
||||||
|
builder.AddSerilogDynamicConsole();
|
||||||
|
})
|
||||||
|
.BuildServiceProvider();
|
||||||
|
|
||||||
|
// act
|
||||||
|
var logger = services.GetService(typeof(ILogger<A.B.C.D.TestClass>)) as ILogger<A.B.C.D.TestClass>;
|
||||||
|
|
||||||
|
// assert
|
||||||
|
Assert.NotNull(logger);
|
||||||
|
Assert.True(logger.IsEnabled(LogLevel.Information), "Information level should be enabled");
|
||||||
|
Assert.False(logger.IsEnabled(LogLevel.Debug), "Debug level should NOT be enabled");
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void DynamicLevelSetting_WorksWith_ConsoleFilters()
|
||||||
|
{
|
||||||
|
// arrange
|
||||||
|
var configuration = new ConfigurationBuilder().AddInMemoryCollection(appsettings).Build();
|
||||||
|
var services = new ServiceCollection()
|
||||||
|
.AddSingleton<IConfiguration>(configuration)
|
||||||
|
.AddLogging(builder =>
|
||||||
|
{
|
||||||
|
builder.AddSerilogDynamicConsole();
|
||||||
|
})
|
||||||
|
.BuildServiceProvider();
|
||||||
|
|
||||||
|
// act
|
||||||
|
var logger = services.GetService(typeof(ILogger<A.B.C.D.TestClass>)) as ILogger<A.B.C.D.TestClass>;
|
||||||
|
|
||||||
|
// assert
|
||||||
|
Assert.NotNull(logger);
|
||||||
|
Assert.True(logger.IsEnabled(LogLevel.Critical), "Critical level should be enabled");
|
||||||
|
Assert.False(logger.IsEnabled(LogLevel.Error), "Error level should NOT be enabled");
|
||||||
|
Assert.False(logger.IsEnabled(LogLevel.Warning), "Warning level should NOT be enabled");
|
||||||
|
Assert.False(logger.IsEnabled(LogLevel.Debug), "Debug level should NOT be enabled");
|
||||||
|
Assert.False(logger.IsEnabled(LogLevel.Trace), "Trace level should NOT be enabled yet");
|
||||||
|
|
||||||
|
// change the log level and confirm it worked
|
||||||
|
var provider = services.GetRequiredService(typeof(ILoggerProvider)) as SerilogDynamicProvider;
|
||||||
|
provider.SetLogLevel("A.B.C.D", LogLevel.Trace);
|
||||||
|
var levels = provider.GetLoggerConfigurations().Where(c => c.Name.StartsWith("A.B.C.D"))
|
||||||
|
.Select(x => x.EffectiveLevel);
|
||||||
|
|
||||||
|
Assert.NotNull(levels);
|
||||||
|
Assert.True(levels.All(x => x == LogLevel.Trace));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void AddDynamicConsole_AddsAllLoggerProviders()
|
||||||
|
{
|
||||||
|
// arrange
|
||||||
|
var configuration = new ConfigurationBuilder().AddInMemoryCollection(appsettings).Build();
|
||||||
|
var services = new ServiceCollection()
|
||||||
|
.AddSingleton<IConfiguration>(configuration)
|
||||||
|
.AddLogging(builder =>
|
||||||
|
{
|
||||||
|
builder.AddSerilogDynamicConsole();
|
||||||
|
}).BuildServiceProvider();
|
||||||
|
|
||||||
|
// act
|
||||||
|
var dlogProvider = services.GetService<IDynamicLoggerProvider>();
|
||||||
|
var logProviders = services.GetServices<ILoggerProvider>();
|
||||||
|
|
||||||
|
// assert
|
||||||
|
Assert.NotNull(dlogProvider);
|
||||||
|
Assert.NotEmpty(logProviders);
|
||||||
|
Assert.Single(logProviders);
|
||||||
|
Assert.IsType<SerilogDynamicProvider>(logProviders.SingleOrDefault());
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void AddDynamicConsole_AddsLoggerProvider_DisposeTwiceSucceeds()
|
||||||
|
{
|
||||||
|
// arrange
|
||||||
|
var configuration = new ConfigurationBuilder().AddInMemoryCollection(appsettings).Build();
|
||||||
|
var services = new ServiceCollection()
|
||||||
|
.AddSingleton<IConfiguration>(configuration)
|
||||||
|
.AddLogging(builder =>
|
||||||
|
{
|
||||||
|
builder.AddSerilogDynamicConsole();
|
||||||
|
}).BuildServiceProvider();
|
||||||
|
|
||||||
|
// act
|
||||||
|
var dlogProvider = services.GetService<IDynamicLoggerProvider>();
|
||||||
|
var logProviders = services.GetServices<ILoggerProvider>();
|
||||||
|
|
||||||
|
// assert
|
||||||
|
services.Dispose();
|
||||||
|
dlogProvider.Dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void AddDynamicConsole_WithConfigurationParam_AddsServices()
|
||||||
|
{
|
||||||
|
// arrange
|
||||||
|
var configuration = new ConfigurationBuilder().AddInMemoryCollection(appsettings).Build();
|
||||||
|
var services = new ServiceCollection()
|
||||||
|
.AddSingleton<IConfiguration>(configuration)
|
||||||
|
.AddLogging(builder => builder.AddSerilogDynamicConsole())
|
||||||
|
.BuildServiceProvider();
|
||||||
|
|
||||||
|
// act
|
||||||
|
var dlogProvider = services.GetService<IDynamicLoggerProvider>();
|
||||||
|
var logProviders = services.GetServices<ILoggerProvider>();
|
||||||
|
|
||||||
|
// assert
|
||||||
|
Assert.NotNull(dlogProvider);
|
||||||
|
Assert.NotEmpty(logProviders);
|
||||||
|
Assert.Single(logProviders);
|
||||||
|
Assert.IsType<SerilogDynamicProvider>(logProviders.SingleOrDefault());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,38 @@
|
||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
<Import Project="..\..\versions.props" />
|
||||||
|
<PropertyGroup>
|
||||||
|
<TargetFrameworks>net461;netcoreapp2.1</TargetFrameworks>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<None Update="xunit.runner.json">
|
||||||
|
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||||
|
</None>
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="Microsoft.AspNetCore.Mvc" Version="$(AspNetCoreMvcTestVersion)" />
|
||||||
|
<PackageReference Include="Microsoft.AspNetCore.TestHost" Version="$(AspNetCoreTestVersion)" />
|
||||||
|
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="$(TestSdkVersion)" />
|
||||||
|
<PackageReference Include="xunit" Version="$(XunitVersion)" />
|
||||||
|
<PackageReference Include="xunit.runner.visualstudio" Version="$(XunitStudioVersion)" />
|
||||||
|
<DotNetCliToolReference Include="dotnet-xunit" Version="$(XunitVersion)" />
|
||||||
|
|
||||||
|
<PackageReference Include="StyleCop.Analyzers" Version="$(StyleCopVersion)">
|
||||||
|
<PrivateAssets>All</PrivateAssets>
|
||||||
|
</PackageReference>
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<NoWarn>SA1101;SA1124;SA1201;SA1309;SA1310;SA1401;SA1600;SA1652;1591</NoWarn>
|
||||||
|
</PropertyGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<AdditionalFiles Include="..\..\stylecop.json">
|
||||||
|
<Link>stylecop.json</Link>
|
||||||
|
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
|
||||||
|
</AdditionalFiles>
|
||||||
|
</ItemGroup>
|
||||||
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\..\src\Steeltoe.Extensions.Logging.SerilogDynamicLogger\Steeltoe.Extensions.Logging.SerilogDynamicLogger.csproj" />
|
||||||
|
</ItemGroup>
|
||||||
|
</Project>
|
|
@ -0,0 +1,20 @@
|
||||||
|
// Copyright 2017 the original author or authors.
|
||||||
|
//
|
||||||
|
// 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 A.B.C.D
|
||||||
|
{
|
||||||
|
public class TestClass
|
||||||
|
{
|
||||||
|
}
|
||||||
|
}
|
Загрузка…
Ссылка в новой задаче