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
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
@@ -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;
@@ -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;
@@ -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
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;

@@ -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
@@ -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
@@ -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.");
Original file line number Diff line number Diff line change
@@ -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
@@ -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)
}
}

@@ -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
@@ -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
@@ -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) {
@@ -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