Skip to content

Commit

Permalink
Bugfix/udp tcp sink fixes #155 (#184)
Browse files Browse the repository at this point in the history
* 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
  • Loading branch information
EEParker authored Apr 9, 2024
1 parent 8f32f6f commit e5482b3
Show file tree
Hide file tree
Showing 13 changed files with 738 additions and 11 deletions.
7 changes: 7 additions & 0 deletions serilog-sinks-splunk.sln
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".github", ".github", "{A9F9
.github\workflows\ci.yml = .github\workflows\ci.yml
EndProjectSection
EndProject
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}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand Down Expand Up @@ -73,6 +75,10 @@ Global
{1B9DEFA3-D600-45FA-93A5-79006076FB5C}.Debug|Any CPU.Build.0 = Debug|Any CPU
{1B9DEFA3-D600-45FA-93A5-79006076FB5C}.Release|Any CPU.ActiveCfg = Release|Any CPU
{1B9DEFA3-D600-45FA-93A5-79006076FB5C}.Release|Any CPU.Build.0 = Release|Any CPU
{9F0EA87D-1D36-47C0-887E-29A1F7DB6979}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{9F0EA87D-1D36-47C0-887E-29A1F7DB6979}.Debug|Any CPU.Build.0 = Debug|Any CPU
{9F0EA87D-1D36-47C0-887E-29A1F7DB6979}.Release|Any CPU.ActiveCfg = Release|Any CPU
{9F0EA87D-1D36-47C0-887E-29A1F7DB6979}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand All @@ -84,6 +90,7 @@ Global
{F74FCFD0-536B-4311-AA66-0BD16112D895} = {7A774CBB-A6E9-4854-B4DB-4CF860B0C1C5}
{FE1504A6-5444-4B87-819C-E6F477662B7F} = {7A774CBB-A6E9-4854-B4DB-4CF860B0C1C5}
{21EEF50A-C0FC-4406-97A1-8F5F499AE2FC} = {1C75E4A9-4CB1-497C-AD17-B438882051A1}
{9F0EA87D-1D36-47C0-887E-29A1F7DB6979} = {B9451AD8-09B9-4C09-A152-FBAE24806614}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {D7BFF439-D18D-4124-A36F-15CFB8E84BCC}
Expand Down
2 changes: 1 addition & 1 deletion src/Serilog.Sinks.Splunk/Serilog.Sinks.Splunk.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
<PackageId>Serilog.Sinks.Splunk</PackageId>
<PackageTags>serilog;splunk;logging;event;collector;hec</PackageTags>
<AssemblyOriginatorKeyFile>../../assets/Serilog.snk</AssemblyOriginatorKeyFile>
<PublicSign Condition=" '$(OS)' != 'Windows_NT' ">true</PublicSign>
<PublicSign Condition=" '$(Configuration)' == 'Debug' ">true</PublicSign>
<SignAssembly>true</SignAssembly>
<RootNamespace>Serilog</RootNamespace>
<IsPackable>true</IsPackable>
Expand Down
11 changes: 5 additions & 6 deletions src/Serilog.Sinks.TCP/Serilog.Sinks.Splunk.TCP.csproj
Original file line number Diff line number Diff line change
@@ -1,21 +1,20 @@
<Project Sdk="Microsoft.NET.Sdk">

<Import Project="../common.props"/>
<Import Project="../common.props" />

<PropertyGroup>
<Description>The Splunk TCP Sink for Serilog</Description>
<TargetFrameworks>netstandard2.1;netstandard2.0</TargetFrameworks>
<TargetFrameworks>netstandard2.1;netstandard2.0;net6.0;net8.0</TargetFrameworks>
<AssemblyName>Serilog.Sinks.Splunk.TCP</AssemblyName>
<PackageId>Serilog.Sinks.Splunk.TCP</PackageId>
<PackageTags>serilog;splunk;logging;tcp</PackageTags>
<AssemblyOriginatorKeyFile>../../assets/Serilog.snk</AssemblyOriginatorKeyFile>
<PublicSign Condition=" '$(OS)' != 'Windows_NT' ">true</PublicSign>
<PublicSign Condition=" '$(Configuration)' == 'Debug' ">true</PublicSign>
<SignAssembly>true</SignAssembly>
<IsPackable>true</IsPackable>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Serilog.Sinks.Splunk" Version="4.0.0" />
<PackageReference Include="Splunk.Logging.Common.Core" Version="1.0.0" />
<ProjectReference Include="..\Serilog.Sinks.Splunk\Serilog.Sinks.Splunk.csproj" />
</ItemGroup>
</Project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
/*
* Copyright 2014 Splunk, Inc.
*
* 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.Net;
using System.Net.Sockets;
using System.Threading;
using System.Threading.Tasks;

namespace Splunk.Logging
{
/// <summary>
/// TcpConnectionPolicy implementation that tries to reconnect after
/// increasingly long intervals.
/// </summary>
/// <remarks>
/// The intervals double every time, starting from 0s, 1s, 2s, 4s, ...
/// until 10 minutes between connections, when it plateaus and does
/// not increase the interval length any further.
/// </remarks>
public class ExponentialBackoffTcpReconnectionPolicy : ITcpReconnectionPolicy
{
private int ceiling = 10 * 60; // 10 minutes in seconds

public Socket Connect(Func<IPAddress, int, Socket> connect, IPAddress host, int port, CancellationToken cancellationToken)

Check warning on line 37 in src/Serilog.Sinks.TCP/Splunk.Logging/ExponentialBackoffTcpReconnectionPolicy.cs

View workflow job for this annotation

GitHub Actions / build

Missing XML comment for publicly visible type or member 'ExponentialBackoffTcpReconnectionPolicy.Connect(Func<IPAddress, int, Socket>, IPAddress, int, CancellationToken)'

Check warning on line 37 in src/Serilog.Sinks.TCP/Splunk.Logging/ExponentialBackoffTcpReconnectionPolicy.cs

View workflow job for this annotation

GitHub Actions / build

Missing XML comment for publicly visible type or member 'ExponentialBackoffTcpReconnectionPolicy.Connect(Func<IPAddress, int, Socket>, IPAddress, int, CancellationToken)'
{
int delay = 1; // in seconds
while (!cancellationToken.IsCancellationRequested)
{
try
{
return connect(host, port);
}
catch (SocketException) { }

// If this is cancelled via the cancellationToken instead of
// completing its delay, the next while-loop test will fail,
// the loop will terminate, and the method will return null
// with no additional connection attempts.
Task.Delay(delay * 1000, cancellationToken).Wait();
// The nth delay is min(10 minutes, 2^n - 1 seconds).
delay = Math.Min((delay + 1) * 2 - 1, ceiling);
}

// cancellationToken has been cancelled.
return null;
}
}
}
82 changes: 82 additions & 0 deletions src/Serilog.Sinks.TCP/Splunk.Logging/FixedSizeQueue.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
/*
* Copyright 2014 Splunk, Inc.
*
* 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.Collections.Concurrent;
using System.Threading;

namespace Splunk.Logging
{
/// <summary>
/// A queue with a maximum size. When the queue is at its maximum size
/// and a new item is queued, the oldest item in the queue is dropped.
/// </summary>
/// <typeparam name="T"></typeparam>
internal class FixedSizeQueue<T>
{
public int Size { get; private set; }
public IProgress<bool> Progress = new Progress<bool>();
public bool IsCompleted { get; private set; }

private readonly BlockingCollection<T> _collection = new BlockingCollection<T>();

public FixedSizeQueue(int size)
: base()
{
Size = size;
IsCompleted = false;
}

public void Enqueue(T obj)
{
lock (this)
{
if (IsCompleted)
{
throw new InvalidOperationException("Tried to add an item to a completed queue.");
}

_collection.Add(obj);

while (_collection.Count > Size)
{
_collection.Take();
}
Progress.Report(true);
}
}

public void CompleteAdding()
{
lock (this)
{
IsCompleted = true;
}
}

public T Dequeue(CancellationToken cancellationToken)
{
return _collection.Take(cancellationToken);
}

public T Dequeue()
{
return _collection.Take();
}


public decimal Count { get { return _collection.Count; } }
}
}
66 changes: 66 additions & 0 deletions src/Serilog.Sinks.TCP/Splunk.Logging/ITcpReconnectionPolicy.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
/*
* Copyright 2014 Splunk, Inc.
*
* 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.Net;
using System.Net.Sockets;
using System.Threading;

namespace Splunk.Logging
{
/// <summary>
/// TcpConnectionPolicy encapsulates a policy for what logging via TCP should
/// do when there is a socket error.
/// </summary>
/// <remarks>
/// TCP loggers in this library (TcpTraceListener and TcpEventSink) take a
/// TcpConnectionPolicy as an argument to their constructor. When the TCP
/// session the logger uses has an error, the logger suspends logging and calls
/// the Reconnect method of an implementation of TcpConnectionPolicy to get a
/// new socket.
/// </remarks>
public interface ITcpReconnectionPolicy
{
// A blocking method that should eventually return a Socket when it finally
// manages to get a connection, or throw a TcpReconnectFailure if the policy
// says to give up trying to connect.
/// <summary>
/// Try to reestablish a TCP connection.
/// </summary>
/// <remarks>
/// The method should block until it either
///
/// 1. succeeds and returns a connected TCP socket, or
/// 2. fails and throws a TcpReconnectFailure exception, or
/// 3. the cancellationToken is cancelled, in which case the method should
/// return null.
///
/// The method takes a zero-parameter function that encapsulates trying to
/// make a single connection and a cancellation token to stop the method
/// if the logging system that invoked it is disposed.
///
/// For example, the default ExponentialBackoffTcpConnectionPolicy invokes
/// connect after increasingly long intervals until it makes a successful
/// connnection, or is cancelled by the cancellationToken, at which point
/// it returns null.
/// </remarks>
/// <param name="connect">A zero-parameter function that tries once to
/// establish a connection.</param>
/// <param name="cancellationToken">A token used to cancel the reconnect
/// attempt when the invoking logger is disposed.</param>
/// <returns>A connected TCP socket.</returns>
Socket Connect(Func<IPAddress, int, Socket> connect, IPAddress host, int port, CancellationToken cancellationToken);

Check warning on line 64 in src/Serilog.Sinks.TCP/Splunk.Logging/ITcpReconnectionPolicy.cs

View workflow job for this annotation

GitHub Actions / build

Parameter 'host' has no matching param tag in the XML comment for 'ITcpReconnectionPolicy.Connect(Func<IPAddress, int, Socket>, IPAddress, int, CancellationToken)' (but other parameters do)

Check warning on line 64 in src/Serilog.Sinks.TCP/Splunk.Logging/ITcpReconnectionPolicy.cs

View workflow job for this annotation

GitHub Actions / build

Parameter 'port' has no matching param tag in the XML comment for 'ITcpReconnectionPolicy.Connect(Func<IPAddress, int, Socket>, IPAddress, int, CancellationToken)' (but other parameters do)

Check warning on line 64 in src/Serilog.Sinks.TCP/Splunk.Logging/ITcpReconnectionPolicy.cs

View workflow job for this annotation

GitHub Actions / build

Parameter 'host' has no matching param tag in the XML comment for 'ITcpReconnectionPolicy.Connect(Func<IPAddress, int, Socket>, IPAddress, int, CancellationToken)' (but other parameters do)

Check warning on line 64 in src/Serilog.Sinks.TCP/Splunk.Logging/ITcpReconnectionPolicy.cs

View workflow job for this annotation

GitHub Actions / build

Parameter 'port' has no matching param tag in the XML comment for 'ITcpReconnectionPolicy.Connect(Func<IPAddress, int, Socket>, IPAddress, int, CancellationToken)' (but other parameters do)
}
}
Loading

0 comments on commit e5482b3

Please sign in to comment.