Skip to content

Commit e5482b3

Browse files
authored
Bugfix/udp tcp sink fixes #155 (#184)
* Create unit tests for TCP collector. * Signing assembly breaks Splunk TCP collector. Switch to ProjectReference * Alternative solution Embedding sources from Splunk.Loggin.Common * Add target for net6.0 & net8.0
1 parent 8f32f6f commit e5482b3

File tree

13 files changed

+738
-11
lines changed

13 files changed

+738
-11
lines changed

serilog-sinks-splunk.sln

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".github", ".github", "{A9F9
4343
.github\workflows\ci.yml = .github\workflows\ci.yml
4444
EndProjectSection
4545
EndProject
46+
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Serilog.Sinks.Splunk.TCP.Tests", "test\Serilog.Sinks.Splunk.TCP.Tests\Serilog.Sinks.Splunk.TCP.Tests.csproj", "{9F0EA87D-1D36-47C0-887E-29A1F7DB6979}"
47+
EndProject
4648
Global
4749
GlobalSection(SolutionConfigurationPlatforms) = preSolution
4850
Debug|Any CPU = Debug|Any CPU
@@ -73,6 +75,10 @@ Global
7375
{1B9DEFA3-D600-45FA-93A5-79006076FB5C}.Debug|Any CPU.Build.0 = Debug|Any CPU
7476
{1B9DEFA3-D600-45FA-93A5-79006076FB5C}.Release|Any CPU.ActiveCfg = Release|Any CPU
7577
{1B9DEFA3-D600-45FA-93A5-79006076FB5C}.Release|Any CPU.Build.0 = Release|Any CPU
78+
{9F0EA87D-1D36-47C0-887E-29A1F7DB6979}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
79+
{9F0EA87D-1D36-47C0-887E-29A1F7DB6979}.Debug|Any CPU.Build.0 = Debug|Any CPU
80+
{9F0EA87D-1D36-47C0-887E-29A1F7DB6979}.Release|Any CPU.ActiveCfg = Release|Any CPU
81+
{9F0EA87D-1D36-47C0-887E-29A1F7DB6979}.Release|Any CPU.Build.0 = Release|Any CPU
7682
EndGlobalSection
7783
GlobalSection(SolutionProperties) = preSolution
7884
HideSolutionNode = FALSE
@@ -84,6 +90,7 @@ Global
8490
{F74FCFD0-536B-4311-AA66-0BD16112D895} = {7A774CBB-A6E9-4854-B4DB-4CF860B0C1C5}
8591
{FE1504A6-5444-4B87-819C-E6F477662B7F} = {7A774CBB-A6E9-4854-B4DB-4CF860B0C1C5}
8692
{21EEF50A-C0FC-4406-97A1-8F5F499AE2FC} = {1C75E4A9-4CB1-497C-AD17-B438882051A1}
93+
{9F0EA87D-1D36-47C0-887E-29A1F7DB6979} = {B9451AD8-09B9-4C09-A152-FBAE24806614}
8794
EndGlobalSection
8895
GlobalSection(ExtensibilityGlobals) = postSolution
8996
SolutionGuid = {D7BFF439-D18D-4124-A36F-15CFB8E84BCC}

src/Serilog.Sinks.Splunk/Serilog.Sinks.Splunk.csproj

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
<PackageId>Serilog.Sinks.Splunk</PackageId>
1111
<PackageTags>serilog;splunk;logging;event;collector;hec</PackageTags>
1212
<AssemblyOriginatorKeyFile>../../assets/Serilog.snk</AssemblyOriginatorKeyFile>
13-
<PublicSign Condition=" '$(OS)' != 'Windows_NT' ">true</PublicSign>
13+
<PublicSign Condition=" '$(Configuration)' == 'Debug' ">true</PublicSign>
1414
<SignAssembly>true</SignAssembly>
1515
<RootNamespace>Serilog</RootNamespace>
1616
<IsPackable>true</IsPackable>
Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,20 @@
11
<Project Sdk="Microsoft.NET.Sdk">
22

3-
<Import Project="../common.props"/>
4-
3+
<Import Project="../common.props" />
4+
55
<PropertyGroup>
66
<Description>The Splunk TCP Sink for Serilog</Description>
7-
<TargetFrameworks>netstandard2.1;netstandard2.0</TargetFrameworks>
7+
<TargetFrameworks>netstandard2.1;netstandard2.0;net6.0;net8.0</TargetFrameworks>
88
<AssemblyName>Serilog.Sinks.Splunk.TCP</AssemblyName>
99
<PackageId>Serilog.Sinks.Splunk.TCP</PackageId>
1010
<PackageTags>serilog;splunk;logging;tcp</PackageTags>
1111
<AssemblyOriginatorKeyFile>../../assets/Serilog.snk</AssemblyOriginatorKeyFile>
12-
<PublicSign Condition=" '$(OS)' != 'Windows_NT' ">true</PublicSign>
12+
<PublicSign Condition=" '$(Configuration)' == 'Debug' ">true</PublicSign>
1313
<SignAssembly>true</SignAssembly>
1414
<IsPackable>true</IsPackable>
1515
</PropertyGroup>
1616

1717
<ItemGroup>
18-
<PackageReference Include="Serilog.Sinks.Splunk" Version="4.0.0" />
19-
<PackageReference Include="Splunk.Logging.Common.Core" Version="1.0.0" />
18+
<ProjectReference Include="..\Serilog.Sinks.Splunk\Serilog.Sinks.Splunk.csproj" />
2019
</ItemGroup>
2120
</Project>
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
/*
2+
* Copyright 2014 Splunk, Inc.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License"): you may
5+
* not use this file except in compliance with the License. You may obtain
6+
* a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12+
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13+
* License for the specific language governing permissions and limitations
14+
* under the License.
15+
*/
16+
using System;
17+
using System.Net;
18+
using System.Net.Sockets;
19+
using System.Threading;
20+
using System.Threading.Tasks;
21+
22+
namespace Splunk.Logging
23+
{
24+
/// <summary>
25+
/// TcpConnectionPolicy implementation that tries to reconnect after
26+
/// increasingly long intervals.
27+
/// </summary>
28+
/// <remarks>
29+
/// The intervals double every time, starting from 0s, 1s, 2s, 4s, ...
30+
/// until 10 minutes between connections, when it plateaus and does
31+
/// not increase the interval length any further.
32+
/// </remarks>
33+
public class ExponentialBackoffTcpReconnectionPolicy : ITcpReconnectionPolicy
34+
{
35+
private int ceiling = 10 * 60; // 10 minutes in seconds
36+
37+
public Socket Connect(Func<IPAddress, int, Socket> connect, IPAddress host, int port, CancellationToken cancellationToken)
38+
{
39+
int delay = 1; // in seconds
40+
while (!cancellationToken.IsCancellationRequested)
41+
{
42+
try
43+
{
44+
return connect(host, port);
45+
}
46+
catch (SocketException) { }
47+
48+
// If this is cancelled via the cancellationToken instead of
49+
// completing its delay, the next while-loop test will fail,
50+
// the loop will terminate, and the method will return null
51+
// with no additional connection attempts.
52+
Task.Delay(delay * 1000, cancellationToken).Wait();
53+
// The nth delay is min(10 minutes, 2^n - 1 seconds).
54+
delay = Math.Min((delay + 1) * 2 - 1, ceiling);
55+
}
56+
57+
// cancellationToken has been cancelled.
58+
return null;
59+
}
60+
}
61+
}
Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
/*
2+
* Copyright 2014 Splunk, Inc.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License"): you may
5+
* not use this file except in compliance with the License. You may obtain
6+
* a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12+
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13+
* License for the specific language governing permissions and limitations
14+
* under the License.
15+
*/
16+
using System;
17+
using System.Collections.Concurrent;
18+
using System.Threading;
19+
20+
namespace Splunk.Logging
21+
{
22+
/// <summary>
23+
/// A queue with a maximum size. When the queue is at its maximum size
24+
/// and a new item is queued, the oldest item in the queue is dropped.
25+
/// </summary>
26+
/// <typeparam name="T"></typeparam>
27+
internal class FixedSizeQueue<T>
28+
{
29+
public int Size { get; private set; }
30+
public IProgress<bool> Progress = new Progress<bool>();
31+
public bool IsCompleted { get; private set; }
32+
33+
private readonly BlockingCollection<T> _collection = new BlockingCollection<T>();
34+
35+
public FixedSizeQueue(int size)
36+
: base()
37+
{
38+
Size = size;
39+
IsCompleted = false;
40+
}
41+
42+
public void Enqueue(T obj)
43+
{
44+
lock (this)
45+
{
46+
if (IsCompleted)
47+
{
48+
throw new InvalidOperationException("Tried to add an item to a completed queue.");
49+
}
50+
51+
_collection.Add(obj);
52+
53+
while (_collection.Count > Size)
54+
{
55+
_collection.Take();
56+
}
57+
Progress.Report(true);
58+
}
59+
}
60+
61+
public void CompleteAdding()
62+
{
63+
lock (this)
64+
{
65+
IsCompleted = true;
66+
}
67+
}
68+
69+
public T Dequeue(CancellationToken cancellationToken)
70+
{
71+
return _collection.Take(cancellationToken);
72+
}
73+
74+
public T Dequeue()
75+
{
76+
return _collection.Take();
77+
}
78+
79+
80+
public decimal Count { get { return _collection.Count; } }
81+
}
82+
}
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
/*
2+
* Copyright 2014 Splunk, Inc.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License"): you may
5+
* not use this file except in compliance with the License. You may obtain
6+
* a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12+
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13+
* License for the specific language governing permissions and limitations
14+
* under the License.
15+
*/
16+
using System;
17+
using System.Net;
18+
using System.Net.Sockets;
19+
using System.Threading;
20+
21+
namespace Splunk.Logging
22+
{
23+
/// <summary>
24+
/// TcpConnectionPolicy encapsulates a policy for what logging via TCP should
25+
/// do when there is a socket error.
26+
/// </summary>
27+
/// <remarks>
28+
/// TCP loggers in this library (TcpTraceListener and TcpEventSink) take a
29+
/// TcpConnectionPolicy as an argument to their constructor. When the TCP
30+
/// session the logger uses has an error, the logger suspends logging and calls
31+
/// the Reconnect method of an implementation of TcpConnectionPolicy to get a
32+
/// new socket.
33+
/// </remarks>
34+
public interface ITcpReconnectionPolicy
35+
{
36+
// A blocking method that should eventually return a Socket when it finally
37+
// manages to get a connection, or throw a TcpReconnectFailure if the policy
38+
// says to give up trying to connect.
39+
/// <summary>
40+
/// Try to reestablish a TCP connection.
41+
/// </summary>
42+
/// <remarks>
43+
/// The method should block until it either
44+
///
45+
/// 1. succeeds and returns a connected TCP socket, or
46+
/// 2. fails and throws a TcpReconnectFailure exception, or
47+
/// 3. the cancellationToken is cancelled, in which case the method should
48+
/// return null.
49+
///
50+
/// The method takes a zero-parameter function that encapsulates trying to
51+
/// make a single connection and a cancellation token to stop the method
52+
/// if the logging system that invoked it is disposed.
53+
///
54+
/// For example, the default ExponentialBackoffTcpConnectionPolicy invokes
55+
/// connect after increasingly long intervals until it makes a successful
56+
/// connnection, or is cancelled by the cancellationToken, at which point
57+
/// it returns null.
58+
/// </remarks>
59+
/// <param name="connect">A zero-parameter function that tries once to
60+
/// establish a connection.</param>
61+
/// <param name="cancellationToken">A token used to cancel the reconnect
62+
/// attempt when the invoking logger is disposed.</param>
63+
/// <returns>A connected TCP socket.</returns>
64+
Socket Connect(Func<IPAddress, int, Socket> connect, IPAddress host, int port, CancellationToken cancellationToken);
65+
}
66+
}

0 commit comments

Comments
 (0)