<ruby id="bdb3f"></ruby>

    <p id="bdb3f"><cite id="bdb3f"></cite></p>

      <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
        <p id="bdb3f"><cite id="bdb3f"></cite></p>

          <pre id="bdb3f"></pre>
          <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

          <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
          <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

          <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                <ruby id="bdb3f"></ruby>

                ??碼云GVP開源項目 12k star Uniapp+ElementUI 功能強大 支持多語言、二開方便! 廣告
                從前面4.6.3小節的內容可以知道,Logcat工具是通過調用源代碼文件logcat.cpp中的函數printNextEntry來輸出日志記錄的。 在前面的4.6.2小節中還提到,如果全局變量g_printBinary的值等于1,那么就說明以二進制格式來輸出日志記錄。這時候函數printNextEntry就調用函數printBinary來處理。 **system/core/logcat/logcat.cpp** ~~~ 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); } ~~~ 由于不需要對日志記錄進行解析,即不用將它的優先級、標簽以及內容解析出來,因此,函數printBinary的實現很簡單,可直接調用函數write將它輸出到文件或者打印到標準輸出中。 如果全局變量g_printBinary的值等于0,那么就要以文本格式來輸出日志記錄。這時候函數printNextEntry就調用函數processBuffer來處理。 **system/core/logcat/logcat.cpp** ~~~ 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; } ~~~ 前面得到的日志記錄只是一塊二進制數據,保存在一個logger_entry結構體中,因此,函數processBuffer在將它輸出之前,首先要將它的內容轉換為一個AndroidLogEntry結構體。AndroidLogEntry結構體描述了一條日志記錄的寫入時間、優先級、標簽、內容以及寫入進程ID。如果日志記錄的類 型是二進制格式的,即是類型為events的日志記錄,那么函數processBuffer就會調用函數android_log_processBinaryLogBuffer對它進行解析;否則,就調用函數android_log_processLogBuffer對它進行解析。日志記錄解析完成之后,函數processBuffer最后就調用函數android_log_printLogLine將它輸出到文件或者打印到標準輸出中。這三個函數的實現我們在后面再詳細分析,現在先完成對函數processBuffer的分析。 一條日志記錄解析完成之后,Logcat工具就得到了它的優先級和標簽。由于在啟動Logcat工具時,可能設置了日志記錄輸出過濾器,因此,函數processBuffer就需要調用函數android_log_shouldPrintLine判斷一條日志記錄是否能夠輸出。在前面的4.6.2小節中提到,Logcat工具的日志記錄輸出過濾器列表保存在全局變量g_logformat中,因此,函數processBuffer就以它作為參數來調用函數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); } ~~~ 函數filterPriForTag首先在參數p_format中檢查是否對日志記錄標簽tag設置了輸出過濾器。如果設置了,那么函數filterPriForTag就會返回它的過濾優先級。只有這個過濾優先級低于即將要輸出的日志記錄的優先級時,該日志記錄才可以輸出。 函數filterPriForTag的實現如下所示。 **system/core/liblog/logprint.c** ~~~ 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; } ~~~ 第6行到第17行的for循環檢查在參數p_format的日志記錄輸出過濾器列表中是否為日志記錄標簽tag設置了輸出過濾器。如果設置了,就返回該日志記錄標簽所對應的優先級;否則,就返回全局設置的日志記錄輸出優先級,即返回參數p_format的成員變量global_pri。另外,如果我們為日志記錄標簽tag設置了輸出過濾器,但是將該輸出過濾器的優先級設置為ANDROID_LOG_DEFAULT,那么函數filterPriForTag實際上返回的是全局設置的日志記錄輸出優先級,如第12行代碼所示。 回到函數processBuffer中,如果函數android_log_shouldPrintLine的返回值為true,那么它就會先調用函數android_log_printLogLine輸出日志記錄,然后再將輸出的日志記錄的字節數bytesWritten增加到全局變量g_outByteCount中,表示到目前為止,一共往文件g_outFD中輸出了多少個字節的日志記錄。一旦輸出到文件g_outFD中的日志記錄的字節數大于全局變量g_logRotateSizeKBytes的值時,那么Logcat就會將接下來的其他日志記錄輸出到另外一個文件中。全局變量g_logRotateSizeKBytes的值是由Logcat工具的啟動選項r來指定的,如果沒有指定該選項,那么全局變量g_logRotateSizeKBytes的值就會等于0,表示將所有的日志記錄都輸出到同一個文件中。如果全局變量g_logRotateSizeKBytes的值大于0,那么總共可以用來作為日志記錄輸出文件的個數就由Logcat工具的選項n來指定。如果沒有指定選項n,那么默認就有四個日志記錄輸出文件。第44行調用函數rotateLogs來設置下一個日志記錄輸出文件,它的實現如下所示。 **system/core/logcat/logcat.cpp** ~~~ static void rotateLogs() { int err; // Can't rotate logs if we're not outputting to a file if (g_outputFileName == NULL) { return; } close(g_outFD); for (int i = g_maxRotatedLogs ; i > 0 ; i--) { char *file0, *file1; asprintf(&file1, "%s.%d", g_outputFileName, i); if (i - 1 == 0) { asprintf(&file0, "%s", g_outputFileName); } else { asprintf(&file0, "%s.%d", g_outputFileName, i - 1); } err = rename (file0, file1); if (err < 0 && errno != ENOENT) { perror("while rotating log files"); } free(file1); free(file0); } g_outFD = openLogFile (g_outputFileName); if (g_outFD < 0) { perror ("couldn't open output file"); exit(-1); } g_outByteCount = 0; } ~~~ 假設在啟動Logcat工具時,通過選項f來指定日志記錄輸出文件為“logfile”,并且通過選項n來指定日志記錄輸出文件個數為3,那么函數rotateLogs就分別將日志記錄輸出文件設置為logfile、logfile.1、logfile.2和logfile.3。 > 這四個文件是循環使用的,即當文件logfile.3的大小達到限定的值之后,Logcat工具就將接下來的日志記錄重新輸出到文件logfile、logfile.1和logfile.2中。依此類推,當文件logfile.2的大小再次達到限定的值之后,最后又將新的日志記錄輸出到文件logfile.3中。 分析完成函數processBuffer的實現之后,接下來我們開始分析函數android_log_processLogBuffer、android_log_processBinaryLogBuffer和android_log_printLogLine的實現。 首先分析函數android_log_processLogBuffer的實現,它是用來解析類型為main、system和radio的日志記錄的,如下所示。 **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; } ~~~ 日志記錄的優先級字段的長度為1個字節,保存在logger_entry 結構體buf內部的緩沖區msg的第一個字節中。日志記錄的標簽字段從logger_entry 結構體buf內部的緩沖區msg的第二個字節開始,一直到后面的‘\0’字符為止,因此,第19行可以通過調用函數strlen來計算它的長度。logger_entry 結構體buf內部的緩沖區msg 剩余的字節即為日志記錄的內容字段,它的長度等于緩沖區的總長度減去日志記錄標簽的長度,再減去1個字節的日志記錄優先級,以及2個字符串結束字符‘\0’,其中一個是日志記錄標簽字段的,另一個是日志記錄內容字段的。 接下來,我們繼續分析函數android_log_processBinaryLogBuffer的實現,它是用來解析類型為events的日志記錄的,我們分段來閱讀。 **system/core/liblog/logprint.c** ~~~ /** * 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) { size_t inCount; unsigned int tagIndex; const unsigned char* eventData; entry->tv_sec = buf->sec; entry->tv_nsec = buf->nsec; entry->priority = ANDROID_LOG_INFO; entry->pid = buf->pid; entry->tid = buf->tid; /* * Pull the tag out. */ eventData = (const unsigned char*) buf->msg; inCount = buf->len; if (inCount < 4) return -1; tagIndex = get4LE(eventData); eventData += 4; inCount -= 4; if (map != NULL) { entry->tag = android_lookupEventTag(map, tagIndex); } else { entry->tag = NULL; } ~~~ 第17行到21行代碼用來初始化AndroidLogEntry 結構體entry中的日志記錄寫入時間、寫入進程ID以及優先級等信息。從前面4.1小節中的圖4-3可以知道,類型為events的日志記錄是沒有優先級這個字段的,但是為了和其他類型的日志記錄統一處理,Logcat工具將它們的優先級設置為ANDROID_LOG_INFO。 第26行得到logger_entry 結構體buf內部的緩沖區msg的地址,并且保存在變量eventData中。由于類型為events的日志記錄的標簽是使用一個整數值來描述的,并且保存在logger_entry 結構體buf內部的緩沖區msg的前面4個字節中,因此第30行就調用函數get4LE將它獲取回來,并且保存在變量tagIndex中。 函數get4LE的實現如下所示。 **system/core/liblog/logprint.c** ~~~ /* * Extract a 4-byte value from a byte stream. */ static inline uint32_t get4LE(const uint8_t* src) { return src[0] | (src[1] << 8) | (src[2] << 16) | (src[3] << 24); } ~~~ 它只是簡單地將緩沖區src前面4個字節的內容組合在一起形成一個整數,然后返回給調用者。 回到函數android_log_processBinaryLogBuffer中,接下來第34行檢查參數map的值是否為NULL。如果是,就直接將AndroidLogEntry 結構體entry的成員變量tag設置為NULL,表示當前正在處理的一個日志記錄的標簽值沒有對應的描述字符串;否則,第35行就會調用函數android_lookupEventTag在參數map中找到與日志記錄標簽值tagIndex對應的描述字符串。 > 參數map指向一個EventTagMap結構體,它是Logcat工具在啟動時,通過解析目標設備上的/system/etc/event-log-tags文件得到的。因此,Logcat工具就可以通過它來找到與日志記錄標簽值tagIndex對應的描述字符串。 數android_lookupEventTag的實現如下所示。 **system/core/liblog/event_tag_map.c** ~~~ /* * Look up an entry in the map. * * The entries are sorted by tag number, so we can do a binary search. */ const char* android_lookupEventTag(const EventTagMap* map, int tag) { int hi, lo, mid; lo = 0; hi = map->numTags-1; while (lo <= hi) { int cmp; mid = (lo+hi)/2; cmp = map->tagArray[mid].tagIndex - tag; if (cmp < 0) { /* tag is bigger */ lo = mid + 1; } else if (cmp > 0) { /* tag is smaller */ hi = mid - 1; } else { /* found */ return map->tagArray[mid].tagStr; } } return NULL; } ~~~ 在前面的4.6.2小節中介紹Logcat工具的初始化過程時提到,Logcat工具解析完成目標設備上的/system/etc/event-log-tags文之后,就得到了一個日志記錄標簽描述表,表中描述了與每一個日志標簽所對應的描述字符串,最后會按照標簽值從小到大的順序保存在EventTagMap結構體內部EventTag結構體數組tagArray中。因此,函數android_lookupEventTag就可以使用二分法從這個數組中得到與日志記錄標簽值tag對應的描述字符串,并且將它返回給調用者。 回到函數android_log_processBinaryLogBuffer中,繼續往下執行。 **system/core/liblog/logprint.c** ~~~ /* * If we don't have a map, or didn't find the tag number in the map, * stuff a generated tag value into the start of the output buffer and * shift the buffer pointers down. */ if (entry->tag == NULL) { int tagLen; tagLen = snprintf(messageBuf, messageBufLen, "[%d]", tagIndex); entry->tag = messageBuf; messageBuf += tagLen+1; messageBufLen -= tagLen+1; } /* * Format the event log data into the buffer. */ char* outBuf = messageBuf; size_t outRemaining = messageBufLen-1; /* leave one for nul byte */ int result; result = android_log_printBinaryEvent(&eventData, &inCount, &outBuf, &outRemaining); ~~~ 如果前面沒有在EventTagMap結構體map中找到即將要輸出的日志記錄的標簽值描述字符串,即第44行的if語句為true,那么第47行就將該標簽值作為要輸出的日志記錄的標簽值描述字符串。接著第59行調用函數android_log_printBinaryEvent繼續解析日志記錄的內容字段。 要輸出的日志記錄解析完成之后,輸出結果就保存在緩沖區messageBuf中。在調用函數android_log_printBinaryEvent來解析要輸出的日志記錄的內容字段之前,第57行會在緩沖區messageBuf的后面保留一個字節,用來保存一個字符串結束字符‘\0’。這樣,Logcat工具就可以將緩沖區messageBuf的內容作為一個字符串輸出到文件或者打印到標準輸出中。 函數android_log_printBinaryEvent的實現如下所示。 **system/core/liblog/logprint.c** ~~~ /* * Recursively convert binary log data to printable form. * * This needs to be recursive because you can have lists of lists. * * If we run out of room, we stop processing immediately. It's important * for us to check for space on every output element to avoid producing * garbled output. * * Returns 0 on success, 1 on buffer full, -1 on failure. */ static int android_log_printBinaryEvent(const unsigned char** pEventData, size_t* pEventDataLen, char** pOutBuf, size_t* pOutBufLen) { const unsigned char* eventData = *pEventData; size_t eventDataLen = *pEventDataLen; char* outBuf = *pOutBuf; size_t outBufLen = *pOutBufLen; unsigned char type; size_t outCount; int result = 0; if (eventDataLen < 1) return -1; type = *eventData++; eventDataLen--; //fprintf(stderr, "--- type=%d (rem len=%d)\n", type, eventDataLen); switch (type) { case EVENT_TYPE_INT: /* 32-bit signed int */ { int ival; if (eventDataLen < 4) return -1; ival = get4LE(eventData); eventData += 4; eventDataLen -= 4; outCount = snprintf(outBuf, outBufLen, "%d", ival); if (outCount < outBufLen) { outBuf += outCount; outBufLen -= outCount; } else { /* halt output */ goto no_room; } } break; case EVENT_TYPE_LONG: /* 64-bit signed long */ { long long lval; if (eventDataLen < 8) return -1; lval = get8LE(eventData); eventData += 8; eventDataLen -= 8; outCount = snprintf(outBuf, outBufLen, "%lld", lval); if (outCount < outBufLen) { outBuf += outCount; outBufLen -= outCount; } else { /* halt output */ goto no_room; } } break; case EVENT_TYPE_STRING: /* UTF-8 chars, not NULL-terminated */ { unsigned int strLen; if (eventDataLen < 4) return -1; strLen = get4LE(eventData); eventData += 4; eventDataLen -= 4; if (eventDataLen < strLen) return -1; if (strLen < outBufLen) { memcpy(outBuf, eventData, strLen); outBuf += strLen; outBufLen -= strLen; } else if (outBufLen > 0) { /* copy what we can */ memcpy(outBuf, eventData, outBufLen); outBuf += outBufLen; outBufLen -= outBufLen; goto no_room; } eventData += strLen; eventDataLen -= strLen; break; } case EVENT_TYPE_LIST: /* N items, all different types */ { unsigned char count; int i; if (eventDataLen < 1) return -1; count = *eventData++; eventDataLen--; if (outBufLen > 0) { *outBuf++ = '['; outBufLen--; } else { goto no_room; } for (i = 0; i < count; i++) { result = android_log_printBinaryEvent(&eventData, &eventDataLen, &outBuf, &outBufLen); if (result != 0) goto bail; if (i < count-1) { if (outBufLen > 0) { *outBuf++ = ','; outBufLen--; } else { goto no_room; } } } if (outBufLen > 0) { *outBuf++ = ']'; outBufLen--; } else { goto no_room; } } break; default: fprintf(stderr, "Unknown binary event type %d\n", type); return -1; } bail: *pEventData = eventData; *pEventDataLen = eventDataLen; *pOutBuf = outBuf; *pOutBufLen = outBufLen; return result; no_room: result = 1; goto bail; } ~~~ 函數android_log_printBinaryEvent正好是在前面4.5小節中介紹的四個日志記錄寫入函數android_util_EventLog_writeEvent_Integer、android_util_EventLog_writeEvent_Long、android_util_EventLog_writeEvent_String和android_util_EventLog_writeEvent_Array的逆操作,因此,讀者可以對照這四個函數來分析函數android_log_printBinaryEvent的實現。簡單來說,如果日志記錄內容只有一個值,那么函數android_log_printBinaryEvent就會根據它的類型(整數、長整數和字符串)將它從輸入緩沖區pEventData取回來,并且保存到輸出緩沖區outBuf中。當日志記錄的內容是一個列表時,函數android_log_printBinaryEvent就會通過遞歸調用自己來依次遍歷列表中的數據。如果得到的數據是一個整數、長整數或者字符串,那么處理方式就與前面一樣;否則,當得到的數據又是一個列表時,函數android_log_printBinaryEvent就會繼續對這個子列表執行同樣的遍歷操作。 函數android_log_printBinaryEvent執行完成之后,如果返回值等于0,那么就說明日志記錄解析成功;否則,就說明解析過程中出現了問題,其中,返回值等于-1表示日志記錄緩沖區有誤,返回值等于1表示輸出緩沖區outBuf沒有足夠的空間來容納日志記錄的內容。 回到函數android_log_processBinaryLogBuffer中,繼續往下執行。 **system/core/liblog/logprint.c** ~~~ if (result < 0) { fprintf(stderr, "Binary log entry conversion failed\n"); return -1; } else if (result == 1) { if (outBuf > messageBuf) { /* leave an indicator */ *(outBuf-1) = '!'; } else { /* no room to output anything at all */ *outBuf++ = '!'; outRemaining--; } /* pretend we ate all the data */ inCount = 0; } /* eat the silly terminating '\n' */ if (inCount == 1 && *eventData == '\n') { eventData++; inCount--; } if (inCount != 0) { fprintf(stderr, "Warning: leftover binary log data (%d bytes)\n", inCount); } /* * Terminate the buffer. The NUL byte does not count as part of * entry->messageLen. */ *outBuf = '\0'; entry->messageLen = outBuf - messageBuf; assert(entry->messageLen == (messageBufLen-1) - outRemaining); entry->message = messageBuf; return 0; } ~~~ 變量result的值是調用函數android_log_printBinaryEvent得到的返回值。如果它的值小于0,即第61行的if語句為true,就說明日志記錄內容有誤,因此,函數android_log_processBinaryLogBuffer就不用進一步對它進行處理了,第63行直接返回錯誤碼-1;如果它的值等于1,即第64行的if語句為true,那么就說明日志記錄輸出緩沖區messageBuf空間不足,這時候函數android_log_processBinaryLogBuffer就在緩沖區messageBuf的最后一個字節上寫入一個號‘!’來說明此種情況。 變量inCount表示原始的日志記錄緩沖區中還有多少個字節未處理。如果它的值等于1,并且剩余未處理的字符為‘\n’,那么就說明要輸出的是一條正常的日志記錄,因為每一條類型為events的日志記錄總是以‘\n’字符結束的。如果要輸出的日志記錄不是這種情況,那么第84行和第85行就會輸出一條警告信息來說明此種情況。 前面提到,函數android_log_processBinaryLogBuffer在調用函數android_log_printBinaryEvent來解析要輸出的日志記錄的內容字段之前,會在日志記錄輸出緩沖區messageBuf的后面保留一個字節,這個字節就是用來寫入一個字符串結束符號‘\0’的,如第92行代碼所示。這樣,Logcat工具就可以把緩沖區messageBuf的內容當作一個字符串來使用,即可以直接將它以文本的形式輸出到文件或者打印到標準輸出中。 最后,我們分析函數android_log_printLogLine的實現,它是日志記錄輸出過程中的最后一個步驟,它的實現如下所示。 **system/core/liblog/logprint.c** ~~~ /** * Either print or do not print log line, based on filter * * Returns count bytes written */ int android_log_printLogLine( AndroidLogFormat *p_format, int fd, const AndroidLogEntry *entry) { int ret; char defaultBuffer[512]; char *outBuffer = NULL; size_t totalLen; outBuffer = android_log_formatLogLine(p_format, defaultBuffer, sizeof(defaultBuffer), entry, &totalLen); if (!outBuffer) return -1; do { ret = write(fd, outBuffer, totalLen); } while (ret < 0 && errno == EINTR); if (ret < 0) { fprintf(stderr, "+++ LOG: write failed (errno=%d)\n", errno); ret = 0; goto done; } if (((size_t)ret) < totalLen) { fprintf(stderr, "+++ LOG: write partial (%d of %d)\n", ret, (int)totalLen); goto done; } done: if (outBuffer != defaultBuffer) { free(outBuffer); } return ret; } ~~~ 第17行調用函數android_log_formatLogLine來格式化要輸出的日志記錄,并且將最后結果保存在緩沖區defaultBuffer中。函數android_log_formatLogLine的實現比較簡單,它將要輸出的日志記錄的內容格式化為“<PREFIX> +MESSAGE+<SUFFIX>”的形式。其中,<PREFIX>和<SUFFIX>的內容取決于Logcat工具在初始化時所設置的日志記錄輸出格式,即參數p_format的內容;而MESSAGE描述了日志記錄的內容字段。有了輸出結果緩沖區defaultBuffer之后,第23行到第25行的while循環就調用函數write將它輸出到文件描述符fd所描述的目標文件中。 在前面的4.6.2小節中介紹Logcat工具的初始化過程時提到,如果在啟動Logcat時,指定了選項v,那么就可以指定日志記錄的輸出格式。選項v后面所跟的參數可以為“brief”、“process”、“tag”、“thread”、“raw”、“time”、“threadtime”或者“long”。每一個參數都對應一個AndroidLogPrintFormat枚舉值,分別為FORMAT_BRIEF、FORMAT_PROCESS、FORMAT_TAG、FORMAT_THREAD、FORMAT_RAW、FORMAT_TIME、FORMAT_THREADTIME和FORMAT_LONG。 下面我們就介紹每一個AndroidLogPrintFormat枚舉值所對應的日志記錄輸出格式。 (1)FORMAT_BRIEF:<PREFIX>和<SUFFIX>的內容分別為“<priority>/<tag>(<pid>): ”和“\n”。 (2)FORMAT_PROCESS:<PREFIX>和<SUFFIX>的內容分別為“<priority>(<pid>) ”和“ (<tag>)\n”。 (3)FORMAT_TAG:<PREFIX>和<SUFFIX>的內容分別為“<priority>/(<tag>): ”和“\n”。 (4)FORMAT_THREAD:<PREFIX>和<SUFFIX>的內容分別為“<priority>(<pid>:<tid>) ”和“\n”。 (5)FORMAT_RAW:<PREFIX>和<SUFFIX>的內容分別為空值和“\n”。 (6)FORMAT_TIME:<PREFIX>和<SUFFIX>的內容分別為“<sec>.<nsec> <priority>/<tag>(<pid>): ”和“\n”。 (7)FORMAT_THREADTIME:<PREFIX>和<SUFFIX>的內容分別為 “<sec>.<nsec> <pid> <tid><priority> <tag>: ”和“\n”。 (8)FORMAT_LONG:<PREFIX>和<SUFFIX>的內容分別為“[ <sec>.<nsec> <pid>:<tid><priority>/<tag> ]”和“\n\n”。 例如,假設應用程序(進程ID為396)中調用Java接口android.util.Log來往Logger日志驅動程序中寫入了以下一條日志記錄: `android.util.Log.i("LogTag", "Log Content.");` 并且假設Logcat工具在啟動時,被指定以FORMAT_BRIEF格式來輸出該日志記錄的內容,那么Logcat工具從Logger日志驅動程序中讀出這條日志記錄之后,就會打印出以下的輸出: `I/LogTag(396): Log Content.` 至此,日志記錄的輸出過程就分析完成了。
                  <ruby id="bdb3f"></ruby>

                  <p id="bdb3f"><cite id="bdb3f"></cite></p>

                    <p id="bdb3f"><cite id="bdb3f"><th id="bdb3f"></th></cite></p><p id="bdb3f"></p>
                      <p id="bdb3f"><cite id="bdb3f"></cite></p>

                        <pre id="bdb3f"></pre>
                        <pre id="bdb3f"><del id="bdb3f"><thead id="bdb3f"></thead></del></pre>

                        <ruby id="bdb3f"><mark id="bdb3f"></mark></ruby><ruby id="bdb3f"></ruby>
                        <pre id="bdb3f"><pre id="bdb3f"><mark id="bdb3f"></mark></pre></pre><output id="bdb3f"></output><p id="bdb3f"></p><p id="bdb3f"></p>

                        <pre id="bdb3f"><del id="bdb3f"><progress id="bdb3f"></progress></del></pre>

                              <ruby id="bdb3f"></ruby>

                              哎呀哎呀视频在线观看