Observability
Tayra integrates with the .NET System.Diagnostics APIs to provide first-class OpenTelemetry support. All encryption operations, key management calls, and cache interactions emit distributed traces and metrics that you can export to any OpenTelemetry-compatible backend.
Overview
Tayra exposes two instrumentation primitives:
TayraActivitySource-- AnActivitySourcenamed"Tayra"for distributed tracingTayraMetrics-- AMeternamed"Tayra"for counters and histograms
Both are static singletons in the Tayra.Diagnostics namespace. They emit data automatically during normal Tayra operations -- no configuration is required to instrument your code.
Distributed Tracing
Subscribing to Activities
Tayra emits activities for every key management and encryption operation. Subscribe using the .NET ActivityListener or the OpenTelemetry SDK:
// Tayra emits activities via a static ActivitySource named "Tayra".
// Subscribe to it using the .NET ActivityListener or the OpenTelemetry SDK.
using var listener = new ActivityListener
{
ShouldListenTo = source => source.Name == TayraActivitySource.Name,
Sample = (ref ActivityCreationOptions<ActivityContext> _) => ActivitySamplingResult.AllData,
ActivityStarted = activity =>
{
Console.WriteLine($" Activity started: {activity.OperationName}");
},
ActivityStopped = activity =>
{
Console.WriteLine($" Activity stopped: {activity.OperationName} " +
$"(duration: {activity.Duration.TotalMilliseconds:F2}ms)");
foreach (var tag in activity.Tags)
{
Console.WriteLine($" Tag: {tag.Key} = {tag.Value}");
}
},
};
ActivitySource.AddActivityListener(listener);
// Now perform an operation — activities are emitted automatically
var tracedPatient = new PatientRecord
{
PatientId = Guid.NewGuid(),
FullName = "Trace Demo",
Allergies = new List<string> { "Dust" },
Department = "ENT",
};
await tayra.EncryptAsync(tracedPatient);
await tayra.DecryptAsync(tracedPatient);Activity Names
The following activities are emitted by Tayra:
| Activity Name | Description |
|---|---|
FieldEncrypter.Encrypt | Encrypting all PII fields on an object |
FieldEncrypter.Decrypt | Decrypting all PII fields on an object |
CryptoEngine.GetOrCreateKey | Retrieving or creating an encryption key |
CryptoEngine.GetKey | Retrieving an existing key (returns null if shredded) |
CryptoEngine.DeleteKey | Deleting a single encryption key (crypto-shredding) |
CryptoEngine.DeleteAllKeys | Bulk-deleting keys by prefix |
CryptoEngine.KeyExists | Checking whether a key exists |
CryptoEngine.RotateKey | Rotating a key to a new version |
Activity Tags
Each activity includes contextual tags:
| Tag | Activities | Description |
|---|---|---|
tayra.entity_type | FieldEncrypter.* | The .NET type name of the entity being processed |
tayra.field_count | FieldEncrypter.* | Number of PII fields on the entity |
tayra.key_id | CryptoEngine.GetOrCreateKey, GetKey, DeleteKey, KeyExists | The key ID being operated on |
tayra.subject_prefix | CryptoEngine.DeleteAllKeys | The prefix used for bulk deletion |
tayra.key_id_base | CryptoEngine.RotateKey | The base key ID being rotated |
OpenTelemetry SDK Integration
To export traces to an OTLP-compatible backend (Jaeger, Zipkin, Grafana Tempo, etc.):
using OpenTelemetry.Trace;
builder.Services.AddOpenTelemetry()
.WithTracing(tracing => tracing
.AddSource("Tayra") // Subscribe to Tayra's ActivitySource
.AddOtlpExporter());Metrics
Subscribing to Metrics
Tayra records counters and histograms for key management and encryption operations:
// Tayra exposes metrics via a static Meter named "Tayra".
// Use a MeterListener for testing or the OpenTelemetry SDK for production.
using var meterListener = new MeterListener();
meterListener.InstrumentPublished = (instrument, listener) =>
{
if (instrument.Meter.Name == TayraMetrics.MeterName)
{
listener.EnableMeasurementEvents(instrument);
}
};
meterListener.SetMeasurementEventCallback<long>((instrument, measurement, tags, state) =>
{
Console.WriteLine($" Metric: {instrument.Name} = {measurement}");
});
meterListener.SetMeasurementEventCallback<double>((instrument, measurement, tags, state) =>
{
Console.WriteLine($" Metric: {instrument.Name} = {measurement:F2} {instrument.Unit}");
});
meterListener.Start();
// Encrypt/decrypt to trigger metric recording
var metricsPatient = new PatientRecord
{
PatientId = Guid.NewGuid(),
FullName = "Metrics Demo",
Allergies = new List<string> { "Gluten" },
Department = "GI",
};
await tayra.EncryptAsync(metricsPatient);
await tayra.DecryptAsync(metricsPatient);
// Collect recorded measurements
meterListener.RecordObservableInstruments();Metric Instruments
| Metric Name | Type | Unit | Description |
|---|---|---|---|
tayra.keys.created | Counter<long> | Number of encryption keys created | |
tayra.keys.deleted | Counter<long> | Number of encryption keys deleted | |
tayra.keys.expired | Counter<long> | Number of keys expired by the retention policy | |
tayra.encrypt.count | Counter<long> | Number of encrypt operations | |
tayra.decrypt.count | Counter<long> | Number of decrypt operations | |
tayra.cache.hits | Counter<long> | Number of key cache hits | |
tayra.cache.misses | Counter<long> | Number of key cache misses | |
tayra.encrypt.duration | Histogram<double> | ms | Duration of encrypt operations |
tayra.decrypt.duration | Histogram<double> | ms | Duration of decrypt operations |
tayra.keystore.duration | Histogram<double> | ms | Duration of key store operations |
tayra.access.reports | Counter<long> | Number of data subject access reports generated | |
tayra.breach.assessments | Counter<long> | Number of breach impact assessments performed | |
tayra.migration.rows_encrypted | Counter<long> | Number of rows encrypted during data migration |
OpenTelemetry SDK Integration
To export metrics to Prometheus, OTLP, or another backend:
using OpenTelemetry.Metrics;
builder.Services.AddOpenTelemetry()
.WithMetrics(metrics => metrics
.AddMeter("Tayra") // Subscribe to Tayra's Meter
.AddOtlpExporter()
// Or for Prometheus:
// .AddPrometheusExporter()
);Cache Hit Ratio
The tayra.cache.hits and tayra.cache.misses counters let you monitor key cache effectiveness. A healthy deployment should see a high hit ratio (>90%) after warmup. If the miss ratio is high, consider increasing the KeyCacheDuration in TayraOptions:
services.AddTayra(options =>
{
options.KeyCacheDuration = TimeSpan.FromMinutes(15);
});Pre-built Grafana Dashboard
Tayra ships a pre-built Grafana dashboard template that visualizes all of these metrics out of the box. Import dashboards/grafana/tayra-dashboard.json into Grafana and connect it to your Prometheus data source -- no manual panel configuration required. See Grafana Dashboards for details.
Audit Trail Integration
The DefaultTayraAuditLogger also emits ActivityEvent entries on the current Activity for every audit event. This means audit events appear as span events in your distributed traces, providing a unified view of what happened during each request. See Audit Trail for details.
Grafana Dashboards
Tayra ships pre-built Grafana dashboard templates and Prometheus alerting rules for all the metrics listed above. See Grafana Dashboards for import instructions and panel descriptions.
See Also
- Grafana Dashboards -- Pre-built dashboard and alerting rules
- Audit Trail -- GDPR Art. 30 compliance logging
- Health Checks -- Key store connectivity monitoring
- Configuration -- Cache duration and key size options
