我用Flutter Deskstop做了一个Mars Xlog日志解析工具

前言

我的上篇文章 2022腾讯Mars Xlog日志系统集成指南-iOS篇 详细的介绍iOS下接入Xlog日志系统的指南。 Xlog日志接入了之后,我们要对它进行解析,如果不解析那就是一堆加密的二进制数据。常规的解析方案就是使用Mars官方的python文件 修改一下private key就可以直接解析了,但是由于是使用的python2且需要pip安装一堆库,许多人用起来苦不堪言,所以我基于Flutter Deskstop 做的Mars Xlog日志解析工具XlogDecoder就此诞生了。此工具采用Flutter编写,目前仅支持Mac,内置解码库无需python环境。

代码过程

一、编译Python文件成可执行文件

Mars解析Xlog日志,主要由官方源码里面的这三个Python文件构成,一个是加密的解析脚本、一个是不加密的解析脚本、一个是提供生成RSA Key的脚本。如果我们要做一个桌面GUI工具,不依赖本机环境的话,我们只能将这三个py文件编译成可执行文件供应用直接运行。

编译python文件成可执行文件,我们使用大名鼎鼎的pyinstaller制作。

  • 安装方式
pip3 install pyinstaller

由于Mars的脚本都是python2,我们需要这样,且版本较低,我找了很久才最终确认能支持编译的版本。我们需要这样安装 (注意Mac 12.3 之后会移除自带python2,如果需要要自己安装python2)


python -m pip install PyInstaller==3.3.1

  • 开始编译可执行文件

decode_mars_crypt_log_file.py

由于默认是写死的private_key, 我们需要对脚本做一定修改,让脚本支持输入参数做private key

//decode_mars_crypt_log_file.py 修改

PRIV_KEY=sys.argv[1]
# PRIV_KEY = "145aa7717bf9745b91e9569b80bbf1eedaa6cc6cd0e26317d810e35710f44cf8"

//main 函数改成接收参数2
main(sys.argv[2:])

然后执行编译命令:

python -m PyInstaller ./decode_mars_crypt_log_file.py

编译出来产物在新增的disk目录下,会有一个与你py文件同名的文件夹,余下两个py文件我们用同样的方式,编译即可。

二、导入编译产物到flutter项目

我们将编译出来的三个可执行文件导入到flutter image目录,

pubspec.yaml 指定目录

三、应用内获取assets目录

Flutter编译打包后,所有资源都是放到一个flutter_assets目录里面,我强行找到它即可。


static final String _assetsPath = Platform.isWindows
      ? '../data/flutter_assets/images'
      : '../../Frameworks/App.framework/Resources/flutter_assets/images';
  static File mainFile = File(Platform.resolvedExecutable);
  static Directory _assetsDir =
      Directory(path.normalize(path.join(mainFile.path, _assetsPath)));

四、通过相关UI事件以及Flutter 自带 Process类运行执行文件

  • 如生成一个新的RSA


//运行方法

Future<String> genKey() async {
    var pyPath = path.joinAll([
      _assetsDir.path,
      "gen_key",
      "gen_key"
    ]);

    var process = await Process.run(pyPath, []);
    print("result:\n");
    print("${process.stdout}");
    return process.stdout;
  }
  
//按钮事件

PushButton(
                                buttonSize: ButtonSize.large,
                                child: Text('生成 RSA Key'),
                                onPressed: () async {
                                  var result = await controller.genKey();
                                  showMacosAlertDialog(
                                    barrierDismissible: true,
                                    useRootNavigator: false,
                                    context: context,
                                    builder: (context) => MacosAlertDialog(
                                      appIcon: Image.asset(
                                        "images/app_icon.png",
                                        width: 64,
                                        height: 64,
                                      ),
                                      title: Text(
                                        '请记住你生成的RSA Key',
                                        style: TextStyle(
                                            fontWeight: FontWeight.w500,
                                            fontSize: 16),
                                      ),
                                      message: Text(
                                        result,
                                      ),
                                      horizontalActions: false,
                                      primaryButton: PushButton(
                                        buttonSize: ButtonSize.large,
                                        child: const Text('复制'),
                                        onPressed: () {
                                          ClipboardData data =
                                              ClipboardData(text: result);
                                          Clipboard.setData(data);
                                          showToast("已复制到粘贴板",
                                              textPadding: EdgeInsets.all(15));
                                          Navigator.of(context).pop();
                                        },
                                      ),
                                      secondaryButton: PushButton(
                                        buttonSize: ButtonSize.large,
                                        child: const Text('取消'),
                                        onPressed: Navigator.of(context).pop,
                                      ),
                                    ),
                                  );
                                },
                              ),
 

  • 解析日志

解析日志我们可以选择直接拖动xlog日志文件过来,也可以选择点击按钮选择。如果是文件夹拖过来我也会遍历目录符合xlog后缀的我全都是添加进去队列解析。

核心解析日志方法:


void beginCompressTask({required XlogInfoItemViewModel vm}) async {
    if (savePath.value.length == 0) {
      print("save path no define");
      vm.updateStatus(XlogInfoStatus.fail);
      taskList.refresh();
      return;
    }

    if (this.isEnableCrypt.value == true && (this.cryptMd5.value.isEmpty || this.cryptMd5.value.length != 64)) {
      print("private key is empty");
      showToast("Private Key 为空或长度不对(64位)", textPadding: EdgeInsets.all(15));
      vm.updateStatus(XlogInfoStatus.fail);
      taskList.refresh();
      return;
    }

    print("save path : $savePath");

    var dir = await createDirectory(savePath.value);
    if (dir == null) {
      vm.updateStatus(XlogInfoStatus.fail);
      taskList.refresh();
      return;
    }

    var pyPath = path.joinAll([
      _assetsDir.path,
      "decode_mars_crypt_log_file",
      "decode_mars_crypt_log_file"
    ]);

    if (this.isEnableCrypt.value == false) {
      pyPath = path.joinAll([
        _assetsDir.path,
        "decode_mars_nocrypt_log_file",
        "decode_mars_nocrypt_log_file"
      ]);
    }

    List<String> args = <String>[vm.file.path];
    if (this.isEnableCrypt.value == true) {
      args.insert(0, this.cryptMd5.value);
    }

    var process = await Process.run(pyPath, args);

    if (process.exitCode != 0) {
      showToast("Xlog解析失败,请检查你的Private Key是否正确", textPadding: EdgeInsets.all(15));
      vm.updateStatus(XlogInfoStatus.fail);
      taskList.refresh();
      return;
    }

    var file = File(vm.file.path + ".log");

    var isExist = await file.exists();
    if (isExist) {
      await Process.run("mv", [
        "-f",
        file.path,
        savePath.value,
      ]);
      vm.saveFile = File(path.joinAll([savePath.value, file.fileName]));
      vm.updateStatus(XlogInfoStatus.success);
      taskList.refresh();
    } else {
      vm.updateStatus(XlogInfoStatus.fail);
      taskList.refresh();
    }
  }

末尾

本次开源的工具暂时只支持Mac。 pyinstaller输出的也是mac才支持的可执行文件,由于pyinstaller也是跨平台的,理论上在windows机器安装 pyinstaller 直接输出适配windows的可执行文件,不同平台则替换一下相应文件,就可以适配windows。如果本次开源了反响比较大,后续我也会支持windows。否则本篇文章则可以给大家供学习,理论上Flutter的很多工具类软件都可以基于此方法编写。

附件

iOS接入xlog的Demo参考:

本篇文章开源地址:

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值