前言:主要事項
1、異常數(shù)據(jù)的發(fā)現(xiàn)及補充烙肺、日志分類優(yōu)化
2纳猪、日志數(shù)據(jù)的文件保存、滾動存儲
3桃笙、日志文件上傳
4氏堤、日志回撈
一、日志收集
1搏明、日志分類
1.1鼠锈、日志目標
序號 | 目標(Target) | ||
---|---|---|---|
1 | app | ||
2 | sdk | 三方庫 | |
3 | h5 | 網(wǎng)頁 |
<a name="日志類型"></a>
1.2、日志類型
序號 | 類型 | 描述 | |
---|---|---|---|
1 | api_app | app中的網(wǎng)絡請求 | |
2 | api_app_cache | app中的網(wǎng)絡請求的網(wǎng)絡緩存請求 | |
3 | api_buriedPoint | 埋點的網(wǎng)絡請求 | |
4 | sdk_other | sdk的各種事件(初始化等) | |
5 | sdk_api | sdk中的網(wǎng)絡請求 | |
6 | dart | 語法 | |
7 | widget | 視圖(布局像素越界等) | |
8 | click_other | 點擊星著、 | |
9 | click_share | 分享 | |
10 | native_route | 路由/跳轉(zhuǎn) | |
11 | h5_route | 與網(wǎng)頁跳轉(zhuǎn)有關 | |
12 | h5_js | js交互 | |
13 | monitor_network | 監(jiān)控:網(wǎng)絡類型變化 | |
14 | monitor_lifecycle | 監(jiān)控:生命周期變化 | |
15 | buriedPoint_other | 埋點數(shù)據(jù)生成等 | |
16 | im | IM | |
17 | heartbeat | 心跳 | |
18 | other | 其他 |
2购笆、日志等級
序號 | 目標(Level) | 描述 | |
---|---|---|---|
1 | Normal | 正常信息(目前用于請求開始) | |
2 | Success | 成功信息(目前用于請求結(jié)束:成功) | |
3 | Warning | 警告信息(目前用于請求結(jié)束:報錯) | |
4 | Error | 錯誤日志(目前用于請求結(jié)束:失敗) | |
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 | 網(wǎng)頁 | H5 | h5_route衫哥、h5_js |
8 | sdk | sdk | sdk_api、sdk_other |
9 | code | code | dart襟锐、widget |
10 | 埋點 | buriedPoint | api_buriedPoint炕檩、buriedPoint_other |
11 | 監(jiān)控 | monitor | monitor_lifecycle、monitor_network |
12 | 其他 | other | other |
13 | api結(jié)果 | api_result | type=api_app & level != Normal |
14 | im | im | 本地緩存消息捌斧、歷史消息笛质、收到的消息等 |
15 | 心跳 | heartbeat |
<img src="日志系統(tǒng)/devtool_log_home_page.gif" alt="devtool_log_home_page" style="zoom:50%;" />
4、日志的補充收集及其策略
要收集的數(shù)據(jù)已在上述《1.2捞蚂、日志類型》中說明妇押。
4.1、頁面跳轉(zhuǎn)
詳見上述 《3姓迅、日志整合歸類》中的"路由"
4.2敲霍、截斷的收集
序號 | 關鍵字 | 場景 |
---|---|---|
1 | break | for 、 swtich |
2 | return |
4.3丁存、用戶關鍵行為
序號 | 行為 | 用途的場景說明 |
---|---|---|
1 | 用戶的資源選擇 | 判斷選擇的本地資源是否上傳到錯誤的存儲桶 |
二肩杈、日志數(shù)據(jù)的文件保存、滾動存儲
1解寝、數(shù)據(jù)的文件保存
寫入xlog文件中扩然。
2、數(shù)據(jù)的文件滾動存儲
2.1聋伦、日志的過期清理
1夫偶、本地日志文件結(jié)構
-- 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 每個日志文件超過多少后創(chuàng)建新文件 perFileMaxMB 5
3觉增、清理方案
清理時機:在切換前后臺的時候兵拢,
- 日志配置信息的更新
- 根據(jù)最新的日志配置信息,延遲10s后(避免影響啟動)逾礁,進行清理判斷及清理
- 遍歷總?cè)罩灸夸?Document/log 下的所有日志文件夾
- 如果天日志文件夾個數(shù)超過保留個數(shù)(避免是用保留時間说铃,導致保留時間內(nèi)只有一個日志,也被刪掉)嘹履,則按順序刪除早期日志
- 遍歷天日志目錄 Document/log/yyyy-MM-dd 下的所有日志文件夾
- 如果天日志文件夾個數(shù)超過保留個數(shù)(避免是用保留時間腻扇,導致保留時間內(nèi)只有一個日志,也被刪掉)植捎,則按順序刪除早期日志
2.2衙解、日志文件的滾動寫入
要增加的日志記錄,如果添加后會超過日志文件的大小焰枢,則使用新文件寫入蚓峦。
細節(jié)點:
1舌剂、日志文件的大小判斷,不要每次都去讀取文件暑椰,而是記錄住大小變化
2霍转、日志不要有一條寫一條,而是使用寫入緩沖區(qū)+定時器一汽,5s嘗試寫入一次
3避消、數(shù)據(jù)的加密、解密
1召夹、加密:
寫入日志字符串時候岩喷,對編碼后的字節(jié)數(shù)據(jù),額外增加一個三位隨機值监憎。
/// 使用 GZip 壓縮算法將字符串進行壓縮纱意,并返回壓縮后的字節(jié)數(shù)據(jù)(List<int> 類型)。
List<int> _gzipCompressString(String inputString) {
final codec = GZipCodec();
// 將輸入字符串 inputString 編碼為 UTF-8 格式的字節(jié)數(shù)據(jù)
final encoded = utf8.encode(inputString);
// 對編碼后的字節(jié)數(shù)據(jù)進行壓縮鲸阔,并返回壓縮后的字節(jié)數(shù)據(jù)偷霉。
final compressedData = codec.encode(encoded);
return compressedData;
}
/// 加密日志字符串
String _encryptLogString(String logString) {
final compressedData = _gzipCompressString(logString); // 壓縮字符串,并返回壓縮后的字節(jié)數(shù)據(jù)(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文件并解析數(shù)據(jù)
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] + ']' # 去除加密時候額外添加的三位隨機數(shù)
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')
# 添加最后一個數(shù)據(jù)條目
if current_data:
parsed_data.append(current_data)
return parsed_data
# 將解析后的數(shù)據(jù)寫入與輸入文件同名的.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)
# 將解析后的數(shù)據(jù)寫入與輸入文件同名的.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、日志文件命名及目錄規(guī)范
以用戶1000001
在2020-01
時候上傳聲音文件 beautiful_scenery.aac
為例渔扎,其路徑完整示例如下:
https://media.xxx.com/app1_test1/audio/1/1000001/2020-01/beautiful_scenery.aac
要上傳的存儲桶:
序號 | 說明 | 參數(shù) | 約定值示例 |
---|---|---|---|
1 | 存儲桶region regionGetFunction |
網(wǎng)絡環(huán)境 | ap-shanghai |
2 | 存儲桶bucket bucketGetFunction |
網(wǎng)絡環(huán)境 | 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標志_環(huán)境標志 eg:app1_pro、app1_test1愧旦、app1_dev1 |
文件區(qū)分 |
1.2 | 是否在存儲桶里進行進一步路徑區(qū)分(可選) | 根據(jù)場景決定是否對桶進行進一步分類。 if ( mediaType == xxx ) { if(mediaScene == yyy) { } } |
如 1定罢、都是圖片笤虫,但自拍圖片要求獨立出來 2、都是多媒體祖凫,但音視頻要區(qū)分開 |
2.1 | 用戶1級 | int.parse(uid) % 1000; // 取余數(shù) | 用戶名取余琼蚯,有效減少同層上文件夾個數(shù) |
2.2 | 用戶2級 | uid | 用戶名 |
3 | 年月 | DateTime.now().toString().substring(0, 7); | 年-月 |
4 | 文件名 |
{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惠况、日志記錄接口(區(qū)別于回撈接口遭庶,需要在回撈前就有日志信息)
1、是否記錄日志的開關及其配置信息
序號 | 說明 | 字段 | 示例 |
---|---|---|---|
網(wǎng)頁配置1 | 是否開啟日志記錄 | isLogOn | 1:開啟稠屠、其他:關 |
網(wǎng)頁配置2 | 要開啟日志的用戶(灰度上線) (當且僅當開啟上述日志記錄時有效) ??:未配置但開關為開時為全記錄 |
logUserids | ["101", "102"] |
網(wǎng)頁配置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 | 每個日志文件超過多少后創(chuàng)建新文件 | perFileMaxMB | 5 |
4榨了、日志文件問題回滾
序號 | 說明 | 字段 | 示例 |
---|---|---|---|
網(wǎng)頁配置1 | 強制刪除什么時間前的日志 | removeLogBeforeDate | 2020-12-31 |
網(wǎng)頁配置2 | 強制刪除哪個用戶的日志 | removeUserids | ["101", "102"] |
網(wǎng)頁配置2 | 強制刪除哪個設備的日志 | logDeviceIds | ["udid001", "udid002"] |
接口回值1 | 強制刪除什么時間前的日志(不刪除時空) | removeLogBeforeDate | 2020-12-31 或 空值 |
接口名定義:
function getLoggerConfig(userid, deviceId)
接口結(jié)果的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、日志回撈
序號 | 說明 | 字段 | 示例 |
---|---|---|---|
網(wǎng)頁配置1 | 要回撈日志的用戶 | salvageUserids | ["101", "102"] |
網(wǎng)頁配置2 | 要回撈日志的設備 | salvageDeviceIds | ["udid001", "udid002"] |
網(wǎng)頁配置3 | 日志回撈的開始時間 | salvageStartDate | 2020-01-01 |
網(wǎng)頁配置4 | 日志回撈的結(jié)束時間 | salvageEndDate | 2020-01-07 |
接口回值1 | 此用戶此設備日志回撈的開始時間 | salvageStartDate | 2020-01-01 |
接口回值1 | 此用戶此設備日志回撈的結(jié)束時間 | salvageEndDate | 2020-01-07 |
2龙屉、日志文件問題回滾
序號 | 說明 | 字段 | 示例 |
---|---|---|---|
網(wǎng)頁配置1 | 強制刪除什么時間前的日志 | removeLogBeforeDate | 2020-12-31 |
網(wǎng)頁配置2 | 強制刪除哪個用戶的日志 | removeUserids | ["101", "102"] |
網(wǎng)頁配置2 | 強制刪除哪個設備的日志 | logDeviceIds | ["udid001", "udid002"] |
接口回值1 | 強制刪除什么時間前的日志(不刪除時空) | removeLogBeforeDate | 2020-12-31 或 空值 |
接口名定義:
function getLoggerSalvageAndRevert(salvageUserids, salvageDeviceIds)
接口結(jié)果的json示例如下:
{
"salvageId": "1001",
"salvageStartDate": "2020-01-01",
"salvageEndDate": "2020-01-07",
"removeLogBeforeDate": "2020-12-31"
}
3呐粘、日志文件上傳到cos后同步給后臺
用途:日志文件上傳到cos后同步給后臺,后臺數(shù)據(jù)庫記錄每個用戶转捕,每個設備都撈到了什么數(shù)據(jù)作岖。
接口名定義:
function addLoggerFile(userid, deviceId)
接口結(jié)果的json示例如下:
{
}
四、日志回撈
1五芝、后臺通過接口返回要回撈的信息痘儡。
2、前端用戶在下次使用与柑,收到后臺信息時候谤辜,進行本地日志的上傳。在騰云存儲桶能收到對應日志价捧,即代表成功丑念。
五、方案實施
灰度方案:請參照 灰度系統(tǒng) 结蟋。(附:日志記錄開關里目前已有灰度策略脯倚。)
目的:避免功能異常,出現(xiàn)集體性問題嵌屎。