python日誌處理模塊

一 日誌處理模塊概述

1 日誌級別

日誌級別level

數值

CRITICAL

50

ERROR

40

WARNING

30 ,默認日誌級別

INFO

20

DEBUG

10

NOTSET

0,表示不設置

日誌級別是指產生日誌的嚴重程度 設置一個級別後,嚴重程度低於次級別的日誌消息將會被忽略 數字越高,優先級別越高

#!/usr/bin/python3.6  #conding:utf-8  import  threading  import time  import  logging  logging.basicConfig(level=logging.INFO)  #設置日誌級別為info  def  add(x,y):      logging.warning(x+y)  # 此處定義的日誌級別是warning,因此能夠被打印出來,默認的日誌打印格式是 級別:用戶:信息  t=threading.Timer(1,add,args=(3,4)) # 此處表示6秒後出結果  t.start()

結果如下

默認的日誌打印格式是級別:用戶:信息

2 格式字符串

1 常見格式

屬性名

格式

描述

日誌消息內容

%(message)s

當調用formatter.format() 時設置

asctime

%(asctime)s

創建logrecord時的可讀時間,默認格式是Y-m-d H:M:S, (逗號後面的數字是毫秒部分的時間)

函數名

%(funcName)s

日誌調用所在的函數名

日誌級別名稱

%(levelname)s

消息級別名稱,DEBUG,INFO,WARNING,ERROR,CRITICAL

日誌級別數值

%(levelno)s

消息的級別數字DEBUG,INFO,WARNING,ERROR,CRITICAL

行號

%(lineno)d

日誌調用所在源碼行號

模塊

%(module)s

模塊(filename的名字部分)

進程ID

%(process)d

進程ID

線程ID

%(thread)d

線程ID

進程名稱

%(processName)s

進程名

線程名稱

%(threadName)s

線程名字

d 格式化成數字,s格式化成字符串

#!/usr/bin/python3.6  #conding:utf-8  import  threading  import time  FORMAT="%(asctime)-15stThread Name: %(threadName)st%(message)s"  # 此處定義日誌格式  import  logging  logging.basicConfig(level=logging.INFO,format=FORMAT)  #設置日誌級別為info,此處format為引用日誌格式  def  add(x,y):      logging.warning(x+y)  # 此處定義的日誌級別是warning,因此能夠被打印出來    t=threading.Thread(target=add,args=(3,4),name='a1')  t.start()

結果如下

2 格式化時間字串

#!/usr/bin/python3.6  #conding:utf-8  import  threading  import time  FORMAT="%(asctime)-15stThread Name: %(threadName)st%(message)s"  # 此處定義日誌格式  import  logging  logging.basicConfig(level=logging.INFO,format=FORMAT,datefmt='%y-%m-%d %H:%M:%S %z')  #設置日誌級別為info,此處format為引用日誌格式,此處通過定義日誌格式來  # 確定打印的日誌為正常格式,其和%(asctime)互相作用  def  add(x,y):      logging.warning(x+y)  # 此處定義的日誌級別是warning,因此能夠被打印出來    t=threading.Thread(target=add,args=(3,4),name='a1')  t.start()

結果如下

不同打印風格

#!/usr/bin/python3.6  #conding:utf-8  import  threading  import time  FORMAT="%(asctime)-15stThread Name: %(threadName)st%(message)s"  # 此處定義日誌格式  import  logging  logging.basicConfig(level=logging.INFO,format=FORMAT,datefmt='%y-%m-%d %H:%M:%S %z')  #設置日誌級別為info,此處format為引用日誌格式,此處通過定義日誌格式來  # 確定打印的日誌為正常格式,其和%(asctime)互相作用  def  add(x,y):      logging.warning("%d",x+y)  # 此處定義的日誌級別是warning,因此能夠被打印出來,此處是c風格處理日誌      logging.info("{} 值為 {}".format(threading.enumerate(),x+y)) # 此處是format方式處理    t=threading.Thread(target=add,args=(3,4),name='a1')  t.start()

打印結果如下

3 新變量處理

定義一個新的變量來進行存儲

#!/usr/bin/python3.6  #conding:utf-8  import  threading  import time  FORMAT="%(asctime)s t threadName: %(threadName)s t %(message)s t %(myname1)s"  # 此處定義日誌格式,此處定義變量名為myname  import  logging  logging.basicConfig(level=logging.INFO,format=FORMAT,datefmt='%y-%m-%d %H:%M:%S %z')  #設置日誌級別為info,此處format為引用日誌格式,此處通過定義日誌格式來  # 確定打印的日誌為正常格式,其和%(asctime)互相作用  d={"myname1":"zhangsan"}  #此處定義變量值為zhangsan,是字典格式  def  add(x,y):      logging.warning("%d",x+y,extra=d)  # 此處定義的日誌級別是warning,因此能夠被打印出來,此處是c風格處理日誌,通過此處引用extra 字典得到結果  t=threading.Thread(target=add,args=(3,4),name='a1')  t.start()

結果如下

4 輸出到文件

#!/usr/bin/python3.6  #conding:utf-8  import  threading  import time  FORMAT="%(asctime)s t threadName: %(threadName)s t %(message)s t %(myname1)s"  # 此處定義日誌格式,此處定義變量名為myname  import  logging  logging.basicConfig(level=logging.INFO,format=FORMAT,datefmt='%y-%m-%d %H:%M:%S %z',filename='/root/test.log')  #設置日誌級別為info,此處format為引用日誌格式,此處通過定義日誌格式來  # 確定打印的日誌為正常格式,其和%(asctime)互相作用,此處指定打印的路徑,若不存在,則創建並追加,如此下面便不會再次打印  d={"myname1":"zhangsan"}  #此處定義變量值為zhangsan,是字典格式  def  add(x,y):      logging.warning("%d",x+y,extra=d)  # 此處定義的日誌級別是warning,因此能夠被打印出來,此處是c風格處理日誌,通過此處引用extra 字典得到結果  t=threading.Thread(target=add,args=(3,4),name='a1')  t.start()

結果如下

其相當於輸出重定向到文件

二 logger 類

1 概述

使用工廠方法返回一個logger實例。 logger.getLogger([name=None)] 指定name,返回一個名稱為name的Logger實例,如果再次使用相同的名字,是實例化一個對象。 未指定name,則返回logger實例,名稱是root。及根Logger Logger 是層次結構的,使用. 點號分割。如'a',或'a.b'或'a.b.c.d',a是a.b的父parent,a.b是a的子child,對於foo來說,名字為foo.bar,foo.bar.baz,foo.bam都是foo的後代。

2 基本實驗

1 打印空name的logger

#!/usr/bin/python3.6  #conding:utf-8  import   logging    log=logging.getLogger()  print (log.name) #打印當前實例的名稱,其默認名稱為root  print (type(log))# 打印當前實例的類型

結果如下

2 打印非空name的logger

#!/usr/bin/python3.6  #conding:utf-8  import   logging    log=logging.getLogger('a')  # 此處定義其名稱,  print (log.name) #打印當前實例的名稱  print (log,type(log))# 打印當前實例的類型和實例情況

結果如下

3 打印層次化logger

#!/usr/bin/python3.6  #conding:utf-8  import   logging    #!/usr/bin/poython3.6  #conding:utf-8    import  logging  root=logging.getLogger()  print (root,id(root))  # 此處返回跟和跟地址    log=logging.getLogger('a')  # 此處定義其名稱,  print (log.name) #打印當前實例的名稱  print (log,type(log),log.parent,id(log),id(log.parent))# 打印當前實例的類型和實例情況及其父類情況,此處打印當前給情況和父根情況  #  log1=logging.getLogger('a.b')  print (log1.name)  print (log1,type(log1),log1.parent,id(log1.parent))# 此處打印當前根父跟情況

結果如下

上述表明,其a.b的父類是a,a的父類是root ,其直接存在父子級別

4 繼承和重用

#!/usr/bin/python3.6  #conding:utf-8  import  logging  FORMAT="%(asctime)s %(thread)d %(message)s"  logging.basicConfig(level=logging.INFO,datefmt='%Y-%m-%d %H:%M:%S',format=FORMAT)  root=logging.getLogger()  root.warning('my root')    log=logging.getLogger('a')  # 此處定義其名稱,  log.warning('my log')    log1=logging.getLogger('a.b')  log1.warning('my log1')

5 規範化定義name

#!/usr/bin/python3.6  #conding:utf-8  import  logging  FORMAT="%(asctime)s %(thread)d %(message)s"  logging.basicConfig(level=logging.INFO,datefmt='%Y-%m-%d %H:%M:%S',format=FORMAT)  root=logging.getLogger()  print (root,id(root))  root.warning('my root')  loga=logging.getLogger(__name__) # 此處使用模塊名稱進行定義名字  print (loga,id(loga),loga.name)  loga.warning('my loga')# loga.name 也不能在此中打印出來  logb=logging.getLogger("{}.{}".format(__name__,'abcd'))  # 此處使用模塊名稱下的具體方法定義名稱,通過format進行字符串的拼接  print (logb,id(logb),logb.name)  logb.warning('my logb') # logb.name不能打印出來

結果如下

6 獲取和修改日誌級別

獲取和設置相關級別

#!/usr/bin/python3.6  #conding:utf-8  import  logging  FORMAT="%(asctime)s %(thread)d %(message)s"  logging.basicConfig(level=logging.INFO,datefmt='%Y-%m-%d %H:%M:%S',format=FORMAT)  root=logging.getLogger()  print (root,id(root))  print (root.getEffectiveLevel())#此處打印日誌級別  loga=logging.getLogger(__name__) # 此處使用模塊名稱進行定義名字  loga.warning('my loga')# loga.name 也不能在此中打印出來  print (loga.getEffectiveLevel())#此處打印日誌級別  logb=logging.getLogger("{}.{}".format(__name__,'abcd'))  # 此處使用模塊名稱下的具體方法定義名稱,通過format進行字符串的拼接  logb.warning('my logb') # logb.name不能打印出來  logb.debug('my debug') # 此處的debug日誌將不能被打印出來  print (logb.getEffectiveLevel())  #此處打印日誌級別

結果如下

修改日誌級別並進行配置和獲取

#!/usr/bin/python3.6  #conding:utf-8  import  logging  FORMAT="%(asctime)s %(thread)d %(message)s"  logging.basicConfig(level=logging.INFO,datefmt='%Y-%m-%d %H:%M:%S',format=FORMAT)  root=logging.getLogger()  print (root,id(root))  print (root.getEffectiveLevel())#此處打印日誌級別  loga=logging.getLogger(__name__) # 此處使用模塊名稱進行定義名字  loga.info('my loga')# loga.name 也不能在此中打印出來  print (root.getEffectiveLevel())#此處打印日誌級別  print (loga.getEffectiveLevel())#此處打印日誌級別  loga.setLevel(28)# 此時日誌級別位於INFO 和 WARNING之間,  print  (loga.getEffectiveLevel())  # 獲取修改後的日誌級別    print ('+'*20,'logb')  logb=logging.getLogger("{}.{}".format(__name__,'abcd'))  # 此處使用模塊名稱下的具體方法定義名稱,通過format進行字符串的拼接  logb.info('my logb') # 此處因為自身沒有配置日誌級別,因此繼承了上面的日誌級別,因此,其不能打印  logb.setLevel(10)  logb.info('my logb') # 此處因為自身設置了日誌級別,因此其可以打印  logb.warning('my debug')  print (logb.getEffectiveLevel())  #此處打印日誌級別

結果如下

7 總結:

全局可以設定,但自己模塊可以根據自己的情況進行調整和修改 上述用於設置模塊和模塊對應函數或類的日誌級別,通過上述的定義可以確定打印日誌的級別和相關的配置情況 向在模塊級別的進行配置和修改日誌的級別設置 想做個性化比較難 重要的主要是level和format 的情況

2 handler

1 概述

handler 控制日誌信息的輸出目的地,可以是控制台,文件 可以單獨設置level 可以單獨設置格式 可以設置過濾器


handler分為 1 StreamHandler # 不指定使用sys.stderr A FileHandler # 文件輸出 B _StderrHandler # 標準輸出 2 NullHandler # 什麼都不做 Handler父類 ,FileHandle和_StderrHandler和NullHandler是子類,子類可以繼承父類的相關屬性

2 handler 和 logger的關係

#!/usr/bin/python3.6  #conding:utf-8  import  logging  FORMAT="%(asctime)s Threadinfo: %(threadName)s %(message)s"  logging.basicConfig(level=logging.INFO,format=FORMAT)    log1=logging.getLogger('s')  #此處實例化出來一個對象  log1.setLevel(logging.WARNING) #設置此對象的相關屬性    h1=logging.StreamHandler()  # 實例化一個標準輸出的handler  h1.setLevel(logging.INFO)  # 設置handler的級別  log1.addFilter(h1)  # 將h1加入到log1  log1.info('info  log1')  # 打印INFO日誌。  log1.warning('warning  log1')  # 打印WARING ,此處只能打印WAENING

結果如下

此處表明,雖然handler設置了info級別,但其沒不能影響log1的設置,仍然不能打印info級別

#!/usr/bin/python3.6  #conding:utf-8  import  logging  FORMAT="%(asctime)s Threadinfo: %(threadName)s %(message)s"  logging.basicConfig(level=logging.INFO,format=FORMAT)    log1=logging.getLogger('s')  #此處實例化出來一個對象  log1.setLevel(logging.INFO) #設置此對象的相關屬性    h1=logging.StreamHandler()  # 實例化一個標準輸出的handler  h1.setLevel(logging.ERROR)  # 設置handler的級別  log1.addFilter(h1)  # 將h1加入到log1  log1.warning('warning  log1')  # 打印WARING ,此處只能打印WAENING  log1.info('info log1')    log2=logging.getLogger('s.s1')  #此處繼承了s1的屬性  print (log2.getEffectiveLevel())  log2.warning('wangring  log2') #此處設置的warning 和handler中設置的ERROR不衝突,因為ERROR 不針對當前設置

結果如下

此處雖然handler設置了ERROR 級別,但info級別還是能夠打印出來,其不會影響當前級別的日誌,而繼承的日誌也沒受到handler的影響直接打印 日誌輸出到文件

#!/usr/bin/python3.6  #conding:utf-8  import  logging  FORMAT="%(asctime)s Threadinfo: %(threadName)s %(message)s"  logging.basicConfig(level=logging.INFO,format=FORMAT)    log1=logging.getLogger('s')  #此處實例化出來一個對象  log1.setLevel(logging.INFO) #設置此對象的相關屬性    h1=logging.FileHandler('/root/test1.log')  # 實例化一個文件輸出的handler  h1.setLevel(logging.ERROR)  # 設置handler的級別  log1.addHandler(h1)  # 將h1加入到log1  log1.warning('warning  log1')  # 打印WARING ,此處只能打印WAENING  log1.info('info log1')  log1.error('error  log1')  #打印error級別

結果如下

文件中結果如下

#!/usr/bin/python3.6  #conding:utf-8  import  logging  FORMAT="%(asctime)s Threadinfo: %(threadName)s %(message)s"  logging.basicConfig(level=logging.INFO,format=FORMAT)    log1=logging.getLogger('s')  #此處實例化出來一個對象  log1.setLevel(logging.WARNING) #設置此對象的相關屬性    h1=logging.FileHandler('/root/test1.log')  # 實例化一個文件輸出的handler  h1.setLevel(logging.INFO)  # 設置handler的級別  log1.addHandler(h1)  # 將h1加入到log1  log1.warning('warning  log1')  # 打印WARING ,此處只能打印WAENING  log1.info('info log1')

結果如下

文件結果

#!/usr/bin/python3.6  #conding:utf-8  import  logging  FORMAT="%(asctime)s Threadinfo: %(threadName)s %(message)s"  logging.basicConfig(level=logging.INFO,format=FORMAT)    log1=logging.getLogger('s')  #此處實例化出來一個對象  log1.setLevel(logging.WARNING) #設置此對象的相關屬性    h1=logging.FileHandler('/root/test1.log')  # 實例化一個文件輸出的handler  h1.setLevel(logging.INFO)  # 設置handler的級別  log1.addHandler(h1)  # 將h1加入到log1  log1.warning('warning  log1')  # 打印WARING ,此處只能打印WAENING  log1.info('info log1')    log2=logging.getLogger('s.s1')  print (log2.getEffectiveLevel())  log2.info('log2 info')

文件結果

#!/usr/bin/python3.6  #conding:utf-8  import  logging  FORMAT="%(asctime)s Threadinfo: %(threadName)s %(message)s"  logging.basicConfig(level=logging.INFO,format=FORMAT)    log1=logging.getLogger('s')  #此處實例化出來一個對象  log1.setLevel(logging.INFO) #設置此對象的相關屬性    h1=logging.FileHandler('/root/test1.log')  # 實例化一個文件輸出的handler  h1.setLevel(logging.INFO)  # 設置handler的級別  log1.addHandler(h1)  # 將h1加入到log1  log1.warning('warning  log1')  # 打印WARING ,此處只能打印WAENING  log1.info('info log1')    log2=logging.getLogger('s.s1')  print (log2.getEffectiveLevel())  log2.info('log2 info')

結果

文件結果

3 添加格式化處理

#!/usr/bin/python3.6  #conding:utf-8  import  logging  FORMAT="%(asctime)s Threadinfo: %(threadName)s %(message)s"  logging.basicConfig(level=logging.INFO,format=FORMAT)    log1=logging.getLogger('s')  #此處實例化出來一個對象  log1.setLevel(logging.INFO) #設置此對象的相關屬性    h1=logging.FileHandler('/root/test1.log')  # 實例化一個文件輸出的handler  h1.setLevel(logging.INFO)  # 設置handler的級別  fmtr=logging.Formatter("%(asctime)s Threadinfo: %(threadName)s %(message)s  %(thread)s")  h1.setFormatter(fmtr)  log1.addHandler(h1)  # 將h1加入到log1  log1.warning('warning  log1')  # 打印WARING ,此處只能打印WAENING  log1.info('info log1')    log2=logging.getLogger('s.s1')  print (log2.getEffectiveLevel())  log2.info('log2 info')

結果如下

文件結果如下

4 handlers 和 formatter

import logging    root=logging.getLogger()  root.setLevel(logging.ERROR)  print ('root',root.handlers)  #打印handler列表    h0=logging.StreamHandler()  h0.setLevel(logging.WARNING)  root.addHandler(h0)  print ('root',root.handlers)  # 打印列表    for h  in root.handlers:      print ("root  handler = {} ,formatter= {}".format(h,h.formatter))  # 打印默認的formatter

結果如下

import logging    root=logging.getLogger()  root.setLevel(logging.ERROR)  print ('root',root.handlers)  #打印handler列表    h0=logging.StreamHandler()  h0.setLevel(logging.WARNING)  root.addHandler(h0)  print ('root',root.handlers)  # 打印列表    for h  in root.handlers:      print ("root  handler = {} ,formatter= {}".format(h,h.formatter))  # 打印默認的formatter    log1=logging.getLogger('s')  log1.setLevel(logging.ERROR)  h1=logging.FileHandler('/root/test2.log')  log1.addHandler(h1)  print ('log1',log1.handlers)    log2=logging.getLogger('s.s1')  log2.setLevel(logging.CRITICAL)  h2=logging.FileHandler('/root/test2.log')  h2.setLevel(logging.WARNING)  print ('log2 formatter',h2.formatter)  # handler 默認無Formatter  f2=logging.Formatter("log2 %(name)s %(asctime)s %(message)s")  h2.setFormatter(f2)  print ('log2.formatter',h2.formatter)  log2.addHandler(h2)  print ('log2',log2.handlers)

結果為

文件結果為

5 總結:

1 每一個logger實例的level如同入水口,讓水流進來,如果這個門檻太高,水流就不能進來,其相關低級別的信息一定不能被打印出來


2 如果level沒有設置,則使用父類logger的,如果父類沒有,則繼續尋找父類的,最終找到root,而root的默認設置是WARNING


3 消息傳遞流程 在某個logger上產生某種級別的消息,首先和logger的level檢查,如果消息level低於logger的EffectiveLevel有效級別,則丟棄消息,如果通過(大於等於)檢查後,消息交給所有的handler處理,每一個handler需要和自己的level比較來決定是否處理,如果沒有一個handler,或者消息已經被處handler處理過了,則需要通過本logger的propagate屬性是否是True,True則會把這個消息繼續傳遞給父logger,父logger成為新的logger。新的logger直接把消息交給新的logger的所有handler,handler都處理完了,如果paragate屬性是True,新的logger的父成為新的logger,它的所有handler處理消息。


4 logger 實例初始化的paragate屬性為True,及允許向父傳遞logger消息


5 logging.basicConfig


如果root沒有handler,就默認創建一個StreamHandler ,如果設置了filename,則就創建了一個FileHandler,如果設置了format參數,它就會生成一個formatter對象,並把這個formatter加入到剛才創建的handler上,然後把這些handler加入到root.handlers列表上,level是設置給root logger的。如果root.handlers列表不是空,則logging.basicConfig的調用什麼都不做。

6 filter過濾器

可以為handler增加過濾器,所以過濾器隻影響某一個handler,不會影響整個處理流程

import logging  FORMAT="%(asctime)s %(thread)d  %(message)s"  logging.basicConfig(level=logging.ERROR,format=FORMAT,datefmt="%y-%m-%d %H:%M%S %z")  #重新格式化傳值,定義日誌輸出到文件    root=logging.getLogger()  print (root,id(root))    loga=logging.getLogger('s') # 模塊和模塊下的某個函數或類  print (loga,id(loga),id(loga.parent))  loga.setLevel(logging.INFO)  print (loga.getEffectiveLevel()) #默認對應的是常量20    hadr=logging.FileHandler('/root/loga.txt')  # 定義,不指定則是標準錯誤輸出  hadr.setLevel(logging.INFO)  fmtr=logging.Formatter("%(asctime)s %(thread)d  %(threadName)s   %(message)s")  #此處是一個實例。此處可以自己定義  hadr.setFormatter(fmtr)  fltr=logging.Filter('s') # 此處設置過濾器為s,則為s,s.s1均可通過  hadr.addFilter(fltr)  loga.addHandler(hadr)    logb=logging.getLogger('s.s1')  logb.getEffectiveLevel()  logb.info('logb info')

結果如下

文件結果如下

import logging  FORMAT="%(asctime)s %(thread)d  %(message)s"  logging.basicConfig(level=logging.ERROR,format=FORMAT,datefmt="%y-%m-%d %H:%M%S %z")  #重新格式化傳值,定義日誌輸出到文件    root=logging.getLogger()  print (root,id(root))    loga=logging.getLogger('s') # 模塊和模塊下的某個函數或類  print (loga,id(loga),id(loga.parent))  loga.setLevel(logging.INFO)  print (loga.getEffectiveLevel()) #默認對應的是常量20    hadr=logging.FileHandler('/root/loga.txt')  # 定義,不指定則是標準錯誤輸出  hadr.setLevel(logging.INFO)  fmtr=logging.Formatter("%(asctime)s %(thread)d  %(threadName)s   %(message)s")  #此處是一個實例。此處可以自己定義  hadr.setFormatter(fmtr)  fltr=logging.Filter('s') # 此處設置過濾器為s,則為s,s.s1均可通過  hadr.addFilter(fltr)  loga.addHandler(hadr)  loga.info('loga info')    logb=logging.getLogger('s.s1')  logb.getEffectiveLevel()  logb.info('logb info')    logc=logging.getLogger('s.s1')  logc.getEffectiveLevel()  logc.info('logc info')

結果如下

文件如下

import logging  FORMAT="%(asctime)s %(thread)d  %(message)s"  logging.basicConfig(level=logging.ERROR,format=FORMAT,datefmt="%y-%m-%d %H:%M%S %z")  #重新格式化傳值,定義日誌輸出到文件    root=logging.getLogger()  print (root,id(root))    loga=logging.getLogger('s') # 模塊和模塊下的某個函數或類  print (loga,id(loga),id(loga.parent))  loga.setLevel(logging.INFO)  print (loga.getEffectiveLevel()) #默認對應的是常量20    hadr=logging.FileHandler('/root/loga.txt')  # 定義,不指定則是標準錯誤輸出  hadr.setLevel(logging.INFO)  fmtr=logging.Formatter("%(asctime)s %(thread)d  %(threadName)s   %(message)s")  #此處是一個實例。此處可以自己定義  hadr.setFormatter(fmtr)  fltr=logging.Filter('a') # 此處設置過濾器為s,則為s,s.s1均可通過  hadr.addFilter(fltr)  loga.addHandler(hadr)  loga.info('loga info')    logb=logging.getLogger('s.s1')  logb.getEffectiveLevel()  logb.info('logb info')    logc=logging.getLogger('s.s1')  logc.getEffectiveLevel()  logc.info('logc info')

文件如下

消息loga,它的名字是's',因此過濾器名字設置為s,則s.s1,s.x和s都能通過,但其他的則不能通過,不設置過濾器名字,所有消息都能通過,設置為a,則和s,s.s1和s.s2無任何關係,因此其不能使用,實例對象只有一個,一旦同名,也是只有一個,線程的不安全不會影響logging,只會影響print。