Skip to content

Commit

Permalink
NLog ApplicationInsightsTarget with support for IncludeMldc
Browse files Browse the repository at this point in the history
  • Loading branch information
snakefoot committed Jun 17, 2023
1 parent 2488006 commit 8948cf1
Show file tree
Hide file tree
Showing 4 changed files with 142 additions and 53 deletions.
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;
}
}
}
}

0 comments on commit 8948cf1

Please sign in to comment.