Skip to content
This repository was archived by the owner on Dec 13, 2018. It is now read-only.

Add a way to get configuration section associated with logger provider #706

Merged
merged 4 commits into from
Sep 21, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -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 Microsoft.Extensions.Configuration;

namespace Microsoft.Extensions.Logging.Configuration
{
/// <summary>
/// Allows access to configuration section associated with logger provider
/// </summary>
/// <typeparam name="T">Type of logger provider to get configuration for</typeparam>
public interface ILoggerProviderConfiguration<T>
{
/// <summary>
/// Configuration section for requested logger provider
/// </summary>
IConfiguration Configuration { get; }
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.

using System;
using Microsoft.Extensions.Configuration;

namespace Microsoft.Extensions.Logging.Configuration
{
/// <summary>
/// Allows access to configuration section associated with logger provider
/// </summary>
public interface ILoggerProviderConfigurationFactory
{
/// <summary>
/// Return configuration section associated with logger provider
/// </summary>
/// <param name="providerType">The logger provider type</param>
IConfiguration GetConfiguration(Type providerType);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
// 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 Microsoft.Extensions.Configuration;

namespace Microsoft.Extensions.Logging.Configuration
{
internal class LoggerProviderConfiguration<T> : ILoggerProviderConfiguration<T>
{
public LoggerProviderConfiguration(ILoggerProviderConfigurationFactory providerConfigurationFactory)
{
Configuration = providerConfigurationFactory.GetConfiguration(typeof(T));
}

public IConfiguration Configuration { get; }
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.

using System;
using System.Collections.Generic;
using System.Reflection;
using Microsoft.Extensions.Configuration;

namespace Microsoft.Extensions.Logging.Configuration
{
internal class LoggerProviderConfigurationFactory : ILoggerProviderConfigurationFactory
{
private readonly IEnumerable<LoggingConfiguration> _configurations;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is pretty weird. Usually configuration is composed at the provider level not by using multiple IConfiguration instances. Why do we do this?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We allow composition of logging configurations for light-up scenarios (AI is adding another logging config file), so when you call AddConfiguration multiple times filtering rules get flattened in one list. I just simulated the same behavior for configuration sections.


public LoggerProviderConfigurationFactory(IEnumerable<LoggingConfiguration> configurations)
{
_configurations = configurations;
}

public IConfiguration GetConfiguration(Type providerType)
{
if (providerType == null)
{
throw new ArgumentNullException(nameof(providerType));
}

var fullName = providerType.FullName;
var alias = ProviderAliasUtilities.GetAlias(providerType);
var configurationBuilder = new ConfigurationBuilder();
foreach (var configuration in _configurations)
{
var sectionFromFullName = configuration.Configuration.GetSection(fullName);
if (sectionFromFullName.Exists())
{
configurationBuilder.AddConfiguration(sectionFromFullName);
}

if (!string.IsNullOrWhiteSpace(alias))
{
var sectionFromAlias = configuration.Configuration.GetSection(alias);
if (sectionFromAlias.Exists())
{
configurationBuilder.AddConfiguration(sectionFromAlias);
}
}
}
return configurationBuilder.Build();
}
}
}
Original file line number Diff line number Diff line change
@@ -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 Microsoft.Extensions.Options;

namespace Microsoft.Extensions.Logging.Configuration
{
/// <inheritdoc />
public class LoggerProviderOptionsChangeTokenSource<TOptions, TProvider> : ConfigurationChangeTokenSource<TOptions>
{
/// <inheritdoc />
public LoggerProviderOptionsChangeTokenSource(ILoggerProviderConfiguration<TProvider> providerConfiguration) : base(providerConfiguration.Configuration)
{
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
// 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 Microsoft.Extensions.DependencyInjection.Extensions;

namespace Microsoft.Extensions.Logging.Configuration
{
/// <summary>
/// Extension methods for setting up logging services in an <see cref="ILoggingBuilder" />.
/// </summary>
public static class LoggingBuilderConfigurationExtensions
{
/// <summary>
/// Adds services required to consume <see cref="ILoggerProviderConfigurationFactory"/> or <see cref="ILoggerProviderConfiguration{T}"/>
/// </summary>
public static void AddConfiguration(this ILoggingBuilder builder)
{
builder.Services.TryAddSingleton<ILoggerProviderConfigurationFactory, LoggerProviderConfigurationFactory>();
builder.Services.TryAddSingleton(typeof(ILoggerProviderConfiguration<>), typeof(LoggerProviderConfiguration<>));
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging.Configuration;
using Microsoft.Extensions.Options;

namespace Microsoft.Extensions.Logging
Expand All @@ -20,9 +21,13 @@ public static class LoggingBuilderExtensions
/// <returns>The builder.</returns>
public static ILoggingBuilder AddConfiguration(this ILoggingBuilder builder, IConfiguration configuration)
{
builder.AddConfiguration();

builder.Services.AddSingleton<IConfigureOptions<LoggerFilterOptions>>(new LoggerFilterConfigureOptions(configuration));
builder.Services.AddSingleton<IOptionsChangeTokenSource<LoggerFilterOptions>>(new ConfigurationChangeTokenSource<LoggerFilterOptions>(configuration));

builder.Services.AddSingleton(new LoggingConfiguration(configuration));

return builder;
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
// 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 Microsoft.Extensions.Configuration;

namespace Microsoft.Extensions.Logging.Configuration
{
internal class LoggingConfiguration
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is so we don't assume that we can just take IE<IConfiguration> right?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yep, I don't want random IConfiguration's in DI, especially if they were passed into logging.

{
public IConfiguration Configuration { get; }

public LoggingConfiguration(IConfiguration configuration)
{
Configuration = configuration;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@
using System;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging.Configuration;
using Microsoft.Extensions.Logging.Console;
using Microsoft.Extensions.Options;

namespace Microsoft.Extensions.Logging
{
Expand All @@ -16,8 +18,11 @@ public static class ConsoleLoggerExtensions
/// <param name="builder">The <see cref="ILoggingBuilder"/> to use.</param>
public static ILoggingBuilder AddConsole(this ILoggingBuilder builder)
{
builder.Services.AddSingleton<ILoggerProvider, ConsoleLoggerProvider>();
builder.AddConfiguration();

builder.Services.AddSingleton<ILoggerProvider, ConsoleLoggerProvider>();
builder.Services.AddSingleton<IConfigureOptions<ConsoleLoggerOptions>, ConsoleLoggerOptionsSetup>();
builder.Services.AddSingleton<IOptionsChangeTokenSource<ConsoleLoggerOptions>, LoggerProviderOptionsChangeTokenSource<ConsoleLoggerOptions, ConsoleLoggerProvider>>();
return builder;
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.

using System;

namespace Microsoft.Extensions.Logging.Console
{
public class ConsoleLoggerOptions
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging.Configuration;
using Microsoft.Extensions.Options;

namespace Microsoft.Extensions.Logging.Console
{
internal class ConsoleLoggerOptionsSetup : ConfigureFromConfigurationOptions<ConsoleLoggerOptions>
{
public ConsoleLoggerOptionsSetup(ILoggerProviderConfiguration<ConsoleLoggerProvider> providerConfiguration)
: base(providerConfiguration.Configuration)
{
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@

<ItemGroup>
<ProjectReference Include="..\Microsoft.Extensions.Logging\Microsoft.Extensions.Logging.csproj" />
<ProjectReference Include="..\Microsoft.Extensions.Logging.Configuration\Microsoft.Extensions.Logging.Configuration.csproj" />
</ItemGroup>

<ItemGroup>
Expand Down
25 changes: 1 addition & 24 deletions src/Microsoft.Extensions.Logging/LoggerRuleSelector.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,11 @@
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.

using System;
using System.Reflection;

namespace Microsoft.Extensions.Logging
{
internal class LoggerRuleSelector
{
private const string AliasAttibuteTypeFullName = "Microsoft.Extensions.Logging.ProviderAliasAttribute";
private const string AliasAttibuteAliasProperty = "Alias";

public void Select(LoggerFilterOptions options, Type providerType, string category, out LogLevel? minLevel, out Func<string, string, LogLevel, bool> filter)
{
filter = null;
Expand All @@ -24,7 +20,7 @@ public void Select(LoggerFilterOptions options, Type providerType, string catego
// 4. If there are multiple rules use last
// 5. If there are no applicable rules use global minimal level

var providerAlias = GetAlias(providerType);
var providerAlias = ProviderAliasUtilities.GetAlias(providerType);
LoggerFilterRule current = null;
foreach (var rule in options.Rules)
{
Expand All @@ -42,25 +38,6 @@ public void Select(LoggerFilterOptions options, Type providerType, string catego
}
}

private string GetAlias(Type providerType)
{
foreach (var attribute in providerType.GetTypeInfo().GetCustomAttributes(inherit: false))
{
if (attribute.GetType().FullName == AliasAttibuteTypeFullName)
{
var valueProperty = attribute
.GetType()
.GetProperty(AliasAttibuteAliasProperty, BindingFlags.Public | BindingFlags.Instance);

if (valueProperty != null)
{
return valueProperty.GetValue(attribute) as string;
}
}
}

return null;
}

private static bool IsBetter(LoggerFilterRule rule, LoggerFilterRule current, string logger, string category)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
<ItemGroup>
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" />
<PackageReference Include="Microsoft.Extensions.Options" />
<PackageReference Include="Microsoft.Extensions.Configuration.Binder" />
</ItemGroup>

</Project>
3 changes: 3 additions & 0 deletions src/Microsoft.Extensions.Logging/Properties/AssemlyInfo.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
using System.Runtime.CompilerServices;

[assembly: InternalsVisibleTo("Microsoft.Extensions.Logging.Configuration, PublicKey=0024000004800000940000000602000000240000525341310004000001000100f33a29044fa9d740c9b3213a93e57c84b472c84e0b8a0e1ae48e67a9f8f6de9d5f7f3d52ac23e48ac51801f1dc950abe901da34d2a9e3baadb141a17c77ef3c565dd5ee5054b91cf63bb3c6ab83f72ab3aafe93d0fc3c2348b764fafb0b1c0733de51459aeab46580384bf9d74c4e28164b7cde247f891ba07891c9d872ad2bb")]
34 changes: 34 additions & 0 deletions src/Microsoft.Extensions.Logging/ProviderAliasUtilities.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.

using System;
using System.Reflection;

namespace Microsoft.Extensions.Logging
{
internal class ProviderAliasUtilities
{
private const string AliasAttibuteTypeFullName = "Microsoft.Extensions.Logging.ProviderAliasAttribute";
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is by name so that 3rd party providers don't have to take an unneeded dependency ?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Some of them cannot build against aspnetcore 2.0 (AI)

private const string AliasAttibuteAliasProperty = "Alias";

internal static string GetAlias(Type providerType)
{
foreach (var attribute in providerType.GetTypeInfo().GetCustomAttributes(inherit: false))
{
if (attribute.GetType().FullName == AliasAttibuteTypeFullName)
{
var valueProperty = attribute
.GetType()
.GetProperty(AliasAttibuteAliasProperty, BindingFlags.Public | BindingFlags.Instance);

if (valueProperty != null)
{
return valueProperty.GetValue(attribute) as string;
}
}
}

return null;
}
}
}
18 changes: 18 additions & 0 deletions test/Microsoft.Extensions.Logging.Test/ConsoleLoggerTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
using System.Linq;
using System.Threading;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging.Console;
using Microsoft.Extensions.Logging.Console.Internal;
using Microsoft.Extensions.Logging.Test.Console;
Expand Down Expand Up @@ -913,6 +914,23 @@ public void ConsoleLoggerOptions_IncludeScopes_IsAppliedToLoggers()
Assert.False(logger.IncludeScopes);
}

[Fact]
public void ConsoleLoggerOptions_IncludeScopes_IsReadFromLoggingConfiguration()
{
var configuration = new ConfigurationBuilder().AddInMemoryCollection(new[] { new KeyValuePair<string, string>("Console:IncludeScopes", "true") }).Build();

var loggerProvider = new ServiceCollection()
.AddLogging(builder => builder
.AddConfiguration(configuration)
.AddConsole())
.BuildServiceProvider()
.GetRequiredService<ILoggerProvider>();

var consoleLoggerProvider = Assert.IsType<ConsoleLoggerProvider>(loggerProvider);
var logger = (ConsoleLogger)consoleLoggerProvider.CreateLogger("Category");
Assert.True(logger.IncludeScopes);
}

public static TheoryData<LogLevel, string> LevelsWithPrefixes => new TheoryData<LogLevel, string>()
{
{LogLevel.Critical, "crit"},
Expand Down
Loading