From 5c4771f1b38077612c0d4f0a555bac146f5d627e Mon Sep 17 00:00:00 2001 From: Bianco Veigel Date: Tue, 15 Nov 2022 17:28:38 +0100 Subject: [PATCH 1/3] add support for go-carbon http receiver --- Graphite.Test/Graphite.Test.csproj | 10 ++-- Graphite/AbstractCarbonClient.cs | 80 ++++++++++++++++++++++++++++++ Graphite/CarbonClient.cs | 63 ++--------------------- Graphite/CarbonHttpClient.cs | 75 ++++++++++++++++++++++++++++ Graphite/Graphite.csproj | 6 +-- 5 files changed, 166 insertions(+), 68 deletions(-) create mode 100644 Graphite/AbstractCarbonClient.cs create mode 100644 Graphite/CarbonHttpClient.cs diff --git a/Graphite.Test/Graphite.Test.csproj b/Graphite.Test/Graphite.Test.csproj index f928e4b..d2a77d1 100644 --- a/Graphite.Test/Graphite.Test.csproj +++ b/Graphite.Test/Graphite.Test.csproj @@ -1,7 +1,7 @@  - netcoreapp2.1 + net6.0 false ahd.Graphite.Test ahd.Graphite.Test @@ -20,10 +20,10 @@ - - - - + + + + all runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/Graphite/AbstractCarbonClient.cs b/Graphite/AbstractCarbonClient.cs new file mode 100644 index 0000000..0d1c946 --- /dev/null +++ b/Graphite/AbstractCarbonClient.cs @@ -0,0 +1,80 @@ +using System; +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; + +namespace ahd.Graphite +{ + /// + /// Base class for clients for submitting data to carbon + /// + public abstract class AbstractCarbonClient + { + /// + /// Send a single datapoint + /// + /// metric path + /// metric value + /// The token to monitor for cancellation requests. The default value is . + /// + public Task SendAsync(string series, double value, CancellationToken cancellationToken = default(CancellationToken)) + { + return SendAsync(series, value, DateTime.Now, cancellationToken); + } + + /// + /// Send a single datapoint + /// + /// metric path + /// metric value + /// metric timestamp + /// The token to monitor for cancellation requests. The default value is . + /// + public Task SendAsync(string series, double value, DateTime timestamp, CancellationToken cancellationToken = default(CancellationToken)) + { + return SendAsync(new []{new Datapoint(series, value, timestamp)}, cancellationToken); + } + + /// + /// Send a list of datapoints + /// + /// + /// + public Task SendAsync(params Datapoint[] datapoints) + { + return SendAsync(datapoints, CancellationToken.None); + } + + /// + /// Send a list of datapoints + /// + /// The token to monitor for cancellation requests. The default value is . + /// + /// + public Task SendAsync(CancellationToken cancellationToken, params Datapoint[] datapoints) + { + return SendAsync(datapoints, cancellationToken); + } + + /// + /// Send a list of datapoints + /// + /// + /// The token to monitor for cancellation requests. + /// + public Task SendAsync(Datapoint[] datapoints, CancellationToken cancellationToken) + { + ICollection points = datapoints; + return SendAsync(points, cancellationToken); + } + + /// + /// Send a list of datapoints + /// + /// + /// The token to monitor for cancellation requests. The default value is . + /// + public abstract Task SendAsync(ICollection datapoints, CancellationToken cancellationToken = default(CancellationToken)); + + } +} \ No newline at end of file diff --git a/Graphite/CarbonClient.cs b/Graphite/CarbonClient.cs index 02577a9..5f41694 100644 --- a/Graphite/CarbonClient.cs +++ b/Graphite/CarbonClient.cs @@ -9,7 +9,7 @@ namespace ahd.Graphite /// /// Client for submitting data to carbon /// - public class CarbonClient + public class CarbonClient:AbstractCarbonClient { private readonly CarbonConnectionPool _carbonPool; @@ -27,6 +27,7 @@ public CarbonClient():this("localhost") public CarbonClient(string host):this(host, new PlaintextGraphiteFormatter()) { } + /// /// Creates a client with the specified host and formatter /// @@ -63,64 +64,6 @@ public CarbonClient(string host, IGraphiteFormatter formatter) /// Use ip dual stack for sending metrics. Defaults to true. /// public bool UseDualStack { get; set; } - - /// - /// Send a single datapoint - /// - /// metric path - /// metric value - /// The token to monitor for cancellation requests. The default value is . - /// - public Task SendAsync(string series, double value, CancellationToken cancellationToken = default(CancellationToken)) - { - return SendAsync(series, value, DateTime.Now, cancellationToken); - } - - /// - /// Send a single datapoint - /// - /// metric path - /// metric value - /// metric timestamp - /// The token to monitor for cancellation requests. The default value is . - /// - public Task SendAsync(string series, double value, DateTime timestamp, CancellationToken cancellationToken = default(CancellationToken)) - { - return SendAsync(new []{new Datapoint(series, value, timestamp)}, cancellationToken); - } - - /// - /// Send a list of datapoints in up to batches - /// - /// - /// - public Task SendAsync(params Datapoint[] datapoints) - { - return SendAsync(datapoints, CancellationToken.None); - } - - /// - /// Send a list of datapoints in up to batches - /// - /// The token to monitor for cancellation requests. The default value is . - /// - /// - public Task SendAsync(CancellationToken cancellationToken, params Datapoint[] datapoints) - { - return SendAsync(datapoints, cancellationToken); - } - - /// - /// Send a list of datapoints in up to batches - /// - /// - /// The token to monitor for cancellation requests. - /// - public Task SendAsync(Datapoint[] datapoints, CancellationToken cancellationToken) - { - ICollection points = datapoints; - return SendAsync(points, cancellationToken); - } /// /// Send a list of datapoints in up to batches @@ -128,7 +71,7 @@ public Task SendAsync(Datapoint[] datapoints, CancellationToken cancellationToke /// /// The token to monitor for cancellation requests. The default value is . /// - public async Task SendAsync(ICollection datapoints, CancellationToken cancellationToken = default(CancellationToken)) + public override async Task SendAsync(ICollection datapoints, CancellationToken cancellationToken = default(CancellationToken)) { if (datapoints == null || datapoints.Count == 0) throw new ArgumentNullException(nameof(datapoints)); var batches = GetBatches(datapoints); diff --git a/Graphite/CarbonHttpClient.cs b/Graphite/CarbonHttpClient.cs new file mode 100644 index 0000000..eed2edd --- /dev/null +++ b/Graphite/CarbonHttpClient.cs @@ -0,0 +1,75 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Net.Http; +using System.Net.Http.Headers; +using System.Threading; +using System.Threading.Tasks; +using ahd.Graphite.Exceptions; + +namespace ahd.Graphite +{ + /// + /// Client for submitting data to carbon via HTTP using the pickle protocol + /// + public class CarbonHttpClient:AbstractCarbonClient + { + private readonly HttpClient _client; + private readonly PickleGraphiteFormatter _formatter; + + /// + /// Creates a client for localhost + /// + public CarbonHttpClient():this("https://localhost") + { + } + + /// + /// Creates a client with the specified endpoint + /// + /// carbon http endpoint + public CarbonHttpClient(string baseAddress):this(new Uri(baseAddress)) + { + } + + /// + /// Creates a client with the specified endpoint + /// + /// carbon http endpoint + public CarbonHttpClient(Uri baseAddress):this(new HttpClient{BaseAddress = baseAddress}) + { + } + + /// + /// Creates a client using the supplied http client + /// + /// preconfigured http client + public CarbonHttpClient(HttpClient client) + { + _client = client; + _formatter = new PickleGraphiteFormatter(); + } + + /// + public override async Task SendAsync(ICollection datapoints, CancellationToken cancellationToken = default(CancellationToken)) + { + var response = await _client.PostAsync("/", Serialize(datapoints), cancellationToken).ConfigureAwait(false); + await response.EnsureSuccessStatusCodeAsync().ConfigureAwait(false); + } + + private HttpContent Serialize(ICollection datapoints) + { + using (var ms = new MemoryStream()) + { + _formatter.Write(ms, datapoints); + return new ByteArrayContent(ms.ToArray()) + { + Headers = + { + ContentType = new MediaTypeHeaderValue("application/python-pickle") + } + }; + } + } + } +} \ No newline at end of file diff --git a/Graphite/Graphite.csproj b/Graphite/Graphite.csproj index b165682..69218e0 100644 --- a/Graphite/Graphite.csproj +++ b/Graphite/Graphite.csproj @@ -53,9 +53,9 @@ - - - + + + From d3e5c190feb25f145c28eb41d093d365b1f40702 Mon Sep 17 00:00:00 2001 From: Bianco Veigel Date: Tue, 15 Nov 2022 17:31:39 +0100 Subject: [PATCH 2/3] fix default endpoint --- Graphite/CarbonHttpClient.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Graphite/CarbonHttpClient.cs b/Graphite/CarbonHttpClient.cs index eed2edd..34fc19a 100644 --- a/Graphite/CarbonHttpClient.cs +++ b/Graphite/CarbonHttpClient.cs @@ -18,9 +18,9 @@ public class CarbonHttpClient:AbstractCarbonClient private readonly PickleGraphiteFormatter _formatter; /// - /// Creates a client for localhost + /// Creates a client for localhost:2007 /// - public CarbonHttpClient():this("https://localhost") + public CarbonHttpClient():this("http://localhost:2007") { } From 3255a950c470e988257ba38684b9b852ef0706e7 Mon Sep 17 00:00:00 2001 From: Bianco Veigel Date: Tue, 15 Nov 2022 19:01:53 +0100 Subject: [PATCH 3/3] remove pickle header from http content --- Graphite/CarbonHttpClient.cs | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/Graphite/CarbonHttpClient.cs b/Graphite/CarbonHttpClient.cs index 34fc19a..82e6ddd 100644 --- a/Graphite/CarbonHttpClient.cs +++ b/Graphite/CarbonHttpClient.cs @@ -1,11 +1,13 @@ using System; using System.Collections.Generic; using System.IO; +using System.Linq; using System.Net.Http; using System.Net.Http.Headers; using System.Threading; using System.Threading.Tasks; using ahd.Graphite.Exceptions; +using Razorvine.Pickle; namespace ahd.Graphite { @@ -15,7 +17,6 @@ namespace ahd.Graphite public class CarbonHttpClient:AbstractCarbonClient { private readonly HttpClient _client; - private readonly PickleGraphiteFormatter _formatter; /// /// Creates a client for localhost:2007 @@ -47,7 +48,6 @@ public CarbonHttpClient(Uri baseAddress):this(new HttpClient{BaseAddress = baseA public CarbonHttpClient(HttpClient client) { _client = client; - _formatter = new PickleGraphiteFormatter(); } /// @@ -59,10 +59,11 @@ public CarbonHttpClient(HttpClient client) private HttpContent Serialize(ICollection datapoints) { - using (var ms = new MemoryStream()) + using (var pickler = new Pickler()) { - _formatter.Write(ms, datapoints); - return new ByteArrayContent(ms.ToArray()) + var data = datapoints.Select(x => new object[] { x.Series, new object[] { x.UnixTimestamp, x.Value } }); + var pickled = pickler.dumps(data); + return new ByteArrayContent(pickled) { Headers = {