From 734392e42597749aa8dee550f3ea53e4afa82451 Mon Sep 17 00:00:00 2001 From: EEParker Date: Fri, 15 Mar 2024 23:06:32 -0500 Subject: [PATCH 1/9] Fixes #161 add subsecond decimal configuration --- .../Splunk/CompactSplunkJsonFormatter.cs | 9 +++++++-- .../Sinks/Splunk/Epoch.cs | 4 ++-- .../Sinks/Splunk/EventCollectorSink.cs | 20 ++++++++++++------- .../Sinks/Splunk/SplunkJsonFormatter.cs | 14 +++++++++---- .../SplunkLoggingConfigurationExtensions.cs | 18 ++++++++++++----- 5 files changed, 45 insertions(+), 20 deletions(-) diff --git a/src/Serilog.Sinks.Splunk/Sinks/Splunk/CompactSplunkJsonFormatter.cs b/src/Serilog.Sinks.Splunk/Sinks/Splunk/CompactSplunkJsonFormatter.cs index 79132fa..f0472e3 100644 --- a/src/Serilog.Sinks.Splunk/Sinks/Splunk/CompactSplunkJsonFormatter.cs +++ b/src/Serilog.Sinks.Splunk/Sinks/Splunk/CompactSplunkJsonFormatter.cs @@ -31,6 +31,7 @@ public class CompactSplunkJsonFormatter : ITextFormatter private static readonly JsonValueFormatter ValueFormatter = new JsonValueFormatter(typeTagName: "$type"); private readonly string _suffix; private readonly bool _renderTemplate; + private readonly int _subSecondDecimals; /// /// Construct a . @@ -40,9 +41,13 @@ public class CompactSplunkJsonFormatter : ITextFormatter /// The host of the event /// The Splunk index to log to /// If true, the template used will be rendered and written to the output as a property named MessageTemplate - public CompactSplunkJsonFormatter(bool renderTemplate = false, string source = null, string sourceType = null, string host = null, string index = null) + /// Timestamp sub-second precision + + public CompactSplunkJsonFormatter(bool renderTemplate = false, string source = null, string sourceType = null, string host = null, string index = null, int subSecondDecimals = 3) { _renderTemplate = renderTemplate; + _subSecondDecimals = subSecondDecimals; + var suffixWriter = new StringWriter(); suffixWriter.Write("}"); // Terminates "event" @@ -80,7 +85,7 @@ public void Format(LogEvent logEvent, TextWriter output) if (output == null) throw new ArgumentNullException(nameof(output)); output.Write("{\"time\":\""); - output.Write(logEvent.Timestamp.ToEpoch().ToString(CultureInfo.InvariantCulture)); + output.Write(logEvent.Timestamp.ToEpoch(_subSecondDecimals).ToString(CultureInfo.InvariantCulture)); output.Write("\",\"event\":{\"@l\":\""); output.Write(logEvent.Level); output.Write('"'); diff --git a/src/Serilog.Sinks.Splunk/Sinks/Splunk/Epoch.cs b/src/Serilog.Sinks.Splunk/Sinks/Splunk/Epoch.cs index 0f4c39b..fe662af 100644 --- a/src/Serilog.Sinks.Splunk/Sinks/Splunk/Epoch.cs +++ b/src/Serilog.Sinks.Splunk/Sinks/Splunk/Epoch.cs @@ -20,7 +20,7 @@ internal static class EpochExtensions { private static DateTimeOffset Epoch = new DateTimeOffset(1970,1,1,0,0,0,TimeSpan.Zero); - public static double ToEpoch(this DateTimeOffset value) + public static double ToEpoch(this DateTimeOffset value, int subSecondDecimals = 3) { // From Splunk HTTP Collector Protocol // The default time format is epoch time format, in the format .. @@ -28,7 +28,7 @@ public static double ToEpoch(this DateTimeOffset value) // or Monday, June 1, 2015, at 7:50:55 PM GMT. // See: http://dev.splunk.com/view/SP-CAAAE6P - return Math.Round((value - Epoch).TotalSeconds, 3, MidpointRounding.AwayFromZero); + return Math.Round((value - Epoch).TotalSeconds, subSecondDecimals, MidpointRounding.AwayFromZero); } } } diff --git a/src/Serilog.Sinks.Splunk/Sinks/Splunk/EventCollectorSink.cs b/src/Serilog.Sinks.Splunk/Sinks/Splunk/EventCollectorSink.cs index 1754dd2..a7fb4fc 100644 --- a/src/Serilog.Sinks.Splunk/Sinks/Splunk/EventCollectorSink.cs +++ b/src/Serilog.Sinks.Splunk/Sinks/Splunk/EventCollectorSink.cs @@ -56,17 +56,20 @@ public class EventCollectorSink : IBatchedLogEventSink /// The token to use when authenticating with the event collector /// The format provider used when rendering the message /// Whether to render the message template + /// Timestamp sub-second precision public EventCollectorSink( string splunkHost, string eventCollectorToken, IFormatProvider formatProvider = null, - bool renderTemplate = true) + bool renderTemplate = true, + int subSecondDecimals = 3) : this( splunkHost, eventCollectorToken, null, null, null, null, null, formatProvider, - renderTemplate) + renderTemplate, + subSecondDecimals: subSecondDecimals) { } @@ -83,6 +86,7 @@ public class EventCollectorSink : IBatchedLogEventSink /// The source type of the event /// The host of the event /// The handler used to send HTTP requests + /// Timestamp sub-second precision public EventCollectorSink( string splunkHost, string eventCollectorToken, @@ -93,13 +97,13 @@ public class EventCollectorSink : IBatchedLogEventSink string index, IFormatProvider formatProvider = null, bool renderTemplate = true, - HttpMessageHandler messageHandler = null) + HttpMessageHandler messageHandler = null, + int subSecondDecimals = 3) : this( splunkHost, eventCollectorToken, uriPath, - - new SplunkJsonFormatter(renderTemplate, formatProvider, source, sourceType, host, index), + new SplunkJsonFormatter(renderTemplate, formatProvider, source, sourceType, host, index, subSecondDecimals: subSecondDecimals), messageHandler) { } @@ -118,6 +122,7 @@ public class EventCollectorSink : IBatchedLogEventSink /// The source type of the event /// The host of the event /// The handler used to send HTTP requests + /// Timestamp sub-second precision public EventCollectorSink( string splunkHost, string eventCollectorToken, @@ -129,13 +134,14 @@ public class EventCollectorSink : IBatchedLogEventSink CustomFields fields, IFormatProvider formatProvider = null, bool renderTemplate = true, - HttpMessageHandler messageHandler = null) + HttpMessageHandler messageHandler = null, + int subSecondDecimals = 3) // TODO here is the jsonformatter creation. We must make way to test output of jsonformatter. : this( splunkHost, eventCollectorToken, uriPath, - new SplunkJsonFormatter(renderTemplate, formatProvider, source, sourceType, host, index, fields), + new SplunkJsonFormatter(renderTemplate, formatProvider, source, sourceType, host, index, fields, subSecondDecimals: subSecondDecimals), messageHandler) { } diff --git a/src/Serilog.Sinks.Splunk/Sinks/Splunk/SplunkJsonFormatter.cs b/src/Serilog.Sinks.Splunk/Sinks/Splunk/SplunkJsonFormatter.cs index 17ee054..06191e8 100644 --- a/src/Serilog.Sinks.Splunk/Sinks/Splunk/SplunkJsonFormatter.cs +++ b/src/Serilog.Sinks.Splunk/Sinks/Splunk/SplunkJsonFormatter.cs @@ -32,6 +32,7 @@ public class SplunkJsonFormatter : ITextFormatter private readonly bool _renderTemplate; private readonly IFormatProvider _formatProvider; + private readonly int _subSecondDecimals; private readonly string _suffix; /// @@ -56,14 +57,16 @@ public class SplunkJsonFormatter : ITextFormatter /// The source of the event /// The source type of the event /// The host of the event + /// Timestamp sub-second precision public SplunkJsonFormatter( bool renderTemplate, IFormatProvider formatProvider, string source, string sourceType, string host, - string index) - : this(renderTemplate, formatProvider, source, sourceType, host, index, null) + string index, + int subSecondDecimals = 3) + : this(renderTemplate, formatProvider, source, sourceType, host, index, null, subSecondDecimals) { } @@ -77,6 +80,7 @@ public class SplunkJsonFormatter : ITextFormatter /// The source type of the event /// The host of the event /// Object that describes extra splunk fields that should be indexed with event see: http://dev.splunk.com/view/event-collector/SP-CAAAFB6 + /// Timestamp sub-second precision public SplunkJsonFormatter( bool renderTemplate, IFormatProvider formatProvider, @@ -84,10 +88,12 @@ public class SplunkJsonFormatter : ITextFormatter string sourceType, string host, string index, - CustomFields customFields) + CustomFields customFields, + int subSecondDecimals = 3) { _renderTemplate = renderTemplate; _formatProvider = formatProvider; + _subSecondDecimals = subSecondDecimals; using (var suffixWriter = new StringWriter()) { @@ -157,7 +163,7 @@ public void Format(LogEvent logEvent, TextWriter output) if (output == null) throw new ArgumentNullException(nameof(output)); output.Write("{\"time\":\""); - output.Write(logEvent.Timestamp.ToEpoch().ToString(CultureInfo.InvariantCulture)); + output.Write(logEvent.Timestamp.ToEpoch(_subSecondDecimals).ToString(CultureInfo.InvariantCulture)); output.Write("\",\"event\":{\"Level\":\""); output.Write(logEvent.Level); output.Write('"'); diff --git a/src/Serilog.Sinks.Splunk/SplunkLoggingConfigurationExtensions.cs b/src/Serilog.Sinks.Splunk/SplunkLoggingConfigurationExtensions.cs index 05165f6..5face81 100644 --- a/src/Serilog.Sinks.Splunk/SplunkLoggingConfigurationExtensions.cs +++ b/src/Serilog.Sinks.Splunk/SplunkLoggingConfigurationExtensions.cs @@ -17,7 +17,6 @@ using Serilog.Core; using Serilog.Events; using Serilog.Formatting; -using Serilog.Formatting.Json; using Serilog.Sinks.PeriodicBatching; using Serilog.Sinks.Splunk; using System; @@ -49,6 +48,7 @@ public static class SplunkLoggingConfigurationExtensions /// Maximum number of events in the queue /// The handler used to send HTTP requests /// A switch allowing the pass-through minimum level to be changed at runtime. + /// Timestamp sub-second precision /// public static LoggerConfiguration EventCollector( this LoggerSinkConfiguration configuration, @@ -66,7 +66,8 @@ public static class SplunkLoggingConfigurationExtensions int batchSizeLimit = 100, int? queueLimit = null, HttpMessageHandler messageHandler = null, - LoggingLevelSwitch levelSwitch = null) + LoggingLevelSwitch levelSwitch = null, + int subSecondDecimals = 3) { if (configuration == null) throw new ArgumentNullException(nameof(configuration)); @@ -88,7 +89,9 @@ public static class SplunkLoggingConfigurationExtensions index, formatProvider, renderTemplate, - messageHandler); + messageHandler, + subSecondDecimals: subSecondDecimals); + var batchingSink = new PeriodicBatchingSink(eventCollectorSink, batchingOptions); return configuration.Sink(batchingSink, restrictedToMinimumLevel, levelSwitch); @@ -168,6 +171,7 @@ public static class SplunkLoggingConfigurationExtensions /// The handler used to send HTTP requests /// A switch allowing the pass-through minimum level to be changed at runtime. /// Customfields that will be indexed in splunk with this event + /// Timestamp sub-second precision /// public static LoggerConfiguration EventCollector( this LoggerSinkConfiguration configuration, @@ -186,7 +190,8 @@ public static class SplunkLoggingConfigurationExtensions int batchSizeLimit = 100, int? queueLimit = null, HttpMessageHandler messageHandler = null, - LoggingLevelSwitch levelSwitch = null) + LoggingLevelSwitch levelSwitch = null, + int subSecondDecimals = 3) { if (configuration == null) throw new ArgumentNullException(nameof(configuration)); @@ -209,7 +214,10 @@ public static class SplunkLoggingConfigurationExtensions fields, formatProvider, renderTemplate, - messageHandler); + messageHandler, + subSecondDecimals: subSecondDecimals + ); + var batchingSink = new PeriodicBatchingSink(eventCollectorSink, batchingOptions); From 72d2528a6b846b507859df264c5d8b84a5324fd4 Mon Sep 17 00:00:00 2001 From: EEParker Date: Fri, 15 Mar 2024 23:23:29 -0500 Subject: [PATCH 2/9] Fixes #167 Add a flag for RenderedMessage --- .../Sinks/Splunk/EventCollectorSink.cs | 19 +++++++---- .../Sinks/Splunk/SplunkJsonFormatter.cs | 19 ++++++++--- .../SplunkLoggingConfigurationExtensions.cs | 8 +++-- .../SplunkJsonFormatterTests.cs | 32 +++++++++---------- 4 files changed, 50 insertions(+), 28 deletions(-) diff --git a/src/Serilog.Sinks.Splunk/Sinks/Splunk/EventCollectorSink.cs b/src/Serilog.Sinks.Splunk/Sinks/Splunk/EventCollectorSink.cs index a7fb4fc..5d6b2e4 100644 --- a/src/Serilog.Sinks.Splunk/Sinks/Splunk/EventCollectorSink.cs +++ b/src/Serilog.Sinks.Splunk/Sinks/Splunk/EventCollectorSink.cs @@ -12,6 +12,10 @@ // See the License for the specific language governing permissions and // limitations under the License. +using Serilog.Debugging; +using Serilog.Events; +using Serilog.Formatting; +using Serilog.Sinks.PeriodicBatching; using System; using System.Collections.Generic; using System.IO; @@ -19,10 +23,6 @@ using System.Net; using System.Net.Http; using System.Threading.Tasks; -using Serilog.Debugging; -using Serilog.Events; -using Serilog.Formatting; -using Serilog.Sinks.PeriodicBatching; namespace Serilog.Sinks.Splunk { @@ -56,12 +56,14 @@ public class EventCollectorSink : IBatchedLogEventSink /// The token to use when authenticating with the event collector /// The format provider used when rendering the message /// Whether to render the message template + /// Include "RenderedMessage" parameter from output JSON message. /// Timestamp sub-second precision public EventCollectorSink( string splunkHost, string eventCollectorToken, IFormatProvider formatProvider = null, bool renderTemplate = true, + bool renderMessage = true, int subSecondDecimals = 3) : this( splunkHost, @@ -69,6 +71,7 @@ public class EventCollectorSink : IBatchedLogEventSink null, null, null, null, null, formatProvider, renderTemplate, + renderMessage, subSecondDecimals: subSecondDecimals) { } @@ -86,6 +89,7 @@ public class EventCollectorSink : IBatchedLogEventSink /// The source type of the event /// The host of the event /// The handler used to send HTTP requests + /// Include "RenderedMessage" parameter from output JSON message. /// Timestamp sub-second precision public EventCollectorSink( string splunkHost, @@ -97,13 +101,14 @@ public class EventCollectorSink : IBatchedLogEventSink string index, IFormatProvider formatProvider = null, bool renderTemplate = true, + bool renderMessage = true, HttpMessageHandler messageHandler = null, int subSecondDecimals = 3) : this( splunkHost, eventCollectorToken, uriPath, - new SplunkJsonFormatter(renderTemplate, formatProvider, source, sourceType, host, index, subSecondDecimals: subSecondDecimals), + new SplunkJsonFormatter(renderTemplate, renderMessage, formatProvider, source, sourceType, host, index, subSecondDecimals: subSecondDecimals), messageHandler) { } @@ -122,6 +127,7 @@ public class EventCollectorSink : IBatchedLogEventSink /// The source type of the event /// The host of the event /// The handler used to send HTTP requests + /// Include "RenderedMessage" parameter from output JSON message. /// Timestamp sub-second precision public EventCollectorSink( string splunkHost, @@ -134,6 +140,7 @@ public class EventCollectorSink : IBatchedLogEventSink CustomFields fields, IFormatProvider formatProvider = null, bool renderTemplate = true, + bool renderMessage = true, HttpMessageHandler messageHandler = null, int subSecondDecimals = 3) // TODO here is the jsonformatter creation. We must make way to test output of jsonformatter. @@ -141,7 +148,7 @@ public class EventCollectorSink : IBatchedLogEventSink splunkHost, eventCollectorToken, uriPath, - new SplunkJsonFormatter(renderTemplate, formatProvider, source, sourceType, host, index, fields, subSecondDecimals: subSecondDecimals), + new SplunkJsonFormatter(renderTemplate, renderMessage, formatProvider, source, sourceType, host, index, fields, subSecondDecimals: subSecondDecimals), messageHandler) { } diff --git a/src/Serilog.Sinks.Splunk/Sinks/Splunk/SplunkJsonFormatter.cs b/src/Serilog.Sinks.Splunk/Sinks/Splunk/SplunkJsonFormatter.cs index 06191e8..cf66b72 100644 --- a/src/Serilog.Sinks.Splunk/Sinks/Splunk/SplunkJsonFormatter.cs +++ b/src/Serilog.Sinks.Splunk/Sinks/Splunk/SplunkJsonFormatter.cs @@ -33,6 +33,7 @@ public class SplunkJsonFormatter : ITextFormatter private readonly bool _renderTemplate; private readonly IFormatProvider _formatProvider; private readonly int _subSecondDecimals; + private readonly bool _renderMessage; private readonly string _suffix; /// @@ -41,10 +42,12 @@ public class SplunkJsonFormatter : ITextFormatter /// /// Supplies culture-specific formatting information, or null. /// If true, the template used will be rendered and written to the output as a property named MessageTemplate + /// Removes the "RenderedMessage" parameter from output JSON message. public SplunkJsonFormatter( bool renderTemplate, + bool renderMessage, IFormatProvider formatProvider) - : this(renderTemplate, formatProvider, null, null, null, null) + : this(renderTemplate, renderMessage, formatProvider, null, null, null, null) { } @@ -58,15 +61,17 @@ public class SplunkJsonFormatter : ITextFormatter /// The source type of the event /// The host of the event /// Timestamp sub-second precision + /// Removes the "RenderedMessage" parameter from output JSON message. public SplunkJsonFormatter( bool renderTemplate, + bool renderMessage, IFormatProvider formatProvider, string source, string sourceType, string host, string index, int subSecondDecimals = 3) - : this(renderTemplate, formatProvider, source, sourceType, host, index, null, subSecondDecimals) + : this(renderTemplate, renderMessage, formatProvider, source, sourceType, host, index, null, subSecondDecimals: subSecondDecimals) { } @@ -81,8 +86,10 @@ public class SplunkJsonFormatter : ITextFormatter /// The host of the event /// Object that describes extra splunk fields that should be indexed with event see: http://dev.splunk.com/view/event-collector/SP-CAAAFB6 /// Timestamp sub-second precision + /// Include "RenderedMessage" parameter from output JSON message. public SplunkJsonFormatter( bool renderTemplate, + bool renderMessage, IFormatProvider formatProvider, string source, string sourceType, @@ -94,6 +101,7 @@ public class SplunkJsonFormatter : ITextFormatter _renderTemplate = renderTemplate; _formatProvider = formatProvider; _subSecondDecimals = subSecondDecimals; + _renderMessage = renderMessage; using (var suffixWriter = new StringWriter()) { @@ -174,8 +182,11 @@ public void Format(LogEvent logEvent, TextWriter output) JsonValueFormatter.WriteQuotedJsonString(logEvent.MessageTemplate.Text, output); } - output.Write(",\"RenderedMessage\":"); - JsonValueFormatter.WriteQuotedJsonString(logEvent.RenderMessage(_formatProvider), output); + if (!_renderMessage) + { + output.Write(",\"RenderedMessage\":"); + JsonValueFormatter.WriteQuotedJsonString(logEvent.RenderMessage(_formatProvider), output); + } if (logEvent.Exception != null) { diff --git a/src/Serilog.Sinks.Splunk/SplunkLoggingConfigurationExtensions.cs b/src/Serilog.Sinks.Splunk/SplunkLoggingConfigurationExtensions.cs index 5face81..d6c3367 100644 --- a/src/Serilog.Sinks.Splunk/SplunkLoggingConfigurationExtensions.cs +++ b/src/Serilog.Sinks.Splunk/SplunkLoggingConfigurationExtensions.cs @@ -43,6 +43,7 @@ public static class SplunkLoggingConfigurationExtensions /// The minimum log event level required in order to write an event to the sink. /// Supplies culture-specific formatting information, or null. /// If true, the message template will be rendered + /// Include "RenderedMessage" parameter from output JSON message. /// The interval in seconds that the queue should be instpected for batching /// The size of the batch /// Maximum number of events in the queue @@ -62,6 +63,7 @@ public static class SplunkLoggingConfigurationExtensions LogEventLevel restrictedToMinimumLevel = LevelAlias.Minimum, IFormatProvider formatProvider = null, bool renderTemplate = true, + bool renderMessage = true, int batchIntervalInSeconds = 2, int batchSizeLimit = 100, int? queueLimit = null, @@ -89,7 +91,7 @@ public static class SplunkLoggingConfigurationExtensions index, formatProvider, renderTemplate, - messageHandler, + renderMessage, subSecondDecimals: subSecondDecimals); var batchingSink = new PeriodicBatchingSink(eventCollectorSink, batchingOptions); @@ -106,7 +108,6 @@ public static class SplunkLoggingConfigurationExtensions /// The text formatter used to render log events into a JSON format for consumption by Splunk /// Change the default endpoint of the Event Collector e.g. services/collector/event /// The minimum log event level required in order to write an event to the sink. - /// The interval in seconds that the queue should be instpected for batching /// The size of the batch /// Maximum number of events in the queue @@ -171,6 +172,7 @@ public static class SplunkLoggingConfigurationExtensions /// The handler used to send HTTP requests /// A switch allowing the pass-through minimum level to be changed at runtime. /// Customfields that will be indexed in splunk with this event + /// Include "RenderedMessage" parameter from output JSON message. /// Timestamp sub-second precision /// public static LoggerConfiguration EventCollector( @@ -186,6 +188,7 @@ public static class SplunkLoggingConfigurationExtensions LogEventLevel restrictedToMinimumLevel = LevelAlias.Minimum, IFormatProvider formatProvider = null, bool renderTemplate = true, + bool renderMessage = true, int batchIntervalInSeconds = 2, int batchSizeLimit = 100, int? queueLimit = null, @@ -214,6 +217,7 @@ public static class SplunkLoggingConfigurationExtensions fields, formatProvider, renderTemplate, + renderMessage, messageHandler, subSecondDecimals: subSecondDecimals ); diff --git a/test/Serilog.Sinks.Splunk.Tests/SplunkJsonFormatterTests.cs b/test/Serilog.Sinks.Splunk.Tests/SplunkJsonFormatterTests.cs index 2310062..8ae5327 100644 --- a/test/Serilog.Sinks.Splunk.Tests/SplunkJsonFormatterTests.cs +++ b/test/Serilog.Sinks.Splunk.Tests/SplunkJsonFormatterTests.cs @@ -1,28 +1,28 @@ -using System; -using System.Collections.Generic; -using System.IO; +using Newtonsoft.Json; using Newtonsoft.Json.Linq; -using Xunit; -using Serilog.Sinks.Splunk.Tests.Support; using Serilog.Events; using Serilog.Parsing; +using Serilog.Sinks.Splunk.Tests.Support; +using System; +using System.Collections.Generic; using System.Globalization; -using Newtonsoft.Json; +using System.IO; +using Xunit; namespace Serilog.Sinks.Splunk.Tests { public class SplunkJsonFormatterTests { - void AssertValidJson(Action act, + void AssertValidJson(Action act, string source = "", - string sourceType= "", - string host= "", - string index= "", - CustomFields fields=null) + string sourceType = "", + string host = "", + string index = "", + CustomFields fields = null) { StringWriter outputRendered = new StringWriter(), output = new StringWriter(); var log = new LoggerConfiguration() - .WriteTo.Sink(new TextWriterSink(output, new SplunkJsonFormatter(false, null, source, sourceType, host, index))) - .WriteTo.Sink(new TextWriterSink(outputRendered, new SplunkJsonFormatter(true, null, source, sourceType, host, index))) + .WriteTo.Sink(new TextWriterSink(output, new SplunkJsonFormatter(false, true, null, source, sourceType, host, index))) + .WriteTo.Sink(new TextWriterSink(outputRendered, new SplunkJsonFormatter(true, true, null, source, sourceType, host, index))) .CreateLogger(); act(log); @@ -67,7 +67,7 @@ public void AMinimalEventWithSourceIsValidJson() { AssertValidJson(log => log.Information("One {Property}", 42), source: "A Test Source"); } - + [Fact] public void AMinimalEventWithSourceTypeIsValidJson() { @@ -122,7 +122,7 @@ public void Test_CustomFields_Jsonformatter_for_Splunk_Sink() var timeStamp = DateTimeOffset.Now; // var timeStampUnix = (Math.Round(timeStamp.ToUnixTimeMilliseconds() / 1000.0, 3)).ToString("##.###", CultureInfo.InvariantCulture); //req dotnet 4.6.2 var timeStampUnix = ((Int32)(DateTime.UtcNow.Subtract(new DateTime(1970, 1, 1))).TotalSeconds).ToString("##.###", CultureInfo.InvariantCulture); - var sut = new SplunkJsonFormatter(renderTemplate: true, formatProvider: null, source: "BackPackTestServerChannel", sourceType: "_json", host: "Wanda", index: "Main", customFields: metaData); + var sut = new SplunkJsonFormatter(renderTemplate: true, renderMessage: true, formatProvider: null, source: "BackPackTestServerChannel", sourceType: "_json", host: "Wanda", index: "Main", customFields: metaData); try { var willnotwork = a / b; @@ -189,6 +189,6 @@ public class TestEventResultObject public TestCustomFields Fields { get; set; } } - #endregion + #endregion } } \ No newline at end of file From ac79391e5a8879abbb6244fd3ae5024ced5ba051 Mon Sep 17 00:00:00 2001 From: EEParker Date: Sat, 16 Mar 2024 16:54:14 -0500 Subject: [PATCH 3/9] Update SubSecondPrecision to use an enum. Based on 3,6,9 decimals, see https://docs.splunk.com/Documentation/Splunk/9.2.0/SearchReference/Commontimeformatvariables --- .../Splunk/CompactSplunkJsonFormatter.cs | 18 ++++++--- .../Sinks/Splunk/Epoch.cs | 6 +-- .../Sinks/Splunk/EventCollectorSink.cs | 18 ++++----- .../Sinks/Splunk/SplunkJsonFormatter.cs | 16 ++++---- .../Sinks/Splunk/SubSecondPrecision.cs | 37 +++++++++++++++++++ .../SplunkLoggingConfigurationExtensions.cs | 12 +++--- 6 files changed, 75 insertions(+), 32 deletions(-) create mode 100644 src/Serilog.Sinks.Splunk/Sinks/Splunk/SubSecondPrecision.cs diff --git a/src/Serilog.Sinks.Splunk/Sinks/Splunk/CompactSplunkJsonFormatter.cs b/src/Serilog.Sinks.Splunk/Sinks/Splunk/CompactSplunkJsonFormatter.cs index f0472e3..f1dc5e3 100644 --- a/src/Serilog.Sinks.Splunk/Sinks/Splunk/CompactSplunkJsonFormatter.cs +++ b/src/Serilog.Sinks.Splunk/Sinks/Splunk/CompactSplunkJsonFormatter.cs @@ -31,7 +31,7 @@ public class CompactSplunkJsonFormatter : ITextFormatter private static readonly JsonValueFormatter ValueFormatter = new JsonValueFormatter(typeTagName: "$type"); private readonly string _suffix; private readonly bool _renderTemplate; - private readonly int _subSecondDecimals; + private readonly SubSecondPrecision _subSecondPrecision; /// /// Construct a . @@ -41,12 +41,18 @@ public class CompactSplunkJsonFormatter : ITextFormatter /// The host of the event /// The Splunk index to log to /// If true, the template used will be rendered and written to the output as a property named MessageTemplate - /// Timestamp sub-second precision - - public CompactSplunkJsonFormatter(bool renderTemplate = false, string source = null, string sourceType = null, string host = null, string index = null, int subSecondDecimals = 3) + /// Timestamp sub-second precision + + public CompactSplunkJsonFormatter( + bool renderTemplate = false, + string source = null, + string sourceType = null, + string host = null, + string index = null, + SubSecondPrecision subSecondPrecision = SubSecondPrecision.Milliseconds) { _renderTemplate = renderTemplate; - _subSecondDecimals = subSecondDecimals; + _subSecondPrecision = subSecondPrecision; var suffixWriter = new StringWriter(); suffixWriter.Write("}"); // Terminates "event" @@ -85,7 +91,7 @@ public void Format(LogEvent logEvent, TextWriter output) if (output == null) throw new ArgumentNullException(nameof(output)); output.Write("{\"time\":\""); - output.Write(logEvent.Timestamp.ToEpoch(_subSecondDecimals).ToString(CultureInfo.InvariantCulture)); + output.Write(logEvent.Timestamp.ToEpoch(_subSecondPrecision).ToString(CultureInfo.InvariantCulture)); output.Write("\",\"event\":{\"@l\":\""); output.Write(logEvent.Level); output.Write('"'); diff --git a/src/Serilog.Sinks.Splunk/Sinks/Splunk/Epoch.cs b/src/Serilog.Sinks.Splunk/Sinks/Splunk/Epoch.cs index fe662af..2ec7e18 100644 --- a/src/Serilog.Sinks.Splunk/Sinks/Splunk/Epoch.cs +++ b/src/Serilog.Sinks.Splunk/Sinks/Splunk/Epoch.cs @@ -18,9 +18,9 @@ namespace Serilog.Sinks.Splunk internal static class EpochExtensions { - private static DateTimeOffset Epoch = new DateTimeOffset(1970,1,1,0,0,0,TimeSpan.Zero); + private static DateTimeOffset Epoch = new DateTimeOffset(1970, 1, 1, 0, 0, 0, TimeSpan.Zero); - public static double ToEpoch(this DateTimeOffset value, int subSecondDecimals = 3) + public static double ToEpoch(this DateTimeOffset value, SubSecondPrecision subSecondPrecision = SubSecondPrecision.Milliseconds) { // From Splunk HTTP Collector Protocol // The default time format is epoch time format, in the format .. @@ -28,7 +28,7 @@ public static double ToEpoch(this DateTimeOffset value, int subSecondDecimals = // or Monday, June 1, 2015, at 7:50:55 PM GMT. // See: http://dev.splunk.com/view/SP-CAAAE6P - return Math.Round((value - Epoch).TotalSeconds, subSecondDecimals, MidpointRounding.AwayFromZero); + return Math.Round((value - Epoch).TotalSeconds, (int)subSecondPrecision, MidpointRounding.AwayFromZero); } } } diff --git a/src/Serilog.Sinks.Splunk/Sinks/Splunk/EventCollectorSink.cs b/src/Serilog.Sinks.Splunk/Sinks/Splunk/EventCollectorSink.cs index 5d6b2e4..f6162b5 100644 --- a/src/Serilog.Sinks.Splunk/Sinks/Splunk/EventCollectorSink.cs +++ b/src/Serilog.Sinks.Splunk/Sinks/Splunk/EventCollectorSink.cs @@ -57,14 +57,14 @@ public class EventCollectorSink : IBatchedLogEventSink /// The format provider used when rendering the message /// Whether to render the message template /// Include "RenderedMessage" parameter from output JSON message. - /// Timestamp sub-second precision + /// Timestamp sub-second precision public EventCollectorSink( string splunkHost, string eventCollectorToken, IFormatProvider formatProvider = null, bool renderTemplate = true, bool renderMessage = true, - int subSecondDecimals = 3) + SubSecondPrecision subSecondPrecision = SubSecondPrecision.Milliseconds) : this( splunkHost, eventCollectorToken, @@ -72,7 +72,7 @@ public class EventCollectorSink : IBatchedLogEventSink formatProvider, renderTemplate, renderMessage, - subSecondDecimals: subSecondDecimals) + subSecondPrecision: subSecondPrecision) { } @@ -90,7 +90,7 @@ public class EventCollectorSink : IBatchedLogEventSink /// The host of the event /// The handler used to send HTTP requests /// Include "RenderedMessage" parameter from output JSON message. - /// Timestamp sub-second precision + /// Timestamp sub-second precision public EventCollectorSink( string splunkHost, string eventCollectorToken, @@ -103,12 +103,12 @@ public class EventCollectorSink : IBatchedLogEventSink bool renderTemplate = true, bool renderMessage = true, HttpMessageHandler messageHandler = null, - int subSecondDecimals = 3) + SubSecondPrecision subSecondPrecision = SubSecondPrecision.Milliseconds) : this( splunkHost, eventCollectorToken, uriPath, - new SplunkJsonFormatter(renderTemplate, renderMessage, formatProvider, source, sourceType, host, index, subSecondDecimals: subSecondDecimals), + new SplunkJsonFormatter(renderTemplate, renderMessage, formatProvider, source, sourceType, host, index, subSecondPrecision: subSecondPrecision), messageHandler) { } @@ -128,7 +128,7 @@ public class EventCollectorSink : IBatchedLogEventSink /// The host of the event /// The handler used to send HTTP requests /// Include "RenderedMessage" parameter from output JSON message. - /// Timestamp sub-second precision + /// Timestamp sub-second precision public EventCollectorSink( string splunkHost, string eventCollectorToken, @@ -142,13 +142,13 @@ public class EventCollectorSink : IBatchedLogEventSink bool renderTemplate = true, bool renderMessage = true, HttpMessageHandler messageHandler = null, - int subSecondDecimals = 3) + SubSecondPrecision subSecondPrecision = SubSecondPrecision.Milliseconds) // TODO here is the jsonformatter creation. We must make way to test output of jsonformatter. : this( splunkHost, eventCollectorToken, uriPath, - new SplunkJsonFormatter(renderTemplate, renderMessage, formatProvider, source, sourceType, host, index, fields, subSecondDecimals: subSecondDecimals), + new SplunkJsonFormatter(renderTemplate, renderMessage, formatProvider, source, sourceType, host, index, fields, subSecondPrecision: subSecondPrecision), messageHandler) { } diff --git a/src/Serilog.Sinks.Splunk/Sinks/Splunk/SplunkJsonFormatter.cs b/src/Serilog.Sinks.Splunk/Sinks/Splunk/SplunkJsonFormatter.cs index cf66b72..30599bd 100644 --- a/src/Serilog.Sinks.Splunk/Sinks/Splunk/SplunkJsonFormatter.cs +++ b/src/Serilog.Sinks.Splunk/Sinks/Splunk/SplunkJsonFormatter.cs @@ -32,7 +32,7 @@ public class SplunkJsonFormatter : ITextFormatter private readonly bool _renderTemplate; private readonly IFormatProvider _formatProvider; - private readonly int _subSecondDecimals; + private readonly SubSecondPrecision _subSecondPrecision; private readonly bool _renderMessage; private readonly string _suffix; @@ -60,7 +60,7 @@ public class SplunkJsonFormatter : ITextFormatter /// The source of the event /// The source type of the event /// The host of the event - /// Timestamp sub-second precision + /// Timestamp sub-second precision /// Removes the "RenderedMessage" parameter from output JSON message. public SplunkJsonFormatter( bool renderTemplate, @@ -70,8 +70,8 @@ public class SplunkJsonFormatter : ITextFormatter string sourceType, string host, string index, - int subSecondDecimals = 3) - : this(renderTemplate, renderMessage, formatProvider, source, sourceType, host, index, null, subSecondDecimals: subSecondDecimals) + SubSecondPrecision subSecondPrecision = SubSecondPrecision.Milliseconds) + : this(renderTemplate, renderMessage, formatProvider, source, sourceType, host, index, null, subSecondPrecision: subSecondPrecision) { } @@ -85,7 +85,7 @@ public class SplunkJsonFormatter : ITextFormatter /// The source type of the event /// The host of the event /// Object that describes extra splunk fields that should be indexed with event see: http://dev.splunk.com/view/event-collector/SP-CAAAFB6 - /// Timestamp sub-second precision + /// Timestamp sub-second precision /// Include "RenderedMessage" parameter from output JSON message. public SplunkJsonFormatter( bool renderTemplate, @@ -96,11 +96,11 @@ public class SplunkJsonFormatter : ITextFormatter string host, string index, CustomFields customFields, - int subSecondDecimals = 3) + SubSecondPrecision subSecondPrecision = SubSecondPrecision.Milliseconds) { _renderTemplate = renderTemplate; _formatProvider = formatProvider; - _subSecondDecimals = subSecondDecimals; + _subSecondPrecision = subSecondPrecision; _renderMessage = renderMessage; using (var suffixWriter = new StringWriter()) @@ -171,7 +171,7 @@ public void Format(LogEvent logEvent, TextWriter output) if (output == null) throw new ArgumentNullException(nameof(output)); output.Write("{\"time\":\""); - output.Write(logEvent.Timestamp.ToEpoch(_subSecondDecimals).ToString(CultureInfo.InvariantCulture)); + output.Write(logEvent.Timestamp.ToEpoch(_subSecondPrecision).ToString(CultureInfo.InvariantCulture)); output.Write("\",\"event\":{\"Level\":\""); output.Write(logEvent.Level); output.Write('"'); diff --git a/src/Serilog.Sinks.Splunk/Sinks/Splunk/SubSecondPrecision.cs b/src/Serilog.Sinks.Splunk/Sinks/Splunk/SubSecondPrecision.cs new file mode 100644 index 0000000..7788daa --- /dev/null +++ b/src/Serilog.Sinks.Splunk/Sinks/Splunk/SubSecondPrecision.cs @@ -0,0 +1,37 @@ +// Copyright 2016 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 +// +// 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. +// See the License for the specific language governing permissions and +// limitations under the License. + +namespace Serilog.Sinks.Splunk +{ + /// + /// Enum representing the precision of sub-second time measurements. + /// + public enum SubSecondPrecision + { + /// + /// Represents precision in milliseconds. Value corresponds to the number of decimal places. + /// + Milliseconds = 3, + + /// + /// Represents precision in microseconds. Value corresponds to the number of decimal places. + /// + Microseconds = 6, + + /// + /// Represents precision in nanoseconds. Value corresponds to the number of decimal places. + /// + Nanoseconds = 9 + } +} diff --git a/src/Serilog.Sinks.Splunk/SplunkLoggingConfigurationExtensions.cs b/src/Serilog.Sinks.Splunk/SplunkLoggingConfigurationExtensions.cs index d6c3367..26e127e 100644 --- a/src/Serilog.Sinks.Splunk/SplunkLoggingConfigurationExtensions.cs +++ b/src/Serilog.Sinks.Splunk/SplunkLoggingConfigurationExtensions.cs @@ -49,7 +49,7 @@ public static class SplunkLoggingConfigurationExtensions /// Maximum number of events in the queue /// The handler used to send HTTP requests /// A switch allowing the pass-through minimum level to be changed at runtime. - /// Timestamp sub-second precision + /// Timestamp sub-second precision /// public static LoggerConfiguration EventCollector( this LoggerSinkConfiguration configuration, @@ -69,7 +69,7 @@ public static class SplunkLoggingConfigurationExtensions int? queueLimit = null, HttpMessageHandler messageHandler = null, LoggingLevelSwitch levelSwitch = null, - int subSecondDecimals = 3) + SubSecondPrecision subSecondPrecision = SubSecondPrecision.Milliseconds) { if (configuration == null) throw new ArgumentNullException(nameof(configuration)); @@ -92,7 +92,7 @@ public static class SplunkLoggingConfigurationExtensions formatProvider, renderTemplate, renderMessage, - subSecondDecimals: subSecondDecimals); + subSecondPrecision: subSecondPrecision); var batchingSink = new PeriodicBatchingSink(eventCollectorSink, batchingOptions); @@ -173,7 +173,7 @@ public static class SplunkLoggingConfigurationExtensions /// A switch allowing the pass-through minimum level to be changed at runtime. /// Customfields that will be indexed in splunk with this event /// Include "RenderedMessage" parameter from output JSON message. - /// Timestamp sub-second precision + /// Timestamp sub-second precision /// public static LoggerConfiguration EventCollector( this LoggerSinkConfiguration configuration, @@ -194,7 +194,7 @@ public static class SplunkLoggingConfigurationExtensions int? queueLimit = null, HttpMessageHandler messageHandler = null, LoggingLevelSwitch levelSwitch = null, - int subSecondDecimals = 3) + SubSecondPrecision subSecondPrecision = SubSecondPrecision.Milliseconds) { if (configuration == null) throw new ArgumentNullException(nameof(configuration)); @@ -219,7 +219,7 @@ public static class SplunkLoggingConfigurationExtensions renderTemplate, renderMessage, messageHandler, - subSecondDecimals: subSecondDecimals + subSecondPrecision: subSecondPrecision ); From d1bce249f6f9e64665e53dfa90cfafdb1b1564ad Mon Sep 17 00:00:00 2001 From: EEParker Date: Sat, 16 Mar 2024 17:06:59 -0500 Subject: [PATCH 4/9] Add unit tests and update documentation --- .../Sinks/Splunk/Epoch.cs | 13 +++- .../EpochExtensionsTests.cs | 66 +++++++++++++++++++ .../SplunkJsonFormatterTests.cs | 1 + 3 files changed, 78 insertions(+), 2 deletions(-) create mode 100644 test/Serilog.Sinks.Splunk.Tests/EpochExtensionsTests.cs diff --git a/src/Serilog.Sinks.Splunk/Sinks/Splunk/Epoch.cs b/src/Serilog.Sinks.Splunk/Sinks/Splunk/Epoch.cs index 2ec7e18..f1e7bdb 100644 --- a/src/Serilog.Sinks.Splunk/Sinks/Splunk/Epoch.cs +++ b/src/Serilog.Sinks.Splunk/Sinks/Splunk/Epoch.cs @@ -16,17 +16,26 @@ namespace Serilog.Sinks.Splunk { using System; - internal static class EpochExtensions + /// + /// Provides extension methods for DateTimeOffset to convert to epoch time. + /// + public static class EpochExtensions { private static DateTimeOffset Epoch = new DateTimeOffset(1970, 1, 1, 0, 0, 0, TimeSpan.Zero); + /// + /// Converts a DateTimeOffset value to epoch time with specified sub-second precision. + /// + /// The DateTimeOffset value to convert. + /// The precision of sub-second time measurements. + /// The epoch time representation of the DateTimeOffset value. public static double ToEpoch(this DateTimeOffset value, SubSecondPrecision subSecondPrecision = SubSecondPrecision.Milliseconds) { // From Splunk HTTP Collector Protocol // The default time format is epoch time format, in the format .. // For example, 1433188255.500 indicates 1433188255 seconds and 500 milliseconds after epoch, // or Monday, June 1, 2015, at 7:50:55 PM GMT. - // See: http://dev.splunk.com/view/SP-CAAAE6P + // See: https://docs.splunk.com/Documentation/Splunk/9.2.0/SearchReference/Commontimeformatvariables return Math.Round((value - Epoch).TotalSeconds, (int)subSecondPrecision, MidpointRounding.AwayFromZero); } diff --git a/test/Serilog.Sinks.Splunk.Tests/EpochExtensionsTests.cs b/test/Serilog.Sinks.Splunk.Tests/EpochExtensionsTests.cs new file mode 100644 index 0000000..7bc79b5 --- /dev/null +++ b/test/Serilog.Sinks.Splunk.Tests/EpochExtensionsTests.cs @@ -0,0 +1,66 @@ +using Serilog.Sinks.Splunk; +using System; +using Xunit; + +namespace Serilog.Sinks.Splunk.Tests +{ + public class EpochExtensionsTests + { + [Fact] + public void ToEpoch_ShouldReturnCorrectEpochTime() + { + // Arrange + var dateTimeOffset = new DateTimeOffset(2022, 1, 1, 0, 0, 0, TimeSpan.Zero); + var expectedEpochTime = 1640995200.000; // Epoch time for 2022-01-01 00:00:00 + + // Act + var result = dateTimeOffset.ToEpoch(); + + // Assert + Assert.Equal(expectedEpochTime, result); + } + + [Fact] + public void ToEpoch_ShouldReturnCorrectEpochTime_Milliseconds() + { + // Arrange + var dateTimeOffset = new DateTimeOffset(2022, 1, 1, 0, 0, 0, 123, TimeSpan.Zero); + var expectedEpochTime = 1640995200.123; // Epoch time for 2022-01-01 00:00:00.123 + + // Act + var result = dateTimeOffset.ToEpoch(SubSecondPrecision.Milliseconds); + + // Assert + Assert.Equal(expectedEpochTime, result); + } + + [Fact] + public void ToEpoch_ShouldReturnCorrectEpochTime_Microseconds() + { + // Arrange + var dateTimeOffset = new DateTimeOffset(2022, 1, 1, 0, 0, 0, 123, TimeSpan.Zero) + TimeSpan.FromMicroseconds(456); + var expectedEpochTime = 1640995200.123456; // Epoch time for 2022-01-01 00:00:00.123 + + // Act + var result = dateTimeOffset.ToEpoch(SubSecondPrecision.Microseconds); + + // Assert + Assert.Equal(expectedEpochTime, result); + } + + [Fact] + public void ToEpoch_ShouldReturnCorrectEpochTime_Nanoseconds() + { + // Arrange + // using from ticks here, NanoSeconds is not available in TimeSpan. Nanoseconds Per Tick = 100L. + var dateTimeOffset = new DateTimeOffset(2022, 1, 1, 0, 0, 0, 123, TimeSpan.Zero) + TimeSpan.FromMicroseconds(456) + TimeSpan.FromTicks(7); + var expectedEpochTime = 1640995200.123456700; // Epoch time for 2022-01-01 00:00:00.123 + + // Act + var result = dateTimeOffset.ToEpoch(SubSecondPrecision.Nanoseconds); + + // Assert + Assert.Equal(expectedEpochTime, result); + } + } +} \ No newline at end of file diff --git a/test/Serilog.Sinks.Splunk.Tests/SplunkJsonFormatterTests.cs b/test/Serilog.Sinks.Splunk.Tests/SplunkJsonFormatterTests.cs index 8ae5327..af34962 100644 --- a/test/Serilog.Sinks.Splunk.Tests/SplunkJsonFormatterTests.cs +++ b/test/Serilog.Sinks.Splunk.Tests/SplunkJsonFormatterTests.cs @@ -10,6 +10,7 @@ using Xunit; namespace Serilog.Sinks.Splunk.Tests { + public class SplunkJsonFormatterTests { void AssertValidJson(Action act, From b7cd9f71e3fe57697190bb116fde50eb9f2a7200 Mon Sep 17 00:00:00 2001 From: Victorio Berra Date: Tue, 19 Mar 2024 09:49:30 -0500 Subject: [PATCH 5/9] Invert renderMessage check --- src/Serilog.Sinks.Splunk/Sinks/Splunk/SplunkJsonFormatter.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Serilog.Sinks.Splunk/Sinks/Splunk/SplunkJsonFormatter.cs b/src/Serilog.Sinks.Splunk/Sinks/Splunk/SplunkJsonFormatter.cs index 30599bd..01e162a 100644 --- a/src/Serilog.Sinks.Splunk/Sinks/Splunk/SplunkJsonFormatter.cs +++ b/src/Serilog.Sinks.Splunk/Sinks/Splunk/SplunkJsonFormatter.cs @@ -182,7 +182,7 @@ public void Format(LogEvent logEvent, TextWriter output) JsonValueFormatter.WriteQuotedJsonString(logEvent.MessageTemplate.Text, output); } - if (!_renderMessage) + if (_renderMessage) { output.Write(",\"RenderedMessage\":"); JsonValueFormatter.WriteQuotedJsonString(logEvent.RenderMessage(_formatProvider), output); From b33566336dc0bf7d90effa01e9f19fb835a85086 Mon Sep 17 00:00:00 2001 From: EEParker Date: Mon, 25 Mar 2024 13:13:55 -0500 Subject: [PATCH 6/9] Update sample docker-compose project - add new tests - add a docker-compose vscode project - allow subsecond precision. --- .editorconfig | 181 ++++++++++++++++++ CHANGES.md | 2 +- Create-DockerfileSolutionRestore.ps1 | 33 ++++ Dockerfile | 10 - DockerfileSolutionRestore.txt | 13 ++ docker-compose.dcproj | 17 ++ docker-compose.override.yml | 13 ++ docker-compose.yml | 33 ++-- sample/Sample/Dockerfile | 32 ++++ sample/Sample/Program.cs | 68 ++++++- sample/Sample/Sample.csproj | 2 +- sample/Sample/appsettings.json | 2 +- sample/splunk/Dockerfile | 2 +- sample/splunk/default.yml | 7 + .../apps/splunk_httpinput/local/inputs.conf | 2 +- sample/splunk/etc/system/local/props.conf | 2 + serilog-sinks-splunk.sln | 30 ++- .../Sinks/Splunk/EventCollectorSink.cs | 2 +- 18 files changed, 403 insertions(+), 48 deletions(-) create mode 100644 .editorconfig create mode 100644 Create-DockerfileSolutionRestore.ps1 delete mode 100644 Dockerfile create mode 100644 DockerfileSolutionRestore.txt create mode 100644 docker-compose.dcproj create mode 100644 docker-compose.override.yml create mode 100644 sample/Sample/Dockerfile create mode 100644 sample/splunk/default.yml create mode 100644 sample/splunk/etc/system/local/props.conf diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..e10c2dd --- /dev/null +++ b/.editorconfig @@ -0,0 +1,181 @@ +# To learn more about .editorconfig see https://aka.ms/editorconfigdocs +############################### +# Core EditorConfig Options # +############################### +# All files +[*] +indent_style = space +trim_trailing_whitespace = true + +[Caddyfile] +indent_size = tab + +# XML project files +[*.{csproj,vbproj,vcxproj,vcxproj.filters,proj,projitems,shproj,dcproj}] +indent_size = 2 + +# XML config files +[*.{props,targets,ruleset,config,nuspec,resx,vsixmanifest,vsct}] +indent_size = 2 + +# JSON config files +[*.{json,jsonc}] +indent_size = 2 +insert_final_newline = true + +# YAML config files +[*.{yml,yaml}] +indent_size = 2 +insert_final_newline = true + +# Code files +[*.{cs,csx,vb,vbx}] +indent_size = 4 +insert_final_newline = true +charset = utf-8-bom + +# Markdown +[*.{md, mmd}] +indent_size = 4 +insert_final_newline = true +trim_trailing_whitespace = false + +############################### +# .NET Coding Conventions # +############################### +[*.{cs,vb}] +# Instance fields are camelCase and start with _ +dotnet_naming_rule.instance_fields_should_be_camel_case.severity = suggestion +dotnet_naming_rule.instance_fields_should_be_camel_case.symbols = instance_fields +dotnet_naming_rule.instance_fields_should_be_camel_case.style = instance_field_style +dotnet_naming_symbols.instance_fields.applicable_kinds = field +dotnet_naming_style.instance_field_style.capitalization = camel_case +dotnet_naming_style.instance_field_style.required_prefix = _ +# Organize usings +dotnet_sort_system_directives_first = true +# this. preferences +dotnet_style_qualification_for_field = false:silent +dotnet_style_qualification_for_property = false:silent +dotnet_style_qualification_for_method = false:silent +dotnet_style_qualification_for_event = false:silent +# Language keywords vs BCL types preferences +dotnet_style_predefined_type_for_locals_parameters_members = true:silent +dotnet_style_predefined_type_for_member_access = true:silent +# Parentheses preferences +dotnet_style_parentheses_in_arithmetic_binary_operators = always_for_clarity:silent +dotnet_style_parentheses_in_relational_binary_operators = always_for_clarity:silent +dotnet_style_parentheses_in_other_binary_operators = always_for_clarity:silent +dotnet_style_parentheses_in_other_operators = never_if_unnecessary:silent +# Modifier preferences +dotnet_style_require_accessibility_modifiers = for_non_interface_members:silent +dotnet_style_readonly_field = true:suggestion +# Expression-level preferences +dotnet_style_object_initializer = true:suggestion +dotnet_style_collection_initializer = true:suggestion +dotnet_style_explicit_tuple_names = true:suggestion +dotnet_style_null_propagation = true:suggestion +dotnet_style_coalesce_expression = true:suggestion +dotnet_style_prefer_is_null_check_over_reference_equality_method = true:silent +dotnet_style_prefer_inferred_tuple_names = true:suggestion +dotnet_style_prefer_inferred_anonymous_type_member_names = true:suggestion +dotnet_style_prefer_auto_properties = true:silent +dotnet_style_prefer_conditional_expression_over_assignment = true:silent +dotnet_style_prefer_conditional_expression_over_return = true:silent +############################### +# Naming Conventions # +############################### +# Style Definitions +dotnet_naming_style.pascal_case_style.capitalization = pascal_case +# Use PascalCase for constant fields +dotnet_naming_rule.constant_fields_should_be_pascal_case.severity = suggestion +dotnet_naming_rule.constant_fields_should_be_pascal_case.symbols = constant_fields +dotnet_naming_rule.constant_fields_should_be_pascal_case.style = pascal_case_style +dotnet_naming_symbols.constant_fields.applicable_kinds = field +dotnet_naming_symbols.constant_fields.applicable_accessibilities = * +dotnet_naming_symbols.constant_fields.required_modifiers = const +dotnet_style_prefer_simplified_boolean_expressions = true:suggestion +dotnet_style_operator_placement_when_wrapping = beginning_of_line +tab_width = 4 +end_of_line = crlf +dotnet_style_prefer_compound_assignment = true:suggestion +dotnet_style_prefer_simplified_interpolation = true:suggestion +dotnet_style_namespace_match_folder = true:suggestion +dotnet_style_prefer_collection_expression = true:suggestion +############################### +# C# Coding Conventions # +############################### +[*.cs] +# var preferences +csharp_style_var_for_built_in_types = true:silent +csharp_style_var_when_type_is_apparent = true:silent +csharp_style_var_elsewhere = true:silent +# Expression-bodied members +csharp_style_expression_bodied_methods = false:silent +csharp_style_expression_bodied_constructors = false:silent +csharp_style_expression_bodied_operators = false:silent +csharp_style_expression_bodied_properties = true:silent +csharp_style_expression_bodied_indexers = true:silent +csharp_style_expression_bodied_accessors = true:silent +# Pattern matching preferences +csharp_style_pattern_matching_over_is_with_cast_check = true:suggestion +csharp_style_pattern_matching_over_as_with_null_check = true:suggestion +# Null-checking preferences +csharp_style_throw_expression = true:suggestion +csharp_style_conditional_delegate_call = true:suggestion +# Modifier preferences +csharp_preferred_modifier_order = public,private,protected,internal,static,extern,new,virtual,abstract,sealed,override,readonly,unsafe,volatile,async:suggestion +# Expression-level preferences +csharp_prefer_braces = true:silent +csharp_style_deconstructed_variable_declaration = true:suggestion +csharp_prefer_simple_default_expression = true:suggestion +csharp_style_pattern_local_over_anonymous_function = true:suggestion +csharp_style_inlined_variable_declaration = true:suggestion +############################### +# C# Formatting Rules # +############################### +# New line preferences +csharp_new_line_before_open_brace = all +csharp_new_line_before_else = true +csharp_new_line_before_catch = true +csharp_new_line_before_finally = true +csharp_new_line_before_members_in_object_initializers = true +csharp_new_line_before_members_in_anonymous_types = true +csharp_new_line_between_query_expression_clauses = true +# Indentation preferences +csharp_indent_case_contents = true +csharp_indent_switch_labels = true +csharp_indent_labels = flush_left +# Space preferences +csharp_space_after_cast = false +csharp_space_after_keywords_in_control_flow_statements = true +csharp_space_between_method_call_parameter_list_parentheses = false +csharp_space_between_method_declaration_parameter_list_parentheses = false +csharp_space_between_parentheses = false +csharp_space_before_colon_in_inheritance_clause = true +csharp_space_after_colon_in_inheritance_clause = true +csharp_space_around_binary_operators = before_and_after +csharp_space_between_method_declaration_empty_parameter_list_parentheses = false +csharp_space_between_method_call_name_and_opening_parenthesis = false +csharp_space_between_method_call_empty_parameter_list_parentheses = false +# Wrapping preferences +csharp_preserve_single_line_statements = true +csharp_preserve_single_line_blocks = true +csharp_using_directive_placement = outside_namespace:suggestion +csharp_prefer_simple_using_statement = true:suggestion +csharp_style_namespace_declarations = file_scoped:suggestion +csharp_style_expression_bodied_lambdas = true:silent +csharp_style_expression_bodied_local_functions = true:silent +csharp_style_prefer_null_check_over_type_check = true:suggestion +csharp_style_prefer_method_group_conversion = true:silent +csharp_style_prefer_top_level_statements = true:silent +csharp_style_prefer_local_over_anonymous_function = true:suggestion +csharp_style_prefer_index_operator = true:suggestion +csharp_style_prefer_range_operator = true:suggestion +csharp_style_implicit_object_creation_when_type_is_apparent = true:suggestion +csharp_style_prefer_primary_constructors = true:suggestion +############################### +# VB Coding Conventions # +############################### +[*.vb] +# Modifier preferences +visual_basic_preferred_modifier_order = Partial,Default,Private,Protected,Public,Friend,NotOverridable,Overridable,MustOverride,Overloads,Overrides,MustInherit,NotInheritable,Static,Shared,Shadows,ReadOnly,WriteOnly,Dim,Const,WithEvents,Widening,Narrowing,Custom,Async:suggestion diff --git a/CHANGES.md b/CHANGES.md index b76714a..f606b53 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -69,7 +69,7 @@ - Event Collector fluent interface changed to `.WriteTo.EventCollector` - Event Collector Sink targeting core - TCP/UDP Sinks targeting 4.5 *ONLY* - - Updated Event Collector HTTP Client to add URI endpoint to host: "services/collector" if not included. + - Updated Event Collector HTTP Client to add URI endpoint to host: "services/collector/event" if not included. - Event Collector changed to use epoch time [#15](https://github.com/serilog/serilog-sinks-splunk/pull/15) ## 1.8 diff --git a/Create-DockerfileSolutionRestore.ps1 b/Create-DockerfileSolutionRestore.ps1 new file mode 100644 index 0000000..05ec027 --- /dev/null +++ b/Create-DockerfileSolutionRestore.ps1 @@ -0,0 +1,33 @@ +param ( + [string]$solution = "serilog-sinks-splunk.sln" +) + +$outfile = "DockerfileSolutionRestore.txt" + +# This script creates the $outfile file, with Dockerfile commands to restore all the packages for the solution, +# so you can insert them (by hand) into Dockerfiles right before the "COPY . ." line +# to increase build speed by optimizing the use of docker build images cache. + +# This script is only needed when adding or removing projects from the solution. + +Write-Output "" > $outfile +Add-Content -Path $outfile "# Create this ""restore-solution"" section by running ./Create-DockerfileSolutionRestore.ps1, to optimize build cache reuse" +Select-String -Path $solution -Pattern ', "(.*?\.csproj)"' | ForEach-Object { $_.Matches.Groups[1].Value.Replace("\", "/") } | Sort-Object | ForEach-Object {"COPY [""$_"", """ + $_.Substring(0, $_.LastIndexOf("/") + 1) + """]"} | Out-File -FilePath $outfile -Append +Add-Content -Path $outfile "COPY [""docker-compose.dcproj"", ""./""]" +Add-Content -Path $outfile "COPY [""nuget.config"", ""./""]" +Add-Content -Path $outfile "COPY [""$solution"", ""./""]" +Add-Content -Path $outfile "RUN dotnet restore ""$solution""" +Add-Content -Path $outfile "" + + +Add-Content -Path $outfile "# Docker Compose Paths" + +Get-ChildItem -Path "./" -Recurse -Filter "Dockerfile" | + Resolve-Path -Relative | + ForEach-Object { $_.Replace("\", "/") } + Sort-Object | + ForEach-Object {" ""$_"""} | +Out-File -FilePath $outfile -Append + + +Get-Content $outfile diff --git a/Dockerfile b/Dockerfile deleted file mode 100644 index ac43bb8..0000000 --- a/Dockerfile +++ /dev/null @@ -1,10 +0,0 @@ -FROM microsoft/dotnet:8.0-sdk AS build -ADD . / -WORKDIR /sample/Sample -RUN dotnet restore -RUN dotnet publish -c Release -o out -f net8.0 - -FROM microsoft/dotnet:8.0-runtime AS runtime -WORKDIR /sample/Sample -COPY --from=build /sample/Sample/out ./ -ENTRYPOINT ["dotnet", "Sample.dll"] \ No newline at end of file diff --git a/DockerfileSolutionRestore.txt b/DockerfileSolutionRestore.txt new file mode 100644 index 0000000..a783374 --- /dev/null +++ b/DockerfileSolutionRestore.txt @@ -0,0 +1,13 @@ + +# Create this "restore-solution" section by running ./Create-DockerfileSolutionRestore.ps1, to optimize build cache reuse +COPY ["sample/Sample/Sample.csproj", "sample/Sample/"] +COPY ["src/Serilog.Sinks.Splunk/Serilog.Sinks.Splunk.csproj", "src/Serilog.Sinks.Splunk/"] +COPY ["src/Serilog.Sinks.TCP/Serilog.Sinks.Splunk.TCP.csproj", "src/Serilog.Sinks.TCP/"] +COPY ["src/Serilog.Sinks.UDP/Serilog.Sinks.Splunk.UDP.csproj", "src/Serilog.Sinks.UDP/"] +COPY ["test/Serilog.Sinks.Splunk.Tests/Serilog.Sinks.Splunk.Tests.csproj", "test/Serilog.Sinks.Splunk.Tests/"] +COPY ["docker-compose.dcproj", "./"] +COPY ["nuget.config", "./"] +COPY ["serilog-sinks-splunk.sln", "./"] +RUN dotnet restore "serilog-sinks-splunk.sln" + +# Docker Compose Paths diff --git a/docker-compose.dcproj b/docker-compose.dcproj new file mode 100644 index 0000000..a21fe1d --- /dev/null +++ b/docker-compose.dcproj @@ -0,0 +1,17 @@ + + + + 2.1 + Linux + 1b9defa3-d600-45fa-93a5-79006076fb5c + serilogsinkssplunk + + + + + docker-compose.yml + + + + + \ No newline at end of file diff --git a/docker-compose.override.yml b/docker-compose.override.yml new file mode 100644 index 0000000..6c2a928 --- /dev/null +++ b/docker-compose.override.yml @@ -0,0 +1,13 @@ +version: '3.4' +#https://learn.microsoft.com/en-us/visualstudio/containers/docker-compose-properties + +services: + splunk: + ports: + - 8000:8000 + - 8088:8088 + - 8089:8089 + + sampleconsoleapp: + ports: + - 8080:8080 diff --git a/docker-compose.yml b/docker-compose.yml index db99c14..7639e45 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,25 +1,22 @@ version: '3' services: splunk: - build: ./sample/splunk - image: serilog-splunk + container_name: splunk + build: + context: . + dockerfile: sample/splunk/Dockerfile + volumes: + - "./sample/splunk/default.yml:/tmp/defaults/default.yml" environment: - SPLUNK_START_ARGS: --accept-license --answer-yes --seed-passwd changeme - SPLUNK_ENABLE_LISTEN: 9997 - SPLUNK_PASSWORD: changemeplease! - ports: - - 8000:8000 - - 8088:8088 - - 8089:8089 - networks: - splunkbase_docker: + SPLUNK_START_ARGS: --accept-license --answer-yes --seed-passwd changeme + SPLUNK_ENABLE_LISTEN: 9997 + SPLUNK_PASSWORD: changemeplease! + SPLUNK_HEC_TOKEN: 00112233-4455-6677-8899-AABBCCDDEEFF + sampleconsoleapp: + container_name: sample depends_on: - "splunk" - build: . - image: serilog-console-sample - networks: - splunkbase_docker: -networks: - splunkbase_docker: - + build: + context: . + dockerfile: sample/Sample/Dockerfile diff --git a/sample/Sample/Dockerfile b/sample/Sample/Dockerfile new file mode 100644 index 0000000..d462557 --- /dev/null +++ b/sample/Sample/Dockerfile @@ -0,0 +1,32 @@ +#See https://aka.ms/customizecontainer to learn how to customize your debug container and how Visual Studio uses this Dockerfile to build your images for faster debugging. +FROM mcr.microsoft.com/dotnet/runtime:8.0 AS base +WORKDIR /app +EXPOSE 8080 + +FROM mcr.microsoft.com/dotnet/sdk:8.0 AS build +ARG BUILD_CONFIGURATION=Release +WORKDIR /src + +# Create this "restore-solution" section by running ./Create-DockerfileSolutionRestore.ps1, to optimize build cache reuse +COPY ["sample/Sample/Sample.csproj", "sample/Sample/"] +COPY ["src/Serilog.Sinks.Splunk/Serilog.Sinks.Splunk.csproj", "src/Serilog.Sinks.Splunk/"] +COPY ["src/Serilog.Sinks.TCP/Serilog.Sinks.Splunk.TCP.csproj", "src/Serilog.Sinks.TCP/"] +COPY ["src/Serilog.Sinks.UDP/Serilog.Sinks.Splunk.UDP.csproj", "src/Serilog.Sinks.UDP/"] +COPY ["test/Serilog.Sinks.Splunk.Tests/Serilog.Sinks.Splunk.Tests.csproj", "test/Serilog.Sinks.Splunk.Tests/"] +COPY ["docker-compose.dcproj", "./"] +COPY ["nuget.config", "./"] +COPY ["serilog-sinks-splunk.sln", "./"] +RUN dotnet restore "serilog-sinks-splunk.sln" + +COPY . . +WORKDIR "/src/sample/Sample/" +RUN dotnet build "./Sample.csproj" -c $BUILD_CONFIGURATION -o /app/build + +FROM build AS publish +ARG BUILD_CONFIGURATION=Release +RUN dotnet publish "./Sample.csproj" -c $BUILD_CONFIGURATION -o /app/publish /p:UseAppHost=false + +FROM base AS final +WORKDIR /app +COPY --from=publish /app/publish . +ENTRYPOINT ["dotnet", "Sample.dll"] \ No newline at end of file diff --git a/sample/Sample/Program.cs b/sample/Sample/Program.cs index 1f1dfd9..c96be1d 100644 --- a/sample/Sample/Program.cs +++ b/sample/Sample/Program.cs @@ -5,6 +5,7 @@ using System.Collections.Generic; using System.IO; using System.Linq; +using System.Threading.Tasks; namespace Sample { @@ -15,7 +16,7 @@ public class Program const string SPLUNK_HEC_TOKEN = "00112233-4455-6677-8899-AABBCCDDEEFF"; // Your HEC token. See http://docs.splunk.com/Documentation/Splunk/latest/Data/UsetheHTTPEventCollector public static string EventCollectorToken = SPLUNK_HEC_TOKEN; - public static void Main(string[] args) + public static async Task Main(string[] args) { // Bootstrap a simple logger. var logger = new LoggerConfiguration() @@ -27,9 +28,9 @@ public static void Main(string[] args) logger.Information("Sample app starting up..."); logger.Information("Startup arguments \"{Arguments}\".", args); - var eventsToCreate = 100; + var eventsToCreate = 10; var runSSL = false; - var millisecsToWait = 60000; + var secToWait = 30; if (args.Length > 0) eventsToCreate = int.Parse(args[0]); @@ -38,7 +39,7 @@ public static void Main(string[] args) runSSL = bool.Parse(args[1]); if (args.Length == 3) - millisecsToWait = int.Parse(args[2]); + secToWait = int.Parse(args[2]); Serilog.Debugging.SelfLog.Enable(msg => { @@ -46,8 +47,17 @@ public static void Main(string[] args) throw new Exception("Failed to write to Serilog.", new Exception(msg)); }); - logger.Information("Waiting {MillisecondsToWait} millisecs...", millisecsToWait); - System.Threading.Thread.Sleep(millisecsToWait); + + while (secToWait-- > 0) + { + logger.Information("Waiting {secToWait} seconds...", secToWait); + await Task.Delay(1000); + } + + logger.Information("Creating logger {MethodName}.", nameof(OverridingSubsecondPrecisionMicroseconds)); + OverridingSubsecondPrecisionMicroseconds(eventsToCreate); + logger.Information("Creating logger {MethodName}.", nameof(OverridingSubsecondPrecisionNanoseconds)); + OverridingSubsecondPrecisionNanoseconds(eventsToCreate); logger.Information("Creating logger {MethodName}.", nameof(UsingAppSettingsJson)); UsingAppSettingsJson(eventsToCreate); @@ -131,6 +141,50 @@ private static void WithCompactSplunkFormatter(int eventsToCreate) Log.CloseAndFlush(); } + public static void OverridingSubsecondPrecisionMicroseconds(int eventsToCreate) + { + // Override Source + Log.Logger = new LoggerConfiguration() + .MinimumLevel.Debug() + .WriteTo.Console() + .WriteTo.EventCollector( + SPLUNK_ENDPOINT, + Program.EventCollectorToken, + subSecondPrecision: SubSecondPrecision.Microseconds) + .Enrich.WithProperty("Serilog.Sinks.Splunk.Sample", "ViaEventCollector") + .Enrich.WithProperty("Serilog.Sinks.Splunk.Sample.TestType", "Source Override") + .CreateLogger(); + + foreach (var i in Enumerable.Range(0, eventsToCreate)) + { + Log.Information("Running source override loop {Counter} subseconds: {subsecondPrecision}", i, SubSecondPrecision.Microseconds.ToString()); + } + + Log.CloseAndFlush(); + } + + public static void OverridingSubsecondPrecisionNanoseconds(int eventsToCreate) + { + // Override Source + Log.Logger = new LoggerConfiguration() + .MinimumLevel.Debug() + .WriteTo.Console() + .WriteTo.EventCollector( + SPLUNK_ENDPOINT, + Program.EventCollectorToken, + subSecondPrecision: SubSecondPrecision.Nanoseconds) + .Enrich.WithProperty("Serilog.Sinks.Splunk.Sample", "ViaEventCollector") + .Enrich.WithProperty("Serilog.Sinks.Splunk.Sample.TestType", "Source Override") + .CreateLogger(); + + foreach (var i in Enumerable.Range(0, eventsToCreate)) + { + Log.Information("Running source override loop {Counter} subseconds: {subsecondPrecision}", i, SubSecondPrecision.Nanoseconds.ToString()); + } + + Log.CloseAndFlush(); + } + public static void OverridingSource(int eventsToCreate) { // Override Source @@ -229,7 +283,7 @@ public static void UsingHostOnly(int eventsToCreate) SPLUNK_ENDPOINT, Program.EventCollectorToken) .Enrich.WithProperty("Serilog.Sinks.Splunk.Sample", "ViaEventCollector") - .Enrich.WithProperty("Serilog.Sinks.Splunk.Sample.TestType", "Vanilla No services/collector in uri") + .Enrich.WithProperty("Serilog.Sinks.Splunk.Sample.TestType", "Vanilla No services/collector/event in uri") .CreateLogger(); foreach (var i in Enumerable.Range(0, eventsToCreate)) diff --git a/sample/Sample/Sample.csproj b/sample/Sample/Sample.csproj index 0521562..6fd1eb4 100644 --- a/sample/Sample/Sample.csproj +++ b/sample/Sample/Sample.csproj @@ -16,7 +16,7 @@ - + PreserveNewest diff --git a/sample/Sample/appsettings.json b/sample/Sample/appsettings.json index 70ed38e..7fab711 100644 --- a/sample/Sample/appsettings.json +++ b/sample/Sample/appsettings.json @@ -21,4 +21,4 @@ "Serilog.Sinks.Splunk.Sample.TestType": "AppSettings.json" } } -} \ No newline at end of file +} diff --git a/sample/splunk/Dockerfile b/sample/splunk/Dockerfile index 9f46425..f006f84 100644 --- a/sample/splunk/Dockerfile +++ b/sample/splunk/Dockerfile @@ -1,2 +1,2 @@ FROM splunk/splunk:latest -ADD etc ${SPLUNK_HOME}/etc \ No newline at end of file +ADD sample/splunk/etc ${SPLUNK_HOME}/etc \ No newline at end of file diff --git a/sample/splunk/default.yml b/sample/splunk/default.yml new file mode 100644 index 0000000..c344bef --- /dev/null +++ b/sample/splunk/default.yml @@ -0,0 +1,7 @@ +splunk: + hec: + enable: True + ssl: false + port: 8088 + # hec.token is used only for ingestion (receiving Splunk events) + token: 00112233-4455-6677-8899-AABBCCDDEEFF diff --git a/sample/splunk/etc/apps/splunk_httpinput/local/inputs.conf b/sample/splunk/etc/apps/splunk_httpinput/local/inputs.conf index ff47956..a65d8a1 100644 --- a/sample/splunk/etc/apps/splunk_httpinput/local/inputs.conf +++ b/sample/splunk/etc/apps/splunk_httpinput/local/inputs.conf @@ -2,5 +2,5 @@ disabled = 0 enableSSL = 0 -[http://serilog_sample] +[http://splunk-sample] token = 00112233-4455-6677-8899-AABBCCDDEEFF \ No newline at end of file diff --git a/sample/splunk/etc/system/local/props.conf b/sample/splunk/etc/system/local/props.conf new file mode 100644 index 0000000..7a7be15 --- /dev/null +++ b/sample/splunk/etc/system/local/props.conf @@ -0,0 +1,2 @@ +TIME_PREFIX = \"time\"\:\s*\" +TIME_FORMAT = %Y-%m-%dT%H:%M:%S.%9Q%Z \ No newline at end of file diff --git a/serilog-sinks-splunk.sln b/serilog-sinks-splunk.sln index ba502cd..c1f15b4 100644 --- a/serilog-sinks-splunk.sln +++ b/serilog-sinks-splunk.sln @@ -1,12 +1,13 @@  Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio 15 -VisualStudioVersion = 15.0.26430.12 +# Visual Studio Version 17 +VisualStudioVersion = 17.9.34714.143 MinimumVisualStudioVersion = 10.0.40219.1 Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{7A774CBB-A6E9-4854-B4DB-4CF860B0C1C5}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "assets", "assets", "{B9B13339-749C-4098-8845-780ED4FA488A}" ProjectSection(SolutionItems) = preProject + .editorconfig = .editorconfig .travis.yml = .travis.yml appveyor.yml = appveyor.yml Build.ps1 = Build.ps1 @@ -20,15 +21,22 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "sample", "sample", "{1C75E4 EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "test", "test", "{B9451AD8-09B9-4C09-A152-FBAE24806614}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Serilog.Sinks.Splunk", "src\Serilog.Sinks.Splunk\Serilog.Sinks.Splunk.csproj", "{32CF915C-3ECD-496C-8FDB-1214581274A6}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Serilog.Sinks.Splunk", "src\Serilog.Sinks.Splunk\Serilog.Sinks.Splunk.csproj", "{32CF915C-3ECD-496C-8FDB-1214581274A6}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Serilog.Sinks.Splunk.Tests", "test\Serilog.Sinks.Splunk.Tests\Serilog.Sinks.Splunk.Tests.csproj", "{3C2D8E01-5580-426A-BDD9-EC59CD98E618}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Serilog.Sinks.Splunk.Tests", "test\Serilog.Sinks.Splunk.Tests\Serilog.Sinks.Splunk.Tests.csproj", "{3C2D8E01-5580-426A-BDD9-EC59CD98E618}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Sample", "sample\Sample\Sample.csproj", "{4A4E361D-8BBE-4DDD-9E6C-53960C2B5F5B}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Sample", "sample\Sample\Sample.csproj", "{4A4E361D-8BBE-4DDD-9E6C-53960C2B5F5B}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Serilog.Sinks.Splunk.TCP", "src\Serilog.Sinks.TCP\Serilog.Sinks.Splunk.TCP.csproj", "{F74FCFD0-536B-4311-AA66-0BD16112D895}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Serilog.Sinks.Splunk.TCP", "src\Serilog.Sinks.TCP\Serilog.Sinks.Splunk.TCP.csproj", "{F74FCFD0-536B-4311-AA66-0BD16112D895}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Serilog.Sinks.Splunk.UDP", "src\Serilog.Sinks.UDP\Serilog.Sinks.Splunk.UDP.csproj", "{FE1504A6-5444-4B87-819C-E6F477662B7F}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Serilog.Sinks.Splunk.UDP", "src\Serilog.Sinks.UDP\Serilog.Sinks.Splunk.UDP.csproj", "{FE1504A6-5444-4B87-819C-E6F477662B7F}" +EndProject +Project("{E53339B2-1760-4266-BCC7-CA923CBCF16C}") = "docker-compose", "docker-compose.dcproj", "{1B9DEFA3-D600-45FA-93A5-79006076FB5C}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "splunk", "splunk", "{21EEF50A-C0FC-4406-97A1-8F5F499AE2FC}" + ProjectSection(SolutionItems) = preProject + sample\splunk\Dockerfile = sample\splunk\Dockerfile + EndProjectSection EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -56,6 +64,10 @@ Global {FE1504A6-5444-4B87-819C-E6F477662B7F}.Debug|Any CPU.Build.0 = Debug|Any CPU {FE1504A6-5444-4B87-819C-E6F477662B7F}.Release|Any CPU.ActiveCfg = Release|Any CPU {FE1504A6-5444-4B87-819C-E6F477662B7F}.Release|Any CPU.Build.0 = Release|Any CPU + {1B9DEFA3-D600-45FA-93A5-79006076FB5C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {1B9DEFA3-D600-45FA-93A5-79006076FB5C}.Debug|Any CPU.Build.0 = Debug|Any CPU + {1B9DEFA3-D600-45FA-93A5-79006076FB5C}.Release|Any CPU.ActiveCfg = Release|Any CPU + {1B9DEFA3-D600-45FA-93A5-79006076FB5C}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -66,5 +78,9 @@ Global {4A4E361D-8BBE-4DDD-9E6C-53960C2B5F5B} = {1C75E4A9-4CB1-497C-AD17-B438882051A1} {F74FCFD0-536B-4311-AA66-0BD16112D895} = {7A774CBB-A6E9-4854-B4DB-4CF860B0C1C5} {FE1504A6-5444-4B87-819C-E6F477662B7F} = {7A774CBB-A6E9-4854-B4DB-4CF860B0C1C5} + {21EEF50A-C0FC-4406-97A1-8F5F499AE2FC} = {1C75E4A9-4CB1-497C-AD17-B438882051A1} + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {D7BFF439-D18D-4124-A36F-15CFB8E84BCC} EndGlobalSection EndGlobal diff --git a/src/Serilog.Sinks.Splunk/Sinks/Splunk/EventCollectorSink.cs b/src/Serilog.Sinks.Splunk/Sinks/Splunk/EventCollectorSink.cs index f6162b5..5346a7c 100644 --- a/src/Serilog.Sinks.Splunk/Sinks/Splunk/EventCollectorSink.cs +++ b/src/Serilog.Sinks.Splunk/Sinks/Splunk/EventCollectorSink.cs @@ -1,4 +1,4 @@ -// Copyright 2016 Serilog Contributors +// Copyright 2016 Serilog Contributors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. From b0e77ade5169bbcb02beaecfbc205de20633d614 Mon Sep 17 00:00:00 2001 From: "Jeff Parker, PE" Date: Tue, 26 Mar 2024 08:04:15 -0500 Subject: [PATCH 7/9] Add comment for configuring splunk --- .../Sinks/Splunk/CompactSplunkJsonFormatter.cs | 2 +- src/Serilog.Sinks.Splunk/Sinks/Splunk/Epoch.cs | 2 +- src/Serilog.Sinks.Splunk/Sinks/Splunk/EventCollectorSink.cs | 6 +++--- .../Sinks/Splunk/SplunkJsonFormatter.cs | 4 ++-- .../SplunkLoggingConfigurationExtensions.cs | 4 ++-- 5 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/Serilog.Sinks.Splunk/Sinks/Splunk/CompactSplunkJsonFormatter.cs b/src/Serilog.Sinks.Splunk/Sinks/Splunk/CompactSplunkJsonFormatter.cs index f1dc5e3..84957bb 100644 --- a/src/Serilog.Sinks.Splunk/Sinks/Splunk/CompactSplunkJsonFormatter.cs +++ b/src/Serilog.Sinks.Splunk/Sinks/Splunk/CompactSplunkJsonFormatter.cs @@ -41,7 +41,7 @@ public class CompactSplunkJsonFormatter : ITextFormatter /// The host of the event /// The Splunk index to log to /// If true, the template used will be rendered and written to the output as a property named MessageTemplate - /// Timestamp sub-second precision + /// Timestamp sub-second precision. Splunk props.conf setup is required. public CompactSplunkJsonFormatter( bool renderTemplate = false, diff --git a/src/Serilog.Sinks.Splunk/Sinks/Splunk/Epoch.cs b/src/Serilog.Sinks.Splunk/Sinks/Splunk/Epoch.cs index f1e7bdb..442843d 100644 --- a/src/Serilog.Sinks.Splunk/Sinks/Splunk/Epoch.cs +++ b/src/Serilog.Sinks.Splunk/Sinks/Splunk/Epoch.cs @@ -27,7 +27,7 @@ public static class EpochExtensions /// Converts a DateTimeOffset value to epoch time with specified sub-second precision. /// /// The DateTimeOffset value to convert. - /// The precision of sub-second time measurements. + /// Timestamp sub-second precision. /// The epoch time representation of the DateTimeOffset value. public static double ToEpoch(this DateTimeOffset value, SubSecondPrecision subSecondPrecision = SubSecondPrecision.Milliseconds) { diff --git a/src/Serilog.Sinks.Splunk/Sinks/Splunk/EventCollectorSink.cs b/src/Serilog.Sinks.Splunk/Sinks/Splunk/EventCollectorSink.cs index 5346a7c..79c5e2f 100644 --- a/src/Serilog.Sinks.Splunk/Sinks/Splunk/EventCollectorSink.cs +++ b/src/Serilog.Sinks.Splunk/Sinks/Splunk/EventCollectorSink.cs @@ -57,7 +57,7 @@ public class EventCollectorSink : IBatchedLogEventSink /// The format provider used when rendering the message /// Whether to render the message template /// Include "RenderedMessage" parameter from output JSON message. - /// Timestamp sub-second precision + /// Timestamp sub-second precision. Splunk props.conf setup is required. public EventCollectorSink( string splunkHost, string eventCollectorToken, @@ -90,7 +90,7 @@ public class EventCollectorSink : IBatchedLogEventSink /// The host of the event /// The handler used to send HTTP requests /// Include "RenderedMessage" parameter from output JSON message. - /// Timestamp sub-second precision + /// Timestamp sub-second precision. Splunk props.conf setup is required. public EventCollectorSink( string splunkHost, string eventCollectorToken, @@ -128,7 +128,7 @@ public class EventCollectorSink : IBatchedLogEventSink /// The host of the event /// The handler used to send HTTP requests /// Include "RenderedMessage" parameter from output JSON message. - /// Timestamp sub-second precision + /// Timestamp sub-second precision. Splunk props.conf setup is required. public EventCollectorSink( string splunkHost, string eventCollectorToken, diff --git a/src/Serilog.Sinks.Splunk/Sinks/Splunk/SplunkJsonFormatter.cs b/src/Serilog.Sinks.Splunk/Sinks/Splunk/SplunkJsonFormatter.cs index 01e162a..71d0966 100644 --- a/src/Serilog.Sinks.Splunk/Sinks/Splunk/SplunkJsonFormatter.cs +++ b/src/Serilog.Sinks.Splunk/Sinks/Splunk/SplunkJsonFormatter.cs @@ -60,7 +60,7 @@ public class SplunkJsonFormatter : ITextFormatter /// The source of the event /// The source type of the event /// The host of the event - /// Timestamp sub-second precision + /// Timestamp sub-second precision. Splunk props.conf setup is required. /// Removes the "RenderedMessage" parameter from output JSON message. public SplunkJsonFormatter( bool renderTemplate, @@ -85,7 +85,7 @@ public class SplunkJsonFormatter : ITextFormatter /// The source type of the event /// The host of the event /// Object that describes extra splunk fields that should be indexed with event see: http://dev.splunk.com/view/event-collector/SP-CAAAFB6 - /// Timestamp sub-second precision + /// Timestamp sub-second precision. Splunk props.conf setup is required. /// Include "RenderedMessage" parameter from output JSON message. public SplunkJsonFormatter( bool renderTemplate, diff --git a/src/Serilog.Sinks.Splunk/SplunkLoggingConfigurationExtensions.cs b/src/Serilog.Sinks.Splunk/SplunkLoggingConfigurationExtensions.cs index 26e127e..333dd81 100644 --- a/src/Serilog.Sinks.Splunk/SplunkLoggingConfigurationExtensions.cs +++ b/src/Serilog.Sinks.Splunk/SplunkLoggingConfigurationExtensions.cs @@ -49,7 +49,7 @@ public static class SplunkLoggingConfigurationExtensions /// Maximum number of events in the queue /// The handler used to send HTTP requests /// A switch allowing the pass-through minimum level to be changed at runtime. - /// Timestamp sub-second precision + /// Timestamp sub-second precision. Splunk props.conf setup is required. /// public static LoggerConfiguration EventCollector( this LoggerSinkConfiguration configuration, @@ -173,7 +173,7 @@ public static class SplunkLoggingConfigurationExtensions /// A switch allowing the pass-through minimum level to be changed at runtime. /// Customfields that will be indexed in splunk with this event /// Include "RenderedMessage" parameter from output JSON message. - /// Timestamp sub-second precision + /// Timestamp sub-second precision. Splunk props.conf setup is required. /// public static LoggerConfiguration EventCollector( this LoggerSinkConfiguration configuration, From 867db7c9f2a41e5ddd32f2bb99cb9610bd231010 Mon Sep 17 00:00:00 2001 From: EEParker Date: Tue, 26 Mar 2024 10:15:26 -0500 Subject: [PATCH 8/9] Fix formatting for nanoseconds --- Create-DockerfileSolutionRestore.ps1 | 1 - sample/Sample/Dockerfile | 1 - sample/Sample/Program.cs | 68 +++++++++---------- sample/splunk/etc/system/local/props.conf | 4 +- .../Splunk/CompactSplunkJsonFormatter.cs | 4 +- .../Sinks/Splunk/Epoch.cs | 26 ++++++- .../Sinks/Splunk/SplunkJsonFormatter.cs | 2 +- .../EpochExtensionsTests.cs | 11 +-- .../Serilog.Sinks.Splunk.Tests.csproj | 23 +++---- 9 files changed, 80 insertions(+), 60 deletions(-) diff --git a/Create-DockerfileSolutionRestore.ps1 b/Create-DockerfileSolutionRestore.ps1 index 05ec027..bd52768 100644 --- a/Create-DockerfileSolutionRestore.ps1 +++ b/Create-DockerfileSolutionRestore.ps1 @@ -14,7 +14,6 @@ Write-Output "" > $outfile Add-Content -Path $outfile "# Create this ""restore-solution"" section by running ./Create-DockerfileSolutionRestore.ps1, to optimize build cache reuse" Select-String -Path $solution -Pattern ', "(.*?\.csproj)"' | ForEach-Object { $_.Matches.Groups[1].Value.Replace("\", "/") } | Sort-Object | ForEach-Object {"COPY [""$_"", """ + $_.Substring(0, $_.LastIndexOf("/") + 1) + """]"} | Out-File -FilePath $outfile -Append Add-Content -Path $outfile "COPY [""docker-compose.dcproj"", ""./""]" -Add-Content -Path $outfile "COPY [""nuget.config"", ""./""]" Add-Content -Path $outfile "COPY [""$solution"", ""./""]" Add-Content -Path $outfile "RUN dotnet restore ""$solution""" Add-Content -Path $outfile "" diff --git a/sample/Sample/Dockerfile b/sample/Sample/Dockerfile index d462557..a23ba1c 100644 --- a/sample/Sample/Dockerfile +++ b/sample/Sample/Dockerfile @@ -14,7 +14,6 @@ COPY ["src/Serilog.Sinks.TCP/Serilog.Sinks.Splunk.TCP.csproj", "src/Serilog.Sink COPY ["src/Serilog.Sinks.UDP/Serilog.Sinks.Splunk.UDP.csproj", "src/Serilog.Sinks.UDP/"] COPY ["test/Serilog.Sinks.Splunk.Tests/Serilog.Sinks.Splunk.Tests.csproj", "test/Serilog.Sinks.Splunk.Tests/"] COPY ["docker-compose.dcproj", "./"] -COPY ["nuget.config", "./"] COPY ["serilog-sinks-splunk.sln", "./"] RUN dotnet restore "serilog-sinks-splunk.sln" diff --git a/sample/Sample/Program.cs b/sample/Sample/Program.cs index c96be1d..56eacef 100644 --- a/sample/Sample/Program.cs +++ b/sample/Sample/Program.cs @@ -54,43 +54,43 @@ public static async Task Main(string[] args) await Task.Delay(1000); } - logger.Information("Creating logger {MethodName}.", nameof(OverridingSubsecondPrecisionMicroseconds)); - OverridingSubsecondPrecisionMicroseconds(eventsToCreate); + // logger.Information("Creating logger {MethodName}.", nameof(OverridingSubsecondPrecisionMicroseconds)); + // OverridingSubsecondPrecisionMicroseconds(eventsToCreate); logger.Information("Creating logger {MethodName}.", nameof(OverridingSubsecondPrecisionNanoseconds)); OverridingSubsecondPrecisionNanoseconds(eventsToCreate); - logger.Information("Creating logger {MethodName}.", nameof(UsingAppSettingsJson)); - UsingAppSettingsJson(eventsToCreate); - - logger.Information("Creating logger {MethodName}.", nameof(UsingHostOnly)); - UsingHostOnly(eventsToCreate); - - logger.Information("Creating logger {MethodName}.", nameof(UsingFullUri)); - UsingFullUri(eventsToCreate); - - logger.Information("Creating logger {MethodName}.", nameof(OverridingSource)); - OverridingSource(eventsToCreate); - - logger.Information("Creating logger {MethodName}.", nameof(OverridingSourceType)); - OverridingSourceType(eventsToCreate); - - logger.Information("Creating logger {MethodName}.", nameof(OverridingHost)); - OverridingHost(eventsToCreate); - - logger.Information("Creating logger {MethodName}.", nameof(WithNoTemplate)); - WithNoTemplate(eventsToCreate); - - logger.Information("Creating logger {MethodName}.", nameof(WithCompactSplunkFormatter)); - WithCompactSplunkFormatter(eventsToCreate); - - if (runSSL) - { - logger.Information("Creating logger {MethodName}.", nameof(UsingSSL)); - UsingSSL(eventsToCreate); - } - - logger.Information("Creating logger {MethodName}.", nameof(AddCustomFields)); - AddCustomFields(eventsToCreate); + //logger.Information("Creating logger {MethodName}.", nameof(UsingAppSettingsJson)); + //UsingAppSettingsJson(eventsToCreate); + // + //logger.Information("Creating logger {MethodName}.", nameof(UsingHostOnly)); + //UsingHostOnly(eventsToCreate); + // + //logger.Information("Creating logger {MethodName}.", nameof(UsingFullUri)); + //UsingFullUri(eventsToCreate); + // + //logger.Information("Creating logger {MethodName}.", nameof(OverridingSource)); + //OverridingSource(eventsToCreate); + // + //logger.Information("Creating logger {MethodName}.", nameof(OverridingSourceType)); + //OverridingSourceType(eventsToCreate); + // + //logger.Information("Creating logger {MethodName}.", nameof(OverridingHost)); + //OverridingHost(eventsToCreate); + // + //logger.Information("Creating logger {MethodName}.", nameof(WithNoTemplate)); + //WithNoTemplate(eventsToCreate); + // + //logger.Information("Creating logger {MethodName}.", nameof(WithCompactSplunkFormatter)); + //WithCompactSplunkFormatter(eventsToCreate); + // + //if (runSSL) + //{ + // logger.Information("Creating logger {MethodName}.", nameof(UsingSSL)); + // UsingSSL(eventsToCreate); + //} + // + //logger.Information("Creating logger {MethodName}.", nameof(AddCustomFields)); + //AddCustomFields(eventsToCreate); } finally { diff --git a/sample/splunk/etc/system/local/props.conf b/sample/splunk/etc/system/local/props.conf index 7a7be15..665e07b 100644 --- a/sample/splunk/etc/system/local/props.conf +++ b/sample/splunk/etc/system/local/props.conf @@ -1,2 +1,4 @@ TIME_PREFIX = \"time\"\:\s*\" -TIME_FORMAT = %Y-%m-%dT%H:%M:%S.%9Q%Z \ No newline at end of file +TIME_FORMAT = %Y-%m-%d %H:%M:%S.%9N +ADD_EXTRA_TIME_FIELDS = all +MAX_TIMESTAMP_LOOKAHEAD = 30 \ No newline at end of file diff --git a/src/Serilog.Sinks.Splunk/Sinks/Splunk/CompactSplunkJsonFormatter.cs b/src/Serilog.Sinks.Splunk/Sinks/Splunk/CompactSplunkJsonFormatter.cs index 84957bb..7b56f49 100644 --- a/src/Serilog.Sinks.Splunk/Sinks/Splunk/CompactSplunkJsonFormatter.cs +++ b/src/Serilog.Sinks.Splunk/Sinks/Splunk/CompactSplunkJsonFormatter.cs @@ -91,7 +91,7 @@ public void Format(LogEvent logEvent, TextWriter output) if (output == null) throw new ArgumentNullException(nameof(output)); output.Write("{\"time\":\""); - output.Write(logEvent.Timestamp.ToEpoch(_subSecondPrecision).ToString(CultureInfo.InvariantCulture)); + output.Write(logEvent.Timestamp.ToEpoch(_subSecondPrecision)); output.Write("\",\"event\":{\"@l\":\""); output.Write(logEvent.Level); output.Write('"'); @@ -145,4 +145,4 @@ public void Format(LogEvent logEvent, TextWriter output) output.WriteLine(_suffix); } } -} \ No newline at end of file +} diff --git a/src/Serilog.Sinks.Splunk/Sinks/Splunk/Epoch.cs b/src/Serilog.Sinks.Splunk/Sinks/Splunk/Epoch.cs index 442843d..1d85a2d 100644 --- a/src/Serilog.Sinks.Splunk/Sinks/Splunk/Epoch.cs +++ b/src/Serilog.Sinks.Splunk/Sinks/Splunk/Epoch.cs @@ -1,4 +1,4 @@ -// Copyright 2016 Serilog Contributors +// Copyright 2016 Serilog Contributors // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. @@ -15,6 +15,7 @@ namespace Serilog.Sinks.Splunk { using System; + using System.Globalization; /// /// Provides extension methods for DateTimeOffset to convert to epoch time. @@ -22,6 +23,9 @@ namespace Serilog.Sinks.Splunk public static class EpochExtensions { private static DateTimeOffset Epoch = new DateTimeOffset(1970, 1, 1, 0, 0, 0, TimeSpan.Zero); + const string MillisecondFormat = "#.000"; + const string MicrosecondFormat = "#.000000"; + const string NanosecondFormat = "#.000000000"; /// /// Converts a DateTimeOffset value to epoch time with specified sub-second precision. @@ -29,7 +33,7 @@ public static class EpochExtensions /// The DateTimeOffset value to convert. /// Timestamp sub-second precision. /// The epoch time representation of the DateTimeOffset value. - public static double ToEpoch(this DateTimeOffset value, SubSecondPrecision subSecondPrecision = SubSecondPrecision.Milliseconds) + public static string ToEpoch(this DateTimeOffset value, SubSecondPrecision subSecondPrecision = SubSecondPrecision.Milliseconds) { // From Splunk HTTP Collector Protocol // The default time format is epoch time format, in the format .. @@ -37,7 +41,23 @@ public static double ToEpoch(this DateTimeOffset value, SubSecondPrecision subSe // or Monday, June 1, 2015, at 7:50:55 PM GMT. // See: https://docs.splunk.com/Documentation/Splunk/9.2.0/SearchReference/Commontimeformatvariables - return Math.Round((value - Epoch).TotalSeconds, (int)subSecondPrecision, MidpointRounding.AwayFromZero); + var totalSeconds = ToSeconds(value.Ticks - Epoch.Ticks); + var format = subSecondPrecision switch { + SubSecondPrecision.Nanoseconds => NanosecondFormat, + SubSecondPrecision.Microseconds => MicrosecondFormat, + _ => MillisecondFormat, + }; + + return Math.Round(totalSeconds, (int)subSecondPrecision, MidpointRounding.AwayFromZero).ToString(format, CultureInfo.InvariantCulture); + } + + private static decimal ToSeconds(long ticks) + { + long wholeSecondPortion = (ticks / TimeSpan.TicksPerSecond) * TimeSpan.TicksPerSecond; + long subsecondPortion = ticks - wholeSecondPortion; + decimal wholeSeconds = wholeSecondPortion / (decimal)TimeSpan.TicksPerSecond; + decimal subseconds = subsecondPortion / (decimal)TimeSpan.TicksPerSecond; + return wholeSeconds + subseconds; } } } diff --git a/src/Serilog.Sinks.Splunk/Sinks/Splunk/SplunkJsonFormatter.cs b/src/Serilog.Sinks.Splunk/Sinks/Splunk/SplunkJsonFormatter.cs index 71d0966..ac9eaa5 100644 --- a/src/Serilog.Sinks.Splunk/Sinks/Splunk/SplunkJsonFormatter.cs +++ b/src/Serilog.Sinks.Splunk/Sinks/Splunk/SplunkJsonFormatter.cs @@ -171,7 +171,7 @@ public void Format(LogEvent logEvent, TextWriter output) if (output == null) throw new ArgumentNullException(nameof(output)); output.Write("{\"time\":\""); - output.Write(logEvent.Timestamp.ToEpoch(_subSecondPrecision).ToString(CultureInfo.InvariantCulture)); + output.Write(logEvent.Timestamp.ToEpoch(_subSecondPrecision)); output.Write("\",\"event\":{\"Level\":\""); output.Write(logEvent.Level); output.Write('"'); diff --git a/test/Serilog.Sinks.Splunk.Tests/EpochExtensionsTests.cs b/test/Serilog.Sinks.Splunk.Tests/EpochExtensionsTests.cs index 7bc79b5..949905d 100644 --- a/test/Serilog.Sinks.Splunk.Tests/EpochExtensionsTests.cs +++ b/test/Serilog.Sinks.Splunk.Tests/EpochExtensionsTests.cs @@ -1,5 +1,6 @@ using Serilog.Sinks.Splunk; using System; +using System.Diagnostics; using Xunit; namespace Serilog.Sinks.Splunk.Tests @@ -11,7 +12,7 @@ public void ToEpoch_ShouldReturnCorrectEpochTime() { // Arrange var dateTimeOffset = new DateTimeOffset(2022, 1, 1, 0, 0, 0, TimeSpan.Zero); - var expectedEpochTime = 1640995200.000; // Epoch time for 2022-01-01 00:00:00 + var expectedEpochTime = "1640995200.000"; // Epoch time for 2022-01-01 00:00:00 // Act var result = dateTimeOffset.ToEpoch(); @@ -25,7 +26,7 @@ public void ToEpoch_ShouldReturnCorrectEpochTime_Milliseconds() { // Arrange var dateTimeOffset = new DateTimeOffset(2022, 1, 1, 0, 0, 0, 123, TimeSpan.Zero); - var expectedEpochTime = 1640995200.123; // Epoch time for 2022-01-01 00:00:00.123 + var expectedEpochTime = "1640995200.123"; // Epoch time for 2022-01-01 00:00:00.123 // Act var result = dateTimeOffset.ToEpoch(SubSecondPrecision.Milliseconds); @@ -39,7 +40,7 @@ public void ToEpoch_ShouldReturnCorrectEpochTime_Microseconds() { // Arrange var dateTimeOffset = new DateTimeOffset(2022, 1, 1, 0, 0, 0, 123, TimeSpan.Zero) + TimeSpan.FromMicroseconds(456); - var expectedEpochTime = 1640995200.123456; // Epoch time for 2022-01-01 00:00:00.123 + var expectedEpochTime = "1640995200.123456"; // Epoch time for 2022-01-01 00:00:00.123 // Act var result = dateTimeOffset.ToEpoch(SubSecondPrecision.Microseconds); @@ -54,7 +55,7 @@ public void ToEpoch_ShouldReturnCorrectEpochTime_Nanoseconds() // Arrange // using from ticks here, NanoSeconds is not available in TimeSpan. Nanoseconds Per Tick = 100L. var dateTimeOffset = new DateTimeOffset(2022, 1, 1, 0, 0, 0, 123, TimeSpan.Zero) + TimeSpan.FromMicroseconds(456) + TimeSpan.FromTicks(7); - var expectedEpochTime = 1640995200.123456700; // Epoch time for 2022-01-01 00:00:00.123 + var expectedEpochTime = "1640995200.123456700"; // Epoch time for 2022-01-01 00:00:00.123 // Act var result = dateTimeOffset.ToEpoch(SubSecondPrecision.Nanoseconds); @@ -63,4 +64,4 @@ public void ToEpoch_ShouldReturnCorrectEpochTime_Nanoseconds() Assert.Equal(expectedEpochTime, result); } } -} \ No newline at end of file +} diff --git a/test/Serilog.Sinks.Splunk.Tests/Serilog.Sinks.Splunk.Tests.csproj b/test/Serilog.Sinks.Splunk.Tests/Serilog.Sinks.Splunk.Tests.csproj index 7f73a20..3ab383c 100644 --- a/test/Serilog.Sinks.Splunk.Tests/Serilog.Sinks.Splunk.Tests.csproj +++ b/test/Serilog.Sinks.Splunk.Tests/Serilog.Sinks.Splunk.Tests.csproj @@ -3,25 +3,24 @@ net8.0 Serilog.Sinks.Splunk.Tests - Serilog.Sinks.Splunk.Tests - true - $(PackageTargetFallback);dnxcore50;portable-net45+win8 - 1.0.4 - ../../assets/Serilog.snk - true - true - true true - - + - all - runtime; build; native; contentfiles; analyzers; buildtransitive + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all From e9c5d09578b6e26f9d8fb11b17a08bd43062811a Mon Sep 17 00:00:00 2001 From: EEParker Date: Tue, 26 Mar 2024 10:18:32 -0500 Subject: [PATCH 9/9] Enable all tests in sample app --- sample/Sample/Program.cs | 69 ++++++++++++++++++++-------------------- 1 file changed, 35 insertions(+), 34 deletions(-) diff --git a/sample/Sample/Program.cs b/sample/Sample/Program.cs index 56eacef..459c3a7 100644 --- a/sample/Sample/Program.cs +++ b/sample/Sample/Program.cs @@ -54,43 +54,44 @@ public static async Task Main(string[] args) await Task.Delay(1000); } - // logger.Information("Creating logger {MethodName}.", nameof(OverridingSubsecondPrecisionMicroseconds)); - // OverridingSubsecondPrecisionMicroseconds(eventsToCreate); + logger.Information("Creating logger {MethodName}.", nameof(OverridingSubsecondPrecisionMicroseconds)); + OverridingSubsecondPrecisionMicroseconds(eventsToCreate); + logger.Information("Creating logger {MethodName}.", nameof(OverridingSubsecondPrecisionNanoseconds)); OverridingSubsecondPrecisionNanoseconds(eventsToCreate); - //logger.Information("Creating logger {MethodName}.", nameof(UsingAppSettingsJson)); - //UsingAppSettingsJson(eventsToCreate); - // - //logger.Information("Creating logger {MethodName}.", nameof(UsingHostOnly)); - //UsingHostOnly(eventsToCreate); - // - //logger.Information("Creating logger {MethodName}.", nameof(UsingFullUri)); - //UsingFullUri(eventsToCreate); - // - //logger.Information("Creating logger {MethodName}.", nameof(OverridingSource)); - //OverridingSource(eventsToCreate); - // - //logger.Information("Creating logger {MethodName}.", nameof(OverridingSourceType)); - //OverridingSourceType(eventsToCreate); - // - //logger.Information("Creating logger {MethodName}.", nameof(OverridingHost)); - //OverridingHost(eventsToCreate); - // - //logger.Information("Creating logger {MethodName}.", nameof(WithNoTemplate)); - //WithNoTemplate(eventsToCreate); - // - //logger.Information("Creating logger {MethodName}.", nameof(WithCompactSplunkFormatter)); - //WithCompactSplunkFormatter(eventsToCreate); - // - //if (runSSL) - //{ - // logger.Information("Creating logger {MethodName}.", nameof(UsingSSL)); - // UsingSSL(eventsToCreate); - //} - // - //logger.Information("Creating logger {MethodName}.", nameof(AddCustomFields)); - //AddCustomFields(eventsToCreate); + logger.Information("Creating logger {MethodName}.", nameof(UsingAppSettingsJson)); + UsingAppSettingsJson(eventsToCreate); + + logger.Information("Creating logger {MethodName}.", nameof(UsingHostOnly)); + UsingHostOnly(eventsToCreate); + + logger.Information("Creating logger {MethodName}.", nameof(UsingFullUri)); + UsingFullUri(eventsToCreate); + + logger.Information("Creating logger {MethodName}.", nameof(OverridingSource)); + OverridingSource(eventsToCreate); + + logger.Information("Creating logger {MethodName}.", nameof(OverridingSourceType)); + OverridingSourceType(eventsToCreate); + + logger.Information("Creating logger {MethodName}.", nameof(OverridingHost)); + OverridingHost(eventsToCreate); + + logger.Information("Creating logger {MethodName}.", nameof(WithNoTemplate)); + WithNoTemplate(eventsToCreate); + + logger.Information("Creating logger {MethodName}.", nameof(WithCompactSplunkFormatter)); + WithCompactSplunkFormatter(eventsToCreate); + + if (runSSL) + { + logger.Information("Creating logger {MethodName}.", nameof(UsingSSL)); + UsingSSL(eventsToCreate); + } + + logger.Information("Creating logger {MethodName}.", nameof(AddCustomFields)); + AddCustomFields(eventsToCreate); } finally {