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.dart';
7 : import '../data/metric_point.dart';
8 : import '../meter.dart';
9 : import '../storage/sum_storage.dart';
10 : import 'base_instrument.dart';
11 :
12 : /// A synchronous instrument that records monotonically increasing values.
13 : ///
14 : /// A Counter is used to measure a non-negative, monotonically increasing value.
15 : /// Counters only allow positive increments and are appropriate for values that
16 : /// never decrease, such as:
17 : /// - Request count
18 : /// - Completed operations
19 : /// - Error count
20 : /// - CPU time used
21 : /// - Bytes sent/received
22 : ///
23 : /// If the value can decrease, use an UpDownCounter instead.
24 : ///
25 : /// More information:
26 : /// https://opentelemetry.io/docs/specs/otel/metrics/api/#counter
27 : class Counter<T extends num> implements APICounter<T>, SDKInstrument {
28 : /// The underlying API Counter.
29 : final APICounter<T> _apiCounter;
30 :
31 : /// The Meter that created this Counter.
32 : final Meter _meter;
33 :
34 : /// Storage for accumulating counter measurements.
35 : final SumStorage<T> _storage;
36 :
37 : /// Creates a new Counter instance.
38 : ///
39 : /// @param apiCounter The API Counter to delegate API calls to
40 : /// @param meter The Meter that created this Counter
41 18 : Counter({required APICounter<T> apiCounter, required Meter meter})
42 : : _apiCounter = apiCounter,
43 : _meter = meter,
44 18 : _storage = SumStorage<T>(
45 : isMonotonic: true,
46 36 : exemplarFilter: meter.provider.exemplarFilter,
47 : ) {
48 90 : _meter.provider.registerInstrument(_meter.name, this);
49 : }
50 :
51 : /// Gets the name of this counter.
52 18 : @override
53 36 : String get name => _apiCounter.name;
54 :
55 : /// Gets the unit of measurement for this counter.
56 13 : @override
57 26 : String? get unit => _apiCounter.unit;
58 :
59 : /// Gets the description of this counter.
60 13 : @override
61 26 : String? get description => _apiCounter.description;
62 :
63 : /// Checks if this counter is enabled.
64 : ///
65 : /// If false, measurements will be dropped and not recorded.
66 16 : @override
67 32 : bool isEnabled() => _meter.isEnabled();
68 :
69 : /// Gets the meter that created this counter.
70 18 : @override
71 18 : APIMeter get meter => _meter;
72 :
73 1 : @override
74 2 : InstrumentAdvisory? get advisory => _apiCounter.advisory;
75 :
76 : /// Always true for Counter instruments.
77 1 : @override
78 : bool get isCounter => true;
79 :
80 : /// Always false for Counter instruments.
81 1 : @override
82 : bool get isUpDownCounter => false;
83 :
84 : /// Always false for Counter instruments.
85 1 : @override
86 : bool get isGauge => false;
87 :
88 : /// Always false for Counter instruments.
89 1 : @override
90 : bool get isHistogram => false;
91 :
92 : /// Records a measurement with this counter.
93 : ///
94 : /// This method increments the counter by the given value. The value must be
95 : /// non-negative, or an ArgumentError will be thrown.
96 : ///
97 : /// @param value The amount to increment the counter by (must be non-negative)
98 : /// @param attributes Optional attributes to associate with this measurement
99 : /// @throws ArgumentError if value is negative
100 16 : @override
101 : void add(T value, [Attributes? attributes]) {
102 : // First use the API implementation (no-op by default)
103 32 : _apiCounter.add(value, attributes);
104 :
105 : // Check for negative values
106 16 : if (value < 0) {
107 1 : throw ArgumentError('Counter value must be non-negative');
108 : }
109 :
110 : // Only record if enabled
111 16 : if (!isEnabled()) return;
112 :
113 : // Record the measurement in our storage
114 48 : _storage.record(value, attributes, Context.current);
115 : }
116 :
117 : /// Records a measurement with attributes specified as a map.
118 : ///
119 : /// This is a convenience method that converts the map to Attributes
120 : /// and calls add().
121 : ///
122 : /// @param value The amount to increment the counter by (must be non-negative)
123 : /// @param attributeMap Map of attribute names to values
124 1 : @override
125 : void addWithMap(T value, Map<String, Object> attributeMap) {
126 : // Just convert to Attributes and call add
127 : final attributes =
128 2 : attributeMap.isEmpty ? null : attributeMap.toAttributes();
129 1 : add(value, attributes);
130 : }
131 :
132 : /// Gets the current value of the counter for a specific set of attributes.
133 : ///
134 : /// If no attributes are provided, returns the sum of all values across all attributes.
135 : ///
136 : /// @param attributes Optional attributes to filter by
137 : /// @return The current value of the counter
138 4 : T getValue([Attributes? attributes]) {
139 8 : return _storage.getValue(attributes);
140 : }
141 :
142 : /// Gets the current points for this counter.
143 : ///
144 : /// This is used by the SDK to collect metrics for export.
145 : ///
146 : /// @return A list of metric points containing the current counter values
147 12 : List<MetricPoint<T>> collectPoints() {
148 24 : return _storage.collectPoints();
149 : }
150 :
151 : /// Collects metrics for this counter.
152 : ///
153 : /// This method is called by the SDK to collect metrics for export.
154 : ///
155 : /// @return A list of metrics containing the current counter values
156 12 : @override
157 : List<Metric> collectMetrics() {
158 13 : if (!isEnabled()) return [];
159 :
160 : // Get the points from storage
161 12 : final points = collectPoints();
162 :
163 12 : if (points.isEmpty) return [];
164 :
165 12 : final metric = Metric(
166 12 : name: name,
167 12 : description: description,
168 12 : unit: unit,
169 : type: MetricType.sum,
170 24 : isMonotonic: _storage.isMonotonic,
171 : points: points,
172 : );
173 :
174 12 : return [metric];
175 : }
176 :
177 : /// Resets the counter.
178 : ///
179 : /// This is only used for Delta temporality and should not be called
180 : /// by application code.
181 2 : void reset() {
182 4 : _storage.reset();
183 : }
184 : }
|