diff --git a/.travis.yml b/.travis.yml index ba8d764..79d1441 100644 --- a/.travis.yml +++ b/.travis.yml @@ -5,7 +5,7 @@ matrix: - os: linux # Ubuntu 14.04 dist: trusty sudo: required - dotnet: 1.0.4 + dotnet: 2.0.0 script: - ./build.sh \ No newline at end of file diff --git a/CHANGES.md b/CHANGES.md index 2d917bb..9434222 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,20 +1,33 @@ -##2.2.0 -- [#47] Tooling updates to VS2017 +## 2.4.0 +- [#62](https://github.com/serilog/serilog-sinks-splunk/issues/62) Default fields added by serilog to splunk +- [#63](https://github.com/serilog/serilog-sinks-splunk/issues/63) Possible thread leak when ILogger instances are disposed -##2.1.2 -- [#43](https://github.com/serilog/serilog-sinks-splunk/pull/43) - Extend sink & static configuration to allow for custom JSON formatter. +## 2.3.0 +- [#59](https://github.com/serilog/serilog-sinks-splunk/issues/59) Added ability to use custom fields with HEC. See http://dev.splunk.com/view/event-collector/SP-CAAAFB6. -##2.1.1 +## 2.2.1 +- [#47](https://github.com/serilog/serilog-sinks-splunk/issues/47) Tooling updates to VS2017 +- [#48](https://github.com/serilog/serilog-sinks-splunk/issues/48) +- [#49](https://github.com/serilog/serilog-sinks-splunk/issues/49) +- [#52](https://github.com/serilog/serilog-sinks-splunk/issues/52) + +## 2.1.3 +- [#45](https://github.com/serilog/serilog-sinks-splunk/issues/45) - Deadlock fix on UI thread. + +## 2.1.2 +- [#43](https://github.com/serilog/serilog-sinks-splunk/issues/43) - Extend sink & static configuration to allow for custom JSON formatter. + +## 2.1.1 - [#38](https://github.com/serilog/serilog-sinks-splunk/issues/38) - Fix for HttpEventlogCollector and sourceType - Clean up of sample app using examples of host, sourcetype, source override -##2.1.0 +## 2.1.0 * Change to use a standalone formatter -* Resolves #32 & #26 by exposing `HttpMessageHandler` -* Resolves #30 by ignoring OSX build and including tests in `build.sh` for TravisCI +* Resolves - [#32](https://github.com/serilog/serilog-sinks-splunk/issues/32) & - [#26](https://github.com/serilog/serilog-sinks-splunk/issues/26) by exposing `HttpMessageHandler` +* Resolves - [#30](https://github.com/serilog/serilog-sinks-splunk/issues/30) by ignoring OSX build and including tests in `build.sh` for TravisCI -##2.0 +## 2.0 - Support for DotNet Core - Event Collector fluent interface changed to `.WriteTo.EventCollector` - Event Collector Sink targeting core @@ -22,22 +35,22 @@ - Updated Event Collector HTTP Client to add URI endpoint to host: "services/collector" if not included. - Event Collector changed to use epoch time [#15](https://github.com/serilog/serilog-sinks-splunk/pull/15) -##1.8 +## 1.8 - Event Collector changed to use epoch time [#15](https://github.com/serilog/serilog-sinks-splunk/pull/15) -##1.7 +## 1.7 - Better support for formatting including [#578](https://github.com/serilog/serilog/issues/578) - Cleanup on Event Collector -##1.6.50 +## 1.6.50 - Streaming support for Event Collector -##1.6.42 +## 1.6.42 - Added support for Splunk 6.3 Event Collector - Deprecated Splunk HTTP Sink using Management Port/API -##1.5.30 +## 1.5.30 - Added switch for template rendering - ##1.5.0 +## 1.5.0 - Moved the sink from its [original location](https://github.com/serilog/serilog) diff --git a/build.sh b/build.sh index 69a9e8d..9c61ab2 100755 --- a/build.sh +++ b/build.sh @@ -5,10 +5,11 @@ dotnet restore for path in src/**/*.csproj; do dotnet build -f netstandard1.1 -c Release ${path} + dotnet build -f netstandard1.3 -c Release ${path} done for path in test/*.Tests/*.csproj; do - dotnet test -f netcoreapp1.0 -c Release ${path} + dotnet test -f netcoreapp2.0 -c Release ${path} done -dotnet build -f netcoreapp1.0 -c Release sample/Sample/Sample.csproj \ No newline at end of file +dotnet build -f netcoreapp2.0 -c Release sample/Sample/Sample.csproj \ No newline at end of file diff --git a/sample/Sample/Program.cs b/sample/Sample/Program.cs index fff3420..f07930c 100644 --- a/sample/Sample/Program.cs +++ b/sample/Sample/Program.cs @@ -47,7 +47,7 @@ private static void WithCompactSplunkFormatter(int eventsToCreate) // Vanilla Test with full uri specified Log.Logger = new LoggerConfiguration() .MinimumLevel.Debug() - .WriteTo.LiterateConsole() + .WriteTo.Console() .WriteTo.EventCollector( SPLUNK_FULL_ENDPOINT, Program.EventCollectorToken, new CompactSplunkJsonFormatter()) @@ -70,7 +70,7 @@ public static void OverridingSource(int eventsToCreate) // Override Source Log.Logger = new LoggerConfiguration() .MinimumLevel.Debug() - .WriteTo.LiterateConsole() + .WriteTo.Console() .WriteTo.EventCollector( SPLUNK_ENDPOINT, Program.EventCollectorToken, @@ -92,7 +92,7 @@ public static void OverridingSourceType(int eventsToCreate) // Override Source Log.Logger = new LoggerConfiguration() .MinimumLevel.Debug() - .WriteTo.LiterateConsole() + .WriteTo.Console() .WriteTo.EventCollector( SPLUNK_ENDPOINT, Program.EventCollectorToken, @@ -114,7 +114,7 @@ public static void OverridingHost(int eventsToCreate) // Override Host Log.Logger = new LoggerConfiguration() .MinimumLevel.Debug() - .WriteTo.LiterateConsole() + .WriteTo.Console() .WriteTo.EventCollector( SPLUNK_ENDPOINT, Program.EventCollectorToken, @@ -136,7 +136,7 @@ public static void UsingFullUri(int eventsToCreate) // Vanilla Test with full uri specified Log.Logger = new LoggerConfiguration() .MinimumLevel.Debug() - .WriteTo.LiterateConsole() + .WriteTo.Console() .WriteTo.EventCollector( SPLUNK_FULL_ENDPOINT, Program.EventCollectorToken) @@ -158,7 +158,7 @@ public static void UsingHostOnly(int eventsToCreate) // Vanilla Tests just host Log.Logger = new LoggerConfiguration() .MinimumLevel.Debug() - .WriteTo.LiterateConsole() + .WriteTo.Console() .WriteTo.EventCollector( SPLUNK_ENDPOINT, Program.EventCollectorToken) @@ -179,7 +179,7 @@ public static void WithNoTemplate(int eventsToCreate) // No Template Log.Logger = new LoggerConfiguration() .MinimumLevel.Debug() - .WriteTo.LiterateConsole() + .WriteTo.Console() .WriteTo.EventCollector( SPLUNK_ENDPOINT, Program.EventCollectorToken, @@ -201,7 +201,7 @@ public static void UsingSSL(int eventsToCreate) // SSL Log.Logger = new LoggerConfiguration() .MinimumLevel.Debug() - .WriteTo.LiterateConsole() + .WriteTo.Console() .WriteTo.EventCollector( SPLUNK_ENDPOINT, Program.EventCollectorToken) @@ -228,7 +228,7 @@ public static void AddCustomFields(int eventsToCreate) // Override Source Log.Logger = new LoggerConfiguration() .MinimumLevel.Debug() - .WriteTo.LiterateConsole() + .WriteTo.Console() .WriteTo.EventCollector( splunkHost: SPLUNK_ENDPOINT , eventCollectorToken: SPLUNK_HEC_TOKEN diff --git a/sample/Sample/Sample.csproj b/sample/Sample/Sample.csproj index ea50423..541aaa2 100644 --- a/sample/Sample/Sample.csproj +++ b/sample/Sample/Sample.csproj @@ -2,12 +2,12 @@ Exe - netcoreapp1.0 + netcoreapp2.0 - - + + diff --git a/src/Serilog.Sinks.Splunk/Serilog.Sinks.Splunk.csproj b/src/Serilog.Sinks.Splunk/Serilog.Sinks.Splunk.csproj index a73a5aa..322d1c9 100644 --- a/src/Serilog.Sinks.Splunk/Serilog.Sinks.Splunk.csproj +++ b/src/Serilog.Sinks.Splunk/Serilog.Sinks.Splunk.csproj @@ -2,9 +2,9 @@ The Splunk Sink for Serilog - 2.3.0 + 2.3.1 Matthew Erbs, Serilog Contributors - net45;netstandard1.1 + net45;netstandard1.1;netstandard1.3 true Serilog.Sinks.Splunk Serilog.Sinks.Splunk @@ -27,15 +27,17 @@ - - - - - + + + + + - + + + diff --git a/src/Serilog.Sinks.Splunk/Sinks/Splunk/EventCollectorSink.cs b/src/Serilog.Sinks.Splunk/Sinks/Splunk/EventCollectorSink.cs index 647fe6b..cdfe05f 100644 --- a/src/Serilog.Sinks.Splunk/Sinks/Splunk/EventCollectorSink.cs +++ b/src/Serilog.Sinks.Splunk/Sinks/Splunk/EventCollectorSink.cs @@ -13,33 +13,30 @@ // limitations under the License. using System; -using System.Collections.Concurrent; using System.Collections.Generic; using System.IO; using System.Linq; using System.Net; using System.Net.Http; -using System.Threading; using System.Threading.Tasks; -using Serilog.Core; using Serilog.Debugging; using Serilog.Events; using Serilog.Formatting; +using Serilog.Sinks.PeriodicBatching; namespace Serilog.Sinks.Splunk { /// /// A sink to log to the Event Collector available in Splunk 6.3 /// - public class EventCollectorSink : ILogEventSink, IDisposable + public class EventCollectorSink : PeriodicBatchingSink { private readonly string _splunkHost; private readonly string _uriPath; - private readonly int _batchSizeLimitLimit; private readonly ITextFormatter _jsonFormatter; - private readonly ConcurrentQueue _queue; private readonly EventCollectorClient _httpClient; + /// /// Taken from Splunk.Logging.Common /// @@ -115,6 +112,7 @@ public EventCollectorSink( messageHandler) { } + /// /// Creates a new instance of the sink with Customfields /// @@ -152,12 +150,11 @@ public EventCollectorSink( uriPath, batchIntervalInSeconds, batchSizeLimit, - new SplunkJsonFormatter(renderTemplate, formatProvider, source, sourceType, host, index,fields), + new SplunkJsonFormatter(renderTemplate, formatProvider, source, sourceType, host, index, fields), messageHandler) { } - /// /// Creates a new instance of the sink /// @@ -176,67 +173,25 @@ public EventCollectorSink( int batchSizeLimit, ITextFormatter jsonFormatter, HttpMessageHandler messageHandler = null) + : base(batchSizeLimit, TimeSpan.FromSeconds(batchIntervalInSeconds)) { _uriPath = uriPath; _splunkHost = splunkHost; - _queue = new ConcurrentQueue(); _jsonFormatter = jsonFormatter; - _batchSizeLimitLimit = batchSizeLimit; - var batchInterval = TimeSpan.FromSeconds(batchIntervalInSeconds); _httpClient = messageHandler != null ? new EventCollectorClient(eventCollectorToken, messageHandler) : new EventCollectorClient(eventCollectorToken); - - var cancellationToken = new CancellationToken(); - - RepeatAction.OnInterval( - batchInterval, - async () => await ProcessQueue(), - cancellationToken); } /// - /// Emits the provided log event from a sink + /// Emit a batch of log events, running asynchronously. /// - /// - public void Emit(LogEvent logEvent) - { - if (logEvent == null) throw new ArgumentNullException(nameof(logEvent)); - - _queue.Enqueue(logEvent); - } - - private async Task ProcessQueue() - { - try - { - do - { - var count = 0; - var events = new Queue(); - LogEvent next; - - while (count < _batchSizeLimitLimit && _queue.TryDequeue(out next)) - { - count++; - events.Enqueue(next); - } - - if (events.Count == 0) - return; - - await Send(events); - - } while (true); - } - catch (Exception ex) - { - SelfLog.WriteLine("Exception while emitting batch from {0}: {1}", this, ex); - } - } - - private async Task Send(IEnumerable events) + /// The events to emit. + /// + /// Override either or , not both. + /// + protected override async Task EmitBatchAsync(IEnumerable events) { var allEvents = new StringWriter(); @@ -248,56 +203,22 @@ private async Task Send(IEnumerable events) var request = new EventCollectorRequest(_splunkHost, allEvents.ToString(), _uriPath); var response = await _httpClient.SendAsync(request).ConfigureAwait(false); - if (response.IsSuccessStatusCode) - { - //Do Nothing? - } - else + if (!response.IsSuccessStatusCode) { //Application Errors sent via HTTP Event Collector if (HttpEventCollectorApplicationErrors.Any(x => x == response.StatusCode)) { + // By not throwing an exception here the PeriodicBatchingSink will assume the batch succeeded and not send it again. SelfLog.WriteLine( "A status code of {0} was received when attempting to send to {1}. The event has been discarded and will not be placed back in the queue.", response.StatusCode.ToString(), _splunkHost); } else { - //Put the item back in the queue & retry on next go - SelfLog.WriteLine( - "A status code of {0} was received when attempting to send to {1}. The event has been placed back in the queue", - response.StatusCode.ToString(), _splunkHost); - - foreach (var logEvent in events) - { - _queue.Enqueue(logEvent); - } + // EnsureSuccessStatusCode will throw an exception and the PeriodicBatchingSink will catch/log the exception and retry the batch. + response.EnsureSuccessStatusCode(); } } } - - /// - public void Dispose() - { - Dispose(true); - } - - /// - protected virtual void Dispose(bool disposing) - { - if (!disposing) return; - - var remainingEvents = new List(); - - while (!_queue.IsEmpty) - { - LogEvent next; - _queue.TryDequeue(out next); - remainingEvents.Add(next); - } - - Send(remainingEvents).Wait(); - _httpClient.Dispose(); - } } } diff --git a/src/Serilog.Sinks.Splunk/Sinks/Splunk/RepeatAction.cs b/src/Serilog.Sinks.Splunk/Sinks/Splunk/RepeatAction.cs deleted file mode 100644 index ee39d6f..0000000 --- a/src/Serilog.Sinks.Splunk/Sinks/Splunk/RepeatAction.cs +++ /dev/null @@ -1,47 +0,0 @@ -// 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. - -using System; -using System.Threading; -using System.Threading.Tasks; - -namespace Serilog.Sinks.Splunk -{ - internal static class RepeatAction - { - public static Task OnInterval(TimeSpan pollInterval, Action action, CancellationToken token, - TaskCreationOptions taskCreationOptions, TaskScheduler taskScheduler) - { - return Task.Factory.StartNew(() => - { - for (;;) - { - if (token.WaitCancellationRequested(pollInterval)) - break; - action(); - } - }, token, taskCreationOptions, taskScheduler); - } - - public static Task OnInterval(TimeSpan pollInterval, Action action, CancellationToken token) - { - return OnInterval(pollInterval, action, token, TaskCreationOptions.LongRunning, TaskScheduler.Default); - } - - public static bool WaitCancellationRequested(this CancellationToken token, TimeSpan timeout) - { - return token.WaitHandle.WaitOne(timeout); - } - } -} \ No newline at end of file diff --git a/src/Serilog.Sinks.Splunk/Sinks/Splunk/SplunkJsonFormatter.cs b/src/Serilog.Sinks.Splunk/Sinks/Splunk/SplunkJsonFormatter.cs index cc6b64f..55b197e 100644 --- a/src/Serilog.Sinks.Splunk/Sinks/Splunk/SplunkJsonFormatter.cs +++ b/src/Serilog.Sinks.Splunk/Sinks/Splunk/SplunkJsonFormatter.cs @@ -22,6 +22,7 @@ namespace Serilog.Sinks.Splunk { + /// /// /// Renders log events into a default JSON format for consumption by Splunk. /// @@ -29,12 +30,13 @@ public class SplunkJsonFormatter : ITextFormatter { static readonly JsonValueFormatter ValueFormatter = new JsonValueFormatter(); - readonly bool _renderTemplate; - readonly IFormatProvider _formatProvider; - readonly string _suffix; + private readonly bool _renderTemplate; + private readonly IFormatProvider _formatProvider; + private readonly string _suffix; + /// /// - /// Construct a . + /// Construct a . /// /// 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 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 93f3e28..40da3da 100644 --- a/test/Serilog.Sinks.Splunk.Tests/Serilog.Sinks.Splunk.Tests.csproj +++ b/test/Serilog.Sinks.Splunk.Tests/Serilog.Sinks.Splunk.Tests.csproj @@ -1,7 +1,7 @@  - net452;netcoreapp1.0 + net452;netcoreapp2.0 Serilog.Sinks.Splunk.Tests Serilog.Sinks.Splunk.Tests true @@ -25,8 +25,12 @@ - - + + + + + +