2026-06-16 17:39:23 +08:00
|
|
|
package com.xuqm.sdk.bugcollect
|
2026-06-16 12:10:58 +08:00
|
|
|
|
|
|
|
|
object Fingerprint {
|
2026-06-17 15:30:40 +08:00
|
|
|
// exceptionType = exception class name (e.g. "NullPointerException"), NOT the level string
|
2026-06-23 15:19:51 +08:00
|
|
|
// Only the innermost "at" frame (throw site) is used for grouping.
|
|
|
|
|
// Kotlin coroutine dispatcher frames (frame 2+) vary across runs of the same logical error,
|
|
|
|
|
// which caused identical errors to create separate issues.
|
2026-06-17 15:30:40 +08:00
|
|
|
fun compute(exceptionType: String, message: String, stack: String): String {
|
2026-06-23 15:19:51 +08:00
|
|
|
val top1 = stack.lines().filter { it.trim().startsWith("at ") }.take(1).joinToString("|")
|
2026-06-16 12:10:58 +08:00
|
|
|
val normalized = message.replace(Regex("\\b\\d{4,}\\b"), "N")
|
2026-06-23 15:19:51 +08:00
|
|
|
return sha256("$exceptionType:$normalized:$top1")
|
2026-06-16 12:10:58 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private fun sha256(s: String): String =
|
|
|
|
|
java.security.MessageDigest.getInstance("SHA-256")
|
|
|
|
|
.digest(s.toByteArray())
|
|
|
|
|
.joinToString("") { "%02x".format(it) }
|
|
|
|
|
}
|