Bugly Android 日志
Bugly Android 日志由两部分组成:
- Diagnose:提供日志捞取、日志染色、条件采集等端上日志服务。
- Logger:提供本地打日志能力,底层基于微信xlog方案。此模块可选接入,业务前期可使用自有打日志模块,通过实现
LoggerAdapter
接口与Diagnose配合使用。
支持能力:
- 日志捞取(支持在线日志配置push、日志批量捞取)
- 日志染色(支持批量染色)
- 主动上报(支持日志上报、大文件上报)
一、快速开始
1. 新建应用
在Bugly平台新建应用,获取Bugly AppId 和 AppKey 。
2. 添加依赖
1. 在project级别的build.gradle添加maven地址。
repositories {
maven { url 'https://repo1.maven.org/maven2/' }
}
2. 在Module的build.gradle文件中添加依赖和属性配置。
dependencies {
// Logger(可选)
// 如果需要STL静态链接版本,则把"logger"替换为"logger-static"
implementation 'com.tencent.tdos-diagnose:logger:0.4.11'
// Diagnose
implementation 'com.tencent.tdos-diagnose:diagnose:0.4.11'
}
Logger模块的STL动态链接版本使用 libc++_shared
,如果项目中已经有这个库,则动态链接方式编译出的安装包较小;如果项目中没有 libc++_shared
或者集成了其它冲突的STL实现,则可以选择静态链接版本。
3. 初始化
// Logger(可选)
TDLogConfig logConfig = new TDLogConfig.Builder(context)
.setLogPath(logPath) // 日志输出目录,需提前获取写权限
.setLogLevel(LogLevel.DEBUG) // 默认VERBOSE
.setConsoleLog(true) // 默认true
.setMaxFileSize(byte) // 默认50M
.setMaxAliveFileSize(byte) // 默认不限制,最小值200M
.setMaxAliveDay(day) // 默认7天, 最小值1天
.setPubKey(pubKey) // 日志加密公钥(可选,联系Bugly小助手提供公私钥给bugly后台进行配置)
.build();
TDLog.initialize(context, logConfig);
// Diagnose
TDDiagConfig diagConfig = new TDDiagConfig.Builder()
.setAppId(appId) // 【重要】在Bugly申请的AppId
.setAppKey(appKey) // 【重要】在Bugly平台申请的AppKey
.setEnvironment(TDDiagConfig.ENV_PRO) // 【重要】设置环境(默认OA环境:TDDiagConfig.ENV_NORMAL、专业版需设置为:TDDiagConfig.ENV_PRO,海外版需设置为:TDDiagConfig.ENV_OVERSEAS)
.setLoggerAdapter(TDLog.getLogImpl()) // 业务可自行实现LoggerAdapter来适配自有日志工具
.setDeviceInfoAdapter(deviceInfoAdapter) // 按照隐私合规的要求,注入隐私信息(可选,默认从android.os.Build读取)
.setAppVersion("1.0.0") // 自定义app版本(可选,默认从PackageInfo读取)
.setTrafficQuota(total, metered) // 设置每日流量配额(可选,默认不限制,建议设置)
.setUploadCountLimit(limit, period, timeUnit) // 设置自动上报频率限制(可选,默认不限制,建议设置)
.setImportantLabels(...label) // 设置标签白名单(设置不受TrafficQuota和UploadCountLimit限制的标签)
.build();
TDDiag.initialize(context, config);
专业版 或 海外版 必须调用 setEnvironment
设置环境,否则日志会上报失败。
初始化完成后即可日志打印。
TDLog.i("tag", "xxxxxx");
TDLog.e("tag", "xxxxxx", e);
TDDiag
和 TDLog
的更多用法详见 API文档 部分。
4. 设置设备唯一ID
在Bugly Android 日志SDK中,userId是用于捞日志和染色的标识(由于历史原因命名为userId),建议使用设备唯一ID,可在初始化后同步调用传入,也可在App获取到唯一标识后异步调用传入。
TDDiag.setUserId("uid");
5. Push支持(可选)
日志SDK会默认在初始化时拉取配置检查有无日志捞取命令。接入Push后可更及时收到日志捞取命令,减少等待时间。
业务需要把自有的Push通道与Bugly平台对接(Bugly平台不提供Push能力),当收到捞日志/染色Push后,调用以下接口,完成日志捞取/染色。
TDDiag.onPush("{}");
6. 主动检查指令下发(可选)
一般情况下,通过启动时设置userId和接入Push,即可获得很好的捞取指令配置下发实时性。
如果App希望增加更多检查指令配置下发的时机(例如App进入前台时检查),可在对应的时机调用:
TDDiag.syncConfig(false); // false表示不强制刷新
SDK对主动检查指令配置下发有频率限制,限制策略由后台根据负载情况动态决定。
不建议在任何没有用户交互的自动触发场景进行强制刷新,避免给后台带来不可控的负载压力。
7. 验证Release构建(混淆)
完成以上步骤后,请验证一下混淆后的安装包是否工作正常(打印日志、下发指令、主动上报等)。
一般不需要进行额外配置,Bugly日志AAR产物中已经包含了混淆规则proguard.txt
。但如果你发现混淆后的安装包工作不正常,有可能是你的构建工具不支持,可尝试手动配置排除问题。
# Logger
-keep class com.tencent.mars.xlog.** { *; }
# Diagnose
-keep class com.tencent.tddiag.protocol.* { public *; }
-keep class com.tencent.tddiag.upload.UploadTask { public *; }
二、日志线上捞取
Bugly 平台提供了日志捞取指令的下发功能,用户可以在 Bugly 管理端的 日志查询 页面进行指令的下发和上报结果查询。
通过 新建查询 入口,新建一个日志捞取命令,捞取命令需选择捞取的 日志时间范围 和 设备唯一ID,支持为捞取增加标签和附件文件路径。
待命令下发后,可等待SDK拉取配置,并上传对应日志文件。
三、主动上报日志
SDK 支持业务主动调用上报接口,上传自定义日志、文件等信息。
该功能可用于用户反馈、异常发生等场景及时上报本地日志信息,帮助保留问题现场。
示例
- 示例1:上传最近半小时的日志
val now = System.currentTimeMillis() / 1000
TDDiag.upload(new UploadParam("recentLog")
.setTimestamp(now - 1800, now)
.setListener(uploadListener, true));
- 示例2:上传自定义文件
TDDiag.upload(new UploadParam("customFile")
.setExtraPaths(listOf(path))
.setExtraInfo(msg, null));
- 示例3:(Crash时)创建上传最近5分钟日志的任务,下次启动时上传
long now = System.currentTimeMillis() / 1000;
TDDiag.upload(new UploadParam("crash")
.setTimestamp(now - 300, now)
.setSaveSync(true));
上传后可在 Bugly 管理端的 日志查询->主动上报 页查看上报的日志信息。
更多用法详见 API文档 部分。
限制规则
为了避免过度使用而浪费用户的设备资源和流量,主动上报有以下限制规则:
- 文件大小限制:限制压缩前的文件大小,超过上限则只保留修改时间较新的部分文件。默认值WiFi 500M、4G 200M,可通过(kotlin)
TDDiag.uploadLogs(..., sizeLimit = byte)
或(Java)UploadHelper.setSizeLimit(sizeLimit)
修改限制。 - 次数限制:通过初始化参数
TDDiagConfig.Builder#setUploadCountLimit
设置。 - 流量限制:通过初始化参数
TDDiagConfig.Builder.setTrafficQuota(total, metered)
设置。 - 重试限制:启动时最多重试5个任务,超过时将按优先级从低到高丢弃部分任务。优先级 捞取>白名单标签>其它主动上报 。
- 熔断机制:连续上传失败10次后,将停用主动上报6h。
白名单
对于一些关键上报,可以通过初始化参数TDDiagConfig.Builder.setImportantLabels(...labels)
设置标签白名单以绕过主动上报的限制规则。
仅允许把用户行为触发的标签加到白名单,例如用户反馈场景,以避免过度使用。建议把设置标签白名单的代码设为强制CR。
四、合理指定上报内容
Diagnose 模块在打包上传时,会检查压缩前的文件大小,如果超过上限,则只打包修改时间较新的部分文件。指令捞取的上限为500M,主动上报的上限为WiFi 500M、4G 200M。
为了避免文件过大,打包内容不完整,应当:
- 合理指定时间区间,Logger的日志文件是按小时存储的,假如现在是
18:35
,指定时间区间[now-3600s, now)
将得到17点~18点的日志文件;指定时间区间[now-1800s, now)
将得到18点的日志文件。 - 合理指定附加文件,避免传入多层级的目录。
五、API文档
1. Diagnose
TDDiag
object TDDiag {
/**
* 初始化
*
* @param context
* @param config
*/
fun initialize(context: Context, config: TDDiagConfig)
/**
* 初始化
*
* @param context
* @param config
* @param host 在多个进程初始化时,需要保证只有一个进程设置为true,默认主进程,用于拉取配置和上传日志
*/
fun initialize(context: Context, config: TDDiagConfig, host: Boolean)
/**
* 设置userId
*/
fun setUserId(id: String)
/**
* 接收Push
*/
fun onPush(data: String)
/**
* 主动上报日志
*
* @param label 标签,必填,不超过64字节
* @param fileList 文件路径列表
* @param summary 摘要,不超过256字节,支持搜索
* @param extraInfo 附加信息
* @param listener UI回调
* @param saveSync 仅同步保存任务,下次启动时上传
* @see upload
*/
@Deprecated("Use TDDiag.upload(param) instead")
@JvmStatic
fun uploadFiles(
label: String,
fileList: List<File>,
summary: String? = null,
extraInfo: String? = null,
listener: UploadListener? = null,
saveSync: Boolean = false
)
/**
* 主动上报日志
*
* @param label 标签,必填,不超过64字节
* @param pathList 文件路径列表
* @param summary 摘要,不超过256字节,支持搜索
* @param extraInfo 附加信息
* @param listener UI回调
* @param saveSync 仅同步保存任务,下次启动时上传
* @see upload
*/
@Deprecated("Use TDDiag.upload(param) instead")
@JvmStatic
fun uploadPaths(
label: String,
pathList: List<String>,
summary: String? = null,
extraInfo: String? = null,
listener: UploadListener? = null,
saveSync: Boolean = false
)
/**
* 主动上报日志
*
* @param label 标签,必填,不超过64字节
* @param startTimestamp 开始时间戳(单位:秒),包含此值
* @param endTimestamp 结束时间戳(单位:秒),不含此值
* @param extraPathList 文件路径列表
* @param summary 摘要,不超过256字节,支持搜索
* @param extraInfo 附加信息
* @param listener UI回调
* @param saveSync 仅同步保存任务,下次启动时上传
* @param sizeLimit (压缩前)文件大小限制,当值小于[限制规则](README.md)时有效,默认不限制
* @see upload
*/
@Deprecated("Use TDDiag.upload(param) instead")
@JvmStatic
fun uploadLogs(
label: String,
startTimestamp: Long,
endTimestamp: Long,
extraPathList: List<String>? = null,
summary: String? = null,
extraInfo: String? = null,
listener: UploadListener? = null,
saveSync: Boolean = false,
sizeLimit: Long = Long.MAX_VALUE
)
/**
* 主动上报日志
*/
@JvmStatic
fun upload(param: UploadParam)
/**
* 主动刷新配置
*
* @param force 强制刷新,不检查拉取间隔
*/
fun syncConfig(force: Boolean = false): Boolean
}
TDDiagConfig.Builder
class TDDiagConfig.Builder {
/**
* 设置从[TDS-调试诊断平台](https://diagnose.woa.com/)申请的appId
*/
fun setAppId(appId: String): Builder
/**
* 设置从[TDS-调试诊断平台](https://diagnose.woa.com/)申请的appKey
*/
fun setAppKey(appKey: String): Builder
/**
* 设置LoggerAdapter,适配打日志模块
*/
fun setLoggerAdapter(loggerAdapter: LoggerAdapter): Builder
/**
* 注入合规的隐私信息(可选,默认从android.os.Build读取)
*/
fun setDeviceInfoAdapter(deviceInfoAdapter: DeviceInfoAdapter): Builder
/**
* 自定义app版本(可选,默认从PackageInfo读取)
*/
fun setAppVersion(appVersion: String): Builder
/**
* 设置标签白名单
*/
fun setImportantLabels(vararg labels: String): Builder
/**
* 设置每日流量配额(可选,默认不限制)
*
* @param total 总流量(单位:字节),要求 >= [metered]
* @param metered 4G流量(单位:字节),要求 > 0
*/
fun setTrafficQuota(total: Long, metered: Long): Builder
/**
* 设置自动上报频率限制(可选,默认不限制)
*
* @param limit 次数
* @param period 周期
* @param timeUnit 周期单位
*/
fun setUploadCountLimit(limit: Long, period: Long, timeUnit: TimeUnit): Builder
/**
* 设置环境(可选)
*
* @param env 默认[TDDiagConfig.ENV_NORMAL],海外版[TDDiagConfig.ENV_OVERSEAS]
*/
fun setEnvironment(env: String): Builder
/**
* 生成[TDDiagConfig]
*/
fun build(): TDDiagConfig
}
UploadParam
/**
* 主动上传参数类
*/
class UploadParam {
/**
* @param label 标签,必填,不超过64字节
*/
constructor(label: String)
/**
* 设置日志范围
*
* @param startTimestamp 开始时间戳(单位:秒),包含此值
* @param endTimestamp 结束时间戳(单位:秒),不含此值
*/
fun setTimestamp(startTimestamp: Long, endTimestamp: Long): UploadParam
/**
* 设置附带文件(或文件夹)列表
*/
fun setExtraPaths(pathList: List<String>): UploadParam
/**
* 设置附带文件(或文件夹)列表
*/
fun setExtraFiles(fileList: List<File>): UploadParam
/**
* 设置附加信息
*
* @param summary 摘要,不超过256字节,支持搜索
* @param extraInfo 附加信息
*/
fun setExtraInfo(summary: String?, extraInfo: String?): UploadParam
/**
* 设置UI回调
*
* @param listener UI回调
* @param disableAsyncRetry 是否禁用重试,默认false,可设为true后在上层自定义重试方式
*/
fun setListener(listener: UploadListener?, disableAsyncRetry: Boolean): UploadParam
/**
* 设置仅同步保存任务,下次启动时上传
*/
fun setSaveSync(saveSync: Boolean = true): UploadParam
/**
* 设置(压缩前)文件大小限制,当值小于[限制规则](README.md)时有效
*/
fun setSizeLimit(sizeLimit: Long): UploadParam
/**
* 设置是否收集日志cache文件,默认不收集
*/
fun setIncludeCache(includeCache: Boolean = true): UploadParam
/**
* 附加检索信息,用于平台互通(跨系统检索)
*
* @param queryKey 专门用于检索日志的key,不超过128字节
* @param source 调用方来源,不超过128字节
*/
fun appendExtQueryInfo(queryKey: String, source: String): UploadParam
}
UploadHelper
/**
* 主动上传日志辅助类
*/
@Deprecated("Use TDDiag.upload(UploadParam) instead")
class UploadHelper {
/**
* @param label 标签,必填,不超过64字节
*/
constructor(label: String)
/**
* 设置日志范围
*
* @param startTimestamp 开始时间戳(单位:秒),包含此值
* @param endTimestamp 结束时间戳(单位:秒),不含此值
*/
fun setTimestamp(startTimestamp: Long, endTimestamp: Long): UploadHelper
/**
* 设置附带文件(或文件夹)列表
*/
fun setExtraPaths(pathList: List<String>): UploadHelper
/**
* 设置附带文件(或文件夹)列表
*/
fun setExtraFiles(fileList: List<File>): UploadHelper
/**
* 设置附加信息
*
* @param summary 摘要,不超过256字节,支持搜索
* @param extraInfo 附加信息
*/
fun setExtraInfo(summary: String?, extraInfo: String?): UploadHelper
/**
* 设置UI回调
*/
fun setListener(listener: UploadListener): UploadHelper
/**
* 设置仅同步保存任务,下次启动时上传
*/
fun setSaveSync(): UploadHelper
/**
* 设置(压缩前)文件大小限制,当值小于[限制规则]时有效
*/
fun setSizeLimit(sizeLimit: Long): UploadHelper
/**
* 执行[TDDiag.uploadLogs]
*/
fun run()
}
LoggerAdapter
/**
* 日志组件适配接口
*/
public interface LoggerAdapter {
/**
* 设置染色级别
*/
void setColorLevel(@LogLevel int level);
/**
* 获取日志文件
*
* @param startTimestamp 开始时间戳(单位:秒),包含此值
* @param endTimestamp 结束时间戳(单位:秒),不含此值
* @return 日志文件列表
*/
@Nullable
List<File> getLogFiles(long startTimestamp, long endTimestamp);
/**
* 获取日志文件
*
* @param startTimestamp 开始时间戳(单位:秒),包含此值
* @param endTimestamp 结束时间戳(单位:秒),不含此值
* @param includeCache 是否收集cache文件
* @return 日志文件列表
*/
@Nullable
default List<File> getLogFiles(long startTimestamp, long endTimestamp, boolean includeCache) {
return getLogFiles(startTimestamp, endTimestamp);
}
/**
* 强制将Log写入文件
*/
void flushLog();
/**
* 打印sdk自身的日志
*/
void printDiagnoseLog(@NotNull String tag, @NotNull String msg, @Nullable Throwable tr);
/**
* 获取日志加密公钥(可选,默认null表示未开启加密)
*/
@Nullable
default String getPubKey() {
return null;
}
}
DeviceInfoAdapter
/**
* 隐私信息适配接口
*/
interface DeviceInfoAdapter {
/**
* {@link android.os.Build#BRAND}
*/
fun getBrand(): String
/**
* {@link android.os.Build#MODEL}
*/
fun getModel(): String
}
UploadListener
interface UploadListener {
/**
* 开始上传
*/
fun onStart()
/**
* 进度更新,可能回调0次或多次
*
* @param percent 0~100
*/
fun onProgress(percent: Int)
/**
* 上传成功
*/
fun onSuccess()
/**
* 上传失败
*
* @param reason 失败原因:
* * -4:本地限制策略,包括:次数限制、流量限制、熔断
* * -3:向后台同步上传url失败,一般是网络波动
* * -2:管理端配置的标签抽样限制
* * -1:重复任务
* * 1:文件上传网络错误
* * 2:压缩后的日志文件大小超过阈值
* * 3:压缩IO错误,一般是存储不足
* * 4:文件上传时cos后台返回http错误
* * 5:无本地日志
*/
fun onFailure(@UploadLogFailReasonType reason: Int)
}
2. Logger
TDLog
public class TDLog {
/**
* 初始化
*/
public static void initialize(Context context, TDLogConfig config)
/**
* 创建子实例
*/
public static ILogInstance getSubInstance(String category, boolean consoleLog, int maxAliveDay, long maxAliveFileSize)
/**
* 获取默认{@link LoggerAdapter}实例
*/
public static ITDLog getLogImpl()
/**
* 打印{@link LogLevel#VERBOSE}日志
*/
public static void v(String tag, String message)
/**
* 打印{@link LogLevel#VERBOSE}日志
*/
public static void v(String tag, String message, Throwable throwable)
/**
* 打印{@link LogLevel#DEBUG}日志
*/
public static void d(String tag, String message)
/**
* 打印{@link LogLevel#DEBUG}日志
*/
public static void d(String tag, String message, Throwable throwable)
/**
* 打印{@link LogLevel#INFO}日志
*/
public static void i(String tag, String message)
/**
* 打印{@link LogLevel#INFO}日志
*/
public static void i(String tag, String message, Throwable throwable)
/**
* 打印{@link LogLevel#WARN}日志
*/
public static void w(String tag, String message)
/**
* 打印{@link LogLevel#WARN}日志
*/
public static void w(String tag, String message, Throwable throwable)
/**
* 打印{@link LogLevel#ERROR}日志
*/
public static void e(String tag, String message)
/**
* 打印{@link LogLevel#ERROR}日志
*/
public static void e(String tag, String message, Throwable throwable)
/**
* 打印{@link TDLogInfo}日志
*/
public static void log(TDLogInfo info)
/**
* 强制把缓冲区日志写入文件
*/
public static void flushLog()
/**
* 关闭日志实例
*/
public static void closeLog()
}
TDLogConfig.Build
/**
* 日志组件的配置构造器
*/
public static class TDLogConfig.Build {
/**
* constructor
*/
public Builder(Context context)
/**
* 设置最低日志级别
*/
public Builder setLogLevel(@LogLevel int logLevel)
/**
* 设置是否输出到Logcat
*/
public Builder setConsoleLog(boolean consoleLog)
/**
* 设置日志文件存储路径
*/
public Builder setLogPath(String logPath)
/**
* 设置日志文件加密的公钥
*/
public Builder setPubKey(String key)
/**
* 设置单个日志文件大小的最大值
*/
public Builder setMaxFileSize(long maxFileSize)
/**
* 设置日志文件保存天数
*/
public Builder setMaxAliveDay(int maxAliveDay)
/**
* 设置日志文件总大小的最大值
*/
public Builder setMaxAliveFileSize(long maxAliveFileSize)
/**
* 生成{@link TDLogConfig}实例
*/
public TDLogConfig build()
}
TDLogInfo
public class TDLogInfo {
/**
* Tag
*/
public String tag;
/**
* 二级Tag
*/
public String subTag;
/**
* 三级Tag
*/
public String thirdTag;
/**
* 日志级别
*/
@LogLevel
public int level;
/**
* 线程id
*/
public long tid;
/**
* 线程名
*/
public String tName;
/**
* 日志内容
*/
public String message;
/**
* Throwable对象
*/
public Throwable throwable;
/**
* 时间,单位毫秒,默认0表示不指定
*/
public long timeMillis;
}
ILogInstance
/**
* 日志实例接口
*/
public interface ILogInstance {
/**
* 打印日志
*/
void log(String tag, @LogLevel int level, String message, @Nullable Throwable throwable);
/**
* 打印带自定义信息的日志
*/
void log(TDLogInfo info);
/**
* 强制将Log写入文件
*/
void flushLog();
/**
* 关闭当前Log实例
*/
void closeLog();
}