Line data Source code
1 : // Copyright The OpenTelemetry Authors
2 : // SPDX-License-Identifier: Apache-2.0
3 :
4 : import 'package:dartastic_opentelemetry_api/dartastic_opentelemetry_api.dart';
5 :
6 : import '../data/metric_point.dart';
7 : import '../exemplar_filter.dart';
8 : import '../exemplar_reservoir.dart';
9 : import 'metric_storage.dart';
10 :
11 : /// GaugeStorage is used for storing the last recorded value for each set of attributes.
12 : class GaugeStorage<T extends num> extends NumericStorage<T>
13 : with ExemplarSampling<T> {
14 : /// Map of attribute sets to gauge data.
15 : final Map<Attributes, _GaugePointData<T>> _points = {};
16 :
17 : /// The exemplar filter used by this storage.
18 : @override
19 : final ExemplarFilter exemplarFilter;
20 :
21 : /// Creates a new GaugeStorage instance.
22 12 : GaugeStorage({ExemplarFilter? exemplarFilter})
23 : : exemplarFilter = exemplarFilter ?? const TraceBasedExemplarFilter();
24 :
25 : /// Records a measurement with the given attributes and context.
26 10 : @override
27 : void record(T value,
28 : [Attributes? attributes, Context? context, DateTime? timestamp]) {
29 : // Create a normalized key for lookup
30 5 : final key = attributes ?? _emptyAttributes();
31 10 : final existingKey = _findMatchingKey(key);
32 :
33 : _GaugePointData<T> pointData;
34 : if (existingKey != null) {
35 16 : pointData = _points[existingKey]!;
36 8 : pointData.value = value;
37 16 : pointData.updateTime = DateTime.now();
38 : } else {
39 10 : pointData = _GaugePointData<T>(
40 : value: value,
41 10 : updateTime: DateTime.now(),
42 : reservoir:
43 10 : SimpleFixedSizeExemplarReservoir(1), // Simple fixed size of 1
44 : );
45 : // Always update with the latest value
46 20 : _points[key] = pointData;
47 : }
48 :
49 22 : maybeOffer(pointData.reservoir, value, key, context ?? Context.current,
50 10 : timestamp ?? DateTime.now());
51 : }
52 :
53 : /// Helper to get empty attributes safely
54 5 : Attributes _emptyAttributes() {
55 : // If OTelFactory is not initialized yet, create an empty attributes directly
56 5 : if (OTelFactory.otelFactory == null) {
57 0 : return OTelAPI.attributes(); // Use the API's static method instead
58 : }
59 10 : return OTelFactory.otelFactory!.attributes();
60 : }
61 :
62 : /// Gets the current value for the given attributes.
63 : /// Returns 0 if no value has been recorded for these attributes.
64 5 : @override
65 : T getValue([Attributes? attributes]) {
66 : // Create a normalized key for lookup
67 2 : final key = attributes ?? _emptyAttributes();
68 :
69 : // Find matching attributes
70 5 : final existingKey = _findMatchingKey(key);
71 :
72 : if (existingKey != null) {
73 15 : return _points[existingKey]!.value;
74 : } else {
75 : // Convert 0 to the appropriate generic type
76 1 : if (T == int) {
77 : return 0 as T;
78 1 : } else if (T == double) {
79 : return 0.0 as T;
80 : } else {
81 : return 0 as T;
82 : }
83 : }
84 : }
85 :
86 : /// Finds a key in the points map that equals the given key
87 10 : Attributes? _findMatchingKey(Attributes key) {
88 30 : for (final existingKey in _points.keys) {
89 10 : if (existingKey == key) {
90 : // Using the == operator which should call equals
91 : return existingKey;
92 : }
93 : }
94 : return null;
95 : }
96 :
97 : /// Collects the current set of metric points.
98 9 : @override
99 : List<MetricPoint<T>> collectPoints() {
100 9 : final now = DateTime.now();
101 :
102 36 : return _points.entries.map((entry) {
103 9 : final data = entry.value;
104 :
105 9 : return MetricPoint<T>.gauge(
106 9 : attributes: entry.key,
107 9 : startTime: data.updateTime, // For gauges, start time is the update time
108 : time: now,
109 9 : value: data.value,
110 27 : exemplars: data.reservoir.collectAndReset(entry.key),
111 : );
112 9 : }).toList();
113 : }
114 :
115 : /// Resets all points (not typically used for Gauges, but required by interface).
116 1 : @override
117 : void reset() {
118 2 : _points.clear();
119 : }
120 : }
121 :
122 : /// Data for a single gauge point.
123 : class _GaugePointData<T extends num> {
124 : /// The current value.
125 : T value;
126 :
127 : /// The time this value was recorded.
128 : DateTime updateTime;
129 :
130 : /// Reservoir for this point.
131 : final ExemplarReservoir reservoir;
132 :
133 10 : _GaugePointData(
134 : {required this.value, required this.updateTime, required this.reservoir});
135 : }
|