Skip to content

Commit

Permalink
Merge branch 'main' into feat/cpu-info-for-device-context
Browse files Browse the repository at this point in the history
  • Loading branch information
markushi committed Mar 28, 2023
2 parents 3991f2f + 17ab223 commit 7079193
Show file tree
Hide file tree
Showing 24 changed files with 798 additions and 49 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,17 @@

### Features

- Add `name` and `geo` to `User` ([#2556](https://github.com/getsentry/sentry-java/pull/2556))
- Add time-to-initial-display and time-to-full-display measurements to Activity transactions ([#2611](https://github.com/getsentry/sentry-java/pull/2611))
- Read integration list written by sentry gradle plugin from manifest ([#2598](https://github.com/getsentry/sentry-java/pull/2598))
- Add Logcat adapter ([#2620](https://github.com/getsentry/sentry-java/pull/2620))
- Provide CPU count/frequency data as device context ([#2622](https://github.com/getsentry/sentry-java/pull/2622))

### Fixes

- Fix Automatic UI transactions having wrong durations ([#2623](https://github.com/getsentry/sentry-java/pull/2623))
- Fix wrong default environment in Session ([#2610](https://github.com/getsentry/sentry-java/pull/2610))
- Pass through unknown sentry baggage keys into SentryEnvelopeHeader ([#2618](https://github.com/getsentry/sentry-java/pull/2618))

## 6.16.0

Expand Down
4 changes: 3 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -96,7 +96,9 @@ This directory is also included in `.gitignore` not to be shown as pending chang

# Sentry Self Hosted Compatibility

Since version 3.0.0 of this SDK, Sentry version >= v20.6.0 is required. This only applies to on-premise Sentry, if you are using [sentry.io](http://sentry.io/) no action is needed.
Since version 3.0.0 of this SDK, Sentry version >= v20.6.0 is required. This only applies to self-hosted Sentry, if you are using [sentry.io](http://sentry.io/) no action is needed.

Since version 6.0.0 of this SDK, Sentry version >= v21.9.0 is required or you have to manually disable sending client reports via the `sendClientReports` option. This only applies to self-hosted Sentry, if you are using [sentry.io](http://sentry.io/) no action is needed.

# Resources

Expand Down
18 changes: 18 additions & 0 deletions sentry-android-core/api/sentry-android-core.api
Original file line number Diff line number Diff line change
Expand Up @@ -220,6 +220,24 @@ public final class io/sentry/android/core/SentryInitProvider {
public fun shutdown ()V
}

public final class io/sentry/android/core/SentryLogcatAdapter {
public fun <init> ()V
public static fun d (Ljava/lang/String;Ljava/lang/String;)I
public static fun d (Ljava/lang/String;Ljava/lang/String;Ljava/lang/Throwable;)I
public static fun e (Ljava/lang/String;Ljava/lang/String;)I
public static fun e (Ljava/lang/String;Ljava/lang/String;Ljava/lang/Throwable;)I
public static fun i (Ljava/lang/String;Ljava/lang/String;)I
public static fun i (Ljava/lang/String;Ljava/lang/String;Ljava/lang/Throwable;)I
public static fun v (Ljava/lang/String;Ljava/lang/String;)I
public static fun v (Ljava/lang/String;Ljava/lang/String;Ljava/lang/Throwable;)I
public static fun w (Ljava/lang/String;Ljava/lang/String;)I
public static fun w (Ljava/lang/String;Ljava/lang/String;Ljava/lang/Throwable;)I
public static fun w (Ljava/lang/String;Ljava/lang/Throwable;)I
public static fun wtf (Ljava/lang/String;Ljava/lang/String;)I
public static fun wtf (Ljava/lang/String;Ljava/lang/String;Ljava/lang/Throwable;)I
public static fun wtf (Ljava/lang/String;Ljava/lang/Throwable;)I
}

public final class io/sentry/android/core/SentryPerformanceProvider : android/app/Application$ActivityLifecycleCallbacks {
public fun <init> ()V
public fun attachInfo (Landroid/content/Context;Landroid/content/pm/ProviderInfo;)V
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -208,18 +208,24 @@ private void startTracing(final @NotNull Activity activity) {
}
});

// This will be the start timestamp of the transaction, as well as the ttid/ttfd spans
final @NotNull SentryDate ttidStartTime;

if (!(firstActivityCreated || appStartTime == null || coldStart == null)) {
transactionOptions.setStartTimestamp(appStartTime);
// The first activity ttid/ttfd spans should start at the app start time
ttidStartTime = appStartTime;
} else {
// The ttid/ttfd spans should start when the previous activity called its onPause method
ttidStartTime = lastPausedTime;
}
transactionOptions.setStartTimestamp(ttidStartTime);

// we can only bind to the scope if there's no running transaction
ITransaction transaction =
hub.startTransaction(
new TransactionContext(activityName, TransactionNameSource.COMPONENT, UI_LOAD_OP),
transactionOptions);

final @NotNull SentryDate ttidStartTime;

// in case appStartTime isn't available, we don't create a span for it.
if (!(firstActivityCreated || appStartTime == null || coldStart == null)) {
// start specific span for app start
Expand All @@ -233,12 +239,6 @@ private void startTracing(final @NotNull Activity activity) {
// in case there's already an end time (e.g. due to deferred SDK init)
// we can finish the app-start span
finishAppStartSpan();

// The first activity ttid/ttfd spans should start at the app start time
ttidStartTime = appStartTime;
} else {
// The ttid/ttfd spans should start when the previous activity called its onPause method
ttidStartTime = lastPausedTime;
}
ttidSpanMap.put(
activity,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
package io.sentry.android.core;

import android.util.Log;
import io.sentry.Breadcrumb;
import io.sentry.Sentry;
import io.sentry.SentryLevel;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

/**
* This class replaces {@link android.util.Log} with its own implementations which creates a {@link
* io.sentry.Breadcrumb} for each log. It only replaces log functions that meet a minimum level set
* by the user on the Sentry Android Gradle Plugin.
*/
@ApiStatus.Internal
public final class SentryLogcatAdapter {

private static void addAsBreadcrumb(
@Nullable String tag, @NotNull SentryLevel level, @Nullable String msg) {
addAsBreadcrumb(tag, level, msg, null);
}

private static void addAsBreadcrumb(
@Nullable String tag, @NotNull SentryLevel level, @Nullable Throwable tr) {
addAsBreadcrumb(tag, level, null, tr);
}

private static void addAsBreadcrumb(
@Nullable final String tag,
@NotNull final SentryLevel level,
@Nullable final String msg,
@Nullable final Throwable tr) {
Breadcrumb breadcrumb = new Breadcrumb();
breadcrumb.setCategory("Logcat");
breadcrumb.setMessage(msg);
breadcrumb.setLevel(level);
if (tag != null) {
breadcrumb.setData("tag", tag);
}
if (tr != null && tr.getMessage() != null) {
breadcrumb.setData("throwable", tr.getMessage());
}
Sentry.addBreadcrumb(breadcrumb);
}

public static int v(@Nullable String tag, @Nullable String msg) {
addAsBreadcrumb(tag, SentryLevel.DEBUG, msg);
return Log.v(tag, msg);
}

public static int v(@Nullable String tag, @Nullable String msg, @Nullable Throwable tr) {
addAsBreadcrumb(tag, SentryLevel.DEBUG, msg, tr);
return Log.v(tag, msg, tr);
}

public static int d(@Nullable String tag, @Nullable String msg) {
addAsBreadcrumb(tag, SentryLevel.DEBUG, msg);
return Log.d(tag, msg);
}

public static int d(@Nullable String tag, @Nullable String msg, @Nullable Throwable tr) {
addAsBreadcrumb(tag, SentryLevel.DEBUG, msg, tr);
return Log.d(tag, msg, tr);
}

public static int i(@Nullable String tag, @Nullable String msg) {
addAsBreadcrumb(tag, SentryLevel.INFO, msg);
return Log.i(tag, msg);
}

public static int i(@Nullable String tag, @Nullable String msg, @Nullable Throwable tr) {
addAsBreadcrumb(tag, SentryLevel.INFO, msg, tr);
return Log.i(tag, msg, tr);
}

public static int w(@Nullable String tag, @Nullable String msg) {
addAsBreadcrumb(tag, SentryLevel.WARNING, msg);
return Log.w(tag, msg);
}

public static int w(@Nullable String tag, @Nullable String msg, @Nullable Throwable tr) {
addAsBreadcrumb(tag, SentryLevel.WARNING, msg, tr);
return Log.w(tag, msg, tr);
}

public static int w(@Nullable String tag, @Nullable Throwable tr) {
addAsBreadcrumb(tag, SentryLevel.WARNING, tr);
return Log.w(tag, tr);
}

public static int e(@Nullable String tag, @Nullable String msg) {
addAsBreadcrumb(tag, SentryLevel.ERROR, msg);
return Log.e(tag, msg);
}

public static int e(@Nullable String tag, @Nullable String msg, @Nullable Throwable tr) {
addAsBreadcrumb(tag, SentryLevel.ERROR, msg, tr);
return Log.e(tag, msg, tr);
}

public static int wtf(@Nullable String tag, @Nullable String msg) {
addAsBreadcrumb(tag, SentryLevel.ERROR, msg);
return Log.wtf(tag, msg);
}

public static int wtf(@Nullable String tag, @Nullable Throwable tr) {
addAsBreadcrumb(tag, SentryLevel.ERROR, tr);
return Log.wtf(tag, tr);
}

public static int wtf(@Nullable String tag, @Nullable String msg, @Nullable Throwable tr) {
addAsBreadcrumb(tag, SentryLevel.ERROR, msg, tr);
return Log.wtf(tag, msg, tr);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -32,9 +32,9 @@ import io.sentry.protocol.MeasurementValue
import io.sentry.protocol.TransactionNameSource
import io.sentry.test.getProperty
import org.junit.runner.RunWith
import org.mockito.ArgumentCaptor
import org.mockito.kotlin.any
import org.mockito.kotlin.anyOrNull
import org.mockito.kotlin.argumentCaptor
import org.mockito.kotlin.check
import org.mockito.kotlin.eq
import org.mockito.kotlin.mock
Expand Down Expand Up @@ -73,7 +73,9 @@ class ActivityLifecycleIntegrationTest {
val activityFramesTracker = mock<ActivityFramesTracker>()
val fullyDisplayedReporter = FullyDisplayedReporter.getInstance()
val transactionFinishedCallback = mock<TransactionFinishedCallback>()
lateinit var transaction: SentryTracer

// we init the transaction with a mock to avoid errors when finishing it after tests that don't start it
var transaction: SentryTracer = mock()
val buildInfo = mock<BuildInfoProvider>()

fun getSut(
Expand All @@ -85,15 +87,13 @@ class ActivityLifecycleIntegrationTest {

whenever(hub.options).thenReturn(options)

// TODO: we should let the ActivityLifecycleIntegration create the proper transaction here
val transactionOptions = TransactionOptions().apply {
isWaitForChildren = true
if (options.isEnableActivityLifecycleTracingAutoFinish) {
idleTimeout = options.idleTimeout
}
// We let the ActivityLifecycleIntegration create the proper transaction here
val argumentCaptor = argumentCaptor<TransactionOptions>()
whenever(hub.startTransaction(any(), argumentCaptor.capture())).thenAnswer {
val t = SentryTracer(context, hub, argumentCaptor.lastValue, transactionFinishedCallback)
transaction = t
return@thenAnswer t
}
transaction = SentryTracer(context, hub, transactionOptions, transactionFinishedCallback)
whenever(hub.startTransaction(any(), any<TransactionOptions>())).thenReturn(transaction)
whenever(buildInfo.sdkInfoVersion).thenReturn(apiVersion)

whenever(application.getSystemService(any())).thenReturn(am)
Expand Down Expand Up @@ -842,7 +842,7 @@ class ActivityLifecycleIntegrationTest {
sut.onActivityCreated(activity, fixture.bundle)

// call only once
verify(fixture.hub).startTransaction(any(), check<TransactionOptions> { assertNull(it.startTimestamp) })
verify(fixture.hub).startTransaction(any(), check<TransactionOptions> { assertNotEquals(date, it.startTimestamp) })
}

@Test
Expand Down Expand Up @@ -1024,26 +1024,19 @@ class ActivityLifecycleIntegrationTest {
fixture.options.tracesSampleRate = 1.0
sut.register(fixture.hub, fixture.options)

// Using ArgumentCaptor because multiple verify() throws an assertionError, even if the test passes
val parameters: ArgumentCaptor<TransactionOptions> = ArgumentCaptor.forClass(TransactionOptions::class.java)
val date = SentryNanotimeDate(Date(0), 0)
setAppStartTime()

val activity = mock<Activity>()
// First invocation: we expect to start a transaction with the appStartTime
sut.onActivityCreated(activity, fixture.bundle)
sut.onActivityPostResumed(activity)
assertEquals(date.nanoTimestamp(), fixture.transaction.startDate.nanoTimestamp())

val newActivity = mock<Activity>()
// Second invocation: we expect the transaction start timestamp not to be set
// Second invocation: we expect to start a transaction with a different start timestamp
sut.onActivityCreated(newActivity, fixture.bundle)

verify(fixture.hub, times(2)).startTransaction(any(), parameters.capture())
val capturedValues = parameters.allValues
// The first invocation contains the appStartTime
assertEquals(date.nanoTimestamp(), capturedValues[0].startTimestamp!!.nanoTimestamp())
// The second invocation contains no start timestamp
assertNull(capturedValues[1].startTimestamp)
assertNotEquals(date.nanoTimestamp(), fixture.transaction.startDate.nanoTimestamp())
}

@Test
Expand Down Expand Up @@ -1294,6 +1287,24 @@ class ActivityLifecycleIntegrationTest {
)
}

@Test
fun `transaction has same start timestamp of ttid and ttfd`() {
val sut = fixture.getSut()
val activity = mock<Activity>()
fixture.options.tracesSampleRate = 1.0
fixture.options.isEnableTimeToFullDisplayTracing = true

sut.register(fixture.hub, fixture.options)
sut.onActivityCreated(activity, fixture.bundle)

// The ttid span should be running
val ttidSpan = sut.ttidSpanMap[activity]
assertNotNull(ttidSpan)

assertEquals(ttidSpan.startDate, fixture.transaction.startDate)
assertEquals(sut.ttfdSpan?.startDate, fixture.transaction.startDate)
}

private fun runFirstDraw(view: View) {
// Removes OnDrawListener in the next OnGlobalLayout after onDraw
view.viewTreeObserver.dispatchOnDraw()
Expand Down
Loading

0 comments on commit 7079193

Please sign in to comment.