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/metric.dart';
7 : import '../data/metric_point.dart';
8 : import '../meter.dart';
9 : import '../storage/sum_storage.dart';
10 : import 'base_instrument.dart';
11 :
12 : /// UpDownCounter is a synchronous instrument that records additive values.
13 : ///
14 : /// An UpDownCounter is used to measure a value that increases and decreases.
15 : /// For example, the number of active requests, queue size, pool size.
16 : class UpDownCounter<T extends num>
17 : implements APIUpDownCounter<T>, SDKInstrument {
18 : /// The underlying API UpDownCounter.
19 : final APIUpDownCounter<T> _apiCounter;
20 :
21 : /// The Meter that created this UpDownCounter.
22 : final Meter _meter;
23 :
24 : /// Storage for accumulating up-down counter measurements.
25 : final SumStorage<T> _storage = SumStorage<T>(isMonotonic: false);
26 :
27 : /// Creates a new UpDownCounter instance.
28 4 : UpDownCounter({
29 : required APIUpDownCounter<T> apiCounter,
30 : required Meter meter,
31 : }) : _apiCounter = apiCounter,
32 : _meter = meter {
33 : // Register this instrument with the meter provider
34 20 : _meter.provider.registerInstrument(_meter.name, this);
35 : }
36 :
37 4 : @override
38 8 : String get name => _apiCounter.name;
39 :
40 2 : @override
41 4 : String? get unit => _apiCounter.unit;
42 :
43 2 : @override
44 4 : String? get description => _apiCounter.description;
45 :
46 1 : @override
47 2 : bool get enabled => _meter.enabled;
48 :
49 4 : @override
50 4 : APIMeter get meter => _meter;
51 :
52 1 : @override
53 : bool get isCounter => false;
54 :
55 1 : @override
56 : bool get isUpDownCounter => true;
57 :
58 1 : @override
59 : bool get isGauge => false;
60 :
61 1 : @override
62 : bool get isHistogram => false;
63 :
64 3 : @override
65 : void add(T value, [Attributes? attributes]) {
66 : // First use the API implementation (no-op by default)
67 6 : _apiCounter.add(value, attributes);
68 :
69 : // In the SDK, we only check the meter's enabled state
70 6 : if (!_meter.enabled) return;
71 :
72 : // Record the measurement in our storage
73 6 : _storage.record(value, attributes);
74 : }
75 :
76 1 : @override
77 : void addWithMap(T value, Map<String, Object> attributeMap) {
78 : // Just convert to Attributes and call add
79 : final attributes =
80 2 : attributeMap.isEmpty ? null : attributeMap.toAttributes();
81 1 : add(value, attributes);
82 : }
83 :
84 : /// Gets the current value of the counter for a specific set of attributes.
85 : /// If no attributes are provided, returns the sum of all values across all attributes.
86 3 : T getValue([Attributes? attributes]) {
87 6 : return _storage.getValue(attributes);
88 : }
89 :
90 : /// Gets the current points for this counter.
91 : /// This is used by the SDK to collect metrics.
92 1 : List<MetricPoint<T>> collectPoints() {
93 2 : return _storage.collectPoints();
94 : }
95 :
96 1 : @override
97 : List<Metric> collectMetrics() {
98 2 : if (!enabled) return [];
99 :
100 : // Get the points from storage
101 1 : final points = collectPoints();
102 :
103 1 : if (points.isEmpty) return [];
104 :
105 : // Create a metric with the collected points
106 1 : final metric = Metric(
107 1 : name: name,
108 1 : description: description,
109 1 : unit: unit,
110 : type: MetricType.sum, // UpDownCounter is still a sum, just not monotonic
111 : points: points,
112 : );
113 :
114 1 : return [metric];
115 : }
116 :
117 : /// Resets the counter. This is only used for Delta temporality.
118 2 : void reset() {
119 4 : _storage.reset();
120 : }
121 : }
|