DFX框架之hilog源码详解:日志流控机制设计与实现解析
·
一、功能概述
#include "flow_control.h"
#include <fstream>
#include <iostream>
#include <strstream>
#include <string>
#include <ctime>
#include <atomic>
#include <unordered_map>
#include <unistd.h>
#include <hilog/log.h>
#include "properties.h"
#include "log_timestamp.h"
#include "log_utils.h"
namespace OHOS {
namespace HiviewDFX {
constexpr int FLOW_CTL_NORAML = 0;
constexpr int FLOW_CTL_DROPPED = -1;
using DomainInfo = struct {
uint32_t quota;
uint32_t sumLen;
uint32_t dropped;
LogTimeStamp startTime;
};
static std::unordered_map<uint32_t, DomainInfo> g_domainMap;
int FlowCtrlDomain(const HilogMsg& hilogMsg)
{
static const LogTimeStamp PERIOD(1, 0);
if (hilogMsg.type == LOG_APP) {
return FLOW_CTL_NORAML;
}
int ret = FLOW_CTL_NORAML;
uint32_t domainId = hilogMsg.domain;
auto logLen = hilogMsg.len - sizeof(HilogMsg) - 1 - 1; /* quota length exclude '\0' of tag and log content */
LogTimeStamp tsNow(hilogMsg.mono_sec, hilogMsg.tv_nsec);
auto it = g_domainMap.find(domainId);
if (it != g_domainMap.end()) {
LogTimeStamp start = it->second.startTime;
start += PERIOD;
/* in statistic period(1 second) */
if (tsNow > start) { /* new statistic period */
it->second.startTime = tsNow;
it->second.sumLen = logLen;
ret = static_cast<int>(it->second.dropped);
it->second.dropped = 0;
} else {
if (it->second.sumLen <= it->second.quota) { /* under quota */
it->second.sumLen += logLen;
ret = FLOW_CTL_NORAML;
} else { /* over quota */
it->second.dropped++;
ret = FLOW_CTL_DROPPED;
}
}
} else {
int quota = GetDomainQuota(domainId);
DomainInfo info{static_cast<uint32_t>(quota), logLen, 0, tsNow};
g_domainMap.emplace(domainId, info);
}
return ret;
}
} // namespace HiviewDFX
} // namespace OHOS
代码路径:services\hilogd\flow_control.cpp
基于日志域(domain)的流量控制模块,核心功能:
- 按domain维度统计日志流量(g_domainMap维护状态)
- 基于时间窗口(1秒)进行配额检测
- 自动触发丢弃机制(返回FLOW_CTL_DROPPED)防止日志过载
二、核心实现原理
1. 统计周期管理
static const LogTimeStamp PERIOD(1, 0); // 1秒统计周期
- 使用LogTimeStamp记录每个domain的起始时间戳
- 当新日志时间超过当前周期时重置统计(tsNow > start + PERIOD)
2. 配额检查逻辑
if (it->second.sumLen <= it->second.quota) {
// 正常累计
} else {
// 触发丢弃
}
- 计算有效日志长度:logLen = msg长度 - 协议头 - 两个'\0'
- 动态累加当前周期内的日志总量
3. 新domain初始化
int quota = GetDomainQuota(domainId); // 获取动态配额
g_domainMap.emplace(domainId, {quota, logLen, 0, tsNow});
- 通过GetDomainQuota获取domain专属配额
- 初始化时即计入首条日志长度
三、设计亮点
- 高效数据结构:使用unordered_map实现O(1)复杂度的domain查找
- 精准时间控制:基于mono_sec和tv_nsec的高精度时间判断
- 状态自维护:自动清理过期domain(依赖日志触发清理)
四、优化思考
- 线程安全:当前实现未加锁,多线程环境需改为std::unordered_map+互斥锁
- 时间精度:可考虑使用steady_clock避免系统时间跳变
- 动态调整:结合GetDomainQuota实现运行时配额热更新
该实现已在OpenHarmony的HiLog组件中实际应用,有效保障了系统日志服务的稳定性。开发者可通过调整domain配额参数,实现不同业务场景的精细化流控。
更多推荐
所有评论(0)