Skip to content

Commit ed83106

Browse files
authored
Merge branch 'main' into feat/text-scale-value
2 parents 21fee1c + 4b943a1 commit ed83106

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

68 files changed

+3054
-110
lines changed

.github/workflows/metrics.yml

+1-1
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ jobs:
6060
- uses: actions/setup-java@v4
6161
if: ${{ matrix.platform == 'android' }}
6262
with:
63-
java-version: "11"
63+
java-version: "17"
6464
distribution: "adopt"
6565

6666
- run: ./metrics/prepare.sh

CHANGELOG.md

+20-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,25 @@
11
# Changelog
22

3-
## Unreleased
3+
## 7.19.0
4+
5+
### Features
6+
7+
- Experimental: Add support for Sentry Developer Metrics ([#1940](https://github.com/getsentry/sentry-dart/pull/1940), [#1949](https://github.com/getsentry/sentry-dart/pull/1949), [#1954](https://github.com/getsentry/sentry-dart/pull/1954), [#1958](https://github.com/getsentry/sentry-dart/pull/1958))
8+
Use the Metrics API to track processing time, download sizes, user signups, and conversion rates and correlate them back to tracing data in order to get deeper insights and solve issues faster. Our API supports counters, distributions, sets, gauges and timers, and it's easy to get started:
9+
```dart
10+
Sentry.metrics()
11+
.increment(
12+
'button_login_click', // key
13+
value: 1.0,
14+
unit: null,
15+
tags: {"provider": "e-mail"}
16+
);
17+
```
18+
To learn more about Sentry Developer Metrics, head over to our [Dart](https://docs.sentry.io/platforms/dart/metrics/) and [Flutter](https://docs.sentry.io/platforms/flutter/metrics/) docs page.
19+
20+
### Dependencies
21+
22+
- Expand `package_info_plus` version range to `6.0.0` ([#1948](https://github.com/getsentry/sentry-dart/pull/1948))
423

524
### Features
625

dart/lib/src/hub.dart

+48
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,9 @@ import 'dart:async';
22
import 'dart:collection';
33

44
import 'package:meta/meta.dart';
5+
import 'metrics/metric.dart';
6+
import 'metrics/metrics_aggregator.dart';
7+
import 'metrics/metrics_api.dart';
58
import 'profiling.dart';
69
import 'propagation_context.dart';
710
import 'transport/data_category.dart';
@@ -38,6 +41,14 @@ class Hub {
3841

3942
late final _WeakMap _throwableToSpan;
4043

44+
late final MetricsApi _metricsApi;
45+
46+
@internal
47+
MetricsApi get metricsApi => _metricsApi;
48+
49+
@internal
50+
MetricsAggregator? get metricsAggregator => _peek().client.metricsAggregator;
51+
4152
factory Hub(SentryOptions options) {
4253
_validateOptions(options);
4354

@@ -49,6 +60,7 @@ class Hub {
4960
_stack.add(_StackItem(_getClient(_options), Scope(_options)));
5061
_isEnabled = true;
5162
_throwableToSpan = _WeakMap(_options);
63+
_metricsApi = MetricsApi(hub: this);
5264
}
5365

5466
static void _validateOptions(SentryOptions options) {
@@ -554,6 +566,42 @@ class Hub {
554566
return sentryId;
555567
}
556568

569+
@internal
570+
Future<SentryId> captureMetrics(
571+
Map<int, Iterable<Metric>> metricsBuckets) async {
572+
var sentryId = SentryId.empty();
573+
574+
if (!_isEnabled) {
575+
_options.logger(
576+
SentryLevel.warning,
577+
"Instance is disabled and this 'captureMetrics' call is a no-op.",
578+
);
579+
} else if (!_options.enableMetrics) {
580+
_options.logger(
581+
SentryLevel.info,
582+
"Metrics are disabled and this 'captureMetrics' call is a no-op.",
583+
);
584+
} else if (metricsBuckets.isEmpty) {
585+
_options.logger(
586+
SentryLevel.info,
587+
"Metrics are empty and this 'captureMetrics' call is a no-op.",
588+
);
589+
} else {
590+
final item = _peek();
591+
try {
592+
sentryId = await item.client.captureMetrics(metricsBuckets);
593+
} catch (exception, stackTrace) {
594+
_options.logger(
595+
SentryLevel.error,
596+
'Error while capturing metrics.',
597+
exception: exception,
598+
stackTrace: stackTrace,
599+
);
600+
}
601+
}
602+
return sentryId;
603+
}
604+
557605
@internal
558606
void setSpanContext(
559607
dynamic throwable,

dart/lib/src/hub_adapter.dart

+15
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,9 @@ import 'package:meta/meta.dart';
44
import 'hint.dart';
55

66
import 'hub.dart';
7+
import 'metrics/metric.dart';
8+
import 'metrics/metrics_aggregator.dart';
9+
import 'metrics/metrics_api.dart';
710
import 'profiling.dart';
811
import 'protocol.dart';
912
import 'scope.dart';
@@ -23,6 +26,10 @@ class HubAdapter implements Hub {
2326
@internal
2427
SentryOptions get options => Sentry.currentHub.options;
2528

29+
@override
30+
@internal
31+
MetricsApi get metricsApi => Sentry.currentHub.metricsApi;
32+
2633
factory HubAdapter() {
2734
return _instance;
2835
}
@@ -181,4 +188,12 @@ class HubAdapter implements Hub {
181188

182189
@override
183190
Scope get scope => Sentry.currentHub.scope;
191+
192+
@override
193+
Future<SentryId> captureMetrics(Map<int, Iterable<Metric>> metricsBuckets) =>
194+
Sentry.currentHub.captureMetrics(metricsBuckets);
195+
196+
@override
197+
MetricsAggregator? get metricsAggregator =>
198+
Sentry.currentHub.metricsAggregator;
184199
}
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+
}

0 commit comments

Comments
 (0)