MDC介紹 -- 一種多執行緒下日誌管理實踐方式
一:MDC介紹
MDC(Mapped Diagnostic Context,對映除錯上下文)是 log4j 和 logback 提供的一種方便在多執行緒條件下記錄日誌的功能。某些應用程式採用多執行緒的方式來處理多個使用者的請求。在一個使用者的使用過程中,可能有多個不同的執行緒來進行處理。典型的例子是 Web 應用伺服器。當用戶訪問某個頁面時,應用伺服器可能會建立一個新的執行緒來處理該請求,也可能從執行緒池中複用已有的執行緒。在一個使用者的會話存續期間,可能有多個執行緒處理過該使用者的請求。這使得比較難以區分不同使用者所對應的日誌。當需要追蹤某個使用者在系統中的相關日誌記錄時,就會變得很麻煩。
一種解決的辦法是採用自定義的日誌格式,把使用者的資訊採用某種方式編碼在日誌記錄中。這種方式的問題在於要求在每個使用日誌記錄器的類中,都可以訪問到使用者相關的資訊。這樣才可能在記錄日誌時使用。這樣的條件通常是比較難以滿足的。MDC 的作用是解決這個問題。
MDC 可以看成是一個與當前執行緒繫結的雜湊表,可以往其中新增鍵值對。MDC 中包含的內容可以被同一執行緒中執行的程式碼所訪問。當前執行緒的子執行緒會繼承其父執行緒中的 MDC 的內容。當需要記錄日誌時,只需要從 MDC 中獲取所需的資訊即可。MDC 的內容則由程式在適當的時候儲存進去。對於一個 Web 應用來說,通常是在請求被處理的最開始儲存這些資料。
為了驗證MDC的正確性,我寫了個簡單的多執行緒程式,程式碼如下:
- import org.apache.log4j.MDC;
- publicclass ThreadTest extends Thread {
- privateint i ;
- public ThreadTest(){
- }
- public ThreadTest(int i){
- this.i = i;
- }
- publicvoid run(){
- System.out.println(++i);
-
MDC.put("username"
- for (int j = 0; j < 100; j++) {
- System.out.println("aaa" + i);
- if(j==10){
- try {
- this.sleep(1000);
- } catch (InterruptedException e) {
- e.printStackTrace();
- }
- }
- }
- System.out.println("run: " + i + " " + MDC.get("username"));
- }
- publicstaticvoid main(String args[]) throws InterruptedException{
- ThreadTest t1 = new ThreadTest(1);
- t1.start();
- ThreadTest t2 = new ThreadTest(2);
- t2.start();
- }
- }
執行結果如下:
- 2
- 3
- aaa3
- aaa3
- aaa2
- aaa3
- aaa2
- aaa3
- aaa2
- aaa3
- aaa2
- aaa3
- aaa2
- aaa3
- aaa2
- aaa2
- aaa2
- aaa2
- aaa2
- run: 22
- aaa3
- aaa3
- aaa3
- run: 33
從結果中可以看出:程序t1與t2在MDC中的值是沒有相互影響的,確保了多程序下程序之間在MDC存放的值是沒有相互的影響的或者說是無關的(程序t1在MDC中的username的鍵值為2;程序t2在MDC中的username的鍵值為3)。
分析:
MDC類put方法:
檢視文字列印- publicstaticvoid put(String key, Object o)
- {
- mdc.put0(key, o);
- }
- privatevoid put0(String key, Object o)
- {
- if (this.java1) {
- return;
- }
- Hashtable ht = (Hashtable)((ThreadLocalMap)this.tlm).get();
- if (ht == null) {
- ht = new Hashtable(7);
- ((ThreadLocalMap)this.tlm).set(ht);
- }
- ht.put(key, o);
- }
結合類java.lang.ThreadLocal<T>及Thread類可以知道,MDC中的put方法其實就是講鍵值對放入一個Hashtable物件中,然後賦值給當前執行緒的ThreadLocal.ThreadLocalMap物件,即threadLocals,這保證了各個執行緒的在MDC鍵值對的獨立性。
下邊為java.lang.ThreadLocal<T>的部分程式碼:
檢視文字列印- publicclass ThreadLocal<T> {
- publicvoidset(T value) {
- Thread t = Thread.currentThread();
- ThreadLocalMap map = getMap(t);
- if (map != null)
- map.set(this, value);
- else
- createMap(t, value);
- }
- public T get() {
- Thread t = Thread.currentThread();
- ThreadLocalMap map = getMap(t);
- if (map != null) {
- ThreadLocalMap.Entry e = map.getEntry(this);
- if (e != null)
- return (T)e.value;
- }
- return setInitialValue();
- }
- ThreadLocalMap getMap(Thread t) {
- return t.threadLocals;
- }
- }
Thread類的部分程式碼:
檢視文字列印- publicclass Thread implements Runnable {
- ThreadLocal.ThreadLocalMap threadLocals = null;
- ......................
- .........................
- staticclass ThreadLocalMap { //ThreadLocalMap為Thread類的內部類
- }
- }
二:日誌聚合與分析(摘自:http://www.ibm.com/developerworks/cn/java/j-lo-practicelog/index.html) -- 此部分是為了方便自己查閱,從參考資料中摘出來放到這裡的
在程式中正確的地方輸出合適的日誌訊息,只是合理使用日誌的第一步。日誌記錄的真正作用在於當有問題發生時,能夠幫助開發人員很快的定位問題所在。不過一個實用的系統通常由很多個不同的部分組成。這其中包括所開發的程式本身,也包括所依賴的第三方應用程式。以一個典型的電子商務網站為例,除了程式本身,還包括所依賴的底層作業系統、應用伺服器、資料庫、HTTP 伺服器和代理伺服器和快取等。當一個問題發生時,真正的原因可能來自程式本身,也可能來自所依賴的第三方程式。這就意味著開發人員可能需要檢查不同伺服器上不同應用程式的日誌來確定真正的原因。
日誌聚合的作用就在於可以把來自不同伺服器上不同應用程式產生的日誌聚合起來,存放在單一的伺服器上,方便進行搜尋和分析。在日誌聚合方面,已經有不少成熟的開源軟體可以很好的滿足需求。本文中要介紹的是 logstash,一個流行的事件和日誌管理開源軟體。logstash 採用了一種簡單的處理模式:輸入 -> 過濾器 -> 輸出。logstash 可以作為代理程式安裝到每臺需要收集日誌的機器上。logstash 提供了非常多的外掛來處理不同型別的資料輸入。典型的包括控制檯、檔案和 syslog 等;對於輸入的資料,可以使用過濾器來進行處理。典型的處理方式是把日誌訊息轉換成結構化的欄位;過濾之後的結果可以被輸出到不同的目的地,比如 ElasticSearch、檔案、電子郵件和資料庫等。
Logstash 在使用起來很簡單。從官方網站下載 jar 包並執行即可。在執行時需要指定一個配置檔案。配置檔案中定義了輸入、過濾器和輸出的相關配置。清單 9 給出了一個簡單的 logstash 配置檔案的示例。
清單 9. logstash 配置檔案示例
檢視文字列印- input {
- file {
- path => [ "/var/log/*.log", "/var/log/messages", "/var/log/syslog" ]
- type => 'syslog'
- }
- }
- output {
- stdout {
- debug => true
- debug_format => "json"
- }
- }
清單 9 中定義了 logstash 收集日誌時的輸入(input)和輸出(output)的相關配置。輸入型別是檔案(file)。每種型別輸入都有相應的配置。對於檔案來說,需要配置的是檔案的路徑。對每種型別的輸入,都需要指定一個型別(type)。該型別用來區分來自不同輸入的記錄。程式碼中使用的輸出是控制檯。配置檔案完成之後,通過“java -jar logstash-1.1.13-flatjar.jar agent -f logstash-simple.conf”就可以啟動 logstash。
在日誌分析中,比較重要的是結構化的資訊。而日誌資訊通常只是一段文字,其中的不同欄位表示不同的含義。不同的應用程式產生的日誌的格式並不相同。在分析時需要關注的是其中包含的不同欄位。比如 Apache 伺服器會產生與使用者訪問請求相關的日誌。在日誌中包含了訪問者的各種資訊,包括 IP 地址、時間、HTTP 狀態碼、響應內容的長度和 User Agent 字串等資訊。在 logstash 收集到日誌資訊之後,可以根據一定的規則把日誌資訊中包含的資料提取出來並命名。logstash 提供了 grok 外掛可以完成這樣的功能。grok 基於正則表示式來工作,同時提供了非常多的常用型別資料的提取模式,如清單 10 所示。
清單 10. 使用 grok 提取日誌記錄中的內容
點選檢視程式碼清單
在經過上面 grok 外掛的提取之後,Apache 訪問日誌被轉換成包含欄位 client、method、request、status、bytes 和 useragent 的格式化資料。可以根據這些欄位來進行搜尋。這對於分析問題和進行統計都是很有幫助的。
當日志記錄通過 logstash 進行收集和處理之後,通常會把這些日誌記錄儲存到資料庫中進行分析和處理。目前比較流行的方式是儲存到 ElasticSearch 中,從而可以利用 ElasticSearch 提供的索引和搜尋能力來分析日誌。已經有不少的開源軟體在 ElasticSearch 基礎之上開發出相應的日誌管理功能,可以很方便的進行搜尋和分析。本文中介紹的是 Graylog2。
Graylog2 由伺服器和 Web 介面兩部分組成。伺服器負責接收日誌記錄並儲存到 ElasticSearch 之中。Web 介面則可以檢視和搜尋日誌,並提供其他的輔助功能。logstash 提供了外掛 gelf,可以把 logstash 收集和處理過的日誌記錄傳送到 Graylog2 的伺服器。這樣就可以利用 Graylog2 的 Web 介面來進行查詢和分析。只需要把清單 9 中的 logstash 的配置檔案中的 output 部分改成清單 11 中所示即可。
清單 11. 配置 logstash 輸出到 Graylog2
檢視文字列印- output {
- gelf {
- host => '127.0.0.1'
-
相關推薦
MDC介紹 -- 一種多執行緒下日誌管理實踐方式
一:MDC介紹 MDC(Mapped Diagnostic Context,對映除錯上下文)是 log4j 和 logback 提供的一種方便在多執行緒條件下記錄日誌的功能。某些應用程式採用多執行緒的方式來處理多個使用者的請求。在一個使用者的使用過程中,可能有多個不
一種多執行緒安全的單例模式
template <typename T> class StaticSingletonT { public: static T* SafeInstance() { static T* s_instance_ptr = NULL; if (!s_i
一種多執行緒基於計數無鎖實現(C#)(轉載)
轉自:http://blog.csdn.net/chzuping/article/details/10960061 chzuping的專欄 本文介紹一種不加鎖,不使用原子操作的多執行緒同步機制。先申明下,該方案為我在實際程式設計中創造出來的,事先我沒有在其中地方
一、多執行緒基礎概念、實現執行緒三種方法、中斷執行緒方法,以及執行緒狀態轉化
1、CPU核心數和執行緒數的關係 1:1的關係,引入超執行緒之後,就是1:2 2、cpu時間輪轉機制,即RR排程 3、程序和執行緒 程序:程式執行資源分配最小單位,程序內部有多個執行緒,多個執行緒之間會共享程序資源 執行緒:CPU排程的最小單位 4、並行和併發
Java 多執行緒下,2種安全、效能靠譜的單例模式
懶漢式-雙重核驗: package com.zzf.concurrence.singleinstance; /** * 懶漢式-雙重核驗 * @author zzf * */ public class SingleEHan { private Single
多執行緒下解決資源競爭的7種方法
前言 一般情況下,只要涉及到多執行緒程式設計,程式的複雜性就會顯著上升,效能顯著下降,BUG出現的概率大大提升。 多執行緒程式設計本意是將一段程式並行執行,提升資料處理能力,但是由於大部分情況下都涉及到共有資源的競爭,所以修改資源 物件時必須加鎖處理。但是鎖的實現有很多種方法,下面就來一起了解一下在
多執行緒下HashMap的死迴圈
多執行緒下HashMap的死迴圈 Java的HashMap是非執行緒安全的。多執行緒下應該用ConcurrentHashMap。 多執行緒下[HashMap]的問題(這裡主要說死迴圈問題): 1、多執行緒put操作後,get操作導致死迴圈。 2、多執行緒
Qt 筆記:另一種建立執行緒的方式
class QThread :public Qt { // ....... // ....... protected: virtual void run() = 0; // ....... // ....... }; ps:面向物件程式設計實踐的早起,
多執行緒下synchronized修飾static方法與非static方法的區別
一直對多執行緒的概念比較模糊,今天就寫了個關於變數原子操作的小程式,好讓自己加深一下理解 程式碼如下: package atomic; public class JoinThread extends Thread {
Boost ptree 解析json字串 多執行緒下程式crash
分享一下我老師大神的人工智慧教程!零基礎,通俗易懂!http://blog.csdn.net/jiangjunshow 也歡迎大家轉載本篇文章。分享知識,造福人民,實現我們中華民族偉大復興!  
Python3基礎之(三十 一)多執行緒&多程序
一、多執行緒 Threading 多執行緒 Threading 是一種讓程式擁有分身效果. 能同時處理多件事情. 一般的程式只能從上到下一行行執行程式碼, 不過多執行緒 (Threading)就能打破這種限制. 讓你的程式鮮活起來. 二、多程序 Multiprocessing
JavaSE基礎學習筆記及案例(二)多執行緒(下)與簡單工廠模式的瞭解
1.多執行緒(下) 1.1單例設計模式:保證類在記憶體中只存在一個物件 ************餓漢式與懶漢式的區別【面試題】 餓漢式單例模式:以空間換時間 懶漢式單例模式:以時間換空間(不推薦使用,僅在面試中用到) 3.多執行緒訪問時:餓漢式不會建立多個物件;而懶漢式
Java多執行緒程式設計核心技術(一)Java多執行緒技能
主題: 這套文章主要是把書上的內容自己消化一遍,把核心內容跟重點挑出來。讀者可以用最少的時間瞭解這本書的內容。 第一章主要講解多執行緒的概念,使用方法,一些基本的api;每小節的內容不超過十句話的總結如下: 層級結構上是: 1.
機器學習筆記(十九):TensorFlow實戰十一(多執行緒輸入資料)
1 - 引言 為了加速模型訓練的時間,TensorFlow提供了一套多執行緒處理輸入資料的框架。 下面我們來詳細的介紹如何使用多執行緒來加速我們的模型訓練速度 2 - 佇列與多執行緒 在TensorFlow中,佇列和變數類似,我們可以修改它們的狀態。下面給出一個示例來展示如
25 多執行緒(下)&GUI
25.01_多執行緒(單例設計模式)(掌握) 單例設計模式:保證類在記憶體中只有一個物件。 如何保證類在記憶體中只有一個物件呢? (1)控制類的建立,不讓其他類來建立本類的物件。private (2)在本類中定義一個本類的物件。Singl
一、多執行緒[建立,interrupt,setDaemon,getPriority,isAlive等]
一、多執行緒簡介 在CPU上的執行緒,執行一個任務,巨集觀當然是同時執行的,但微觀裡的確實序列執行的,CPU通過執行緒中斷,讓某一個執行緒掛起來,然後切換到另一個執行緒,執行一會兒,再切換回來。但是執行緒切換來回會犧牲一定的效能,如果增加CPU那麼執行緒是可以達到並行的。
(一)多執行緒說學逗唱:關於執行緒那不得不說的二三事
(二)多執行緒說學逗唱:新手村偶遇Thread類 為什麼一上來就要寫這個 這個是啥,那個那個是啥,直接進去主題不好嗎?以前我也是這麼想的,可是後來呀…總之,一個不刨根問底的程式設計師不是好程式設計師,要深究一個知識點還就得知道他是從哪裡來,到哪裡去,既然來到這個事件
每天一例多執行緒[day2]-----synchronized與多個執行緒多個鎖
package com.jeff.base.sync002; /** * 多個執行緒多個鎖 * * 關鍵字synchronized取得的鎖都是物件鎖,而不是把一段程式碼(方法)當做鎖, * 所以程式碼中哪個執行緒先執行syn
MySQL---當Java遇上MySQL⑤---單執行緒與多執行緒下的事務
事務transaction 原子性(atomicity):組成事務處理的語句形成了一個邏輯單元,不能只執行其中的一部分。 一致性(consistency):在事務處理執行前後,資料庫是一致的(資料庫資料完整性約束)。 隔離性(isolcation):一個事務處理對另
第二十一講 多執行緒——多執行緒間的通訊——多個生產者和消費者
首先,試著思考一下執行如下程式,看會得出什麼結果。 // 描述資源 class Res { private String name; // 資源名稱 private int count = 1; // 資源編號 // 定義標記。