Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add metrics using prometheus #175

Merged
merged 11 commits into from
Oct 24, 2023
23 changes: 9 additions & 14 deletions .github/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,21 +3,12 @@


<p align="left">

[![build and test](https://github.com/hamed-shirbandi/TaskoMask/actions/workflows/ci.yml/badge.svg?branch=master)](https://github.com/hamed-shirbandi/TaskoMask/actions/workflows/ci.yml)
[![Mutation testing](https://img.shields.io/endpoint?style=flat&url=https%3A%2F%2Fbadge-api.stryker-mutator.io%2Fgithub.com%2Fhamed-shirbandi%2FTaskoMask%2Fmaster)](https://dashboard.stryker-mutator.io/reports/github.com/hamed-shirbandi/TaskoMask/master)
<a href="https://github.com/hamed-shirbandi/TaskoMask/issues">
<img alt="GitHub issues" src="https://img.shields.io/github/issues/hamed-shirbandi/TaskoMask">
</a>
<a href="http://taskomask.ir">
<img src="https://img.shields.io/website?url=http://taskomask.ir">
</a>
<a href="https://github.com/hamed-shirbandi/TaskoMask/blob/master/LICENSE">
<img src="https://img.shields.io/github/license/hamed-shirbandi/TaskoMask">
</a>
<a href="https://github.com/hamed-shirbandi/TaskoMask/graphs/contributors">
<img src="https://img.shields.io/github/contributors/hamed-shirbandi/TaskoMask">
</a>
<a href="https://github.com/hamed-shirbandi/TaskoMask/issues"><img alt="GitHub issues" src="https://img.shields.io/github/issues/hamed-shirbandi/TaskoMask"></a>
<a href="http://taskomask.ir"> <img src="https://img.shields.io/website?url=http://taskomask.ir"></a>
<a href="https://github.com/hamed-shirbandi/TaskoMask/blob/master/LICENSE"><img src="https://img.shields.io/github/license/hamed-shirbandi/TaskoMask"></a>
<a href="https://github.com/hamed-shirbandi/TaskoMask/graphs/contributors"><img src="https://img.shields.io/github/contributors/hamed-shirbandi/TaskoMask"></a>
</p>

[TaskoMask](https://github.com/hamed-shirbandi/TaskoMask/wiki/User-Guide-Documentation) is an open-source task management system built on the .Net framework. The primary objective of this project is to demonstrate the practical application of advanced software development concepts such as DDD (Domain-Driven Design), TDD (Test-Driven Development), BDD (Behavior-Driven Development), and Microservices.
Expand Down Expand Up @@ -72,7 +63,7 @@ Here is a comprehensive list of the patterns, principles, approaches, and method
- [Service discovery](https://microservices.io/patterns/3rd-party-registration.html) : Kubernetes - Consul
- [Circuit Breaker](https://microservices.io/patterns/reliability/circuit-breaker.html) : Polly
- [Log aggregation](https://microservices.io/patterns/observability/application-logging.html) : Serilog - Seq
- [Application metrics](https://microservices.io/patterns/observability/application-metrics.html) : Opentelemetry-dotnet - Prometheus
- [Application metrics](https://microservices.io/patterns/observability/application-metrics.html) : prometheus-net
- [Distributed tracing](https://microservices.io/patterns/observability/distributed-tracing.html) : Opentelemetry-dotnet - Jaeger
- [Health check API](https://microservices.io/patterns/observability/health-check-api.html) : AspNetCore.HealthChecks
- [IDP](https://en.wikipedia.org/wiki/Identity_provider) : DuendeSoftware IdentityServer
Expand Down Expand Up @@ -159,6 +150,8 @@ Here is a comprehensive list of the tools and technologies we have employed to i
- ASP.NET Identity
- MongoDB
- Redis
- Entity Framework
- SQL
- [Ocelot](https://ocelot.readthedocs.io/) : .NET core API Gateway
- [DuendeSoftware IdentityServer](https://docs.duendesoftware.com/identityserver/v6) : OpenID Connect and OAuth 2.x framework for ASP.NET Core
- [MassTransit](https://masstransit-project.com/) : a framework on top of message transports such as RabbitMQ
Expand All @@ -176,6 +169,7 @@ Here is a comprehensive list of the tools and technologies we have employed to i
- [Swagger](https://www.nuget.org/packages/Swashbuckle.AspNetCore) : expose Swagger JSON endpoints from APIs
- [Serilog](https://serilog.net/) : provides diagnostic logging
- [AspNetCore.HealthChecks](https://github.com/Xabaril/AspNetCore.Diagnostics.HealthChecks) : ASP.NET Core Health Check
- [prometheus-net](https://github.com/prometheus-net/prometheus-net) : .NET library to instrument your code with Prometheus metrics
- [MvcPagedList.Core](https://www.nuget.org/packages/MvcPagedList.Core/) : easily paging in ASP.NET Core MVC
- [EasyCaching](https://github.com/dotnetcore/EasyCaching) : caching library
- [stryker-net](https://github.com/stryker-mutator/stryker-net): Mutation testing for .NET
Expand Down Expand Up @@ -257,6 +251,7 @@ This project is authored by [Hamed Shirbandi](https://github.com/hamed-shirbandi


* ### Oct, 2023
- [x] Instrument with Prometheus metrics
- [x] Implement build system using nuke
- [x] Implement Mutation Testing using Stryker
- [x] Integrate CI with nuke and stryker
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Server.Kestrel.Core;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Prometheus;
using TaskoMask.BuildingBlocks.Web.MVC.Configuration.Metric;
using TaskoMask.BuildingBlocks.Web.MVC.Services.AuthenticatedUser;

namespace TaskoMask.BuildingBlocks.Web.MVC.Configuration
Expand All @@ -17,7 +20,7 @@ public static class MvcConfiguration
/// <summary>
///
/// </summary>
public static void AddMvcPreConfigured(this IServiceCollection services)
public static void AddMvcPreConfigured(this IServiceCollection services, IConfiguration configuration)
{
if (services == null) throw new ArgumentNullException(nameof(services));

Expand All @@ -28,14 +31,16 @@ public static void AddMvcPreConfigured(this IServiceCollection services)
services.AddAuthenticatedUserService();

services.AddWebServerOptions();

services.AddMetrics(configuration);
}



/// <summary>
///
/// </summary>
public static void UseMvcPreConfigured(this IApplicationBuilder app , IWebHostEnvironment env)
public static void UseMvcPreConfigured(this IApplicationBuilder app , IWebHostEnvironment env, IConfiguration configuration)
{
if (app == null) throw new ArgumentNullException(nameof(app));

Expand All @@ -51,6 +56,8 @@ public static void UseMvcPreConfigured(this IApplicationBuilder app , IWebHostEn

app.UseRouting();

app.UseMetrics(configuration);

app.UseAuthentication();

app.UseAuthorization();
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using TaskoMask.BuildingBlocks.Web.MVC.Configuration.Metric;
using TaskoMask.BuildingBlocks.Web.MVC.Services.AuthenticatedUser;

namespace TaskoMask.BuildingBlocks.Web.MVC.Configuration
Expand All @@ -16,7 +18,7 @@ public static class RazorPagesConfiguration
/// <summary>
///
/// </summary>
public static void AddRazorPagesPreConfigured(this IServiceCollection services)
public static void AddRazorPagesPreConfigured(this IServiceCollection services, IConfiguration configuration)
{
if (services == null) throw new ArgumentNullException(nameof(services));

Expand All @@ -27,22 +29,28 @@ public static void AddRazorPagesPreConfigured(this IServiceCollection services)
services.AddHttpContextAccessor();

services.AddAuthenticatedUserService();

services.AddMetrics(configuration);
}



/// <summary>
///
/// </summary>
public static void UseRazorPagesPreConfigured(this IApplicationBuilder app, IWebHostEnvironment env)
public static void UseRazorPagesPreConfigured(this IApplicationBuilder app, IWebHostEnvironment env, IConfiguration configuration)
{
if (app == null) throw new ArgumentNullException(nameof(app));

if (env.IsDevelopment())
app.UseDeveloperExceptionPage();

app.UseStaticFiles();

app.UseRouting();

app.UseMetrics(configuration);

app.UseAuthorization();
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using MonoApi.Services.Afrr.Activations.Api.Helpers;
using TaskoMask.BuildingBlocks.Web.MVC.Configuration.Captcha;
using TaskoMask.BuildingBlocks.Web.MVC.Configuration.Jwt;
using TaskoMask.BuildingBlocks.Web.MVC.Configuration.Metric;
using TaskoMask.BuildingBlocks.Web.MVC.Configuration.Swagger;
using TaskoMask.BuildingBlocks.Web.MVC.Services.AuthenticatedUser;

Expand Down Expand Up @@ -41,14 +43,16 @@ public static void AddWebApiPreConfigured(this IServiceCollection services, ICon
services.AddJwtAuthentication(configuration);

services.AddCors();

services.AddMetrics(configuration);
}



/// <summary>
///
/// </summary>
public static void UseWebApiPreConfigured(this IApplicationBuilder app, IWebHostEnvironment env)
public static void UseWebApiPreConfigured(this IApplicationBuilder app, IWebHostEnvironment env,IConfiguration configuration)
{
if (app == null) throw new ArgumentNullException(nameof(app));

Expand All @@ -69,6 +73,8 @@ public static void UseWebApiPreConfigured(this IApplicationBuilder app, IWebHost
.AllowAnyMethod()
.AllowAnyHeader());

app.UseMetrics(configuration);

app.UseAuthentication();

app.UseAuthorization();
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
using Microsoft.AspNetCore.Builder;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Prometheus;

namespace TaskoMask.BuildingBlocks.Web.MVC.Configuration.Metric
{
public static class MetricConfiguration
{
public static void AddMetrics(this IServiceCollection services, IConfiguration configuration)
{
var metricOptions = configuration.GetSection("Metric").Get<MetricOptions>();

//It starts the metrics exporter as a background service using a stand alone kestrel
if (metricOptions.StandAloneKestrelServerEnabled)
{
services.AddMetricServer(options =>
{
options.Port = metricOptions.Port;
options.Url = metricOptions.Url;
options.Hostname = metricOptions.Hostname;
});
}

//Inject IMetricFactory to be used in application objects instead of coupling their implementation with Metrics
services.AddSingleton<IMetricFactory>(Metrics.DefaultFactory);
}

public static void UseMetrics(this IApplicationBuilder app, IConfiguration configuration)
{
var metricOptions = configuration.GetSection("Metric").Get<MetricOptions>();

//If kestrel server is not enabled then use current app server
if (!metricOptions.StandAloneKestrelServerEnabled)
app.UseMetricServer(metricOptions.Port, metricOptions.Url);

if (metricOptions.HttpMetricsEnabled)
{
app.UseHttpMetrics(options =>
{
options.AddCustomLabel("host", context => context.Request.Host.Host);
});
}

if (metricOptions.SuppressDefaultMetrics)
Metrics.SuppressDefaultMetrics();
}
}
}
35 changes: 35 additions & 0 deletions src/1-BuildingBlocks/Web.MVC/Configuration/Metric/MetricOptions.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
namespace TaskoMask.BuildingBlocks.Web.MVC.Configuration.Metric
{
public class MetricOptions
{
/// <summary>
/// Set it to true if you want to run the metrics on a stand alone kestrel server
/// </summary>
public bool StandAloneKestrelServerEnabled { get; set; }
public ushort Port { get; set; }
public string Url { get; set; }

/// <summary>
/// It is used when StandAloneKestrelServerEnabled is true.
/// Will listen for requests using this hostname. "+" indicates listen on all hostnames.
/// By setting this to "localhost", you can easily prevent access from remote systems.-
/// </summary>
public string Hostname { get; set; }

/// <summary>
/// Set it to true if you want to use the built-in metrics for http requests.
/// The metrics are:
/// Number of HTTP requests in progress.
/// Total number of received HTTP requests.
/// Duration of HTTP requests.
/// </summary>
public bool HttpMetricsEnabled { get; set; }


/// <summary>
/// The library enables various default metrics and integrations by default.
/// If these default metrics are not desirable, set SuppressDefaultMetrics to true.
/// </summary>
public bool SuppressDefaultMetrics { get; set; }
}
}
1 change: 1 addition & 0 deletions src/1-BuildingBlocks/Web.MVC/Web.MVC.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
<PackageReference Include="Serilog.Sinks.Seq" Version="5.1.1" />
<PackageReference Include="Grpc.AspNetCore" Version="2.49.0" />
<PackageReference Include="Grpc.Net.Client" Version="2.49.0" />
<PackageReference Include="prometheus-net.AspNetCore" Version="8.0.1" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Infrastructure\Infrastructure.csproj" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
using TaskoMask.Services.Boards.Read.Api.Infrastructure.DI;
using Microsoft.AspNetCore.Builder;
using TaskoMask.BuildingBlocks.Web.Grpc.Configuration;
using Microsoft.Extensions.Configuration;

namespace TaskoMask.Services.Boards.Read.Api.Configuration
{
Expand Down Expand Up @@ -35,12 +36,12 @@ public static WebApplication ConfigureServices(this WebApplicationBuilder builde
/// <summary>
///
/// </summary>
public static WebApplication ConfigurePipeline(this WebApplication app)
public static WebApplication ConfigurePipeline(this WebApplication app, IConfiguration configuration)
{

app.UseSerilogRequestLogging();

app.UseWebApiPreConfigured(app.Environment);
app.UseWebApiPreConfigured(app.Environment, configuration);

app.Services.InitialDatabase();

Expand Down
2 changes: 1 addition & 1 deletion src/2-Services/Boards/Api/Boards.Read.Api/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,6 @@

var builder = WebApplication.CreateBuilder(args);

var app = builder.ConfigureServices().ConfigurePipeline();
var app = builder.ConfigureServices().ConfigurePipeline(builder.Configuration);

app.Run();
10 changes: 9 additions & 1 deletion src/2-Services/Boards/Api/Boards.Read.Api/appsettings.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,14 @@
"UserName": "guest",
"Password": "guest"
},
"Metric": {
"StandAloneKestrelServerEnabled": false,
"Port": 5025,
"Url": "/metrics",
"Hostname": "+",
"HttpMetricsEnabled": true,
"SuppressDefaultMetrics": false
},
"Caching": {
"CacheTimeInMinutes": 60,
"Enabled": true
Expand All @@ -30,7 +38,7 @@
"IncludeXmlComments": "TaskoMask.Services.Boards.Read.Api.xml,TaskoMask.BuildingBlocks.Contracts.xml"
},
"Url": {
"Owner-Read-Service": "https://localhost:5021",
"Owner-Read-Service": "https://localhost:5021"
},
"AllowedHosts": "*",
"Serilog": {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
using TaskoMask.Services.Boards.Write.Api.Infrastructure.CrossCutting.DI;
using TaskoMask.Services.Boards.Write.Api.Infrastructure.Data.DbContext;
using Microsoft.AspNetCore.Builder;
using Microsoft.Extensions.Configuration;

namespace TaskoMask.Services.Boards.Write.Api.Configuration
{
Expand All @@ -30,12 +31,12 @@ public static WebApplication ConfigureServices(this WebApplicationBuilder builde
/// <summary>
///
/// </summary>
public static WebApplication ConfigurePipeline(this WebApplication app)
public static WebApplication ConfigurePipeline(this WebApplication app, IConfiguration configuration)
{

app.UseSerilogRequestLogging();

app.UseWebApiPreConfigured(app.Environment);
app.UseWebApiPreConfigured(app.Environment, configuration);

app.Services.InitialDatabasesAndSeedEssentialData();

Expand Down
2 changes: 1 addition & 1 deletion src/2-Services/Boards/Api/Boards.Write.Api/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,6 @@

var builder = WebApplication.CreateBuilder(args);

var app = builder.ConfigureServices().ConfigurePipeline();
var app = builder.ConfigureServices().ConfigurePipeline(builder.Configuration);

app.Run();
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
"dotnetRunMessages": true,
"applicationUrl": "https://localhost:5023;http://localhost:5022"
},
"Docker (1)": {
"Docker": {
"commandName": "Docker",
"launchBrowser": true,
"launchUrl": "{Scheme}://{ServiceHost}:{ServicePort}/",
Expand Down
Loading