android log

21746 ワード

Android logの重要性は言うまでもなく、私たちが問題を分析する根拠であり、コードを理解する良いアシスタントです.本文は以下の2つの方面からlogに対していくつか簡単な総括をします:1.ログ分類2.ログ印刷制御
1.ログ分類
Androidで印刷されたロゴは以下の種類に分けられます:1.main log 2. sytem log 3. radio log 4. event log 5. kernel log 6. crash log 7. security logこれらのlogはsystem/core/liblog/Logger_ですwrite.c印刷を除去する;分類はLog_id.hでは定義されています.
typedef enum log_id {
  LOG_ID_MIN = 0,

  LOG_ID_MAIN = 0,
  LOG_ID_RADIO = 1,
  LOG_ID_EVENTS = 2,
  LOG_ID_SYSTEM = 3,
  LOG_ID_CRASH = 4,
  LOG_ID_SECURITY = 5,
  LOG_ID_KERNEL = 6, /* place last, third-parties can not use it */

  LOG_ID_MAX
} log_id_t;
#endif

私のAP側の研究開発にとって、よく印刷するロゴは前の4種類です.印刷logで呼び出すクラスはandroidである.util.Log. android.util.Log.JAva内では、よく使われる4中のlogも定義されています.
    /** @hide */ public static final int LOG_ID_MAIN = 0;
    /** @hide */ public static final int LOG_ID_RADIO = 1;
    /** @hide */ public static final int LOG_ID_EVENTS = 2;
    /** @hide */ public static final int LOG_ID_SYSTEM = 3;
    /** @hide */ public static final int LOG_ID_CRASH = 4;

main logを印刷する場合はandroidを直接呼び出します.util.ログの中の方法でいいです.system logを印刷する場合はandroidを呼び出すことができる.util.Slog. event logを印刷する場合はandroidを呼び出すことができます.utile.Eventlog. radio logを印刷する場合はandroidを呼び出すことができます.telephony.Rlog. 上のいくつかのクラスは印刷するカテゴリを書いただけで、もちろん実際の開発ではカスタマイズされたツールlogクラスを包装し、印刷をよりよく制御することができます.
2.Log印刷制御——adbコマンドでpropertyを設定することでlogの印刷を制御できる
logの印刷はリソースを消費し、一部のlogには機密情報が含まれているため、logの印刷を制御する必要がある.
仕事の中でよく見られるlog印刷制御方式は、DBGがtrueやfalseのような比較的簡単な制御である可能性がある.propertyなどによって制御される可能性もあります.これは一般的に私たちが自分でロゴをコントロールする方法で、何も言うことはありません.
......
if (DBG) {
    //log  
}
......

Androidソース生もlogの制御メカニズムを提供しており、このメカニズムを理解した後、adbコマンドを使用してpropertyを設定することでlogの印刷を制御することができます.以下、このメカニズムについて説明します.
androidでもtelephonu.Rlogかandroidかutil.Slogはandroidを通じてutil.logのnative方法Log.println_nativeでlogを印刷します.対応するjniファイルはandroid_util_Log.cpp.android.util.logはまたnativeメソッドisLoggable(String tag,int level)を提供し、このメソッドは現在のlevelのlogが印刷できるかどうかを判断するために使用することができる.isLoggableメソッドandroid_util_Log.cppでの対応方法はandroid_util_Log_isLoggable.
static jboolean android_util_Log_isLoggable(JNIEnv* env, jobject clazz, jstring tag, jint level)
{
    if (tag == NULL) {
        return false;
    }

    const char* chars = env->GetStringUTFChars(tag, NULL);
    if (!chars) {
        return false;
    }

    jboolean result = isLoggable(chars, level);

    env->ReleaseStringUTFChars(tag, chars);
    return result;
}

android_util_Log_isLoggableは関数isLoggableを呼び出しました.この関数は簡単です.
static jboolean isLoggable(const char* tag, jint level) {
    return __android_log_is_loggable(level, tag, ANDROID_LOG_INFO);
}

__android_log_is_loggableはpropertiesです.cで定義されている(関数__android_log_level(const char*tag,size_t len,int default_prio)は、Android N上で/system/core/liblog/log_に定義されているis_loggable.cではAndroid Oがpropertiesに置かれています.cファイル内).
LIBLOG_ABI_PUBLIC int __android_log_is_loggable(int prio, const char* tag,
                                                int default_prio) {
  int logLevel =
      __android_log_level(tag, (tag && *tag) ? strlen(tag) : 0, default_prio);
  return logLevel >= 0 && prio >= logLevel;
}

この関数は呼び出しによって_android_log_レベルはtagが設定したレベルを取得し、デフォルトはANDROID_です.LOG_INFO. 次に見てみましょうandroid_log_levelは、指定したtagに対応するlevelをどのように取得するか.
static int __android_log_level(const char* tag, size_t len, int default_prio) {
  /* sizeof() is used on this array below */
  static const char log_namespace[] = "persist.log.tag.";//property   ,        property。
  static const size_t base_offset = 8; /* skip "persist." */
  /* calculate the size of our key temporary buffer */
  const size_t taglen = tag ? len : 0;
  /* sizeof(log_namespace) = strlen(log_namespace) + 1 */
  char key[sizeof(log_namespace) + taglen];//  key   ,            property,e.x. persist.log.tag.TelecomFramework
  char* kp;
  size_t i;
  char c = 0;
  /*
   * Single layer cache of four properties. Priorities are:
   *    log.tag.
   *    persist.log.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;//    ,                ;          tag。
  static size_t last_tag_len;//    ;        tag   。
  static uint32_t global_serial;//    ;           property       。
  /* some compilers erroneously see uninitialized use. !not_locked */
  uint32_t current_global_serial = 0;
  static struct cache_char tag_cache[2];//    ;         。
  static struct cache_char 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();//     0,     0 。
  //          property       。
  if (!not_locked) {//  lock
    /*
     *  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].cache)) {
        change_detected = 1;
      }
    }
    for (i = 0; i < (sizeof(global_cache) / sizeof(global_cache[0])); ++i) {
      if (check_cache(&global_cache[i].cache)) {
        global_change_detected = 1;
      }
    }

    current_global_serial = __system_property_area_serial();
    if (current_global_serial != global_serial) {
      change_detected = 1;
      global_change_detected = 1;
    }
  }

  //               tag       tag  ,            。
  if (taglen) {
    int local_change_detected = change_detected;
    if (!not_locked) {
      if (!last_tag || !last_tag[0] || (last_tag[0] != tag[0]) ||
          strncmp(last_tag + 1, tag + 1, last_tag_len - 1)) {//last_tag null,       
        /* invalidate log.tag. cache */
        for (i = 0; i < (sizeof(tag_cache) / sizeof(tag_cache[0])); ++i) {
          tag_cache[i].cache.pinfo = NULL;
          tag_cache[i].c = '\0';
        }
        if (last_tag) last_tag[0] = '\0';//     ,     if          
        local_change_detected = 1;
      }
      if (!last_tag || !last_tag[0]) {//      tag     tag     
        if (!last_tag) {
          last_tag = calloc(1, len + 1);
          last_tag_len = 0;
          if (last_tag) last_tag_len = len + 1;
        } else if (len >= last_tag_len) {
          last_tag = realloc(last_tag, len + 1);
          last_tag_len = 0;
          if (last_tag) last_tag_len = len + 1;
        }
        if (last_tag) {
          strncpy(last_tag, tag, len);
          last_tag[len] = '\0';
        }
      }
    }
    strncpy(key + sizeof(log_namespace) - 1, tag, len);//  key   "persist.log.tag.*"
    key[sizeof(log_namespace) - 1 + len] = '\0';

    //        tag     property:"persist.log.tag.*"  "log.tag.*"
    kp = key;
    for (i = 0; i < (sizeof(tag_cache) / sizeof(tag_cache[0])); ++i) {
      struct cache_char* cache = &tag_cache[i];
      struct cache_char temp_cache;

      if (not_locked) {
        temp_cache.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;//
    }
  }

  //      "persist.log",      。
  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';//  key   "persist.log"

      kp = key;
      for (i = 0; i < (sizeof(global_cache) / sizeof(global_cache[0])); ++i) {
        struct cache_char* cache = &global_cache[i];
        struct cache_char temp_cache;

        if (not_locked) {
          temp_cache = *cache;
          if (temp_cache.cache.pinfo != cache->cache.pinfo) { /* check atomic */
            temp_cache.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();
  }

  //      property  ,     log level。
  switch (toupper(c)) {
    /* clang-format off */
    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 */
    /* clang-format on */
  }
  return default_prio;//          property,       。
}

上の関数が完成した論理は、本当にpropertyを実行して検索したのは関数refresh_です.Cache(struct cache_char*cache,const char*key)、この関数は言わないで、比較的簡単です.
私たちは普段仕事中で、いくつかのロゴを印刷するために、携帯電話/dataディレクトリの下でpush localをよく使います.prop.
adb root
adb remount
adb push local.prop /data/local.prop
adb shell chmod 644 /data/local.prop
adb shell chown root.root /data/local.prop
adb reboot

local.propファイルの内容は以下の通りです.
log.tag.Telecom=VERBOSE
log.tag.RIL-SIM=VERBOSE
log.tag.SIMRecords=VERBOSE
log.tag.DCT=VERBOSE

携帯電話が再起動するとinitは/data/localをロードします.propでは、ファイルの内容がpropertyとしてロードされます.したがって、log文が実行されると、_android_log_レベルは、対応するpropertyを調べ、設定したレベルを取得できます.実はpushファイルは少し煩雑で、上の分析によると、adbコマンドでproperty制御log出力を設定することができます(有効でなければ、携帯電話を再起動してから試してもいいです).使用できるproperty nameは次のとおりです.
  • log.tag.* (起動時に失われる)
  • persist.log.tag.*
  • persist.log(優先度は前の2つより低いので、前の2つが設定されている場合は、後の2つは有効ではありません)
  • [例1]Telecomをtagとしたすべてのlogを出力するには、以下の方法を用いることができる.adb shell setprop log.tag.Telecom Vまたはadb shell setprop persist.log.tag.Telecom V
    [例2]以下のコマンドは、そのためのlogを出力することができますが、使用は推奨されません.logが多すぎると表示に不便をもたらす可能性があります.adb shell setprop persist.log V