Skip to content

Commit 6e64409

Browse files
authored
Add extra logs for sending media (#5218)
* Add extra logs for sending media We have some issues with stuck media uploads that could use some logs to understand where the process gets stuck. * Fix mocks for test
1 parent 5a3f803 commit 6e64409

File tree

5 files changed

+36
-5
lines changed

5 files changed

+36
-5
lines changed

features/messages/impl/src/main/kotlin/io/element/android/features/messages/impl/attachments/preview/AttachmentsPreviewPresenter.kt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ import io.element.android.features.messages.impl.attachments.Attachment
2424
import io.element.android.features.messages.impl.attachments.video.MediaOptimizationSelectorPresenter
2525
import io.element.android.libraries.androidutils.file.TemporaryUriDeleter
2626
import io.element.android.libraries.androidutils.file.safeDelete
27+
import io.element.android.libraries.androidutils.hash.hash
2728
import io.element.android.libraries.architecture.Presenter
2829
import io.element.android.libraries.core.coroutine.CoroutineDispatchers
2930
import io.element.android.libraries.core.coroutine.firstInstanceOf
@@ -261,6 +262,7 @@ class AttachmentsPreviewPresenter @AssistedInject constructor(
261262
mediaOptimizationConfig = mediaOptimizationConfig,
262263
).fold(
263264
onSuccess = { mediaUploadInfo ->
265+
Timber.d("Media ${mediaUploadInfo.file.path.orEmpty().hash()} finished processing, it's now ready to upload")
264266
sendActionState.value = SendActionState.Sending.ReadyToUpload(mediaUploadInfo)
265267
},
266268
onFailure = {

features/messages/impl/src/test/kotlin/io/element/android/features/messages/impl/attachments/AttachmentsPreviewPresenterTest.kt

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@ import io.element.android.tests.testutils.lambda.lambdaRecorder
5858
import io.element.android.tests.testutils.lambda.value
5959
import io.element.android.tests.testutils.test
6060
import io.element.android.tests.testutils.testCoroutineDispatchers
61+
import io.mockk.every
6162
import io.mockk.mockk
6263
import kotlinx.collections.immutable.persistentListOf
6364
import kotlinx.coroutines.CompletableDeferred
@@ -77,7 +78,9 @@ class AttachmentsPreviewPresenterTest {
7778
@get:Rule
7879
val warmUpRule = WarmUpRule()
7980

80-
private val mockMediaUrl: Uri = mockk("localMediaUri")
81+
private val mockMediaUrl: Uri = mockk("localMediaUri") {
82+
every { path } returns "/path/to/media"
83+
}
8184

8285
@Test
8386
fun `present - initial state`() = runTest {

libraries/matrix/impl/src/main/kotlin/io/element/android/libraries/matrix/impl/timeline/RustTimeline.kt

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77

88
package io.element.android.libraries.matrix.impl.timeline
99

10+
import io.element.android.libraries.androidutils.hash.hash
1011
import io.element.android.libraries.core.extensions.runCatchingExceptions
1112
import io.element.android.libraries.matrix.api.core.EventId
1213
import io.element.android.libraries.matrix.api.core.RoomId
@@ -338,6 +339,7 @@ class RustTimeline(
338339
formattedCaption: String?,
339340
inReplyToEventId: EventId?,
340341
): Result<MediaUploadHandler> {
342+
Timber.d("Sending image ${file.path.hash()}")
341343
return sendAttachment(listOfNotNull(file, thumbnailFile)) {
342344
inner.sendImage(
343345
params = UploadParameters(
@@ -363,6 +365,7 @@ class RustTimeline(
363365
formattedCaption: String?,
364366
inReplyToEventId: EventId?,
365367
): Result<MediaUploadHandler> {
368+
Timber.d("Sending video ${file.path.hash()}")
366369
return sendAttachment(listOfNotNull(file, thumbnailFile)) {
367370
inner.sendVideo(
368371
params = UploadParameters(
@@ -387,6 +390,7 @@ class RustTimeline(
387390
formattedCaption: String?,
388391
inReplyToEventId: EventId?,
389392
): Result<MediaUploadHandler> {
393+
Timber.d("Sending audio ${file.path.hash()}")
390394
return sendAttachment(listOf(file)) {
391395
inner.sendAudio(
392396
params = UploadParameters(
@@ -410,6 +414,7 @@ class RustTimeline(
410414
formattedCaption: String?,
411415
inReplyToEventId: EventId?,
412416
): Result<MediaUploadHandler> {
417+
Timber.d("Sending file ${file.path.hash()}")
413418
return sendAttachment(listOf(file)) {
414419
inner.sendFile(
415420
params = UploadParameters(

libraries/mediaupload/api/src/main/kotlin/io/element/android/libraries/mediaupload/api/MediaSender.kt

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import android.net.Uri
1111
import dagger.assisted.Assisted
1212
import dagger.assisted.AssistedFactory
1313
import dagger.assisted.AssistedInject
14+
import io.element.android.libraries.androidutils.hash.hash
1415
import io.element.android.libraries.core.extensions.flatMap
1516
import io.element.android.libraries.core.extensions.flatMapCatching
1617
import io.element.android.libraries.matrix.api.core.EventId
@@ -20,6 +21,8 @@ import io.element.android.libraries.matrix.api.room.JoinedRoom
2021
import io.element.android.libraries.matrix.api.timeline.Timeline
2122
import kotlinx.coroutines.CancellationException
2223
import kotlinx.coroutines.Job
24+
import timber.log.Timber
25+
import java.io.File
2326
import java.util.concurrent.ConcurrentHashMap
2427

2528
class MediaSender @AssistedInject constructor(
@@ -43,6 +46,7 @@ class MediaSender @AssistedInject constructor(
4346
mimeType: String,
4447
mediaOptimizationConfig: MediaOptimizationConfig,
4548
): Result<MediaUploadInfo> {
49+
Timber.d("Pre-processing media | uri: ${mediaId(uri)} | mimeType: $mimeType")
4650
return preProcessor
4751
.process(
4852
uri = uri,
@@ -58,15 +62,17 @@ class MediaSender @AssistedInject constructor(
5862
formattedCaption: String?,
5963
inReplyToEventId: EventId?,
6064
): Result<Unit> {
65+
val mediaLogId = mediaId(mediaUploadInfo.file)
6166
return getTimeline().flatMap {
67+
Timber.d("Started sending media $mediaLogId using timeline: ${it.mode}")
6268
it.sendMedia(
6369
uploadInfo = mediaUploadInfo,
6470
caption = caption,
6571
formattedCaption = formattedCaption,
6672
inReplyToEventId = inReplyToEventId,
6773
)
6874
}
69-
.handleSendResult()
75+
.handleSendResult(mediaLogId)
7076
}
7177

7278
suspend fun sendMedia(
@@ -92,7 +98,7 @@ class MediaSender @AssistedInject constructor(
9298
inReplyToEventId = inReplyToEventId,
9399
)
94100
}
95-
.handleSendResult()
101+
.handleSendResult(mediaId(uri))
96102
}
97103

98104
suspend fun sendVoiceMessage(
@@ -122,17 +128,19 @@ class MediaSender @AssistedInject constructor(
122128
inReplyToEventId = inReplyToEventId,
123129
)
124130
}
125-
.handleSendResult()
131+
.handleSendResult(mediaId(uri))
126132
}
127133

128-
private fun Result<Unit>.handleSendResult() = this
134+
private fun Result<Unit>.handleSendResult(mediaId: String) = this
129135
.onFailure { error ->
130136
val job = ongoingUploadJobs.remove(Job)
137+
Timber.e(error, "Sending media $mediaId failed. Removing ongoing upload job. Total: ${ongoingUploadJobs.size}")
131138
if (error !is CancellationException) {
132139
job?.cancel()
133140
}
134141
}
135142
.onSuccess {
143+
Timber.d("Sent media $mediaId successfully. Removing ongoing upload job. Total: ${ongoingUploadJobs.size}")
136144
ongoingUploadJobs.remove(Job)
137145
}
138146

@@ -195,6 +203,7 @@ class MediaSender @AssistedInject constructor(
195203
@Suppress("RunCatchingNotAllowed")
196204
return handler
197205
.mapCatching { uploadHandler ->
206+
Timber.d("Added ongoing upload job, total: ${ongoingUploadJobs.size + 1}")
198207
ongoingUploadJobs[Job] = uploadHandler
199208
uploadHandler.await()
200209
}
@@ -214,3 +223,6 @@ class MediaSender @AssistedInject constructor(
214223
*/
215224
fun cleanUp() = preProcessor.cleanUp()
216225
}
226+
227+
private fun mediaId(uri: Uri?): String = uri?.path.orEmpty().hash()
228+
private fun mediaId(file: File): String = file.path.orEmpty().hash()

libraries/mediaupload/impl/src/main/kotlin/io/element/android/libraries/mediaupload/impl/AndroidMediaPreProcessor.kt

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import io.element.android.libraries.androidutils.file.TemporaryUriDeleter
1717
import io.element.android.libraries.androidutils.file.createTmpFile
1818
import io.element.android.libraries.androidutils.file.getFileName
1919
import io.element.android.libraries.androidutils.file.safeRenameTo
20+
import io.element.android.libraries.androidutils.hash.hash
2021
import io.element.android.libraries.androidutils.media.runAndRelease
2122
import io.element.android.libraries.core.coroutine.CoroutineDispatchers
2223
import io.element.android.libraries.core.data.tryOrNull
@@ -107,6 +108,8 @@ class AndroidMediaPreProcessor @Inject constructor(
107108
}.mapFailure { MediaPreProcessor.Failure(it) }
108109

109110
override fun cleanUp() {
111+
Timber.d("Cleaning up temporary media files")
112+
110113
// Clear temporary files created in older versions of the app
111114
cacheDir.listFiles()?.onEach { file ->
112115
if (file.isFile) {
@@ -129,6 +132,7 @@ class AndroidMediaPreProcessor @Inject constructor(
129132
}
130133

131134
private suspend fun processFile(uri: Uri, mimeType: String): MediaUploadInfo {
135+
Timber.d("Processing file ${uri.path.orEmpty().hash()}")
132136
val file = copyToTmpFile(uri)
133137
val info = FileInfo(
134138
mimetype = mimeType,
@@ -140,6 +144,7 @@ class AndroidMediaPreProcessor @Inject constructor(
140144
}
141145

142146
private fun MediaUploadInfo.postProcess(uri: Uri): MediaUploadInfo {
147+
Timber.d("Finished processing, post-processing ${uri.path.orEmpty().hash()}")
143148
val name = context.getFileName(uri) ?: return this
144149
val renamedFile = File(context.cacheDir, name).also {
145150
file.safeRenameTo(it)
@@ -154,6 +159,7 @@ class AndroidMediaPreProcessor @Inject constructor(
154159
}
155160

156161
private suspend fun processImage(uri: Uri, mimeType: String, shouldBeCompressed: Boolean): MediaUploadInfo {
162+
Timber.d("Processing image ${uri.path.orEmpty().hash()}")
157163
suspend fun processImageWithCompression(): MediaUploadInfo {
158164
// Read the orientation metadata from its own stream. Trying to reuse this stream for compression will fail.
159165
val orientation = contentResolver.openInputStream(uri).use { input ->
@@ -217,6 +223,7 @@ class AndroidMediaPreProcessor @Inject constructor(
217223
}
218224

219225
private suspend fun processVideo(uri: Uri, mimeType: String?, videoCompressionPreset: VideoCompressionPreset): MediaUploadInfo {
226+
Timber.d("Processing video ${uri.path.orEmpty().hash()}")
220227
val resultFile = runCatchingExceptions {
221228
videoCompressor.compress(uri, videoCompressionPreset)
222229
.onEach {
@@ -244,12 +251,14 @@ class AndroidMediaPreProcessor @Inject constructor(
244251
thumbnailFile = thumbnailInfo?.file
245252
)
246253
} else {
254+
Timber.d("Could not transcode video ${uri.path.orEmpty().hash()}, sending original file as plain file")
247255
// If the video could not be compressed, just use the original one, but send it as a file
248256
return processFile(uri, MimeTypes.OctetStream)
249257
}
250258
}
251259

252260
private suspend fun processAudio(uri: Uri, mimeType: String?): MediaUploadInfo {
261+
Timber.d("Processing audio ${uri.path.orEmpty().hash()}")
253262
val file = copyToTmpFile(uri)
254263
return MediaMetadataRetriever().runAndRelease {
255264
setDataSource(context, Uri.fromFile(file))

0 commit comments

Comments
 (0)