Skip to content

Commit

Permalink
poc: file attachment api
Browse files Browse the repository at this point in the history
  • Loading branch information
fractalwrench committed Jan 14, 2025
1 parent 425639c commit f1ed6ad
Show file tree
Hide file tree
Showing 11 changed files with 221 additions and 12 deletions.
2 changes: 2 additions & 0 deletions embrace-android-api/api/embrace-android-api.api
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,8 @@ public abstract interface class io/embrace/android/embracesdk/internal/api/LogsA
public abstract fun logInfo (Ljava/lang/String;)V
public abstract fun logMessage (Ljava/lang/String;Lio/embrace/android/embracesdk/Severity;)V
public abstract fun logMessage (Ljava/lang/String;Lio/embrace/android/embracesdk/Severity;Ljava/util/Map;)V
public abstract fun logMessage (Ljava/lang/String;Lio/embrace/android/embracesdk/Severity;Ljava/util/Map;Ljava/lang/String;Ljava/lang/String;J)V
public abstract fun logMessage (Ljava/lang/String;Lio/embrace/android/embracesdk/Severity;Ljava/util/Map;[B)V
public abstract fun logPushNotification (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Integer;Ljava/lang/Integer;Ljava/lang/Boolean;Ljava/lang/Boolean;)V
public abstract fun logWarning (Ljava/lang/String;)V
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,44 @@ public interface LogsApi {
properties: Map<String, Any>?,
)

/**
* Remotely logs a message at the given severity level with an attachment. These log messages will appear
* as part of the session timeline, and can be used to describe what was happening at a particular
* time within the app.
*
* @param message the message to remotely log
* @param severity the severity level of the log message
* @param properties the properties to attach to the log message
* @param attachment an attachment to include with the log message. This must be < 1MB in size.
*/
public fun logMessage(
message: String,
severity: Severity,
properties: Map<String, Any>?,
attachment: ByteArray,
)

/**
* Remotely logs a message at the given severity level with an attachment that has been persisted on a 3rd party
* hosting solution. These log messages will appear as part of the session timeline, and can be used to
* describe what was happening at a particular time within the app.
*
* @param message the message to remotely log
* @param severity the severity level of the log message
* @param properties the properties to attach to the log message
* @param attachmentId a UUID that identifies the attachment
* @param attachmentUrl a URL that gives the location of the attachment
* @param attachmentSize the size of the attachment in bytes
*/
public fun logMessage(
message: String,
severity: Severity,
properties: Map<String, Any>?,
attachmentId: String,
attachmentUrl: String,
attachmentSize: Long,
)

/**
* Remotely logs a message at INFO level. These log messages will appear as part of the session
* timeline, and can be used to describe what was happening at a particular time within the app.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import java.util.UUID
/**
* Holds attributes that describe an attachment to a log record.
*/
internal sealed class Attachment(
sealed class Attachment(
val size: Long,
val id: String,
val counter: AttachmentCounter,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ package io.embrace.android.embracesdk.internal.logs.attachments
/**
* Enumerates the states where an attachment could not be added to a log record.
*/
internal enum class AttachmentErrorCode {
enum class AttachmentErrorCode {
ATTACHMENT_TOO_LARGE,
UNSUCCESSFUL_UPLOAD,
OVER_MAX_ATTACHMENTS,
Expand Down
2 changes: 2 additions & 0 deletions embrace-android-sdk/api/embrace-android-sdk.api
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,8 @@ public final class io/embrace/android/embracesdk/Embrace : io/embrace/android/em
public fun logInfo (Ljava/lang/String;)V
public fun logMessage (Ljava/lang/String;Lio/embrace/android/embracesdk/Severity;)V
public fun logMessage (Ljava/lang/String;Lio/embrace/android/embracesdk/Severity;Ljava/util/Map;)V
public fun logMessage (Ljava/lang/String;Lio/embrace/android/embracesdk/Severity;Ljava/util/Map;Ljava/lang/String;Ljava/lang/String;J)V
public fun logMessage (Ljava/lang/String;Lio/embrace/android/embracesdk/Severity;Ljava/util/Map;[B)V
public fun logPushNotification (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/Integer;Ljava/lang/Integer;Ljava/lang/Boolean;Ljava/lang/Boolean;)V
public fun logWarning (Ljava/lang/String;)V
public fun logWebView (Ljava/lang/String;)V
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
package io.embrace.android.embracesdk.testcases.features

import androidx.test.ext.junit.runners.AndroidJUnit4
import io.embrace.android.embracesdk.Severity
import io.embrace.android.embracesdk.testframework.IntegrationTestRule
import io.embrace.android.embracesdk.testframework.assertions.assertOtelLogReceived
import io.embrace.android.embracesdk.testframework.assertions.getLastLog
import io.embrace.android.embracesdk.testframework.assertions.getOtelSeverity
import java.util.UUID
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith

@RunWith(AndroidJUnit4::class)
internal class FileAttachmentFeatureTest {

private val attachmentId = UUID.randomUUID()

@Rule
@JvmField
val testRule: IntegrationTestRule = IntegrationTestRule()

@Test
fun `log message with user hosted file attachment`() {
val size = 509L
val url = "https://example.com/my-file.txt"
val id = attachmentId.toString()
testRule.runTest(
testCaseAction = {
recordSession {
embrace.logMessage(
message = "test message",
severity = Severity.INFO,
properties = mapOf("key" to "value"),
attachmentId = id,
attachmentUrl = url,
attachmentSize = size,
)
}
},
assertAction = {
val log = getSingleLogEnvelope().getLastLog()
assertOtelLogReceived(
logReceived = log,
expectedMessage = "test message",
expectedSeverityNumber = getOtelSeverity(Severity.INFO).severityNumber,
expectedSeverityText = Severity.INFO.name,
expectedState = "foreground",
expectedProperties = mapOf(
"key" to "value",
),
)
}
)
}

@Test
fun `log message with embrace hosted file attachment`() {
testRule.runTest(
testCaseAction = {
recordSession {
embrace.logMessage(
message = "test message",
severity = Severity.INFO,
properties = mapOf("key" to "value"),
attachment = "Hello, world!".toByteArray()
)
}
},
assertAction = {
val log = getSingleLogEnvelope().getLastLog()
assertOtelLogReceived(
logReceived = log,
expectedMessage = "test message",
expectedSeverityNumber = getOtelSeverity(Severity.INFO).severityNumber,
expectedSeverityText = Severity.INFO.name,
expectedState = "foreground",
expectedProperties = mapOf(
"key" to "value",
),
)
}
)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,13 @@ import io.embrace.android.embracesdk.testframework.IntegrationTestRule
import io.embrace.android.embracesdk.testframework.actions.EmbraceSetupInterface
import io.embrace.android.embracesdk.testframework.assertions.assertOtelLogReceived
import io.embrace.android.embracesdk.testframework.assertions.getLastLog
import io.embrace.android.embracesdk.testframework.assertions.getOtelSeverity
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith

@RunWith(AndroidJUnit4::class)
internal class EmbraceLoggingFeatureTest {
internal class LogFeatureTest {

private val instrumentedConfig = FakeInstrumentedConfig(enabledFeatures = FakeEnabledFeatureConfig(bgActivityCapture = true))

Expand Down Expand Up @@ -399,14 +400,6 @@ internal class EmbraceLoggingFeatureTest {
logOrchestrator.flush(false)
}

private fun getOtelSeverity(severity: Severity): io.opentelemetry.api.logs.Severity {
return when (severity) {
Severity.INFO -> io.opentelemetry.api.logs.Severity.INFO
Severity.WARNING -> io.opentelemetry.api.logs.Severity.WARN
Severity.ERROR -> io.opentelemetry.api.logs.Severity.ERROR
}
}

private fun getEmbraceSeverity(severityNumber: Int): Severity {
return when (severityNumber) {
io.opentelemetry.api.logs.Severity.INFO.severityNumber -> Severity.INFO
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package io.embrace.android.embracesdk.testframework.assertions

import io.embrace.android.embracesdk.Severity
import io.embrace.android.embracesdk.internal.opentelemetry.embExceptionHandling
import io.embrace.android.embracesdk.internal.opentelemetry.embState
import io.embrace.android.embracesdk.internal.payload.Log
Expand Down Expand Up @@ -53,8 +54,16 @@ internal fun assertOtelLogReceived(
}
}

internal fun getOtelSeverity(severity: Severity): io.opentelemetry.api.logs.Severity {
return when (severity) {
Severity.INFO -> io.opentelemetry.api.logs.Severity.INFO
Severity.WARNING -> io.opentelemetry.api.logs.Severity.WARN
Severity.ERROR -> io.opentelemetry.api.logs.Severity.ERROR
}
}

private fun assertAttribute(log: Log, name: String, expectedValue: String) {
val attribute = log.attributes?.find { it.key == name }
assertNotNull(attribute)
assertNotNull("Attribute not found: $name", attribute)
assertEquals(expectedValue, attribute?.data)
}
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,26 @@ public class Embrace private constructor(
impl.logMessage(message, severity, properties)
}

override fun logMessage(
message: String,
severity: Severity,
properties: Map<String, Any>?,
attachment: ByteArray,
) {
impl.logMessage(message, severity, properties, attachment)
}

override fun logMessage(
message: String,
severity: Severity,
properties: Map<String, Any>?,
attachmentId: String,
attachmentUrl: String,
attachmentSize: Long,
) {
impl.logMessage(message, severity, properties, attachmentId, attachmentUrl, attachmentSize)
}

override fun logException(throwable: Throwable) {
impl.logException(throwable)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -318,6 +318,38 @@ internal class EmbraceImpl @JvmOverloads constructor(
)
}

override fun logMessage(
message: String,
severity: Severity,
properties: Map<String, Any>?,
attachment: ByteArray,
) {
logsApiDelegate.logMessage(
message,
severity,
properties,
attachment
)
}

override fun logMessage(
message: String,
severity: Severity,
properties: Map<String, Any>?,
attachmentId: String,
attachmentUrl: String,
attachmentSize: Long,
) {
logsApiDelegate.logMessage(
message,
severity,
properties,
attachmentId,
attachmentUrl,
attachmentSize
)
}

/**
* Gets the [EmbraceInternalInterface] that should be used as the sole source of
* communication with other Android SDK modules.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,34 @@ internal class LogsApiDelegate(
)
}

override fun logMessage(
message: String,
severity: Severity,
properties: Map<String, Any>?,
attachment: ByteArray,
) {
logMessageImpl(
severity = severity,
message = message,
properties = properties,
)
}

override fun logMessage(
message: String,
severity: Severity,
properties: Map<String, Any>?,
attachmentId: String,
attachmentUrl: String,
attachmentSize: Long,
) {
logMessageImpl(
severity = severity,
message = message,
properties = properties,
)
}

override fun logException(
throwable: Throwable,
severity: Severity,
Expand Down

0 comments on commit f1ed6ad

Please sign in to comment.