diff --git a/src/Serilog.Sinks.AzureEventHub/Sinks/AzureEventHub/AzureEventHubBatchingSink.cs b/src/Serilog.Sinks.AzureEventHub/Sinks/AzureEventHub/AzureEventHubBatchingSink.cs index 945ced6..b5c643c 100644 --- a/src/Serilog.Sinks.AzureEventHub/Sinks/AzureEventHub/AzureEventHubBatchingSink.cs +++ b/src/Serilog.Sinks.AzureEventHub/Sinks/AzureEventHub/AzureEventHubBatchingSink.cs @@ -20,6 +20,7 @@ using Microsoft.Azure.EventHubs; using Serilog.Events; using Serilog.Formatting; +using Serilog.Sinks.AzureEventHub.Extensions; using Serilog.Sinks.PeriodicBatching; namespace Serilog.Sinks.AzureEventHub @@ -77,9 +78,10 @@ protected override Task EmitBatchAsync(IEnumerable events) body = Encoding.UTF8.GetBytes(render.ToString()); } var eventHubData = new EventData(body); - + eventHubData.Properties.Add("Type", "SerilogEvent"); eventHubData.Properties.Add("Level", logEvent.Level.ToString()); + eventHubData.AddEventProperties(logEvent.Properties); batchedEvents.Add(eventHubData); } diff --git a/src/Serilog.Sinks.AzureEventHub/Sinks/AzureEventHub/AzureEventHubSink.cs b/src/Serilog.Sinks.AzureEventHub/Sinks/AzureEventHub/AzureEventHubSink.cs index 13d1bd2..8918ec6 100644 --- a/src/Serilog.Sinks.AzureEventHub/Sinks/AzureEventHub/AzureEventHubSink.cs +++ b/src/Serilog.Sinks.AzureEventHub/Sinks/AzureEventHub/AzureEventHubSink.cs @@ -1,11 +1,11 @@ // Copyright 2014 Serilog Contributors -// +// // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// +// +// http://www.apache.org/licenses/LICENSE-2.0 +// // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. @@ -13,12 +13,17 @@ // limitations under the License. using System; +using System.Collections; +using System.Collections.Generic; +using System.Dynamic; using System.IO; +using System.Linq; using System.Text; using Microsoft.Azure.EventHubs; using Serilog.Core; using Serilog.Events; using Serilog.Formatting; +using Serilog.Sinks.AzureEventHub.Extensions; namespace Serilog.Sinks.AzureEventHub { @@ -36,8 +41,8 @@ public class AzureEventHubSink : ILogEventSink /// The EventHubClient to use in this sink. /// Provides formatting for outputting log data public AzureEventHubSink( - EventHubClient eventHubClient, - ITextFormatter formatter) + EventHubClient eventHubClient, + ITextFormatter formatter) { _eventHubClient = eventHubClient; _formatter = formatter; @@ -59,6 +64,8 @@ public void Emit(LogEvent logEvent) eventHubData.Properties.Add("Type", "SerilogEvent"); eventHubData.Properties.Add("Level", logEvent.Level.ToString()); + eventHubData.AddEventProperties(logEvent.Properties); + //Unfortunately no support for async in Serilog yet //https://github.com/serilog/serilog/issues/134 _eventHubClient.SendAsync(eventHubData, Guid.NewGuid().ToString()).GetAwaiter().GetResult(); diff --git a/src/Serilog.Sinks.AzureEventHub/Sinks/Extensions/EventDataExtension.cs b/src/Serilog.Sinks.AzureEventHub/Sinks/Extensions/EventDataExtension.cs new file mode 100644 index 0000000..0b7dd93 --- /dev/null +++ b/src/Serilog.Sinks.AzureEventHub/Sinks/Extensions/EventDataExtension.cs @@ -0,0 +1,107 @@ +using Microsoft.Azure.EventHubs; +using Serilog.Events; +using System; +using System.Collections.Generic; +using System.Dynamic; +using System.Linq; +using System.Text; +using System.Reflection; + +namespace Serilog.Sinks.AzureEventHub.Extensions +{ + /// + /// Extensions for EventData Class + /// + public static class EventDataExtension + { + /// + /// Convert and Map LogEvent Properties to Azure Hub Event Data Properties + /// + /// + /// + public static void AddEventProperties(this EventData logEvent, IReadOnlyDictionary properties) + { + foreach (var item in properties) + { + var value = GetEventPropertyValue(item.Value); + if (value is IDictionary dic) + { + FaltternDictionary(dic, logEvent, item.Key); + } + else + { + logEvent.Properties.Add(item.Key, value); + } + } + + } + + private static void FaltternDictionary(IDictionary nestedDictionary, EventData logEvent, string parentPropertyName) + { + foreach (var item in nestedDictionary) + { + var propertyName = $"{parentPropertyName}.{item.Key}"; + if (item.Value is IDictionary dic) + { + FaltternDictionary(dic, logEvent, propertyName); + } + else + { + logEvent.Properties.Add(propertyName, item.Value); + } + } + } + + private static object GetEventPropertyValue(LogEventPropertyValue data) + { + switch (data) + { + case ScalarValue value: + // Because it can't serialize enums + var isEnum = value.Value?.GetType().GetTypeInfo().IsEnum; + if (isEnum != null && (bool)isEnum) + return value.Value.ToString(); + return value.Value; + case DictionaryValue dictValue: + { + var expObject = new ExpandoObject() as IDictionary; + foreach (var item in dictValue.Elements) + { + if (item.Key.Value is string key) + expObject.Add(key, GetEventPropertyValue(item.Value)); + } + return expObject; + } + + case SequenceValue seq: + var sequenceItems = seq.Elements.Select(GetEventPropertyValue).ToArray(); + return string.Join(", ", sequenceItems); + + case StructureValue str: + try + { + if (str.TypeTag == null) + return str.Properties.ToDictionary(p => p.Name, p => GetEventPropertyValue(p.Value)); + + if (!str.TypeTag.StartsWith("DictionaryEntry") && !str.TypeTag.StartsWith("KeyValuePair")) + return str.Properties.ToDictionary(p => p.Name, p => GetEventPropertyValue(p.Value)); + + var key = GetEventPropertyValue(str.Properties[0].Value); + if (key == null) + return null; + + var expObject = new ExpandoObject() as IDictionary; + expObject.Add(key.ToString(), GetEventPropertyValue(str.Properties[1].Value)); + return expObject; + } + catch (Exception ex) + { + Console.WriteLine(ex.Message); + } + break; + } + + return null; + } + } +} \ No newline at end of file