重点文件 android\system\core\include\log\log.h android\system\core\liblog\log_is_loggable.c
log输出等级判断 log是否输出,在底层log输出模块代码实现中通过level判断
static int __write_to_log_daemon(log_id_t log_id, struct iovec *vec, size_t nr) { ...... if (!__android_log_is_loggable(prio, tag, ANDROID_LOG_VERBOSE)) { return -EPERM; } ...... }log等级定义如下
/* * 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;判断是否输出log的逻辑 判断一个tag对应的log是否输出逻辑
LIBLOG_ABI_PUBLIC int __android_log_is_loggable(int prio, const char *tag, int default_prio) { int logLevel = __android_log_level(tag, default_prio); return logLevel >= 0 && prio >= logLevel; }获取对应tag log输出优先级,默认优先级是ANDROID_LOG_VERBOSE
static int __android_log_level(const char *tag, int default_prio) { /* sizeof() is used on this array below */ static const char log_namespace[] = "persist.log.tag."; static const size_t base_offset = 8; /* skip "persist." */ /* calculate the size of our key temporary buffer */ const size_t taglen = (tag && *tag) ? strlen(tag) : 0; /* sizeof(log_namespace) = strlen(log_namespace) + 1 */ char key[sizeof(log_namespace) + taglen]; /* may be > PROPERTY_KEY_MAX */ char *kp; size_t i; char c = 0; /* * Single layer cache of four properties. Priorities are: * log.tag.<tag> * persist.log.tag.<tag> * log.tag * persist.log.tag * Where the missing tag matches all tags and becomes the * system global default. We do not support ro.log.tag* . */ static char last_tag[PROP_NAME_MAX]; static uint32_t global_serial; /* some compilers erroneously see uninitialized use. !not_locked */ uint32_t current_global_serial = 0; static struct cache tag_cache[2]; static struct cache global_cache[2]; int change_detected; int global_change_detected; int not_locked; strcpy(key, log_namespace); global_change_detected = change_detected = not_locked = lock(); if (!not_locked) { /* * check all known serial numbers to changes. */ for (i = 0; i < (sizeof(tag_cache) / sizeof(tag_cache[0])); ++i) { if (check_cache(&tag_cache[i])) { change_detected = 1; } } for (i = 0; i < (sizeof(global_cache) / sizeof(global_cache[0])); ++i) { if (check_cache(&global_cache[i])) { global_change_detected = 1; } } current_global_serial = __system_property_area_serial(); if (current_global_serial != global_serial) { change_detected = 1; global_change_detected = 1; } } if (taglen) { int local_change_detected = change_detected; if (!not_locked) { if (!last_tag[0] || (last_tag[0] != tag[0]) || strncmp(last_tag + 1, tag + 1, sizeof(last_tag) - 1)) { /* invalidate log.tag.<tag> cache */ for (i = 0; i < (sizeof(tag_cache) / sizeof(tag_cache[0])); ++i) { tag_cache[i].pinfo = NULL; tag_cache[i].c = '\0'; } last_tag[0] = '\0'; local_change_detected = 1; } if (!last_tag[0]) { strncpy(last_tag, tag, sizeof(last_tag)); } } strcpy(key + sizeof(log_namespace) - 1, tag); kp = key; for (i = 0; i < (sizeof(tag_cache) / sizeof(tag_cache[0])); ++i) { struct cache *cache = &tag_cache[i]; struct cache temp_cache; if (not_locked) { temp_cache.pinfo = NULL; temp_cache.c = '\0'; cache = &temp_cache; } if (local_change_detected) { refresh_cache(cache, kp); } if (cache->c) { c = cache->c; break; } kp = key + base_offset; } } switch (toupper(c)) { /* if invalid, resort to global */ case 'V': case 'D': case 'I': case 'W': case 'E': case 'F': /* Not officially supported */ case 'A': case 'S': case BOOLEAN_FALSE: /* Not officially supported */ break; default: /* clear '.' after log.tag */ key[sizeof(log_namespace) - 2] = '\0'; kp = key; for (i = 0; i < (sizeof(global_cache) / sizeof(global_cache[0])); ++i) { struct cache *cache = &global_cache[i]; struct cache temp_cache; if (not_locked) { temp_cache = *cache; if (temp_cache.pinfo != cache->pinfo) { /* check atomic */ temp_cache.pinfo = NULL; temp_cache.c = '\0'; } cache = &temp_cache; } if (global_change_detected) { refresh_cache(cache, kp); } if (cache->c) { c = cache->c; break; } kp = key + base_offset; } break; } if (!not_locked) { global_serial = current_global_serial; unlock(); } switch (toupper(c)) { case 'V': return ANDROID_LOG_VERBOSE; case 'D': return ANDROID_LOG_DEBUG; case 'I': return ANDROID_LOG_INFO; case 'W': return ANDROID_LOG_WARN; case 'E': return ANDROID_LOG_ERROR; case 'F': /* FALLTHRU */ /* Not officially supported */ case 'A': return ANDROID_LOG_FATAL; case BOOLEAN_FALSE: /* FALLTHRU */ /* Not Officially supported */ case 'S': return -1; /* ANDROID_LOG_SUPPRESS */ } return default_prio; }从代码看,源码没有实现单个tag的输出控制,这部分可以根据需求自行实现。源码中实现了对log的全局控制,可以通过设置系统属性
log.tag persist.log.tag属性控制log输出 设置的值为以下不同level的首字母,如ANDROID_LOG_DEBUG,设置为D
ANDROID_LOG_VERBOSE, ANDROID_LOG_DEBUG, ANDROID_LOG_INFO, ANDROID_LOG_WARN, ANDROID_LOG_ERROR, ANDROID_LOG_FATAL, ANDROID_LOG_SILENT例如:
// 输出大于等于ANDROID_LOG_DEBUG等级的log setprop persist.log.tag D