From c26047a75e05530ccf951f39d70ca3a73cf99ebf Mon Sep 17 00:00:00 2001 From: Michiel van Oudheusden Date: Sat, 19 Sep 2020 23:19:14 +0200 Subject: [PATCH] Make sure the sink (and app) do not crash when the ES is unreachable during the discovery of the version. (#359) --- .ionide/symbolCache.db | Bin 0 -> 8192 bytes CHANGES.md | 3 ++ global.json | 2 +- .../Program.cs | 2 +- .../ElasticSearch/ElasticsearchSinkState.cs | 24 ++++++---- .../ElasticsearchSinkTestsBase.cs | 13 ++++- ...verVersionHandlesUnavailableServerTests.cs | 45 ++++++++++++++++++ .../Templating/DiscoverVersionTests.cs | 39 +++++++++++++++ ...dsTemplateHandlesUnavailableServerTests.cs | 2 +- 9 files changed, 118 insertions(+), 12 deletions(-) create mode 100644 .ionide/symbolCache.db create mode 100644 test/Serilog.Sinks.Elasticsearch.Tests/Templating/DiscoverVersionHandlesUnavailableServerTests.cs create mode 100644 test/Serilog.Sinks.Elasticsearch.Tests/Templating/DiscoverVersionTests.cs diff --git a/.ionide/symbolCache.db b/.ionide/symbolCache.db new file mode 100644 index 0000000000000000000000000000000000000000..8a73c4f77ed2b2c7b869d888125b966bab395731 GIT binary patch literal 8192 zcmeI#O-sWt7zglV2!b&1=3$rIR1h!v0qnL63T4x^2d~-8SRiRi`xM;Wiyy%6Yw9-e zZLMedKQx3iPtxSqT#`N{hO}7u(kT)*?38iN?u1~B@lbl9?4xNP?Hi$P+Filf^{wcK zEZ%=(;XyzE0uX=z1Rwwb2tWV=5P-mEftNKuJ{t`9>p7`~)#=79ysdUsZ#0)tCPfwv z6Df8tFZw6qTTG1AQ{}YC zozAzDbbjz&5$Upf3)!Y Console.WriteLine("Unable to submit event " + e.MessageTemplate), EmitEventFailure = EmitEventFailureHandling.WriteToSelfLog | EmitEventFailureHandling.WriteToFailureSink | diff --git a/src/Serilog.Sinks.Elasticsearch/Sinks/ElasticSearch/ElasticsearchSinkState.cs b/src/Serilog.Sinks.Elasticsearch/Sinks/ElasticSearch/ElasticsearchSinkState.cs index a1560c9f..f41be74c 100644 --- a/src/Serilog.Sinks.Elasticsearch/Sinks/ElasticSearch/ElasticsearchSinkState.cs +++ b/src/Serilog.Sinks.Elasticsearch/Sinks/ElasticSearch/ElasticsearchSinkState.cs @@ -242,17 +242,25 @@ public void DiscoverClusterVersion() { if (!_options.DetectElasticsearchVersion) return; - var response = _client.Cat.Nodes(new CatNodesRequestParameters() + try { - Headers = new[] { "v" } - }); - if (!response.Success) return; - _discoveredVersion = response.Body.Split(new[] { '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries) - .FirstOrDefault(); + var response = _client.Cat.Nodes(new CatNodesRequestParameters() + { + Headers = new[] { "v" } + }); + if (!response.Success) return; + + _discoveredVersion = response.Body.Split(new[] { '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries) + .FirstOrDefault(); - if (_discoveredVersion?.StartsWith("7.") ?? false) - _options.TypeName = "_doc"; + if (_discoveredVersion?.StartsWith("7.") ?? false) + _options.TypeName = "_doc"; + } + catch (Exception ex) + { + SelfLog.WriteLine("Failed to discover the cluster version. {0}", ex); + } } } } diff --git a/test/Serilog.Sinks.Elasticsearch.Tests/ElasticsearchSinkTestsBase.cs b/test/Serilog.Sinks.Elasticsearch.Tests/ElasticsearchSinkTestsBase.cs index 6fc6c7a3..6fe5c84d 100644 --- a/test/Serilog.Sinks.Elasticsearch.Tests/ElasticsearchSinkTestsBase.cs +++ b/test/Serilog.Sinks.Elasticsearch.Tests/ElasticsearchSinkTestsBase.cs @@ -23,6 +23,7 @@ public abstract class ElasticsearchSinkTestsBase protected readonly ElasticsearchSinkOptions _options; protected List _seenHttpPosts = new List(); protected List _seenHttpHeads = new List(); + protected List> _seenHttpGets = new List>(); protected List> _seenHttpPuts = new List>(); private IElasticsearchSerializer _serializer; @@ -32,10 +33,11 @@ protected ElasticsearchSinkTestsBase() { _seenHttpPosts = new List(); _seenHttpHeads = new List(); + _seenHttpGets = new List>(); _seenHttpPuts = new List>(); var connectionPool = new SingleNodeConnectionPool(new Uri("http://localhost:9200")); - _connection = new ConnectionStub(_seenHttpPosts, _seenHttpHeads, _seenHttpPuts, () => _templateExistsReturnCode); + _connection = new ConnectionStub(_seenHttpPosts, _seenHttpHeads, _seenHttpPuts, _seenHttpGets, () => _templateExistsReturnCode); _serializer = JsonNetSerializer.Default(LowLevelRequestResponseSerializer.Instance, new ConnectionSettings(connectionPool, _connection)); _options = new ElasticsearchSinkOptions(connectionPool) @@ -119,6 +121,7 @@ public class ConnectionStub : InMemoryConnection { private Func _templateExistReturnCode; private List _seenHttpHeads; + private List> _seenHttpGets; private List _seenHttpPosts; private List> _seenHttpPuts; @@ -126,12 +129,14 @@ public ConnectionStub( List _seenHttpPosts, List _seenHttpHeads, List> _seenHttpPuts, + List> _seenHttpGets, Func templateExistReturnCode ) { this._seenHttpPosts = _seenHttpPosts; this._seenHttpHeads = _seenHttpHeads; this._seenHttpPuts = _seenHttpPuts; + this._seenHttpGets = _seenHttpGets; this._templateExistReturnCode = templateExistReturnCode; } @@ -149,6 +154,9 @@ public override TReturn Request(RequestData requestData) case HttpMethod.POST: _seenHttpPosts.Add(Encoding.UTF8.GetString(ms.ToArray())); break; + case HttpMethod.GET: + _seenHttpGets.Add(Tuple.Create(requestData.Uri, this._templateExistReturnCode())); + break; case HttpMethod.HEAD: _seenHttpHeads.Add(this._templateExistReturnCode()); break; @@ -172,6 +180,9 @@ public override async Task RequestAsync(RequestData reques case HttpMethod.POST: _seenHttpPosts.Add(Encoding.UTF8.GetString(ms.ToArray())); break; + case HttpMethod.GET: + _seenHttpGets.Add(Tuple.Create(requestData.Uri, this._templateExistReturnCode())); + break; case HttpMethod.HEAD: _seenHttpHeads.Add(this._templateExistReturnCode()); break; diff --git a/test/Serilog.Sinks.Elasticsearch.Tests/Templating/DiscoverVersionHandlesUnavailableServerTests.cs b/test/Serilog.Sinks.Elasticsearch.Tests/Templating/DiscoverVersionHandlesUnavailableServerTests.cs new file mode 100644 index 00000000..b80446f4 --- /dev/null +++ b/test/Serilog.Sinks.Elasticsearch.Tests/Templating/DiscoverVersionHandlesUnavailableServerTests.cs @@ -0,0 +1,45 @@ +using System; +using System.IO; +using System.Text; +using FluentAssertions; +using Xunit; +using Serilog.Debugging; + +namespace Serilog.Sinks.Elasticsearch.Tests.Templating +{ + [Collection("isolation")] + public class DiscoverVersionHandlesUnavailableServerTests : ElasticsearchSinkTestsBase + { + [Fact] + public void Should_not_crash_when_server_is_unavaiable() + { + // If this crashes, the test will fail + CreateLoggerThatCrashes(); + } + + [Fact] + public void Should_write_error_to_self_log() + { + var selfLogMessages = new StringBuilder(); + SelfLog.Enable(new StringWriter(selfLogMessages)); + + // Exception occurs on creation - should be logged + CreateLoggerThatCrashes(); + + var selfLogContents = selfLogMessages.ToString(); + selfLogContents.Should().Contain("Failed to discover the cluster version"); + + } + + private static ILogger CreateLoggerThatCrashes() + { + var loggerConfig = new LoggerConfiguration() + .WriteTo.Elasticsearch(new ElasticsearchSinkOptions(new Uri("http://localhost:9199")) + { + DetectElasticsearchVersion = true + }); + + return loggerConfig.CreateLogger(); + } + } +} \ No newline at end of file diff --git a/test/Serilog.Sinks.Elasticsearch.Tests/Templating/DiscoverVersionTests.cs b/test/Serilog.Sinks.Elasticsearch.Tests/Templating/DiscoverVersionTests.cs new file mode 100644 index 00000000..7d767744 --- /dev/null +++ b/test/Serilog.Sinks.Elasticsearch.Tests/Templating/DiscoverVersionTests.cs @@ -0,0 +1,39 @@ +using System; +using FluentAssertions; +using Xunit; + +namespace Serilog.Sinks.Elasticsearch.Tests.Templating +{ + public class DiscoverVersionTests : ElasticsearchSinkTestsBase + { + private readonly Tuple _templateGet; + + public DiscoverVersionTests() + { + _options.DetectElasticsearchVersion = true; + + var loggerConfig = new LoggerConfiguration() + .MinimumLevel.Debug() + .Enrich.WithMachineName() + .WriteTo.ColoredConsole() + .WriteTo.Elasticsearch(_options); + + var logger = loggerConfig.CreateLogger(); + using ((IDisposable) logger) + { + logger.Error("Test exception. Should not contain an embedded exception object."); + } + + this._seenHttpGets.Should().NotBeNullOrEmpty().And.HaveCount(1); + _templateGet = this._seenHttpGets[0]; + } + + + [Fact] + public void TemplatePutToCorrectUrl() + { + var uri = _templateGet.Item1; + uri.AbsolutePath.Should().Be("/_cat/nodes"); + } + } +} \ No newline at end of file diff --git a/test/Serilog.Sinks.Elasticsearch.Tests/Templating/SendsTemplateHandlesUnavailableServerTests.cs b/test/Serilog.Sinks.Elasticsearch.Tests/Templating/SendsTemplateHandlesUnavailableServerTests.cs index d549f544..2f1b0a51 100644 --- a/test/Serilog.Sinks.Elasticsearch.Tests/Templating/SendsTemplateHandlesUnavailableServerTests.cs +++ b/test/Serilog.Sinks.Elasticsearch.Tests/Templating/SendsTemplateHandlesUnavailableServerTests.cs @@ -11,7 +11,7 @@ namespace Serilog.Sinks.Elasticsearch.Tests.Templating public class SendsTemplateHandlesUnavailableServerTests : ElasticsearchSinkTestsBase { [Fact] - public void Should_not_crash_when_server_is_unavaiable() + public void Should_not_crash_when_server_is_unavailable() { // If this crashes, the test will fail CreateLoggerThatCrashes();