Observability — Logging and Tracing
Benevia Core uses standard .NET telemetry: Microsoft.Extensions.Logging for logs and System.Diagnostics.ActivitySource (OpenTelemetry-compatible) for traces. There is no custom abstraction.
What Core emits
- An
ActivitySourcenamedBenevia.Corewith spans for entity events (Compute,Validate,Changed,PreSave,Added,Deleted), OData operations, and workflow steps. - Standard tags on the active ASP.NET Core request span:
tenant.idanduser.idafter authenticationworkflow.idandworkflow.indexwhen those headers are present
Wiring up OpenTelemetry
Core does not configure an exporter — that is the host application's choice. A typical setup adds the Benevia.Core source, plus Npgsql for database spans:
builder.Services
.AddOpenTelemetry()
.WithTracing(t => t
.AddAspNetCoreInstrumentation()
.AddHttpClientInstrumentation()
.AddSource("Benevia.Core")
.AddSource("Npgsql")
.AddOtlpExporter())
.WithLogging();
Send to whichever backend you prefer (Jaeger, Sentry via the OpenTelemetry bridge, Application Insights, etc.).
Logging from business logic
Inject ILogger<T> like any other service. Use structured logging so fields stay searchable.
using Microsoft.Extensions.Logging;
[Logic]
public class SalesOrderBL(ILogger<SalesOrderBL> logger)
{
public void Submit(SalesOrder.Logic salesOrder)
{
salesOrder.OnSubmit(order =>
{
logger.LogInformation("Submitting order {OrderId} for {CustomerId}",
order.Id, order.CustomerId);
// ...
});
}
}
Tagging the current request span
To enrich the active span without creating a new one, set tags on Activity.Current:
using System.Diagnostics;
Activity.Current?.SetTag("checkout.path", "express");
These tags appear on the request span in your tracing backend and are searchable like any other attribute.