Added metrics to Webhook API (total events, errors)

This commit is contained in:
Mia Rose Winter 2024-04-11 13:03:05 +02:00
parent a83fdbbe7a
commit a64e16f681
Signed by: miawinter
GPG key ID: 4B6F6A83178F595E
3 changed files with 41 additions and 3 deletions

View file

@ -4,16 +4,19 @@
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
using Wave.Data; using Wave.Data;
using Wave.Data.Api.Mailtrap; using Wave.Data.Api.Mailtrap;
using Wave.Utilities.Metrics;
namespace Wave.Controllers; namespace Wave.Controllers;
[ApiController] [ApiController]
[Route("/api/[controller]")] [Route("/api/[controller]")]
public class WebhookController(ILogger<WebhookController> logger, ApplicationDbContext context) : ControllerBase { public class WebhookController(ILogger<WebhookController> logger, ApplicationDbContext context, ApiMetrics metrics)
: ControllerBase {
[HttpPost("mailtrap/{apiKey}")] [HttpPost("mailtrap/{apiKey}")]
[Authorize("EmailApi", AuthenticationSchemes = "ApiKeyInRoute")] [Authorize("EmailApi", AuthenticationSchemes = "ApiKeyInRoute")]
public async Task<IActionResult> Mailtrap(Webhook webhook, string apiKey) { public async Task<IActionResult> Mailtrap(Webhook webhook, string apiKey) {
foreach (var webhookEvent in webhook.Events) { foreach (var webhookEvent in webhook.Events) {
metrics.WebhookEventReceived("Mailtrap", webhookEvent.Type.ToString());
var subscriber = await context.Set<EmailSubscriber>().FirstOrDefaultAsync(s => s.Email == webhookEvent.Email); var subscriber = await context.Set<EmailSubscriber>().FirstOrDefaultAsync(s => s.Email == webhookEvent.Email);
logger.LogDebug("Received Webhook event {EventType} for {email}", logger.LogDebug("Received Webhook event {EventType} for {email}",
@ -24,6 +27,7 @@ public class WebhookController(ILogger<WebhookController> logger, ApplicationDbC
"Received webhook event from mailtrap of type {EventType}, " + "Received webhook event from mailtrap of type {EventType}, " +
"but failed to find subscriber with E-Mail {email}.", "but failed to find subscriber with E-Mail {email}.",
webhookEvent.Type, webhookEvent.Email); webhookEvent.Type, webhookEvent.Email);
metrics.WebhookEventError("Mailtrap", webhookEvent.Type.ToString(), "unknown email");
continue; continue;
} }
@ -59,6 +63,7 @@ public class WebhookController(ILogger<WebhookController> logger, ApplicationDbC
case WebhookEventType.Click: case WebhookEventType.Click:
default: default:
logger.LogInformation("Received unsupported event {EventType} for {email}. Skipping.", webhookEvent.Type, webhookEvent.Email); logger.LogInformation("Received unsupported event {EventType} for {email}. Skipping.", webhookEvent.Type, webhookEvent.Email);
metrics.WebhookEventError("Mailtrap", webhookEvent.Type.ToString(), "unknown type");
return Ok(); return Ok();
} }

View file

@ -26,6 +26,7 @@
using Serilog; using Serilog;
using Serilog.Events; using Serilog.Events;
using Serilog.Sinks.Grafana.Loki; using Serilog.Sinks.Grafana.Loki;
using Wave.Utilities.Metrics;
#region Version Information #region Version Information
@ -275,7 +276,7 @@
#endregion #endregion
#region Open Telemetry #region Open Telemetry & Metrics
var features = builder.Configuration.GetSection(nameof(Features)).Get<Features>(); var features = builder.Configuration.GetSection(nameof(Features)).Get<Features>();
if (features?.Telemetry is true) { if (features?.Telemetry is true) {
@ -292,7 +293,7 @@
.AddMeter("Microsoft.AspNetCore.Http.Connections") .AddMeter("Microsoft.AspNetCore.Http.Connections")
.AddMeter("Microsoft.AspNetCore.Http.Routing") .AddMeter("Microsoft.AspNetCore.Http.Routing")
.AddMeter("Microsoft.AspNetCore.Diagnostics") .AddMeter("Microsoft.AspNetCore.Diagnostics")
.AddMeter("Wave.Api")
.AddPrometheusExporter()); .AddPrometheusExporter());
// Jaeger etc. // Jaeger etc.
@ -303,6 +304,8 @@
tracing.AddOtlpExporter(options => options.Endpoint = new Uri(otlpUrl)); tracing.AddOtlpExporter(options => options.Endpoint = new Uri(otlpUrl));
}); });
} }
builder.Services.AddSingleton<ApiMetrics>();
} }
#endregion #endregion

View file

@ -0,0 +1,30 @@
using System.Diagnostics.Metrics;
namespace Wave.Utilities.Metrics;
public class ApiMetrics {
private Counter<int> WebhookEventCounter { get; }
private Counter<int> WebhookErrorCounter { get; }
public ApiMetrics(IMeterFactory meterFactory) {
var meter = meterFactory.Create("Wave.Api");
WebhookEventCounter = meter.CreateCounter<int>("wave.webhook.events", "{events}",
description: "Counts the incoming webhook events");
WebhookErrorCounter = meter.CreateCounter<int>("wave.webhook.errors", "{events}",
description: "Counts errors in webhook events");
}
public void WebhookEventReceived(string api, string type) {
WebhookEventCounter.Add(1,
new KeyValuePair<string, object?>("wave.webhook.event_type", type),
new KeyValuePair<string, object?>("wave.webhook.api", api));
}
public void WebhookEventError(string api, string type, string reason) {
WebhookErrorCounter.Add(1,
new KeyValuePair<string, object?>("wave.webhook.event_type", type),
new KeyValuePair<string, object?>("wave.webhook.api", api),
new KeyValuePair<string, object?>("wave.error.reason", reason));
}
}