Every Granit module exposes business-level metrics via the .NET System.Diagnostics.Metrics API.
These metrics are automatically exported to Mimir (or any OpenTelemetry-compatible backend) by
AddGranitObservability() — no manual wiring needed.
All Granit metrics follow strict naming and tagging conventions:
Rule Convention File location src/Granit.{Module}/Diagnostics/{Module}Metrics.csClass sealed class {Module}Metrics with IMeterFactory constructor injectionMeter name Granit.{Module} (PascalCase, one per module)Metric name granit.{module}.{entity}.{action} (all lowercase, dot-separated)Tags snake_case, always include tenant_id (coalesced to "global" when null)Tag passing TagList (zero-allocation struct) — never KeyValuePair[]DI registration services.TryAddSingleton<{Module}Metrics>() in AddGranit*()Duration unit seconds ("s"), measured via Stopwatch.GetElapsedTime()
.NET type OpenTelemetry kind Use case Counter<long>Monotonic sum Events counted (requests, deliveries, errors) Histogram<double>Distribution Latency, duration UpDownCounter<long>Non-monotonic sum In-flight operations (active leases)
namespace Granit . MyModule . Diagnostics ;
public sealed class MyModuleMetrics (IMeterFactory meterFactory)
private readonly Meter _meter = meterFactory . Create ( " Granit.MyModule " );
private readonly Counter< long > _operationsCompleted;
private readonly Histogram< double > _operationDuration;
// Initialize instruments in a method or constructor body
public void RecordOperationCompleted (Guid? tenantId, string status)
{ " tenant_id " , tenantId ? . ToString () ?? " global " },
_operationsCompleted . Add ( 1 , tags);
public void RecordOperationDuration (Guid? tenantId, double seconds)
{ " tenant_id " , tenantId ? . ToString () ?? " global " },
_operationDuration . Record (seconds, tags);
public static IServiceCollection AddGranitMyModule ( this IServiceCollection services)
services . TryAddSingleton <MyModuleMetrics>();
public sealed class MyModuleMetricsTests : IDisposable
private readonly ServiceProvider _sp;
private readonly MyModuleMetrics _metrics;
private readonly IMeterFactory _meterFactory;
public MyModuleMetricsTests ()
_sp = new ServiceCollection() . AddMetrics () . BuildServiceProvider ();
_meterFactory = _sp . GetRequiredService <IMeterFactory>();
_metrics = new MyModuleMetrics(_meterFactory);
public void RecordOperationCompleted_IncrementsCounter ()
var collector = new MetricCollector< long >(
_meterFactory, " Granit.MyModule " , " granit.mymodule.operations.completed " );
_metrics . RecordOperationCompleted ( Guid . NewGuid (), " success " );
var measurement = collector . GetMeasurementSnapshot () . Single ();
measurement . Value . ShouldBe ( 1 );
measurement . Tags [ " status " ] . ShouldBe ( " success " );
public void Dispose () => _sp . Dispose ();
Meter: Granit.AI
Metric Type Tags Description granit.ai.requests.completedCounter tenant_id, model, provider, statusLLM requests completed granit.ai.tokens.inputCounter tenant_id, model, providerInput tokens consumed granit.ai.tokens.outputCounter tenant_id, model, providerOutput tokens generated granit.ai.request.durationHistogram (s) tenant_id, model, providerLLM request duration
Meter: Granit.Auditing
Metric Type Tags Description granit.auditing.entries.persistedCounter tenant_id, entity_typeAudit entries written granit.auditing.entries.purgedCounter tenant_idAudit entries purged (retention) granit.auditing.capture.errorsCounter tenant_id, entity_typeCapture failures
Meter: Granit.BackgroundJobs
Metric Type Tags Description granit.background_jobs.execution.completedCounter tenant_id, job_name, statusJob executions completed granit.background_jobs.execution.durationHistogram (s) tenant_id, job_name, statusJob execution duration
Meter: Granit.BlobStorage
Metric Type Tags Description granit.blob_storage.uploads.initiatedCounter tenant_id, providerUpload operations started granit.blob_storage.validations.completedCounter tenant_id, statusBlob validations completed granit.blob_storage.validations.failedCounter tenant_id, reasonBlob validations failed granit.blob_storage.blobs.deletedCounter tenant_idBlobs deleted granit.blob_storage.orphans.cleanedCounter tenant_idOrphan blobs cleaned up granit.blob_storage.confirm.durationHistogram (s) tenant_id, providerBlob confirmation duration
Meter: Granit.DataExchange
Metric Type Tags Description granit.data_exchange.import.jobs.completedCounter tenant_id, statusImport jobs completed granit.data_exchange.import.rows.processedCounter tenant_idRows processed during import granit.data_exchange.import.rows.errorsCounter tenant_idRows that failed during import granit.data_exchange.import.durationHistogram (s) tenant_idImport job duration granit.data_exchange.export.jobs.completedCounter tenant_id, statusExport jobs completed granit.data_exchange.export.rows.exportedCounter tenant_idRows exported granit.data_exchange.export.durationHistogram (s) tenant_idExport job duration
Meter: Granit.Events
Metric Type Tags Description granit.events.event.publishedCounter tenant_id, bus_type, event_typeEvents published (local or distributed) granit.events.handler.executedCounter tenant_id, event_type, statusEvent handler executions
Meter: Granit.Identity
Metric Type Tags Description granit.identity.operations.completedCounter tenant_id, operation, provider, statusIdentity operations completed granit.identity.operations.errorsCounter tenant_id, operation, providerIdentity operation errors granit.identity.operation.durationHistogram (s) tenant_id, operation, providerIdentity operation duration
Meter: Granit.Notifications
Metric Type Tags Description granit.notifications.fanout.triggeredCounter tenant_id, notification_typeNotification fanouts triggered granit.notifications.deliveries.succeededCounter tenant_id, channelDeliveries succeeded granit.notifications.deliveries.failedCounter tenant_id, channelDeliveries failed granit.notifications.delivery.durationHistogram (s) tenant_id, channel, statusDelivery duration
Meter: Granit.Privacy
Metric Type Tags Description granit.privacy.export.requestsCounter tenant_idGDPR export requests initiated granit.privacy.export.fragments.receivedCounter tenant_id, providerData fragments received during export granit.privacy.deletion.requestsCounter tenant_idGDPR deletion requests initiated granit.privacy.export.durationHistogram (s) tenant_id, statusExport saga duration
Meter: Granit.RateLimiting
Metric Type Tags Description granit.rate_limiting.requests.allowedCounter tenant_id, policyRequests allowed through rate limiter granit.rate_limiting.requests.rejectedCounter tenant_id, policyRequests rejected by rate limiter
Meter: Granit.Http.Bulkhead
Metric Type Tags Description granit.http.bulkhead.leases.activeUpDownCounter tenant_idCurrently active bulkhead leases granit.http.bulkhead.requests.rejectedCounter tenant_idRequests rejected by bulkhead
Meter: Granit.Vault
Metric Type Tags Description granit.vault.operations.completedCounter tenant_id, operation, provider, statusVault operations completed granit.vault.operations.errorsCounter tenant_id, operation, providerVault operation errors granit.vault.operation.durationHistogram (s) tenant_id, operation, providerVault operation duration granit.vault.rotations.detectedCounter providerKey rotations detected
Meter: Granit.Webhooks
Metric Type Tags Description granit.webhooks.fanout.triggeredCounter tenant_id, event_typeWebhook fanouts triggered granit.webhooks.deliveries.succeededCounter tenant_id, event_typeWebhook deliveries succeeded granit.webhooks.deliveries.failedCounter tenant_id, event_type, statusWebhook deliveries failed granit.webhooks.subscriptions.suspendedCounter tenant_idSubscriptions auto-suspended granit.webhooks.delivery.durationHistogram (s) tenant_id, event_type, statusWebhook delivery duration
Meter: Granit.Workflow
Metric Type Tags Description granit.workflow.transitions.completedCounter tenant_id, outcome, from_state, to_stateWorkflow transitions completed granit.workflow.transition.durationHistogram (s) tenant_id, outcomeTransition evaluation duration
# Request throughput by module (last 5 min)
rate(granit_ai_requests_completed_total[5m])
or rate(granit_notifications_fanout_triggered_total[5m])
or rate(granit_webhooks_fanout_triggered_total[5m])
or rate(granit_eventbus_events_published_total[5m])
# Webhook delivery error rate per tenant
sum by (tenant_id) (rate(granit_webhooks_deliveries_failed_total[5m]))
sum by (tenant_id) (rate(granit_webhooks_deliveries_succeeded_total[5m]))
+ sum by (tenant_id) (rate(granit_webhooks_deliveries_failed_total[5m]))
# P95 vault operation duration
histogram_quantile(0.95, sum by (le, provider) (
rate(granit_vault_operation_duration_seconds_bucket[5m])
# Token usage per model (last 1 hour)
increase(granit_ai_tokens_input_total[1h])
+ increase(granit_ai_tokens_output_total[1h])
# Export requests per tenant (last 24h)
sum by (tenant_id) (increase(granit_privacy_export_requests_total[24h]))
# Average export duration
histogram_quantile(0.5, sum by (le) (
rate(granit_privacy_export_duration_seconds_bucket[24h])
Alert PromQL condition Severity High webhook failure rate rate(granit_webhooks_deliveries_failed_total[5m]) > 0.1Warning Vault operation errors increase(granit_vault_operations_errors_total[5m]) > 0Critical Slow AI responses histogram_quantile(0.95, rate(granit_ai_request_duration_seconds_bucket[5m])) > 30Warning Background job failures rate(granit_backgroundjobs_executions_completed_total{status="error"}[5m]) > 0Warning Notification delivery failures rate(granit_notifications_deliveries_failed_total[5m]) > 0.05Warning Webhook subscription suspended increase(granit_webhooks_subscriptions_suspended_total[1h]) > 0Warning