Skip to content

Commit

Permalink
Fixes serilog-contrib#161 subsecond decimal option
Browse files Browse the repository at this point in the history
  • Loading branch information
EEParker committed Feb 23, 2024
1 parent c96c3db commit cd9a440
Show file tree
Hide file tree
Showing 6 changed files with 62 additions and 29 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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;

/// <summary>
/// Construct a <see cref="CompactSplunkJsonFormatter"/>.
Expand All @@ -40,9 +41,13 @@ public class CompactSplunkJsonFormatter : ITextFormatter
/// <param name="host">The host of the event</param>
/// <param name="index">The Splunk index to log to</param>
/// <param name="renderTemplate">If true, the template used will be rendered and written to the output as a property named MessageTemplate</param>
public CompactSplunkJsonFormatter(bool renderTemplate = false, string source = null, string sourceType = null, string host = null, string index = null)
/// <param name="subsecondDecimals">Timestamp sub-second precision</param>

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"

Expand Down Expand Up @@ -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('"');
Expand Down
4 changes: 2 additions & 2 deletions src/Serilog.Sinks.Splunk/Sinks/Splunk/Epoch.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,15 +20,15 @@ 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 <sec>.<ms>.
// 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

return Math.Round((value - Epoch).TotalSeconds, 3, MidpointRounding.AwayFromZero);
return Math.Round((value - Epoch).TotalSeconds, subsecondDecimals, MidpointRounding.AwayFromZero);
}
}
}
16 changes: 11 additions & 5 deletions src/Serilog.Sinks.Splunk/Sinks/Splunk/EventCollectorAuditSink.cs
Original file line number Diff line number Diff line change
Expand Up @@ -53,17 +53,20 @@ public class EventCollectorAuditSink : ILogEventSink
/// <param name="eventCollectorToken">The token to use when authenticating with the event collector</param>
/// <param name="formatProvider">The format provider used when rendering the message</param>
/// <param name="renderTemplate">Whether to render the message template</param>
/// <param name="subsecondDecimals">Timestamp sub-second precision</param>
public EventCollectorAuditSink(
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)
{
}

Expand All @@ -80,7 +83,9 @@ public class EventCollectorAuditSink : ILogEventSink
/// <param name="sourceType">The source type of the event</param>
/// <param name="host">The host of the event</param>
/// <param name="messageHandler">The handler used to send HTTP requests</param>
public EventCollectorAuditSink(
/// <param name="subsecondDecimals">Timestamp sub-second precision</param>
public EventCollectorAuditSink(

string splunkHost,
string eventCollectorToken,
string uriPath,
Expand All @@ -90,12 +95,13 @@ public class EventCollectorAuditSink : ILogEventSink
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)
{
}
Expand Down
19 changes: 13 additions & 6 deletions src/Serilog.Sinks.Splunk/Sinks/Splunk/EventCollectorSink.cs
Original file line number Diff line number Diff line change
Expand Up @@ -59,14 +59,16 @@ public class EventCollectorSink : PeriodicBatchingSink
/// <param name="renderTemplate">Whether to render the message template</param>
/// <param name="batchIntervalInSeconds">The interval in seconds that batching should occur</param>
/// <param name="queueLimit">Maximum number of events in the queue</param>
/// <param name="subsecondDecimals">Timestamp sub-second precision</param>
public EventCollectorSink(
string splunkHost,
string eventCollectorToken,
int batchIntervalInSeconds = 5,
int batchSizeLimit = 100,
int? queueLimit = null,
IFormatProvider formatProvider = null,
bool renderTemplate = true)
bool renderTemplate = true,
int subsecondDecimals = 3)
: this(
splunkHost,
eventCollectorToken,
Expand All @@ -75,7 +77,8 @@ public class EventCollectorSink : PeriodicBatchingSink
batchSizeLimit,
queueLimit,
formatProvider,
renderTemplate)
renderTemplate,
subsecondDecimals: subsecondDecimals)
{
}

Expand All @@ -95,6 +98,7 @@ public class EventCollectorSink : PeriodicBatchingSink
/// <param name="sourceType">The source type of the event</param>
/// <param name="host">The host of the event</param>
/// <param name="messageHandler">The handler used to send HTTP requests</param>
/// <param name="subsecondDecimals">Timestamp sub-second precision</param>
public EventCollectorSink(
string splunkHost,
string eventCollectorToken,
Expand All @@ -108,15 +112,16 @@ public class EventCollectorSink : PeriodicBatchingSink
int? queueLimit,
IFormatProvider formatProvider = null,
bool renderTemplate = true,
HttpMessageHandler messageHandler = null)
HttpMessageHandler messageHandler = null,
int subsecondDecimals = 3)
: this(
splunkHost,
eventCollectorToken,
uriPath,
batchIntervalInSeconds,
batchSizeLimit,
queueLimit,
new SplunkJsonFormatter(renderTemplate, formatProvider, source, sourceType, host, index),
new SplunkJsonFormatter(renderTemplate, formatProvider, source, sourceType, host, index, subsecondDecimals: subsecondDecimals),
messageHandler)
{
}
Expand All @@ -138,6 +143,7 @@ public class EventCollectorSink : PeriodicBatchingSink
/// <param name="sourceType">The source type of the event</param>
/// <param name="host">The host of the event</param>
/// <param name="messageHandler">The handler used to send HTTP requests</param>
/// <param name="subsecondDecimals">Timestamp sub-second precision</param>
public EventCollectorSink(
string splunkHost,
string eventCollectorToken,
Expand All @@ -152,7 +158,8 @@ public class EventCollectorSink : PeriodicBatchingSink
int? queueLimit,
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,
Expand All @@ -161,7 +168,7 @@ public class EventCollectorSink : PeriodicBatchingSink
batchIntervalInSeconds,
batchSizeLimit,
queueLimit,
new SplunkJsonFormatter(renderTemplate, formatProvider, source, sourceType, host, index, fields),
new SplunkJsonFormatter(renderTemplate, formatProvider, source, sourceType, host, index, fields, subsecondDecimals: subsecondDecimals),
messageHandler)
{
}
Expand Down
14 changes: 10 additions & 4 deletions src/Serilog.Sinks.Splunk/Sinks/Splunk/SplunkJsonFormatter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ public class SplunkJsonFormatter : ITextFormatter

private readonly bool _renderTemplate;
private readonly IFormatProvider _formatProvider;
private readonly int _subsecondDecimals;
private readonly string _suffix;

/// <inheritdoc />
Expand All @@ -56,14 +57,16 @@ public class SplunkJsonFormatter : ITextFormatter
/// <param name="source">The source of the event</param>
/// <param name="sourceType">The source type of the event</param>
/// <param name="host">The host of the event</param>
/// <param name="subsecondDecimals">Timestamp sub-second precision</param>
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)
{
}

Expand All @@ -77,17 +80,20 @@ public class SplunkJsonFormatter : ITextFormatter
/// <param name="sourceType">The source type of the event</param>
/// <param name="host">The host of the event</param>
/// <param name="customFields">Object that describes extra splunk fields that should be indexed with event see: http://dev.splunk.com/view/event-collector/SP-CAAAFB6 </param>
/// <param name="subsecondDecimals">Timestamp sub-second precision</param>
public SplunkJsonFormatter(
bool renderTemplate,
IFormatProvider formatProvider,
string source,
string sourceType,
string host,
string index,
CustomFields customFields)
CustomFields customFields,
int subsecondDecimals = 3)
{
_renderTemplate = renderTemplate;
_formatProvider = formatProvider;
_subsecondDecimals = subsecondDecimals;

using (var suffixWriter = new StringWriter())
{
Expand Down Expand Up @@ -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('"');
Expand Down
29 changes: 19 additions & 10 deletions src/Serilog.Sinks.Splunk/SplunkLoggingConfigurationExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ public static class SplunkLoggingConfigurationExtensions
/// <param name="queueLimit">Maximum number of events in the queue</param>
/// <param name="messageHandler">The handler used to send HTTP requests</param>
/// <param name="levelSwitch">A switch allowing the pass-through minimum level to be changed at runtime.</param>
/// <param name="subsecondDecimals">Timestamp sub-second precision</param>
/// <returns></returns>
public static LoggerConfiguration EventCollector(
this LoggerSinkConfiguration configuration,
Expand All @@ -64,7 +65,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));

Expand All @@ -81,7 +83,8 @@ public static class SplunkLoggingConfigurationExtensions
queueLimit,
formatProvider,
renderTemplate,
messageHandler);
messageHandler,
subsecondDecimals: subsecondDecimals);

return configuration.Sink(eventCollectorSink, restrictedToMinimumLevel, levelSwitch);
}
Expand Down Expand Up @@ -152,6 +155,7 @@ public static class SplunkLoggingConfigurationExtensions
/// <param name="messageHandler">The handler used to send HTTP requests</param>
/// <param name="levelSwitch">A switch allowing the pass-through minimum level to be changed at runtime.</param>
/// <param name="fields">Customfields that will be indexed in splunk with this event</param>
/// <param name="subsecondDecimals">Timestamp sub-second precision</param>
/// <returns></returns>
public static LoggerConfiguration EventCollector(
this LoggerSinkConfiguration configuration,
Expand All @@ -170,7 +174,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));

Expand All @@ -188,7 +193,8 @@ public static class SplunkLoggingConfigurationExtensions
queueLimit,
formatProvider,
renderTemplate,
messageHandler
messageHandler,
subsecondDecimals: subsecondDecimals
);

return configuration.Sink(eventCollectorSink, restrictedToMinimumLevel, levelSwitch);
Expand All @@ -210,21 +216,23 @@ public static class SplunkLoggingConfigurationExtensions
/// <param name="renderTemplate">If ture, the message template will be rendered</param>
/// <param name="messageHandler">The handler used to send HTTP requests</param>
/// <param name="levelSwitch">A switch allowing the pass-through minimum level to be changed at runtime.</param>
/// <param name="subsecondDecimals">Timestamp sub-second precision</param>
/// <returns></returns>
public static LoggerConfiguration EventCollector(
this LoggerAuditSinkConfiguration configuration,
string splunkHost,
string eventCollectorToken,
string uriPath = "services/collector",
string source = DefaultSource,
string sourceType = DefaultSourceType,
string host = DefaultHost,
string index = DefaultIndex,
string source = ConfigurationDefaults.DefaultSource,
string sourceType = ConfigurationDefaults.DefaultSourceType,
string host = ConfigurationDefaults.DefaultHost,
string index = ConfigurationDefaults.DefaultIndex,
LogEventLevel restrictedToMinimumLevel = LevelAlias.Minimum,
IFormatProvider formatProvider = null,
bool renderTemplate = true,
HttpMessageHandler messageHandler = null,
LoggingLevelSwitch levelSwitch = null)
LoggingLevelSwitch levelSwitch = null,
int subsecondDecimals = 3)
{
if (configuration == null) throw new ArgumentNullException(nameof(configuration));

Expand All @@ -238,7 +246,8 @@ public static class SplunkLoggingConfigurationExtensions
index,
formatProvider,
renderTemplate,
messageHandler);
messageHandler,
subsecondDecimals: subsecondDecimals);

return configuration.Sink(eventCollectorSink, restrictedToMinimumLevel, levelSwitch);
}
Expand Down

0 comments on commit cd9a440

Please sign in to comment.