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