|
glog 的优点
mmap 内存映射实现,支持同步/异步写入模式,采用自定义的二进制文件格式,上层可以自定义序列化方式,兼具灵活,高性能和容错能力。支持增量(按天)归档和全量(按文件)归档 、 日志流式压缩、加密,支持自动清理日志文件,SDK 包含基于 C++ 实现的日志读取功能。
具体背景及原理说明 可看该网址 https://juejin.cn/post/7168662263337861133
1. glog日志框架的依赖
在需要的module 下的build.gradle 下
android{ defaultConfig { ndk { abiFilters "armeabi-v7a"//保留一个架构的so } }}dependencies { implementation "cn.huolala.glog.android:glog-android-static:1.0.0"//货拉拉的日志库}2. Protobuf数据框架的依赖
在项目的根目录下的build.gradle 下
dependencies { classpath 'com.google.protobuf:protobuf-gradle-plugin:0.8.15' // }2.1 Protobuf数据框架的依赖
在需要的module 下的build.gradle 下
apply plugin: 'com.google.protobuf'android{ compileOptions { sourceCompatibility JavaVersion.VERSION_1_8 targetCompatibility JavaVersion.VERSION_1_8 } sourceSets { main { // 定义proto文件目录 默认就是proto 可以不写改步骤 或者重新定义目录 proto { srcDir 'src/main/proto' include '*.proto' } java { srcDir 'src/main/java' } } } }dependencies { implementation 'com.google.protobuf:protobuf-lite:3.0.0'}protobuf { protoc { artifact = 'com.google.protobuf:protoc:3.7.0' } plugins { javalite { artifact = 'com.google.protobuf:protoc-gen-javalite:3.0.0' } }// 默认路径为 build/generated/source/proto// 该目录下会按 buildType 生成 debug /release 目录 generatedFilesBaseDir = "$projectDir/src/main/xxx" //预编译生成的文件 generateProtoTasks { all().each { task -> task.builtins { remove java } task.plugins { javalite {} } } }}3. 简单的封装使用
3.1 glog 的封装及说明
日志内容需要我们自己定义 来把要收集的堆栈信息及线程和异常进行整理成msg 写入import android.content.Context;import android.os.Process;import android.text.TextUtils;import android.util.Log;import java.io.File;import java.io.FileOutputStream;import java.io.IOException;import java.io.InputStream;import java.util.Arrays;import java.util.Date;import java.util.concurrent.atomic.AtomicLong;import glog.android.BuildConfig;import glog.android.Glog;/** * @author: tjf * @date: 2022-12-31 * @desc: Glog 日志框架简单的封装 先initialize 初始化 后再配置setGlogConfig日志信息 * */public class GlogManager { private Glog glog; Context context; private GlogManager() { } public static GlogManager getInstance() { return SingletonHelper.INSTANCE; } private static class SingletonHelper { private static final GlogManager INSTANCE = new GlogManager(); } public void initialize() { Glog.initialize(BuildConfig.DEBUG ? Glog.InternalLogLevel.InternalLogLevelDebug : Glog.InternalLogLevel.InternalLogLevelInfo); } // 初始化实例 配置glog public void setGlogConfig(Context context) { this.context = context; int EXPIRES_SECS = 14 * 24 * 60 * 60; // 14 day //日志的定期删除时间 默认是 7天 final int TOTAL_ARCHIVE_SIZE_LIMIT = 56 * 1024 * 1024; // 56 MB 每个日志文件的大小 限制 默认是16MB大小 glog = new Glog.Builder(context) .protoName("glog_potbuf") // 实例标识,相同标识的实例只创建一次 文件名的前缀 .rootDirectory(context.getFilesDir().getAbsolutePath() + "/glog")//日志存储的文件目录 // 默认在data/user/0/包名/files/glog/文件 .async(true)//是否异步写入 .expireSeconds(EXPIRES_SECS)//日志的定期删除时间 .compressMode(Glog.CompressMode.Zlib)//压缩格式 默认开启压缩 .totalArchiveSizeLimit(TOTAL_ARCHIVE_SIZE_LIMIT)//每个日志文件的大小限制 .encryptMode(Glog.EncryptMode.AES) // 加密方式 默认无 .key("") // ECDH Server public key 加密的公钥 .incrementalArchive(true) //支持增量(按天)归档 和 全量(按文件)归档; // 默认 false 重命名缓存文件的方式归档 true 增量归档,当天日志写入同一文件 .build(); } //写入日志 public void glogWirter(String msg) { glog.write(msg.getBytes()); } //写入proto 格式的日志 tag的 public void glogWirtes_Potobuf(String tag, String msg) { AtomicLong seq = new AtomicLong(); byte[] msgBytes = LogProtos.Log.newBuilder() .setLogLevel(LogProtos.Log.Level.INFO) .setSequence(seq.getAndIncrement()) .setTimestamp(String.valueOf(System.currentTimeMillis())) .setPid(Process.myPid()) .setTid(String.valueOf(Thread.currentThread().getId())) .setTag(tag) .setMsg(msg) .build() .toByteArray(); glog.write(msgBytes); } //写入proto 格式的日志日志 级别 和tag 和内容 public void glogWirtes_Potobuf(LogProtos.Log.Level logLevel, String tag, String msg) { AtomicLong seq = new AtomicLong(); byte[] msgBytes = LogProtos.Log.newBuilder() .setLogLevel(logLevel) .setSequence(seq.getAndIncrement()) .setTimestamp(String.valueOf(System.currentTimeMillis())) .setPid(Process.myPid()) .setTid(String.valueOf(Thread.currentThread().getId())) .setTag(tag) .setMsg(msg) .build() .toByteArray(); glog.write(msgBytes); } //释放清空 public void destroy() { if (glog != null) { glog.flush(); glog.destroy(); } } private final String TAG = "GlogDemo"; // 读取当天的日志 type 1 普通格式 2 proto 格式 public void readNewDateLog(int type) { glog.flush(); String[] logArchiveFiles = glog.getArchivesOfDate(new Date().getTime() / 1000); byte[] inBuf = new byte[Glog.getSingleLogMaxLength()]; Log.i(TAG, "开始读取当天日志, 文件列表:" + Arrays.toString(logArchiveFiles)); for (int i = 0; i < logArchiveFiles.length; i++) { String filename = logArchiveFiles; try { Glog.Reader readers = glog.openReader(filename); String str; StringBuilder stb = new StringBuilder(); while (true) { int count = readers.read(inBuf); if (count < 0) { // 读取结束 break; } else if (count == 0) { // 破损恢复 continue; } byte[] outBuf = new byte[count]; System.arraycopy(inBuf, 0, outBuf, 0, count); if (type == 1) { str = new String(outBuf, "UTF8"); Log.i(TAG, str); } else if (type == 2) { Log.i(TAG, LogProtos(LogProtos.Log.parseFrom(outBuf))); } } } catch (IOException e) { e.printStackTrace(); } } Log.i(TAG, "读取完成"); } // 读取放在assets下的glog 文件 public void readAssetsFile(String fileName) { glog.flush(); String filePath = copyAssetAndWrite(context, fileName); if (TextUtils.isEmpty(filePath)) { Log.i(TAG, "没有该文件 无法解析"); return; } byte[] inBuf = new byte[(int) new File(filePath).length()]; Log.i(TAG, "开始读取, 文件名:" + filePath); try { Glog.Reader readers = glog.openReader(filePath); while (true) { int count = readers.read(inBuf); if (count < 0) { // 读取结束 break; } else if (count == 0) { // 破损恢复 continue; } byte[] outBuf = new byte[count]; System.arraycopy(inBuf, 0, outBuf, 0, count); Log.i(TAG, LogProtos(LogProtos.Log.parseFrom(outBuf))); } } catch (IOException e) { e.printStackTrace(); } Log.i(TAG, "读取完成"); } //Proto 格式的输出 private String LogProtos(LogProtos.Log logs) { return "Log{" + "sequence=" + logs.getSequence() + ", timestamp='" + logs.getTimestamp() + '\'' + ", logLevel=" + logs.getLogLevelValue() + ", pid=" + logs.getPid() + ", tid='" + logs.getTid() + '\'' + ", tag='" + logs.getTag() + '\'' + ", msg='" + logs.getMsg() + '\'' + '}'; } /** * 读取assets 下的文件 到app的缓存目录下 * * @param context * @param fileName * @return */ public String copyAssetAndWrite(Context context, String fileName) { try { File cacheDir = context.getCacheDir(); if (!cacheDir.exists()) { cacheDir.mkdirs(); } File outFile = new File(cacheDir, fileName); if (!outFile.exists()) { boolean res = outFile.createNewFile(); if (!res) { return null; } } else { if (outFile.length() > 10) {//表示已经写入一次 return outFile.getPath(); } } InputStream is = context.getResources().getAssets().open(fileName); FileOutputStream fos = new FileOutputStream(outFile); byte[] buffer = new byte[1024]; int byteCount; while ((byteCount = is.read(buffer)) != -1) { fos.write(buffer, 0, byteCount); } fos.flush(); is.close(); fos.close(); return outFile.getPath(); } catch (IOException e) { e.printStackTrace(); } return null; }}3.2 protos 的格式文件
在main 的同级 下新建一个proto文件夹 新建一个日志类型的proto 的数据结构 Log.proto
建好后 通过AS 的build 下的 rebuild project 来创建 编译的LogProtos 文件生成
syntax = "proto3";//proto3 版本package glog;//包option java_package = "com.tjf.glogpotobufdemo";//包名称option java_outer_classname = "LogProtos";//别名message Log { enum Level { INFO = 0; DEBUG = 1; VERBOSE = 2; WARN = 3; ERROR = 4; } int64 sequence = 1; string timestamp = 2; Level logLevel = 3; int32 pid = 4; string tid = 5; string tag = 6; string msg = 7;}4. 完结
元旦快乐
|
本帖子中包含更多资源
您需要 登录 才可以下载或查看,没有账号?立即注册
×
|