Skip to content

Commit

Permalink
Merge pull request #2 from manan-habib/main
Browse files Browse the repository at this point in the history
Http Client Metrics package Implementation
  • Loading branch information
phnx47 authored Dec 14, 2023
2 parents 45629b2 + bc62db9 commit ab07e84
Show file tree
Hide file tree
Showing 23 changed files with 528 additions and 76 deletions.
33 changes: 0 additions & 33 deletions .github/sync.yml

This file was deleted.

24 changes: 0 additions & 24 deletions .github/workflows/sync.yml

This file was deleted.

31 changes: 31 additions & 0 deletions Prometheus.Client.HttpClient.sln
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.7.34003.232
MinimumVisualStudioVersion = 10.0.40219.1
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Prometheus.Client.HttpClient", "src\Prometheus.Client.HttpClient\Prometheus.Client.HttpClient.csproj", "{C4504E36-2F22-4BAC-8772-2BF1AC960537}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Prometheus.Client.HttpClient.Tests", "tests\Prometheus.Client.HttpClient.Tests\Prometheus.Client.HttpClient.Tests.csproj", "{37ECBCA1-9464-49B8-8D6B-95D0FC050E92}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{C4504E36-2F22-4BAC-8772-2BF1AC960537}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{C4504E36-2F22-4BAC-8772-2BF1AC960537}.Debug|Any CPU.Build.0 = Debug|Any CPU
{C4504E36-2F22-4BAC-8772-2BF1AC960537}.Release|Any CPU.ActiveCfg = Release|Any CPU
{C4504E36-2F22-4BAC-8772-2BF1AC960537}.Release|Any CPU.Build.0 = Release|Any CPU
{37ECBCA1-9464-49B8-8D6B-95D0FC050E92}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{37ECBCA1-9464-49B8-8D6B-95D0FC050E92}.Debug|Any CPU.Build.0 = Debug|Any CPU
{37ECBCA1-9464-49B8-8D6B-95D0FC050E92}.Release|Any CPU.ActiveCfg = Release|Any CPU
{37ECBCA1-9464-49B8-8D6B-95D0FC050E92}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {336BF78E-D206-4FCB-A5AE-013A0AFDEB04}
EndGlobalSection
EndGlobal
Binary file added Prometheus.Client.HttpClient.snk
Binary file not shown.
10 changes: 5 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
# prom-tmpl
# prom-client-httpclient

[![sync](https://img.shields.io/github/actions/workflow/status/prom-client-net/prom-tmpl/sync.yml?branch=main&label=sync&logo=github&style=flat-square)](https://github.com/prom-client-net/prom-tmpl/actions/workflows/sync.yml)
[![license](https://img.shields.io/github/license/prom-client-net/prom-tmpl?style=flat-square)](https://github.com/prom-client-net/prom-tmpl/blob/main/LICENSE)
[![ci](https://img.shields.io/github/actions/workflow/status/prom-client-net/prom-client-httpclient/ci.yml?branch=main&label=ci&logo=github&style=flat-square)](https://github.com/prom-client-net/prom-client-httpclient/actions/workflows/ci.yml)
[![license](https://img.shields.io/github/license/prom-client-net/prom-client-httpclient?style=flat-square)](https://github.com/prom-client-net/prom-client-httpclient/blob/main/LICENSE)

## Contribute

Contributions to the package are always welcome!

* Report any bugs or issues you find on the [issue tracker](https://github.com/prom-client-net/prom-tmpl/issues).
* You can grab the source code at the package's [git repository](https://github.com/prom-client-net/prom-tmpl).
* Report any bugs or issues you find on the [issue tracker](https://github.com/prom-client-net/prom-client-httpclient/issues).
* You can grab the source code at the package's [git repository](https://github.com/prom-client-net/prom-client-httpclient).

## License

Expand Down
12 changes: 0 additions & 12 deletions run-me.sh

This file was deleted.

1 change: 0 additions & 1 deletion src/.gitkeep

This file was deleted.

23 changes: 23 additions & 0 deletions src/Prometheus.Client.HttpClient/Constants.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
using System;
using System.Collections.Generic;
using System.Text;

namespace Prometheus.Client.HttpClient
{
public static class Constants
{
public const string Method = "method";
public const string Host = "host";
public const string Client = "client";
public const string StatusCode = "status_code";

public const string CountMetricName = "httpclient_requests_sent_total";
public const string CountMetricHelp = "Count of HTTP requests that have been completed by an HttpClient.";
public const string InProgresMetricName = "httpclient_requests_in_progress";
public const string InProgresMetricHelp = "Number of requests currently being executed by an HttpClient.";
public const string RequestDurationMetricName = "httpclient_request_duration_seconds";
public const string RequestDurationMetricHelp = "Duration histogram of HTTP requests performed by an HttpClient.";
public const string ResponseDurationMetricName = "httpclient_response_duration_seconds";
public const string ResponseDurationMetricHelp = "Duration histogram of HTTP requests performed by an HttpClient, measuring the duration until the HTTP response finished being processed.";
}
}
47 changes: 47 additions & 0 deletions src/Prometheus.Client.HttpClient/HttpClientMetricsExtensions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
using System;
using System.Collections.Generic;
using System.Text;
using Microsoft.Extensions.DependencyInjection;
using Prometheus.Client.Collectors;
using Prometheus.Client.HttpClient.MessageHandlers;

namespace Prometheus.Client.HttpClient
{
public static class HttpClientMetricsExtensions
{
/// <summary>
/// Configures the HttpClient pipeline to collect Prometheus metrics.
/// </summary>
public static IHttpClientBuilder AddHttpClientMetrics(this IHttpClientBuilder builder, Action<HttpClientMetricsOptions> configure)
{
var options = new HttpClientMetricsOptions();

configure?.Invoke(options);

builder.AddHttpClientMetrics(options);

return builder;
}

/// <summary>
/// Configures the HttpClient pipeline to collect Prometheus metrics.
/// </summary>
public static IHttpClientBuilder AddHttpClientMetrics(this IHttpClientBuilder builder, HttpClientMetricsOptions options = null)
{
options ??= new HttpClientMetricsOptions();

builder = builder.AddHttpMessageHandler(sp =>
{
options.CollectorRegistry ??= (ICollectorRegistry)sp.GetService(typeof(ICollectorRegistry)) ?? Metrics.DefaultCollectorRegistry;
return new HttpClientInProgressHandler(new MetricFactory(options.CollectorRegistry), builder.Name);
});
builder = builder.AddHttpMessageHandler(sp =>
{
options.CollectorRegistry ??= (ICollectorRegistry)sp.GetService(typeof(ICollectorRegistry)) ?? Metrics.DefaultCollectorRegistry;
return new HttpClientRequestDurationHandler(new MetricFactory(options.CollectorRegistry), builder.Name);
});

return builder;
}
}
}
16 changes: 16 additions & 0 deletions src/Prometheus.Client.HttpClient/HttpClientMetricsOptions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
using System;
using System.Collections.Generic;
using System.Text;
using Prometheus.Client.Collectors;

namespace Prometheus.Client.HttpClient
{
public class HttpClientMetricsOptions
{
/// <summary>
/// Allows you to override the registry for default metric.
/// Value will be ignored if a custom metric is set.
/// </summary>
public ICollectorRegistry CollectorRegistry { get; set; }
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
using System;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;

namespace Prometheus.Client.HttpClient.MessageHandlers
{
public class HttpClientInProgressHandler : HttpClientMessageHandlerBase<IMetricFamily<IGauge, ValueTuple<string, string, string, string>>, IGauge>
{
public HttpClientInProgressHandler(IMetricFactory metricFactory, string clientName)
: base(metricFactory, clientName)
{
}

protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
HttpResponseMessage response = null;
var metric = WithLabels(request, null);

metric.Inc();

try
{
response = await base.SendAsync(request, cancellationToken);
}
finally
{
metric.Dec();
}

return response;
}

protected override IMetricFamily<IGauge, ValueTuple<string, string, string, string>> CreateMetricInstance() => MetricFactory.CreateGauge(Constants.InProgresMetricName, Constants.InProgresMetricHelp, (Constants.Host, Constants.Client, Constants.Method, Constants.StatusCode));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
using System;
using System.Net.Http;

namespace Prometheus.Client.HttpClient.MessageHandlers
{
public abstract class HttpClientMessageHandlerBase<TMetricFamily, TMetric> : DelegatingHandler
where TMetricFamily: IMetricFamily<TMetric, ValueTuple<string, string, string, string>>
where TMetric : IMetric
{
private readonly string _clientName;
private readonly TMetricFamily _metric;
protected readonly IMetricFactory MetricFactory;
protected abstract TMetricFamily CreateMetricInstance();
protected HttpClientMessageHandlerBase(IMetricFactory metricFactory, string clientName)
{
_clientName = clientName;
MetricFactory = metricFactory;
_metric = CreateMetricInstance();
}

protected TMetric WithLabels(HttpRequestMessage httpRequest, HttpResponseMessage httpResponse)
{
var host = httpRequest?.RequestUri?.Host ?? string.Empty;
var method = httpRequest?.Method?.Method ?? string.Empty;
var statusCode = GetStatusCode(httpResponse);

return _metric.WithLabels((host, _clientName, method, statusCode));
}

private string GetStatusCode(HttpResponseMessage httpResponse)
{
if (httpResponse == null)
return string.Empty;

return Convert.ToString((int)httpResponse.StatusCode);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
using System;
using System.Diagnostics;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;

namespace Prometheus.Client.HttpClient.MessageHandlers
{
public class HttpClientRequestDurationHandler : HttpClientMessageHandlerBase<IMetricFamily<IHistogram, ValueTuple<string,string,string,string>>, IHistogram>
{
public HttpClientRequestDurationHandler(IMetricFactory metricFactory, string clientName)
: base(metricFactory, clientName)
{
}

protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
{
var stopWatch = Stopwatch.StartNew();

HttpResponseMessage response = null;

try
{
response = await base.SendAsync(request, cancellationToken);
return response;
}
finally
{
stopWatch.Stop();

WithLabels(request, response).Observe(stopWatch.Elapsed.TotalSeconds);
}
}

protected override IMetricFamily<IHistogram, ValueTuple<string, string, string, string>> CreateMetricInstance() => MetricFactory.CreateHistogram(Constants.RequestDurationMetricName, Constants.RequestDurationMetricHelp, (Constants.Host, Constants.Client, Constants.Method, Constants.StatusCode));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFrameworks>netstandard2.1;net6.0;net7.0</TargetFrameworks>
<SignAssembly>False</SignAssembly>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Prometheus.Client" Version="5.2.0" />
</ItemGroup>

<ItemGroup Condition="'$(TargetFramework)' == 'netstandard2.1'">
<PackageReference Include="Microsoft.Extensions.Http" Version="3.1.32" />
</ItemGroup>
<ItemGroup Condition="'$(TargetFramework)' == 'net6.0'">
<PackageReference Include="Microsoft.Extensions.Http" Version="6.0.0" />
</ItemGroup>
<ItemGroup Condition="'$(TargetFramework)' == 'net7.0'">
<PackageReference Include="Microsoft.Extensions.Http" Version="7.0.0" />
</ItemGroup>

<ItemGroup>
<InternalsVisibleTo Include="$(MSBuildProjectName).Tests, PublicKey=002400000480000094000000060200000024000052534131000400000100010005cd7fff58a5f07dd84ba50975028ab8f3e6d86aa42579628f27012bdcb335ab71f06ec08a39d4308e0be6d63507b9afdd4763e7625251a68912bfadb5b411627a397e6f70a20eb28f5c841e8e8354fc19a1d538ee079575a59678edc98597f98f659b697c46d5f1ca1f1b0b8be057c9eb0a1496a2da78074760026ee98a3ceb" />
</ItemGroup>

</Project>
1 change: 0 additions & 1 deletion tests/.gitkeep

This file was deleted.

1 change: 1 addition & 0 deletions tests/Prometheus.Client.HttpClient.Tests/GlobalUsings.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
global using Xunit;
Loading

0 comments on commit ab07e84

Please sign in to comment.