From 53231918b6fdc34c37cd7752e56a76b300a548d6 Mon Sep 17 00:00:00 2001 From: Lucas Gulbranson Date: Mon, 2 Nov 2015 10:17:42 -0600 Subject: [PATCH] Support logging properties into user defined columns --- ...oggerConfigurationMSSqlServerExtensions.cs | 7 +++- .../Sinks/MSSqlServer/MSSqlServerSink.cs | 37 +++++++++++++++++-- 2 files changed, 38 insertions(+), 6 deletions(-) diff --git a/src/Serilog.Sinks.MSSqlServer/LoggerConfigurationMSSqlServerExtensions.cs b/src/Serilog.Sinks.MSSqlServer/LoggerConfigurationMSSqlServerExtensions.cs index 47eb36d8..ba1e1f9d 100644 --- a/src/Serilog.Sinks.MSSqlServer/LoggerConfigurationMSSqlServerExtensions.cs +++ b/src/Serilog.Sinks.MSSqlServer/LoggerConfigurationMSSqlServerExtensions.cs @@ -1,4 +1,5 @@ using System; +using System.Data; using Serilog.Configuration; using Serilog.Events; using Serilog.Sinks.MSSqlServer; @@ -38,6 +39,7 @@ public static class LoggerConfigurationMSSqlServerExtensions /// The time to wait between checking for event batches. /// Supplies culture-specific formatting information, or null. /// Store Timestamp In UTC + /// Additional columns for data storage. /// Logger configuration, allowing configuration to continue. /// A required parameter is null. public static LoggerConfiguration MSSqlServer( @@ -47,14 +49,15 @@ public static LoggerConfiguration MSSqlServer( int batchPostingLimit = MSSqlServerSink.DefaultBatchPostingLimit, TimeSpan? period = null, IFormatProvider formatProvider = null, - bool storeTimestampInUtc = false) + bool storeTimestampInUtc = false, + DataColumn[] additionalDataColumns = null) { if (loggerConfiguration == null) throw new ArgumentNullException("loggerConfiguration"); var defaultedPeriod = period ?? MSSqlServerSink.DefaultPeriod; return loggerConfiguration.Sink( - new MSSqlServerSink(connectionString, tableName, storeProperties, batchPostingLimit, defaultedPeriod, formatProvider, storeTimestampInUtc), + new MSSqlServerSink(connectionString, tableName, storeProperties, batchPostingLimit, defaultedPeriod, formatProvider, storeTimestampInUtc, additionalDataColumns), restrictedToMinimumLevel); } } diff --git a/src/Serilog.Sinks.MSSqlServer/Sinks/MSSqlServer/MSSqlServerSink.cs b/src/Serilog.Sinks.MSSqlServer/Sinks/MSSqlServer/MSSqlServerSink.cs index 9565b69a..dae62929 100644 --- a/src/Serilog.Sinks.MSSqlServer/Sinks/MSSqlServer/MSSqlServerSink.cs +++ b/src/Serilog.Sinks.MSSqlServer/Sinks/MSSqlServer/MSSqlServerSink.cs @@ -1,4 +1,4 @@ -// Copyright 2013 Serilog Contributors +// Copyright 2013 Serilog Contributors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -49,6 +49,8 @@ public class MSSqlServerSink : PeriodicBatchingSink readonly CancellationTokenSource _token = new CancellationTokenSource(); readonly bool _storeTimestampInUtc; + private DataColumn[] _additionalDataColumns; + /// /// Construct a sink posting to the specified database. /// @@ -59,8 +61,9 @@ public class MSSqlServerSink : PeriodicBatchingSink /// The time to wait between checking for event batches. /// Supplies culture-specific formatting information, or null. /// Store Timestamp In UTC + /// Additional columns for data storage. public MSSqlServerSink(string connectionString, string tableName, bool includeProperties, int batchPostingLimit, - TimeSpan period, IFormatProvider formatProvider, bool storeTimestampInUtc) + TimeSpan period, IFormatProvider formatProvider, bool storeTimestampInUtc, DataColumn[] additionalDataColumns = null ) : base(batchPostingLimit, period) { if (string.IsNullOrWhiteSpace(connectionString)) @@ -75,6 +78,7 @@ public MSSqlServerSink(string connectionString, string tableName, bool includePr _includeProperties = includeProperties; _formatProvider = formatProvider; _storeTimestampInUtc = storeTimestampInUtc; + _additionalDataColumns = additionalDataColumns; // Prepare the data table _eventsTable = CreateDataTable(); @@ -162,6 +166,10 @@ DataTable CreateDataTable() }; eventsTable.Columns.Add(props); + if ( _additionalDataColumns != null ) + { + eventsTable.Columns.AddRange(_additionalDataColumns); + } // Create an array for DataColumn objects. var keys = new DataColumn[1]; @@ -171,7 +179,7 @@ DataTable CreateDataTable() return eventsTable; } - void FillDataTable(IEnumerable events) + void FillDataTable(IEnumerable events) { // Add the new rows to the collection. foreach (var logEvent in events) @@ -188,6 +196,10 @@ void FillDataTable(IEnumerable events) { row["Properties"] = ConvertPropertiesToXmlStructure(logEvent.Properties); } + if ( _additionalDataColumns != null ) + { + ConvertPropertiesToColumn( row, logEvent.Properties ); + } _eventsTable.Rows.Add(row); } @@ -213,6 +225,23 @@ static string ConvertPropertiesToXmlStructure( return sb.ToString(); } + /// + /// Mapping values from properties which have a corresponding data row. + /// Matching is done based on Column name and property key + /// + /// + /// + private void ConvertPropertiesToColumn( + DataRow row, IReadOnlyDictionary properties) + { + foreach (var property in properties) + { + if (row.Table.Columns.Contains(property.Key)) + { + row[property.Key] = property.Value.ToString(); + } + } + } /// /// Disposes the connection @@ -223,7 +252,7 @@ protected override void Dispose(bool disposing) _token.Cancel(); if (_eventsTable != null) - _eventsTable.Dispose(); + _eventsTable.Dispose(); base.Dispose(disposing); }