Skip to content

Commit 8948cf1

Browse files
committed
NLog ApplicationInsightsTarget with support for IncludeMldc
1 parent 2488006 commit 8948cf1

File tree

4 files changed

+142
-53
lines changed

4 files changed

+142
-53
lines changed

LOGGING/README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ If your application does not have web.config then it can also be configured manu
4545
<add assembly="Microsoft.ApplicationInsights.NLogTarget" />
4646
</extensions>
4747
<targets>
48-
<target xsi:type="ApplicationInsightsTarget" name="aiTarget">
48+
<target xsi:type="ApplicationInsightsTarget" name="aiTarget" includeEventProperties="true" includeMdlc="false">
4949
<instrumentationKey>Your_Resource_Key</instrumentationKey> <!-- Only required if not using ApplicationInsights.config -->
5050
<contextproperty name="threadid" layout="${threadid}" /> <!-- Can be repeated with more context -->
5151
</target>

LOGGING/src/NLogTarget/ApplicationInsightsTarget.cs

Lines changed: 17 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ namespace Microsoft.ApplicationInsights.NLogTarget
2626
/// The messages will be uploaded to the Application Insights cloud service.
2727
/// </summary>
2828
[Target("ApplicationInsightsTarget")]
29-
public sealed class ApplicationInsightsTarget : TargetWithLayout
29+
public sealed class ApplicationInsightsTarget : TargetWithContext
3030
{
3131
private TelemetryClient telemetryClient;
3232
private DateTime lastLogEventTime;
@@ -39,6 +39,7 @@ public ApplicationInsightsTarget()
3939
{
4040
this.Layout = @"${message}";
4141
this.OptimizeBufferReuse = true;
42+
this.IncludeEventProperties = true;
4243
}
4344

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

53-
/// <summary>
54-
/// Gets the array of custom attributes to be passed into the logevent context.
55-
/// </summary>
56-
[ArrayParameter(typeof(TargetPropertyWithContext), "contextproperty")]
57-
public IList<TargetPropertyWithContext> ContextProperties { get; } = new List<TargetPropertyWithContext>();
58-
5954
/// <summary>
6055
/// Gets the logging controller we will be using.
6156
/// </summary>
@@ -90,20 +85,25 @@ internal void BuildPropertyBag(LogEventInfo logEvent, ITelemetry trace)
9085
propertyBag.Add("UserStackFrame", logEvent.UserStackFrame.ToString());
9186
propertyBag.Add("UserStackFrameNumber", logEvent.UserStackFrameNumber.ToString(CultureInfo.InvariantCulture));
9287
}
93-
94-
for (int i = 0; i < this.ContextProperties.Count; ++i)
88+
else
9589
{
96-
var contextProperty = this.ContextProperties[i];
97-
if (!string.IsNullOrEmpty(contextProperty.Name) && contextProperty.Layout != null)
98-
{
99-
string propertyValue = this.RenderLogEvent(contextProperty.Layout, logEvent);
100-
PopulatePropertyBag(propertyBag, contextProperty.Name, propertyValue);
101-
}
90+
var callsiteClassName = logEvent.CallerClassName;
91+
if (!string.IsNullOrEmpty(callsiteClassName))
92+
propertyBag.Add("UserStackClassName", callsiteClassName);
93+
var callsiteMemberName = logEvent.CallerMemberName;
94+
if (!string.IsNullOrEmpty(callsiteMemberName))
95+
propertyBag.Add("UserStackMemberName", callsiteMemberName);
96+
var callsiteSourceFilePath = logEvent.CallerFilePath;
97+
if (!string.IsNullOrEmpty(callsiteSourceFilePath))
98+
propertyBag.Add("UserStackSourceFile", callsiteSourceFilePath);
99+
var callsiteSourceLineNumber = logEvent.CallerLineNumber;
100+
if (callsiteSourceLineNumber != 0)
101+
propertyBag.Add("UserStackSourceLine", callsiteSourceLineNumber.ToString());
102102
}
103103

104-
if (logEvent.HasProperties)
104+
if (ShouldIncludeProperties(logEvent) || ContextProperties.Count > 0)
105105
{
106-
LoadLogEventProperties(logEvent, propertyBag);
106+
this.GetAllProperties(logEvent, new StringDictionaryConverter(propertyBag));
107107
}
108108
}
109109

@@ -182,40 +182,6 @@ protected override void FlushAsync(AsyncContinuation asyncContinuation)
182182
}
183183
}
184184

185-
private static void LoadLogEventProperties(LogEventInfo logEvent, IDictionary<string, string> propertyBag)
186-
{
187-
if (logEvent.Properties?.Count > 0)
188-
{
189-
foreach (var keyValuePair in logEvent.Properties)
190-
{
191-
string key = keyValuePair.Key.ToString();
192-
object valueObj = keyValuePair.Value;
193-
PopulatePropertyBag(propertyBag, key, valueObj);
194-
}
195-
}
196-
}
197-
198-
private static void PopulatePropertyBag(IDictionary<string, string> propertyBag, string key, object valueObj)
199-
{
200-
if (valueObj == null)
201-
{
202-
return;
203-
}
204-
205-
string value = Convert.ToString(valueObj, CultureInfo.InvariantCulture);
206-
if (propertyBag.ContainsKey(key))
207-
{
208-
if (string.Equals(value, propertyBag[key], StringComparison.Ordinal))
209-
{
210-
return;
211-
}
212-
213-
key += "_1";
214-
}
215-
216-
propertyBag.Add(key, value);
217-
}
218-
219185
private static SeverityLevel? GetSeverityLevel(LogLevel logEventLevel)
220186
{
221187
if (logEventLevel == null)

LOGGING/src/NLogTarget/NLogTarget.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
</PropertyGroup>
1717

1818
<ItemGroup>
19-
<PackageReference Include="NLog" Version="4.5.11" />
19+
<PackageReference Include="NLog" Version="4.7.15" />
2020
<PackageReference Include="Microsoft.Diagnostics.Tracing.EventRegister" Version="1.1.28">
2121
<PrivateAssets>All</PrivateAssets>
2222
</PackageReference>
Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,123 @@
1+
// -----------------------------------------------------------------------
2+
// <copyright file="ApplicationInsightsTarget.cs" company="Microsoft">
3+
// Copyright (c) Microsoft Corporation.
4+
// All rights reserved. 2013
5+
// </copyright>
6+
// -----------------------------------------------------------------------
7+
8+
using System;
9+
using System.Collections;
10+
using System.Collections.Generic;
11+
using System.Linq;
12+
using System.Runtime.InteropServices;
13+
using System.Text;
14+
using System.Threading.Tasks;
15+
16+
namespace Microsoft.ApplicationInsights.NLogTarget
17+
{
18+
/// <summary>
19+
/// Converts from NLog Object-properties to ApplicationInsight String-properties
20+
/// </summary>
21+
class StringDictionaryConverter : IDictionary<string, object>
22+
{
23+
private readonly IDictionary<string, string> _wrapped;
24+
25+
public StringDictionaryConverter(IDictionary<string, string> wrapped)
26+
{
27+
_wrapped = wrapped;
28+
}
29+
30+
public object this[string key] { get => _wrapped[key]; set => _wrapped[key] = SafeValueConverter(value); }
31+
32+
public ICollection<string> Keys => _wrapped.Keys;
33+
34+
public ICollection<object> Values => new List<object>(_wrapped.Values);
35+
36+
public int Count => _wrapped.Count;
37+
38+
public bool IsReadOnly => _wrapped.IsReadOnly;
39+
40+
public void Add(string key, object value)
41+
{
42+
_wrapped.Add(key, SafeValueConverter(value));
43+
}
44+
45+
public void Add(KeyValuePair<string, object> item)
46+
{
47+
_wrapped.Add(new KeyValuePair<string, string>(item.Key, SafeValueConverter(item.Value)));
48+
}
49+
50+
public void Clear()
51+
{
52+
_wrapped.Clear();
53+
}
54+
55+
public bool Contains(KeyValuePair<string, object> item)
56+
{
57+
return _wrapped.Contains(new KeyValuePair<string, string>(item.Key, SafeValueConverter(item.Value)));
58+
}
59+
60+
public bool ContainsKey(string key)
61+
{
62+
return _wrapped.ContainsKey(key);
63+
}
64+
65+
public void CopyTo(KeyValuePair<string, object>[] array, int arrayIndex)
66+
{
67+
foreach (var item in _wrapped)
68+
{
69+
array[arrayIndex++] = new KeyValuePair<string, object>(item.Key, item.Value);
70+
}
71+
}
72+
73+
public IEnumerator<KeyValuePair<string, object>> GetEnumerator()
74+
{
75+
return AsEnumerable().GetEnumerator();
76+
}
77+
78+
public bool Remove(string key)
79+
{
80+
return _wrapped.Remove(key);
81+
}
82+
83+
public bool Remove(KeyValuePair<string, object> item)
84+
{
85+
return _wrapped.Remove(new KeyValuePair<string, string>(item.Key, SafeValueConverter(item.Value)));
86+
}
87+
88+
public bool TryGetValue(string key, out object value)
89+
{
90+
if (_wrapped.TryGetValue(key, out var stringValue))
91+
{
92+
value = stringValue;
93+
return true;
94+
}
95+
96+
value = null;
97+
return false;
98+
}
99+
100+
IEnumerator IEnumerable.GetEnumerator()
101+
{
102+
return ((IEnumerable)_wrapped).GetEnumerator();
103+
}
104+
105+
private IEnumerable<KeyValuePair<string, object>> AsEnumerable()
106+
{
107+
foreach (var item in _wrapped)
108+
yield return new KeyValuePair<string, object>(item.Key, item.Value);
109+
}
110+
111+
private static string SafeValueConverter(object value)
112+
{
113+
try
114+
{
115+
return Convert.ToString(value, System.Globalization.CultureInfo.InvariantCulture);
116+
}
117+
catch
118+
{
119+
return string.Empty;
120+
}
121+
}
122+
}
123+
}

0 commit comments

Comments
 (0)