日志模块

Tor笔记目录索引

Tor报错模块位于src/lib/log/文件夹

该模块除去对日志输出统一封装外,还实现了针对多线程的日志输出。由于在多线程中,每个线程可能在同一时间输出,如果不对日志输出进行控制,很可能会导致日志混在一起。

该模块用到了smartlist模块,用于实现动态数组。因此,smartlist 模块以及更早的子系统都不能使用日志模块进行日志输出

子系统结构

子系统结构定义解释

const subsys_fns_t sys_logging = {
  .name = "log",
  .supported = true,
  /* Logging depends on threads, approx time, raw logging, and security.
   * Most other lib modules depend on logging. */
  .level = -90,
  .initialize = subsys_logging_initialize,
  .shutdown = subsys_logging_shutdown,
};

初始化子系统

初始化过程中,会初始化互斥锁、以及pending_cb_messagespending_startup_messages两种类型的日志链表

日志类型

严重性等级

Tor 的日志共分为以下几种类型debuginfonoticewarnerr
除此之外,还有critalertemerg几种未用到的日志类型(程序中不会并且不能使用他们)
在大部分函数中,对应的参数为int severity

日志输出模块

在当前版本共有 30 个模块,因此区分一条日志由哪个模块输出也非常重要。比如LD_CRYPTO说明是加密模块输出的日志
在大部分函数中,对应的参数为log_domain_mask_t domain

日志文件

在这里,日志文件不一定就是一个文件,也有可能是一个回调函数
更贴切的理解是:对应的日志等级、模块的处理方法。

如果对应类型的日志要输出的一个文件,则会输出到对应的文件;否则则会则会执行对应的回调函数执行更复杂的操作。

Tor 中,所有的日志文件存储在logfiles变量中

typedef struct logfile_t {
  struct logfile_t *next; // 链表尾指针
  char *filename; // 输出文件名
  int fd; // 输出文件号
  int seems_dead; // 该文件是否可能失效
  int needs_close; // 输出流结束是否需要关闭文件
  int is_temporary; // 是否需要在初始化子系统后关闭文件(临时的日志文件)
  int is_syslog; // 是否是系统日志输出
  int is_android; // 是否是安卓环境
  char *android_tag; // 安卓日志标签
  log_callback callback; // 日志回调
  log_severity_list_t *severities;// 日志对应的模块
} logfile_t;

输出日志

在外部模块中,将使用下面这些函数输出日志:

  • log_debug(domain, args, ...)
  • log_info(domain, args, ...)
  • log_notice(domain, args, ...)
  • log_warn(domain, args, ...)
  • log_err(domain, args, ...)

这些函数将会调用void log_fn_(),对可变参数进行处理,并最终调用logv()执行具体的日志逻辑

flush_pending_log_callbacks

void flush_pending_log_callbacks(void)

该函数会将pending_cb_messages中所有的日志全部处理,并清空等待列表
其中的每一条日志,都会遍历logfiles找到对应的告警级别、模块信息执行对应的回调函数

logv

void logv(int severity, log_domain_mask_t domain, const char *funcname, const char *suffix, const char *format, struct __va_list_tag *ap)

这是各种输出日志的具体执行的函数,首先会调用flush_pending_log_callbacks执行所有的回调日志

接着如果queue_startup_messages未满,则会将当前日志插入进去,并判断该日志在logfiles对应的告警级别、模块信息对应的文件是否需要输出,如果需要则输出到对应的文件中
如果对应的日志存在回调函数,并且该日志不带有LD_NOCB标志时,则会将其插入至pending_cb_messages,否则会直接执行对应的回调函数
如果对应的日志文件既不存在回调函数、不是 Android 环境、不是系统输出,则会输出到日志文件对应的文件中,如果写出失败,则会标记该日志文件可能已经无效

参考资料

  1. Tor源码文件分析 -- Log
  2. Tor 源码分析第三部分—— 日志设施与智能链表