主要處理多個應用程序運行在同一個web容器,并且每個應用使用各自獨立的日志環境。
——想想一個tomcat運行了多個項目——也就是“一個JVM,多個ClassLoader,每個ClassLoader擁有自己獨立的Logger Context”的問題。
Context Selectors
調用LoggerFactory.getLogger("foo")的時候,會要求SLF4J綁定一個ILoggerFactory,如果SLF4J使用的是logback,那么“綁定ILoggerFactory”的方法,會代理給一個ContextSelector實例,該實例會根據情況返回最合適的LoggerContext對象——這個對象實現了ILoggerFactory接口。
默認總是返回DefaultContextSelector,可以在系統屬性里通過logback.ContextSelector屬性指定自己的類。
ContextJNDISelector
根據JNDI查找Selector——這樣可以在各個項目的web.xml里配置自己的JNDI環境變量。
為了在單個web項目重啟和關閉的時候,對應的logger Context可以被回收,最好配置ContextDetachingSCL監聽器
不過每次獲取logger都查找JNDI太慢,可以配置LoggerContextFilter過濾器來避免——會在每個http請求到來的時候把logger context放在線程內部,從而允許之后跳過JNDI搜索。
此時最好把logger全聲明成static的,減少logger的獲取總次數。
Taming static references in shared libraries
如果有一個類屬于共享包,被兩個web項目同時引用
1,當這個類使用非靜態logger時,一切良好,來自兩個web項目的調用,會返回各自的logger context。
2,當這個類使用靜態logger時,會錯誤的返回首次調用它的web項目的logger context——對這個問題ContextJNDISelector無能為力(eluded a solution for eons... 哈哈哈哈)
對于上述的“來自共類的靜態logger,獲取不同的logger context”的問題,有幾個的辦法:
1,共享類改成非靜態logger——一般不現實,因為共享類的源碼往往不受項目控制。
2,把共享類挪到web項目里——也夠嗆,一個兩個類還好,那么多第三方依賴咋弄?
3,所以還有一種取巧的辦法:用SiftingAppender,根據JNDI信息把不同的日志分別輸出到不同的文件里去(logback提供了JNDIBasedContextDiscriminator幫助做這事兒)——也就是說,兩個項目分享同一個logger,等到其關聯的appender需要把event輸出到文件時,再臨時決定輸出到哪個文件。
上述第3種方法有一個問題:
假如項目A和項目B都使用共享包S。
項目A啟動并調用S的時候,就已經把S的logger context設置為A了,日志會寫入A.log中。
B啟動并調用S的時候logger contex依然是A(不過因為配置了SiftingAppender的存在,B的日志依然可以寫到正確的B.log中)
——也就是說,共享logger的問題并沒有實質解決,這個logger還是只能屬于A或者B其中之一,只不過logger在寫日志前根據MDC里的JNDI可以將日志寫到正確的文件中去。
而初始化了兩個不同的logger context都指向B.log,存在多個線程寫同一個文件的不安全問題——需要對appender配置prudent參數來解決(會影響性能)。
(正常使用都是一個web container部署一個web項目,很少遇到上述情況。這部分文檔看的很蛋疼……)