diff --git a/LOGGING/README.md b/LOGGING/README.md index c022877113..5098cb0cce 100644 --- a/LOGGING/README.md +++ b/LOGGING/README.md @@ -45,7 +45,7 @@ If your application does not have web.config then it can also be configured manu - + Your_Resource_Key diff --git a/LOGGING/src/NLogTarget/ApplicationInsightsTarget.cs b/LOGGING/src/NLogTarget/ApplicationInsightsTarget.cs index 7d5e65d079..2e596cb486 100644 --- a/LOGGING/src/NLogTarget/ApplicationInsightsTarget.cs +++ b/LOGGING/src/NLogTarget/ApplicationInsightsTarget.cs @@ -26,7 +26,7 @@ namespace Microsoft.ApplicationInsights.NLogTarget /// The messages will be uploaded to the Application Insights cloud service. /// [Target("ApplicationInsightsTarget")] - public sealed class ApplicationInsightsTarget : TargetWithLayout + public sealed class ApplicationInsightsTarget : TargetWithContext { private TelemetryClient telemetryClient; private DateTime lastLogEventTime; @@ -39,6 +39,7 @@ public ApplicationInsightsTarget() { this.Layout = @"${message}"; this.OptimizeBufferReuse = true; + this.IncludeEventProperties = true; } /// @@ -50,12 +51,6 @@ public string InstrumentationKey set => this.instrumentationKeyLayout = value ?? string.Empty; } - /// - /// Gets the array of custom attributes to be passed into the logevent context. - /// - [ArrayParameter(typeof(TargetPropertyWithContext), "contextproperty")] - public IList ContextProperties { get; } = new List(); - /// /// Gets the logging controller we will be using. /// @@ -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)); } } @@ -182,40 +182,6 @@ protected override void FlushAsync(AsyncContinuation asyncContinuation) } } - private static void LoadLogEventProperties(LogEventInfo logEvent, IDictionary 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 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) diff --git a/LOGGING/src/NLogTarget/NLogTarget.csproj b/LOGGING/src/NLogTarget/NLogTarget.csproj index e6c2e9ddae..065ea1d2ba 100644 --- a/LOGGING/src/NLogTarget/NLogTarget.csproj +++ b/LOGGING/src/NLogTarget/NLogTarget.csproj @@ -16,7 +16,7 @@ - + All diff --git a/LOGGING/src/NLogTarget/StringDictionaryConverter.cs b/LOGGING/src/NLogTarget/StringDictionaryConverter.cs new file mode 100644 index 0000000000..941a19b552 --- /dev/null +++ b/LOGGING/src/NLogTarget/StringDictionaryConverter.cs @@ -0,0 +1,123 @@ +// ----------------------------------------------------------------------- +// +// Copyright (c) Microsoft Corporation. +// All rights reserved. 2013 +// +// ----------------------------------------------------------------------- + +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 +{ + /// + /// Converts from NLog Object-properties to ApplicationInsight String-properties + /// + class StringDictionaryConverter : IDictionary + { + private readonly IDictionary _wrapped; + + public StringDictionaryConverter(IDictionary wrapped) + { + _wrapped = wrapped; + } + + public object this[string key] { get => _wrapped[key]; set => _wrapped[key] = SafeValueConverter(value); } + + public ICollection Keys => _wrapped.Keys; + + public ICollection Values => new List(_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 item) + { + _wrapped.Add(new KeyValuePair(item.Key, SafeValueConverter(item.Value))); + } + + public void Clear() + { + _wrapped.Clear(); + } + + public bool Contains(KeyValuePair item) + { + return _wrapped.Contains(new KeyValuePair(item.Key, SafeValueConverter(item.Value))); + } + + public bool ContainsKey(string key) + { + return _wrapped.ContainsKey(key); + } + + public void CopyTo(KeyValuePair[] array, int arrayIndex) + { + foreach (var item in _wrapped) + { + array[arrayIndex++] = new KeyValuePair(item.Key, item.Value); + } + } + + public IEnumerator> GetEnumerator() + { + return AsEnumerable().GetEnumerator(); + } + + public bool Remove(string key) + { + return _wrapped.Remove(key); + } + + public bool Remove(KeyValuePair item) + { + return _wrapped.Remove(new KeyValuePair(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> AsEnumerable() + { + foreach (var item in _wrapped) + yield return new KeyValuePair(item.Key, item.Value); + } + + private static string SafeValueConverter(object value) + { + try + { + return Convert.ToString(value, System.Globalization.CultureInfo.InvariantCulture); + } + catch + { + return string.Empty; + } + } + } +}