通过属性控制log输出
程序员文章站
2024-02-27 23:47:15
...
通过属性控制log输出
涉及代码目录
android\system\core\include\log
android\system\core\liblog
重点文件
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