注册 登录  
 加关注
   显示下一条  |  关闭
温馨提示!由于新浪微博认证机制调整,您的新浪微博帐号绑定已过期,请重新绑定!立即重新绑定新浪微博》  |  关闭

民主与科学

独立之人格,自由之思想

 
 
 

日志

 
 

Logcat源码分析(五)  

2011-10-15 10:53:37|  分类: 深入研究 |  标签: |举报 |字号 订阅

  下载LOFTER 我的照片书  |
四、输出日志设备文件的内容
        从前面的分析中看出,最终日志设备文件内容的输出是通过logcat.cpp文件的printNextEntry函数进行的:
static void printNextEntry(log_device_t* dev) {  
    maybePrintStart(dev);  
    if (g_printBinary) {  
        printBinary(&dev->queue->entry);  
    } else {  
        processBuffer(dev, &dev->queue->entry);  
    }  
    skipNextEntry(dev);  
}  
 g_printBinarytrue时,以二进制方式输出日志内容到指定的文件中:
void printBinary(struct logger_entry *buf)  
{  
    size_t size = sizeof(logger_entry) + buf->len;  
    int ret;  
      
    do {  
        ret = write(g_outFD, buf, size);  
    } while (ret < 0 && errno == EINTR);  
}  
g_printBinary为false的情况,调用processBuffer进一步处理 
static void processBuffer(log_device_t* dev, struct logger_entry *buf)  
{  
    int bytesWritten = 0;  
    int err;  
    AndroidLogEntry entry;  
    char binaryMsgBuf[1024];  
  
    if (dev->binary) {  
        err = Android_log_processBinaryLogBuffer(buf, &entry, g_eventTagMap,  
                binaryMsgBuf, sizeof(binaryMsgBuf));  
        //printf(">>> pri=%d len=%d msg='%s'\n",   
        //    entry.priority, entry.messageLen, entry.message);   
    } else {  
        err = Android_log_processLogBuffer(buf, &entry);  
    }  
    if (err < 0) {  
        goto error;  
    }  
  
    if (Android_log_shouldPrintLine(g_logformat, entry.tag, entry.priority)) {  
        if (false && g_devCount > 1) {  
            binaryMsgBuf[0] = dev->label;  
            binaryMsgBuf[1] = ' ';  
            bytesWritten = write(g_outFD, binaryMsgBuf, 2);  
            if (bytesWritten < 0) {  
                perror("output error");  
                exit(-1);  
            }  
        }  
  
        bytesWritten = Android_log_printLogLine(g_logformat, g_outFD, &entry);  
  
        if (bytesWritten < 0) {  
            perror("output error");  
            exit(-1);  
        }  
    }  
  
    g_outByteCount += bytesWritten;  
  
    if (g_logRotateSizeKBytes > 0   
        && (g_outByteCount / 1024) >= g_logRotateSizeKBytes  
    ) {  
        rotateLogs();  
    }  
  
error:  
    //fprintf (stderr, "Error processing record\n");   
    return;  
}
dev->binary为true,日志记录项是二进制形式,不同于我们在《Logger详解》文中提到的常规格式:
        logger_entry + priority + tag + msg
        这里我们不关注这种情况,有兴趣的读者可以自已分析,Android_log_processBinaryLogBuffer函数定义在system/core/liblog/logprint.c文件中,它的作用是将一条二进制形式的日志记录转换为ASCII形式,并保存在entry参数中,它的原型为
 
/** 
 * Convert a binary log entry to ASCII form. 
 * 
 * For convenience we mimic the processLogBuffer API.  There is no 
 * pre-defined output length for the binary data, since we're free to format 
 * it however we choose, which means we can't really use a fixed-size buffer 
 * here. 
 */  
int Android_log_processBinaryLogBuffer(struct logger_entry *buf,  
    AndroidLogEntry *entry, const EventTagMap* map, char* messageBuf,  
    int messageBufLen);  
        通常情况下,dev->binary为false,调用Android_log_processLogBuffer函数将日志记录由logger_entry格式转换为AndroidLogEntry格式。logger_entry格式在在 Logger详解一文中已经有详细描述,这里不述。
AndroidLogEntry结构体定义在system/core/include/cutils/logprint.h中:
typedef struct AndroidLogEntry_t {  
    time_t tv_sec;  
    long tv_nsec;  
    Android_LogPriority priority;  
    pid_t pid;  
    pthread_t tid;  
    const char * tag;  
    size_t messageLen;  
    const char * message;  
} AndroidLogEntry;  
Android_LogPriority是一个枚举类型,定义在system/core/include/android/log.h文件中:
/* 
 * Android log priority values, in ascending priority order. 
 */  
typedef enum Android_LogPriority {  
    Android_LOG_UNKNOWN = 0,  
    Android_LOG_DEFAULT,    /* only for SetMinPriority() */  
    Android_LOG_VERBOSE,  
    Android_LOG_DEBUG,  
    Android_LOG_INFO,  
    Android_LOG_WARN,  
    Android_LOG_ERROR,  
    Android_LOG_FATAL,  
    Android_LOG_SILENT,     /* only for SetMinPriority(); must be last */  
} Android_LogPriority;  
 
Android_log_processLogBuffer定义在system/core/liblog/logprint.c文件中:
 
/** 
 * Splits a wire-format buffer into an AndroidLogEntry 
 * entry allocated by caller. Pointers will point directly into buf 
 * 
 * Returns 0 on success and -1 on invalid wire format (entry will be 
 * in unspecified state) 
 */  
int Android_log_processLogBuffer(struct logger_entry *buf,  
                                 AndroidLogEntry *entry)  
{  
    size_t tag_len;  
  
    entry->tv_sec = buf->sec;  
    entry->tv_nsec = buf->nsec;  
    entry->priority = buf->msg[0];  
    entry->pid = buf->pid;  
    entry->tid = buf->tid;  
    entry->tag = buf->msg + 1;  
    tag_len = strlen(entry->tag);  
    entry->messageLen = buf->len - tag_len - 3;  
    entry->message = entry->tag + tag_len + 1;  
  
    return 0;  
}  
        结合logger_entry结构体中日志项的格式定义(logger_entry + priority + tag + msg),这个函数很直观,不再累述。
        调用完Android_log_processLogBuffer函数后,日志记录的具体信息就保存在本地变量entry中了,接着调用android_log_shouldPrintLine函数来判断这条日志记录是否应该输出。
        在分析Android_log_shouldPrintLine函数之前,我们先了解数据结构AndroidLogFormat,这个结构体定义在system/core/liblog/logprint.c文件中:
struct AndroidLogFormat_t {  
    Android_LogPriority global_pri;  
    FilterInfo *filters;  
    AndroidLogPrintFormat format;  
};  
AndroidLogPrintFormatFilterInfo_t也是定义在system/core/liblog/logprint.c文件中:
/**
 * Returns FORMAT_OFF on invalid string
 */
AndroidLogPrintFormat android_log_formatFromString(const char * formatString)
{
    static AndroidLogPrintFormat format;

    if (strcmp(formatString, "brief") == 0) format = FORMAT_BRIEF;
    else if (strcmp(formatString, "process") == 0) format = FORMAT_PROCESS;
    else if (strcmp(formatString, "tag") == 0) format = FORMAT_TAG;
    else if (strcmp(formatString, "thread") == 0) format = FORMAT_THREAD;
    else if (strcmp(formatString, "raw") == 0) format = FORMAT_RAW;
    else if (strcmp(formatString, "time") == 0) format = FORMAT_TIME;
    else if (strcmp(formatString, "threadtime") == 0) format = FORMAT_THREADTIME;
    else if (strcmp(formatString, "long") == 0) format = FORMAT_LONG;
    else format = FORMAT_OFF;

    return format;
typedef struct FilterInfo_t
    char *mTag;  
    Android_LogPriority mPri;  
    struct FilterInfo_t *p_next;  
} FilterInfo;  
 因此,可以看出,AndroidLogFormat结构体定义了日志过滤规范。在logcat.c文件中,定义了变量
static AndroidLogFormat * g_logformat;  
 这个变量是在main函数里面进行分配的:
g_logformat = Android_log_format_new();  
  在main函数里面,在分析logcat命令行参数时,会将g_logformat进行初始化,有兴趣的读者可以自行分析。
 回到Android_log_shouldPrintLine函数中,它定义在system/core/liblog/logprint.c文件中:
/** 
 * returns 1 if this log line should be printed based on its priority 
 * and tag, and 0 if it should not 
 */  
int Android_log_shouldPrintLine (  
        AndroidLogFormat *p_format, const char *tag, android_LogPriority pri)  
{  
    return pri >= filterPriForTag(p_format, tag);  
}
这个函数判断在p_format中根据tag值,找到对应的pri值,如果返回来的pri值小于等于参数传进来的pri值,那么就表示这条日志记录可以输出。我们来看filterPriForTag函数的实现        
static Android_LogPriority filterPriForTag(  
        AndroidLogFormat *p_format, const char *tag)  
{  
    FilterInfo *p_curFilter;  
  
    for (p_curFilter = p_format->filters  
            ; p_curFilter != NULL  
            ; p_curFilter = p_curFilter->p_next  
    ) {  
        if (0 == strcmp(tag, p_curFilter->mTag)) {  
            if (p_curFilter->mPri == Android_LOG_DEFAULT) {  
                return p_format->global_pri;  
            } else {  
                return p_curFilter->mPri;  
            }  
        }  
    }  
  
    return p_format->global_pri;  
}  
        如果在p_format中找到与tag值对应的filter,并且该filter的mPri不等于Android_LOG_DEFAULT,那么就返回该filter的成员变量mPri的值;其它情况下,返回p_format->global_pri的值。
  评论这张
 
阅读(1708)| 评论(0)
推荐 转载

历史上的今天

评论

<#--最新日志,群博日志--> <#--推荐日志--> <#--引用记录--> <#--博主推荐--> <#--随机阅读--> <#--首页推荐--> <#--历史上的今天--> <#--被推荐日志--> <#--上一篇,下一篇--> <#-- 热度 --> <#-- 网易新闻广告 --> <#--右边模块结构--> <#--评论模块结构--> <#--引用模块结构--> <#--博主发起的投票-->
 
 
 
 
 
 
 
 
 
 
 
 
 
 

页脚

网易公司版权所有 ©1997-2017