资讯动态

ClusterFuzz 机器人日志体系全解:bot/logs 目录各日志文件的作用与实现原理

发布时间:2026/10/13 16:27:55 来源:尧图企业网站定制
测试应用安全后端【免费下载链接】clusterfuzzScalable fuzzing infrastructure.项目地址https://gitcode.com/gh_mirrors/clu/clusterfuzz点击查看免费下载本文围绕 ClusterFuzz 仓库中 bot/logs/README.md 所描述的日志文件约定结合src/clusterfuzz/_internal/metrics/logs.py等源码系统讲解机器人bot运行时产生的各类日志文件——bot.log、heartbeat.log、run_fuzzer.log、run_testcase.log、run_heartbeat.log与run.log——各自记录什么内容、由哪个进程写入、如何配置与轮转。读完本文你将能准确理解 ClusterFuzz bot 的日志布局并据此排查机器人为什么不干活心跳是否中断fuzzer 是否在跑等典型问题。一、bot/logs机器人日志的落盘目录在 ClusterFuzz 中机器人bot负责拉取构建产物、运行 fuzzer、执行 testcase、上报崩溃等核心工作整个过程会产生大量运行日志。这些日志统一存放在仓库根目录下的bot/logs/目录中。该目录本身只是一个占位目录原文Placeholder for log files generated by the bots真正的日志文件由 bot 在运行过程中动态生成。日志目录的实际位置由环境变量LOG_DIR决定。从 environment.py 的源码可以看到set_bot_environment()会把LOG_DIR指向root/bot/logsos.environ[BUILD_URLS_DIR] os.path.join(bot_dir, build-urls) os.environ[LOG_DIR] os.path.join(bot_dir, logs) os.environ[CACHE_DIR] os.path.join(bot_dir, cache)同时在 run.py 中启动脚本也是通过environment.get_value(LOG_DIR)读取该目录并拼接出bot.log的完整路径后传给心跳脚本。二、六个核心日志文件逐一解析根据 bot/logs/README.mdbot 运行时会生成以下日志文件。下表先给出总览随后逐个说明其写入方与典型内容。日志文件记录内容写入进程源码调用点bot.logbot 当前正在做什么获取构建、运行 fuzzer、执行 testcase 等run_bot.py的任务循环heartbeat.log心跳监控器当前正在做什么heartbeat.py的心跳检查run_fuzzer.logfuzzer 引擎libFuzzer、AFL 等的执行情况fuzzer 启动器如 AFL launcherrun_testcase.log通用 testcase 执行信息testcase_manager.pyrun_heartbeat.log以无限循环方式运行心跳监控器的包装脚本信息run_heartbeat.pyrun.log运行 bot、心跳监控器、更新源码等包装脚本信息run.py主循环注意README 中写作run_heatbeat.log属于笔误源码 logs.py 中实际注册的文件名是run_heartbeat.log。2.1 bot.log —— 机器人主任务日志作用记录 bot 当前正在执行的工作例如获取构建产物fetching builds、运行 fuzzer、运行生成的 testcase 等是排查bot 此刻在干什么的第一入口。写入方src/python/bot/startup/run_bot.py。该脚本在main()中调用logs.configure(run_bot)见 run_bot.py随后进入任务循环。任务处理过程中的异常都会记入此日志例如except commands.AlreadyRunningError: exception_occurred False except task_utils.UworkerMsgParseError: logs.log_error(Task cannot be retried because of utask parse error.) ... except Exception: logs.log_error(Error occurred while working on task.)排查价值当 bot 长时间没有产出时检查bot.log可以看到它是在拉构建、跑 fuzzer 还是卡在某个任务上。2.2 heartbeat.log —— 心跳监控日志作用记录心跳监控器heartbeat monitor当前正在做什么。心跳监控器的作用是确认 bot 进程是否仍然存活。写入方src/python/bot/startup/heartbeat.py其模块注释明确写着Heartbeat script that monitors whether the bot is still running or not见 heartbeat.py。它在main()中调用logs.configure(heartbeat)heartbeat.py。核心逻辑beat()函数会检查 bot 是否卡死——如果任务超时tasks.TASK_COMPLETION_BUFFER缓冲期内未完成它会遍历进程找到卡死的run_bot进程并杀掉同时做临时目录、testcase 目录清理logs.log(Killing stale bot (pid %d) which seems to have stuck. % process_id) process_handler.terminate_root_and_child_processes(process_id) ... shell.clear_temp_directory() shell.clear_testcase_directories()此外心跳脚本还会监控bot.log的最后修改时间只有日志文件被更新说明 bot 有活动时才向服务端上报心跳data_handler.update_heartbeat()详见 heartbeat.py。2.3 run.log —— 机器人总控包装脚本日志作用记录总控包装脚本wrapper script的运行情况包括启动 bot、启动心跳监控器、更新源码等。写入方src/python/bot/startup/run.py它是对整个 bot 生命周期的主控脚本。main()中调用logs.configure(run)run.py随后进入run_loop()无限循环每次循环先检查是否需要更新源码update_source_code_if_needed()再启动心跳、运行 bot 子进程。start_bot()会捕获子进程的输出并根据退出码决定日志级别run.pyif exit_code 0: logs.log(log_message) elif exit_code 1: # Anecdotally, exit1 means theres a fatal Python exception. logs.log_error(log_message) else: logs.log_warn(log_message)即退出码 0 记普通日志退出码 1致命 Python 异常记错误其余记警告。run.py的循环中还注册了atexit钩子在进程退出时停止心跳与 Android 心跳run.py。2.4 run_heartbeat.log —— 心跳包装脚本日志作用记录以无限循环方式运行心跳监控器的包装脚本的信息。写入方src/python/bot/startup/run_heartbeat.py模块注释为Heartbeat script wrapperrun_heartbeat.py。main()中调用logs.configure(run_heartbeat)run_heartbeat.py并启动健康检查响应服务run_health_responser_server()随后在while True循环中反复调用heartbeat.py子进程while True: beat_command [beat_interpreter, beat_script_path, str(previous_state), log_filename] try: previous_state subprocess.check_output( beat_command, stderrsubprocess.STDOUT) except subprocess.CalledProcessError as e: logs.log_error(Failed to beat., outpute.output) ...注意run_heartbeat.py的用法是Usage: log file见 run_heartbeat.py它接收bot.log的路径作为参数交由heartbeat.py去监控其修改时间。run.py正是这样拼接命令的run.pyheartbeat_command %s %s %s % (heartbeat_interpreter, heartbeat_script_path, bot_log)2.5 run_fuzzer.log —— Fuzzer 执行日志作用记录 fuzzer 引擎如 libFuzzer、AFL的执行情况当前主要用于监控 fuzzing 引擎的运行。写入方各 fuzzer 引擎的启动器。以 AFL 为例src/clusterfuzz/_internal/bot/fuzzers/afl/launcher.py在main()中调用logs.configure(run_fuzzer)launcher.py之后把语料合并、新单元生成等关键事件写入该日志例如logs.log(Merging corpus.) logs.log(Merge completed successfully.) logs.log(New units added to corpus: %d. % new_units_added, statsstats_getter.stats)排查价值判断 fuzzer 是否真正在运行、语料合并是否成功、是否有新单元产出。2.6 run_testcase.log —— Testcase 执行日志作用记录通用 testcase 的执行信息。写入方src/clusterfuzz/_internal/bot/testcase_manager.py。由于 testcase 可能运行在独立子进程中尤其在 Windows 上子进程不共享父进程的 loggerrun_testcase_and_return_result_in_queue()会重新初始化日志并额外附带testcase_path作为上下文信息testcase_manager.py# Since this is running in its own process, initialize the log handler again. logs.configure(run_testcase, { testcase_path: file_path, })执行过程中会记录资源依赖检测、复现测试、崩溃栈比对等关键步骤例如logs.log(Detected file dependency: %s. % file_path) logs.log(Crash is reproducible.) logs.log(Crash is not reproducible. Crash count: %d/%d. % (...))三、日志系统的实现原理metrics/logs.py所有日志文件都由同一套日志基础设施驱动核心实现在 src/clusterfuzz/_internal/metrics/logs.py。3.1 logger 名称 → 文件路径映射get_logging_config_dict(name)中维护了 logger 名与日志文件的映射logs.pylogging_handler { run_bot: get_handler_config(bot/logs/bot.log, 3), run: get_handler_config(bot/logs/run.log, 1), run_heartbeat: get_handler_config(bot/logs/run_heartbeat.log, 1), heartbeat: get_handler_config(bot/logs/heartbeat.log, 1), run_fuzzer: get_handler_config(bot/logs/run_fuzzer.log, 1), run_testcase: get_handler_config(bot/logs/run_testcase.log, 1), android_heartbeat: get_handler_config(bot/logs/android_heartbeat.log, 1), run_cron: get_handler_config(bot/logs/run_cron.log, 1), }从上表可见除 README 中列出的 6 个文件外源码还额外注册了android_heartbeat.logAndroid 心跳由android_heartbeat.py写入与run_cron.log定时任务由run_cron.py写入说明bot/logs目录的实际产物比 README 列举的略多。3.2 日志轮转与格式get_handler_config()为每个文件配置了logging.handlers.RotatingFileHandlerlogs.pyreturn { class: logging.handlers.RotatingFileHandler, level: logging.INFO, formatter: simple, filename: file_path, maxBytes: max_bytes, backupCount: backup_count, encoding: utf8, }关键参数maxBytes单文件上限本地开发环境_is_local()为0不触发轮转非本地环境为LOCAL_LOG_LIMIT 500000即约 500 KB。backupCount备份份数bot.log保留 3 份历史轮转文件其余日志保留 1 份。encoding统一使用utf8。日志格式%(asctime)s - %(name)s - %(levelname)s - %(message)s即时间 - logger 名 - 级别 - 消息。3.3 消息长度截断emit()在写入前会调用truncate()对超长消息进行中间截断logs.py避免单条日志撑爆文件if len(msg) limit: return msg half limit // 2 return \n.join([ msg[:half], ...%d characters truncated... % (len(msg) - limit), msg[-half:] ])本地文件日志的单条上限为LOCAL_LOG_MESSAGE_LIMIT 100000面向 Stackdriverfluentd的 JSON 日志上限为STACKDRIVER_LOG_MESSAGE_LIMIT 80000。3.4 日志 API 与级别logs.py对外暴露的 API 包括logs.configure(name, extrasNone)初始化指定 logger并安装sys.excepthook以捕获未处理异常logs.pylogs.log(message, levelINFO, **extras)普通信息logs.log_warn(message, **extras)警告logs.log_error(message, **extras)错误同时递增监控指标LOG_ERROR_COUNTlogs.pylogs.log_fatal_and_exit(message, **extras)致命错误并sys.exit(-1)。每个日志条目还自动附带源码位置信息get_source_location()提取调用方文件、行号、函数名便于回溯日志来自哪段代码。四、日志开关与环境变量bot/logs文件日志并非总是启用其行为由环境变量控制见 logs.py环境变量默认值作用LOG_TO_FILETrue是否写入bot/logs/*.log文件LOG_TO_CONSOLE空是否同时输出到控制台通常用于测试LOG_TO_FLUENTDTrue是否通过 fluentd 转发结构化日志LOG_TO_GCP空是否直接使用 Google Cloud LoggingLOG_DIRroot/bot/logs日志目录路径ROOT_DIR—仓库根目录用于拼接日志文件绝对路径_file_logging_enabled()明确指出文件日志默认开启但在 App Engineconfigure_appengine()或 Kubernetesconfigure_k8s()环境下会被禁用因为这两者使用各自专属的日志通道同理fluentd 日志在本地开发环境会被禁用logs.py。五、典型排查场景速查结合各日志的职责可以在遇到问题时快速定位bot 不执行任务先看bot.log——确认它是否停留在获取构建阶段、卡在某个任务或反复出现Error occurred while working on task.再看run.log——确认总控循环是否仍在运行、run_bot.py子进程的退出码是否为 0/1。bot 被判定失联心跳超时看heartbeat.log是否有Killing stale bot (pid ...) which seems to have stuck.同时确认bot.log是否在持续更新心跳依据其修改时间判断 bot 活跃度。fuzzer 不产出看run_fuzzer.log中语料合并Merging corpus.与New units added to corpus的记录确认 fuzzer 是否真正启动。崩溃复现失败/不稳定看run_testcase.log中的依赖检测Detected file dependency与复现结果Crash is reproducible./Crash is not reproducible.记录。日志文件异常增大所有日志均按约 500 KB × backupCount轮转其中bot.log保留 3 份备份其余保留 1 份若发现单文件远超此规模说明运行环境的LOG_TO_FILE配置或本地开发模式maxBytes 0与预期不符。六、小结bot/logs/目录是 ClusterFuzz bot 运行状态的体检表run.log与run_heartbeat.log记录外层包装进程的生命周期bot.log与heartbeat.log反映主任务与存活监控run_fuzzer.log与run_testcase.log则深入到 fuzzer 引擎与 testcase 执行细节。理解这六个文件的写入方分别对应 run.py、run_bot.py、heartbeat.py、run_heartbeat.py、launcher.py、testcase_manager.py以及 logs.py 中的配置机制就能在 bot 集群出问题时快速缩小排查范围。赞分享测试应用安全后端【免费下载链接】clusterfuzzScalable fuzzing infrastructure.项目地址https://gitcode.com/gh_mirrors/clu/clusterfuzz点击查看免费下载相关推荐ClusterFuzz Bot 日志体系完全指南bot/logs 目录日志文件的作用、配置与排查实践ClusterFuzz Bot 日志体系完全指南bot/logs 目录日志文件的作用、配置与排查实践 导读 ClusterFuzz 的可扩展模糊测试scal应用安全后端开发工具ClusterFuzz 结构化日志Structured Logs完全指南上下文字段体系与 GCP 查询实战ClusterFuzz 结构化日志Structured Logs完全指南上下文字段体系与 GCP 查询实战 ClusterFuzz 通过一套统一的结构化元应用安全后端开发工具OneUptime 日志监控器Logs Monitor完全指南基于 OpenTelemetry 的日志告警配置与实现原理OneUptime 日志监控器Logs Monitor完全指南基于 OpenTelemetry 的日志告警配置与实现原理 导读 日志监控器Logs Mo可观测性后端运维前端云原生微服务AI Agent上一篇Salt APT 包管理模块salt.modules.aptpkg完全指南pkg 虚拟模块在 Debian/Ubuntu 上的实现与实战下一篇editor 一文读懂Portable Text 开源文本编辑器完整指南创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑