日志模块
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_messages
、pending_startup_messages
两种类型的日志链表
日志类型
严重性等级
Tor 的日志共分为以下几种类型debug
、info
、notice
、warn
、err
除此之外,还有crit
、alert
、emerg
几种未用到的日志类型(程序中不会并且不能使用他们)
在大部分函数中,对应的参数为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 环境、不是系统输出,则会输出到日志文件对应的文件中,如果写出失败,则会标记该日志文件可能已经无效