Skip to content

Commit

Permalink
Add CheckInUtils.withCheckIn which abstracts away some of the manua…
Browse files Browse the repository at this point in the history
…l check-ins complexity (#2959)
  • Loading branch information
adinauer committed Oct 3, 2023
1 parent d342d37 commit a10bf9f
Show file tree
Hide file tree
Showing 4 changed files with 125 additions and 2 deletions.
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
# Changelog

## Unreleased

### Features

- Add `CheckInUtils.withCheckIn` which abstracts away some of the manual check-ins complexity ([#2959](https://github.com/getsentry/sentry-java/pull/2959))

## 6.30.0

### Features
Expand Down
1 change: 1 addition & 0 deletions sentry/api/sentry.api
Original file line number Diff line number Diff line change
Expand Up @@ -4317,6 +4317,7 @@ public abstract class io/sentry/transport/TransportResult {
public final class io/sentry/util/CheckInUtils {
public fun <init> ()V
public static fun isIgnored (Ljava/util/List;Ljava/lang/String;)Z
public static fun withCheckIn (Ljava/lang/String;Ljava/util/concurrent/Callable;)Ljava/lang/Object;
}

public final class io/sentry/util/ClassLoaderUtils {
Expand Down
44 changes: 42 additions & 2 deletions sentry/src/main/java/io/sentry/util/CheckInUtils.java
Original file line number Diff line number Diff line change
@@ -1,14 +1,54 @@
package io.sentry.util;

import io.sentry.CheckIn;
import io.sentry.CheckInStatus;
import io.sentry.DateUtils;
import io.sentry.IHub;
import io.sentry.Sentry;
import io.sentry.protocol.SentryId;
import java.util.List;
import java.util.concurrent.Callable;
import org.jetbrains.annotations.ApiStatus;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

/** Checks if a check-in for a monitor (CRON) has been ignored. */
@ApiStatus.Internal
@ApiStatus.Experimental
public final class CheckInUtils {

/**
* Helper method to send monitor check-ins for a {@link Callable}
*
* @param callable - the {@link Callable} to be called
* @return the return value of the {@link Callable}
* @param <U> - the result type of the {@link Callable}
*/
public static <U> U withCheckIn(
final @NotNull String monitorSlug, final @NotNull Callable<U> callable) throws Exception {
final @NotNull IHub hub = Sentry.getCurrentHub();
final long startTime = System.currentTimeMillis();
boolean didError = false;

hub.pushScope();
TracingUtils.startNewTrace(hub);

@Nullable
SentryId checkInId = hub.captureCheckIn(new CheckIn(monitorSlug, CheckInStatus.IN_PROGRESS));
try {
return callable.call();
} catch (Throwable t) {
didError = true;
throw t;
} finally {
final @NotNull CheckInStatus status = didError ? CheckInStatus.ERROR : CheckInStatus.OK;
CheckIn checkIn = new CheckIn(checkInId, monitorSlug, status);
checkIn.setDuration(DateUtils.millisToSeconds(System.currentTimeMillis() - startTime));
hub.captureCheckIn(checkIn);
hub.popScope();
}
}

/** Checks if a check-in for a monitor (CRON) has been ignored. */
@ApiStatus.Internal
public static boolean isIgnored(
final @Nullable List<String> ignoredSlugs, final @NotNull String slug) {
if (ignoredSlugs == null || ignoredSlugs.isEmpty()) {
Expand Down
76 changes: 76 additions & 0 deletions sentry/src/test/java/io/sentry/util/CheckInUtilsTest.kt
Original file line number Diff line number Diff line change
@@ -1,6 +1,17 @@
package io.sentry.util

import io.sentry.CheckInStatus
import io.sentry.IHub
import io.sentry.Sentry
import org.mockito.Mockito
import org.mockito.kotlin.any
import org.mockito.kotlin.check
import org.mockito.kotlin.inOrder
import org.mockito.kotlin.mock
import java.lang.AssertionError
import java.lang.RuntimeException
import kotlin.test.Test
import kotlin.test.assertEquals
import kotlin.test.assertFalse
import kotlin.test.assertTrue

Expand Down Expand Up @@ -35,4 +46,69 @@ class CheckInUtilsTest {
fun `does not ignore if slug is does not match ignored list`() {
assertFalse(CheckInUtils.isIgnored(listOf("slug-.*"), "slugA"))
}

@Test
fun `sends check-in for wrapped supplier`() {
Mockito.mockStatic(Sentry::class.java).use { sentry ->
val hub = mock<IHub>()
sentry.`when`<Any> { Sentry.getCurrentHub() }.thenReturn(hub)
val returnValue = CheckInUtils.withCheckIn("monitor-1") {
return@withCheckIn "test1"
}

assertEquals("test1", returnValue)
inOrder(hub) {
verify(hub).pushScope()
verify(hub).configureScope(any())
verify(hub).captureCheckIn(
check {
assertEquals("monitor-1", it.monitorSlug)
assertEquals(CheckInStatus.IN_PROGRESS.apiName(), it.status)
}
)
verify(hub).captureCheckIn(
check {
assertEquals("monitor-1", it.monitorSlug)
assertEquals(CheckInStatus.OK.apiName(), it.status)
}
)
verify(hub).popScope()
}
}
}

@Test
fun `sends check-in for wrapped supplier with exception`() {
Mockito.mockStatic(Sentry::class.java).use { sentry ->
val hub = mock<IHub>()
sentry.`when`<Any> { Sentry.getCurrentHub() }.thenReturn(hub)

try {
CheckInUtils.withCheckIn("monitor-1") {
throw RuntimeException("thrown on purpose")
}
throw AssertionError("expected exception to be rethrown")
} catch (e: Exception) {
assertEquals("thrown on purpose", e.message)
}

inOrder(hub) {
verify(hub).pushScope()
verify(hub).configureScope(any())
verify(hub).captureCheckIn(
check {
assertEquals("monitor-1", it.monitorSlug)
assertEquals(CheckInStatus.IN_PROGRESS.apiName(), it.status)
}
)
verify(hub).captureCheckIn(
check {
assertEquals("monitor-1", it.monitorSlug)
assertEquals(CheckInStatus.ERROR.apiName(), it.status)
}
)
verify(hub).popScope()
}
}
}
}

0 comments on commit a10bf9f

Please sign in to comment.