[AppInsights] moving to newer .NET logging dependencies

This commit is contained in:
Brett Samblanet 2018-08-06 16:19:31 -07:00
Родитель 7b19acdfa6
Коммит b945a81fb0
29 изменённых файлов: 741 добавлений и 445 удалений

Просмотреть файл

@ -3,6 +3,7 @@
using System.Threading.Tasks;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
@ -22,16 +23,22 @@ namespace SampleHost
.AddServiceBus()
.AddEventHubs();
})
.AddApplicationInsights()
.ConfigureAppConfiguration(b =>
{
// Adding command line as a configuration source
b.AddCommandLine(args);
})
.ConfigureLogging(b =>
.ConfigureLogging((context, b) =>
{
b.SetMinimumLevel(LogLevel.Debug);
b.AddConsole();
// If this key exists in any config, use it to enable App Insights
string appInsightsKey = context.Configuration["APPINSIGHTS_INSTRUMENTATIONKEY"];
if (!string.IsNullOrEmpty(appInsightsKey))
{
b.AddApplicationInsights(o => o.InstrumentationKey = appInsightsKey);
}
})
.UseConsoleLifetime();

Просмотреть файл

@ -1,33 +0,0 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the MIT License. See License.txt in the project root for license information.
using System;
using Microsoft.Azure.WebJobs.Logging;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Hosting;
namespace SampleHost
{
public static class SampleLoggingExtensions
{
public static IHostBuilder AddApplicationInsights(this IHostBuilder builder)
{
// If AppInsights is enabled, build up a LoggerFactory
string instrumentationKey = Environment.GetEnvironmentVariable("APPINSIGHTS_INSTRUMENTATIONKEY");
if (!string.IsNullOrEmpty(instrumentationKey))
{
var filter = new LogCategoryFilter
{
DefaultLevel = LogLevel.Debug
};
filter.CategoryLevels[LogCategories.Results] = LogLevel.Debug;
filter.CategoryLevels[LogCategories.Aggregator] = LogLevel.Debug;
builder.AddApplicationInsights(instrumentationKey, filter.Filter, null);
}
return builder;
}
}
}

Просмотреть файл

@ -11,8 +11,6 @@ namespace Microsoft.Azure.WebJobs
/// </summary>
public sealed class JobHostOptions
{
private string _hostId;
/// <summary>
/// Returns true if <see cref="UseDevelopmentSettings"/> has been called on this instance.
/// </summary>

Просмотреть файл

@ -3,9 +3,6 @@
using System;
using Microsoft.Azure.WebJobs;
using Microsoft.Azure.WebJobs.Host.Bindings;
using Microsoft.Azure.WebJobs.Host.Config;
using Microsoft.Azure.WebJobs.Host.Loggers;
using Microsoft.Azure.WebJobs.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;

Просмотреть файл

@ -1,55 +0,0 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the MIT License. See License.txt in the project root for license information.
using System.Collections.Generic;
using System.Linq;
using Microsoft.Extensions.Logging;
namespace Microsoft.Azure.WebJobs.Logging
{
/// <summary>
/// Provides a filter for use with an <see cref="ILogger"/>.
/// </summary>
public class LogCategoryFilter
{
/// <summary>
/// The minimum <see cref="LogLevel"/> required for logs with categories that do not match
/// any category in <see cref="CategoryLevels"/>.
/// </summary>
public LogLevel DefaultLevel { get; set; } = LogLevel.Information;
/// <summary>
/// A collection of filters that are used by <see cref="Filter"/> to determine if a log
/// will be written or filtered.
/// </summary>
public IDictionary<string, LogLevel> CategoryLevels { get; } = new Dictionary<string, LogLevel> { };
/// <summary>
/// Pass this function as a filter parameter to a <see cref="ILoggerProvider"/> to enable filtering
/// based on the specified <see cref="CategoryLevels"/>. The filter will match the longest possible key in
/// <see cref="CategoryLevels"/> and return true if the level is equal to or greater than the filter. If
/// there is no match, the value of <see cref="DefaultLevel"/> is used.
/// </summary>
/// <param name="category">The category of the current log message.</param>
/// <param name="level">The <see cref="LogLevel"/> of the current log message.</param>
/// <returns>True if the level is equal to or greater than the matched filter. Otherwise, false.</returns>
public bool Filter(string category, LogLevel level)
{
// find the longest loglevel that matches the category
IEnumerable<string> matches = CategoryLevels.Keys.Where(k => category.StartsWith(k, System.StringComparison.CurrentCulture));
string longestMatch = matches?.Max();
LogLevel requestedLevel;
if (string.IsNullOrEmpty(longestMatch))
{
requestedLevel = DefaultLevel;
}
else
{
requestedLevel = CategoryLevels[longestMatch];
}
return level >= requestedLevel;
}
}
}

Просмотреть файл

@ -16,6 +16,7 @@
<ItemGroup>
<PackageReference Include="Microsoft.Extensions.Hosting" Version="2.1.0" />
<PackageReference Include="Microsoft.Extensions.Logging" Version="2.1.0" />
<PackageReference Include="Microsoft.Extensions.Logging.Configuration" Version="2.1.0" />
<PackageReference Include="StyleCop.Analyzers" Version="1.1.0-beta004">
<PrivateAssets>all</PrivateAssets>
</PackageReference>

Просмотреть файл

@ -1,228 +0,0 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the MIT License. See License.txt in the project root for license information.
using System;
using System.Collections.Generic;
using System.Reflection;
using Microsoft.ApplicationInsights;
using Microsoft.ApplicationInsights.Channel;
using Microsoft.ApplicationInsights.DependencyCollector;
using Microsoft.ApplicationInsights.Extensibility;
using Microsoft.ApplicationInsights.Extensibility.Implementation;
using Microsoft.ApplicationInsights.Extensibility.PerfCounterCollector.QuickPulse;
using Microsoft.ApplicationInsights.SnapshotCollector;
using Microsoft.ApplicationInsights.WindowsServer;
using Microsoft.ApplicationInsights.WindowsServer.Channel.Implementation;
using Microsoft.ApplicationInsights.WindowsServer.TelemetryChannel;
using Microsoft.Azure.WebJobs;
using Microsoft.Azure.WebJobs.Logging;
using Microsoft.Azure.WebJobs.Logging.ApplicationInsights;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
namespace Microsoft.Extensions.Logging
{
/// <summary>
/// Extensions for ApplicationInsights configurationon an <see cref="IHostBuilder"/>.
/// </summary>
public static class ApplicationInsightsHostBuilderExtensions
{
/// <summary>
/// Registers Application Insights and <see cref="ApplicationInsightsLoggerProvider"/> with an <see cref="IHostBuilder"/>.
/// </summary>
/// <param name="builder">The host builder.</param>
/// <param name="instrumentationKey">The Application Insights instrumentation key.</param>
/// <param name="samplingSettings">The <see cref="SamplingPercentageEstimatorSettings"/> to use for configuring adaptive sampling. If null, sampling is disabled.</param>
/// <returns>A <see cref="IHostBuilder"/> for chaining additional operations.</returns>
public static IHostBuilder AddApplicationInsights(this IHostBuilder builder, string instrumentationKey, SamplingPercentageEstimatorSettings samplingSettings)
{
return AddApplicationInsights(builder, instrumentationKey, (_, level) => level > LogLevel.Debug, samplingSettings);
}
/// <summary>
/// Registers Application Insights and <see cref="ApplicationInsightsLoggerProvider"/> with an <see cref="IHostBuilder"/>。
/// </summary>
/// <param name="builder">The host builder.</param>
/// <param name="instrumentationKey">The Application Insights instrumentation key.</param>
/// <param name="samplingSettings">Sampling settings</param>
/// <param name="snapshotCollectorConfiguration">Snapshot collector configuration.</param>
/// <returns>A <see cref="IHostBuilder"/> for chaining additional operations.</returns>
public static IHostBuilder AddApplicationInsights(this IHostBuilder builder, string instrumentationKey, SamplingPercentageEstimatorSettings samplingSettings, SnapshotCollectorConfiguration snapshotCollectorConfiguration)
{
return AddApplicationInsights(builder, instrumentationKey, (_, level) => level > LogLevel.Debug, samplingSettings, snapshotCollectorConfiguration);
}
/// <summary>
/// Registers Application Insights and <see cref="ApplicationInsightsLoggerProvider"/> with an <see cref="IHostBuilder"/>.
/// </summary>
/// <param name="builder">The host builder.</param>
/// <param name="instrumentationKey">The Application Insights instrumentation key.</param>
/// <param name="filter">A filter that returns true if a message with the specified <see cref="LogLevel"/>
/// and category should be logged. You can use <see cref="LogCategoryFilter.Filter(string, LogLevel)"/>
/// or write a custom filter.</param>
/// <param name="samplingSettings">The <see cref="SamplingPercentageEstimatorSettings"/> to use for configuring adaptive sampling. If null, sampling is disabled.</param>
/// <param name="snapshotCollectorConfiguration">The <see cref="SnapshotCollectorConfiguration" /> to use for configuring snapshot collector. If null, snapshot collector is disabled.</param>
/// <returns>A <see cref="IHostBuilder"/> for chaining additional operations.</returns>
public static IHostBuilder AddApplicationInsights(
this IHostBuilder builder,
string instrumentationKey,
Func<string, LogLevel, bool> filter,
SamplingPercentageEstimatorSettings samplingSettings,
SnapshotCollectorConfiguration snapshotCollectorConfiguration = null)
{
if (string.IsNullOrEmpty(instrumentationKey))
{
return builder;
}
builder.ConfigureServices((context, services) =>
{
services.AddSingleton<ITelemetryInitializer, HttpDependenciesParsingTelemetryInitializer>();
services.AddSingleton<ITelemetryInitializer, WebJobsRoleEnvironmentTelemetryInitializer>();
services.AddSingleton<ITelemetryInitializer, WebJobsTelemetryInitializer>();
services.AddSingleton<ITelemetryInitializer, WebJobsSanitizingInitializer>();
services.AddSingleton<ITelemetryModule, QuickPulseTelemetryModule>();
services.AddSingleton<ITelemetryModule, DependencyTrackingTelemetryModule>(provider =>
{
var dependencyCollector = new DependencyTrackingTelemetryModule();
var excludedDomains = dependencyCollector.ExcludeComponentCorrelationHttpHeadersOnDomains;
excludedDomains.Add("core.windows.net");
excludedDomains.Add("core.chinacloudapi.cn");
excludedDomains.Add("core.cloudapi.de");
excludedDomains.Add("core.usgovcloudapi.net");
excludedDomains.Add("localhost");
excludedDomains.Add("127.0.0.1");
return dependencyCollector;
});
services.AddSingleton<ITelemetryModule, AppServicesHeartbeatTelemetryModule>();
ServerTelemetryChannel serverChannel = new ServerTelemetryChannel();
services.AddSingleton<ITelemetryChannel>(serverChannel);
services.AddSingleton<TelemetryConfiguration>(provider =>
{
// Because of https://github.com/Microsoft/ApplicationInsights-dotnet-server/issues/943
// we have to touch (and create) Active configuration before initializing telemetry modules
TelemetryConfiguration activeConfig = TelemetryConfiguration.Active;
ITelemetryChannel channel = provider.GetService<ITelemetryChannel>();
TelemetryConfiguration config = TelemetryConfiguration.CreateDefault();
SetupTelemetryConfiguration(
config,
instrumentationKey,
filter,
samplingSettings,
snapshotCollectorConfiguration,
channel,
provider.GetServices<ITelemetryInitializer>(),
provider.GetServices<ITelemetryModule>());
// Function users have no access to TelemetryConfiguration from host DI container,
// so we'll expect user to work with TelemetryConfiguration.Active
// Also, some ApplicationInsights internal operations (heartbeats) depend on
// the TelemetryConfiguration.Active being set so, we'll set up Active once per process lifetime.
if (string.IsNullOrEmpty(activeConfig.InstrumentationKey))
{
SetupTelemetryConfiguration(
activeConfig,
instrumentationKey,
filter,
samplingSettings,
snapshotCollectorConfiguration,
new ServerTelemetryChannel(),
provider.GetServices<ITelemetryInitializer>(),
provider.GetServices<ITelemetryModule>());
}
return config;
});
services.AddSingleton<TelemetryClient>(provider =>
{
TelemetryConfiguration configuration = provider.GetService<TelemetryConfiguration>();
TelemetryClient client = new TelemetryClient(configuration);
string assemblyVersion = GetAssemblyFileVersion(typeof(JobHost).Assembly);
client.Context.GetInternalContext().SdkVersion = $"webjobs: {assemblyVersion}";
return client;
});
services.AddSingleton<ILoggerProvider, ApplicationInsightsLoggerProvider>();
});
return builder;
}
internal static string GetAssemblyFileVersion(Assembly assembly)
{
AssemblyFileVersionAttribute fileVersionAttr = assembly.GetCustomAttribute<AssemblyFileVersionAttribute>();
return fileVersionAttr?.Version ?? LoggingConstants.Unknown;
}
private static void SetupTelemetryConfiguration(
TelemetryConfiguration configuration,
string instrumentationKey,
Func<string, LogLevel, bool> filter,
SamplingPercentageEstimatorSettings samplingSettings,
SnapshotCollectorConfiguration snapshotCollectorConfiguration,
ITelemetryChannel channel,
IEnumerable<ITelemetryInitializer> telemetryInitializers,
IEnumerable<ITelemetryModule> telemetryModules)
{
if (instrumentationKey != null)
{
configuration.InstrumentationKey = instrumentationKey;
}
configuration.TelemetryChannel = channel;
foreach (ITelemetryInitializer initializer in telemetryInitializers)
{
configuration.TelemetryInitializers.Add(initializer);
}
(channel as ServerTelemetryChannel)?.Initialize(configuration);
QuickPulseTelemetryModule quickPulseModule = null;
foreach (ITelemetryModule module in telemetryModules)
{
if (module is QuickPulseTelemetryModule telemetryModule)
{
quickPulseModule = telemetryModule;
}
module.Initialize(configuration);
}
QuickPulseTelemetryProcessor quickPulseProcessor = null;
configuration.TelemetryProcessorChainBuilder
.Use((next) =>
{
quickPulseProcessor = new QuickPulseTelemetryProcessor(next);
return quickPulseProcessor;
})
.Use((next) => new FilteringTelemetryProcessor(filter, next));
if (samplingSettings != null)
{
configuration.TelemetryProcessorChainBuilder.Use((next) =>
new AdaptiveSamplingTelemetryProcessor(samplingSettings, null, next));
}
if (snapshotCollectorConfiguration != null)
{
configuration.TelemetryProcessorChainBuilder.UseSnapshotCollector(snapshotCollectorConfiguration);
}
configuration.TelemetryProcessorChainBuilder.Build();
quickPulseModule?.RegisterTelemetryProcessor(quickPulseProcessor);
foreach (ITelemetryProcessor processor in configuration.TelemetryProcessors)
{
if (processor is ITelemetryModule module)
{
module.Initialize(configuration);
}
}
}
}
}

Просмотреть файл

@ -0,0 +1,25 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the MIT License. See License.txt in the project root for license information.
using System.Collections.Generic;
using Microsoft.Azure.WebJobs.Logging.ApplicationInsights;
namespace Microsoft.Extensions.Logging
{
/// <summary>
/// This rule is used to replace any rules registered for the App Insights provider and store
/// them all in ChildRules. When constructing the <see cref="FilteringTelemetryProcessor"/>, these
/// rules are pulled out and applied. This allows the .NET logging infrastructure and configuration
/// to work as usual, but still allow the logs to flow through the QuickPulse processor.
/// </summary>
internal class ApplicationInsightsLoggerFilterRule : LoggerFilterRule
{
public ApplicationInsightsLoggerFilterRule(IList<LoggerFilterRule> childRules)
: base(typeof(ApplicationInsightsLoggerProvider).FullName, null, Logging.LogLevel.Trace, null)
{
ChildRules = childRules;
}
public IList<LoggerFilterRule> ChildRules { get; }
}
}

Просмотреть файл

@ -0,0 +1,17 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the MIT License. See License.txt in the project root for license information.
using Microsoft.ApplicationInsights.SnapshotCollector;
using Microsoft.ApplicationInsights.WindowsServer.Channel.Implementation;
namespace Microsoft.Azure.WebJobs.Logging.ApplicationInsights
{
public class ApplicationInsightsLoggerOptions
{
public string InstrumentationKey { get; set; }
public SamplingPercentageEstimatorSettings SamplingSettings { get; set; }
public SnapshotCollectorConfiguration SnapshotConfiguration { get; set; }
}
}

Просмотреть файл

@ -8,8 +8,12 @@ using Microsoft.Extensions.Logging;
namespace Microsoft.Azure.WebJobs.Logging.ApplicationInsights
{
[ProviderAlias(Alias)]
public class ApplicationInsightsLoggerProvider : ILoggerProvider
{
internal const string Alias = "ApplicationInsights";
private readonly TelemetryClient _client;
private bool _disposed;

Просмотреть файл

@ -0,0 +1,60 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the MIT License. See License.txt in the project root for license information.
using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Azure.WebJobs.Logging.ApplicationInsights;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging.Configuration;
namespace Microsoft.Extensions.Logging
{
/// <summary>
/// Extensions for ApplicationInsights configuration on an <see cref="ILoggingBuilder"/>.
/// </summary>
public static class ApplicationInsightsLoggingBuilderExtensions
{
/// <summary>
/// Registers Application Insights and <see cref="ApplicationInsightsLoggerProvider"/> with an <see cref="ILoggingBuilder"/>.
/// </summary>
public static ILoggingBuilder AddApplicationInsights(
this ILoggingBuilder builder)
{
return builder.AddApplicationInsights(null);
}
/// <summary>
/// Registers Application Insights and <see cref="ApplicationInsightsLoggerProvider"/> with an <see cref="ILoggingBuilder"/>.
/// </summary>
public static ILoggingBuilder AddApplicationInsights(
this ILoggingBuilder builder,
Action<ApplicationInsightsLoggerOptions> configure)
{
builder.AddConfiguration();
builder.Services.AddApplicationInsights(configure);
builder.Services.PostConfigure<LoggerFilterOptions>(o =>
{
// We want all logs to flow through the logger so they show up in QuickPulse.
// To do that, we'll hide all registered rules inside of this one. They will be re-populated
// and used by the FilteringTelemetryProcessor futher down the pipeline.
string fullTypeName = typeof(ApplicationInsightsLoggerProvider).FullName;
IList<LoggerFilterRule> matchingRules = o.Rules.Where(r =>
{
return r.ProviderName == fullTypeName
|| r.ProviderName == ApplicationInsightsLoggerProvider.Alias;
}).ToList();
foreach (var rule in matchingRules)
{
o.Rules.Remove(rule);
}
o.Rules.Add(new ApplicationInsightsLoggerFilterRule(matchingRules));
});
return builder;
}
}
}

Просмотреть файл

@ -0,0 +1,227 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the MIT License. See License.txt in the project root for license information.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using Microsoft.ApplicationInsights;
using Microsoft.ApplicationInsights.Channel;
using Microsoft.ApplicationInsights.DependencyCollector;
using Microsoft.ApplicationInsights.Extensibility;
using Microsoft.ApplicationInsights.Extensibility.Implementation;
using Microsoft.ApplicationInsights.Extensibility.PerfCounterCollector.QuickPulse;
using Microsoft.ApplicationInsights.SnapshotCollector;
using Microsoft.ApplicationInsights.WindowsServer;
using Microsoft.ApplicationInsights.WindowsServer.Channel.Implementation;
using Microsoft.ApplicationInsights.WindowsServer.TelemetryChannel;
using Microsoft.Azure.WebJobs;
using Microsoft.Azure.WebJobs.Logging.ApplicationInsights;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Logging.Configuration;
using Microsoft.Extensions.Options;
namespace Microsoft.Extensions.DependencyInjection
{
internal static class ApplicationInsightsServiceCollectionExtensions
{
public static IServiceCollection AddApplicationInsights(this IServiceCollection services, Action<ApplicationInsightsLoggerOptions> configure)
{
services.AddApplicationInsights();
if (configure != null)
{
services.Configure<ApplicationInsightsLoggerOptions>(configure);
}
return services;
}
public static IServiceCollection AddApplicationInsights(this IServiceCollection services)
{
// Bind to the configuration section registered with
services.AddOptions<ApplicationInsightsLoggerOptions>()
.Configure<ILoggerProviderConfiguration<ApplicationInsightsLoggerProvider>>((options, config) =>
{
config.Configuration?.Bind(options);
});
services.AddSingleton<ITelemetryInitializer, HttpDependenciesParsingTelemetryInitializer>();
services.AddSingleton<ITelemetryInitializer, WebJobsRoleEnvironmentTelemetryInitializer>();
services.AddSingleton<ITelemetryInitializer, WebJobsTelemetryInitializer>();
services.AddSingleton<ITelemetryInitializer, WebJobsSanitizingInitializer>();
services.AddSingleton<ITelemetryModule, QuickPulseTelemetryModule>();
services.AddSingleton<ITelemetryModule, DependencyTrackingTelemetryModule>(provider =>
{
var dependencyCollector = new DependencyTrackingTelemetryModule();
var excludedDomains = dependencyCollector.ExcludeComponentCorrelationHttpHeadersOnDomains;
excludedDomains.Add("core.windows.net");
excludedDomains.Add("core.chinacloudapi.cn");
excludedDomains.Add("core.cloudapi.de");
excludedDomains.Add("core.usgovcloudapi.net");
excludedDomains.Add("localhost");
excludedDomains.Add("127.0.0.1");
return dependencyCollector;
});
services.AddSingleton<ITelemetryModule, AppServicesHeartbeatTelemetryModule>();
ServerTelemetryChannel serverChannel = new ServerTelemetryChannel();
services.AddSingleton<ITelemetryChannel>(serverChannel);
services.AddSingleton<TelemetryConfiguration>(provider =>
{
ApplicationInsightsLoggerOptions options = provider.GetService<IOptions<ApplicationInsightsLoggerOptions>>().Value;
LoggerFilterOptions filterOptions = CreateFilterOptions(provider.GetService<IOptions<LoggerFilterOptions>>().Value);
// Because of https://github.com/Microsoft/ApplicationInsights-dotnet-server/issues/943
// we have to touch (and create) Active configuration before initializing telemetry modules
TelemetryConfiguration activeConfig = TelemetryConfiguration.Active;
ITelemetryChannel channel = provider.GetService<ITelemetryChannel>();
TelemetryConfiguration config = TelemetryConfiguration.CreateDefault();
SetupTelemetryConfiguration(
config,
options.InstrumentationKey,
options.SamplingSettings,
options.SnapshotConfiguration,
channel,
provider.GetServices<ITelemetryInitializer>(),
provider.GetServices<ITelemetryModule>(),
filterOptions);
// Function users have no access to TelemetryConfiguration from host DI container,
// so we'll expect user to work with TelemetryConfiguration.Active
// Also, some ApplicationInsights internal operations (heartbeats) depend on
// the TelemetryConfiguration.Active being set so, we'll set up Active once per process lifetime.
if (string.IsNullOrEmpty(activeConfig.InstrumentationKey))
{
SetupTelemetryConfiguration(
activeConfig,
options.InstrumentationKey,
options.SamplingSettings,
options.SnapshotConfiguration,
new ServerTelemetryChannel(),
provider.GetServices<ITelemetryInitializer>(),
provider.GetServices<ITelemetryModule>(),
filterOptions);
}
return config;
});
services.AddSingleton<TelemetryClient>(provider =>
{
TelemetryConfiguration configuration = provider.GetService<TelemetryConfiguration>();
TelemetryClient client = new TelemetryClient(configuration);
string assemblyVersion = GetAssemblyFileVersion(typeof(JobHost).Assembly);
client.Context.GetInternalContext().SdkVersion = $"webjobs: {assemblyVersion}";
return client;
});
services.AddSingleton<ILoggerProvider, ApplicationInsightsLoggerProvider>();
return services;
}
internal static LoggerFilterOptions CreateFilterOptions(LoggerFilterOptions registeredOptions)
{
// We want our own copy of the rules, excluding the 'allow-all' rule that we added for this provider.
LoggerFilterOptions customFilterOptions = new LoggerFilterOptions
{
MinLevel = registeredOptions.MinLevel
};
ApplicationInsightsLoggerFilterRule allowAllRule = registeredOptions.Rules.OfType<ApplicationInsightsLoggerFilterRule>().Single();
// Copy all existing rules
foreach (LoggerFilterRule rule in registeredOptions.Rules)
{
if (rule != allowAllRule)
{
customFilterOptions.Rules.Add(rule);
}
}
// Copy 'hidden' rules
foreach (LoggerFilterRule rule in allowAllRule.ChildRules)
{
customFilterOptions.Rules.Add(rule);
}
return customFilterOptions;
}
private static void SetupTelemetryConfiguration(
TelemetryConfiguration configuration,
string instrumentationKey,
SamplingPercentageEstimatorSettings samplingSettings,
SnapshotCollectorConfiguration snapshotCollectorConfiguration,
ITelemetryChannel channel,
IEnumerable<ITelemetryInitializer> telemetryInitializers,
IEnumerable<ITelemetryModule> telemetryModules,
LoggerFilterOptions filterOptions)
{
if (instrumentationKey != null)
{
configuration.InstrumentationKey = instrumentationKey;
}
configuration.TelemetryChannel = channel;
foreach (ITelemetryInitializer initializer in telemetryInitializers)
{
configuration.TelemetryInitializers.Add(initializer);
}
(channel as ServerTelemetryChannel)?.Initialize(configuration);
QuickPulseTelemetryModule quickPulseModule = null;
foreach (ITelemetryModule module in telemetryModules)
{
if (module is QuickPulseTelemetryModule telemetryModule)
{
quickPulseModule = telemetryModule;
}
module.Initialize(configuration);
}
QuickPulseTelemetryProcessor quickPulseProcessor = null;
configuration.TelemetryProcessorChainBuilder
.Use((next) =>
{
quickPulseProcessor = new QuickPulseTelemetryProcessor(next);
return quickPulseProcessor;
})
.Use((next) => new FilteringTelemetryProcessor(filterOptions, next));
if (samplingSettings != null)
{
configuration.TelemetryProcessorChainBuilder.Use((next) =>
new AdaptiveSamplingTelemetryProcessor(samplingSettings, null, next));
}
if (snapshotCollectorConfiguration != null)
{
configuration.TelemetryProcessorChainBuilder.UseSnapshotCollector(snapshotCollectorConfiguration);
}
configuration.TelemetryProcessorChainBuilder.Build();
quickPulseModule?.RegisterTelemetryProcessor(quickPulseProcessor);
foreach (ITelemetryProcessor processor in configuration.TelemetryProcessors)
{
if (processor is ITelemetryModule module)
{
module.Initialize(configuration);
}
}
}
internal static string GetAssemblyFileVersion(Assembly assembly)
{
AssemblyFileVersionAttribute fileVersionAttr = assembly.GetCustomAttribute<AssemblyFileVersionAttribute>();
return fileVersionAttr?.Version ?? LoggingConstants.Unknown;
}
}
}

Просмотреть файл

@ -2,6 +2,7 @@
// Licensed under the MIT License. See License.txt in the project root for license information.
using System;
using System.Collections.Concurrent;
using Microsoft.ApplicationInsights.Channel;
using Microsoft.ApplicationInsights.DataContracts;
using Microsoft.ApplicationInsights.Extensibility;
@ -12,12 +13,16 @@ namespace Microsoft.Azure.WebJobs.Logging.ApplicationInsights
{
internal class FilteringTelemetryProcessor : ITelemetryProcessor
{
private readonly Func<string, LogLevel, bool> _filter;
private static readonly LoggerRuleSelector RuleSelector = new LoggerRuleSelector();
private static readonly Type ProviderType = typeof(ApplicationInsightsLoggerProvider);
private readonly ConcurrentDictionary<string, LoggerFilterRule> _ruleMap = new ConcurrentDictionary<string, LoggerFilterRule>();
private readonly LoggerFilterOptions _filterOptions;
private ITelemetryProcessor _next;
public FilteringTelemetryProcessor(Func<string, LogLevel, bool> filter, ITelemetryProcessor next)
public FilteringTelemetryProcessor(LoggerFilterOptions filterOptions, ITelemetryProcessor next)
{
_filter = filter;
_filterOptions = filterOptions;
_next = next;
}
@ -33,7 +38,7 @@ namespace Microsoft.Azure.WebJobs.Logging.ApplicationInsights
{
bool enabled = true;
if (item is ISupportProperties telemetry && _filter != null)
if (item is ISupportProperties telemetry && _filterOptions != null)
{
if (!telemetry.Properties.TryGetValue(LogConstants.CategoryNameKey, out string categoryName))
{
@ -55,11 +60,28 @@ namespace Microsoft.Azure.WebJobs.Logging.ApplicationInsights
if (telemetry.Properties.TryGetValue(LogConstants.LogLevelKey, out string logLevelString) &&
Enum.TryParse(logLevelString, out LogLevel logLevel))
{
enabled = _filter(categoryName, logLevel);
LoggerFilterRule filterRule = _ruleMap.GetOrAdd(categoryName, SelectRule(categoryName));
if (filterRule.LogLevel != null && logLevel < filterRule.LogLevel)
{
enabled = false;
}
else if (filterRule.Filter != null)
{
enabled = filterRule.Filter(ProviderType.FullName, categoryName, logLevel);
}
}
}
return enabled;
}
private LoggerFilterRule SelectRule(string categoryName)
{
RuleSelector.Select(_filterOptions, ProviderType, categoryName,
out LogLevel? minLevel, out Func<string, string, LogLevel, bool> filter);
return new LoggerFilterRule(ProviderType.FullName, categoryName, minLevel, filter);
}
}
}

Просмотреть файл

@ -0,0 +1,90 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the MIT License. See License.txt in the project root for license information.
// This file is copied directly from:
// https://github.com/aspnet/Logging/blob/cc350d7ef616ef292c1b4ae7130b8c2b45fc1164/src/Microsoft.Extensions.Logging/LoggerRuleSelector.cs.
using System;
namespace Microsoft.Extensions.Logging
{
internal class LoggerRuleSelector
{
public void Select(LoggerFilterOptions options, Type providerType, string category, out LogLevel? minLevel, out Func<string, string, LogLevel, bool> filter)
{
filter = null;
minLevel = options.MinLevel;
// Filter rule selection:
// 1. Select rules for current logger type, if there is none, select ones without logger type specified
// 2. Select rules with longest matching categories
// 3. If there nothing matched by category take all rules without category
// 3. If there is only one rule use it's level and filter
// 4. If there are multiple rules use last
// 5. If there are no applicable rules use global minimal level
var providerAlias = ProviderAliasUtilities.GetAlias(providerType);
LoggerFilterRule current = null;
foreach (var rule in options.Rules)
{
if (IsBetter(rule, current, providerType.FullName, category)
|| (!string.IsNullOrEmpty(providerAlias) && IsBetter(rule, current, providerAlias, category)))
{
current = rule;
}
}
if (current != null)
{
filter = current.Filter;
minLevel = current.LogLevel;
}
}
private static bool IsBetter(LoggerFilterRule rule, LoggerFilterRule current, string logger, string category)
{
// Skip rules with inapplicable type or category
if (rule.ProviderName != null && rule.ProviderName != logger)
{
return false;
}
if (rule.CategoryName != null && !category.StartsWith(rule.CategoryName, StringComparison.OrdinalIgnoreCase))
{
return false;
}
if (current?.ProviderName != null)
{
if (rule.ProviderName == null)
{
return false;
}
}
else
{
// We want to skip category check when going from no provider to having provider
if (rule.ProviderName != null)
{
return true;
}
}
if (current?.CategoryName != null)
{
if (rule.CategoryName == null)
{
return false;
}
if (current.CategoryName.Length > rule.CategoryName.Length)
{
return false;
}
}
return true;
}
}
}

Просмотреть файл

@ -0,0 +1,18 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the MIT License. See License.txt in the project root for license information.
using System;
using System.Linq;
using System.Reflection;
namespace Microsoft.Extensions.Logging
{
internal static class ProviderAliasUtilities
{
internal static string GetAlias(Type providerType)
{
var attribute = providerType.GetCustomAttributes<ProviderAliasAttribute>(inherit: false).FirstOrDefault();
return attribute?.Alias;
}
}
}

Просмотреть файл

@ -26,12 +26,8 @@ namespace Microsoft.Azure.WebJobs.EventHubs.UnitTests
public EventHubConfigurationTests()
{
_loggerFactory = new LoggerFactory();
var filter = new LogCategoryFilter
{
DefaultLevel = LogLevel.Debug
};
_loggerProvider = new TestLoggerProvider(filter.Filter);
_loggerProvider = new TestLoggerProvider();
_loggerFactory.AddProvider(_loggerProvider);
}

Просмотреть файл

@ -42,16 +42,8 @@ namespace Microsoft.Azure.WebJobs.Host.EndToEndTests
private const string _dateFormat = "HH':'mm':'ss'.'fffK";
private IHost ConfigureHost(LogCategoryFilter filter = null)
private IHost ConfigureHost(LogLevel minLevel = LogLevel.Information)
{
if (filter == null)
{
filter = new LogCategoryFilter
{
DefaultLevel = LogLevel.Information
};
}
var host = new HostBuilder()
.ConfigureDefaultTestHost<ApplicationInsightsEndToEndTests>()
.ConfigureServices(services =>
@ -61,7 +53,11 @@ namespace Microsoft.Azure.WebJobs.Host.EndToEndTests
o.IsEnabled = false;
});
})
.AddApplicationInsights(_mockApplicationInsightsKey, filter.Filter, null)
.ConfigureLogging(b =>
{
b.SetMinimumLevel(minLevel);
b.AddApplicationInsights(o => o.InstrumentationKey = _mockApplicationInsightsKey);
})
.ConfigureServices(services =>
{
var quickPulse = services.Single(s => s.ImplementationType == typeof(QuickPulseTelemetryModule));
@ -192,13 +188,8 @@ namespace Microsoft.Azure.WebJobs.Host.EndToEndTests
[InlineData(LogLevel.Warning, 2, 0)] // 2x warning trace per request
public async Task QuickPulse_Works_EvenIfFiltered(LogLevel defaultLevel, int tracesPerRequest, int additionalTraces)
{
LogCategoryFilter filter = new LogCategoryFilter
{
DefaultLevel = defaultLevel
};
using (var qpListener = new QuickPulseEventListener())
using (IHost host = ConfigureHost(filter))
using (IHost host = ConfigureHost(defaultLevel))
{
var listener = new ApplicationInsightsTestListener();
int functionsCalled = 0;

Просмотреть файл

@ -94,7 +94,7 @@ namespace Microsoft.Azure.WebJobs.Host.EndToEndTests.ApplicationInsights
{
ValidateBlobDependency(
inputDep,
_inputContainerName,
_inputContainerName,
"in",
nameof(BlobInputAndOutputBindings),
request.Context.Operation.Id,
@ -104,7 +104,7 @@ namespace Microsoft.Azure.WebJobs.Host.EndToEndTests.ApplicationInsights
foreach (var outputDep in outDependencies)
{
ValidateBlobDependency(
outputDep,
outputDep,
_outputContainerName,
"out",
nameof(BlobInputAndOutputBindings),
@ -270,7 +270,7 @@ namespace Microsoft.Azure.WebJobs.Host.EndToEndTests.ApplicationInsights
_functionWaitHandle.Set();
}
[NoAutomaticTrigger]
public static async Task UserCodeHttpCall()
{
@ -307,16 +307,16 @@ namespace Microsoft.Azure.WebJobs.Host.EndToEndTests.ApplicationInsights
}
private void ValidateBlobDependency(
DependencyTelemetry dependency,
string containerName,
string blobName,
DependencyTelemetry dependency,
string containerName,
string blobName,
string operationName,
string operationId,
string requestId)
{
Assert.Equal("Azure blob", dependency.Type);
Assert.Equal(containerName, dependency.Properties["Container"]);
// container creation does not have blob info
if (dependency.Properties.ContainsKey("Blob"))
{
@ -361,7 +361,6 @@ namespace Microsoft.Azure.WebJobs.Host.EndToEndTests.ApplicationInsights
public IHost ConfigureHost(LogLevel logLevel)
{
var filter = new LogCategoryFilter { DefaultLevel = logLevel };
_resolver = new RandomNameResolver();
IHost host = new HostBuilder()
@ -377,7 +376,11 @@ namespace Microsoft.Azure.WebJobs.Host.EndToEndTests.ApplicationInsights
o.IsEnabled = false;
});
})
.AddApplicationInsights(_mockApplicationInsightsKey, filter.Filter, null)
.ConfigureLogging(b =>
{
b.SetMinimumLevel(logLevel);
b.AddApplicationInsights(o => o.InstrumentationKey = _mockApplicationInsightsKey);
})
.Build();
TelemetryConfiguration telemteryConfiguration = host.Services.GetService<TelemetryConfiguration>();

Просмотреть файл

@ -102,9 +102,7 @@ namespace Microsoft.Azure.WebJobs.Host.UnitTests.Singleton
public SingletonManagerTests()
{
ILoggerFactory loggerFactory = new LoggerFactory();
// We want to see all logs, so set the default level to Trace.
LogCategoryFilter filter = new LogCategoryFilter { DefaultLevel = Microsoft.Extensions.Logging.LogLevel.Trace };
_loggerProvider = new TestLoggerProvider(filter.Filter);
_loggerProvider = new TestLoggerProvider();
loggerFactory.AddProvider(_loggerProvider);
var logger = loggerFactory?.CreateLogger(LogCategories.Singleton);

Просмотреть файл

@ -10,17 +10,15 @@ namespace Microsoft.Azure.WebJobs.Host.TestCommon
{
public class TestLogger : ILogger
{
private readonly Func<string, LogLevel, bool> _filter;
private readonly Action<LogMessage> _logAction;
private IList<LogMessage> _logMessages = new List<LogMessage>();
// protect against changes to logMessages while enumerating
private object _syncLock = new object();
public TestLogger(string category, Func<string, LogLevel, bool> filter = null, Action<LogMessage> logAction = null)
public TestLogger(string category, Action<LogMessage> logAction = null)
{
Category = category;
_filter = filter;
_logAction = logAction;
}
@ -33,7 +31,7 @@ namespace Microsoft.Azure.WebJobs.Host.TestCommon
public bool IsEnabled(LogLevel logLevel)
{
return _filter?.Invoke(Category, logLevel) ?? true;
return true;
}
public IList<LogMessage> GetLogMessages()

Просмотреть файл

@ -4,20 +4,19 @@
using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Azure.WebJobs.Logging;
using Microsoft.Extensions.Logging;
namespace Microsoft.Azure.WebJobs.Host.TestCommon
{
public class TestLoggerProvider : ILoggerProvider
{
private readonly Func<string, LogLevel, bool> _filter;
private readonly LoggerFilterOptions _filter;
private readonly Action<LogMessage> _logAction;
private Dictionary<string, TestLogger> _loggerCache { get; } = new Dictionary<string, TestLogger>();
public TestLoggerProvider(Func<string, LogLevel, bool> filter = null, Action<LogMessage> logAction = null)
public TestLoggerProvider(Action<LogMessage> logAction = null)
{
_filter = filter ?? new LogCategoryFilter().Filter;
_filter = new LoggerFilterOptions();
_logAction = logAction;
}
@ -27,7 +26,7 @@ namespace Microsoft.Azure.WebJobs.Host.TestCommon
{
if (!_loggerCache.TryGetValue(categoryName, out TestLogger logger))
{
logger = new TestLogger(categoryName, _filter, _logAction);
logger = new TestLogger(categoryName, _logAction);
_loggerCache.Add(categoryName, logger);
}

Просмотреть файл

@ -246,7 +246,7 @@ namespace Microsoft.Azure.WebJobs.Host.UnitTests.Listeners
public async Task FunctionListener_DoesNotThrow_IfHandled()
{
ILoggerFactory handlingLoggerFactory = new LoggerFactory();
TestLoggerProvider handlingLoggerProvider = new TestLoggerProvider(null, (m) => (m.Exception as RecoverableException).Handled = true);
TestLoggerProvider handlingLoggerProvider = new TestLoggerProvider((m) => (m.Exception as RecoverableException).Handled = true);
handlingLoggerFactory.AddProvider(handlingLoggerProvider);
Mock<IListener> badListener = new Mock<IListener>(MockBehavior.Strict);
@ -272,7 +272,7 @@ namespace Microsoft.Azure.WebJobs.Host.UnitTests.Listeners
public async Task FunctionListener_DoesNotStop_IfNotStarted()
{
ILoggerFactory handlingLoggerFactory = new LoggerFactory();
TestLoggerProvider handlingLoggerProvider = new TestLoggerProvider(null, (m) => (m.Exception as RecoverableException).Handled = true);
TestLoggerProvider handlingLoggerProvider = new TestLoggerProvider((m) => (m.Exception as RecoverableException).Handled = true);
handlingLoggerFactory.AddProvider(handlingLoggerProvider);
Mock<IListener> badListener = new Mock<IListener>(MockBehavior.Strict);

Просмотреть файл

@ -2,6 +2,7 @@
// Licensed under the MIT License. See License.txt in the project root for license information.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using Microsoft.ApplicationInsights;
@ -15,9 +16,11 @@ using Microsoft.ApplicationInsights.WindowsServer;
using Microsoft.ApplicationInsights.WindowsServer.Channel.Implementation;
using Microsoft.ApplicationInsights.WindowsServer.TelemetryChannel;
using Microsoft.Azure.WebJobs.Logging.ApplicationInsights;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using Xunit;
namespace Microsoft.Azure.WebJobs.Host.UnitTests.Loggers
@ -27,7 +30,13 @@ namespace Microsoft.Azure.WebJobs.Host.UnitTests.Loggers
[Fact]
public void DependencyInjectionConfiguration_Configures()
{
using (var host = new HostBuilder().AddApplicationInsights("some key", (c, l) => true, null).Build())
var builder = new HostBuilder()
.ConfigureLogging(b =>
{
b.AddApplicationInsights(o => o.InstrumentationKey = "some key");
});
using (var host = builder.Build())
{
var config = host.Services.GetService<TelemetryConfiguration>();
@ -73,9 +82,16 @@ namespace Microsoft.Azure.WebJobs.Host.UnitTests.Loggers
[Fact]
public void DependencyInjectionConfiguration_ConfiguresSampling()
{
var samplingSettings = new SamplingPercentageEstimatorSettings {MaxTelemetryItemsPerSecond = 1};
var samplingSettings = new SamplingPercentageEstimatorSettings { MaxTelemetryItemsPerSecond = 1 };
using (var host = new HostBuilder()
.AddApplicationInsights("some key", (c, l) => true, samplingSettings)
.ConfigureLogging(b =>
{
b.AddApplicationInsights(o =>
{
o.InstrumentationKey = "some key";
o.SamplingSettings = samplingSettings;
});
})
.Build())
{
var config = host.Services.GetService<TelemetryConfiguration>();
@ -84,7 +100,7 @@ namespace Microsoft.Azure.WebJobs.Host.UnitTests.Loggers
Assert.IsType<FilteringTelemetryProcessor>(config.TelemetryProcessors[1]);
Assert.IsType<AdaptiveSamplingTelemetryProcessor>(config.TelemetryProcessors[2]);
Assert.Equal(samplingSettings.MaxTelemetryItemsPerSecond, ((AdaptiveSamplingTelemetryProcessor) config.TelemetryProcessors[2]).MaxTelemetryItemsPerSecond);
Assert.Equal(samplingSettings.MaxTelemetryItemsPerSecond, ((AdaptiveSamplingTelemetryProcessor)config.TelemetryProcessors[2]).MaxTelemetryItemsPerSecond);
}
}
@ -93,8 +109,14 @@ namespace Microsoft.Azure.WebJobs.Host.UnitTests.Loggers
{
var samplingSettings = new SamplingPercentageEstimatorSettings { MaxTelemetryItemsPerSecond = 1 };
using (var host = new HostBuilder()
.AddApplicationInsights("some key", samplingSettings)
.Build())
.ConfigureLogging(b =>
{
b.AddApplicationInsights(o =>
{
o.InstrumentationKey = "some key";
o.SamplingSettings = samplingSettings;
});
}).Build())
{
var config = host.Services.GetService<TelemetryConfiguration>();
Assert.Equal(4, config.TelemetryProcessors.Count);
@ -111,8 +133,14 @@ namespace Microsoft.Azure.WebJobs.Host.UnitTests.Loggers
{
var snapshotConfiguration = new SnapshotCollectorConfiguration();
using (var host = new HostBuilder()
.AddApplicationInsights("some key", (c, l) => true, null, snapshotConfiguration)
.Build())
.ConfigureLogging(b =>
{
b.AddApplicationInsights(o =>
{
o.InstrumentationKey = "some key";
o.SnapshotConfiguration = snapshotConfiguration;
});
}).Build())
{
var config = host.Services.GetService<TelemetryConfiguration>();
Assert.Equal(4, config.TelemetryProcessors.Count);
@ -126,8 +154,13 @@ namespace Microsoft.Azure.WebJobs.Host.UnitTests.Loggers
public void DependencyInjectionConfiguration_ConfiguresActive()
{
using (var host = new HostBuilder()
.AddApplicationInsights("some key", (c, l) => true, null)
.Build())
.ConfigureLogging(b =>
{
b.AddApplicationInsights(o =>
{
o.InstrumentationKey = "some key";
});
}).Build())
{
var config = TelemetryConfiguration.Active;
// Verify Initializers
@ -169,6 +202,167 @@ namespace Microsoft.Azure.WebJobs.Host.UnitTests.Loggers
}
}
[Fact]
public void CreateFilterOptions_MinLevel()
{
using (var host = CreateHost((c, b) => b.SetMinimumLevel(LogLevel.None)))
{
var options = GetInternalLoggerFilterOptions(host.Services);
var rule = SelectAppInsightsRule(options, "Any");
Assert.Equal(LogLevel.None, rule.LogLevel);
}
}
[Fact]
public void CreateFilterOptions_CategoryFilter()
{
// Only return true if the category matches
using (var host = CreateHost((c, b) => b.AddFilter("MyFakeCategory", LogLevel.Error)))
{
var options = GetInternalLoggerFilterOptions(host.Services);
var rule = SelectAppInsightsRule(options, "MyFakeCategory");
Assert.Equal(LogLevel.Error, rule.LogLevel);
// Make sure the default still applies to other categories
rule = SelectAppInsightsRule(options, "AnotherCategory");
Assert.Equal(LogLevel.Information, rule.LogLevel);
}
}
[Fact]
public void CreateFilterOptions_CustomFilter()
{
// Only return true if the category matches
using (var host = CreateHost((c, b) => b.AddFilter((cat, l) => cat == "MyFakeCategory")))
{
var options = GetInternalLoggerFilterOptions(host.Services);
var rule = SelectAppInsightsRule(options, null);
Assert.Null(rule.LogLevel);
Assert.False(rule.Filter(null, "SomeOtherCategory", LogLevel.Information));
Assert.True(rule.Filter(null, "MyFakeCategory", LogLevel.Information));
}
}
[Fact]
public void CreateFilterOptions_AppInsightsFilter()
{
// Make sure we allow custom filters for our logger
using (var host = CreateHost((c, b) => b.AddFilter<ApplicationInsightsLoggerProvider>((cat, l) => cat == "MyFakeCategory")))
{
var options = GetInternalLoggerFilterOptions(host.Services);
var rule = SelectAppInsightsRule(options, null);
Assert.Null(rule.LogLevel);
Assert.False(rule.Filter(null, "SomeOtherCategory", LogLevel.Information));
Assert.True(rule.Filter(null, "MyFakeCategory", LogLevel.Information));
}
}
[Fact]
public void CreateFilterOptions_AppInsightsMinLevel()
{
// Make sure we allow custom filters for our logger
using (var host = CreateHost((c, b) => b.AddFilter<ApplicationInsightsLoggerProvider>("MyFakeCategory", LogLevel.Critical)))
{
var options = GetInternalLoggerFilterOptions(host.Services);
var rule = SelectAppInsightsRule(options, "MyFakeCategory");
Assert.Equal(LogLevel.Critical, rule.LogLevel);
}
}
[Fact]
public void CreateFilterOptions_AppInsightsMultipleCustomFilters()
{
using (var host = CreateHost((c, b) =>
{
// Last one should win
b.AddFilter<ApplicationInsightsLoggerProvider>((cat, l) => cat == "1");
b.AddFilter<ApplicationInsightsLoggerProvider>((cat, l) => cat == "2");
b.AddFilter<ApplicationInsightsLoggerProvider>((cat, l) => cat == "3");
}))
{
var options = GetInternalLoggerFilterOptions(host.Services);
var rule = SelectAppInsightsRule(options, "MyFakeCategory");
Assert.Null(rule.LogLevel);
Assert.False(rule.Filter(null, "1", LogLevel.Critical));
Assert.False(rule.Filter(null, "2", LogLevel.Critical));
Assert.True(rule.Filter(null, "3", LogLevel.Critical));
}
}
[Theory]
[InlineData("Logging")]
[InlineData("Multiple:Sections")]
public void Filter_BindsToConfiguration(string configSection)
{
using (var host = CreateHost(
configureLogging: (c, b) =>
{
// This is how logging config sections are registered by applications
b.AddConfiguration(c.Configuration.GetSection(configSection));
},
configureConfiguration: b =>
{
b.AddInMemoryCollection(new Dictionary<string, string>
{
{ $"{configSection}:{ApplicationInsightsLoggerProvider.Alias}:LogLevel:Default", LogLevel.Warning.ToString() }
});
}))
{
var options = GetInternalLoggerFilterOptions(host.Services);
var rule = SelectAppInsightsRule(options, "MyFakeCategory");
Assert.Equal(LogLevel.Warning, rule.LogLevel);
}
}
private static IHost CreateHost(Action<HostBuilderContext, ILoggingBuilder> configureLogging = null, Action<IConfigurationBuilder> configureConfiguration = null)
{
var builder = new HostBuilder()
.ConfigureWebJobs()
.ConfigureLogging((c, b) =>
{
b.AddApplicationInsights(o =>
{
o.InstrumentationKey = "some key";
});
configureLogging?.Invoke(c, b);
});
if (configureConfiguration != null)
{
builder.ConfigureAppConfiguration(configureConfiguration);
}
return builder.Build();
}
private static LoggerFilterOptions GetInternalLoggerFilterOptions(IServiceProvider services)
{
var filterOptions = services.GetService<IOptions<LoggerFilterOptions>>().Value;
// The previous options will set the level to Trace
var rule = SelectAppInsightsRule(filterOptions, "UnknownCategory");
Assert.Equal(LogLevel.Trace, rule.LogLevel);
// These are the options to be used by the filtering processor
var internalOptions = ApplicationInsightsServiceCollectionExtensions.CreateFilterOptions(filterOptions);
Assert.NotSame(filterOptions, internalOptions);
return internalOptions;
}
// Helper to pull out the calculated rule
private static LoggerFilterRule SelectAppInsightsRule(LoggerFilterOptions options, string category)
{
var providerType = typeof(ApplicationInsightsLoggerProvider);
var ruleSelector = new LoggerRuleSelector();
ruleSelector.Select(options, providerType, category, out LogLevel? minLevel, out Func<string, string, LogLevel, bool> filter);
return new LoggerFilterRule(providerType.FullName, category, minLevel, filter);
}
public void Dispose()
{
TelemetryConfiguration.Active.Dispose();

Просмотреть файл

@ -55,8 +55,14 @@ namespace Microsoft.Azure.WebJobs.Host.UnitTests.Loggers
};
_host = new HostBuilder()
.AddApplicationInsights("some key", (c, l) => true, null)
.Build();
.ConfigureLogging(b =>
{
b.SetMinimumLevel(LogLevel.Trace);
b.AddApplicationInsights(o =>
{
o.InstrumentationKey = "some key";
});
}).Build();
TelemetryConfiguration telemteryConfiguration = _host.Services.GetService<TelemetryConfiguration>();
telemteryConfiguration.TelemetryChannel = _channel;

Просмотреть файл

@ -1,6 +1,7 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the MIT License. See License.txt in the project root for license information.
using System;
using System.Collections.Generic;
using Microsoft.ApplicationInsights.Channel;
@ -31,14 +32,13 @@ namespace Microsoft.Azure.WebJobs.Host.UnitTests.Loggers
[InlineData(LogLevel.Trace, LogLevel.None, LogLevel.Information, false)]
public void Processor_UsesFilter(LogLevel defaultLevel, LogLevel categoryLevel, LogLevel telemetryLevel, bool isEnabled)
{
var filter = new LogCategoryFilter
var filter = new LoggerFilterOptions
{
DefaultLevel = defaultLevel
MinLevel = defaultLevel
};
filter.AddFilter(LogCategories.Results, categoryLevel);
filter.CategoryLevels[LogCategories.Results] = categoryLevel;
var processor = new FilteringTelemetryProcessor(filter.Filter, _nextTelemetryProcessorMock.Object);
var processor = new FilteringTelemetryProcessor(filter, _nextTelemetryProcessorMock.Object);
var telemetry = new TestTelemetry();
telemetry.Properties[LogConstants.CategoryNameKey] = LogCategories.Results;
@ -59,9 +59,9 @@ namespace Microsoft.Azure.WebJobs.Host.UnitTests.Loggers
[Fact]
public void Processor_No_ISupportProperties_DoesNotFilter()
{
var filter = new LogCategoryFilter();
var filter = new LoggerFilterOptions();
var processor = new FilteringTelemetryProcessor(filter.Filter, _nextTelemetryProcessorMock.Object);
var processor = new FilteringTelemetryProcessor(filter, _nextTelemetryProcessorMock.Object);
var telemetry = new Mock<ITelemetry>(MockBehavior.Strict);
@ -75,12 +75,12 @@ namespace Microsoft.Azure.WebJobs.Host.UnitTests.Loggers
[InlineData(LogLevel.Error, true)]
public void Processor_MissingCategory_FiltersAsDefault(LogLevel telemetryLevel, bool isEnabled)
{
var filter = new LogCategoryFilter
var filter = new LoggerFilterOptions
{
DefaultLevel = LogLevel.Information
MinLevel = LogLevel.Information
};
var processor = new FilteringTelemetryProcessor(filter.Filter, _nextTelemetryProcessorMock.Object);
var processor = new FilteringTelemetryProcessor(filter, _nextTelemetryProcessorMock.Object);
var telemetry = new TestTelemetry();
telemetry.Properties[LogConstants.LogLevelKey] = telemetryLevel.ToString();
@ -102,12 +102,12 @@ namespace Microsoft.Azure.WebJobs.Host.UnitTests.Loggers
public void Processor_MissingLogLevel_DoesNotFilter()
{
// If no loglevel, we're not sure what to do, so just let it through
var filter = new LogCategoryFilter
var filter = new LoggerFilterOptions
{
DefaultLevel = LogLevel.Information
MinLevel = LogLevel.Information
};
var processor = new FilteringTelemetryProcessor(filter.Filter, _nextTelemetryProcessorMock.Object);
var processor = new FilteringTelemetryProcessor(filter, _nextTelemetryProcessorMock.Object);
var telemetry = new TestTelemetry();
telemetry.Properties[LogConstants.CategoryNameKey] = LogCategories.Results;
@ -121,12 +121,12 @@ namespace Microsoft.Azure.WebJobs.Host.UnitTests.Loggers
public void Processor_InvalidLogLevel_DoesNotFilter()
{
// If no valid loglevel, we're not sure what to do, so just let it through
var filter = new LogCategoryFilter
var filter = new LoggerFilterOptions
{
DefaultLevel = LogLevel.Information
MinLevel = LogLevel.Information
};
var processor = new FilteringTelemetryProcessor(filter.Filter, _nextTelemetryProcessorMock.Object);
var processor = new FilteringTelemetryProcessor(filter, _nextTelemetryProcessorMock.Object);
var telemetry = new TestTelemetry();
telemetry.Properties[LogConstants.CategoryNameKey] = LogCategories.Results;

Просмотреть файл

@ -1,34 +0,0 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the MIT License. See License.txt in the project root for license information.
using Microsoft.Azure.WebJobs.Logging;
using Microsoft.Extensions.Logging;
using Xunit;
namespace Microsoft.Azure.WebJobs.Host.UnitTests.Loggers
{
public class LogCategoryFilterTests
{
[Fact]
public void Filter_MatchesLongestCategory()
{
var filter = new LogCategoryFilter();
filter.DefaultLevel = LogLevel.Error;
filter.CategoryLevels.Add("Microsoft", LogLevel.Critical);
filter.CategoryLevels.Add("Microsoft.Azure", LogLevel.Error);
filter.CategoryLevels.Add("Microsoft.Azure.WebJobs", LogLevel.Information);
filter.CategoryLevels.Add("Microsoft.Azure.WebJobs.Host", LogLevel.Trace);
Assert.False(filter.Filter("Microsoft", LogLevel.Information));
Assert.False(filter.Filter("Microsoft.Azure", LogLevel.Information));
Assert.False(filter.Filter("Microsoft.Azure.WebJob", LogLevel.Information));
Assert.False(filter.Filter("NoMatch", LogLevel.Information));
Assert.True(filter.Filter("Microsoft", LogLevel.Critical));
Assert.True(filter.Filter("Microsoft.Azure", LogLevel.Critical));
Assert.True(filter.Filter("Microsoft.Azure.WebJobs.Extensions", LogLevel.Information));
Assert.True(filter.Filter("Microsoft.Azure.WebJobs.Host", LogLevel.Debug));
Assert.True(filter.Filter("NoMatch", LogLevel.Error));
}
}
}

Просмотреть файл

@ -1,10 +1,6 @@
// Copyright (c) .NET Foundation. All rights reserved.
// Licensed under the MIT License. See License.txt in the project root for license information.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using Microsoft.Azure.WebJobs.Host.TestCommon;
using Microsoft.Azure.WebJobs.Logging;
using Microsoft.Azure.WebJobs.Logging.ApplicationInsights;
@ -18,7 +14,7 @@ namespace Microsoft.Azure.WebJobs.Host.UnitTests
/// we review such additions carefully.
/// </summary>
public class PublicSurfaceTests
{
{
[Fact]
public void AssemblyReferences_InJobsHostAssembly()
{
@ -188,7 +184,6 @@ namespace Microsoft.Azure.WebJobs.Host.UnitTests
"JobHostService",
"ListenerFactoryContext",
"LogCategories",
"LogCategoryFilter",
"LogConstants",
"LoggerExtensions",
"NameResolverExtensions",
@ -227,11 +222,12 @@ namespace Microsoft.Azure.WebJobs.Host.UnitTests
var expected = new[]
{
"ApplicationInsightsLoggerOptions",
"ApplicationInsightsLoggerProvider",
"ApplicationInsightsHostBuilderExtensions"
"ApplicationInsightsLoggingBuilderExtensions"
};
TestHelpers.AssertPublicTypes(expected, assembly);
}
}
}
}

Просмотреть файл

@ -12,6 +12,7 @@
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Extensions.Configuration" Version="2.1.0" />
<PackageReference Include="StyleCop.Analyzers" Version="1.1.0-beta004">
<PrivateAssets>all</PrivateAssets>
</PackageReference>

Просмотреть файл

@ -20,9 +20,7 @@ namespace Microsoft.Azure.WebJobs.ServiceBus.UnitTests.Config
public ServiceBusConfigurationTests()
{
_loggerFactory = new LoggerFactory();
var filter = new LogCategoryFilter();
filter.DefaultLevel = LogLevel.Debug;
_loggerProvider = new TestLoggerProvider(filter.Filter);
_loggerProvider = new TestLoggerProvider();
_loggerFactory.AddProvider(_loggerProvider);
}