一、功能概述

#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)的流量控制模块,核心功能:

  1. 按domain维度统计日志流量(g_domainMap维护状态)
  2. 基于时间窗口(1秒)进行配额检测
  3. 自动触发丢弃机制(返回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专属配额
  • 初始化时即计入首条日志长度

三、设计亮点

  1. 高效数据结构:使用unordered_map实现O(1)复杂度的domain查找
  2. 精准时间控制:基于mono_sec和tv_nsec的高精度时间判断
  3. 状态自维护:自动清理过期domain(依赖日志触发清理)

四、优化思考

  1. 线程安全:当前实现未加锁,多线程环境需改为std::unordered_map+互斥锁
  2. 时间精度:可考虑使用steady_clock避免系统时间跳变
  3. 动态调整:结合GetDomainQuota实现运行时配额热更新

该实现已在OpenHarmony的HiLog组件中实际应用,有效保障了系统日志服务的稳定性。开发者可通过调整domain配额参数,实现不同业务场景的精细化流控。

Logo

社区规范:仅讨论OpenHarmony相关问题。

更多推荐