Skip to content
This repository has been archived by the owner on Aug 30, 2023. It is now read-only.

feat: attach threads/stacktraces #267

Merged
merged 4 commits into from
Feb 17, 2020
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
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,8 @@ public final class MainEventProcessor implements EventProcessor {
new SentryStackTraceFactory(options.getInAppExcludes(), options.getInAppIncludes());

sentryExceptionFactory = new SentryExceptionFactory(sentryStackTraceFactory);
sentryThreadFactory = new SentryThreadFactory(sentryStackTraceFactory);
sentryThreadFactory =
new SentryThreadFactory(sentryStackTraceFactory, this.options.isAttachStacktrace());
}

MainEventProcessor(
Expand Down Expand Up @@ -71,7 +72,7 @@ private void processNonCachedEvent(SentryEvent event) {
event.setDist(options.getDist());
}

if (event.getThreads() == null) {
if (event.getThreads() == null && options.isAttachThreads()) {
event.setThreads(sentryThreadFactory.getCurrentThreads());
}
}
Expand Down
45 changes: 45 additions & 0 deletions sentry-core/src/main/java/io/sentry/core/SentryOptions.java
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,15 @@ public class SentryOptions {
/** Sets the distribution. Think about it together with release and environment */
private @Nullable String dist;

/** When enabled, threads are automatically attached to all logged events. */
private boolean attachThreads = true;

/**
* When enabled, stack traces are automatically attached to all threads logged. Stack traces are
* always attached to exceptions but when this is set stack traces are also sent with threads
*/
private boolean attachStacktrace;

/**
* Adds an event processor
*
Expand Down Expand Up @@ -587,6 +596,42 @@ public void setTransportGate(@Nullable ITransportGate transportGate) {
this.transportGate = transportGate;
}

/**
* Checks if the AttachStacktrace is enabled or not
*
* @return true if enabled or false otherwise
*/
public boolean isAttachStacktrace() {
return attachStacktrace;
}

/**
* Sets the attachStacktrace to enabled or disabled
*
* @param attachStacktrace true if enabled or false otherwise
*/
public void setAttachStacktrace(boolean attachStacktrace) {
this.attachStacktrace = attachStacktrace;
}

/**
* Checks if the AttachThreads is enabled or not
*
* @return true if enabled or false otherwise
*/
public boolean isAttachThreads() {
return attachThreads;
}

/**
* Sets the attachThreads to enabled or disabled
*
* @param attachThreads true if enabled or false otherwise
*/
public void setAttachThreads(boolean attachThreads) {
this.attachThreads = attachThreads;
}

/** The BeforeSend callback */
public interface BeforeSendCallback {

Expand Down
22 changes: 20 additions & 2 deletions sentry-core/src/main/java/io/sentry/core/SentryThreadFactory.java
Original file line number Diff line number Diff line change
Expand Up @@ -17,14 +17,32 @@ final class SentryThreadFactory {
/** the SentryStackTraceFactory */
private final @NotNull SentryStackTraceFactory sentryStackTraceFactory;

/**
* When enabled, stack traces are automatically attached to all threads logged. Stack traces are
* always attached to exceptions but when this is set stack traces are also sent with threads
*/
private final boolean attachStacktrace;

/**
* ctor SentryThreadFactory that takes a SentryStackTraceFactory
*
* @param sentryStackTraceFactory the SentryStackTraceFactory
* @param attachStacktrace the attachStacktrace
*/
public SentryThreadFactory(final @NotNull SentryStackTraceFactory sentryStackTraceFactory) {
public SentryThreadFactory(
final @NotNull SentryStackTraceFactory sentryStackTraceFactory, boolean attachStacktrace) {
this.sentryStackTraceFactory =
Objects.requireNonNull(sentryStackTraceFactory, "The SentryStackTraceFactory is required.");
this.attachStacktrace = attachStacktrace;
}

/**
* ctor SentryThreadFactory that takes a SentryStackTraceFactory
*
* @param sentryStackTraceFactory the SentryStackTraceFactory
*/
public SentryThreadFactory(final @NotNull SentryStackTraceFactory sentryStackTraceFactory) {
this(sentryStackTraceFactory, false);
}

/**
Expand Down Expand Up @@ -92,7 +110,7 @@ List<SentryThread> getCurrentThreads(final @NotNull Map<Thread, StackTraceElemen
final List<SentryStackFrame> frames =
sentryStackTraceFactory.getStackFrames(stackFramesElements);

if (frames != null && frames.size() > 0) {
if (attachStacktrace && frames != null && frames.size() > 0) {
sentryThread.setStacktrace(new SentryStackTrace(frames));
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import com.nhaarman.mockitokotlin2.mock
import kotlin.test.Test
import kotlin.test.assertEquals
import kotlin.test.assertFalse
import kotlin.test.assertNotNull
import kotlin.test.assertNull
import kotlin.test.assertSame
import kotlin.test.assertTrue
Expand All @@ -16,7 +17,10 @@ class MainEventProcessorTest {
environment = "environment"
dist = "dist"
}
fun getSut() = MainEventProcessor(sentryOptions)
fun getSut(attachThreads: Boolean = true): MainEventProcessor {
sentryOptions.isAttachThreads = attachThreads
return MainEventProcessor(sentryOptions)
}
}

private val fixture = Fixture()
Expand Down Expand Up @@ -74,6 +78,26 @@ class MainEventProcessorTest {
assertNull(event.threads)
}

@Test
fun `when processing an event and attach threads is disabled, threads should not be set`() {
val sut = fixture.getSut(false)

var event = SentryEvent()
event = sut.process(event, null)

assertNull(event.threads)
}

@Test
fun `when processing an event and attach threads is enabled, threads should be set`() {
val sut = fixture.getSut()

var event = SentryEvent()
event = sut.process(event, null)

assertNotNull(event.threads)
}

private fun generateCrashedEvent(crashedThread: Thread = Thread.currentThread()) = SentryEvent().apply {
val mockThrowable = mock<Throwable>()
val actualThrowable = UncaughtExceptionHandlerIntegration.getUnhandledThrowable(crashedThread, mockThrowable)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,35 +2,54 @@ package io.sentry.core

import kotlin.test.Test
import kotlin.test.assertEquals
import kotlin.test.assertFalse
import kotlin.test.assertNotNull
import kotlin.test.assertNotSame
import kotlin.test.assertNull
import kotlin.test.assertTrue

class SentryThreadFactoryTest {

private val sut = SentryThreadFactory(SentryStackTraceFactory(listOf("io.sentry"), listOf()))
class Fixture {
internal fun getSut(attachStacktrace: Boolean = true) = SentryThreadFactory(SentryStackTraceFactory(listOf("io.sentry"), listOf()), attachStacktrace)
}

private val fixture = Fixture()

@Test
fun `when getCurrentThreads is called, not empty result`() {
val sut = fixture.getSut()
val threads = sut.currentThreads
assertNotSame(0, threads!!.count())
}

@Test
fun `when currentThreads is called, current thread is marked crashed`() =
fun `when currentThreads is called, current thread is marked crashed`() {
val sut = fixture.getSut()
assertEquals(1, sut.currentThreads!!.filter { it.isCrashed }.count())
}

@Test
fun `when currentThreads is called, thread state is captured`() =
fun `when currentThreads is called, thread state is captured`() {
val sut = fixture.getSut()
assertTrue(sut.currentThreads!!.all { it.state != null })
}

@Test
fun `when currentThreads is called, some thread stack frames are captured`() =
fun `when currentThreads is called, some thread stack frames are captured`() {
val sut = fixture.getSut()
assertTrue(sut.currentThreads!!.filter { it.stacktrace != null }.any { it.stacktrace.frames.count() > 0 })
}

@Test
fun `when currentThreads and attachStacktrace is disabled, stack frames are not captured`() {
val sut = fixture.getSut(false)
assertFalse(sut.currentThreads!!.filter { it.stacktrace != null }.any { it.stacktrace.frames.count() > 0 })
}

@Test
fun `when getAllStackTraces don't return the current thread, add it manually`() {
val sut = fixture.getSut()
val stackTraces = Thread.getAllStackTraces()
val currentThread = Thread.currentThread()
stackTraces.remove(currentThread)
Expand All @@ -42,6 +61,7 @@ class SentryThreadFactoryTest {

@Test
fun `When passing empty param to getCurrentThreads, returns null`() {
val sut = fixture.getSut()
val threads = sut.getCurrentThreads(mapOf())

assertNull(threads)
Expand Down