< Summary

Information
Class: AsiBackbone.OpenTelemetry.OpenTelemetryGovernanceEmitter
Assembly: AsiBackbone.OpenTelemetry
File(s): /home/runner/work/AsiBackbone/AsiBackbone/src/AsiBackbone.OpenTelemetry/OpenTelemetryGovernanceEmitter.cs
Line coverage
89%
Covered lines: 134
Uncovered lines: 16
Coverable lines: 150
Total lines: 285
Line coverage: 89.3%
Branch coverage
39%
Covered branches: 22
Total branches: 56
Branch coverage: 39.2%
Method coverage

Feature is only available for sponsors

Upgrade to PRO version

Metrics

MethodBranch coverage Crap Score Cyclomatic complexity Line coverage
.cctor()100%11100%
.ctor()100%11100%
.ctor(...)100%11100%
EmitAsync()100%8895.55%
EmitActivity(...)83.33%66100%
BuildTags(...)50%44100%
EmitDeliveredMetrics(...)100%11100%
EmitFailureMetrics(...)50%22100%
BuildMetricTags(...)100%11100%
AddTag(...)50%22100%
AddTag(...)50%22100%
AddTag(...)50%22100%
AddTag(...)100%11100%
GetEventName(...)12.5%2342428.57%
IsFailureStatus(...)0%4260%

File(s)

/home/runner/work/AsiBackbone/AsiBackbone/src/AsiBackbone.OpenTelemetry/OpenTelemetryGovernanceEmitter.cs

#LineLine coverage
 1using System.Diagnostics;
 2using System.Diagnostics.Metrics;
 3using AsiBackbone.Core.Emissions;
 4
 5namespace AsiBackbone.OpenTelemetry;
 6
 7/// <summary>
 8/// Emits provider-neutral governance envelopes through OpenTelemetry-friendly .NET diagnostics primitives.
 9/// </summary>
 10/// <remarks>
 11/// This emitter records activity events, activity tags, and low-cardinality metrics. It does not configure exporters or
 12/// </remarks>
 13public sealed class OpenTelemetryGovernanceEmitter : IAsiBackboneGovernanceEmitter
 14{
 215    private static readonly ActivitySource ActivitySource = new(OpenTelemetryGovernanceInstrumentation.ActivitySourceNam
 216    private static readonly Meter Meter = new(OpenTelemetryGovernanceInstrumentation.MeterName);
 217    private static readonly Counter<long> EmissionsCounter = Meter.CreateCounter<long>(
 218        OpenTelemetryGovernanceInstrumentation.EmissionsCounterName,
 219        description: "Counts AsiBackbone governance emission attempts accepted by the OpenTelemetry diagnostics provider
 220    private static readonly Counter<long> EmissionFailuresCounter = Meter.CreateCounter<long>(
 221        OpenTelemetryGovernanceInstrumentation.EmissionFailuresCounterName,
 222        description: "Counts AsiBackbone governance emission failures normalized by the OpenTelemetry diagnostics provid
 223    private static readonly Histogram<double> EmissionLatencyHistogram = Meter.CreateHistogram<double>(
 224        OpenTelemetryGovernanceInstrumentation.EmissionLatencyHistogramName,
 225        unit: "ms",
 226        description: "Measures local OpenTelemetry governance emission latency in milliseconds.");
 27
 28    private readonly OpenTelemetryGovernanceEmitterOptions options;
 29
 30    /// <summary>
 31    /// Initializes a new instance of the <see cref="OpenTelemetryGovernanceEmitter" /> class using default options.
 32    /// </summary>
 33    public OpenTelemetryGovernanceEmitter()
 434        : this(new OpenTelemetryGovernanceEmitterOptions())
 35    {
 436    }
 37
 38    /// <summary>
 39    /// Initializes a new instance of the <see cref="OpenTelemetryGovernanceEmitter" /> class using host-owned options.
 40    /// </summary>
 41    /// <param name="options">The OpenTelemetry governance emitter options.</param>
 642    public OpenTelemetryGovernanceEmitter(OpenTelemetryGovernanceEmitterOptions options)
 43    {
 644        ArgumentNullException.ThrowIfNull(options);
 645        options.Validate();
 46
 647        this.options = options;
 648    }
 49
 50    /// <inheritdoc />
 51    public async ValueTask<GovernanceEmissionResult> EmitAsync(
 52        GovernanceEmissionEnvelope envelope,
 53        CancellationToken cancellationToken = default)
 54    {
 655        ArgumentNullException.ThrowIfNull(envelope);
 656        cancellationToken.ThrowIfCancellationRequested();
 57
 658        var stopwatch = Stopwatch.StartNew();
 59
 60        try
 61        {
 662            if (options.BeforeEmitAsync is not null)
 63            {
 264                await options.BeforeEmitAsync(envelope, cancellationToken).ConfigureAwait(false);
 65            }
 66
 467            cancellationToken.ThrowIfCancellationRequested();
 468            double latencyMs = stopwatch.Elapsed.TotalMilliseconds;
 469            string eventName = GetEventName(envelope);
 470            string providerName = options.ProviderName.Trim();
 71
 472            if (options.EmitActivityEvents)
 73            {
 474                EmitActivity(envelope, eventName, providerName, options.DefaultActivityName, latencyMs);
 75            }
 76
 477            if (options.EmitMetrics)
 78            {
 479                EmitDeliveredMetrics(envelope, providerName, latencyMs);
 80            }
 81
 482            return GovernanceEmissionResult.Delivered(
 483                providerName,
 484                envelope.EnvelopeId,
 485                new Dictionary<string, string>(StringComparer.Ordinal)
 486                {
 487                    ["opentelemetry.activity_source"] = OpenTelemetryGovernanceInstrumentation.ActivitySourceName,
 488                    ["opentelemetry.meter"] = OpenTelemetryGovernanceInstrumentation.MeterName,
 489                    ["opentelemetry.event_name"] = eventName,
 490                    ["opentelemetry.emission_latency_ms"] = latencyMs.ToString("0.###", System.Globalization.CultureInfo
 491                });
 92        }
 093        catch (OperationCanceledException) when (cancellationToken.IsCancellationRequested)
 94        {
 095            throw;
 96        }
 297        catch (Exception ex)
 98        {
 299            double latencyMs = stopwatch.Elapsed.TotalMilliseconds;
 2100            var error = GovernanceEmissionError.Create(
 2101                "opentelemetry.emission.exception",
 2102                $"OpenTelemetry governance emission failed with {ex.GetType().Name}.",
 2103                isRetryable: true,
 2104                providerName: options.ProviderName,
 2105                providerErrorCode: ex.GetType().FullName);
 106
 2107            if (options.EmitMetrics)
 108            {
 2109                EmitFailureMetrics(envelope, error, latencyMs);
 110            }
 111
 2112            return GovernanceEmissionResult.RetryableFailure(
 2113                error,
 2114                providerName: options.ProviderName,
 2115                metadata: new Dictionary<string, string>(StringComparer.Ordinal)
 2116                {
 2117                    ["opentelemetry.activity_source"] = OpenTelemetryGovernanceInstrumentation.ActivitySourceName,
 2118                    ["opentelemetry.meter"] = OpenTelemetryGovernanceInstrumentation.MeterName,
 2119                    ["opentelemetry.emission_latency_ms"] = latencyMs.ToString("0.###", System.Globalization.CultureInfo
 2120                });
 121        }
 6122    }
 123
 124    private static void EmitActivity(
 125        GovernanceEmissionEnvelope envelope,
 126        string eventName,
 127        string providerName,
 128        string defaultActivityName,
 129        double latencyMs)
 130    {
 4131        ActivityTagsCollection tags = BuildTags(envelope, providerName, GovernanceEmissionStatus.Delivered.ToString(), l
 4132        string activityName = string.IsNullOrWhiteSpace(envelope.OperationName)
 4133            ? defaultActivityName
 4134            : envelope.OperationName;
 135
 4136        using Activity? activity = ActivitySource.StartActivity(activityName, ActivityKind.Internal);
 137
 4138        if (activity is null)
 139        {
 2140            return;
 141        }
 142
 108143        foreach (KeyValuePair<string, object?> tag in tags)
 144        {
 52145            _ = activity.SetTag(tag.Key, tag.Value);
 146        }
 147
 2148        _ = activity.AddEvent(new ActivityEvent(eventName, envelope.OccurredUtc, tags));
 6149    }
 150
 151    private static ActivityTagsCollection BuildTags(
 152        GovernanceEmissionEnvelope envelope,
 153        string providerName,
 154        string result,
 155        double latencyMs)
 156    {
 4157        ActivityTagsCollection tags = [];
 158
 4159        AddTag(tags, OpenTelemetryGovernanceAttributes.EnvelopeId, envelope.EnvelopeId);
 4160        AddTag(tags, OpenTelemetryGovernanceAttributes.SchemaVersion, envelope.SchemaVersion);
 4161        AddTag(tags, OpenTelemetryGovernanceAttributes.EventType, envelope.EventType.ToString());
 4162        AddTag(tags, OpenTelemetryGovernanceAttributes.EventId, envelope.EventId);
 4163        AddTag(tags, OpenTelemetryGovernanceAttributes.CorrelationId, envelope.CorrelationId);
 4164        AddTag(tags, OpenTelemetryGovernanceAttributes.AuditResidueId, envelope.AuditResidueId);
 4165        AddTag(tags, OpenTelemetryGovernanceAttributes.TraceId, envelope.TraceId);
 4166        AddTag(tags, OpenTelemetryGovernanceAttributes.SpanId, envelope.SpanId);
 4167        AddTag(tags, OpenTelemetryGovernanceAttributes.ParentSpanId, envelope.ParentSpanId);
 4168        AddTag(tags, OpenTelemetryGovernanceAttributes.DecisionOutcome, envelope.Outcome);
 4169        AddTag(tags, OpenTelemetryGovernanceAttributes.DecisionStage, envelope.DecisionStage);
 4170        AddTag(tags, OpenTelemetryGovernanceAttributes.PolicyVersion, envelope.PolicyVersion);
 4171        AddTag(tags, OpenTelemetryGovernanceAttributes.PolicyHash, envelope.PolicyHash);
 4172        AddTag(tags, OpenTelemetryGovernanceAttributes.LifecycleStage, envelope.LifecycleStage?.ToString());
 4173        AddTag(tags, OpenTelemetryGovernanceAttributes.LifecycleStageSequence, envelope.LifecycleStageSequence);
 4174        AddTag(tags, OpenTelemetryGovernanceAttributes.GatewayExecutionId, envelope.GatewayExecutionId);
 4175        AddTag(tags, OpenTelemetryGovernanceAttributes.OutboxSequence, envelope.OutboxSequence);
 4176        AddTag(tags, OpenTelemetryGovernanceAttributes.EmitterProvider, providerName);
 4177        AddTag(tags, OpenTelemetryGovernanceAttributes.EmitterStatus, envelope.EmitterStatus);
 4178        AddTag(tags, OpenTelemetryGovernanceAttributes.EmitterResult, result);
 4179        AddTag(tags, OpenTelemetryGovernanceAttributes.EmissionLatencyMs, latencyMs);
 180
 4181        if (envelope.Payload is not null)
 182        {
 4183            AddTag(tags, OpenTelemetryGovernanceAttributes.PayloadType, envelope.Payload.PayloadType);
 4184            AddTag(tags, OpenTelemetryGovernanceAttributes.PayloadSchemaVersion, envelope.Payload.SchemaVersion);
 4185            AddTag(tags, OpenTelemetryGovernanceAttributes.PayloadContentType, envelope.Payload.ContentType);
 4186            AddTag(tags, OpenTelemetryGovernanceAttributes.PayloadContentHash, envelope.Payload.ContentHash);
 4187            AddTag(tags, OpenTelemetryGovernanceAttributes.PayloadSizeBytes, envelope.Payload.SizeBytes);
 188        }
 189
 4190        return tags;
 191    }
 192
 193    private static void EmitDeliveredMetrics(
 194        GovernanceEmissionEnvelope envelope,
 195        string providerName,
 196        double latencyMs)
 197    {
 4198        TagList tags = BuildMetricTags(envelope, providerName, GovernanceEmissionStatus.Delivered.ToString());
 4199        EmissionsCounter.Add(1, tags);
 4200        EmissionLatencyHistogram.Record(latencyMs, tags);
 4201    }
 202
 203    private static void EmitFailureMetrics(
 204        GovernanceEmissionEnvelope envelope,
 205        GovernanceEmissionError error,
 206        double latencyMs)
 207    {
 2208        TagList tags = BuildMetricTags(envelope, error.ProviderName ?? OpenTelemetryGovernanceInstrumentation.ProviderNa
 2209        tags.Add(OpenTelemetryGovernanceAttributes.MetricFailureCode, error.Code);
 2210        tags.Add(OpenTelemetryGovernanceAttributes.MetricRetryable, error.IsRetryable);
 211
 2212        EmissionFailuresCounter.Add(1, tags);
 2213        EmissionLatencyHistogram.Record(latencyMs, tags);
 2214    }
 215
 216    private static TagList BuildMetricTags(
 217        GovernanceEmissionEnvelope envelope,
 218        string providerName,
 219        string result)
 220    {
 6221        TagList tags = new()
 6222        {
 6223            { OpenTelemetryGovernanceAttributes.MetricEventType, envelope.EventType.ToString() },
 6224            { OpenTelemetryGovernanceAttributes.MetricResult, result },
 6225            { OpenTelemetryGovernanceAttributes.MetricProvider, providerName }
 6226        };
 227
 6228        return tags;
 229    }
 230
 231    private static void AddTag(ActivityTagsCollection tags, string key, string? value)
 232    {
 88233        if (!string.IsNullOrWhiteSpace(value))
 234        {
 88235            tags.Add(key, value);
 236        }
 88237    }
 238
 239    private static void AddTag(ActivityTagsCollection tags, string key, long? value)
 240    {
 8241        if (value.HasValue)
 242        {
 8243            tags.Add(key, value.Value);
 244        }
 8245    }
 246
 247    private static void AddTag(ActivityTagsCollection tags, string key, int? value)
 248    {
 4249        if (value.HasValue)
 250        {
 4251            tags.Add(key, value.Value);
 252        }
 4253    }
 254
 255    private static void AddTag(ActivityTagsCollection tags, string key, double value)
 256    {
 4257        tags.Add(key, value);
 4258    }
 259
 260    private static string GetEventName(GovernanceEmissionEnvelope envelope)
 261    {
 4262        return envelope.EventType switch
 4263        {
 4264            GovernanceEmissionEventType.Decision => OpenTelemetryGovernanceInstrumentation.DecisionEvaluatedEventName,
 0265            GovernanceEmissionEventType.Acknowledgment => OpenTelemetryGovernanceInstrumentation.AcknowledgmentRecordedE
 0266            GovernanceEmissionEventType.CapabilityToken => OpenTelemetryGovernanceInstrumentation.CapabilityTokenIssuedE
 0267            GovernanceEmissionEventType.Gateway => OpenTelemetryGovernanceInstrumentation.GatewayCompletedEventName,
 0268            GovernanceEmissionEventType.AuditResidue => OpenTelemetryGovernanceInstrumentation.AuditResidueCreatedEventN
 0269            GovernanceEmissionEventType.AuditLifecycle => OpenTelemetryGovernanceInstrumentation.LifecycleRecordedEventN
 0270            GovernanceEmissionEventType.Outbox => OpenTelemetryGovernanceInstrumentation.OutboxUpdatedEventName,
 0271            GovernanceEmissionEventType.ProviderEmission => IsFailureStatus(envelope.EmitterStatus)
 0272                ? OpenTelemetryGovernanceInstrumentation.EmissionFailedEventName
 0273                : OpenTelemetryGovernanceInstrumentation.EmissionDeliveredEventName,
 0274            _ => OpenTelemetryGovernanceInstrumentation.GenericGovernanceEventName
 4275        };
 276    }
 277
 278    private static bool IsFailureStatus(string? status)
 279    {
 0280        return status is not null
 0281            && (status.Contains("fail", StringComparison.OrdinalIgnoreCase)
 0282                || status.Contains("dead", StringComparison.OrdinalIgnoreCase)
 0283                || status.Contains("retry", StringComparison.OrdinalIgnoreCase));
 284    }
 285}