Skip to content

Commit

Permalink
Merge pull request #2476 from DataDog/yl/compose/clean-telementry
Browse files Browse the repository at this point in the history
RUM-7870: Remove non-critical Compose reflection error from telemetry
  • Loading branch information
ambushwork authored Jan 6, 2025
2 parents 979bd88 + 8b94d73 commit 285ae49
Show file tree
Hide file tree
Showing 2 changed files with 78 additions and 19 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -81,22 +81,39 @@ internal object ComposeReflection {
val PainterFieldOfContentPainterModifier =
ContentPainterModifierClass?.getDeclaredFieldSafe("painter")

val ContentPainterElementClass = getClassSafe("coil.compose.ContentPainterElement")
val ContentPainterElementClass = getClassSafe(
"coil.compose.ContentPainterElement",
isCritical = false
)
val PainterFieldOfContentPainterElement =
ContentPainterElementClass?.getDeclaredFieldSafe("painter")

val AsyncImagePainterClass = getClassSafe("coil.compose.AsyncImagePainter")
val AsyncImagePainterClass = getClassSafe(
"coil.compose.AsyncImagePainter",
isCritical = false
)
val PainterFieldOfAsyncImagePainter = AsyncImagePainterClass?.getDeclaredFieldSafe("_painter")

// Region of MultiParagraph text
val ParagraphInfoListField =
MultiParagraph::class.java.getDeclaredFieldSafe("paragraphInfoList")
val ParagraphInfoListField = MultiParagraph::class.java.getDeclaredFieldSafe(
"paragraphInfoList",
isCritical = false
)
val ParagraphInfoClass = getClassSafe("androidx.compose.ui.text.ParagraphInfo")
val ParagraphField = ParagraphInfoClass?.getDeclaredFieldSafe("paragraph")
val ParagraphField = ParagraphInfoClass?.getDeclaredFieldSafe(
"paragraph",
isCritical = false
)
val AndroidParagraphClass = getClassSafe("androidx.compose.ui.text.AndroidParagraph")
val LayoutField = AndroidParagraphClass?.getDeclaredFieldSafe("layout")
val LayoutField = AndroidParagraphClass?.getDeclaredFieldSafe(
"layout",
isCritical = false
)
val TextLayoutClass = getClassSafe("androidx.compose.ui.text.android.TextLayout")
val StaticLayoutField = TextLayoutClass?.getDeclaredFieldSafe("layout")
val StaticLayoutField = TextLayoutClass?.getDeclaredFieldSafe(
"layout",
isCritical = false
)
}

internal fun Field.accessible(): Field {
Expand Down Expand Up @@ -128,49 +145,70 @@ internal fun Field.getSafe(target: Any?): Any? {
}
}

internal fun getClassSafe(className: String): Class<*>? {
internal fun getClassSafe(className: String, isCritical: Boolean = true): Class<*>? {
return try {
Class.forName(className)
} catch (e: LinkageError) {
logReflectionException(className, LOG_TYPE_CLASS, LOG_REASON_LINKAGE_ERROR, e)
if (isCritical) {
logReflectionException(className, LOG_TYPE_CLASS, LOG_REASON_LINKAGE_ERROR, e)
}
null
} catch (e: ExceptionInInitializerError) {
logReflectionException(className, LOG_TYPE_CLASS, LOG_REASON_INITIALIZATION_ERROR, e)
if (isCritical) {
logReflectionException(className, LOG_TYPE_CLASS, LOG_REASON_INITIALIZATION_ERROR, e)
}
null
} catch (e: ClassNotFoundException) {
logNoSuchException(className, LOG_TYPE_CLASS, e)
if (isCritical) {
logNoSuchException(className, LOG_TYPE_CLASS, e)
}
null
}
}

@Suppress("TooGenericExceptionCaught")
internal fun Class<*>.getDeclaredFieldSafe(fieldName: String): Field? {
internal fun Class<*>.getDeclaredFieldSafe(fieldName: String, isCritical: Boolean = true): Field? {
return try {
getDeclaredField(fieldName).accessible()
} catch (e: SecurityException) {
logSecurityException(fieldName, LOG_TYPE_FIELD, e)
if (isCritical) {
logSecurityException(fieldName, LOG_TYPE_FIELD, e)
}
null
} catch (e: NullPointerException) {
logNullPointerException(fieldName, LOG_TYPE_FIELD, e)
if (isCritical) {
logNullPointerException(fieldName, LOG_TYPE_FIELD, e)
}
null
} catch (e: NoSuchFieldException) {
logNoSuchException(fieldName, LOG_TYPE_FIELD, e)
if (isCritical) {
logNoSuchException(fieldName, LOG_TYPE_FIELD, e)
}
null
}
}

@Suppress("TooGenericExceptionCaught")
internal fun Class<*>.getDeclaredMethodSafe(methodName: String): Method? {
internal fun Class<*>.getDeclaredMethodSafe(
methodName: String,
isCritical: Boolean = true
): Method? {
return try {
getDeclaredMethod(methodName).accessible()
} catch (e: SecurityException) {
logSecurityException(methodName, LOG_TYPE_METHOD, e)
if (isCritical) {
logSecurityException(methodName, LOG_TYPE_METHOD, e)
}
null
} catch (e: NullPointerException) {
logNullPointerException(methodName, LOG_TYPE_METHOD, e)
if (isCritical) {
logNullPointerException(methodName, LOG_TYPE_METHOD, e)
}
null
} catch (e: NoSuchMethodException) {
logNoSuchException(methodName, LOG_TYPE_METHOD, e)
if (isCritical) {
logNoSuchException(methodName, LOG_TYPE_METHOD, e)
}
null
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.Path
import androidx.compose.ui.graphics.Shape
import androidx.compose.ui.graphics.painter.BitmapPainter
import androidx.compose.ui.graphics.painter.Painter
import androidx.compose.ui.graphics.vector.VectorPainter
import androidx.compose.ui.semantics.ProgressBarRangeInfo
import androidx.compose.ui.semantics.SemanticsActions
Expand All @@ -24,6 +25,9 @@ import androidx.compose.ui.semantics.getOrNull
import androidx.compose.ui.text.TextLayoutInput
import androidx.compose.ui.text.TextLayoutResult
import androidx.compose.ui.unit.Density
import com.datadog.android.Datadog
import com.datadog.android.api.InternalLogger
import com.datadog.android.api.feature.FeatureSdkCore
import com.datadog.android.sessionreplay.ImagePrivacy
import com.datadog.android.sessionreplay.TextAndInputPrivacy
import com.datadog.android.sessionreplay.TouchPrivacy
Expand Down Expand Up @@ -250,6 +254,7 @@ internal class SemanticsUtils(private val reflectionUtils: ReflectionUtils = Ref
is BitmapPainter -> reflectionUtils.getBitmapInBitmapPainter(painter)
is VectorPainter -> reflectionUtils.getBitmapInVectorPainter(painter)
else -> {
logUnsupportedPainter(painter)
null
}
}
Expand All @@ -263,6 +268,22 @@ internal class SemanticsUtils(private val reflectionUtils: ReflectionUtils = Ref
}
}

private fun logUnsupportedPainter(painter: Painter?) {
val painterType = painter?.javaClass?.simpleName ?: "null"
(Datadog.getInstance() as? FeatureSdkCore)?.internalLogger?.log(
level = InternalLogger.Level.ERROR,
targets = listOf(
InternalLogger.Target.MAINTAINER,
InternalLogger.Target.TELEMETRY
),
messageBuilder = { "Unsupported painter type in Compose: $painterType" },
onlyOnce = true,
additionalProperties = mapOf(
"painter.type" to painterType
)
)
}

private fun resolveModifierColor(semanticsNode: SemanticsNode): Color? {
val modifier = semanticsNode.layoutInfo.getModifierInfo().firstOrNull {
reflectionUtils.isTextStringSimpleElement(it.modifier)
Expand Down

0 comments on commit 285ae49

Please sign in to comment.