• AndroidT(13) -- logger_write 库实现解析(四)


    1.概览

      经过上及章节的分析可见,不论是那种风格的LOG接口实现,最终调用的都是logger_write库中过提供的一些列接口来处理log内容的。下面就来看看 logger_write 库的实现,本章节会随着后续涉及接口的不断增加而动态更新。

    2. __android_log_write_log_message 的实现

    __android_log_write_log_message
        //code 1
        if (log_message->tag == nullptr)
            log_message->tag = GetDefaultTag().c_str();
        //code 2
        logger_function(log_message)
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    2.1 log tag确定 – code 1

      一般使用Android 的LOG系统前,会先定义LOG_TAG宏的。但是如果没定义的话,总不能null吧,所以Android LOG系统贴心的使用程序的简称作为在用户没有设置LOG_TAG场景下的TAG值,对于代码如下

    static const char* getprogname() {
    #ifdef _WIN32
        ...
    #else
      return program_invocation_short_name;
    #endif
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

      我们不关心Win32的场景,所以返回的就是 program_invocation_short_name 了,它的描述见man手册

    NAME
           program_invocation_name, program_invocation_short_name - obtain name used to invoke calling program
    
    SYNOPSIS
           #define _GNU_SOURCE         /* See feature_test_macros(7) */
           #include 
    
           extern char *program_invocation_name;
           extern char *program_invocation_short_name;
    
    DESCRIPTION
           program_invocation_name  contains the name that was used to invoke the calling program.  This is the same as the value of argv[0] in main(), with the difference that the scope of program_invocation_name is global.
           program_invocation_short_name contains the basename component of name that was used to invoke the calling program.  That is, it is the same value  as program_invocation_name, with all text up to and including the final slash (/), if any, removed.
           These variables are automatically initialized by the glibc run-time startup code.
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    2.2 logger_function – __android_log_logd_logger

      本章使用默认值 __android_log_logd_logger 分析即可,因为只有它是往logd的几个buffer送的。看下它的实现

    //system\logging\liblog\logger_write.cpp
    static __android_logger_function logger_function = __android_log_logd_logger;
    void __android_log_logd_logger(const struct __android_log_message* log_message) {
        //code 1
        int buffer_id = log_message->buffer_id == LOG_ID_DEFAULT ? LOG_ID_MAIN : log_message->buffer_id;
        //code 2
        struct iovec vec[3];
        vec[0].iov_base =
            const_cast<unsigned char*>(reinterpret_cast<const unsigned char*>(&log_message->priority));
        vec[0].iov_len = 1;
        vec[1].iov_base = const_cast<void*>(static_cast<const void*>(log_message->tag));
        vec[1].iov_len = strlen(log_message->tag) + 1;
        vec[2].iov_base = const_cast<void*>(static_cast<const void*>(log_message->message));
        vec[2].iov_len = strlen(log_message->message) + 1;
        //code 3
        write_to_log(static_cast<log_id_t>(buffer_id), vec, 3);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

      code 1 处就是上一章所说的为什么 LOG_ID_DEFAULT 和 LOG_ID_MAIN 等价的原因。
      code 2 则是根据 __android_log_message 数据来组装 iovec 数据
      code 3 则开始处理 iovec 类型化的 __android_log_message 数据,下面看看它的实现

    2.3 write_to_log 实现

    //system\logging\liblog\logger_write.cpp
    static int write_to_log(log_id_t log_id, struct iovec* vec, size_t nr)
        //code 1
        struct timespec ts
        clock_gettime(CLOCK_REALTIME, &ts);
        //code 2
        ret = check_log_uid_permissions();
        //code 3
        ret = LogdWrite(log_id, &ts, vec, nr);
        PmsgWrite(log_id, &ts, vec, nr);
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

      code 1 用于获取real时间,注意了这个时间是包括休眠时间的,详细信息可以参考之前的博文。
      code 2 进行用户权限的检查。
      code 3 则是处理 iovec化的log数据了,其中我们只关注 LogdWrite

    2.4 LogdWrite 实现

    //system\logging\liblog\logd_writer.cpp
    int LogdWrite(log_id_t logId, struct timespec* ts, struct iovec* vec, size_t nr) {
        //code 1
        LogdSocket& logd_socket =
            logId == LOG_ID_SECURITY ? LogdSocket::BlockingSocket() : LogdSocket::NonBlockingSocket();
        logd_socket.sock()
    
        //code 2
        header.tid = gettid();
        header.realtime.tv_sec = ts->tv_sec;
        header.realtime.tv_nsec = ts->tv_nsec;
        header.id = logId;
        //code 3 
        newVec[0].iov_base = (unsigned char*)&header;
        newVec[0].iov_len = sizeof(header);
        for (payloadSize = 0, i = headerLength; i < nr + headerLength; i++) {
            newVec[i].iov_base = vec[i - headerLength].iov_base;
            payloadSize += newVec[i].iov_len = vec[i - headerLength].iov_len;
        }
        //code 4
        ret = TEMP_FAILURE_RETRY(writev(logd_socket.sock(), newVec, i));
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

      LogdWrite 主要做了如下几个事
        1)和logd的 logdw 建立链接,成功连接上后则会获得到一个fd,这样就可以使用它和远端服务 logd进行通讯了,这块和socket变成无异。

    LogdSocket::NonBlockingSocket
        static LogdSocket logd_socket(false/*blocking*/);
            blocking_(blocking)
        return logd_socket;
    
    • 1
    • 2
    • 3
    • 4

          从名字上也很清楚的知道,读非 LOG_ID_SECURITY 的log是不等待的。
        2)构造头信息,头信息记录LOG所来自的线程ID、LOG产生的时间戳(real time)、记录LogId及要写入的buffer类型。它的定义如下

    //system/logging/liblog/include/private/android_logger.h
    /* Header Structure to logd, and second header for pstore */
    typedef struct __attribute__((__packed__)) {
      uint8_t id;
      uint16_t tid;
      log_time realtime;
    } android_log_header_t;
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

        3)插入log的头信息到log内容,最终的结构图入下

    newVec[0] --> android_log_header_t
    newVec[1] --> log_message->priority
    newVec[2] --> log_message->tag
    newVec[3] --> log_message->message
    
    • 1
    • 2
    • 3
    • 4

        4)将上面重组的数据 newVec 通过socket发送到logd,logd 接收到log内容后就开始进行处理了。至于 logd 的工作请见下回分解~

  • 相关阅读:
    物联网AI MicroPython学习之语法 TIMER硬件定时器
    java计算机毕业设计酒店管理系统源码+mysql数据库+系统+lw文档+部署
    使用docker创建redis实例、主从复制、哨兵集群
    GBase8s数据库INTO TEMP 子句创建临时表来保存查询结果。
    使用OpenFlow和Ryu控制器实现网络交换机的软件定义网络(SDN)控制
    [附源码]Python计算机毕业设计Django的个人理财系统
    # vue3 ref 和 reactive 函数
    【优化调度】基于粒子群算法实现机组发电调度附matlab代码
    java语法基础-java面向对象编程-包的定义及使用
    go实现分布式锁
  • 原文地址:https://blog.csdn.net/u014787262/article/details/128174375