Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

NLog ApplicationInsightsTarget with support for IncludeMldc #2103

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
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
2 changes: 1 addition & 1 deletion LOGGING/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ If your application does not have web.config then it can also be configured manu
<add assembly="Microsoft.ApplicationInsights.NLogTarget" />
</extensions>
<targets>
<target xsi:type="ApplicationInsightsTarget" name="aiTarget">
<target xsi:type="ApplicationInsightsTarget" name="aiTarget" includeEventProperties="true" includeMdlc="false">
<instrumentationKey>Your_Resource_Key</instrumentationKey> <!-- Only required if not using ApplicationInsights.config -->
<contextproperty name="threadid" layout="${threadid}" /> <!-- Can be repeated with more context -->
</target>
Expand Down
68 changes: 17 additions & 51 deletions LOGGING/src/NLogTarget/ApplicationInsightsTarget.cs
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ namespace Microsoft.ApplicationInsights.NLogTarget
/// The messages will be uploaded to the Application Insights cloud service.
/// </summary>
[Target("ApplicationInsightsTarget")]
public sealed class ApplicationInsightsTarget : TargetWithLayout
public sealed class ApplicationInsightsTarget : TargetWithContext
{
private TelemetryClient telemetryClient;
private DateTime lastLogEventTime;
Expand All @@ -39,6 +39,7 @@ public ApplicationInsightsTarget()
{
this.Layout = @"${message}";
this.OptimizeBufferReuse = true;
this.IncludeEventProperties = true;
}

/// <summary>
Expand All @@ -50,12 +51,6 @@ public string InstrumentationKey
set => this.instrumentationKeyLayout = value ?? string.Empty;
}

/// <summary>
/// Gets the array of custom attributes to be passed into the logevent context.
/// </summary>
[ArrayParameter(typeof(TargetPropertyWithContext), "contextproperty")]
public IList<TargetPropertyWithContext> ContextProperties { get; } = new List<TargetPropertyWithContext>();

/// <summary>
/// Gets the logging controller we will be using.
/// </summary>
Expand Down Expand Up @@ -90,20 +85,25 @@ internal void BuildPropertyBag(LogEventInfo logEvent, ITelemetry trace)
propertyBag.Add("UserStackFrame", logEvent.UserStackFrame.ToString());
propertyBag.Add("UserStackFrameNumber", logEvent.UserStackFrameNumber.ToString(CultureInfo.InvariantCulture));
}

for (int i = 0; i < this.ContextProperties.Count; ++i)
else
{
var contextProperty = this.ContextProperties[i];
if (!string.IsNullOrEmpty(contextProperty.Name) && contextProperty.Layout != null)
{
string propertyValue = this.RenderLogEvent(contextProperty.Layout, logEvent);
PopulatePropertyBag(propertyBag, contextProperty.Name, propertyValue);
}
var callsiteClassName = logEvent.CallerClassName;
if (!string.IsNullOrEmpty(callsiteClassName))
propertyBag.Add("UserStackClassName", callsiteClassName);
var callsiteMemberName = logEvent.CallerMemberName;
if (!string.IsNullOrEmpty(callsiteMemberName))
propertyBag.Add("UserStackMemberName", callsiteMemberName);
var callsiteSourceFilePath = logEvent.CallerFilePath;
if (!string.IsNullOrEmpty(callsiteSourceFilePath))
propertyBag.Add("UserStackSourceFile", callsiteSourceFilePath);
var callsiteSourceLineNumber = logEvent.CallerLineNumber;
if (callsiteSourceLineNumber != 0)
propertyBag.Add("UserStackSourceLine", callsiteSourceLineNumber.ToString());
}

if (logEvent.HasProperties)
if (ShouldIncludeProperties(logEvent) || ContextProperties.Count > 0)
{
LoadLogEventProperties(logEvent, propertyBag);
this.GetAllProperties(logEvent, new StringDictionaryConverter(propertyBag));
}
}

Expand Down Expand Up @@ -182,40 +182,6 @@ protected override void FlushAsync(AsyncContinuation asyncContinuation)
}
}

private static void LoadLogEventProperties(LogEventInfo logEvent, IDictionary<string, string> propertyBag)
{
if (logEvent.Properties?.Count > 0)
{
foreach (var keyValuePair in logEvent.Properties)
{
string key = keyValuePair.Key.ToString();
object valueObj = keyValuePair.Value;
PopulatePropertyBag(propertyBag, key, valueObj);
}
}
}

private static void PopulatePropertyBag(IDictionary<string, string> propertyBag, string key, object valueObj)
{
if (valueObj == null)
{
return;
}

string value = Convert.ToString(valueObj, CultureInfo.InvariantCulture);
if (propertyBag.ContainsKey(key))
{
if (string.Equals(value, propertyBag[key], StringComparison.Ordinal))
{
return;
}

key += "_1";
}

propertyBag.Add(key, value);
}

private static SeverityLevel? GetSeverityLevel(LogLevel logEventLevel)
{
if (logEventLevel == null)
Expand Down
2 changes: 1 addition & 1 deletion LOGGING/src/NLogTarget/NLogTarget.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
</PropertyGroup>

<ItemGroup>
<PackageReference Include="NLog" Version="4.5.11" />
<PackageReference Include="NLog" Version="4.7.15" />
<PackageReference Include="Microsoft.Diagnostics.Tracing.EventRegister" Version="1.1.28">
<PrivateAssets>All</PrivateAssets>
</PackageReference>
Expand Down
123 changes: 123 additions & 0 deletions LOGGING/src/NLogTarget/StringDictionaryConverter.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
// -----------------------------------------------------------------------
// <copyright file="ApplicationInsightsTarget.cs" company="Microsoft">
// Copyright (c) Microsoft Corporation.
// All rights reserved. 2013
// </copyright>
// -----------------------------------------------------------------------

using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;

namespace Microsoft.ApplicationInsights.NLogTarget
{
/// <summary>
/// Converts from NLog Object-properties to ApplicationInsight String-properties
/// </summary>
class StringDictionaryConverter : IDictionary<string, object>
{
private readonly IDictionary<string, string> _wrapped;

public StringDictionaryConverter(IDictionary<string, string> wrapped)
{
_wrapped = wrapped;
}

public object this[string key] { get => _wrapped[key]; set => _wrapped[key] = SafeValueConverter(value); }

public ICollection<string> Keys => _wrapped.Keys;

public ICollection<object> Values => new List<object>(_wrapped.Values);

public int Count => _wrapped.Count;

public bool IsReadOnly => _wrapped.IsReadOnly;

public void Add(string key, object value)
{
_wrapped.Add(key, SafeValueConverter(value));
}

public void Add(KeyValuePair<string, object> item)
{
_wrapped.Add(new KeyValuePair<string, string>(item.Key, SafeValueConverter(item.Value)));
}

public void Clear()
{
_wrapped.Clear();
}

public bool Contains(KeyValuePair<string, object> item)
{
return _wrapped.Contains(new KeyValuePair<string, string>(item.Key, SafeValueConverter(item.Value)));
}

public bool ContainsKey(string key)
{
return _wrapped.ContainsKey(key);
}

public void CopyTo(KeyValuePair<string, object>[] array, int arrayIndex)
{
foreach (var item in _wrapped)
{
array[arrayIndex++] = new KeyValuePair<string, object>(item.Key, item.Value);
}
}

public IEnumerator<KeyValuePair<string, object>> GetEnumerator()
{
return AsEnumerable().GetEnumerator();
}

public bool Remove(string key)
{
return _wrapped.Remove(key);
}

public bool Remove(KeyValuePair<string, object> item)
{
return _wrapped.Remove(new KeyValuePair<string, string>(item.Key, SafeValueConverter(item.Value)));
}

public bool TryGetValue(string key, out object value)
{
if (_wrapped.TryGetValue(key, out var stringValue))
{
value = stringValue;
return true;
}

value = null;
return false;
}

IEnumerator IEnumerable.GetEnumerator()
{
return ((IEnumerable)_wrapped).GetEnumerator();
}

private IEnumerable<KeyValuePair<string, object>> AsEnumerable()
{
foreach (var item in _wrapped)
yield return new KeyValuePair<string, object>(item.Key, item.Value);
}

private static string SafeValueConverter(object value)
{
try
{
return Convert.ToString(value, System.Globalization.CultureInfo.InvariantCulture);
}
catch
{
return string.Empty;
}
}
}
}
Loading