Skip to content

Observability — Serilog & OpenTelemetry

Granit.Observability wires Serilog structured logging and OpenTelemetry (traces + metrics) into a single AddGranitObservability() call. Logs ship to Loki, traces to Tempo, and metrics to Mimir — all via an OTLP collector.

PackageRoleDepends on
Granit.ObservabilitySerilog + OpenTelemetry (traces, metrics, logs), OTLP exportGranit
Granit.Observability.AIAI-driven log analysis — feeds Loki excerpts into an LLM for incident summariesGranit.Observability, Granit.AI

Beyond the framework stack, six satellite packages in granit-business register declarative MetricDefinition and DashboardDefinition records consumed by Granit.Analytics and Granit.Dashboards: Granit.BlobStorage.{Analytics,Dashboards}, Granit.Webhooks.{Analytics,Dashboards}, Granit.Notifications.Analytics, Granit.Identity.Federated.Analytics. They live next to the modules they describe so analytics contracts ship with the domain that owns them — see Application metrics reference.

graph LR
    App[ASP.NET Core App] --> Serilog
    App --> OTel[OpenTelemetry SDK]

    Serilog -->|WriteTo.OpenTelemetry| Collector[OTLP Collector :4317]
    OTel -->|OTLP gRPC| Collector

    Collector --> Loki[Loki — Logs]
    Collector --> Tempo[Tempo — Traces]
    Collector --> Mimir[Mimir — Metrics]

    Loki --> Grafana
    Tempo --> Grafana
    Mimir --> Grafana
[DependsOn(typeof(GranitObservabilityModule))]
public class AppModule : GranitModule { }
{
"Observability": {
"ServiceName": "my-backend",
"ServiceVersion": "1.2.0",
"OtlpEndpoint": "http://otel-collector:4317",
"ServiceNamespace": "my-company",
"Environment": "production"
}
}

AddGranitObservability() configures two Serilog sinks:

SinkPurpose
ConsoleLocal development, [HH:mm:ss LEV] SourceContext Message
OpenTelemetryOTLP export to Loki via the collector

Every log entry is enriched with ServiceName, ServiceVersion, and Environment properties, matching the OpenTelemetry resource attributes for correlation.

Additional Serilog settings (minimum level, overrides, extra sinks) can be added via standard Serilog configuration in appsettings.jsonReadFrom.Configuration is called before the Granit enrichers.

Three built-in instrumentations are registered automatically:

InstrumentationWhat it captures
ASP.NET CoreInbound HTTP requests (method, route, status code)
HttpClientOutbound HTTP calls (dependency tracking)
EF CoreDatabase queries (command text, duration)

Health check endpoints (/health/*) are filtered out of traces to avoid noise.

Granit modules register their own ActivitySource names via GranitActivitySourceRegistry.Register() during host configuration. AddGranitObservability() reads the registry and calls AddSource() for each — no manual wiring needed.

// Inside a module's AddGranit*() extension method
GranitActivitySourceRegistry.Register("Granit.Workflow");

Around 70 ActivitySource names are pre-registered. Major groups:

DomainActivitySource names
Core dataGranit.Persistence, Granit.Entities, Granit.Encryption, Granit.Events, Granit.Wolverine
Security & identityGranit.HttpSecurity, Granit.DPoP, Granit.TokenManagement, Granit.OpenIddict, Granit.Identity, Granit.Identity.Local, Granit.Identity.{Cognito,EntraId,GoogleCloud,Keycloak}
Vault / KMSGranit.Vault.{Aws,Azure,GoogleCloud,HashiCorp}
Blob storageGranit.BlobStorage.{Azure,Database,FileSystem,GoogleCloud,Proxy,S3}
NotificationsGranit.Notifications, Granit.Notifications.Email.{Acs,AwsSes,Scaleway,SendGrid}, Granit.Notifications.Sms.{Acs,AwsSns}, Granit.Notifications.MobilePush.{Anh,AwsSns}
Privacy / complianceGranit.Privacy, Granit.Auditing
Background workGranit.BackgroundJobs, Granit.Scheduling, Granit.Webhooks, Granit.Browsing
Documents & mediaGranit.Templating, Granit.PdfRendering, Granit.Imaging.{AI,MagickNet}, Granit.Documents, Granit.Documents.PublicLinks, Granit.AssetMetadata, Granit.Renditions
AIGranit.AI, Granit.AI.{AzureOpenAI,Ollama,OpenAI}, Granit.QueryEngine.AI, Granit.Observability.AI, Granit.Timeline.AI
Query engine / BFFGranit.QueryEngine.EfCore, Granit.DataExchange, Granit.DataLookup, Granit.ReferenceData, Granit.Bff
Multi-tenant primitivesGranit.RateLimiting, Granit.Bulkhead, Granit.Features, Granit.Settings, Granit.Localization, Granit.IO
SaaS businessGranit.Activities, Granit.Workflow, Granit.Catalog, Granit.CustomerBalance, Granit.Invoicing, Granit.Payments, Granit.Parties, Granit.Subscriptions, Granit.Tax, Granit.Taxonomy, Granit.Metering, Granit.Analytics
MCP / integrationGranit.Mcp

The canonical list is the GranitActivitySourceRegistry registrations performed inside each module’s AddGranit*() extension — read it in source for the exact set shipped by your current Granit version.

Logs and trace spans must never contain raw PII (GDPR Art. 5, ISO 27001 A.5.34). The LogRedaction static class (namespace Granit.Diagnostics, shipped in the core Granit package) provides redaction helpers for use in [LoggerMessage] call sites and Activity.SetTag() calls:

MethodInputOutputUse case
Email(string)john.doe@example.comjoh***@example.comLog templates
EmailDomain(string)john.doe@example.comexample.comSpan tags (bounded cardinality)
Phone(string)+33612345678+336*****78Log templates
Token(string)dLkj3FDmAbCdEfGhdLkj...fGhDevice tokens, API tokens
IpAddress(string)192.168.1.42192.168.1.***IPv4 /24 masking
Username(string)john_adminjoh***DB credentials, identity
HashPrefix(string)(any)a1b2c3d4Non-reversible correlation (span tags)

Architecture tests enforce PII-safe naming in [LoggerMessage] templates (LoggerMessagePiiConventionTests) and *ActivitySource.cs tag constants (ActivitySourcePiiConventionTests). GUIDs (user IDs, session IDs) are pseudonymous and exempt.

PropertyTypeDefaultDescription
ServiceNamestring"unknown-service"Service name for OTEL resource
ServiceVersionstring"0.0.0"Service version
OtlpEndpointstring"http://localhost:4317"OTLP gRPC endpoint
ServiceNamespacestring"my-company"Service namespace
Environmentstring"development"Deployment environment
EnableTracingbooltrueEnable trace export via OTLP
EnableMetricsbooltrueEnable metrics export via OTLP
CategoryKey typesPackage
ModuleGranitObservabilityModule
OptionsObservabilityOptionsGranit.Observability
RegistryGranitActivitySourceRegistryGranit
PII redactionLogRedactionGranit
ExtensionsAddGranitObservability()Granit.Observability