Line data Source code
1 : // Licensed under the Apache License, Version 2.0
2 : // Copyright 2025, Michael Bushe, All rights reserved.
3 :
4 : import 'package:dartastic_opentelemetry_api/dartastic_opentelemetry_api.dart';
5 :
6 : import '../../otel.dart';
7 :
8 : /// Exemplar is a sample data point that may be used to annotate aggregated
9 : /// metric data points.
10 : ///
11 : /// Exemplars allow correlation between aggregated metric data and the
12 : /// original API calls where measurements were recorded.
13 : class Exemplar {
14 : /// The attributes for this exemplar, typically including trace and span IDs.
15 : final Attributes attributes;
16 :
17 : /// The filtered attributes for this exemplar.
18 : /// These are attributes that were on the original measurement but
19 : /// not included in the aggregation.
20 : final Attributes filteredAttributes;
21 :
22 : /// The timestamp when this exemplar was recorded.
23 : final DateTime timestamp;
24 :
25 : /// The value of this exemplar.
26 : final num value;
27 :
28 : /// The trace ID associated with this exemplar.
29 : final TraceId? traceId;
30 :
31 : /// The span ID associated with this exemplar.
32 : final SpanId? spanId;
33 :
34 : /// Creates a new Exemplar instance.
35 3 : Exemplar({
36 : required this.attributes,
37 : required this.filteredAttributes,
38 : required this.timestamp,
39 : required this.value,
40 : this.traceId,
41 : this.spanId,
42 : });
43 :
44 : /// Creates an exemplar from a measurement.
45 1 : factory Exemplar.fromMeasurement({
46 : required Measurement measurement,
47 : required DateTime timestamp,
48 : required Attributes aggregationAttributes,
49 : SpanId? spanId,
50 : TraceId? traceId,
51 : }) {
52 : // Determine which attributes are filtered out
53 1 : final filteredAttrs = _filterAttributes(
54 1 : measurement.attributes ?? OTelFactory.otelFactory!.attributes(),
55 : aggregationAttributes);
56 :
57 1 : return Exemplar(
58 : attributes: aggregationAttributes,
59 : filteredAttributes: filteredAttrs,
60 : timestamp: timestamp,
61 1 : value: measurement.value,
62 : traceId: traceId,
63 : spanId: spanId,
64 : );
65 : }
66 :
67 : /// Filters attributes to get those that are not included in the aggregation.
68 1 : static Attributes _filterAttributes(
69 : Attributes measurementAttrs, Attributes aggregationAttrs) {
70 1 : final result = <Attribute<Object>>[];
71 :
72 : // Get the attribute keys that are in the measurement but not in the aggregation
73 2 : final aggregationKeys = aggregationAttrs.keys.toSet();
74 2 : for (final attr in measurementAttrs.toList()) {
75 2 : if (!aggregationKeys.contains(attr.key)) {
76 1 : result.add(attr);
77 : }
78 : }
79 :
80 1 : return OTel.attributesFromList(result);
81 : }
82 : }
|