diff --git a/CHANGELOG.md b/CHANGELOG.md index 1aa283a459..2355cb8067 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,7 @@ * Feat: Add locale to device context and deprecate language (#1832) * Ref: change `java.util.Random` to `java.security.SecureRandom` for possible security reasons (#1831) * Feat: Add `SentryFileInputStream` and `SentryFileOutputStream` for File I/O performance instrumentation (#1826) +- Feat: Add `SentryFileReader` and `SentryFileWriter` for File I/O instrumentation (#1843) ## 5.4.3 diff --git a/sentry/api/sentry.api b/sentry/api/sentry.api index 028ba74a7b..7733ebdcc3 100644 --- a/sentry/api/sentry.api +++ b/sentry/api/sentry.api @@ -1471,6 +1471,20 @@ public final class io/sentry/instrumentation/file/SentryFileOutputStream$Factory public static fun create (Ljava/io/FileOutputStream;Ljava/lang/String;Z)Ljava/io/FileOutputStream; } +public final class io/sentry/instrumentation/file/SentryFileReader : java/io/InputStreamReader { + public fun (Ljava/io/File;)V + public fun (Ljava/io/FileDescriptor;)V + public fun (Ljava/lang/String;)V +} + +public final class io/sentry/instrumentation/file/SentryFileWriter : java/io/OutputStreamWriter { + public fun (Ljava/io/File;)V + public fun (Ljava/io/File;Z)V + public fun (Ljava/io/FileDescriptor;)V + public fun (Ljava/lang/String;)V + public fun (Ljava/lang/String;Z)V +} + public final class io/sentry/protocol/App : io/sentry/IUnknownPropertiesConsumer { public static final field TYPE Ljava/lang/String; public fun ()V diff --git a/sentry/src/main/java/io/sentry/instrumentation/file/SentryFileReader.java b/sentry/src/main/java/io/sentry/instrumentation/file/SentryFileReader.java new file mode 100644 index 0000000000..0a225e65a5 --- /dev/null +++ b/sentry/src/main/java/io/sentry/instrumentation/file/SentryFileReader.java @@ -0,0 +1,26 @@ +package io.sentry.instrumentation.file; + +import io.sentry.IHub; +import java.io.File; +import java.io.FileDescriptor; +import java.io.FileNotFoundException; +import java.io.InputStreamReader; +import org.jetbrains.annotations.NotNull; + +public final class SentryFileReader extends InputStreamReader { + public SentryFileReader(final @NotNull String fileName) throws FileNotFoundException { + super(new SentryFileInputStream(fileName)); + } + + public SentryFileReader(final @NotNull File file) throws FileNotFoundException { + super(new SentryFileInputStream(file)); + } + + public SentryFileReader(final @NotNull FileDescriptor fd) { + super(new SentryFileInputStream(fd)); + } + + SentryFileReader(final @NotNull File file, final @NotNull IHub hub) throws FileNotFoundException { + super(new SentryFileInputStream(file, hub)); + } +} diff --git a/sentry/src/main/java/io/sentry/instrumentation/file/SentryFileWriter.java b/sentry/src/main/java/io/sentry/instrumentation/file/SentryFileWriter.java new file mode 100644 index 0000000000..60afc8d11c --- /dev/null +++ b/sentry/src/main/java/io/sentry/instrumentation/file/SentryFileWriter.java @@ -0,0 +1,36 @@ +package io.sentry.instrumentation.file; + +import io.sentry.IHub; +import java.io.File; +import java.io.FileDescriptor; +import java.io.FileNotFoundException; +import java.io.OutputStreamWriter; +import org.jetbrains.annotations.NotNull; + +public final class SentryFileWriter extends OutputStreamWriter { + public SentryFileWriter(final @NotNull String fileName) throws FileNotFoundException { + super(new SentryFileOutputStream(fileName)); + } + + public SentryFileWriter(final @NotNull String fileName, final boolean append) + throws FileNotFoundException { + super(new SentryFileOutputStream(fileName, append)); + } + + public SentryFileWriter(final @NotNull File file) throws FileNotFoundException { + super(new SentryFileOutputStream(file)); + } + + public SentryFileWriter(final @NotNull File file, final boolean append) + throws FileNotFoundException { + super(new SentryFileOutputStream(file, append)); + } + + public SentryFileWriter(final @NotNull FileDescriptor fd) { + super(new SentryFileOutputStream(fd)); + } + + SentryFileWriter(final @NotNull File file, final @NotNull IHub hub) throws FileNotFoundException { + super(new SentryFileOutputStream(file, hub)); + } +} diff --git a/sentry/src/test/java/io/sentry/instrumentation/file/SentryFileReaderTest.kt b/sentry/src/test/java/io/sentry/instrumentation/file/SentryFileReaderTest.kt new file mode 100644 index 0000000000..0757de81ad --- /dev/null +++ b/sentry/src/test/java/io/sentry/instrumentation/file/SentryFileReaderTest.kt @@ -0,0 +1,56 @@ +package io.sentry.instrumentation.file + +import com.nhaarman.mockitokotlin2.mock +import com.nhaarman.mockitokotlin2.whenever +import io.sentry.IHub +import io.sentry.SentryOptions +import io.sentry.SentryTracer +import io.sentry.SpanStatus.OK +import io.sentry.TransactionContext +import org.junit.Rule +import org.junit.Test +import org.junit.rules.TemporaryFolder +import java.io.File +import kotlin.test.assertEquals + +class SentryFileReaderTest { + class Fixture { + val hub = mock() + val sentryTracer = SentryTracer(TransactionContext("name", "op"), hub) + + internal fun getSut( + tmpFile: File, + activeTransaction: Boolean = true, + ): SentryFileReader { + tmpFile.writeText("TEXT") + whenever(hub.options).thenReturn(SentryOptions()) + if (activeTransaction) { + whenever(hub.span).thenReturn(sentryTracer) + } + return SentryFileReader(tmpFile, hub) + } + } + + @get:Rule + val tmpDir = TemporaryFolder() + + private val fixture = Fixture() + + private val tmpFile: File get() = tmpDir.newFile("test.txt") + + @Test + fun `captures a span`() { + val reader = fixture.getSut(tmpFile) + reader.readText() + reader.close() + + assertEquals(fixture.sentryTracer.children.size, 1) + val fileIOSpan = fixture.sentryTracer.children.first() + assertEquals(fileIOSpan.spanContext.operation, "file.read") + assertEquals(fileIOSpan.spanContext.description, "test.txt (4 B)") + assertEquals(fileIOSpan.data["file.size"], 4L) + assertEquals(fileIOSpan.throwable, null) + assertEquals(fileIOSpan.isFinished, true) + assertEquals(fileIOSpan.status, OK) + } +} diff --git a/sentry/src/test/java/io/sentry/instrumentation/file/SentryFileWriterTest.kt b/sentry/src/test/java/io/sentry/instrumentation/file/SentryFileWriterTest.kt new file mode 100644 index 0000000000..174d284402 --- /dev/null +++ b/sentry/src/test/java/io/sentry/instrumentation/file/SentryFileWriterTest.kt @@ -0,0 +1,55 @@ +package io.sentry.instrumentation.file + +import com.nhaarman.mockitokotlin2.mock +import com.nhaarman.mockitokotlin2.whenever +import io.sentry.IHub +import io.sentry.SentryOptions +import io.sentry.SentryTracer +import io.sentry.SpanStatus.OK +import io.sentry.TransactionContext +import org.junit.Rule +import org.junit.Test +import org.junit.rules.TemporaryFolder +import java.io.File +import kotlin.test.assertEquals + +class SentryFileWriterTest { + class Fixture { + val hub = mock() + val sentryTracer = SentryTracer(TransactionContext("name", "op"), hub) + + internal fun getSut( + tmpFile: File, + activeTransaction: Boolean = true, + ): SentryFileWriter { + whenever(hub.options).thenReturn(SentryOptions()) + if (activeTransaction) { + whenever(hub.span).thenReturn(sentryTracer) + } + return SentryFileWriter(tmpFile, hub) + } + } + + @get:Rule + val tmpDir = TemporaryFolder() + + private val fixture = Fixture() + + private val tmpFile: File get() = tmpDir.newFile("test.txt") + + @Test + fun `captures a span`() { + val writer = fixture.getSut(tmpFile) + writer.write("TEXT") + writer.close() + + assertEquals(fixture.sentryTracer.children.size, 1) + val fileIOSpan = fixture.sentryTracer.children.first() + assertEquals(fileIOSpan.spanContext.operation, "file.write") + assertEquals(fileIOSpan.spanContext.description, "test.txt (4 B)") + assertEquals(fileIOSpan.data["file.size"], 4L) + assertEquals(fileIOSpan.throwable, null) + assertEquals(fileIOSpan.isFinished, true) + assertEquals(fileIOSpan.status, OK) + } +}