[譯] 提升日誌質量的 5 大技巧

原文連接5 Techniques to Improve Your Server Logginghtml

如下爲譯文服務器


最近涌現出各類各樣能幫助你理解日誌的新工具,有相似 Scribe、Logstash 這樣的開源項目,也有相似 Splunk 的預付費工具,還有託管服務如 SumoLogic 和 PaperTrail。這些工具的共同點是對日誌數據進行清洗,在大量日誌中提取一些更有價值的文件。網絡

但有一件事這些工具卻心有餘而力不足,由於它們徹底依賴你實際投入的日誌數據,而如何保證數據的質量和數量則須要用戶自行完成。所以,在關鍵時刻,若是你須要基於部分或者遺漏日誌作代碼調試時,事情可能會變得很是棘手。多線程

爲了減小這種狀況發生,在這裏分享五個建議,在你記錄日誌時最好能銘記於心:架構

1. 你好,個人(線程)名字是

正如 Ringo,線程名稱這個屬性是 Java 中最被低估的方法之一。其緣由是線程名稱大部分是描述性的。然而問題一樣出如今這裏,相似人們本身,起名時一般會被賦予必定的意義。而在多線程日誌中,線程名一樣揮着關鍵做用。一般狀況下,大多很多天志框架會記錄當前所調用的線程名稱。可悲的是,咱們一般會看到 http-nio-8080-exec-3 這種名字,簡單地由線程池或容器進行分配。app

出於某種緣由,咱們曾不止一次地聽過這種誤解——線程名稱是不可變的。與之相反,在日誌中,線程名稱佔據基本主要地位,你應該確保能正確使用。好比將它與具體情境結合起來,例如 Servlet 的名字、任務相關,或者一些動態語境如用戶或消息 ID。框架

這樣的話,代碼接口應該是這樣:分佈式

Thread.currentThread().setName(ProcessTask.class.getName() + 「: 「+ message.getID);

更先進的版本將被加載到當前線程的線程局部變量,配置 log appender,並自動將其添加到日誌條目。工具

當多個線程寫入服務器日誌,但你須要集中在單一線程上時,這將會很是有用。若是你在一個分佈式 /SOA 環境下運行,更能看到它得天獨厚的優點。this

2. 分佈式的標識符

在 SOA 或消息驅動的架構,任務執行極可能跨多臺機器。當處理這種環境下的故障時,鏈接相關機器和它們的狀態將是瞭解具體狀況的關鍵。大多很多天志分析器會將這些日誌信息分組,假設你爲它們提供了惟一標識,它們即可以做爲實際日誌消息的一部分。

從設計的角度出發,這意味着,從進入系統到操做完成,每個入站操做應該有其惟一的 ID 對應。請注意,一個持久的標識符,如用戶 ID 可能不是一個好容器。在記錄日誌文件的過程當中,用戶可能有多個操做,這將使得隔離特定流更加困難。UUIDs 多是個不錯的選擇。它的值能夠被加載到實際線程名稱或者做爲 TLS-thread 的局部儲存器。

3. 不要使用文本+驅動器,不要日誌+循環

不少時候,你會看到一段代碼在緊密的循環中運行,並執行相應的日誌操做。基本假設是,該代碼運行的次數是有限的。

極可能運行狀況很是良好。可是當代碼獲得意外輸入時,循環可能並不會中斷。在這種狀況下,你不僅是處理一個無限循環「雖然這樣已經很糟糕了」,你正在處理的代碼正將無限量的數據寫到磁盤或網絡。

在單機場景中它可能會形成一臺服務器崩潰,而在分佈式場景中,被影響的則是整個集羣。所以若是可能,不要在緊密循環中記錄日誌。捕獲錯誤時,這一點尤爲如此。

下面這個例子,記錄了一個 while 循環中的異常:

void read() {
while (hasNext()) {
try {
readData();
} catch {Exception e) {
// this isn’t recommend
logger.error(「error reading data「, e);
}
}
}

若是 readData 拋出異常,而 hasNext 返回值爲 true,這裏將會寫入無限量的日誌數據。要解決這個問題的方法是確保不會記錄這一切:

void read() {
int exceptionsThrown = 0;
while (hasNext()) {
try {
readData();
} catch {Exception e) {
if (exceptionsThrown < THRESHOLD) {
logger.error(「error reading data", e);
exceptionsThrown++;
} else {
// Now the error won’t choke the system.
}
}
}
}

另外一種方法是從循環中移除日誌記錄,並保存第一/最後一個異常對象並在其它地方記錄。

4. 未捕獲的處理程序

Westeros 有最後一道防護牆,而你有 Thread.uncaughtExceptionHandler。所以,儘可能使用它們。若是沒有安裝這些處理程序,在異常拋出時,你只能得到不多有價值的上下文,同時你也沒法控制在結束以前你已經將其記錄,並肯定記錄的位置。

請注意,即便在未捕獲的異常處理程序,看起來你沒有任何辦法訪問線程中(已終止)的任何變量,你仍然能夠得到實際線程對象的引用。若是你堅持# 1步,你仍然會獲得一個有意義的thread.getName()值可記錄。

5. 捕獲外部調用

每當調用一個外部的 API, JVM 異常的概率將大大增長。這包括 Web 服務、 HTTP、 DB、 文件系統、操做系統和任何其餘 JNI 調用。認真對待每一個調用,由於它隨時會爆炸 「它頗有可能發生在一樣的點」。

大多數狀況下,外部 API 故障的緣由是意外輸入,日誌中對其記錄是修復代碼的關鍵。

在這一點上,你能夠選擇不記錄錯誤,只是拋出異常也能夠。在這種狀況下,只要收集到調用的相關參數,並將其解析爲異常錯誤信息。

只要確保異常被捕獲並記錄在更高級別的堆棧調用便可。

try {
return s3client.generatePresignedUrl(request);
} catch (Exception e) {
String err = String.format(「Error generating request: %s bucket: %s key: %s. method: %s", request, bucket, path, method);
log.error(err, e); //you can also throw a nested exception here with err instead.
}

原文連接:5 Techniques to Improve Your Server Logging

本文系 OneAPM 工程師編譯整理。想閱讀更多技術文章,請訪問 OneAPM 官方博客

相關文章
相關標籤/搜索