DFX框架之hilog源码详解:user space层(一)
DFX框架之hilog源码详解:user space层(一) 相关源码 frameworks\libhilog\base\hilog_base.c 该源码对应实现图中1的功能 实现原理解释 该部分源码是日志记录框架的基础功能,能够将日志消息通过 Unix 域套接字发送到日志服务端。并且支持多线程环境下的日志记录,并提供了灵活的日
·
相关源码
frameworks\libhilog\base\hilog_base.c
该源码对应实现图中1的功能
实现原理解释
该部分源码是日志记录框架的基础功能,能够将日志消息通过 Unix 域套接字发送到日志服务端。并且支持多线程环境下的日志记录,并提供了灵活的日志级别控制和可变参数日志打印功能。
相关API解释
-
SendMessage
static int SendMessage(HilogMsg *header, const char *tag, uint16_t tagLen, const char *fmt, uint16_t fmtLen) { // 创建一个 Unix 域套接字,类型为 SOCK_DGRAM(数据报套接字),非阻塞模式(SOCK_NONBLOCK),并且在执行 exec 时关闭套接字(SOCK_CLOEXEC) int socketFd = TEMP_FAILURE_RETRY(socket(AF_UNIX, SOCKET_TYPE, 0)); if (socketFd < 0) { dprintf(ERROR_FD, "HiLogBase: Can't create socket! Errno: %d\n", errno); return socketFd; } // 连接到 hilogd 服务器的 Unix 域套接字地址 long int result = TEMP_FAILURE_RETRY(connect(socketFd, (const struct sockaddr *)(&SOCKET_ADDR), sizeof(SOCKET_ADDR))); if (result < 0) { dprintf(ERROR_FD, "HiLogBase: Can't connect to server. Errno: %d\n", errno); if (socketFd >= 0) { close(socketFd); } return result; } // 获取当前时间(实时时间和单调时间),用于填充日志消息头中的时间戳字段 struct timespec ts = {0}; (void)clock_gettime(CLOCK_REALTIME, &ts); struct timespec ts_mono = {0}; (void)clock_gettime(CLOCK_MONOTONIC, &ts_mono); header->tv_sec = (uint32_t)(ts.tv_sec); header->tv_nsec = (uint32_t)(ts.tv_nsec); header->mono_sec = (uint32_t)(ts_mono.tv_sec); header->len = sizeof(HilogMsg) + tagLen + fmtLen; header->tagLen = tagLen; // 准备要发送的数据块,包括日志消息头、标签和日志内容 struct iovec vec[LOG_LEN] = {0}; vec[0].iov_base = header; // 0 : index of hos log header vec[0].iov_len = sizeof(HilogMsg); // 0 : index of hos log header vec[1].iov_base = (void *)((char *)(tag)); // 1 : index of log tag vec[1].iov_len = tagLen; // 1 : index of log tag vec[2].iov_base = (void *)((char *)(fmt)); // 2 : index of log content vec[2].iov_len = fmtLen; // 2 : index of log content // 使用 writev 函数发送日志消息到 hilogd 服务器 int ret = TEMP_FAILURE_RETRY(writev(socketFd, vec, LOG_LEN)); if (socketFd >= 0) { close(socketFd); } return ret; }
-
HiLogBasePrintArgs
int HiLogBasePrintArgs( const LogType type, const LogLevel level, const unsigned int domain, const char *tag, const char *fmt, va_list ap) { // 定义一个缓冲区用于存储格式化后的日志消息 char buf[MAX_LOG_LEN] = {0}; // 使用 vsnprintfp_s 函数将可变参数列表格式化为字符串,并存储在 buf 中 vsnprintfp_s(buf, MAX_LOG_LEN, MAX_LOG_LEN - 1, true, fmt, ap); // 计算标签字符串的实际长度,并确保不超过最大标签长度 size_t tagLen = strnlen(tag, MAX_TAG_LEN - 1); // 计算格式化后的日志消息的实际长度,并确保不超过最大日志长度 size_t logLen = strnlen(buf, MAX_LOG_LEN - 1); // 定义一个 HilogMsg 结构体变量用于存储日志消息的头部信息 HilogMsg header = {0}; // 设置日志消息的类型 header.type = type; // 设置日志消息的日志级别 header.level = level; #ifndef __RECV_MSG_WITH_UCRED_ // 获取并设置当前进程的进程 ID header.pid = getprocpid(); #endif // 获取并设置当前线程的线程 ID header.tid = (uint32_t)(gettid()); // 设置日志消息的域 ID header.domain = domain; // 调用 SendMessage 函数将日志消息发送到 hilogd 服务器 return SendMessage(&header, tag, tagLen + 1, buf, logLen + 1); }
-
HiLogBasePrint
/** * 打印日志的核心函数,支持可变参数列表。 * * @param type 日志类型,例如系统日志、应用日志等。 * @param level 日志级别,例如 DEBUG、INFO、WARN、ERROR 等。 * @param domain 日志域 ID,用于区分不同的日志来源或模块。 * @param tag 日志标签,通常用于标识日志的来源或模块名称。 * @param fmt 格式化字符串,类似于 printf 的格式化规则。 * @param ... 根据 fmt 提供的实际参数,用于填充格式化字符串中的占位符。 * @return 成功时返回写入的字节数,失败时返回负值。 */ int HiLogBasePrint(LogType type, LogLevel level, unsigned int domain, const char *tag, const char *fmt, ...) { // 检查当前日志是否可以被记录 if (!HiLogBaseIsLoggable(domain, tag, level)) { return -1; // 如果不可记录,直接返回错误码 } int ret; // 用于存储返回值 va_list ap; // 定义可变参数列表 // 初始化可变参数列表 va_start(ap, fmt); // 调用核心日志打印函数,将日志信息发送到日志服务器 ret = HiLogBasePrintArgs(type, level, domain, tag, fmt, ap); // 清理可变参数列表 va_end(ap); return ret; // 返回日志打印的结果 }
-
HiLogBaseIsLoggable
/** * @brief 检查是否可以记录指定域、标签和级别的日志。 * * @param domain 日志域标识符。 * @param tag 日志标签字符串。 * @param level 日志级别。 * @return 如果可以记录日志,返回 true;否则返回 false。 * * 该函数用于判断给定的日志域、标签和级别是否满足记录日志的条件。 * 具体条件包括: * - 日志级别必须在 LOG_LEVEL_MIN 和 LOG_LEVEL_MAX 之间。 * - 标签字符串不能为 NULL。 */ bool HiLogBaseIsLoggable(unsigned int domain, const char *tag, LogLevel level) { // 检查日志级别是否在有效范围内,并且标签字符串是否为 NULL if ((level <= LOG_LEVEL_MIN) || (level >= LOG_LEVEL_MAX) || tag == NULL) { return false; } return true; }
总结
该部分源码提供了以下几个功能
-
日志记录:通过 Unix 域套接字将日志消息发送到日志服务器(
hilogd
) -
日志过滤:根据日志级别范围和标签有效性进行判断,确保只有符合条件的日志才会被记录
-
日志消息结构化:结构体存储日志消息的头部信息,包括时间戳、进程 ID、线程 ID、日志级别、日志域等元数据
-
日志相关常量:如最大日志长度(
MAX_LOG_LEN
)、最大标签长度(MAX_TAG_LEN
)、日志块数量(LOG_LEN
)等
更多推荐
所有评论(0)