From c13909289720b09272b644fbb55b3368edfc01f7 Mon Sep 17 00:00:00 2001 From: mauricio-ddelc Date: Thu, 27 Feb 2020 21:33:54 +0100 Subject: [PATCH 1/4] update to beta version of periodic batching --- .../Honeycomb.Serilog.Sink.csproj | 2 +- .../HoneycombSerilogSink.cs | 20 ++++++++---------- .../HoneycombSinkExtensions.cs | 21 ++++++++++++++++++- .../HoneycombSerilogSinkStub.cs | 8 +++---- .../HoneycombSerilogSinkTests.cs | 3 ++- 5 files changed, 35 insertions(+), 19 deletions(-) diff --git a/src/Honeycomb.Serilog.Sink/Honeycomb.Serilog.Sink.csproj b/src/Honeycomb.Serilog.Sink/Honeycomb.Serilog.Sink.csproj index f9e90c2..5efda88 100644 --- a/src/Honeycomb.Serilog.Sink/Honeycomb.Serilog.Sink.csproj +++ b/src/Honeycomb.Serilog.Sink/Honeycomb.Serilog.Sink.csproj @@ -49,7 +49,7 @@ - + diff --git a/src/Honeycomb.Serilog.Sink/HoneycombSerilogSink.cs b/src/Honeycomb.Serilog.Sink/HoneycombSerilogSink.cs index 232aa3a..6253d8e 100644 --- a/src/Honeycomb.Serilog.Sink/HoneycombSerilogSink.cs +++ b/src/Honeycomb.Serilog.Sink/HoneycombSerilogSink.cs @@ -13,7 +13,7 @@ namespace Honeycomb.Serilog.Sink { - internal class HoneycombSerilogSink : PeriodicBatchingSink + internal class HoneycombSerilogSink : IBatchedLogEventSink { #if NETCOREAPP private static readonly SocketsHttpHandler _socketsHttpHandler = new SocketsHttpHandler { PooledConnectionLifetime = TimeSpan.FromMinutes(30) }; @@ -22,28 +22,21 @@ internal class HoneycombSerilogSink : PeriodicBatchingSink private static readonly Lazy _clientBuilder = new Lazy(BuildHttpClient); protected virtual HttpClient Client => _clientBuilder.Value; #endif - private static readonly Uri _honeycombApiUrl = new Uri("https://api.honeycomb.io/"); - private readonly string _apiKey; - private readonly string _teamId; + private static readonly Uri _honeycombApiUrl = new Uri("https://api.honeycomb.io/"); /// The name of the team to submit the events to /// The API key given in the Honeycomb ui /// The maximum number of events to include in a single batch. /// The time to wait between checking for event batches. - public HoneycombSerilogSink( - string teamId, - string apiKey, - int batchSizeLimit, - TimeSpan period) - : base(batchSizeLimit, period) + public HoneycombSerilogSink(string teamId, string apiKey) { _teamId = string.IsNullOrWhiteSpace(teamId) ? throw new ArgumentNullException(nameof(teamId)) : teamId; _apiKey = string.IsNullOrWhiteSpace(apiKey) ? throw new ArgumentNullException(nameof(apiKey)) : apiKey; } - protected override async Task EmitBatchAsync(IEnumerable events) + public async Task EmitBatchAsync(IEnumerable events) { using (TextWriter writer = new StringWriter()) { @@ -110,5 +103,10 @@ private static HttpClient BuildHttpClient() return client; } + + public Task OnEmptyBatchAsync() + { + return Task.CompletedTask; + } } } diff --git a/src/Honeycomb.Serilog.Sink/HoneycombSinkExtensions.cs b/src/Honeycomb.Serilog.Sink/HoneycombSinkExtensions.cs index e6da33f..4cb8eeb 100644 --- a/src/Honeycomb.Serilog.Sink/HoneycombSinkExtensions.cs +++ b/src/Honeycomb.Serilog.Sink/HoneycombSinkExtensions.cs @@ -2,6 +2,7 @@ using Serilog; using Serilog.Configuration; +using Serilog.Sinks.PeriodicBatching; namespace Honeycomb.Serilog.Sink { @@ -17,7 +18,25 @@ public static LoggerConfiguration HoneycombSink(this LoggerSinkConfiguration log int batchSizeLimit, TimeSpan period) { - return loggerConfiguration.Sink(new HoneycombSerilogSink(teamId, apiKey, batchSizeLimit, period)); + var batchingOptions = new PeriodicBatchingSinkOptions + { + BatchSizeLimit = batchSizeLimit, + Period = period + }; + + return loggerConfiguration.HoneycombSink(teamId, apiKey, batchingOptions); + } + + public static LoggerConfiguration HoneycombSink(this LoggerSinkConfiguration loggerConfiguration, + string teamId, + string apiKey, + PeriodicBatchingSinkOptions batchingOptions) + { + var honeycombSink = new HoneycombSerilogSink(teamId, apiKey); + + var batchingSink = new PeriodicBatchingSink(honeycombSink, batchingOptions); + + return loggerConfiguration.Sink(batchingSink); } } } diff --git a/test/Honeycomb.Serilog.Sink.Tests/HoneycombSerilogSinkStub.cs b/test/Honeycomb.Serilog.Sink.Tests/HoneycombSerilogSinkStub.cs index f8e788e..fd5f4d8 100644 --- a/test/Honeycomb.Serilog.Sink.Tests/HoneycombSerilogSinkStub.cs +++ b/test/Honeycomb.Serilog.Sink.Tests/HoneycombSerilogSinkStub.cs @@ -1,6 +1,4 @@ -using System; -using System.Collections.Generic; -using System.Net.Http; +using System.Net.Http; using System.Threading.Tasks; using Serilog.Events; @@ -11,8 +9,8 @@ internal class HoneycombSerilogSinkStub : HoneycombSerilogSink { private readonly HttpClient _client; - public HoneycombSerilogSinkStub(HttpClient client, string teamId, string apiKey, int batchSizeLimit, TimeSpan period) - : base(teamId, apiKey, batchSizeLimit, period) + public HoneycombSerilogSinkStub(HttpClient client, string teamId, string apiKey) + : base(teamId, apiKey) { _client = client; } diff --git a/test/Honeycomb.Serilog.Sink.Tests/HoneycombSerilogSinkTests.cs b/test/Honeycomb.Serilog.Sink.Tests/HoneycombSerilogSinkTests.cs index 8cd03f1..caea00e 100644 --- a/test/Honeycomb.Serilog.Sink.Tests/HoneycombSerilogSinkTests.cs +++ b/test/Honeycomb.Serilog.Sink.Tests/HoneycombSerilogSinkTests.cs @@ -12,6 +12,7 @@ using Serilog.Events; using Serilog.Parsing; +using Serilog.Sinks.PeriodicBatching; using Xunit; @@ -187,7 +188,7 @@ public async Task Emit_GivenAMessageWithProperties_SendsThemAllAsync() private HoneycombSerilogSinkStub CreateSut(string teamId, string apiKey, HttpClient client = null) { - return new HoneycombSerilogSinkStub(client, teamId, apiKey, 1, TimeSpan.FromMilliseconds(1)); + return new HoneycombSerilogSinkStub(client, teamId, apiKey); } } } From 9f3da6ce3cf8c448f114af5070849f83c69fafc1 Mon Sep 17 00:00:00 2001 From: evilpilaf Date: Sun, 1 Mar 2020 19:12:24 +0100 Subject: [PATCH 2/4] Use release version of Serilog.Sinks.PeriodicBatching 2.3.0 --- src/Honeycomb.Serilog.Sink/Honeycomb.Serilog.Sink.csproj | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Honeycomb.Serilog.Sink/Honeycomb.Serilog.Sink.csproj b/src/Honeycomb.Serilog.Sink/Honeycomb.Serilog.Sink.csproj index 5efda88..4d5e767 100644 --- a/src/Honeycomb.Serilog.Sink/Honeycomb.Serilog.Sink.csproj +++ b/src/Honeycomb.Serilog.Sink/Honeycomb.Serilog.Sink.csproj @@ -49,7 +49,7 @@ - + From e3d6bfc55f8530eb5b726aebc39c87952a932770 Mon Sep 17 00:00:00 2001 From: evilpilaf Date: Mon, 9 Mar 2020 15:56:12 +0100 Subject: [PATCH 3/4] Use default periodic batch options when none are given --- src/Honeycomb.Serilog.Sink/HoneycombSinkExtensions.cs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/Honeycomb.Serilog.Sink/HoneycombSinkExtensions.cs b/src/Honeycomb.Serilog.Sink/HoneycombSinkExtensions.cs index 4cb8eeb..54a2cc9 100644 --- a/src/Honeycomb.Serilog.Sink/HoneycombSinkExtensions.cs +++ b/src/Honeycomb.Serilog.Sink/HoneycombSinkExtensions.cs @@ -1,4 +1,4 @@ -using System; +using System; using Serilog; using Serilog.Configuration; @@ -8,6 +8,7 @@ namespace Honeycomb.Serilog.Sink { public static class HoneycombSinkExtensions { + /// /// The name of the team to submit the events to /// The API key given in the Honeycomb ui /// The maximum number of events to include in a single batch. @@ -30,11 +31,11 @@ public static LoggerConfiguration HoneycombSink(this LoggerSinkConfiguration log public static LoggerConfiguration HoneycombSink(this LoggerSinkConfiguration loggerConfiguration, string teamId, string apiKey, - PeriodicBatchingSinkOptions batchingOptions) + PeriodicBatchingSinkOptions batchingOptions = default) { var honeycombSink = new HoneycombSerilogSink(teamId, apiKey); - var batchingSink = new PeriodicBatchingSink(honeycombSink, batchingOptions); + var batchingSink = new PeriodicBatchingSink(honeycombSink, batchingOptions ?? new PeriodicBatchingSinkOptions()); return loggerConfiguration.Sink(batchingSink); } From 6ee2f9a98ab755b9fd5ac87c5ed8f7bb80b26f66 Mon Sep 17 00:00:00 2001 From: evilpilaf Date: Mon, 9 Mar 2020 15:56:58 +0100 Subject: [PATCH 4/4] Use C#8 language features and extract constant strings --- .../Honeycomb.Serilog.Sink.csproj | 1 + .../HoneycombSerilogSink.cs | 77 +++++++++++++------ 2 files changed, 53 insertions(+), 25 deletions(-) diff --git a/src/Honeycomb.Serilog.Sink/Honeycomb.Serilog.Sink.csproj b/src/Honeycomb.Serilog.Sink/Honeycomb.Serilog.Sink.csproj index 4d5e767..ff1eafc 100644 --- a/src/Honeycomb.Serilog.Sink/Honeycomb.Serilog.Sink.csproj +++ b/src/Honeycomb.Serilog.Sink/Honeycomb.Serilog.Sink.csproj @@ -10,6 +10,7 @@ evilpilaf © $([System.DateTime]::Now.Year) true LICENSE.TXT + latest diff --git a/src/Honeycomb.Serilog.Sink/HoneycombSerilogSink.cs b/src/Honeycomb.Serilog.Sink/HoneycombSerilogSink.cs index 6253d8e..2566aeb 100644 --- a/src/Honeycomb.Serilog.Sink/HoneycombSerilogSink.cs +++ b/src/Honeycomb.Serilog.Sink/HoneycombSerilogSink.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections.Generic; using System.IO; using System.Net.Http; @@ -13,10 +13,19 @@ namespace Honeycomb.Serilog.Sink { - internal class HoneycombSerilogSink : IBatchedLogEventSink + internal class HoneycombSerilogSink : IBatchedLogEventSink, IDisposable { #if NETCOREAPP - private static readonly SocketsHttpHandler _socketsHttpHandler = new SocketsHttpHandler { PooledConnectionLifetime = TimeSpan.FromMinutes(30) }; + private static SocketsHttpHandler _socketsHttpHandler; + + private static SocketsHttpHandler SocketsHttpHandler + { + get + { + return _socketsHttpHandler ??= new SocketsHttpHandler { PooledConnectionLifetime = TimeSpan.FromMinutes(30) }; + } + } + protected virtual HttpClient Client => BuildHttpClient(); #else private static readonly Lazy _clientBuilder = new Lazy(BuildHttpClient); @@ -24,12 +33,17 @@ internal class HoneycombSerilogSink : IBatchedLogEventSink #endif private readonly string _apiKey; private readonly string _teamId; - private static readonly Uri _honeycombApiUrl = new Uri("https://api.honeycomb.io/"); + private static readonly Uri _honeycombApiUrl = new Uri(HoneycombBaseUri); + + private const string JsonContentType = "application/json"; + private const string HoneycombBaseUri = "https://api.honeycomb.io/"; + private const string HoneycombBatchEndpointTemplate = "/1/batch/{0}"; + private const string HoneycombTeamIdHeaderName = "X-Honeycomb-Team"; + + private const string SelfLogMessageText = "Failure sending event to Honeycomb, received {statusCode} response with content {content}"; /// The name of the team to submit the events to /// The API key given in the Honeycomb ui - /// The maximum number of events to include in a single batch. - /// The time to wait between checking for event batches. public HoneycombSerilogSink(string teamId, string apiKey) { _teamId = string.IsNullOrWhiteSpace(teamId) ? throw new ArgumentNullException(nameof(teamId)) : teamId; @@ -38,41 +52,35 @@ public HoneycombSerilogSink(string teamId, string apiKey) public async Task EmitBatchAsync(IEnumerable events) { - using (TextWriter writer = new StringWriter()) - { - BuildLogEvent(events, writer); - await SendBatchedEvents(writer.ToString()); - } + using TextWriter writer = new StringWriter(); + BuildLogEvent(events, writer); + await SendBatchedEvents(writer.ToString()).ConfigureAwait(false); } private async Task SendBatchedEvents(string events) { - var requestMessage = new HttpRequestMessage(HttpMethod.Post, $"/1/batch/{_teamId}") + using var requestMessage = new HttpRequestMessage(HttpMethod.Post, string.Format(HoneycombBatchEndpointTemplate, _teamId)) { - Content = new StringContent(events, Encoding.UTF8, "application/json"), + Content = new StringContent(events, Encoding.UTF8, JsonContentType), Version = new Version(2, 0) }; - requestMessage.Headers.Add("X-Honeycomb-Team", _apiKey); + requestMessage.Headers.Add(HoneycombTeamIdHeaderName, _apiKey); var response = await SendRequest(requestMessage).ConfigureAwait(false); if (!response.IsSuccessStatusCode) { - using (Stream contentStream = await response.Content.ReadAsStreamAsync().ConfigureAwait(false)) - using (var reader = new StreamReader(contentStream)) - { - var responseContent = await reader.ReadToEndAsync().ConfigureAwait(false); - SelfLog.WriteLine("Failure sending event to Honeycomb, received {statusCode} response with content {content}", response.StatusCode, responseContent); - } + using Stream contentStream = await response.Content.ReadAsStreamAsync().ConfigureAwait(false); + using var reader = new StreamReader(contentStream); + var responseContent = await reader.ReadToEndAsync().ConfigureAwait(false); + SelfLog.WriteLine(SelfLogMessageText, response.StatusCode, responseContent); } } private async Task SendRequest(HttpRequestMessage request) { #if NETCOREAPP - using (var client = Client) - { - return await client.SendAsync(request, HttpCompletionOption.ResponseHeadersRead); - } + using var client = Client; + return await client.SendAsync(request, HttpCompletionOption.ResponseHeadersRead); #else return await Client.SendAsync(request, HttpCompletionOption.ResponseHeadersRead); #endif @@ -95,7 +103,7 @@ private static HttpClient BuildHttpClient() { HttpClient client; #if NETCOREAPP - client = new HttpClient(_socketsHttpHandler, disposeHandler: false); + client = new HttpClient(SocketsHttpHandler, disposeHandler: false); #else client = new HttpClient(); #endif @@ -108,5 +116,24 @@ public Task OnEmptyBatchAsync() { return Task.CompletedTask; } + + private void ReleaseUnmanagedResources() + { +#if NETCORE + _socketsHttpHandler?.Dispose(); + _socketsHttpHandler = null; +#endif + } + + public void Dispose() + { + ReleaseUnmanagedResources(); + GC.SuppressFinalize(this); + } + + ~HoneycombSerilogSink() + { + ReleaseUnmanagedResources(); + } } }