Skip to content

Commit 32460b3

Browse files
authored
Merge branch 'main' into feat/span-level-measurement
2 parents afde400 + 39e3ed7 commit 32460b3

Some content is hidden

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

42 files changed

+3251
-15
lines changed

CHANGELOG.md

+1
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
- Fix hub restore point in wrappers: SentryWrapper, SentryTaskDecorator and SentryScheduleHook ([#3225](https://github.com/getsentry/sentry-java/pull/3225))
1616
- We now reset the hub to its previous value on the thread where the `Runnable`/`Callable`/`Supplier` is executed instead of setting it to the hub that was used on the thread where the `Runnable`/`Callable`/`Supplier` was created.
1717
- Fix add missing thread name/id to app start spans ([#3226](https://github.com/getsentry/sentry-java/pull/3226))
18+
- Experimental: Add Metrics API ([#3205](https://github.com/getsentry/sentry-java/pull/3205))
1819

1920
## 7.4.0
2021

sentry-android-core/src/test/java/io/sentry/android/core/ActivityLifecycleIntegrationTest.kt

+1-1
Original file line numberDiff line numberDiff line change
@@ -1024,7 +1024,7 @@ class ActivityLifecycleIntegrationTest {
10241024
// Assert the ttfd span is running and a timeout autoCancel task has been scheduled
10251025
assertNotNull(ttfdSpan)
10261026
assertFalse(ttfdSpan.isFinished)
1027-
assertTrue(deferredExecutorService.scheduledRunnables.isNotEmpty())
1027+
assertTrue(deferredExecutorService.hasScheduledRunnables())
10281028

10291029
// Run the autoClose task and assert the ttfd span is finished with deadlineExceeded
10301030
deferredExecutorService.runAll()

sentry-android-core/src/test/java/io/sentry/android/core/SessionTrackingIntegrationTest.kt

+5
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import androidx.test.core.app.ApplicationProvider
88
import androidx.test.ext.junit.runners.AndroidJUnit4
99
import io.sentry.CheckIn
1010
import io.sentry.Hint
11+
import io.sentry.IMetricsAggregator
1112
import io.sentry.IScope
1213
import io.sentry.ISentryClient
1314
import io.sentry.ProfilingTraceData
@@ -174,5 +175,9 @@ class SessionTrackingIntegrationTest {
174175
override fun getRateLimiter(): RateLimiter? {
175176
TODO("Not yet implemented")
176177
}
178+
179+
override fun getMetricsAggregator(): IMetricsAggregator {
180+
TODO("Not yet implemented")
181+
}
177182
}
178183
}

sentry-samples/sentry-samples-android/src/main/java/io/sentry/samples/android/MyApplication.java

+3
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
import android.app.Application;
44
import android.os.StrictMode;
5+
import io.sentry.Sentry;
56

67
/** Apps. main Application. */
78
public class MyApplication extends Application {
@@ -24,6 +25,8 @@ public void onCreate() {
2425
// });
2526
// */
2627
// });
28+
29+
Sentry.metrics().increment("app.start.cold");
2730
}
2831

2932
private void strictMode() {

sentry-test-support/api/sentry-test-support.api

+1-1
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ public final class io/sentry/SkipError : java/lang/Error {
1414
public final class io/sentry/test/DeferredExecutorService : io/sentry/ISentryExecutorService {
1515
public fun <init> ()V
1616
public fun close (J)V
17-
public final fun getScheduledRunnables ()Ljava/util/ArrayList;
17+
public final fun hasScheduledRunnables ()Z
1818
public fun isClosed ()Z
1919
public final fun runAll ()V
2020
public fun schedule (Ljava/lang/Runnable;J)Ljava/util/concurrent/Future;

sentry-test-support/src/main/kotlin/io/sentry/test/Mocks.kt

+22-6
Original file line numberDiff line numberDiff line change
@@ -28,24 +28,40 @@ class ImmediateExecutorService : ISentryExecutorService {
2828

2929
class DeferredExecutorService : ISentryExecutorService {
3030

31-
private val runnables = ArrayList<Runnable>()
32-
val scheduledRunnables = ArrayList<Runnable>()
31+
private var runnables = ArrayList<Runnable>()
32+
private var scheduledRunnables = ArrayList<Runnable>()
3333

3434
fun runAll() {
35-
runnables.forEach { it.run() }
36-
scheduledRunnables.forEach { it.run() }
35+
// take a snapshot of the runnable list in case
36+
// executing the runnable itself schedules more runnables
37+
val currentRunnableList = runnables
38+
val currentScheduledRunnableList = scheduledRunnables
39+
40+
synchronized(this) {
41+
runnables = ArrayList()
42+
scheduledRunnables = ArrayList()
43+
}
44+
45+
currentRunnableList.forEach { it.run() }
46+
currentScheduledRunnableList.forEach { it.run() }
3747
}
3848

3949
override fun submit(runnable: Runnable): Future<*> {
40-
runnables.add(runnable)
50+
synchronized(this) {
51+
runnables.add(runnable)
52+
}
4153
return mock()
4254
}
4355

4456
override fun <T> submit(callable: Callable<T>): Future<T> = mock()
4557
override fun schedule(runnable: Runnable, delayMillis: Long): Future<*> {
46-
scheduledRunnables.add(runnable)
58+
synchronized(this) {
59+
scheduledRunnables.add(runnable)
60+
}
4761
return mock()
4862
}
4963
override fun close(timeoutMillis: Long) {}
5064
override fun isClosed(): Boolean = false
65+
66+
fun hasScheduledRunnables(): Boolean = scheduledRunnables.isNotEmpty()
5167
}

sentry/api/sentry.api

+203-2
Large diffs are not rendered by default.

sentry/src/main/java/io/sentry/Hub.java

+25-2
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
import io.sentry.clientreport.DiscardReason;
55
import io.sentry.hints.SessionEndHint;
66
import io.sentry.hints.SessionStartHint;
7+
import io.sentry.metrics.MetricsApi;
78
import io.sentry.protocol.SentryId;
89
import io.sentry.protocol.SentryTransaction;
910
import io.sentry.protocol.User;
@@ -17,14 +18,16 @@
1718
import java.io.IOException;
1819
import java.lang.ref.WeakReference;
1920
import java.util.Collections;
21+
import java.util.HashMap;
2022
import java.util.List;
2123
import java.util.Map;
2224
import java.util.WeakHashMap;
2325
import org.jetbrains.annotations.ApiStatus;
2426
import org.jetbrains.annotations.NotNull;
2527
import org.jetbrains.annotations.Nullable;
2628

27-
public final class Hub implements IHub {
29+
public final class Hub implements IHub, MetricsApi.IMetricsInterface {
30+
2831
private volatile @NotNull SentryId lastEventId;
2932
private final @NotNull SentryOptions options;
3033
private volatile boolean isEnabled;
@@ -33,10 +36,10 @@ public final class Hub implements IHub {
3336
private final @NotNull Map<Throwable, Pair<WeakReference<ISpan>, String>> throwableToSpan =
3437
Collections.synchronizedMap(new WeakHashMap<>());
3538
private final @NotNull TransactionPerformanceCollector transactionPerformanceCollector;
39+
private final @NotNull MetricsApi metricsApi;
3640

3741
public Hub(final @NotNull SentryOptions options) {
3842
this(options, createRootStackItem(options));
39-
4043
// Integrations are no longer registered on Hub ctor, but on Sentry.init
4144
}
4245

@@ -52,6 +55,8 @@ private Hub(final @NotNull SentryOptions options, final @NotNull Stack stack) {
5255
// Integrations will use this Hub instance once registered.
5356
// Make sure Hub ready to be used then.
5457
this.isEnabled = true;
58+
59+
this.metricsApi = new MetricsApi(this);
5560
}
5661

5762
private Hub(final @NotNull SentryOptions options, final @NotNull StackItem rootStackItem) {
@@ -937,4 +942,22 @@ private IScope buildLocalScope(
937942
final StackItem item = stack.peek();
938943
return item.getClient().getRateLimiter();
939944
}
945+
946+
@Override
947+
public @NotNull MetricsApi metrics() {
948+
return metricsApi;
949+
}
950+
951+
@Override
952+
public @NotNull IMetricsAggregator getMetricsAggregator() {
953+
return stack.peek().getClient().getMetricsAggregator();
954+
}
955+
956+
@Override
957+
public @NotNull Map<String, String> getDefaultTagsForMetrics() {
958+
final Map<String, String> tags = new HashMap<>(2);
959+
tags.put("release", options.getRelease());
960+
tags.put("environment", options.getEnvironment());
961+
return tags;
962+
}
940963
}

sentry/src/main/java/io/sentry/HubAdapter.java

+6
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package io.sentry;
22

3+
import io.sentry.metrics.MetricsApi;
34
import io.sentry.protocol.SentryId;
45
import io.sentry.protocol.SentryTransaction;
56
import io.sentry.protocol.User;
@@ -272,4 +273,9 @@ public void reportFullyDisplayed() {
272273
public @Nullable RateLimiter getRateLimiter() {
273274
return Sentry.getCurrentHub().getRateLimiter();
274275
}
276+
277+
@Override
278+
public @NotNull MetricsApi metrics() {
279+
return Sentry.getCurrentHub().metrics();
280+
}
275281
}

sentry/src/main/java/io/sentry/IHub.java

+5
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package io.sentry;
22

3+
import io.sentry.metrics.MetricsApi;
34
import io.sentry.protocol.SentryId;
45
import io.sentry.protocol.SentryTransaction;
56
import io.sentry.protocol.User;
@@ -582,4 +583,8 @@ TransactionContext continueTrace(
582583
@ApiStatus.Internal
583584
@Nullable
584585
RateLimiter getRateLimiter();
586+
587+
@ApiStatus.Experimental
588+
@NotNull
589+
MetricsApi metrics();
585590
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
package io.sentry;
2+
3+
import java.io.Closeable;
4+
import java.util.Map;
5+
import org.jetbrains.annotations.NotNull;
6+
import org.jetbrains.annotations.Nullable;
7+
8+
public interface IMetricsAggregator extends Closeable {
9+
10+
/**
11+
* Emits a Counter metric
12+
*
13+
* @param key A unique key identifying the metric
14+
* @param value The value to be added
15+
* @param unit An optional unit, see {@link MeasurementUnit}
16+
* @param tags Optional Tags to associate with the metric
17+
* @param timestampMs The time when the metric was emitted. Defaults to the time at which the
18+
* metric is emitted, if no value is provided.
19+
* @param stackLevel Optional number of stacks levels to ignore when determining the code location
20+
*/
21+
void increment(
22+
final @NotNull String key,
23+
final double value,
24+
final @Nullable MeasurementUnit unit,
25+
final @Nullable Map<String, String> tags,
26+
final long timestampMs,
27+
final int stackLevel);
28+
29+
/**
30+
* Emits a Gauge metric
31+
*
32+
* @param key A unique key identifying the metric
33+
* @param value The value to be added
34+
* @param unit An optional unit, see {@link MeasurementUnit}
35+
* @param tags Optional Tags to associate with the metric
36+
* @param timestampMs The time when the metric was emitted. Defaults to the time at which the
37+
* metric is emitted, if no value is provided.
38+
* @param stackLevel Optional number of stacks levels to ignore when determining the code location
39+
*/
40+
void gauge(
41+
final @NotNull String key,
42+
final double value,
43+
final @Nullable MeasurementUnit unit,
44+
final @Nullable Map<String, String> tags,
45+
final long timestampMs,
46+
final int stackLevel);
47+
48+
/**
49+
* Emits a Distribution metric
50+
*
51+
* @param key A unique key identifying the metric
52+
* @param value The value to be added
53+
* @param unit An optional unit, see {@link MeasurementUnit}
54+
* @param tags Optional Tags to associate with the metric
55+
* @param timestampMs The time when the metric was emitted. Defaults to the time at which the
56+
* metric is emitted, if no value is provided.
57+
* @param stackLevel Optional number of stacks levels to ignore when determining the code location
58+
*/
59+
void distribution(
60+
final @NotNull String key,
61+
final double value,
62+
final @Nullable MeasurementUnit unit,
63+
final @Nullable Map<String, String> tags,
64+
final long timestampMs,
65+
final int stackLevel);
66+
67+
/**
68+
* Emits a Set metric
69+
*
70+
* @param key A unique key identifying the metric
71+
* @param value The value to be added
72+
* @param unit An optional unit, see {@link MeasurementUnit}
73+
* @param tags Optional Tags to associate with the metric
74+
* @param timestampMs The time when the metric was emitted. Defaults to the time at which the
75+
* metric is emitted, if no value is provided.
76+
* @param stackLevel Optional number of stacks levels to ignore when determining the code location
77+
*/
78+
void set(
79+
final @NotNull String key,
80+
final int value,
81+
final @Nullable MeasurementUnit unit,
82+
final @Nullable Map<String, String> tags,
83+
final long timestampMs,
84+
final int stackLevel);
85+
86+
/**
87+
* Emits a Set metric
88+
*
89+
* @param key A unique key identifying the metric
90+
* @param value The value to be added
91+
* @param unit An optional unit, see {@link MeasurementUnit}
92+
* @param tags Optional Tags to associate with the metric
93+
* @param timestampMs The time when the metric was emitted. Defaults to the time at which the
94+
* metric is emitted, if no value is provided.
95+
* @param stackLevel Optional number of stacks levels to ignore when determining the code location
96+
*/
97+
void set(
98+
final @NotNull String key,
99+
final @NotNull String value,
100+
final @Nullable MeasurementUnit unit,
101+
final @Nullable Map<String, String> tags,
102+
final long timestampMs,
103+
final int stackLevel);
104+
105+
/**
106+
* Emits a distribution with the time it takes to run a given code block.
107+
*
108+
* @param key A unique key identifying the metric
109+
* @param callback The code block to measure
110+
* @param unit An optional unit, see {@link MeasurementUnit.Duration}, defaults to seconds
111+
* @param tags Optional Tags to associate with the metric
112+
* @param stackLevel Optional number of stacks levels to ignore when determining the code location
113+
*/
114+
void timing(
115+
final @NotNull String key,
116+
final @NotNull Runnable callback,
117+
final @NotNull MeasurementUnit.Duration unit,
118+
final @Nullable Map<String, String> tags,
119+
final int stackLevel);
120+
121+
void flush(boolean force);
122+
}

sentry/src/main/java/io/sentry/ISentryClient.java

+4
Original file line numberDiff line numberDiff line change
@@ -285,4 +285,8 @@ SentryId captureTransaction(
285285
default boolean isHealthy() {
286286
return true;
287287
}
288+
289+
@ApiStatus.Internal
290+
@NotNull
291+
IMetricsAggregator getMetricsAggregator();
288292
}

0 commit comments

Comments
 (0)