This repository was archived by the owner on Oct 12, 2022. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 49
ILogger implementation for ApplicationInsights #239
Merged
cijothomas
merged 21 commits into
microsoft:develop
from
RamjotSingh:ramjsing/develop-ApplicationInsightsLogger
Dec 31, 2018
Merged
Changes from 8 commits
Commits
Show all changes
21 commits
Select commit
Hold shift + click to select a range
fdd7589
ILogger implementation for ApplicationInsights
RamjotSingh 7e21838
Adding one more tag
RamjotSingh d36c7b4
Taking PR comments
RamjotSingh 2fcabd8
Taking PR comments
RamjotSingh a600dad
Using AI packages for 2.8.1
RamjotSingh 1c1c736
Aligning the Options with the existing ConsoleLoggerOptions
RamjotSingh 591ef6b
Removing IDisposable from AppInsightsLoggers and Provider no longer c…
RamjotSingh 314972f
Renaming a file
RamjotSingh f9728ec
Adding unit tests and shifting from using TelemetryConfiguration to I…
RamjotSingh 218c5c9
Removing uneeded flag.
RamjotSingh 28f50a3
Adding extensions to specify Instrumentation key as part of logger re…
RamjotSingh 615b150
PR comments and adding sourcelink support
RamjotSingh b1662ac
Taking PR comments
RamjotSingh 14acccf
Moving back one version from latest SemVer version package since buil…
RamjotSingh 75e0691
Moving Desktop analyzers one version back
RamjotSingh 694b12e
Merge branch 'develop' into ramjsing/develop-ApplicationInsightsLogger
RamjotSingh 1788dc2
TargetFrameworks -> TargetFramework
RamjotSingh 919caa5
Merge branch 'ramjsing/develop-ApplicationInsightsLogger' of https://…
RamjotSingh 299474b
Restoring to TargetFrameworks
RamjotSingh 1eea0a3
Removing Analyzers
RamjotSingh 701651e
Merge branch 'develop' into ramjsing/develop-ApplicationInsightsLogger
cijothomas File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,193 @@ | ||
| // ----------------------------------------------------------------------- | ||
| // <copyright file="ApplicationInsightsLogger.cs" company="Microsoft"> | ||
| // Copyright (c) Microsoft Corporation. | ||
| // All rights reserved. 2013 | ||
RamjotSingh marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| // </copyright> | ||
| // ----------------------------------------------------------------------- | ||
|
|
||
| namespace Microsoft.Extensions.Logging.ApplicationInsights | ||
| { | ||
| using System; | ||
| using System.Collections.Generic; | ||
| using System.Globalization; | ||
| using System.Text; | ||
| using Microsoft.ApplicationInsights; | ||
| using Microsoft.ApplicationInsights.DataContracts; | ||
|
|
||
| /// <summary> | ||
| /// Application insights logger implementation for <see cref="ILogger"/>. | ||
| /// </summary> | ||
| /// <seealso cref="ILogger" /> | ||
| public class ApplicationInsightsLogger : ILogger | ||
| { | ||
| private readonly string categoryName; | ||
| private readonly TelemetryClient telemetryClient; | ||
| private readonly ApplicationInsightsLoggerOptions applicationInsightsLoggerOptions; | ||
|
|
||
| /// <summary> | ||
| /// Creates a new instance of <see cref="ApplicationInsightsLogger"/>. | ||
| /// </summary> | ||
| public ApplicationInsightsLogger( | ||
| string categoryName, | ||
| TelemetryClient telemetryClient, | ||
| ApplicationInsightsLoggerOptions applicationInsightsLoggerOptions) | ||
| { | ||
| this.categoryName = categoryName; | ||
| this.telemetryClient = telemetryClient; | ||
| this.applicationInsightsLoggerOptions = applicationInsightsLoggerOptions ?? throw new ArgumentNullException(nameof(applicationInsightsLoggerOptions)); | ||
| } | ||
|
|
||
| /// <summary> | ||
| /// Gets or sets the external scope provider. | ||
| /// </summary> | ||
| internal IExternalScopeProvider ExternalScopeProvider { get; set; } | ||
|
|
||
| /// <summary> | ||
| /// Begins a logical operation scope. | ||
| /// </summary> | ||
| /// <typeparam name="TState">Current state.</typeparam> | ||
| /// <param name="state">The identifier for the scope.</param> | ||
| /// <returns> | ||
| /// An IDisposable that ends the logical operation scope on dispose. | ||
| /// </returns> | ||
| public IDisposable BeginScope<TState>(TState state) | ||
| { | ||
| return this.ExternalScopeProvider != null ? this.ExternalScopeProvider.Push(state) : NullScope.Instance; | ||
RamjotSingh marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| } | ||
|
|
||
| /// <summary> | ||
| /// Checks if the given <paramref name="logLevel" /> is enabled. | ||
| /// </summary> | ||
| /// <param name="logLevel">level to be checked.</param> | ||
| /// <returns> | ||
| /// <c>true</c> if enabled. | ||
| /// </returns> | ||
| public bool IsEnabled(LogLevel logLevel) | ||
| { | ||
| return this.telemetryClient.IsEnabled(); | ||
RamjotSingh marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| } | ||
|
|
||
| /// <summary> | ||
| /// Writes a log entry. | ||
| /// </summary> | ||
| /// <typeparam name="TState">State being passed along.</typeparam> | ||
| /// <param name="logLevel">Entry will be written on this level.</param> | ||
| /// <param name="eventId">Id of the event.</param> | ||
| /// <param name="state">The entry to be written. Can be also an object.</param> | ||
| /// <param name="exception">The exception related to this entry.</param> | ||
| /// <param name="formatter">Function to create a <c>string</c> message of the <paramref name="state" /> and <paramref name="exception" />.</param> | ||
| public void Log<TState>(LogLevel logLevel, EventId eventId, TState state, Exception exception, Func<TState, Exception, string> formatter) | ||
| { | ||
| if (this.IsEnabled(logLevel)) | ||
| { | ||
| if (exception == null || !this.applicationInsightsLoggerOptions.TrackExceptionsAsExceptionTelemetry) | ||
| { | ||
| TraceTelemetry traceTelemetry = new TraceTelemetry( | ||
| formatter(state, exception), | ||
| ApplicationInsightsLogger.GetSeverityLevel(logLevel)); | ||
| this.PopulateTelemetry(traceTelemetry, state, eventId); | ||
| this.telemetryClient.TrackTrace(traceTelemetry); | ||
| } | ||
| else | ||
| { | ||
| ExceptionTelemetry exceptionTelemetry = new ExceptionTelemetry(exception) | ||
| { | ||
| Message = formatter(state, exception), | ||
| SeverityLevel = ApplicationInsightsLogger.GetSeverityLevel(logLevel), | ||
| }; | ||
|
|
||
| this.PopulateTelemetry(exceptionTelemetry, state, eventId); | ||
| this.telemetryClient.TrackException(exceptionTelemetry); | ||
| } | ||
| } | ||
| } | ||
|
|
||
| /// <summary> | ||
| /// Converts the <see cref="LogLevel"/> into corresponding Application insights <see cref="SeverityLevel"/>. | ||
| /// </summary> | ||
| /// <param name="logLevel">Logging log level.</param> | ||
| /// <returns>Application insights corresponding SeverityLevel for the LogLevel.</returns> | ||
| private static SeverityLevel GetSeverityLevel(LogLevel logLevel) | ||
| { | ||
| switch (logLevel) | ||
| { | ||
| case LogLevel.Critical: | ||
| return SeverityLevel.Critical; | ||
| case LogLevel.Error: | ||
| return SeverityLevel.Error; | ||
| case LogLevel.Warning: | ||
| return SeverityLevel.Warning; | ||
| case LogLevel.Information: | ||
| return SeverityLevel.Information; | ||
| case LogLevel.Debug: | ||
| case LogLevel.Trace: | ||
| default: | ||
| return SeverityLevel.Verbose; | ||
| } | ||
| } | ||
|
|
||
| /// <summary> | ||
| /// Populates the state, scope and event information for the logging event. | ||
| /// </summary> | ||
| /// <typeparam name="TState">State information for the current event.</typeparam> | ||
| /// <param name="telemetryItem">Telemetry item.</param> | ||
| /// <param name="state">Event state information.</param> | ||
| /// <param name="eventId">Event Id information.</param> | ||
| private void PopulateTelemetry<TState>(ISupportProperties telemetryItem, TState state, EventId eventId) | ||
| { | ||
| IDictionary<string, string> dict = telemetryItem.Properties; | ||
| dict["CategoryName"] = this.categoryName; | ||
|
|
||
| if (eventId.Id != 0) | ||
| { | ||
| dict["EventId"] = eventId.Id.ToString(CultureInfo.InvariantCulture); | ||
| } | ||
|
|
||
| if (!string.IsNullOrEmpty(eventId.Name)) | ||
| { | ||
| dict["EventName"] = eventId.Name; | ||
| } | ||
|
|
||
| if (this.applicationInsightsLoggerOptions.IncludeScopes) | ||
| { | ||
| if (state is IReadOnlyList<KeyValuePair<string, object>> stateDictionary) | ||
| { | ||
| foreach (KeyValuePair<string, object> item in stateDictionary) | ||
| { | ||
| dict[item.Key] = Convert.ToString(item.Value, CultureInfo.InvariantCulture); | ||
| } | ||
| } | ||
|
|
||
| if (this.ExternalScopeProvider != null) | ||
| { | ||
| StringBuilder stringBuilder = new StringBuilder(); | ||
| this.ExternalScopeProvider.ForEachScope( | ||
| (activeScope, builder) => | ||
| { | ||
| // Ideally we expect that the scope to implement IReadOnlyList<KeyValuePair<string, object>>. | ||
| // But this is not guaranteed as user can call BeginScope and pass anything. Hence | ||
| // we try to resolve the scope as Dictionary and if we fail, we just serialize the object and add it. | ||
|
|
||
| if (activeScope is IReadOnlyList<KeyValuePair<string, object>> activeScopeDictionary) | ||
| { | ||
| foreach (KeyValuePair<string, object> item in activeScopeDictionary) | ||
| { | ||
| dict[item.Key] = Convert.ToString(item.Value, CultureInfo.InvariantCulture); | ||
| } | ||
| } | ||
| else | ||
| { | ||
| builder.Append(" => ").Append(activeScope); | ||
| } | ||
| }, | ||
| stringBuilder); | ||
|
|
||
| if (stringBuilder.Length > 0) | ||
| { | ||
| dict["Scope"] = stringBuilder.ToString(); | ||
| } | ||
| } | ||
| } | ||
| } | ||
| } | ||
| } | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,30 @@ | ||
| // ----------------------------------------------------------------------- | ||
| // <copyright file="ApplicationInsightsLoggerOptions.cs" company="Microsoft"> | ||
| // Copyright (c) Microsoft Corporation. | ||
| // All rights reserved. 2013 | ||
| // </copyright> | ||
| // ----------------------------------------------------------------------- | ||
|
|
||
| namespace Microsoft.Extensions.Logging.ApplicationInsights | ||
| { | ||
| using Microsoft.ApplicationInsights.DataContracts; | ||
| using Microsoft.Extensions.Logging; | ||
|
|
||
| /// <summary> | ||
| /// <see cref="ApplicationInsightsLoggerOptions"/> defines the custom behavior of the tracing information sent to Application Insights. | ||
| /// </summary> | ||
| public class ApplicationInsightsLoggerOptions | ||
| { | ||
| /// <summary> | ||
| /// Gets or sets a value indicating whether to track exceptions as <see cref="ExceptionTelemetry"/>. | ||
| /// Defaults to true. | ||
| /// </summary> | ||
| public bool TrackExceptionsAsExceptionTelemetry { get; set; } = true; | ||
|
|
||
| /// <summary> | ||
| /// Gets or sets a value indicating whether the Scope information is excluded from telemetry or not. | ||
RamjotSingh marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| /// Defaults to true. | ||
| /// </summary> | ||
| public bool IncludeScopes { get; set; } = true; | ||
| } | ||
| } | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,120 @@ | ||
| // ----------------------------------------------------------------------- | ||
| // <copyright file="ApplicationInsightsLoggerProvider.cs" company="Microsoft"> | ||
| // Copyright (c) Microsoft Corporation. | ||
| // All rights reserved. 2013 | ||
| // </copyright> | ||
| // ----------------------------------------------------------------------- | ||
|
|
||
| namespace Microsoft.Extensions.Logging.ApplicationInsights | ||
| { | ||
| using System; | ||
| using System.Collections.Concurrent; | ||
| using System.Collections.Generic; | ||
| using System.Text; | ||
| using Microsoft.ApplicationInsights; | ||
| using Microsoft.ApplicationInsights.Extensibility; | ||
| using Microsoft.ApplicationInsights.Extensibility.Implementation; | ||
| using Microsoft.ApplicationInsights.Implementation; | ||
| using Microsoft.Extensions.Options; | ||
|
|
||
| /// <summary> | ||
| /// Application insights logger provider. | ||
| /// </summary> | ||
| /// <seealso cref="Microsoft.Extensions.Logging.ILoggerProvider" /> | ||
| /// <seealso cref="Microsoft.Extensions.Logging.ISupportExternalScope" /> | ||
| [ProviderAlias("ApplicationInsights")] | ||
| public class ApplicationInsightsLoggerProvider : ILoggerProvider, ISupportExternalScope | ||
RamjotSingh marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| { | ||
| /// <summary> | ||
| /// The application insights logger options. | ||
| /// </summary> | ||
| private readonly ApplicationInsightsLoggerOptions applicationInsightsLoggerOptions; | ||
|
|
||
| /// <summary> | ||
| /// The telemetry client to be used to log messages to Application Insights. | ||
| /// </summary> | ||
| private readonly TelemetryClient telemetryClient; | ||
|
|
||
| /// <summary> | ||
| /// The collection of application insights loggers stored against categoryName. | ||
| /// categoryName -> <see cref="ApplicationInsightsLogger"/> map. | ||
| /// </summary> | ||
| private readonly ConcurrentDictionary<string, ApplicationInsightsLogger> applicationInsightsLoggers | ||
| = new ConcurrentDictionary<string, ApplicationInsightsLogger>(); | ||
RamjotSingh marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
RamjotSingh marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
|
|
||
| /// <summary> | ||
| /// The external scope provider to allow setting scope data in messages. | ||
| /// </summary> | ||
| private IExternalScopeProvider externalScopeProvider; | ||
|
|
||
| /// <summary> | ||
| /// Initializes a new instance of the <see cref="ApplicationInsightsLoggerProvider"/> class. | ||
| /// </summary> | ||
| /// <param name="telemetryConfiguration">The telemetry configuration.</param> | ||
| /// <param name="applicationInsightsLoggerOptions">The application insights logger options.</param> | ||
| /// <exception cref="System.ArgumentNullException"> | ||
| /// telemetryConfiguration | ||
| /// or | ||
| /// loggingFilter | ||
| /// or | ||
| /// applicationInsightsLoggerOptions. | ||
| /// </exception> | ||
| public ApplicationInsightsLoggerProvider( | ||
| TelemetryConfiguration telemetryConfiguration, | ||
| IOptions<ApplicationInsightsLoggerOptions> applicationInsightsLoggerOptions) | ||
RamjotSingh marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| { | ||
| if (telemetryConfiguration == null) | ||
| { | ||
| throw new ArgumentNullException(nameof(telemetryConfiguration)); | ||
| } | ||
|
|
||
| this.applicationInsightsLoggerOptions = applicationInsightsLoggerOptions?.Value ?? throw new ArgumentNullException(nameof(applicationInsightsLoggerOptions)); | ||
|
|
||
| this.telemetryClient = new TelemetryClient(telemetryConfiguration); | ||
RamjotSingh marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| this.telemetryClient.Context.GetInternalContext().SdkVersion = SdkVersionUtils.GetSdkVersion("il:"); | ||
| } | ||
|
|
||
| /// <summary> | ||
| /// Creates a new <see cref="ILogger" /> instance. | ||
| /// </summary> | ||
| /// <param name="categoryName">The category name for messages produced by the logger.</param> | ||
| /// <returns>An <see cref="ILogger"/> instance to be used for logging.</returns> | ||
| public ILogger CreateLogger(string categoryName) | ||
| { | ||
| return new ApplicationInsightsLogger( | ||
| categoryName, | ||
| this.telemetryClient, | ||
| this.applicationInsightsLoggerOptions) | ||
| { | ||
| ExternalScopeProvider = this.externalScopeProvider, | ||
| }; | ||
| } | ||
|
|
||
| /// <summary> | ||
| /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. | ||
| /// </summary> | ||
| public void Dispose() | ||
| { | ||
| this.Dispose(true); | ||
| GC.SuppressFinalize(this); | ||
| } | ||
|
|
||
| /// <summary> | ||
| /// Sets the scope provider. This method also updates all the existing logger to also use the new ScopeProvider. | ||
| /// </summary> | ||
| /// <param name="externalScopeProvider">The external scope provider.</param> | ||
| public void SetScopeProvider(IExternalScopeProvider externalScopeProvider) | ||
| { | ||
| this.externalScopeProvider = externalScopeProvider; | ||
| } | ||
|
|
||
| /// <summary> | ||
| /// Releases unmanaged and - optionally - managed resources. | ||
| /// </summary> | ||
| /// <param name="releasedManagedResources">Release managed resources.</param> | ||
| protected virtual void Dispose(bool releasedManagedResources) | ||
| { | ||
| // Nothing to dispose right now. | ||
| } | ||
| } | ||
| } | ||
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.