记录货拉拉的glog日志框架和Protobuf数据框架的配套使用

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[i];
            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. 完结

元旦快乐
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容