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

ndk events apply scoped data #322

Merged
merged 6 commits into from
Mar 25, 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 @@ -25,7 +25,6 @@
import io.sentry.core.SentryEvent;
import io.sentry.core.SentryLevel;
import io.sentry.core.SentryOptions;
import io.sentry.core.hints.Cached;
import io.sentry.core.protocol.App;
import io.sentry.core.protocol.DebugImage;
import io.sentry.core.protocol.DebugMeta;
Expand All @@ -34,6 +33,7 @@
import io.sentry.core.protocol.SdkVersion;
import io.sentry.core.protocol.SentryThread;
import io.sentry.core.protocol.User;
import io.sentry.core.util.ApplyScopeUtils;
import io.sentry.core.util.Objects;
import java.io.BufferedReader;
import java.io.File;
Expand Down Expand Up @@ -114,7 +114,7 @@ public DefaultAndroidEventProcessor(
@Override
public @NotNull SentryEvent process(
final @NotNull SentryEvent event, final @Nullable Object hint) {
if (!(hint instanceof Cached)) {
if (ApplyScopeUtils.shouldApplyScopeData(hint)) {
processNonCachedEvent(event);
} else {
options
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,20 @@
package io.sentry.android.core;

import static io.sentry.core.SentryLevel.ERROR;

import android.os.FileObserver;
import io.sentry.core.IEnvelopeSender;
import io.sentry.core.ILogger;
import io.sentry.core.SentryLevel;
import io.sentry.core.hints.ApplyScopeData;
import io.sentry.core.hints.Cached;
import io.sentry.core.hints.Flushable;
import io.sentry.core.hints.Retryable;
import io.sentry.core.hints.SubmissionResult;
import io.sentry.core.util.Objects;
import java.io.File;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

Expand All @@ -14,14 +23,20 @@ final class EnvelopeFileObserver extends FileObserver {
private final String rootPath;
private final IEnvelopeSender envelopeSender;
private @NotNull final ILogger logger;
private final long flushTimeoutMillis;

// The preferred overload (Taking File instead of String) is only available from API 29
@SuppressWarnings("deprecation")
EnvelopeFileObserver(String path, IEnvelopeSender envelopeSender, @NotNull ILogger logger) {
EnvelopeFileObserver(
String path,
IEnvelopeSender envelopeSender,
@NotNull ILogger logger,
final long flushTimeoutMillis) {
super(path);
this.rootPath = Objects.requireNonNull(path, "File path is required.");
this.envelopeSender = Objects.requireNonNull(envelopeSender, "Envelope sender is required.");
this.logger = Objects.requireNonNull(logger, "Logger is required.");
this.flushTimeoutMillis = flushTimeoutMillis;
}

@Override
Expand All @@ -39,6 +54,55 @@ public void onEvent(int eventType, @Nullable String relativePath) {

// TODO: Only some event types should be pass through?

envelopeSender.processEnvelopeFile(this.rootPath + File.separator + relativePath);
final CachedEnvelopeHint hint = new CachedEnvelopeHint(flushTimeoutMillis, logger);
envelopeSender.processEnvelopeFile(this.rootPath + File.separator + relativePath, hint);
}

private static final class CachedEnvelopeHint
implements Cached, Retryable, SubmissionResult, Flushable, ApplyScopeData {
boolean retry = false;
boolean succeeded = false;

private @NotNull final CountDownLatch latch;
private final long flushTimeoutMillis;
private final @NotNull ILogger logger;

public CachedEnvelopeHint(final long flushTimeoutMillis, final @NotNull ILogger logger) {
this.flushTimeoutMillis = flushTimeoutMillis;
this.latch = new CountDownLatch(1);
this.logger = Objects.requireNonNull(logger, "ILogger is required.");
}

@Override
public boolean waitFlush() {
try {
return latch.await(flushTimeoutMillis, TimeUnit.MILLISECONDS);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
logger.log(ERROR, "Exception while awaiting on lock.", e);
}
return false;
}

@Override
public boolean isRetry() {
return retry;
}

@Override
public void setRetry(boolean retry) {
this.retry = retry;
}

@Override
public void setResult(boolean succeeded) {
this.succeeded = succeeded;
latch.countDown();
}

@Override
public boolean isSuccess() {
return succeeded;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,8 @@ public final void register(IHub hub, SentryOptions options) {
logger,
options.getFlushTimeoutMillis());

observer = new EnvelopeFileObserver(path, envelopeSender, logger);
observer =
new EnvelopeFileObserver(path, envelopeSender, logger, options.getFlushTimeoutMillis());
observer.startWatching();

options.getLogger().log(SentryLevel.DEBUG, "EnvelopeFileObserverIntegration installed.");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,18 @@ package io.sentry.android.core

import android.os.FileObserver
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.nhaarman.mockitokotlin2.any
import com.nhaarman.mockitokotlin2.anyOrNull
import com.nhaarman.mockitokotlin2.argWhere
import com.nhaarman.mockitokotlin2.eq
import com.nhaarman.mockitokotlin2.mock
import com.nhaarman.mockitokotlin2.never
import com.nhaarman.mockitokotlin2.verify
import com.nhaarman.mockitokotlin2.verifyZeroInteractions
import io.sentry.core.IEnvelopeSender
import io.sentry.core.ILogger
import io.sentry.core.SentryOptions
import io.sentry.core.hints.ApplyScopeData
import java.io.File
import kotlin.test.Test
import kotlin.test.assertEquals
Expand All @@ -23,15 +27,15 @@ class EnvelopeFileObserverTest {
var path: String? = "."
var envelopeSender: IEnvelopeSender = mock()
var logger: ILogger = mock()
var options: SentryOptions = SentryOptions()

init {
val options = SentryOptions()
options.isDebug = true
options.setLogger(logger)
}

fun getSut(): EnvelopeFileObserver {
return EnvelopeFileObserver(path, envelopeSender, logger)
return EnvelopeFileObserver(path, envelopeSender, logger, options.flushTimeoutMillis)
}
}

Expand All @@ -42,7 +46,7 @@ class EnvelopeFileObserverTest {
val sut = fixture.getSut()
val param = "file-name.txt"
sut.onEvent(FileObserver.CLOSE_WRITE, param)
verify(fixture.envelopeSender).processEnvelopeFile(fixture.path + File.separator + param)
verify(fixture.envelopeSender).processEnvelopeFile(eq(fixture.path + File.separator + param), any())
}

@Test
Expand All @@ -56,7 +60,7 @@ class EnvelopeFileObserverTest {
fun `when event is fired with null path, envelope reader is not called`() {
val sut = fixture.getSut()
sut.onEvent(0, null)
verify(fixture.envelopeSender, never()).processEnvelopeFile(anyOrNull())
verify(fixture.envelopeSender, never()).processEnvelopeFile(anyOrNull(), any())
}

@Test
Expand All @@ -65,4 +69,12 @@ class EnvelopeFileObserverTest {
val exception = assertFailsWith<Exception> { fixture.getSut() }
assertEquals("File path is required.", exception.message)
}

@Test
fun `envelope sender is called with fully qualified path and ApplyScopeData hint`() {
val sut = fixture.getSut()
val param = "file-name.txt"
sut.onEvent(FileObserver.CLOSE_WRITE, param)
verify(fixture.envelopeSender).processEnvelopeFile(eq(fixture.path + File.separator + param), argWhere { it is ApplyScopeData })
}
}
66 changes: 63 additions & 3 deletions sentry-core/src/main/java/io/sentry/core/DirectoryProcessor.java
Original file line number Diff line number Diff line change
@@ -1,14 +1,25 @@
package io.sentry.core;

import static io.sentry.core.SentryLevel.ERROR;

import io.sentry.core.hints.Cached;
import io.sentry.core.hints.Flushable;
import io.sentry.core.hints.Retryable;
import io.sentry.core.hints.SubmissionResult;
import java.io.File;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

abstract class DirectoryProcessor {

private final @NotNull ILogger logger;
private final long flushTimeoutMillis;

DirectoryProcessor(final @NotNull ILogger logger) {
DirectoryProcessor(final @NotNull ILogger logger, final long flushTimeoutMillis) {
this.logger = logger;
this.flushTimeoutMillis = flushTimeoutMillis;
}

void processDirectory(@NotNull File directory) {
Expand Down Expand Up @@ -41,14 +52,63 @@ void processDirectory(@NotNull File directory) {
directory.getAbsolutePath());

for (File file : listFiles) {
processFile(file);
final SendCachedEventHint hint = new SendCachedEventHint(flushTimeoutMillis, logger);
processFile(file, hint);
}
} catch (Exception e) {
logger.log(SentryLevel.ERROR, e, "Failed processing '%s'", directory.getAbsolutePath());
}
}

protected abstract void processFile(File file);
protected abstract void processFile(File file, @Nullable Object hint);

protected abstract boolean isRelevantFileName(String fileName);

private static final class SendCachedEventHint
implements Cached, Retryable, SubmissionResult, Flushable {
boolean retry = false;
boolean succeeded = false;

private final CountDownLatch latch;
private final long flushTimeoutMillis;
private final @NotNull ILogger logger;

public SendCachedEventHint(final long flushTimeoutMillis, final @NotNull ILogger logger) {
this.flushTimeoutMillis = flushTimeoutMillis;
this.latch = new CountDownLatch(1);
this.logger = logger;
}

@Override
public boolean isRetry() {
return retry;
}

@Override
public void setRetry(boolean retry) {
this.retry = retry;
}

@Override
public boolean waitFlush() {
try {
return latch.await(flushTimeoutMillis, TimeUnit.MILLISECONDS);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
logger.log(ERROR, "Exception while awaiting on lock.", e);
}
return false;
}

@Override
public void setResult(boolean succeeded) {
this.succeeded = succeeded;
latch.countDown();
}

@Override
public boolean isSuccess() {
return succeeded;
}
}
}
Loading