This commit is contained in:
chenjuntao
2023-11-22 16:48:49 +08:00
parent 44a2e616ab
commit f9f4bb84b4
8 changed files with 245 additions and 169 deletions

View File

@ -126,6 +126,10 @@ android {
}
}
packagingOptions {
exclude 'META-INF/gradle/incremental.annotation.processors'
}
buildTypes {
debug {
debuggable true
@ -357,7 +361,9 @@ dependencies {
implementation "com.lg:easyfloat:${easyFloat}"
implementation "com.lg:apksig:${apksig}"
implementation ("com.lg:apksig:${apksig}") {
exclude group: 'com.google.protobuf'
}
implementation "com.lg:gid:${gid}"

View File

@ -1,13 +1,13 @@
package com.gh.common.exposure
import com.aliyun.sls.android.producer.Log
import com.gh.gamecenter.BuildConfig
import com.gh.gamecenter.common.loghub.LoghubHelper
import com.gh.gamecenter.common.loghub.TLogHubHelper
import com.gh.gamecenter.common.utils.FixedSizeLinkedHashSet
import com.gh.gamecenter.common.utils.toJson
import com.gh.gamecenter.core.AppExecutor
import com.gh.gamecenter.feature.exposure.ExposureEvent
import com.lightgame.utils.Utils
import com.volcengine.model.tls.LogItem
/**
* A handful tool for committing logs to aliyun loghub.
@ -78,19 +78,20 @@ object ExposureManager {
private fun uploadExposures(eventSet: HashSet<ExposureEvent>, forced: Boolean) {
eventSet.forEach {
LoghubHelper.uploadLog(buildLog(it), LOG_STORE, forced)
TLogHubHelper.sendLog(buildLog(it), LOG_STORE)
// LoghubHelper.uploadLog(buildLog(it), LOG_STORE, forced)
// it.recycle()
}
}
private fun buildLog(event: ExposureEvent) = Log().apply {
putContent("id", event.id)
putContent("payload", event.payload.toJson())
putContent("event", event.event.toString())
putContent("source", eliminateMultipleBrackets(event.source.toJson()))
putContent("meta", event.meta.toJson())
putContent("real_millisecond", event.timeInMillisecond.toString())
putContent(
private fun buildLog(event: ExposureEvent) = LogItem(System.currentTimeMillis()).apply {
addContent("__id", event.id)
addContent("payload", event.payload.toJson())
addContent("event", event.event.toString())
addContent("source", eliminateMultipleBrackets(event.source.toJson()))
addContent("meta", event.meta.toJson())
addContent("real_millisecond", event.timeInMillisecond.toString())
addContent(
"e-traces", if (event.eTrace != null) {
eliminateMultipleBrackets(event.eTrace?.toJson() ?: "")
} else ""

View File

@ -138,4 +138,6 @@ ext {
acloudPush = "3.8.8.1"
jpushVersion = "5.2.4"
volcTlsVersion = "1.1.4"
}

View File

@ -84,7 +84,13 @@ dependencies {
api "androidx.collection:collection-ktx:${collection}"
api "androidx.activity:activity:${activity}"
api "androidx.activity:activity-ktx:${activity}"
api "com.aliyun.openservices:aliyun-log-android-sdk:${aliyunLog}"
api ("com.volcengine:volc-tls-android-sdk:${volcTlsVersion}", {
exclude group: 'com.squareup.okhttp3'
exclude group: 'junit'
exclude group: 'com.google.code.gson'
exclude group: 'org.projectlombok'
exclude group: 'com.alibaba'
})
debugApi "com.lg:chucker:${chucker}"
releaseApi "com.lg:chucker-no-op:${chucker}"
debugApi "com.squareup.okhttp3:logging-interceptor:${okHttp}"

View File

@ -77,4 +77,7 @@
-keep class com.gh.gamecenter.common.retrofit.* {*;}
### NonStickyMutableLiveData
-keep class androidx.arch.core.internal.** {*;}
-keep class androidx.arch.core.internal.** {*;}
### vol log sdk
-keep class net.jpountz.** { *; }

View File

@ -1,147 +1,147 @@
package com.gh.gamecenter.common.loghub
import com.aliyun.sls.android.producer.Log
import com.aliyun.sls.android.producer.LogProducerClient
import com.aliyun.sls.android.producer.LogProducerConfig
import com.aliyun.sls.android.producer.LogProducerResult
import com.gh.gamecenter.common.HaloApp
import com.gh.gamecenter.common.utils.EnvHelper
import com.gh.gamecenter.common.utils.PackageFlavorHelper
import com.gh.gamecenter.core.AppExecutor
import com.lightgame.utils.Utils
/**
* 上传阿里云日志辅助类
*/
object LoghubHelper {
private const val ACCESS_KEY_ID = "LTAIV3i0sNc4TPK1"
private const val ACCESS_KEY_SECRET = "8dKtTPeE5WYA6ZCeuIBcIVp7eB0ir4"
private const val ENDPOINT = "cn-qingdao.log.aliyuncs.com"
private val mClientMaps by lazy { hashMapOf<String, LogProducerClient>() }
/**
* 上传日志至阿里云 loghub
* @param uploadImmediately 马上上传日志
*/
fun uploadLog(log: Log, logStore: String, uploadImmediately: Boolean) {
// https://github.com/aliyun/aliyun-log-android-sdk/issues/60
// 在主线程直接调用会有概率触发 ANR ,在工作线程调用有机率会创建多个工作线程,麻了,还是继续在工作线程上干吧
AppExecutor.logExecutor.execute {
getClient(logStore)?.addLog(log, if (uploadImmediately) 1 else 0)
}
}
private fun getClient(logStore: String): LogProducerClient? {
// https://jira.shanqu.cc/browse/GHZS-3815?focusedCommentId=50027&page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel#comment-50027
//
val transformedLogStore = if (EnvHelper.isGATApp) "event" else logStore
if (!mClientMaps.containsKey(transformedLogStore)) {
mClientMaps[transformedLogStore] = createClient(transformedLogStore)
}
return mClientMaps[transformedLogStore]
}
private fun createClient(logStore: String): LogProducerClient {
val config = LogProducerConfig(
HaloApp.getInstance().applicationContext,
ENDPOINT,
EnvHelper.logProducerProject,
logStore,
ACCESS_KEY_ID,
ACCESS_KEY_SECRET
).apply {
// // 设置主题
// setTopic("topic")
// // 设置tag信息此tag会附加在每条日志上
// addTag("key", "value")
// 每个缓存的日志包的大小上限取值为1~5242880单位为字节。默认为1024 * 1024
setPacketLogBytes(1024 * 1024)
// 每个缓存的日志包中包含日志数量的最大值取值为1~4096默认为1024
setPacketLogCount(1024)
// 被缓存日志的发送超时时间如果缓存超时则会被立即发送单位为毫秒默认为3000
setPacketTimeout(3000)
// 单个Producer Client实例可以使用的内存的上限超出缓存时add_log接口会立即返回失败
// 默认为64 * 1024 * 1024
setMaxBufferLimit(64 * 1024 * 1024)
// 发送线程数默认为1
setSendThreadCount(1)
//网络连接超时时间整数单位秒默认为10
setConnectTimeoutSec(15)
//日志发送超时时间整数单位秒默认为15
setSendTimeoutSec(15)
//flusher线程销毁最大等待时间整数单位秒默认为1
setDestroyFlusherWaitSec(2)
//sender线程池销毁最大等待时间整数单位秒默认为1
setDestroySenderWaitSec(2)
//数据上传时的压缩类型默认为LZ4压缩0 不压缩1 LZ4压缩默认为1
setCompressType(1)
//设备时间与标准时间之差,值为标准时间-设备时间,一般此种情况用于客户端设备时间不同步的场景
//整数单位秒默认为0比如当前设备时间为1607064208, 标准时间为1607064308则值设置为 1607064308 - 1607064208 = 10
setNtpTimeOffset(0)
//日志时间与本机时间之差,超过该大小后会根据 `drop_delay_log` 选项进行处理。
//一般此种情况只会在设置persistent的情况下出现即设备下线后超过几天/数月启动,发送退出前未发出的日志
//整数单位秒默认为7*24*3600即7天
setMaxLogDelayTime(7 * 24 * 3600)
//对于超过 `max_log_delay_time` 日志的处理策略
//0 不丢弃,把日志时间修改为当前时间; 1 丢弃,默认为 1 (丢弃)
setDropDelayLog(0)
//是否丢弃鉴权失败的日志0 不丢弃1丢弃
//默认为 0即不丢弃
setDropUnauthorizedLog(0)
// 是否使用主线程回调
// false: 使用主线程回调。回调会在主线程上执行,且每个 client 都有自己单独的回调。
// true: 使用 sender 线程回调。回调会在 sender 线程上执行,每次执行回调时都会 attach 一个新的 java 线程,所有 client 共用一个回调。
// 注意:默认使用 sender 线程回调。
setCallbackFromSenderThread(true)
/**
* 以下为开启断点续传的配置, 按照如下配置开启断点续传功能后, 日志会先缓存到本地
*/
// 1 开启断点续传功能, 0 关闭
// 每次发送前会把日志保存到本地的binlog文件只有发送成功才会删除保证日志上传At Least Once
setPersistent(1)
// 持久化的文件名,需要保证文件所在的文件夹已创建。
// !!!!!!!!!!!!!!!!!!!注意!!!!!!!!!!!!!!!!!!!
// 配置多个客户端时,不应设置相同文件
setPersistentFilePath(HaloApp.getInstance().filesDir.absolutePath + "/${logStore}.dat")
// 是否每次AddLog强制刷新高可靠性场景建议打开
setPersistentForceFlush(1)
// 持久化文件滚动个数建议设置成10。
setPersistentMaxFileCount(10)
// 每个持久化文件的大小建议设置成1-10M
setPersistentMaxFileSize(1024 * 1024)
// 本地最多缓存的日志数不建议超过1M通常设置为65536即可
setPersistentMaxLogCount(65536)
}
// 通过 LogProducerConfig 构造一个 LogProducerClient 实例
// callback为可选配置, 如果不需要关注日志的发送成功或失败状态, 可以不注册 callback正式环境不注册callback
return if (!PackageFlavorHelper.IS_TEST_FLAVOR) {
LogProducerClient(config)
} else {
LogProducerClient(config) { resultCode, reqId, errorMessage, logBytes, compressedBytes ->
// resultCode 返回结果代码
// reqId 请求id
// errorMessage 错误信息没有为null
// logBytes 日志大小
// compressedBytes 压缩后日志大小
Utils.log(
"LoghubHelper -> ${
String.format(
"%s %s %s %s %s",
LogProducerResult.fromInt(resultCode),
reqId,
errorMessage,
logBytes,
compressedBytes
)
}"
)
}
}
}
}
//package com.gh.gamecenter.common.loghub
//
//import com.aliyun.sls.android.producer.Log
//import com.aliyun.sls.android.producer.LogProducerClient
//import com.aliyun.sls.android.producer.LogProducerConfig
//import com.aliyun.sls.android.producer.LogProducerResult
//import com.gh.gamecenter.common.HaloApp
//import com.gh.gamecenter.common.utils.EnvHelper
//import com.gh.gamecenter.common.utils.PackageFlavorHelper
//import com.gh.gamecenter.core.AppExecutor
//import com.lightgame.utils.Utils
//
///**
// * 上传阿里云日志辅助类
// */
//object LoghubHelper {
//
// private const val ACCESS_KEY_ID = "LTAIV3i0sNc4TPK1"
// private const val ACCESS_KEY_SECRET = "8dKtTPeE5WYA6ZCeuIBcIVp7eB0ir4"
// private const val ENDPOINT = "cn-qingdao.log.aliyuncs.com"
//
// private val mClientMaps by lazy { hashMapOf<String, LogProducerClient>() }
//
// /**
// * 上传日志至阿里云 loghub
// * @param uploadImmediately 马上上传日志
// */
// fun uploadLog(log: Log, logStore: String, uploadImmediately: Boolean) {
// // https://github.com/aliyun/aliyun-log-android-sdk/issues/60
// // 在主线程直接调用会有概率触发 ANR ,在工作线程调用有机率会创建多个工作线程,麻了,还是继续在工作线程上干吧
// AppExecutor.logExecutor.execute {
// getClient(logStore)?.addLog(log, if (uploadImmediately) 1 else 0)
// }
// }
//
// private fun getClient(logStore: String): LogProducerClient? {
// // https://jira.shanqu.cc/browse/GHZS-3815?focusedCommentId=50027&page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel#comment-50027
// //
// val transformedLogStore = if (EnvHelper.isGATApp) "event" else logStore
// if (!mClientMaps.containsKey(transformedLogStore)) {
// mClientMaps[transformedLogStore] = createClient(transformedLogStore)
// }
// return mClientMaps[transformedLogStore]
// }
//
// private fun createClient(logStore: String): LogProducerClient {
//
// val config = LogProducerConfig(
// HaloApp.getInstance().applicationContext,
// ENDPOINT,
// EnvHelper.logProducerProject,
// logStore,
// ACCESS_KEY_ID,
// ACCESS_KEY_SECRET
// ).apply {
//// // 设置主题
//// setTopic("topic")
//// // 设置tag信息此tag会附加在每条日志上
//// addTag("key", "value")
// // 每个缓存的日志包的大小上限取值为1~5242880单位为字节。默认为1024 * 1024
// setPacketLogBytes(1024 * 1024)
// // 每个缓存的日志包中包含日志数量的最大值取值为1~4096默认为1024
// setPacketLogCount(1024)
// // 被缓存日志的发送超时时间如果缓存超时则会被立即发送单位为毫秒默认为3000
// setPacketTimeout(3000)
// // 单个Producer Client实例可以使用的内存的上限超出缓存时add_log接口会立即返回失败
// // 默认为64 * 1024 * 1024
// setMaxBufferLimit(64 * 1024 * 1024)
// // 发送线程数默认为1
// setSendThreadCount(1)
//
// //网络连接超时时间整数单位秒默认为10
// setConnectTimeoutSec(15)
// //日志发送超时时间整数单位秒默认为15
// setSendTimeoutSec(15)
// //flusher线程销毁最大等待时间整数单位秒默认为1
// setDestroyFlusherWaitSec(2)
// //sender线程池销毁最大等待时间整数单位秒默认为1
// setDestroySenderWaitSec(2)
// //数据上传时的压缩类型默认为LZ4压缩0 不压缩1 LZ4压缩默认为1
// setCompressType(1)
// //设备时间与标准时间之差,值为标准时间-设备时间,一般此种情况用于客户端设备时间不同步的场景
// //整数单位秒默认为0比如当前设备时间为1607064208, 标准时间为1607064308则值设置为 1607064308 - 1607064208 = 10
// setNtpTimeOffset(0)
// //日志时间与本机时间之差,超过该大小后会根据 `drop_delay_log` 选项进行处理。
// //一般此种情况只会在设置persistent的情况下出现即设备下线后超过几天/数月启动,发送退出前未发出的日志
// //整数单位秒默认为7*24*3600即7天
// setMaxLogDelayTime(7 * 24 * 3600)
// //对于超过 `max_log_delay_time` 日志的处理策略
// //0 不丢弃,把日志时间修改为当前时间; 1 丢弃,默认为 1 (丢弃)
// setDropDelayLog(0)
// //是否丢弃鉴权失败的日志0 不丢弃1丢弃
// //默认为 0即不丢弃
// setDropUnauthorizedLog(0)
// // 是否使用主线程回调
// // false: 使用主线程回调。回调会在主线程上执行,且每个 client 都有自己单独的回调。
// // true: 使用 sender 线程回调。回调会在 sender 线程上执行,每次执行回调时都会 attach 一个新的 java 线程,所有 client 共用一个回调。
// // 注意:默认使用 sender 线程回调。
// setCallbackFromSenderThread(true)
//
// /**
// * 以下为开启断点续传的配置, 按照如下配置开启断点续传功能后, 日志会先缓存到本地
// */
// // 1 开启断点续传功能, 0 关闭
// // 每次发送前会把日志保存到本地的binlog文件只有发送成功才会删除保证日志上传At Least Once
// setPersistent(1)
// // 持久化的文件名,需要保证文件所在的文件夹已创建。
// // !!!!!!!!!!!!!!!!!!!注意!!!!!!!!!!!!!!!!!!!
// // 配置多个客户端时,不应设置相同文件
// setPersistentFilePath(HaloApp.getInstance().filesDir.absolutePath + "/${logStore}.dat")
// // 是否每次AddLog强制刷新高可靠性场景建议打开
// setPersistentForceFlush(1)
// // 持久化文件滚动个数建议设置成10。
// setPersistentMaxFileCount(10)
// // 每个持久化文件的大小建议设置成1-10M
// setPersistentMaxFileSize(1024 * 1024)
// // 本地最多缓存的日志数不建议超过1M通常设置为65536即可
// setPersistentMaxLogCount(65536)
// }
//
// // 通过 LogProducerConfig 构造一个 LogProducerClient 实例
// // callback为可选配置, 如果不需要关注日志的发送成功或失败状态, 可以不注册 callback正式环境不注册callback
// return if (!PackageFlavorHelper.IS_TEST_FLAVOR) {
// LogProducerClient(config)
// } else {
// LogProducerClient(config) { resultCode, reqId, errorMessage, logBytes, compressedBytes ->
// // resultCode 返回结果代码
// // reqId 请求id
// // errorMessage 错误信息没有为null
// // logBytes 日志大小
// // compressedBytes 压缩后日志大小
// Utils.log(
// "LoghubHelper -> ${
// String.format(
// "%s %s %s %s %s",
// LogProducerResult.fromInt(resultCode),
// reqId,
// errorMessage,
// logBytes,
// compressedBytes
// )
// }"
// )
// }
// }
// }
//}

View File

@ -1,11 +1,11 @@
package com.gh.gamecenter.common.loghub
import androidx.annotation.Keep
import com.aliyun.sls.android.producer.Log
import com.gh.gamecenter.common.entity.ExposureEntity
import com.gh.gamecenter.common.exposure.meta.Meta
import com.gh.gamecenter.core.runOnUiThread
import com.google.gson.annotations.SerializedName
import com.volcengine.model.tls.LogItem
import org.json.JSONObject
object LoghubUtils {
@ -59,29 +59,29 @@ object LoghubUtils {
private fun uploadEvents(eventList: List<LoghubEvent>, forcedUpload: Boolean) {
for (event in eventList) {
val log = Log()
val log = LogItem(System.currentTimeMillis())
// 特殊处理以下logStore不需要用content包裹数据
if (event.logStore == "collection" || event.logStore == "common" || event.logStore == "halo-api-device-installed") {
val contentJson = JSONObject(event.content)
for (key in contentJson.keys()) {
log.putContent(key, contentJson.get(key).toString())
log.addContent(key, contentJson.get(key).toString())
}
} else {
// isFlat为true代表仍需要平铺数据
if (event.isFlat) {
val contentJson = JSONObject(event.content)
for (key in contentJson.keys()) {
log.putContent(key, contentJson.get(key).toString())
log.addContent(key, contentJson.get(key).toString())
}
// 新数据使用timestamp字段记录时间
log.putContent("timestamp", event.time)
log.addContent("timestamp", event.time)
} else {
log.putContent("current time", event.time)
log.putContent("content", event.content)
log.addContent("current time", event.time)
log.addContent("content", event.content)
}
}
LoghubHelper.uploadLog(log, event.logStore, forcedUpload)
TLogHubHelper.sendLog(log, event.logStore)
}
}

View File

@ -0,0 +1,58 @@
package com.gh.gamecenter.common.loghub
import com.lightgame.utils.Utils
import com.volcengine.model.tls.LogItem
import com.volcengine.model.tls.producer.CallBack
import com.volcengine.model.tls.producer.ProducerConfig
import com.volcengine.service.tls.Producer
import com.volcengine.service.tls.ProducerImpl
object TLogHubHelper {
private const val TAG = "TLogHubHelper"
// 阿里云 logstore to 火山云 topicId 的 mapping
private val topicIdMapping = hashMapOf(
"appointment" to "2429e0ac-ee8b-4d2b-bab6-351d610f12df",
"bbs_community" to "6b3b32b9-6f04-4279-8117-a5f938ef7ebd",
"collection" to "77d6bd2b-c551-43d9-b36d-ae3d46b0fdbb",
"common" to "7fd7ef5e-bfa6-47e5-adc3-0bcb1f8d57bc",
"community" to "d8c296c9-ceb2-470f-8f3c-4c6e4e2f64d1",
"download_debug" to "a48acc1b-c863-459a-8681-99e25a63d19b",
"empty" to "30cf8475-81c0-42b7-8b81-19c9a731a644",
"event" to "ab6d32f5-e76f-4ebe-b484-2b456ffddfd8",
"exposure" to "3612f0f5-a5d4-4db8-91aa-8ec950107e7a",
"genshin" to "13d82937-45f2-4ee1-bd39-c17a63c570d5",
"halo-api-device-installed" to "c2a34712-8d43-4352-a3aa-7d3717b27a22",
"launch_activity" to "607dd48c-fbf9-44dc-8ebb-496bdea55447",
"score" to "af580973-ffef-4cd2-b010-f2589bc29201",
"video_streaming" to "d5efdf24-fa48-4e31-aeb6-8d961c6de51f",
)
private val producer: Producer by lazy {
ProducerImpl(
ProducerConfig(
"https://tls-cn-guangzhou.volces.com",
"cn-guangzhou",
"AKLTYmUwZTI0ZWM4Y2UwNDE5ZGEwYzM0YTcyMDFlZjI4NzE",
"TmpGa05EWTNOVGhpWkRSaU5EazVabUV4WmpVMVlUUTBOemswWkRGbE5qZw==",
""
).apply {
maxThreadCount = 5
}
).also {
it.start()
}
}
fun sendLog(log: LogItem, logStore: String) {
// 如果不需要回调callback参数传null即可
val callBack = CallBack { result -> Utils.log(TAG, "sendLog result ${result.isSuccess}, " + "$logStore -> $log") }
val topicId = topicIdMapping[logStore] ?: return
Utils.log(TAG, "sendLog, $logStore -> $topicId -> $log")
producer.sendLogV2("", topicId, "android", "logFile", log, callBack)
}
}