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