Add OpenTelemetry, performance metrics, and broaden logging + tests
Observability: - New FuchsTelemetry (ActivitySource + Meter) defining business counters (invoices/reminders/reports rendered, emails/sms sent+failed, MT940 rows, MFR calls) and duration histograms (PDF render, report render, email send). - Program.cs wires OpenTelemetry tracing (ASP.NET Core, HttpClient, SqlClient, app source) and metrics (ASP.NET Core, HttpClient, runtime, app meter). OTLP export is enabled only when Fuchs:Telemetry:OtlpEndpoint is set, so a missing collector never affects the app; disable via Fuchs:Telemetry:Enabled. Instrumentation + logging: - Services (Pdf, Invoice, Reminder, Report, Com, Banking, Widget, MfrFactory) now emit spans, record metrics, and log entry/result/timing/errors. - Added dispatch + key-action logging to the previously silent handlers (Banking, Reminder, Reports, Requests). Tests (137 total, +10): - ProcessWebComServiceTests with a stub HttpMessageHandler cover success (API 200), failure (API 500, invalid email, empty mobile), disabled mode, the base64 attachment payload contract, and metric emission via MeterListener. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,56 @@
|
||||
using System.Diagnostics;
|
||||
using System.Diagnostics.Metrics;
|
||||
|
||||
namespace Fuchs.Observability;
|
||||
|
||||
/// <summary>
|
||||
/// Central definition of the application's OpenTelemetry instrumentation:
|
||||
/// a single <see cref="ActivitySource"/> for tracing and a single
|
||||
/// <see cref="Meter"/> with the business / performance instruments.
|
||||
///
|
||||
/// These use the in-box <c>System.Diagnostics</c> APIs, which the OpenTelemetry
|
||||
/// SDK (wired up in <c>Program.cs</c>) collects and exports. Code can therefore
|
||||
/// emit spans and metrics without taking a direct dependency on the OTel SDK.
|
||||
/// </summary>
|
||||
public static class FuchsTelemetry
|
||||
{
|
||||
public const string ServiceName = "Fuchs.Intranet";
|
||||
public static readonly string ServiceVersion =
|
||||
typeof(FuchsTelemetry).Assembly.GetName().Version?.ToString() ?? "1.0.0";
|
||||
|
||||
/// <summary>Tracing source — register this name with the tracer provider.</summary>
|
||||
public static readonly ActivitySource ActivitySource = new(ServiceName, ServiceVersion);
|
||||
|
||||
/// <summary>Metrics meter — register this name with the meter provider.</summary>
|
||||
public static readonly Meter Meter = new(ServiceName, ServiceVersion);
|
||||
|
||||
// ── Business counters ────────────────────────────────────────────────────
|
||||
public static readonly Counter<long> InvoicesRendered =
|
||||
Meter.CreateCounter<long>("fuchs.invoices.rendered", "{invoice}", "Number of invoice PDFs rendered.");
|
||||
public static readonly Counter<long> RemindersRendered =
|
||||
Meter.CreateCounter<long>("fuchs.reminders.rendered", "{reminder}", "Number of reminder PDFs rendered.");
|
||||
public static readonly Counter<long> ReportsRendered =
|
||||
Meter.CreateCounter<long>("fuchs.reports.rendered", "{report}", "Number of reports rendered (by function).");
|
||||
public static readonly Counter<long> EmailsSent =
|
||||
Meter.CreateCounter<long>("fuchs.emails.sent", "{email}", "Number of emails accepted by the mailer API.");
|
||||
public static readonly Counter<long> EmailsFailed =
|
||||
Meter.CreateCounter<long>("fuchs.emails.failed", "{email}", "Number of emails that failed to send.");
|
||||
public static readonly Counter<long> SmsSent =
|
||||
Meter.CreateCounter<long>("fuchs.sms.sent", "{sms}", "Number of SMS messages sent.");
|
||||
public static readonly Counter<long> Mt940RowsParsed =
|
||||
Meter.CreateCounter<long>("fuchs.banking.mt940.rows", "{row}", "Number of MT940 transaction lines parsed.");
|
||||
public static readonly Counter<long> MfrCalls =
|
||||
Meter.CreateCounter<long>("fuchs.mfr.calls", "{call}", "Number of MFR ERP client calls initiated.");
|
||||
|
||||
// ── Performance histograms (durations in milliseconds) ───────────────────
|
||||
public static readonly Histogram<double> PdfRenderDuration =
|
||||
Meter.CreateHistogram<double>("fuchs.pdf.render.duration", "ms", "PDF render duration.");
|
||||
public static readonly Histogram<double> ReportRenderDuration =
|
||||
Meter.CreateHistogram<double>("fuchs.report.render.duration", "ms", "Report render duration.");
|
||||
public static readonly Histogram<double> EmailSendDuration =
|
||||
Meter.CreateHistogram<double>("fuchs.email.send.duration", "ms", "Email send round-trip duration.");
|
||||
|
||||
/// <summary>Starts a span on the application's <see cref="ActivitySource"/>.</summary>
|
||||
public static Activity? StartActivity(string name, ActivityKind kind = ActivityKind.Internal) =>
|
||||
ActivitySource.StartActivity(name, kind);
|
||||
}
|
||||
Reference in New Issue
Block a user