美文网首页
日志系统的建设要点

日志系统的建设要点

作者: dvlproad | 来源:发表于2024-09-03 16:55 被阅读0次

    前言:主要事项

    1、异常数据的发现及补充、日志分类优化
    2、日志数据的文件保存、滚动存储
    3、日志文件上传
    4、日志回捞

    一、日志收集

    1、日志分类

    1.1、日志目标

    序号 目标(Target)
    1 app
    2 sdk 三方库
    3 h5 网页

    <a name="日志类型"></a>

    1.2、日志类型

    序号 类型 描述
    1 api_app app中的网络请求
    2 api_app_cache app中的网络请求的网络缓存请求
    3 api_buriedPoint 埋点的网络请求
    4 sdk_other sdk的各种事件(初始化等)
    5 sdk_api sdk中的网络请求
    6 dart 语法
    7 widget 视图(布局像素越界等)
    8 click_other 点击、
    9 click_share 分享
    10 native_route 路由/跳转
    11 h5_route 与网页跳转有关
    12 h5_js js交互
    13 monitor_network 监控:网络类型变化
    14 monitor_lifecycle 监控:生命周期变化
    15 buriedPoint_other 埋点数据生成等
    16 im IM
    17 heartbeat 心跳
    18 other 其他

    2、日志等级

    序号 目标(Level) 描述
    1 Normal 正常信息(目前用于请求开始)
    2 Success 成功信息(目前用于请求结束:成功)
    3 Warning 警告信息(目前用于请求结束:报错)
    4 Error 错误日志(目前用于请求结束:失败)
    5 Dangerous 危险(处理白屏等) 一般会进行额外的埋点

    <a name="日志整合归类"></a>

    3、日志整合归类

    序号 列表 标志 包含
    1 全部 all 所有
    2 警告 warning 所有的警告
    3 错误 error 所有的错误
    4 接口 api api_app、api_cache(不包括sdk_api、api_buriedPoint)
    5 点击 click click_share、click_other、h5_js
    6 路由 route navite_route、h5_route
    7 网页 H5 h5_route、h5_js
    8 sdk sdk sdk_api、sdk_other
    9 code code dart、widget
    10 埋点 buriedPoint api_buriedPoint、buriedPoint_other
    11 监控 monitor monitor_lifecycle、monitor_network
    12 其他 other other
    13 api结果 api_result type=api_app & level != Normal
    14 im im 本地缓存消息、历史消息、收到的消息等
    15 心跳 heartbeat

    <img src="日志系统/devtool_log_home_page.gif" alt="devtool_log_home_page" style="zoom:50%;" />

    4、日志的补充收集及其策略

    要收集的数据已在上述《1.2、日志类型》中说明。

    4.1、页面跳转

    详见上述 《3、日志整合归类》中的"路由"

    4.2、截断的收集

    序号 关键字 场景
    1 break for 、 swtich
    2 return

    4.3、用户关键行为

    序号 行为 用途的场景说明
    1 用户的资源选择 判断选择的本地资源是否上传到错误的存储桶

    二、日志数据的文件保存、滚动存储

    1、数据的文件保存

    写入xlog文件中。

    2、数据的文件滚动存储

    2.1、日志的过期清理

    1、本地日志文件结构

    -- Document
        -- log
            -- 2020-10-01
                -- xlog_2020-10-01_1.xlog
                -- xlog_2020-10-01_2.xlog
            -- 2020-10-18
                -- xlog_2020-10-18_1.xlog
    

    2、日志保留清理的配置信息(摘自/同 下文的日志接口中的《日志文件配置信息》)

    序号 说明 字段 示例
    接口回值1 最多保留几个天的日志目录 maxDayDirCount 7
    接口回值2 最多每天保留几个文件 maxDayFileCount 10
    接口回值3 每个日志文件超过多少后创建新文件 perFileMaxMB 5

    3、清理方案

    清理时机:在切换前后台的时候,

    • 日志配置信息的更新
    • 根据最新的日志配置信息,延迟10s后(避免影响启动),进行清理判断及清理
      • 遍历总日志目录 Document/log 下的所有日志文件夹
      • 如果天日志文件夹个数超过保留个数(避免是用保留时间,导致保留时间内只有一个日志,也被删掉),则按顺序删除早期日志
        • 遍历天日志目录 Document/log/yyyy-MM-dd 下的所有日志文件夹
        • 如果天日志文件夹个数超过保留个数(避免是用保留时间,导致保留时间内只有一个日志,也被删掉),则按顺序删除早期日志

    2.2、日志文件的滚动写入

    要增加的日志记录,如果添加后会超过日志文件的大小,则使用新文件写入。

    细节点
    1、日志文件的大小判断,不要每次都去读取文件,而是记录住大小变化
    2、日志不要有一条写一条,而是使用写入缓冲区+定时器,5s尝试写入一次

    3、数据的加密、解密

    1、加密:

    写入日志字符串时候,对编码后的字节数据,额外增加一个三位随机值。

      /// 使用 GZip 压缩算法将字符串进行压缩,并返回压缩后的字节数据(List<int> 类型)。
      List<int> _gzipCompressString(String inputString) {
        final codec = GZipCodec();
        
        // 将输入字符串 inputString 编码为 UTF-8 格式的字节数据
        final encoded = utf8.encode(inputString);
        
        // 对编码后的字节数据进行压缩,并返回压缩后的字节数据。
        final compressedData = codec.encode(encoded);
        
        return compressedData;
      }
    
      /// 加密日志字符串
      String _encryptLogString(String logString) {
        final compressedData = _gzipCompressString(logString); // 压缩字符串,并返回压缩后的字节数据(List<int> 类型)
        final randomNumber = Random().nextInt(800) + 100;
        final confound = compressedData.toString().substring(0, compressedData.toString().length - 1) + ", $randomNumber]"; // 在压缩后的字符串尾部加上一个随机字符串
    
        return confound;
      }
    

    2、解密:

    使用python脚本对log文件进行解密

    import sys
    import os
    import gzip
    
    # 读取.clog文件并解析数据
    def parse_clog_file(input_file):
        parsed_data = []
    
        with open(input_file, 'r') as clog_file:
            current_data = ""
            for line in clog_file:
                if current_data:
                    parsed_data.append(current_data)
                binary_data = line[:-6] + ']' # 去除加密时候额外添加的三位随机数
                byte_data = bytes(eval(binary_data))
                decompressed_data = gzip.decompress(byte_data)
                # current_data = byte_data.decode('utf-8')
                print(decompressed_data)
                current_data = decompressed_data.decode('utf-8')
    
            # 添加最后一个数据条目
            if current_data:
                parsed_data.append(current_data)
    
        return parsed_data
    
    # 将解析后的数据写入与输入文件同名的.log文件
    def write_to_log_file(parsed_data, input_file):
        base_name = os.path.splitext(input_file)[0]
        output_file = base_name + ".log"
    
        with open(output_file, 'w') as log_file:
            for data in parsed_data:
                log_file.write(data + "\n")
                log_file.write("\n")
                log_file.write("=================================================================================================" + "\n")
                log_file.write("\n")
    
    if __name__ == "__main__":
        if len(sys.argv) != 2:
            print("Usage: python script.py <clogFileName>")
            sys.exit(1)
    
        input_clog_file = sys.argv[1]
    
        # 解析.clog文件
        parsed_data = parse_clog_file(input_clog_file)
    
        # 将解析后的数据写入与输入文件同名的.log文件
        write_to_log_file(parsed_data, input_clog_file)
    
        print(f"Log data parsed from {input_clog_file} and written to {input_clog_file}.log.")
    

    三、日志文件上传

    1、日志文件命名及目录规范

    以用户10000012020-01时候上传声音文件 beautiful_scenery.aac为例,其路径完整示例如下:

    https://media.xxx.com/app1_test1/audio/1/1000001/2020-01/beautiful_scenery.aac
    

    要上传的存储桶:

    序号 说明 参数 约定值示例
    1 存储桶region
    regionGetFunction
    网络环境 ap-shanghai
    2 存储桶bucket
    bucketGetFunction
    网络环境 xxx-pro-image-1302324914
    3 上传成功后,桶的映射路径值
    cosFileUrlPrefixGetFunction
    1、图片桶:https://images.xxx.com/
    2、媒体桶:https://media.xxx.com/
    3、日志桶:https://static.xxx.com/
    4 要上传到桶的哪个相对路径下
    cosFileRelativePathGetFunction
    上传成功后完整路径=以上桶值+此相对路径
    见下文 见下文
    eg:app1_test1/audio/1/1000001/2020-01/beautiful_scenery.aac

    《要上传到桶的哪个相对路径下 cosFileRelativePathGetFunction 》的文件路径分批说明:

    序号 层次说明 层次值算法 层次值描述
    1.1 存储桶里相对路径的前缀 app标志_环境标志
    eg:app1_pro、app1_test1、app1_dev1
    文件区分
    1.2 是否在存储桶里进行进一步路径区分(可选) 根据场景决定是否对桶进行进一步分类。
    if ( mediaType == xxx ) {
    if(mediaScene == yyy) {

    }
    }

    1、都是图片,但自拍图片要求独立出来
    2、都是多媒体,但音视频要区分开
    2.1 用户1级 int.parse(uid) % 1000; // 取余数 用户名取余,有效减少同层上文件夹个数
    2.2 用户2级 uid 用户名
    3 年月 DateTime.now().toString().substring(0, 7); 年-月
    4 文件名 {fileOriginName}_<br>{DateTime.now().microsecondsSinceEpoch}
    .$fileExtensionType
    文件名

    附1:MediaType 媒体类型

    序号 UploadMediaType 媒体类型 描述
    1 unkonw 未知(默认值)
    2 image 图片
    3 audio 音频
    4 video 视频
    5 xlog 日志文件

    附2:MediaScene场景 说明

    序号 UploadMediaScene场景 描述
    1 unkonw 未知(默认值)
    2 selfie 自拍(安全等级较多)
    3 im 会话聊天
    4 live 直播

    四、日志接口

    1、日志记录接口(区别于回捞接口,需要在回捞前就有日志信息)

    1、是否记录日志的开关及其配置信息

    序号 说明 字段 示例
    网页配置1 是否开启日志记录 isLogOn 1:开启、其他:关
    网页配置2 要开启日志的用户(灰度上线)
    (当且仅当开启上述日志记录时有效)
    📢:未配置但开关为开时为全记录
    logUserids ["101", "102"]
    网页配置3 要开启日志的设备(灰度上线)
    (当且仅当开启上述日志记录时有效)
    📢:未配置但开关为开时为全记录
    logDeviceIds ["udid001", "udid002"]
    接口回值1 是否进行日志记录(灰度上线) needLog 1:记录、其他:不记录

    2、不需要记录什么日志信息

    序号 说明 字段 示例
    接口回值1 不需要记录的日志类型 ignoreLogLevel ["Normal", "Success"]
    接口回值2 不需要记录的日志等级 ignoreLogType ["api_buriedPoint", "buriedPoint_other"]

    3、日志文件配置信息

    序号 说明 字段 示例
    接口回值1 最多保留几个天的日志目录 maxDayDirCount 7
    接口回值2 最多每天保留几个文件 maxDayFileCount 10
    接口回值3 每个日志文件超过多少后创建新文件 perFileMaxMB 5

    4、日志文件问题回滚

    序号 说明 字段 示例
    网页配置1 强制删除什么时间前的日志 removeLogBeforeDate 2020-12-31
    网页配置2 强制删除哪个用户的日志 removeUserids ["101", "102"]
    网页配置2 强制删除哪个设备的日志 logDeviceIds ["udid001", "udid002"]
    接口回值1 强制删除什么时间前的日志(不删除时空) removeLogBeforeDate 2020-12-31 或 空值

    接口名定义:

    function getLoggerConfig(userid, deviceId)
    

    接口结果的json示例如下:

    {
        "logConfig": {
            "needLog": 1,
            "ignoreLogLevel": ["Normal", "Success"],
            "ignoreLogType": ["api_buriedPoint", "buriedPoint_other"],
            "maxDayDirCount": 7,
            "maxDayFileCount": 10,
            "perFileMaxMB": 5
        },
        "logRevert": {
            "removeLogBeforeDate": "2020-12-31"
        }
    }
    

    2、日志回捞及问题回滚接口

    1、日志回捞

    序号 说明 字段 示例
    网页配置1 要回捞日志的用户 salvageUserids ["101", "102"]
    网页配置2 要回捞日志的设备 salvageDeviceIds ["udid001", "udid002"]
    网页配置3 日志回捞的开始时间 salvageStartDate 2020-01-01
    网页配置4 日志回捞的结束时间 salvageEndDate 2020-01-07
    接口回值1 此用户此设备日志回捞的开始时间 salvageStartDate 2020-01-01
    接口回值1 此用户此设备日志回捞的结束时间 salvageEndDate 2020-01-07

    2、日志文件问题回滚

    序号 说明 字段 示例
    网页配置1 强制删除什么时间前的日志 removeLogBeforeDate 2020-12-31
    网页配置2 强制删除哪个用户的日志 removeUserids ["101", "102"]
    网页配置2 强制删除哪个设备的日志 logDeviceIds ["udid001", "udid002"]
    接口回值1 强制删除什么时间前的日志(不删除时空) removeLogBeforeDate 2020-12-31 或 空值

    接口名定义:

    function getLoggerSalvageAndRevert(salvageUserids, salvageDeviceIds)
    

    接口结果的json示例如下:

    {
      "salvageId": "1001", 
        "salvageStartDate": "2020-01-01",
      "salvageEndDate": "2020-01-07",
        "removeLogBeforeDate": "2020-12-31"
    }
    

    3、日志文件上传到cos后同步给后台

    用途:日志文件上传到cos后同步给后台,后台数据库记录每个用户,每个设备都捞到了什么数据。

    接口名定义:

    function addLoggerFile(userid, deviceId)
    

    接口结果的json示例如下:

    {
    
    }
    

    四、日志回捞

    1、后台通过接口返回要回捞的信息。

    2、前端用户在下次使用,收到后台信息时候,进行本地日志的上传。在腾云存储桶能收到对应日志,即代表成功。

    五、方案实施

    灰度方案:请参照 灰度系统 。(附:日志记录开关里目前已有灰度策略。)

    目的:避免功能异常,出现集体性问题。

    End

    相关文章

      网友评论

          本文标题:日志系统的建设要点

          本文链接:https://www.haomeiwen.com/subject/cdokljtx.html