```kotlin import android.app.Application import com.bugsnag.android.Bugsnag import com.bugsnag.android.Configuration import com.bugsnag.android.ErrorTypes import com.bugsnag.android.Event import com.bugsnag.android.ThreadSendPolicy import shark.HeapAnalysis import shark.HeapAnalysisFailure import shark.HeapAnalysisSuccess import shark.Leak import shark.LeakTrace import shark.LeakTraceReference import shark.LibraryLeak class BugsnagLeakUploader(applicationContext: Application) { private val bugsnagClient = Bugsnag.start( applicationContext, Configuration("YOUR_BUGSNAG_API_KEY").apply { enabledErrorTypes = ErrorTypes( anrs = false, ndkCrashes = false, unhandledExceptions = false, unhandledRejections = false ) sendThreads = ThreadSendPolicy.NEVER } ) fun upload(heapAnalysis: HeapAnalysis) { when (heapAnalysis) { is HeapAnalysisSuccess -> { val allLeakTraces = heapAnalysis .allLeaks .toList() .flatMap { leak -> leak.leakTraces.map { leakTrace -> leak to leakTrace } } if (allLeakTraces.isEmpty()) { // Track how often we perform a heap analysis that yields no result. bugsnagClient.notify(NoLeakException()) { event -> event.addHeapAnalysis(heapAnalysis) true } } else { allLeakTraces.forEach { (leak, leakTrace) -> val message = "Memory leak: ${leak.shortDescription}. See LEAK tab." val exception = leakTrace.asFakeException(message) bugsnagClient.notify(exception) { event -> event.addHeapAnalysis(heapAnalysis) event.addLeak(leak) event.addLeakTrace(leakTrace) event.groupingHash = leak.signature true } } } } is HeapAnalysisFailure -> { // Please file any reported failure to // https://github.com/square/leakcanary/issues bugsnagClient.notify(heapAnalysis.exception) } } } class NoLeakException : RuntimeException() private fun Event.addHeapAnalysis(heapAnalysis: HeapAnalysisSuccess) { addMetadata("Leak", "heapDumpPath", heapAnalysis.heapDumpFile.absolutePath) heapAnalysis.metadata.forEach { (key, value) -> addMetadata("Leak", key, value) } addMetadata("Leak", "analysisDurationMs", heapAnalysis.analysisDurationMillis) } private fun Event.addLeak(leak: Leak) { addMetadata("Leak", "libraryLeak", leak is LibraryLeak) if (leak is LibraryLeak) { addMetadata("Leak", "libraryLeakPattern", leak.pattern.toString()) addMetadata("Leak", "libraryLeakDescription", leak.description) } } private fun Event.addLeakTrace(leakTrace: LeakTrace) { addMetadata("Leak", "retainedHeapByteSize", leakTrace.retainedHeapByteSize) addMetadata("Leak", "signature", leakTrace.signature) addMetadata("Leak", "leakTrace", leakTrace.toString()) } private fun LeakTrace.asFakeException(message: String): RuntimeException { val exception = RuntimeException(message) val stackTrace = mutableListOf() stackTrace.add(StackTraceElement("GcRoot", gcRootType.name, "GcRoot.kt", 42)) for (cause in referencePath) { stackTrace.add(buildStackTraceElement(cause)) } exception.stackTrace = stackTrace.toTypedArray() return exception } private fun buildStackTraceElement(reference: LeakTraceReference): StackTraceElement { val file = reference.owningClassName.substringAfterLast(".") + ".kt" return StackTraceElement(reference.owningClassName, reference.referenceDisplayName, file, 42) } } ```