微信 mars高性能的日志模块xlog

xlog 介绍及使用

Posted by Genie on April 15, 2019

update use by swift

2020-6-30

  1. 添加 demo中的Appender2SwiftBridge文件到项目中,
  2. 在xxx-briding-Header.h问价中添加
1
2
//xlog
#import "appender-swift-bridge.h"

在原先的 LogUtil.h 文件中添加

1
2
3
4
5
// swift used
///发布模式
#define LOG_INFO_SWIFT(module, filename, format, ...)  LogInternal(kLevelInfo, module, filename, __LINE__, __FUNCTION__, @"Info:", format, ## __VA_ARGS__)
///开发模式
#define LOG_DEBUG_SWIFT(module, filename, format, ...) LogInternal(kLevelDebug, module, filename, __LINE__, __FUNCTION__, @"Debug:", format, ## __VA_ARGS__)

用来区别OC

在OC中我们通过宏定义就可以低成本实现log落地

FYI

1
2
3
4
5
6
7
8
#ifdef DEBUG
///开发模式
#define NSLog(format, ...)                           LogInternal(kLevelDebug, General, __FILENAME__, __LINE__, __FUNCTION__, @"Debug:", format, ## __VA_ARGS__)
#else
///发布模式
#define NSLog(format, ...)                           LogInternal(kLevelInfo, General, __FILENAME__, __LINE__, __FUNCTION__, @"Info:", format, ## __VA_ARGS__)
#endif

使用的时候还是利用 NSLog 低侵入,但是在swift中我们怎么使用这样的方式?

  1. 建立 Masxlog.swift
  2. write you code live this ..
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
import Foundation

let MSLog = Masxlog.default

class Masxlog {
    static let `default` = Masxlog()

    private var log: JinkeyMarsBridge?

    init() {
        log = JinkeyMarsBridge()
    }

    func config(level: XloggerType, releaseLevel: XloggerType, path: String, prefix: UnsafePointer<Int8>) {
        log?.initXlogger(level, releaseLevel: releaseLevel, path: path, prefix: prefix)
    }

    func log(_ type: XloggerType, tag: String, filename: String, content: String) {
        log?.log(type, tag: tag, filename: filename, content: content)
    }

    func log(_ filename: String, content: String) {
        #if DEBUG
            log?.log(.debug, tag: "模块Swift", filename: filename, content: content)
        #else
            log?.log(.info, tag: "模块Swift", filename: filename, content: content)
        #endif
    }

    func destroy() {
        log?.deinitXlogger()
    }
}

重写 print

1
2
3
4
5
6
7
8
9
public func print(_ items: String..., filename: String = #file, function: String = #function, line: Int = #line, separator: String = " ", terminator: String = "\n") {
    let pretty = "\(URL(fileURLWithPath: filename).lastPathComponent) [#\(line)] \(function)\n\t-> "
    let output = items.map { "\($0)" }.joined(separator: separator)
    #if DEBUG
        Swift.print(pretty + output, terminator: terminator)
    #else
    #endif
    MSLog.log(pretty, content: "\(output)")
}

还有就是通过自定义 print 功能

1
2
3
4
5
6
7
8
9
10
11
12
public func log<T>(_ message: T,
                   file: String = #file,
                   line: Int = #line,
                   column: Int = #column,
                   method: String = #function
) {
    #if DEBUG
        print("[\((file as NSString).lastPathComponent)][\(line)] - \(method): \(message)")
    #else
    #endif
    MSLog.log("\((file as NSString).lastPathComponent)", content: "\(message)")
}

我们可以这样使用 log("msg")

需要注意的是在swift下这样写都无法记录出第三方库的log日志,必须在同模块下。

业务背景

2018下旬在广汽中心和腾讯合作做网约车项目,当时软凌移动端的负责人。我们开发过程中涉及到的第三方组件由于项目背景的缘故,我们只能使用腾讯的,今天我们来记录下项目中使用到的日志功能,如何做到高性能的本地日志存储。

对于移动开发者来说,当用户反馈或者我们发现问题时,我们需要联系用户,用户答应配合后,然后修改代码打开日志重新编包让用户试图重现问题,重现之后才能继续排查。当然这个比较适合做sdk的同学, 对于to C 端的app是很难做到的,但是在我们开发测试过程中本地日志可是很关键(本地日志是为了互补crash 收集)。

Mars介绍

mars 是微信官方的终端基础组件,是一个使用 C++ 编写的业务性无关,平台性无关的基础组件。接入 mars 之后,开发一个应用只需要把开发重心放在业务层和 UI 层上。

mars 架构图

mars 架构图

  1. comm:可以独立使用的公共库,包括 socket、线程、消息队列、协程等;
  2. xlog:高可靠性高性能的运行期日志组件;
  3. SDT: 网络诊断组件;
  4. STN: 信令分发网络模块,也是 Mars 最主要的部分。

xlog 特性

1.流畅性 引入mmap来保证高性能和高可靠性IO。 2.完整性 使用流式压缩加密写入log中间buffer的mmap中,当达到一定大小在写入disk中,减少日志文件体积。 3.容错性 不丢失任何一行日志 4.安全性 日志规范化+加密算法

xlog使用

  • 下载 CMake,并正常安装。 传送门
  • 安装完成之后,使用以下指令创建/usr/local/bin下 CMake 的软链接。
    1
    
    sudo "/Applications/CMake.app/Contents/bin/cmake-gui" --install
    
  • 重新打开 Terminal,即可正常使用 CMake 的各种指令了。也可以在应用程序列表中使用带 GUI 的 CMake 工具。
  • 安装python2.7及以上
  • 执行python build_ios.py
文件名 描述
cmake_build/Darwin.out/mars.framework mars framework
libraries/mars_android_sdk/jni/longlink_packer.cc.rewriteme 长连接协议可扩展部分,使用前请先改名为 longlink_packer.cc,如若想自定义长连接包头以及加解包,重写该文件。
libraries/mars_android_sdk/jni/longlink_packer. 编译 longlink_packer.cc 需要使用的头文件,请勿修改。
libraries/mars_android_sdk/jni/stnproto_logic.h longlink_packer.cc 中 SetClientVersion 函数的声明。如若删除 SetClientVersion 函数,请同时删除该文件。
libraries/mars_android_sdk/jni/shortlink_packer.cc.rewriteme 短连接协议可扩展部分,使用前请先改名为 shortlink_packer.cc,如若想自定义短连接包头(HTTP Head),重写该文件。
libraries/mars_android_sdk/jni/shortlink_packer.h 编译 shortlink_packer.cc 需要使用的头文件,请勿修改。

以下接入部分可参考 mars/samples/iOS 和 mars/samples/Mac。 强调:所有直接调用 C/C++接口的 Objc 源文件必须是 .mm 后缀,不能是.m 后缀。

接入项目 img

其中红色部分为 mars 提供的部分,黄色部分是使用 mars 需要的系统库,绿色部分为需要开发者自行实现的 callback 部分。注意:链接的时候要把生成的 framework 移到别的位置再链接,不要直接用mars/mars/cmake_build/iOS/Darwin.out 目录。因为如果再次执行编译脚本会进行一次 clean, 会删除此目录下的文件。

xlog使用方法

  • 初始化和反初始化 需要在main.mm文件初始化
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
NSString *logPath = [[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0] stringByAppendingString:@"/log"];    
    NSLog(@"logPath: %@", logPath);
    //不备份日志路径
    const char* attrName = "com.apple.MobileBackup";
    u_int8_t attrValue = 1;
    setxattr([logPath UTF8String], attrName, &attrValue, sizeof(attrValue), 0, 0);

#if DEBUG
    xlogger_SetLevel(kLevelDebug);
    appender_set_console_log(true);
#else
    xlogger_SetLevel(kLevelInfo);
    appender_set_console_log(false);
#endif
    /*
     * 设置日志目录、日志文件前缀
     * 特别强调一点pubkey,设置后才会对日志进行加密,若Debug模式下不希望加密,可以设置空"",pubkey在decode_mars_crypt_log_file.py脚本中。
     */
    appender_open(kAppednerAsync, [logPath UTF8String], "fileNameprefix", "pubkey"); 

需要在APP终止方法applicationWillTerminate 中反初始化

1
appender_close();
  • 打印日志

LOG_DEBUG(@”模块名称”, @”日志描述:%@”, 需要打印的具体信息); //开发模式 LOG_INFO(@”模块名称”, @”日志描述:%@”, 需要打印的具体信息); //发布模式

解析xlog文件

  1. 第一种通过下载安装pyelliptic1.5.7
  2. 第二种可以在samples 里面找到PublicComponentV2文件夹

执行python decode_mars_log_file.py “xlog文件” 得到—xlog.log文件,即可以打开

有问题可以联系Email