Skip to content

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 -- An ActivitySource named "Tayra" for distributed tracing
  • TayraMetrics -- A Meter named "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:

cs
// 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);
anchor

Activity Names

The following activities are emitted by Tayra:

Activity NameDescription
FieldEncrypter.EncryptEncrypting all PII fields on an object
FieldEncrypter.DecryptDecrypting all PII fields on an object
CryptoEngine.GetOrCreateKeyRetrieving or creating an encryption key
CryptoEngine.GetKeyRetrieving an existing key (returns null if shredded)
CryptoEngine.DeleteKeyDeleting a single encryption key (crypto-shredding)
CryptoEngine.DeleteAllKeysBulk-deleting keys by prefix
CryptoEngine.KeyExistsChecking whether a key exists
CryptoEngine.RotateKeyRotating a key to a new version

Activity Tags

Each activity includes contextual tags:

TagActivitiesDescription
tayra.entity_typeFieldEncrypter.*The .NET type name of the entity being processed
tayra.field_countFieldEncrypter.*Number of PII fields on the entity
tayra.key_idCryptoEngine.GetOrCreateKey, GetKey, DeleteKey, KeyExistsThe key ID being operated on
tayra.subject_prefixCryptoEngine.DeleteAllKeysThe prefix used for bulk deletion
tayra.key_id_baseCryptoEngine.RotateKeyThe base key ID being rotated

OpenTelemetry SDK Integration

To export traces to an OTLP-compatible backend (Jaeger, Zipkin, Grafana Tempo, etc.):

csharp
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:

cs
// 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();
anchor

Metric Instruments

Metric NameTypeUnitDescription
tayra.keys.createdCounter<long>Number of encryption keys created
tayra.keys.deletedCounter<long>Number of encryption keys deleted
tayra.keys.expiredCounter<long>Number of keys expired by the retention policy
tayra.encrypt.countCounter<long>Number of encrypt operations
tayra.decrypt.countCounter<long>Number of decrypt operations
tayra.cache.hitsCounter<long>Number of key cache hits
tayra.cache.missesCounter<long>Number of key cache misses
tayra.encrypt.durationHistogram<double>msDuration of encrypt operations
tayra.decrypt.durationHistogram<double>msDuration of decrypt operations
tayra.keystore.durationHistogram<double>msDuration of key store operations
tayra.access.reportsCounter<long>Number of data subject access reports generated
tayra.breach.assessmentsCounter<long>Number of breach impact assessments performed
tayra.migration.rows_encryptedCounter<long>Number of rows encrypted during data migration

OpenTelemetry SDK Integration

To export metrics to Prometheus, OTLP, or another backend:

csharp
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:

csharp
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