diff --git a/AobaCore/AobaCore.csproj b/AobaCore/AobaCore.csproj
index 5c456a7..02d6c07 100644
--- a/AobaCore/AobaCore.csproj
+++ b/AobaCore/AobaCore.csproj
@@ -7,10 +7,11 @@
-
-
+
+
-
+
+
diff --git a/AobaCore/Extensions.cs b/AobaCore/Extensions.cs
index 6964596..f04dd0c 100644
--- a/AobaCore/Extensions.cs
+++ b/AobaCore/Extensions.cs
@@ -2,6 +2,7 @@
using Microsoft.Extensions.DependencyInjection;
using MongoDB.Driver;
+using MongoDB.Driver.Core.Extensions.DiagnosticSources;
using System;
using System.Collections.Generic;
@@ -14,7 +15,9 @@ public static class Extensions
{
public static IServiceCollection AddAoba(this IServiceCollection services)
{
- var dbClient = new MongoClient("mongodb://NinoIna:27017");
+ var settings = MongoClientSettings.FromConnectionString("mongodb://NinoIna:27017");
+ settings.ClusterConfigurator = cb => cb.Subscribe(new DiagnosticsActivityEventSubscriber());
+ var dbClient = new MongoClient(settings);
var db = dbClient.GetDatabase("Aoba");
services.AddSingleton(dbClient);
diff --git a/AobaServer/AobaServer.csproj b/AobaServer/AobaServer.csproj
index 851fd5e..2e49415 100644
--- a/AobaServer/AobaServer.csproj
+++ b/AobaServer/AobaServer.csproj
@@ -15,6 +15,11 @@
+
+
+
+
+
diff --git a/AobaServer/AobaAuthenticationHandler.cs b/AobaServer/Auth/AobaAuthenticationHandler.cs
similarity index 97%
rename from AobaServer/AobaAuthenticationHandler.cs
rename to AobaServer/Auth/AobaAuthenticationHandler.cs
index 3404200..d4467d2 100644
--- a/AobaServer/AobaAuthenticationHandler.cs
+++ b/AobaServer/Auth/AobaAuthenticationHandler.cs
@@ -3,7 +3,7 @@ using Microsoft.Extensions.Options;
using System.Text.Encodings.Web;
-namespace AobaServer;
+namespace AobaServer.Auth;
internal class AobaAuthenticationHandler(IOptionsMonitor options, ILoggerFactory logger, UrlEncoder encoder) : AuthenticationHandler(options, logger, encoder)
{
diff --git a/AobaServer/Auth/MetricsTokenValidator.cs b/AobaServer/Auth/MetricsTokenValidator.cs
new file mode 100644
index 0000000..a931e7d
--- /dev/null
+++ b/AobaServer/Auth/MetricsTokenValidator.cs
@@ -0,0 +1,42 @@
+using Microsoft.IdentityModel.Tokens;
+
+using System.IdentityModel.Tokens.Jwt;
+using System.Security.Claims;
+
+namespace AobaServer.Auth;
+
+public class MetricsTokenValidator(AuthInfo authInfo) : JwtSecurityTokenHandler
+{
+ private readonly JwtSecurityTokenHandler _handler = new();
+ public override Task ValidateTokenAsync(string token, TokenValidationParameters validationParameters)
+ {
+ try
+ {
+ var principal = _handler.ValidateToken(token, new TokenValidationParameters
+ {
+ ValidateIssuerSigningKey = true,
+ IssuerSigningKey = new SymmetricSecurityKey(authInfo.SecureKey),
+ ValidateIssuer = true,
+ ValidIssuer = authInfo.Issuer,
+ ValidateAudience = true,
+ ValidAudience = "metrics",
+ ValidateLifetime = false,
+ ClockSkew = TimeSpan.FromMinutes(1)
+ }, out var validatedToken);
+ return Task.FromResult(new TokenValidationResult
+ {
+ IsValid = true,
+ SecurityToken = validatedToken,
+ ClaimsIdentity = new ClaimsIdentity(principal.Identity),
+ });
+ }
+ catch (Exception e)
+ {
+ return Task.FromResult(new TokenValidationResult
+ {
+ IsValid = false,
+ Exception = e
+ });
+ }
+ }
+}
diff --git a/AobaServer/Middleware/OpenTelemetry.cs b/AobaServer/Middleware/OpenTelemetry.cs
new file mode 100644
index 0000000..3ee05a3
--- /dev/null
+++ b/AobaServer/Middleware/OpenTelemetry.cs
@@ -0,0 +1,73 @@
+#nullable enable
+
+using AobaServer.Middleware;
+
+using Microsoft.AspNetCore.Builder;
+using Microsoft.AspNetCore.Routing;
+using Microsoft.Extensions.Configuration;
+using Microsoft.Extensions.DependencyInjection;
+
+using OpenTelemetry;
+using OpenTelemetry.Metrics;
+using OpenTelemetry.Resources;
+using OpenTelemetry.Trace;
+
+
+namespace AobaServer.Middleware;
+
+public static class OpenTelemetry
+{
+ public static void AddObersability(this IServiceCollection services, IConfiguration configuration)
+ {
+ var otel = services.AddOpenTelemetry();
+
+ otel.ConfigureResource(res =>
+ {
+ res.AddService(serviceName: $"Breeze: {Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT")}");
+ });
+
+
+ // Add Metrics for ASP.NET Core and our custom metrics and export to Prometheus
+ otel.WithMetrics(metrics => metrics
+ // Metrics provider from OpenTelemetry
+ .AddAspNetCoreInstrumentation()
+ .AddCustomMetrics()
+ // Metrics provides by ASP.NET Core in .NET 8
+ .AddMeter("Microsoft.AspNetCore.Hosting")
+ .AddMeter("Microsoft.AspNetCore.Server.Kestrel")
+ // Metrics provided by System.Net libraries
+ .AddMeter("System.Net.Http")
+ .AddMeter("System.Net.NameResolution")
+ .AddMeter("MongoDB.Driver.Core.Extensions.DiagnosticSources")
+ .AddPrometheusExporter());
+
+ // Add Tracing for ASP.NET Core and our custom ActivitySource and export to Jaeger
+ var tracingOtlpEndpoint = configuration["OTLP_ENDPOINT_URL"];
+ otel.WithTracing(tracing =>
+ {
+ tracing.AddAspNetCoreInstrumentation();
+ tracing.AddHttpClientInstrumentation();
+ if (!string.IsNullOrWhiteSpace(tracingOtlpEndpoint))
+ {
+ tracing.AddOtlpExporter(otlpOptions =>
+ {
+ otlpOptions.Endpoint = new Uri(tracingOtlpEndpoint);
+ });
+ }
+ });
+ }
+
+
+ public static MeterProviderBuilder AddCustomMetrics(this MeterProviderBuilder builder)
+ {
+
+
+ return builder;
+ }
+
+ public static IEndpointRouteBuilder MapObserability(this IEndpointRouteBuilder endpoints)
+ {
+ endpoints.MapPrometheusScrapingEndpoint().RequireAuthorization(p => p.RequireRole("metrics"));
+ return endpoints;
+ }
+}
diff --git a/AobaServer/Program.cs b/AobaServer/Program.cs
index 08b7593..24205d5 100644
--- a/AobaServer/Program.cs
+++ b/AobaServer/Program.cs
@@ -1,6 +1,8 @@
using AobaCore;
using AobaServer;
+using AobaServer.Auth;
+using AobaServer.Middleware;
using AobaServer.Models;
using Microsoft.AspNetCore.Authentication;
@@ -13,6 +15,7 @@ var builder = WebApplication.CreateBuilder(args);
// Add services to the container.
builder.Services.AddControllers(opt => opt.ModelBinderProviders.Add(new BsonIdModelBinderProvider()));
+builder.Services.AddObersability(builder.Configuration);
var authInfo = AuthInfo.LoadOrCreate("Auth.json", "aobaV2", "aoba");
builder.Services.AddSingleton(authInfo);
@@ -37,6 +40,7 @@ builder.Services.AddAuthentication(options =>
}).AddJwtBearer(JwtBearerDefaults.AuthenticationScheme, options => //Bearer auth
{
options.TokenValidationParameters = validationParams;
+ options.TokenHandlers.Add(new MetricsTokenValidator(authInfo));
options.Events = new JwtBearerEvents
{
OnMessageReceived = ctx => //Retreive token from cookie if not found in headers
@@ -87,6 +91,7 @@ app.UseAuthorization();
app.MapControllers();
+app.MapObserability();
app.Run();