Skip to content
This repository was archived by the owner on Jun 1, 2024. It is now read-only.

Commit c26047a

Browse files
authored
Make sure the sink (and app) do not crash when the ES is unreachable during the discovery of the version. (#359)
1 parent 144369b commit c26047a

File tree

9 files changed

+118
-12
lines changed

9 files changed

+118
-12
lines changed

.ionide/symbolCache.db

8 KB
Binary file not shown.

CHANGES.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
## Changelog
22

3+
8.3
4+
* Do not crash when ES is unreachable and the option `DetectElasticsearchVersion` is set to true.
5+
36
* Disable dot-escaping for field names, because ELK already supports dots in field names.
47
* Support for explicitly setting `Options.TypeName` to `null` this will remove the
58
deprecated `_type` from the bulk payload being sent to Elastic. Earlier an exception was

global.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
{
22
"sdk": {
3-
"version": "2.1.500"
3+
"version": "2.1.807"
44
}
55
}

sample/Serilog.Sinks.Elasticsearch.Sample/Program.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ static void Main(string[] args)
4141
NumberOfReplicas = 1,
4242
NumberOfShards = 2,
4343
//BufferBaseFilename = "./buffer",
44-
RegisterTemplateFailure = RegisterTemplateRecovery.FailSink,
44+
// RegisterTemplateFailure = RegisterTemplateRecovery.FailSink,
4545
FailureCallback = e => Console.WriteLine("Unable to submit event " + e.MessageTemplate),
4646
EmitEventFailure = EmitEventFailureHandling.WriteToSelfLog |
4747
EmitEventFailureHandling.WriteToFailureSink |

src/Serilog.Sinks.Elasticsearch/Sinks/ElasticSearch/ElasticsearchSinkState.cs

Lines changed: 16 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -242,17 +242,25 @@ public void DiscoverClusterVersion()
242242
{
243243
if (!_options.DetectElasticsearchVersion) return;
244244

245-
var response = _client.Cat.Nodes<StringResponse>(new CatNodesRequestParameters()
245+
try
246246
{
247-
Headers = new[] { "v" }
248-
});
249-
if (!response.Success) return;
250247

251-
_discoveredVersion = response.Body.Split(new[] { '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries)
252-
.FirstOrDefault();
248+
var response = _client.Cat.Nodes<StringResponse>(new CatNodesRequestParameters()
249+
{
250+
Headers = new[] { "v" }
251+
});
252+
if (!response.Success) return;
253+
254+
_discoveredVersion = response.Body.Split(new[] { '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries)
255+
.FirstOrDefault();
253256

254-
if (_discoveredVersion?.StartsWith("7.") ?? false)
255-
_options.TypeName = "_doc";
257+
if (_discoveredVersion?.StartsWith("7.") ?? false)
258+
_options.TypeName = "_doc";
259+
}
260+
catch (Exception ex)
261+
{
262+
SelfLog.WriteLine("Failed to discover the cluster version. {0}", ex);
263+
}
256264
}
257265
}
258266
}

test/Serilog.Sinks.Elasticsearch.Tests/ElasticsearchSinkTestsBase.cs

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ public abstract class ElasticsearchSinkTestsBase
2323
protected readonly ElasticsearchSinkOptions _options;
2424
protected List<string> _seenHttpPosts = new List<string>();
2525
protected List<int> _seenHttpHeads = new List<int>();
26+
protected List<Tuple<Uri, int>> _seenHttpGets = new List<Tuple<Uri, int>>();
2627
protected List<Tuple<Uri, string>> _seenHttpPuts = new List<Tuple<Uri, string>>();
2728
private IElasticsearchSerializer _serializer;
2829

@@ -32,10 +33,11 @@ protected ElasticsearchSinkTestsBase()
3233
{
3334
_seenHttpPosts = new List<string>();
3435
_seenHttpHeads = new List<int>();
36+
_seenHttpGets = new List<Tuple<Uri,int>>();
3537
_seenHttpPuts = new List<Tuple<Uri, string>>();
3638

3739
var connectionPool = new SingleNodeConnectionPool(new Uri("http://localhost:9200"));
38-
_connection = new ConnectionStub(_seenHttpPosts, _seenHttpHeads, _seenHttpPuts, () => _templateExistsReturnCode);
40+
_connection = new ConnectionStub(_seenHttpPosts, _seenHttpHeads, _seenHttpPuts, _seenHttpGets, () => _templateExistsReturnCode);
3941
_serializer = JsonNetSerializer.Default(LowLevelRequestResponseSerializer.Instance, new ConnectionSettings(connectionPool, _connection));
4042

4143
_options = new ElasticsearchSinkOptions(connectionPool)
@@ -119,19 +121,22 @@ public class ConnectionStub : InMemoryConnection
119121
{
120122
private Func<int> _templateExistReturnCode;
121123
private List<int> _seenHttpHeads;
124+
private List<Tuple<Uri, int>> _seenHttpGets;
122125
private List<string> _seenHttpPosts;
123126
private List<Tuple<Uri, string>> _seenHttpPuts;
124127

125128
public ConnectionStub(
126129
List<string> _seenHttpPosts,
127130
List<int> _seenHttpHeads,
128131
List<Tuple<Uri, string>> _seenHttpPuts,
132+
List<Tuple<Uri, int>> _seenHttpGets,
129133
Func<int> templateExistReturnCode
130134
)
131135
{
132136
this._seenHttpPosts = _seenHttpPosts;
133137
this._seenHttpHeads = _seenHttpHeads;
134138
this._seenHttpPuts = _seenHttpPuts;
139+
this._seenHttpGets = _seenHttpGets;
135140
this._templateExistReturnCode = templateExistReturnCode;
136141
}
137142

@@ -149,6 +154,9 @@ public override TReturn Request<TReturn>(RequestData requestData)
149154
case HttpMethod.POST:
150155
_seenHttpPosts.Add(Encoding.UTF8.GetString(ms.ToArray()));
151156
break;
157+
case HttpMethod.GET:
158+
_seenHttpGets.Add(Tuple.Create(requestData.Uri, this._templateExistReturnCode()));
159+
break;
152160
case HttpMethod.HEAD:
153161
_seenHttpHeads.Add(this._templateExistReturnCode());
154162
break;
@@ -172,6 +180,9 @@ public override async Task<TResponse> RequestAsync<TResponse>(RequestData reques
172180
case HttpMethod.POST:
173181
_seenHttpPosts.Add(Encoding.UTF8.GetString(ms.ToArray()));
174182
break;
183+
case HttpMethod.GET:
184+
_seenHttpGets.Add(Tuple.Create(requestData.Uri, this._templateExistReturnCode()));
185+
break;
175186
case HttpMethod.HEAD:
176187
_seenHttpHeads.Add(this._templateExistReturnCode());
177188
break;
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
using System;
2+
using System.IO;
3+
using System.Text;
4+
using FluentAssertions;
5+
using Xunit;
6+
using Serilog.Debugging;
7+
8+
namespace Serilog.Sinks.Elasticsearch.Tests.Templating
9+
{
10+
[Collection("isolation")]
11+
public class DiscoverVersionHandlesUnavailableServerTests : ElasticsearchSinkTestsBase
12+
{
13+
[Fact]
14+
public void Should_not_crash_when_server_is_unavaiable()
15+
{
16+
// If this crashes, the test will fail
17+
CreateLoggerThatCrashes();
18+
}
19+
20+
[Fact]
21+
public void Should_write_error_to_self_log()
22+
{
23+
var selfLogMessages = new StringBuilder();
24+
SelfLog.Enable(new StringWriter(selfLogMessages));
25+
26+
// Exception occurs on creation - should be logged
27+
CreateLoggerThatCrashes();
28+
29+
var selfLogContents = selfLogMessages.ToString();
30+
selfLogContents.Should().Contain("Failed to discover the cluster version");
31+
32+
}
33+
34+
private static ILogger CreateLoggerThatCrashes()
35+
{
36+
var loggerConfig = new LoggerConfiguration()
37+
.WriteTo.Elasticsearch(new ElasticsearchSinkOptions(new Uri("http://localhost:9199"))
38+
{
39+
DetectElasticsearchVersion = true
40+
});
41+
42+
return loggerConfig.CreateLogger();
43+
}
44+
}
45+
}
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
using System;
2+
using FluentAssertions;
3+
using Xunit;
4+
5+
namespace Serilog.Sinks.Elasticsearch.Tests.Templating
6+
{
7+
public class DiscoverVersionTests : ElasticsearchSinkTestsBase
8+
{
9+
private readonly Tuple<Uri,int> _templateGet;
10+
11+
public DiscoverVersionTests()
12+
{
13+
_options.DetectElasticsearchVersion = true;
14+
15+
var loggerConfig = new LoggerConfiguration()
16+
.MinimumLevel.Debug()
17+
.Enrich.WithMachineName()
18+
.WriteTo.ColoredConsole()
19+
.WriteTo.Elasticsearch(_options);
20+
21+
var logger = loggerConfig.CreateLogger();
22+
using ((IDisposable) logger)
23+
{
24+
logger.Error("Test exception. Should not contain an embedded exception object.");
25+
}
26+
27+
this._seenHttpGets.Should().NotBeNullOrEmpty().And.HaveCount(1);
28+
_templateGet = this._seenHttpGets[0];
29+
}
30+
31+
32+
[Fact]
33+
public void TemplatePutToCorrectUrl()
34+
{
35+
var uri = _templateGet.Item1;
36+
uri.AbsolutePath.Should().Be("/_cat/nodes");
37+
}
38+
}
39+
}

test/Serilog.Sinks.Elasticsearch.Tests/Templating/SendsTemplateHandlesUnavailableServerTests.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ namespace Serilog.Sinks.Elasticsearch.Tests.Templating
1111
public class SendsTemplateHandlesUnavailableServerTests : ElasticsearchSinkTestsBase
1212
{
1313
[Fact]
14-
public void Should_not_crash_when_server_is_unavaiable()
14+
public void Should_not_crash_when_server_is_unavailable()
1515
{
1616
// If this crashes, the test will fail
1717
CreateLoggerThatCrashes();

0 commit comments

Comments
 (0)