Skip to content

Commit 64400d3

Browse files
authored
Add other metric types and weight (part 2) (#1949)
* added crc32_utils.dart, taken from archive library * added Gauge, Distribution and Set metrics * added weight to Metrics and auto flush when weight is too much * Add timing metric and beforeMetric callback (part 3) (#1954) * added SentryOptions.beforeMetricCallback * added beforeMetricCallback logic in metrics_aggregator.dart * added timing metric api with span auto start * timing api uses span duration as value for the emitted metric if possible * Feat/metrics span summary p4 (#1958) * added local_metrics_aggregator.dart to spans * metrics_aggregator.dart now adds to current span's localMetricsAggregator * added metric_summary.dart * added metricSummary to spans and transaction JSONs
1 parent 64e1732 commit 64400d3

26 files changed

+1678
-112
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
import 'dart:core';
2+
import 'package:meta/meta.dart';
3+
import '../protocol/metric_summary.dart';
4+
import 'metric.dart';
5+
6+
@internal
7+
class LocalMetricsAggregator {
8+
// format: <export key, <metric key, gauge>>
9+
final Map<String, Map<String, GaugeMetric>> _buckets = {};
10+
11+
void add(final Metric metric, final num value) {
12+
final bucket =
13+
_buckets.putIfAbsent(metric.getSpanAggregationKey(), () => {});
14+
15+
bucket.update(metric.getCompositeKey(), (m) => m..add(value),
16+
ifAbsent: () => Metric.fromType(
17+
type: MetricType.gauge,
18+
key: metric.key,
19+
value: value,
20+
unit: metric.unit,
21+
tags: metric.tags) as GaugeMetric);
22+
}
23+
24+
Map<String, List<MetricSummary>> getSummaries() {
25+
final Map<String, List<MetricSummary>> summaries = {};
26+
for (final entry in _buckets.entries) {
27+
final String exportKey = entry.key;
28+
29+
final metricSummaries = entry.value.values
30+
.map((gauge) => MetricSummary.fromGauge(gauge))
31+
.toList();
32+
33+
summaries[exportKey] = metricSummaries;
34+
}
35+
return summaries;
36+
}
37+
}

dart/lib/src/metrics/metric.dart

+140-7
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import 'dart:math';
2+
13
import 'package:meta/meta.dart';
24

35
import '../../sentry.dart';
@@ -26,8 +28,31 @@ abstract class Metric {
2628
required this.tags,
2729
});
2830

31+
factory Metric.fromType({
32+
required final MetricType type,
33+
required final String key,
34+
required final num value,
35+
required final SentryMeasurementUnit unit,
36+
required final Map<String, String> tags,
37+
}) {
38+
switch (type) {
39+
case MetricType.counter:
40+
return CounterMetric._(value: value, key: key, unit: unit, tags: tags);
41+
case MetricType.gauge:
42+
return GaugeMetric._(value: value, key: key, unit: unit, tags: tags);
43+
case MetricType.set:
44+
return SetMetric._(value: value, key: key, unit: unit, tags: tags);
45+
case MetricType.distribution:
46+
return DistributionMetric._(
47+
value: value, key: key, unit: unit, tags: tags);
48+
}
49+
}
50+
2951
/// Add a value to the metric.
30-
add(double value);
52+
add(num value);
53+
54+
/// Return the weight of the current metric.
55+
int getWeight();
3156

3257
/// Serialize the value into a list of Objects to be converted into a String.
3358
Iterable<Object> _serializeValue();
@@ -87,6 +112,10 @@ abstract class Metric {
87112
return ('${type.statsdType}_${key}_${unit.name}_$serializedTags');
88113
}
89114

115+
/// Return a key created by [key], [type] and [unit].
116+
/// This key should be used to aggregate the metric locally in a span.
117+
String getSpanAggregationKey() => '${type.statsdType}:$key@${unit.name}';
118+
90119
/// Remove forbidden characters from the metric key and tag key.
91120
String _normalizeKey(String input) =>
92121
input.replaceAll(forbiddenKeyCharsRegex, '_');
@@ -100,31 +129,135 @@ abstract class Metric {
100129
input.replaceAll(forbiddenUnitCharsRegex, '_');
101130
}
102131

103-
@internal
104-
105132
/// Metric [MetricType.counter] that tracks a value that can only be incremented.
133+
@internal
106134
class CounterMetric extends Metric {
107-
double value;
135+
num value;
108136

109-
CounterMetric({
137+
CounterMetric._({
110138
required this.value,
111139
required super.key,
112140
required super.unit,
113141
required super.tags,
114142
}) : super(type: MetricType.counter);
115143

116144
@override
117-
add(double value) => this.value += value;
145+
add(num value) => this.value += value;
118146

119147
@override
120148
Iterable<Object> _serializeValue() => [value];
149+
150+
@override
151+
int getWeight() => 1;
121152
}
122153

154+
/// Metric [MetricType.gauge] that tracks a value that can go up and down.
123155
@internal
156+
class GaugeMetric extends Metric {
157+
num _last;
158+
num _minimum;
159+
num _maximum;
160+
num _sum;
161+
int _count;
162+
163+
GaugeMetric._({
164+
required num value,
165+
required super.key,
166+
required super.unit,
167+
required super.tags,
168+
}) : _last = value,
169+
_minimum = value,
170+
_maximum = value,
171+
_sum = value,
172+
_count = 1,
173+
super(type: MetricType.gauge);
174+
175+
@override
176+
add(num value) {
177+
_last = value;
178+
_minimum = min(_minimum, value);
179+
_maximum = max(_maximum, value);
180+
_sum += value;
181+
_count++;
182+
}
183+
184+
@override
185+
Iterable<Object> _serializeValue() =>
186+
[_last, _minimum, _maximum, _sum, _count];
187+
188+
@override
189+
int getWeight() => 5;
190+
191+
@visibleForTesting
192+
num get last => _last;
193+
num get minimum => _minimum;
194+
num get maximum => _maximum;
195+
num get sum => _sum;
196+
int get count => _count;
197+
}
198+
199+
/// Metric [MetricType.set] that tracks a set of values on which you can perform
200+
/// aggregations such as count_unique.
201+
@internal
202+
class SetMetric extends Metric {
203+
final Set<int> _values = {};
204+
205+
SetMetric._(
206+
{required num value,
207+
required super.key,
208+
required super.unit,
209+
required super.tags})
210+
: super(type: MetricType.set) {
211+
add(value);
212+
}
213+
214+
@override
215+
add(num value) => _values.add(value.toInt());
216+
217+
@override
218+
Iterable<Object> _serializeValue() => _values;
219+
220+
@override
221+
int getWeight() => _values.length;
222+
223+
@visibleForTesting
224+
Set<num> get values => _values;
225+
}
226+
227+
/// Metric [MetricType.distribution] that tracks a list of values.
228+
@internal
229+
class DistributionMetric extends Metric {
230+
final List<num> _values = [];
231+
232+
DistributionMetric._(
233+
{required num value,
234+
required super.key,
235+
required super.unit,
236+
required super.tags})
237+
: super(type: MetricType.distribution) {
238+
add(value);
239+
}
240+
241+
@override
242+
add(num value) => _values.add(value);
243+
244+
@override
245+
Iterable<Object> _serializeValue() => _values;
246+
247+
@override
248+
int getWeight() => _values.length;
249+
250+
@visibleForTesting
251+
List<num> get values => _values;
252+
}
124253

125254
/// The metric type and its associated statsd encoded value.
255+
@internal
126256
enum MetricType {
127-
counter('c');
257+
counter('c'),
258+
gauge('g'),
259+
distribution('d'),
260+
set('s');
128261

129262
final String statsdType;
130263

0 commit comments

Comments
 (0)