相关源码

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)等

Logo

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

更多推荐