Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 11 additions & 10 deletions Directory.Build.targets
Original file line number Diff line number Diff line change
Expand Up @@ -22,16 +22,17 @@
<!-- Package versions for package references across all projects -->
<ItemGroup>
<PackageReference Update="coverlet.msbuild" Version="2.8.1" />
<PackageReference Update="Microsoft.Extensions.DependencyInjection" Version="5.0.0-preview.2.20160.3" />
<PackageReference Update="Microsoft.Extensions.Hosting" Version="5.0.0-preview.2.20160.3" />
<PackageReference Update="Microsoft.Extensions.Http" Version="5.0.0-preview.2.20160.3" />
<PackageReference Update="Microsoft.Extensions.Logging.Abstractions" Version="5.0.0-preview.2.20160.3" />
<PackageReference Update="Microsoft.Extensions.Options.ConfigurationExtensions" Version="5.0.0-preview.2.20160.3" />
<PackageReference Update="Microsoft.Net.Compilers.Toolset" Version="3.7.0-1.final" />
<PackageReference Update="Microsoft.NET.Test.Sdk" Version="16.5.0" />
<PackageReference Update="Microsoft.SourceLink.GitHub" Version="1.0.0" />
<PackageReference Update="NUnit" Version="3.12.0" />
<PackageReference Update="NUnit3TestAdapter" Version="3.16.1" />
<PackageReference Update="Microsoft.Extensions.Configuration" Version="5.0.0" />
<PackageReference Update="Microsoft.Extensions.DependencyInjection" Version="5.0.1" />
<PackageReference Update="Microsoft.Extensions.Hosting" Version="5.0.0" />
<PackageReference Update="Microsoft.Extensions.Http" Version="5.0.0" />
<PackageReference Update="Microsoft.Extensions.Logging.Abstractions" Version="5.0.0" />
<PackageReference Update="Microsoft.Extensions.Options.ConfigurationExtensions" Version="5.0.0" />
<PackageReference Update="Microsoft.Net.Compilers.Toolset" Version="3.9.0-3.final" />
<PackageReference Update="Microsoft.NET.Test.Sdk" Version="16.9.0-preview-20210106-01" />
<PackageReference Update="Microsoft.SourceLink.GitHub" Version="1.1.0-beta-20204-02" />
<PackageReference Update="NUnit" Version="3.13.0" />
<PackageReference Update="NUnit3TestAdapter" Version="3.17.0" />
</ItemGroup>

</Project>
48 changes: 48 additions & 0 deletions Finite.Metrics.sln
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,14 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Providers", "Providers", "{
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Finite.Metrics.OpenTsdb.UnitTests", "tests\Providers\OpenTsdb\Finite.Metrics.OpenTsdb.UnitTests.csproj", "{8C2BA2A6-9D4A-432D-B8B1-B07E0E01FB2C}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Prometheus", "Prometheus", "{EB50151D-8AD4-4D58-BE3A-CD5EFE1E0621}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Finite.Metrics.Prometheus.Core", "src\Providers\Prometheus\Core\Finite.Metrics.Prometheus.Core.csproj", "{A4F1FDE6-C192-4F7D-B848-39E462783AAA}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Finite.Metrics.Prometheus.AspNetCore", "src\Providers\Prometheus\AspNetCore\Finite.Metrics.Prometheus.AspNetCore.csproj", "{EADC0DC7-668B-4F94-9304-572B64D3FE67}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Finite.Metrics.Prometheus.HostedService", "src\Providers\Prometheus\HostedService\Finite.Metrics.Prometheus.HostedService.csproj", "{693B8847-B557-4B0D-BDF5-238C1DDF14AE}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand Down Expand Up @@ -122,6 +130,42 @@ Global
{8C2BA2A6-9D4A-432D-B8B1-B07E0E01FB2C}.Release|x64.Build.0 = Release|Any CPU
{8C2BA2A6-9D4A-432D-B8B1-B07E0E01FB2C}.Release|x86.ActiveCfg = Release|Any CPU
{8C2BA2A6-9D4A-432D-B8B1-B07E0E01FB2C}.Release|x86.Build.0 = Release|Any CPU
{A4F1FDE6-C192-4F7D-B848-39E462783AAA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{A4F1FDE6-C192-4F7D-B848-39E462783AAA}.Debug|Any CPU.Build.0 = Debug|Any CPU
{A4F1FDE6-C192-4F7D-B848-39E462783AAA}.Debug|x64.ActiveCfg = Debug|Any CPU
{A4F1FDE6-C192-4F7D-B848-39E462783AAA}.Debug|x64.Build.0 = Debug|Any CPU
{A4F1FDE6-C192-4F7D-B848-39E462783AAA}.Debug|x86.ActiveCfg = Debug|Any CPU
{A4F1FDE6-C192-4F7D-B848-39E462783AAA}.Debug|x86.Build.0 = Debug|Any CPU
{A4F1FDE6-C192-4F7D-B848-39E462783AAA}.Release|Any CPU.ActiveCfg = Release|Any CPU
{A4F1FDE6-C192-4F7D-B848-39E462783AAA}.Release|Any CPU.Build.0 = Release|Any CPU
{A4F1FDE6-C192-4F7D-B848-39E462783AAA}.Release|x64.ActiveCfg = Release|Any CPU
{A4F1FDE6-C192-4F7D-B848-39E462783AAA}.Release|x64.Build.0 = Release|Any CPU
{A4F1FDE6-C192-4F7D-B848-39E462783AAA}.Release|x86.ActiveCfg = Release|Any CPU
{A4F1FDE6-C192-4F7D-B848-39E462783AAA}.Release|x86.Build.0 = Release|Any CPU
{EADC0DC7-668B-4F94-9304-572B64D3FE67}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{EADC0DC7-668B-4F94-9304-572B64D3FE67}.Debug|Any CPU.Build.0 = Debug|Any CPU
{EADC0DC7-668B-4F94-9304-572B64D3FE67}.Debug|x64.ActiveCfg = Debug|Any CPU
{EADC0DC7-668B-4F94-9304-572B64D3FE67}.Debug|x64.Build.0 = Debug|Any CPU
{EADC0DC7-668B-4F94-9304-572B64D3FE67}.Debug|x86.ActiveCfg = Debug|Any CPU
{EADC0DC7-668B-4F94-9304-572B64D3FE67}.Debug|x86.Build.0 = Debug|Any CPU
{EADC0DC7-668B-4F94-9304-572B64D3FE67}.Release|Any CPU.ActiveCfg = Release|Any CPU
{EADC0DC7-668B-4F94-9304-572B64D3FE67}.Release|Any CPU.Build.0 = Release|Any CPU
{EADC0DC7-668B-4F94-9304-572B64D3FE67}.Release|x64.ActiveCfg = Release|Any CPU
{EADC0DC7-668B-4F94-9304-572B64D3FE67}.Release|x64.Build.0 = Release|Any CPU
{EADC0DC7-668B-4F94-9304-572B64D3FE67}.Release|x86.ActiveCfg = Release|Any CPU
{EADC0DC7-668B-4F94-9304-572B64D3FE67}.Release|x86.Build.0 = Release|Any CPU
{693B8847-B557-4B0D-BDF5-238C1DDF14AE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{693B8847-B557-4B0D-BDF5-238C1DDF14AE}.Debug|Any CPU.Build.0 = Debug|Any CPU
{693B8847-B557-4B0D-BDF5-238C1DDF14AE}.Debug|x64.ActiveCfg = Debug|Any CPU
{693B8847-B557-4B0D-BDF5-238C1DDF14AE}.Debug|x64.Build.0 = Debug|Any CPU
{693B8847-B557-4B0D-BDF5-238C1DDF14AE}.Debug|x86.ActiveCfg = Debug|Any CPU
{693B8847-B557-4B0D-BDF5-238C1DDF14AE}.Debug|x86.Build.0 = Debug|Any CPU
{693B8847-B557-4B0D-BDF5-238C1DDF14AE}.Release|Any CPU.ActiveCfg = Release|Any CPU
{693B8847-B557-4B0D-BDF5-238C1DDF14AE}.Release|Any CPU.Build.0 = Release|Any CPU
{693B8847-B557-4B0D-BDF5-238C1DDF14AE}.Release|x64.ActiveCfg = Release|Any CPU
{693B8847-B557-4B0D-BDF5-238C1DDF14AE}.Release|x64.Build.0 = Release|Any CPU
{693B8847-B557-4B0D-BDF5-238C1DDF14AE}.Release|x86.ActiveCfg = Release|Any CPU
{693B8847-B557-4B0D-BDF5-238C1DDF14AE}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(NestedProjects) = preSolution
{9E815B92-5565-46FA-A666-18BC0D5333DA} = {7C493A69-008A-4F4B-9AC1-23A64682C4AB}
Expand All @@ -133,5 +177,9 @@ Global
{4AD95898-6A60-4B74-976A-80DD4BFCAFC5} = {62C62A44-4FCE-4B47-96CE-C9282E374813}
{310165FD-B3CA-49FD-929D-9E8BABAF116B} = {62C62A44-4FCE-4B47-96CE-C9282E374813}
{8C2BA2A6-9D4A-432D-B8B1-B07E0E01FB2C} = {310165FD-B3CA-49FD-929D-9E8BABAF116B}
{EB50151D-8AD4-4D58-BE3A-CD5EFE1E0621} = {13420B5B-28D6-40FF-B378-82E6FB742AB4}
{A4F1FDE6-C192-4F7D-B848-39E462783AAA} = {EB50151D-8AD4-4D58-BE3A-CD5EFE1E0621}
{EADC0DC7-668B-4F94-9304-572B64D3FE67} = {EB50151D-8AD4-4D58-BE3A-CD5EFE1E0621}
{693B8847-B557-4B0D-BDF5-238C1DDF14AE} = {EB50151D-8AD4-4D58-BE3A-CD5EFE1E0621}
EndGlobalSection
EndGlobal
1 change: 1 addition & 0 deletions src/Configuration/Finite.Metrics.Configuration.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
</ItemGroup>

<ItemGroup>
<PackageReference Include="Microsoft.Extensions.Configuration" />
<PackageReference Include="Microsoft.Extensions.Options.ConfigurationExtensions" />
</ItemGroup>

Expand Down
7 changes: 5 additions & 2 deletions src/Core/MetricFactory.cs
Original file line number Diff line number Diff line change
Expand Up @@ -38,10 +38,13 @@ public MetricFactory(IEnumerable<IMetricProvider> providers)
public static IMetricFactory Create(Action<IMetricsBuilder> configure)
{
var collection = new ServiceCollection();
_ = collection.AddMetrics(configure);
var builder = collection.AddMetrics();

configure(builder);

var provider = collection.BuildServiceProvider();

var factory = provider.GetService<IMetricFactory>();
var factory = provider.GetRequiredService<IMetricFactory>();

return new DisposingMetricsFactory(factory, provider);
}
Expand Down
25 changes: 2 additions & 23 deletions src/Core/MetricsServiceCollectionExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -21,28 +21,8 @@ public static class MetricsServiceCollectionExtensions
/// The <see cref="IServiceCollection"/> so that additional calls can
/// be chained.
/// </returns>
public static IServiceCollection AddMetrics(
public static IMetricsBuilder AddMetrics(
this IServiceCollection services)
=> AddMetrics(services, builder => { });


/// <summary>
/// Adds metrics services to the specified
/// <see cref="IServiceCollection"/>.
/// </summary>
/// <param name="services">
/// The <see cref="IServiceCollection"/> to add services to.
/// </param>
/// <param name="configure">
/// The <see cref="IMetricsBuilder"/> configuration delegate.
/// </param>
/// <returns>
/// The <see cref="IServiceCollection"/> so that additional calls can
/// be chained.
/// </returns>
public static IServiceCollection AddMetrics(
this IServiceCollection services,
Action<IMetricsBuilder> configure)
{
if (services is null)
throw new ArgumentNullException(nameof(services));
Expand All @@ -51,8 +31,7 @@ public static IServiceCollection AddMetrics(
.Singleton<IMetricFactory, MetricFactory>());
services.TryAdd(ServiceDescriptor.Singleton<IMetric, Metric>());

configure(new MetricsBuilder(services));
return services;
return new MetricsBuilder(services);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>net5.0</TargetFramework>

<Description>ASP.NET Prometheus metrics provider implementation for Finite.Metrics</Description>
</PropertyGroup>

<ItemGroup>
<ProjectReference Include="../Core/Finite.Metrics.Prometheus.Core.csproj" />
</ItemGroup>

<ItemGroup>
<FrameworkReference Include="Microsoft.AspNetCore.App" />
</ItemGroup>

</Project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
using Finite.Metrics.Prometheus.AspNetCore;
using Microsoft.AspNetCore.Builder;
using Microsoft.Extensions.DependencyInjection;

namespace Finite.Metrics
{
/// <summary>
/// Prometheus extensions for <see cref="IMetricsBuilder"/>.
/// </summary>
public static class MetricsBuilderAspNetCorePrometheusExtensions
{
/// <summary>
/// Adds Prometheus services to the specified
/// <see cref="IMetricsBuilder"/>.
/// </summary>
/// <param name="builder">
/// The <see cref="IMetricsBuilder"/> to add services to.
/// </param>
/// <returns>
/// The <paramref name="builder"/>, to allow for chaining.
/// </returns>
/// <remarks>
/// Remember to call <see cref="PrometheusApplicationBuilderExtensions.UsePrometheus(IApplicationBuilder)"/>
/// to add the ASP.NET middleware to the request pipeline.
/// </remarks>
public static IMetricsBuilder AddPrometheus(
this IMetricsBuilder builder)
{
_ = builder.AddPrometheusCore();

_ = builder.Services.AddTransient<PrometheusMetricsMiddleware>();

return builder;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
using System;
using Finite.Metrics.Prometheus.AspNetCore;
using Microsoft.Extensions.Options;

namespace Microsoft.AspNetCore.Builder
{
/// <summary>
/// Extension methods for Finite.Metrics.Prometheus.
/// </summary>
public static class PrometheusApplicationBuilderExtensions
{
/// <summary>
/// Enables Prometheus metrics endpoints for the current request path.
/// </summary>
/// <param name="app">
/// The <see cref="IApplicationBuilder"/> to enable Prometheus metrics
/// endpoints for.
/// </param>
/// <returns>
/// The <see cref="IApplicationBuilder"/> passed in
/// <paramref name="app"/>, for chaining.
/// </returns>
public static IApplicationBuilder UsePrometheus(
this IApplicationBuilder app)
{
if (app is null)
throw new ArgumentNullException(nameof(app));

return app.UseMiddleware<PrometheusMetricsMiddleware>();
}
}
}
68 changes: 68 additions & 0 deletions src/Providers/Prometheus/AspNetCore/PrometheusMetricsMiddleware.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
using System;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;

namespace Finite.Metrics.Prometheus.AspNetCore
{
internal class PrometheusMetricsMiddleware : IMiddleware
{
private readonly ILogger<PrometheusMetricsMiddleware> _logger;
private readonly IPrometheusMetricStore _metricStore;
private readonly PrometheusOptions _options;

public PrometheusMetricsMiddleware(
ILogger<PrometheusMetricsMiddleware> logger,
IPrometheusMetricStore metricStore,
IOptions<PrometheusOptions> options)
{
_logger = logger
?? throw new ArgumentNullException(nameof(logger));
_metricStore = metricStore
?? throw new ArgumentNullException(nameof(metricStore));
_options = options?.Value
?? throw new ArgumentNullException(nameof(options));
}

public Task InvokeAsync(HttpContext context, RequestDelegate next)
{
if (context.GetEndpoint() != null)
{
_logger.LogDebug(
"Skipped Prometheus metrics as an endpoint was matched");
}
else if (!HttpMethods.IsGet(context.Request.Method)
&& !HttpMethods.IsHead(context.Request.Method))
{
_logger.LogDebug(
"Skipped Prometheus metrics as {Method} is not supported",
context.Request.Method);
}
else if (!context.Request.Path.StartsWithSegments(
_options.RequestPath))
{
_logger.LogDebug(
"Skipped Prometheus metrics as {Path} was not matched",
context.Request.Path);
}
else
{
return WriteMetricsAsync(context);
}

return next(context);
}

private async Task WriteMetricsAsync(HttpContext context)
{
foreach (var metric in _metricStore.GetMetrics())
{
await context.Response.WriteAsync(metric,
context.RequestAborted);
await context.Response.WriteAsync("\n",
context.RequestAborted);
}
}
}
}
16 changes: 16 additions & 0 deletions src/Providers/Prometheus/AspNetCore/PrometheusOptions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
using Microsoft.AspNetCore.Http;

namespace Finite.Metrics.Prometheus
{
/// <summary>
/// Options to Prometheus metrics middleware.
/// </summary>
public class PrometheusOptions
{
/// <summary>
/// Gets or sets the request path that maps to the Prometheus metrics
/// endpoint.
/// </summary>
public PathString RequestPath { get; set; }
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFramework>net5.0</TargetFramework>

<Description>Prometheus metrics provider core for Finite.Metrics</Description>
</PropertyGroup>

<ItemGroup>
<ProjectReference Include="../../../Configuration/Finite.Metrics.Configuration.csproj" />
<ProjectReference Include="../../../Core/Finite.Metrics.csproj" />
</ItemGroup>

<ItemGroup>
<PackageReference Include="Microsoft.Extensions.Hosting" />
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" />
<PackageReference Include="Microsoft.Extensions.Options.ConfigurationExtensions" />
</ItemGroup>

</Project>
36 changes: 36 additions & 0 deletions src/Providers/Prometheus/Core/IPrometheusMetricStore.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
using System.Collections.Generic;

namespace Finite.Metrics.Prometheus
{
/// <summary>
/// Represents a store of prometheus metrics.
/// </summary>
public interface IPrometheusMetricStore
{
/// <summary>
/// Stores a metric for later scraping.
/// </summary>
/// <param name="name">
/// The name of the metric.
/// </param>
/// <param name="tags">
/// The tags to store with this metric.
/// </param>
/// <param name="value">
/// The value to store for this metric.
/// </param>
/// <typeparam name="T">
/// The type of <paramref name="value"/>.
/// </typeparam>
void Store<T>(string name, TagValues? tags, T value);

/// <summary>
/// Gets all of the metrics converted to Prometheus' text-based
/// exposition format.
/// </summary>
/// <returns>
/// An enumerable containing the ordered, unique metrics.
/// </returns>
IEnumerable<string> GetMetrics();
}
}
Loading