[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",
|
btrace : "com.bytedance.btrace:rhea-core:2.0.0",
|
||||||
|
|
||||||
mofang_runtime : "com.mogo.eagle.core.mofang:runtime:2.0.6",
|
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",
|
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.content.*
|
||||||
import android.os.Process
|
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.*
|
||||||
import com.mogo.core.log.record.config.*
|
import com.mogo.core.log.record.config.*
|
||||||
import com.mogo.core.log.record.config.crash.*
|
import com.mogo.core.log.record.config.crash.*
|
||||||
import com.mogo.eagle.core.function.api.devatools.logcat.*
|
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 com.zhjt.mogo_core_function_devatools.logcat.uploader.*
|
||||||
import kotlinx.coroutines.*
|
import kotlinx.coroutines.*
|
||||||
import java.io.*
|
import java.io.*
|
||||||
import java.util.concurrent.TimeUnit.MINUTES
|
import java.util.concurrent.TimeUnit.MINUTES
|
||||||
import java.util.concurrent.atomic.AtomicBoolean
|
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 flag by lazy { AtomicBoolean(false) }
|
||||||
|
|
||||||
private val scope by lazy { CoroutineScope(Dispatchers.IO + SupervisorJob()) }
|
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) {
|
override fun init(context: Context) {
|
||||||
val zipDir = File(context.getExternalFilesDir(null), "logcat/zip")
|
val zipDir = File(context.getExternalFilesDir(null), "logcat/zip")
|
||||||
LogcatManager.init(LogcatConfig.Builder().context(context)
|
LogcatManager.init(LogcatConfig.Builder().context(context)
|
||||||
@@ -33,27 +48,60 @@ internal class MoGoLogRecordProviderImpl: IMoGoLogRecordProvider {
|
|||||||
.crashDir(File(context.getExternalFilesDir(null), "logcat/crash"))
|
.crashDir(File(context.getExternalFilesDir(null), "logcat/crash"))
|
||||||
.javaCrash(true)
|
.javaCrash(true)
|
||||||
.anr(true)
|
.anr(true)
|
||||||
|
.nativeCrash(true)
|
||||||
.build())
|
.build())
|
||||||
.uploader(LogRecordUploader()))
|
.uploader(LogRecordUploader()))
|
||||||
scope.launch {
|
scope.launch {
|
||||||
try {
|
try {
|
||||||
if (zipDir.exists()) {
|
zipDir.takeIf { it.exists() }?.deleteRecursively()
|
||||||
zipDir.listFiles()?.forEach {
|
|
||||||
it.delete()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (t: Throwable) {
|
} catch (t: Throwable) {
|
||||||
t.printStackTrace()
|
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() {
|
override fun start() {
|
||||||
if (flag.get()) {
|
if (flag.get()) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
flag.set(true)
|
flag.set(true)
|
||||||
|
MogoAiCloudSocketManager.getInstance(AbsMogoApplication.getApp().applicationContext)
|
||||||
|
.registerOnMessageListener(LogRecordConfig.LOG_RECORD_TYPE, this)
|
||||||
LogcatManager.start()
|
LogcatManager.start()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -61,18 +109,25 @@ internal class MoGoLogRecordProviderImpl: IMoGoLogRecordProvider {
|
|||||||
if (!flag.get()) {
|
if (!flag.get()) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
MogoAiCloudSocketManager.getInstance(AbsMogoApplication.getApp().applicationContext)
|
||||||
|
.unregisterOnMessageListener(LogRecordConfig.LOG_RECORD_TYPE, this)
|
||||||
flag.set(false)
|
flag.set(false)
|
||||||
LogcatManager.stop()
|
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? {
|
override fun export(): File? {
|
||||||
return LogcatManager.export()
|
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
|
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.config.uploader.*
|
||||||
import com.mogo.core.log.record.model.*
|
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 {
|
companion object {
|
||||||
throw AssertionError("Not Implementation")
|
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 stop()
|
||||||
|
|
||||||
fun upload(startTime: Long, endTime: Long)
|
|
||||||
|
|
||||||
fun export(): File?
|
fun export(): File?
|
||||||
|
|
||||||
|
fun testJavaCrash(runOnNewThread: Boolean)
|
||||||
|
|
||||||
|
fun testNativeCrash(runOnNewThread: Boolean)
|
||||||
|
|
||||||
|
fun testAnrCrash()
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user