Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add other metric types and weight (part 2) #1949

Merged
merged 7 commits into from
Apr 4, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 37 additions & 0 deletions dart/lib/src/metrics/local_metrics_aggregator.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import 'dart:core';
import 'package:meta/meta.dart';
import '../protocol/metric_summary.dart';
import 'metric.dart';

@internal
class LocalMetricsAggregator {
// format: <export key, <metric key, gauge>>
final Map<String, Map<String, GaugeMetric>> _buckets = {};

void add(final Metric metric, final num value) {
final bucket =
_buckets.putIfAbsent(metric.getSpanAggregationKey(), () => {});

bucket.update(metric.getCompositeKey(), (m) => m..add(value),
ifAbsent: () => Metric.fromType(
type: MetricType.gauge,
key: metric.key,
value: value,
unit: metric.unit,
tags: metric.tags) as GaugeMetric);
}

Map<String, List<MetricSummary>> getSummaries() {
final Map<String, List<MetricSummary>> summaries = {};
for (final entry in _buckets.entries) {
final String exportKey = entry.key;

final metricSummaries = entry.value.values
.map((gauge) => MetricSummary.fromGauge(gauge))
.toList();

summaries[exportKey] = metricSummaries;
}
return summaries;
}
}
147 changes: 140 additions & 7 deletions dart/lib/src/metrics/metric.dart
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import 'dart:math';

import 'package:meta/meta.dart';

import '../../sentry.dart';
Expand Down Expand Up @@ -26,8 +28,31 @@ abstract class Metric {
required this.tags,
});

factory Metric.fromType({
required final MetricType type,
required final String key,
required final num value,
required final SentryMeasurementUnit unit,
required final Map<String, String> tags,
}) {
switch (type) {
case MetricType.counter:
return CounterMetric._(value: value, key: key, unit: unit, tags: tags);
case MetricType.gauge:
return GaugeMetric._(value: value, key: key, unit: unit, tags: tags);
case MetricType.set:
return SetMetric._(value: value, key: key, unit: unit, tags: tags);
case MetricType.distribution:
return DistributionMetric._(
value: value, key: key, unit: unit, tags: tags);
}
}

/// Add a value to the metric.
add(double value);
add(num value);

/// Return the weight of the current metric.
int getWeight();

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

/// Return a key created by [key], [type] and [unit].
/// This key should be used to aggregate the metric locally in a span.
String getSpanAggregationKey() => '${type.statsdType}:$key@${unit.name}';

/// Remove forbidden characters from the metric key and tag key.
String _normalizeKey(String input) =>
input.replaceAll(forbiddenKeyCharsRegex, '_');
Expand All @@ -100,31 +129,135 @@ abstract class Metric {
input.replaceAll(forbiddenUnitCharsRegex, '_');
}

@internal

/// Metric [MetricType.counter] that tracks a value that can only be incremented.
@internal
class CounterMetric extends Metric {
double value;
num value;

CounterMetric({
CounterMetric._({
required this.value,
required super.key,
required super.unit,
required super.tags,
}) : super(type: MetricType.counter);

@override
add(double value) => this.value += value;
add(num value) => this.value += value;

@override
Iterable<Object> _serializeValue() => [value];

@override
int getWeight() => 1;
}

/// Metric [MetricType.gauge] that tracks a value that can go up and down.
@internal
class GaugeMetric extends Metric {
num _last;
num _minimum;
num _maximum;
num _sum;
int _count;

GaugeMetric._({
required num value,
required super.key,
required super.unit,
required super.tags,
}) : _last = value,
_minimum = value,
_maximum = value,
_sum = value,
_count = 1,
super(type: MetricType.gauge);

@override
add(num value) {
_last = value;
_minimum = min(_minimum, value);
_maximum = max(_maximum, value);
_sum += value;
_count++;
}

@override
Iterable<Object> _serializeValue() =>
[_last, _minimum, _maximum, _sum, _count];

@override
int getWeight() => 5;

@visibleForTesting
num get last => _last;
num get minimum => _minimum;
num get maximum => _maximum;
num get sum => _sum;
int get count => _count;
}

/// Metric [MetricType.set] that tracks a set of values on which you can perform
/// aggregations such as count_unique.
@internal
class SetMetric extends Metric {
final Set<int> _values = {};

SetMetric._(
{required num value,
required super.key,
required super.unit,
required super.tags})
: super(type: MetricType.set) {
add(value);
}

@override
add(num value) => _values.add(value.toInt());
denrase marked this conversation as resolved.
Show resolved Hide resolved

@override
Iterable<Object> _serializeValue() => _values;

@override
int getWeight() => _values.length;

@visibleForTesting
Set<num> get values => _values;
}

/// Metric [MetricType.distribution] that tracks a list of values.
@internal
class DistributionMetric extends Metric {
final List<num> _values = [];

DistributionMetric._(
{required num value,
required super.key,
required super.unit,
required super.tags})
: super(type: MetricType.distribution) {
add(value);
}

@override
add(num value) => _values.add(value);

@override
Iterable<Object> _serializeValue() => _values;

@override
int getWeight() => _values.length;

@visibleForTesting
List<num> get values => _values;
}

/// The metric type and its associated statsd encoded value.
@internal
enum MetricType {
counter('c');
counter('c'),
gauge('g'),
distribution('d'),
set('s');

final String statsdType;

Expand Down
Loading
Loading