圖編號按順序1-4,嫌長能夠跳過定位過程看總結函數
我司項目幾個服務進程的初始化log
都是這樣的:
spa
這些進程都會初始化一個叫 sdsom
的logger
,而且把handler
加到了這個logger
對象中,後面getlogger
的時候咱們是 sdsom.xx
, 這個按點分隔會致使認爲是子logger
,好比是sdsom.A
,就會新建一個logger
叫sdsom.A
,而後把 sdsom
這個logger
設爲它的parent
(圖1),打日誌的時候,會一直往上遍歷,把全部parent
的全部handler
都打一遍(圖2)。這些實例進程間是獨立的,但若是在一個進程裏,好比在A進程中 import
了 B 的模塊,而這個模塊 import
了B本身的log.py
模塊,觸發一次 addHandler (
圖3),就把 B 的 handler
加進了 A進程的 sdsom logger
裏(把它設爲了parent),因此 A 的 sdsom logger
裏有兩個handler (
圖4),因而A 的log
同時打到了B的日誌文件裏。(注意對比圖3 圖4的對象地址 是一致的)日誌
這個logger
父子關係前人要這麼用的緣由,我估計是咱們項目的common
這個模塊,用父子關係能夠實現這樣一個方式:不需另外初始化,log = logging.getLogger('sdsom.common')
只須要執行這一句,這個logger
的parent
就被設爲 <import
這個common
模塊的> 進程的 sdsom logger
,實際上sdsom.xxx
點號後面的內容都沒有影響了,這個common logger
打印時,會調parent
,因而也就被相應進程的handler
打印了。
自己也算方便的機制,但因爲這種方式內部實現不可見, 容易誤用。code
若是要共享日誌, 還有一種方式就是對相應的logger
顯式加handler
:
好比要在其餘日誌裏打印zerorpc
的日誌, 咱們大部分日誌初始化處都有這句: logging.getLogger('zerorpc').addHandler(handler)
, 給rpc
的Logger
加上本身的handler
就能夠了,因爲有了handler
,那麼只要zerorpc
的源碼裏是getLogger('zerorpc')
的(實際源碼中通常是getLogger(__name__)
,在包內__name__
即爲'zerorpc.xxx'
),日誌就能打印到對應進程的日誌裏。
因此咱們徹底能夠不用父子關係,而是像zerorpc
同樣在進程logger
初始化的地方加上:logging.getLogger('common').addHandler(handler)
而後common
裏的模塊直接log
= logging.getLogger('common')
用便可,爲避免和三方庫重複要注意一下命名
固然還有一種方式就是本身的handler
也經過函數觸發,不要在模塊全局上執行,加入一個函數手動調,只在進程初始化時調。對象
logging
的父子關係是一個基礎機制,稍微看下源碼便可理解(其實主要就是圖1圖2):以點號.
分隔,取最後一個點號的左邊爲前綴,以此前綴名做父,一個logger
觸發記錄時,會調用全部父親的handler
。在同一系統中咱們有時要用到這種機制來方便日誌打印,所以有時會不一樣進程使用同一前綴名來初始化logger
。這時,不一樣進程的模塊如有相互import
,容易形成一個日誌打到多個日誌文件裏。如:進程
進程A: A.py: logger = logging.getLogger('xxsystem.A') logger.addHandler(logging.FileHandler('service1.log'))
進程B 兩個模塊: B.py: from C import func logger = logging.getLogger('xxsystem.B') logger.addHandler(logging.FileHandler('service2.log')) C.py: from A import func
這樣,就會形成B
進程的log老是同時打到兩個service1.log, service2.log
日誌文件裏。這裏是簡化環境,只要B的import樹裏有A模塊
,就會形成一樣結果。ip
避免日誌重複的原則是:
在logger
名有相同前綴的狀況下,對於一個模塊兩個進程調的狀況,涉及到會被其餘進程import
的模塊,不該觸發任何同名前綴的logger
的addHandler
操做。 (不能import
<調用了addHandler
方法的> 模塊,自身也不能執行getLogger(prefix).addHandler
)rpc
實際上我司使用這種機制原本也沒有什麼問題,只要注意不要隨便import
,都用getLogger
便可。但因爲代碼不規範仍是出現了不該有的import 日誌初始化模塊
的狀況。get
要達到:源碼
a)
用 getLogger
,只要前綴相同,就會把當前進程的'prefix' logger
設爲父, 因爲上面說的緣由,這個logger
會且只會被打到調用它的進程中(本身的handler
沒有初始化過)b)
傳logger
對象getLogger
, 不然是一個空logger
,哪裏都不會打印。