[6.0.0][全量日志] 全量日志添加上传逻辑
This commit is contained in:
@@ -0,0 +1,94 @@
|
||||
package com.mogo.functions.test
|
||||
|
||||
import android.graphics.Color
|
||||
import android.graphics.drawable.ColorDrawable
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.Button
|
||||
import androidx.test.core.app.ActivityScenario
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||
import androidx.test.filters.LargeTest
|
||||
import androidx.test.platform.app.InstrumentationRegistry
|
||||
import com.mogo.eagle.core.function.call.devatools.CallerDevaToolsManager
|
||||
import com.mogo.eagle.core.function.main.MainLauncherActivity
|
||||
import com.mogo.eagle.core.utilcode.kotlin.onClick
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.runBlocking
|
||||
import org.junit.Before
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
import java.util.concurrent.TimeUnit
|
||||
|
||||
@RunWith(AndroidJUnit4::class)
|
||||
@LargeTest
|
||||
class CrashTest {
|
||||
|
||||
lateinit var launch: ActivityScenario<MainLauncherActivity>
|
||||
|
||||
|
||||
@Before
|
||||
fun before() {
|
||||
launch = ActivityScenario.launch(MainLauncherActivity::class.java)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testJavaCrash() = runBlocking {
|
||||
val arguments = InstrumentationRegistry.getArguments()
|
||||
val delay = arguments.getString("delay", "0")
|
||||
val newThread = arguments.getString("new_thread", "false")
|
||||
val delayLong = delay.toLong()
|
||||
if (delayLong <= 0) {
|
||||
throw AssertionError("illegal state ..")
|
||||
}
|
||||
delay(delayLong)
|
||||
launch.onActivity {
|
||||
CallerDevaToolsManager.logcat()?.testJavaCrash(newThread.toBoolean())
|
||||
}
|
||||
delay(TimeUnit.MINUTES.toMillis(10))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testANRCrash() = runBlocking {
|
||||
val arguments = InstrumentationRegistry.getArguments()
|
||||
val delay = arguments.getString("delay", "0")
|
||||
val delayLong = delay.toLong()
|
||||
if (delayLong <= 0) {
|
||||
throw AssertionError("illegal state ..")
|
||||
}
|
||||
delay(delayLong)
|
||||
launch.onActivity { itx ->
|
||||
|
||||
val content = (itx.findViewById<View>(android.R.id.content) as ViewGroup)
|
||||
content.postDelayed({
|
||||
content.addView(Button(itx).also {
|
||||
it.background = ColorDrawable(Color.RED)
|
||||
it.setPadding(20, 20, 20, 20)
|
||||
it.textSize = 40.0f
|
||||
it.text = "点我触发ANR"
|
||||
it.setTextColor(Color.WHITE)
|
||||
it.onClick {
|
||||
CallerDevaToolsManager.logcat()?.testAnrCrash()
|
||||
}
|
||||
})
|
||||
}, 1000)
|
||||
|
||||
}
|
||||
delay(TimeUnit.MINUTES.toMillis(10))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testNativeCrash() = runBlocking {
|
||||
val arguments = InstrumentationRegistry.getArguments()
|
||||
val delay = arguments.getString("delay", "0")
|
||||
val newThread = arguments.getString("new_thread", "false")
|
||||
val delayLong = delay.toLong()
|
||||
if (delayLong <= 0) {
|
||||
throw AssertionError("illegal state ..")
|
||||
}
|
||||
delay(delayLong)
|
||||
launch.onActivity {
|
||||
CallerDevaToolsManager.logcat()?.testNativeCrash(newThread.toBoolean())
|
||||
}
|
||||
delay(TimeUnit.MINUTES.toMillis(10))
|
||||
}
|
||||
}
|
||||
@@ -221,7 +221,7 @@ ext {
|
||||
btrace : "com.bytedance.btrace:rhea-core:2.0.0",
|
||||
|
||||
mofang_runtime : "com.mogo.eagle.core.mofang:runtime:2.0.6",
|
||||
log_runtime : "com.mogo.eagle.core.log.record:runtime:1.0.7",
|
||||
log_runtime : "com.mogo.eagle.core.log.record:runtime:1.0.8",
|
||||
|
||||
// 安全证书
|
||||
passport_secret : "com.zhidaoauto:sdk-java:1.0.5-SNAPSHOT",
|
||||
|
||||
@@ -2,24 +2,39 @@ package com.zhjt.mogo_core_function_devatools.logcat
|
||||
|
||||
import android.content.*
|
||||
import android.os.Process
|
||||
import android.util.*
|
||||
import android.os.SystemClock
|
||||
import android.util.Log
|
||||
import com.mogo.aicloud.services.socket.IMogoOnMessageListener
|
||||
import com.mogo.aicloud.services.socket.MogoAiCloudSocketManager
|
||||
import com.mogo.commons.AbsMogoApplication
|
||||
import com.mogo.core.log.record.*
|
||||
import com.mogo.core.log.record.config.*
|
||||
import com.mogo.core.log.record.config.crash.*
|
||||
import com.mogo.eagle.core.function.api.devatools.logcat.*
|
||||
import com.mogo.eagle.core.utilcode.util.*
|
||||
import com.zhidao.loglib.bean.RemoteLogPushContent
|
||||
import com.zhjt.mogo_core_function_devatools.logcat.config.LogRecordConfig
|
||||
import com.zhjt.mogo_core_function_devatools.logcat.uploader.*
|
||||
import kotlinx.coroutines.*
|
||||
import java.io.*
|
||||
import java.util.concurrent.TimeUnit.MINUTES
|
||||
import java.util.concurrent.atomic.AtomicBoolean
|
||||
import java.util.concurrent.atomic.AtomicLong
|
||||
|
||||
internal class MoGoLogRecordProviderImpl: IMoGoLogRecordProvider {
|
||||
internal class MoGoLogRecordProviderImpl: IMoGoLogRecordProvider,
|
||||
IMogoOnMessageListener<RemoteLogPushContent> {
|
||||
|
||||
companion object {
|
||||
private const val TAG = "MoGoLogRecordProviderImpl"
|
||||
}
|
||||
|
||||
private val flag by lazy { AtomicBoolean(false) }
|
||||
|
||||
private val scope by lazy { CoroutineScope(Dispatchers.IO + SupervisorJob()) }
|
||||
|
||||
private val uploadThreshold by lazy { MINUTES.toMillis(1) } //全量日志的上传的安全时间,在此时间内,上传任务只会触发一次
|
||||
|
||||
private val lastUploadTime by lazy { AtomicLong(0) }
|
||||
|
||||
override fun init(context: Context) {
|
||||
val zipDir = File(context.getExternalFilesDir(null), "logcat/zip")
|
||||
LogcatManager.init(LogcatConfig.Builder().context(context)
|
||||
@@ -33,27 +48,60 @@ internal class MoGoLogRecordProviderImpl: IMoGoLogRecordProvider {
|
||||
.crashDir(File(context.getExternalFilesDir(null), "logcat/crash"))
|
||||
.javaCrash(true)
|
||||
.anr(true)
|
||||
.nativeCrash(true)
|
||||
.build())
|
||||
.uploader(LogRecordUploader()))
|
||||
scope.launch {
|
||||
try {
|
||||
if (zipDir.exists()) {
|
||||
zipDir.listFiles()?.forEach {
|
||||
it.delete()
|
||||
}
|
||||
}
|
||||
zipDir.takeIf { it.exists() }?.deleteRecursively()
|
||||
} catch (t: Throwable) {
|
||||
t.printStackTrace()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun target(): Class<RemoteLogPushContent> {
|
||||
return RemoteLogPushContent::class.java
|
||||
}
|
||||
|
||||
override fun onMsgReceived(obj: RemoteLogPushContent?) {
|
||||
Log.d(TAG, "--- onMsgReceived --: $obj")
|
||||
if (obj == null) return
|
||||
val endTime = System.currentTimeMillis()
|
||||
val startTime = when(obj.type) {
|
||||
LogRecordConfig.ALL_LOG_15_MINUTES -> endTime - MINUTES.toMillis(15)
|
||||
LogRecordConfig.ALL_LOG_30_MINUTES -> endTime - MINUTES.toMillis(30)
|
||||
LogRecordConfig.ALL_LOG_45_MINUTES -> endTime - MINUTES.toMillis(45)
|
||||
LogRecordConfig.ALL_LOG_60_MINUTES -> endTime - MINUTES.toMillis(60)
|
||||
LogRecordConfig.ALL_LOG_120_MINUTES -> endTime - MINUTES.toMillis(120)
|
||||
LogRecordConfig.ALL_LOG_SAME_DAY -> endTime - (endTime % 86400000)
|
||||
LogRecordConfig.ALL_LOG -> 0
|
||||
else -> endTime
|
||||
}
|
||||
Log.d(TAG, "--- onMsgReceived -- 1: $startTime, $endTime")
|
||||
if (startTime < endTime) {
|
||||
val now = System.currentTimeMillis()
|
||||
val last = lastUploadTime.get()
|
||||
if (last <= 0) {
|
||||
lastUploadTime.set(System.currentTimeMillis())
|
||||
}
|
||||
if (last > 0 && (now - last) <= uploadThreshold) {
|
||||
return
|
||||
}
|
||||
lastUploadTime.set(System.currentTimeMillis())
|
||||
scope.launch {
|
||||
LogcatManager.upload(startTime, endTime)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun start() {
|
||||
if (flag.get()) {
|
||||
return
|
||||
}
|
||||
flag.set(true)
|
||||
MogoAiCloudSocketManager.getInstance(AbsMogoApplication.getApp().applicationContext)
|
||||
.registerOnMessageListener(LogRecordConfig.LOG_RECORD_TYPE, this)
|
||||
LogcatManager.start()
|
||||
}
|
||||
|
||||
@@ -61,18 +109,25 @@ internal class MoGoLogRecordProviderImpl: IMoGoLogRecordProvider {
|
||||
if (!flag.get()) {
|
||||
return
|
||||
}
|
||||
MogoAiCloudSocketManager.getInstance(AbsMogoApplication.getApp().applicationContext)
|
||||
.unregisterOnMessageListener(LogRecordConfig.LOG_RECORD_TYPE, this)
|
||||
flag.set(false)
|
||||
LogcatManager.stop()
|
||||
}
|
||||
|
||||
override fun upload(startTime: Long, endTime: Long) {
|
||||
scope.launch {
|
||||
val result = LogcatManager.upload(startTime, endTime)
|
||||
Log.d(TAG, "上传日志:[startTime:$startTime, endTime: $endTime], 结果: $result")
|
||||
}
|
||||
}
|
||||
|
||||
override fun export(): File? {
|
||||
return LogcatManager.export()
|
||||
}
|
||||
|
||||
override fun testJavaCrash(runOnNewThread: Boolean) {
|
||||
LogcatManager.testJavaCrash(runOnNewThread)
|
||||
}
|
||||
|
||||
override fun testNativeCrash(runOnNewThread: Boolean) {
|
||||
LogcatManager.testNativeCrash(runOnNewThread)
|
||||
}
|
||||
|
||||
override fun testAnrCrash() {
|
||||
SystemClock.sleep(15000)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
package com.zhjt.mogo_core_function_devatools.logcat.config
|
||||
|
||||
internal class LogRecordConfig {
|
||||
|
||||
companion object{
|
||||
|
||||
/**
|
||||
* 长链下行数据的消息类型
|
||||
*/
|
||||
const val LOG_RECORD_TYPE = 500002
|
||||
|
||||
|
||||
/**
|
||||
* 全量日志抓取: 上传从收到抓取指令往前15分钟的日志
|
||||
*/
|
||||
const val ALL_LOG_15_MINUTES = 1
|
||||
|
||||
/**
|
||||
* 全量日志抓取: 上传从收到抓取指令往前30分钟的日志
|
||||
*/
|
||||
const val ALL_LOG_30_MINUTES = 2
|
||||
|
||||
/**
|
||||
* 全量日志抓取: 上传从收到抓取指令往前45分钟的日志
|
||||
*/
|
||||
const val ALL_LOG_45_MINUTES = 3
|
||||
|
||||
/**
|
||||
* 全量日志抓取: 上传从收到抓取指令往前60分钟的日志
|
||||
*/
|
||||
const val ALL_LOG_60_MINUTES = 4
|
||||
|
||||
/**
|
||||
* 全量日志抓取: 上传从收到抓取指令往前120分钟的日志
|
||||
*/
|
||||
const val ALL_LOG_120_MINUTES = 5
|
||||
|
||||
/**
|
||||
* 全量日志抓取: 上传从收到抓取指令往前当天的日志
|
||||
*/
|
||||
const val ALL_LOG_SAME_DAY = 6
|
||||
|
||||
/**
|
||||
* 全量日志抓取: 上传从收到抓取指令之前所有的日志
|
||||
*/
|
||||
const val ALL_LOG = 7
|
||||
}
|
||||
}
|
||||
@@ -1,11 +1,65 @@
|
||||
package com.zhjt.mogo_core_function_devatools.logcat.uploader
|
||||
|
||||
import android.util.Log
|
||||
import com.mogo.core.log.record.config.uploader.*
|
||||
import com.mogo.core.log.record.model.*
|
||||
import com.mogo.eagle.core.utilcode.util.ThreadUtils
|
||||
import com.zhidao.cosupload.callback.CosStatusCallback
|
||||
import com.zhidao.cosupload.callback.CosStatusCallbackManager
|
||||
import com.zhidao.cosupload.model.CallbackData
|
||||
import com.zhidao.loglib.upload.UploadManager
|
||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||
import kotlinx.coroutines.asCoroutineDispatcher
|
||||
import kotlinx.coroutines.flow.channelFlow
|
||||
import kotlinx.coroutines.flow.firstOrNull
|
||||
import kotlinx.coroutines.flow.flowOn
|
||||
|
||||
internal class LogRecordUploader: ILogcatUploader {
|
||||
internal class LogRecordUploader : ILogcatUploader {
|
||||
|
||||
override suspend fun upload(startTime: Long, endTime: Long, generatedZipPath: String): UploadState {
|
||||
throw AssertionError("Not Implementation")
|
||||
companion object {
|
||||
private const val TAG = "LogRecordUploader"
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalCoroutinesApi::class)
|
||||
override suspend fun upload(startTime: Long, endTime: Long, generatedZipPath: String): UploadState =
|
||||
channelFlow {
|
||||
Log.d(TAG, "---- 开始上传 --- 1 ---")
|
||||
CosStatusCallbackManager.getInstance().register(object : CosStatusCallback {
|
||||
|
||||
override fun onStartUpload(data: CallbackData?) {
|
||||
if (data?.localPath == generatedZipPath) {
|
||||
Log.d(TAG, "---- onStartUpload ---: $data")
|
||||
}
|
||||
}
|
||||
|
||||
override fun uploadCosCompleted(data: CallbackData?) {
|
||||
if (data?.localPath == generatedZipPath) {
|
||||
Log.d(TAG, "---- uploadCosCompleted ---: $data")
|
||||
trySend(UploadSuccess)
|
||||
CosStatusCallbackManager.getInstance().unregister(this)
|
||||
}
|
||||
}
|
||||
|
||||
override fun uploadCosFailed(data: CallbackData?) {
|
||||
if (data?.localPath == generatedZipPath) {
|
||||
Log.d(TAG, "---- uploadCosFailed ---: $data")
|
||||
trySend(UploadError(Reason(data.exception)))
|
||||
CosStatusCallbackManager.getInstance().unregister(this)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onProgress(data: CallbackData?) {
|
||||
if (data?.localPath == generatedZipPath) {
|
||||
Log.d(TAG, "---- onProgress ---: $data")
|
||||
}
|
||||
}
|
||||
})
|
||||
try {
|
||||
UploadManager.getInstance().uploadSingleFile(generatedZipPath)
|
||||
} catch (t: Throwable) {
|
||||
Log.d(TAG, "---- 上传失败 ---: ${t.message}")
|
||||
trySend(UploadError(Reason(t.message ?: "")))
|
||||
}
|
||||
}.flowOn(ThreadUtils.getIoPool().asCoroutineDispatcher()).firstOrNull() ?: UploadError(Reason("UnKnown reason"))
|
||||
|
||||
}
|
||||
@@ -11,7 +11,11 @@ interface IMoGoLogRecordProvider {
|
||||
|
||||
fun stop()
|
||||
|
||||
fun upload(startTime: Long, endTime: Long)
|
||||
|
||||
fun export(): File?
|
||||
|
||||
fun testJavaCrash(runOnNewThread: Boolean)
|
||||
|
||||
fun testNativeCrash(runOnNewThread: Boolean)
|
||||
|
||||
fun testAnrCrash()
|
||||
}
|
||||
Reference in New Issue
Block a user