最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Python向日志輸出中添加上下文信息

 更新時(shí)間:2017年05月24日 14:27:32   作者:云游道士  
這篇文章主要介紹了Python向日志輸出中添加上下文信息的方法,非常不錯(cuò),具有參考借鑒價(jià)值,需要的朋友可以參考下

除了傳遞給日志記錄函數(shù)的參數(shù)(如msg)外,有時(shí)候我們還想在日志輸出中包含一些額外的上下文信息。比如,在一個(gè)網(wǎng)絡(luò)應(yīng)用中,可能希望在日志中記錄客戶端的特定信息,如:遠(yuǎn)程客戶端的IP地址和用戶名。這里我們來介紹以下幾種實(shí)現(xiàn)方式:

  • 通過向日志記錄函數(shù)傳遞一個(gè)extra參數(shù)引入上下文信息
  • 使用LoggerAdapters引入上下文信息
  • 使用Filters引入上下文信息

一、通過向日志記錄函數(shù)傳遞一個(gè)extra參數(shù)引入上下文信息

前面我們提到過,可以通過向日志記錄函數(shù)傳遞一個(gè)extra參數(shù)來實(shí)現(xiàn)向日志輸出中添加額外的上下文信息,如:

import logging
import sys
fmt = logging.Formatter("%(asctime)s - %(name)s - %(ip)s - %(username)s - %(message)s")
h_console = logging.StreamHandler(sys.stdout)
h_console.setFormatter(fmt)
logger = logging.getLogger("myPro")
logger.setLevel(logging.DEBUG)
logger.addHandler(h_console)
extra_dict = {"ip": "113.208.78.29", "username": "Petter"}
logger.debug("User Login!", extra=extra_dict)
extra_dict = {"ip": "223.190.65.139", "username": "Jerry"}
logger.info("User Access!", extra=extra_dict)

輸出:

2017-05-14 15:47:25,562 - myPro - 113.208.78.29 - Petter - User Login!
2017-05-14 15:47:25,562 - myPro - 223.190.65.139 - Jerry - User Access!

但是用這種方式來傳遞信息并不是那么方便,因?yàn)槊看握{(diào)用日志記錄方法都要傳遞一個(gè)extra關(guān)鍵詞參數(shù)。即便沒有需要插入的上下文信息也是如此,因?yàn)樵搇ogger設(shè)置的formatter格式中指定的字段必須要存在。所以,我們推薦使用下面兩種方式來實(shí)現(xiàn)上下文信息的引入。

也許可以嘗試在每次創(chuàng)建連接時(shí)都創(chuàng)建一個(gè)Logger實(shí)例來解決上面存在的問題,但是這顯然不是一個(gè)好的解決方案,因?yàn)檫@些Logger實(shí)例并不會進(jìn)行垃圾回收。盡管這在實(shí)踐中不是個(gè)問題,但是當(dāng)Logger數(shù)量變得不可控將會非常難以管理。

二、使用LoggerAdapters引入上下文信息

使用LoggerAdapter類來傳遞上下文信息到日志事件的信息中是一個(gè)非常簡單的方式,可以把它看做第一種實(shí)現(xiàn)方式的優(yōu)化版--因?yàn)樗鼮閑xtra提供了一個(gè)默認(rèn)值。這個(gè)類設(shè)計(jì)的類似于Logger,因此我們可以像使用Logger類的實(shí)例那樣來調(diào)用debug(), info(), warning(),error(), exception(), critical()和log()方法。當(dāng)創(chuàng)建一個(gè)LoggerAdapter的實(shí)例時(shí),我們需要傳遞一個(gè)Logger實(shí)例和一個(gè)包含上下文信息的類字典對象給該類的實(shí)例構(gòu)建方法。當(dāng)調(diào)用LoggerAdapter實(shí)例的一個(gè)日志記錄方法時(shí),該方法會在對日志日志消息和字典對象進(jìn)行處理后,調(diào)用構(gòu)建該實(shí)例時(shí)傳遞給該實(shí)例的logger對象的同名的日志記錄方法。下面是LoggerAdapter類中幾個(gè)方法的定義:

class LoggerAdapter(object):
 """
 An adapter for loggers which makes it easier to specify contextual
 information in logging output.
 """
 def __init__(self, logger, extra):
  """
  Initialize the adapter with a logger and a dict-like object which
  provides contextual information. This constructor signature allows
  easy stacking of LoggerAdapters, if so desired.
  You can effectively pass keyword arguments as shown in the
  following example:
  adapter = LoggerAdapter(someLogger, dict(p1=v1, p2="v2"))
  """
  self.logger = logger
  self.extra = extra
 def process(self, msg, kwargs):
  """
  Process the logging message and keyword arguments passed in to
  a logging call to insert contextual information. You can either
  manipulate the message itself, the keyword args or both. Return
  the message and kwargs modified (or not) to suit your needs.
  Normally, you'll only need to override this one method in a
  LoggerAdapter subclass for your specific needs.
  """
  kwargs["extra"] = self.extra
  return msg, kwargs
 def debug(self, msg, *args, **kwargs):
  """
  Delegate a debug call to the underlying logger, after adding
  contextual information from this adapter instance.
  """
  msg, kwargs = self.process(msg, kwargs)
  self.logger.debug(msg, *args, **kwargs)

通過分析上面的代碼可以得出以下結(jié)論:

  • 上下文信息是在LoggerAdapter類的process()方法中被添加到日志記錄的輸出消息中的,如果要實(shí)現(xiàn)自定義需求只需要實(shí)現(xiàn)LoggerAdapter的子類并重寫process()方法即可;
  • process()方法的默認(rèn)實(shí)現(xiàn)中,沒有修改msg的值,只是為關(guān)鍵詞參數(shù)插入了一個(gè)名為extra的 key,這個(gè)extra的值為傳遞給LoggerAdapter類構(gòu)造方法的參數(shù)值;
  • LoggerAdapter類構(gòu)建方法所接收的extra參數(shù),實(shí)際上就是為了滿足logger的formatter格式要求所提供的默認(rèn)上下文信息。

關(guān)于上面提到的第3個(gè)結(jié)論,我們來看個(gè)例子:

import logging
import sys
# 初始化一個(gè)要傳遞給LoggerAdapter構(gòu)造方法的logger實(shí)例
fmt = logging.Formatter("%(asctime)s - %(name)s - %(ip)s - %(username)s - %(message)s")
h_console = logging.StreamHandler(sys.stdout)
h_console.setFormatter(fmt)
init_logger = logging.getLogger("myPro")
init_logger.setLevel(logging.DEBUG)
init_logger.addHandler(h_console)
# 初始化一個(gè)要傳遞給LoggerAdapter構(gòu)造方法的上下文字典對象
extra_dict = {"ip": "IP", "username": "USERNAME"}
# 獲取一個(gè)LoggerAdapter類的實(shí)例
logger = logging.LoggerAdapter(init_logger, extra_dict)
# 應(yīng)用中的日志記錄方法調(diào)用
logger.info("User Login!")
logger.info("User Login!", extra={"ip": "113.208.78.29", "username": "Petter"})
logger.extra = {"ip": "113.208.78.29", "username": "Petter"}
logger.info("User Login!")
logger.info("User Login!")

輸出結(jié)果:

# 使用extra默認(rèn)值:{"ip": "IP", "username": "USERNAME"}
2017-05-14 17:23:15,148 - myPro - IP - USERNAME - User Login!
# info(msg, extra)方法中傳遞的extra方法沒有覆蓋默認(rèn)值
2017-05-14 17:23:15,148 - myPro - IP - USERNAME - User Login!
# extra默認(rèn)值被修改了
2017-05-14 17:23:15,148 - myPro - 113.208.78.29 - Petter - User Login!
2017-05-14 17:23:15,148 - myPro - 113.208.78.29 - Petter - User Login!

根據(jù)上面的程序輸出結(jié)果,我們會發(fā)現(xiàn)一個(gè)問題:傳遞給LoggerAdapter類構(gòu)造方法的extra參數(shù)值不能被LoggerAdapter實(shí)例的日志記錄函數(shù)(如上面調(diào)用的info()方法)中的extra參數(shù)覆蓋,只能通過修改LoggerAdapter實(shí)例的extra屬性來修改默認(rèn)值(如上面使用的logger.extra=xxx),但是這也就意味著默認(rèn)值被修改了。

解決這個(gè)問題的思路應(yīng)該是:實(shí)現(xiàn)一個(gè)LoggerAdapter的子類,重寫process()方法。其中對于kwargs參數(shù)的操作應(yīng)該是先判斷其本身是否包含extra關(guān)鍵字,如果包含則不使用默認(rèn)值進(jìn)行替換;如果kwargs參數(shù)中不包含extra關(guān)鍵字則取默認(rèn)值。來看具體實(shí)現(xiàn):

import logging
import sys
class MyLoggerAdapter(logging.LoggerAdapter):
 def process(self, msg, kwargs):
  if 'extra' not in kwargs:
   kwargs["extra"] = self.extra
  return msg, kwargs
if __name__ == '__main__':
 # 初始化一個(gè)要傳遞給LoggerAdapter構(gòu)造方法的logger實(shí)例
 fmt = logging.Formatter("%(asctime)s - %(name)s - %(ip)s - %(username)s - %(message)s")
 h_console = logging.StreamHandler(sys.stdout)
 h_console.setFormatter(fmt)
 init_logger = logging.getLogger("myPro")
 init_logger.setLevel(logging.DEBUG)
 init_logger.addHandler(h_console)
 # 初始化一個(gè)要傳遞給LoggerAdapter構(gòu)造方法的上下文字典對象
 extra_dict = {"ip": "IP", "username": "USERNAME"}
 # 獲取一個(gè)自定義LoggerAdapter類的實(shí)例
 logger = MyLoggerAdapter(init_logger, extra_dict)
 # 應(yīng)用中的日志記錄方法調(diào)用
 logger.info("User Login!")
 logger.info("User Login!", extra={"ip": "113.208.78.29", "username": "Petter"})
 logger.info("User Login!")
 logger.info("User Login!")

輸出結(jié)果:

# 使用extra默認(rèn)值:{"ip": "IP", "username": "USERNAME"}
2017-05-22 17:35:38,499 - myPro - IP - USERNAME - User Login!
# info(msg, extra)方法中傳遞的extra方法已覆蓋默認(rèn)值
2017-05-22 17:35:38,499 - myPro - 113.208.78.29 - Petter - User Login!
# extra默認(rèn)值保持不變
2017-05-22 17:35:38,499 - myPro - IP - USERNAME - User Login!
2017-05-22 17:35:38,499 - myPro - IP - USERNAME - User Login!

OK! 問題解決了。

其實(shí),如果我們想不受formatter的限制,在日志輸出中實(shí)現(xiàn)自由的字段插入,可以通過在自定義LoggerAdapter的子類的process()方法中將字典參數(shù)中的關(guān)鍵字信息拼接到日志事件的消息中。很明顯,這些上下文中的字段信息在日志輸出中的位置是有限制的。而使用'extra'的優(yōu)勢在于,這個(gè)類字典對象的值將被合并到這個(gè)LogRecord實(shí)例的__dict__中,這樣就允許我們通過Formatter實(shí)例自定義日志輸出的格式字符串。這雖然使得上下文信息中的字段信息在日志輸出中的位置變得與內(nèi)置字段一樣靈活,但是前提是傳遞給構(gòu)造器方法的這個(gè)類字典對象中的key必須是確定且明了的。

三、使用Filters引入上下文信息

另外,我們還可以使用自定義的Filter.Filter實(shí)例的方式,在filter(record)方法中修改傳遞過來的LogRecord實(shí)例,把要加入的上下文信息作為新的屬性賦值給該實(shí)例,這樣就可以通過指定formatter的字符串格式來輸出這些上下文信息了。

我們模仿上面的實(shí)現(xiàn),在傳遞個(gè)filter(record)方法的LogRecord實(shí)例中添加兩個(gè)與當(dāng)前網(wǎng)絡(luò)請求相關(guān)的信息:ip和username。

import logging
from random import choice
class ContextFilter(logging.Filter):
  ip = 'IP'
  username = 'USER'
  def filter(self, record):
   record.ip = self.ip
   record.username = self.username
   return True
if __name__ == '__main__':
 levels = (logging.DEBUG, logging.INFO, logging.WARNING, logging.ERROR, logging.CRITICAL)
 users = ['Tom', 'Jerry', 'Peter']
 ips = ['113.108.98.34', '219.238.78.91', '43.123.99.68']
 logging.basicConfig(level=logging.DEBUG,
      format='%(asctime)-15s %(name)-5s %(levelname)-8s %(ip)-15s %(username)-8s %(message)s')
 logger = logging.getLogger('myLogger')
 f = ContextFilter()
 logger.addFilter(f)
 logger.debug('A debug message')
 logger.info('An info message with %s', 'some parameters')
 for x in range(5):
  lvl = choice(levels)
  lvlname = logging.getLevelName(lvl)
  filter.ip = choice(ips)
  filter.username = choice(users)
  logger.log(lvl, 'A message at %s level with %d %s', lvlname, 2, 'parameters')

輸出結(jié)果:

2017-05-15 10:21:49,401 myLogger DEBUG    IP              USER     A debug message
2017-05-15 10:21:49,401 myLogger INFO     IP              USER     An info message with some parameters
2017-05-15 10:21:49,401 myLogger INFO     219.238.78.91   Tom      A message at INFO level with 2 parameters
2017-05-15 10:21:49,401 myLogger INFO     219.238.78.91   Peter    A message at INFO level with 2 parameters
2017-05-15 10:21:49,401 myLogger DEBUG    113.108.98.34   Jerry    A message at DEBUG level with 2 parameters
2017-05-15 10:21:49,401 myLogger CRITICAL 43.123.99.68    Tom      A message at CRITICAL level with 2 parameters
2017-05-15 10:21:49,401 myLogger INFO     43.123.99.68    Jerry    A message at INFO level with 2 parameters

需要說明的是: 實(shí)際的網(wǎng)絡(luò)應(yīng)用程序中,可能還要考慮多線程并發(fā)時(shí)的線程安全問題,此時(shí)可以把連接信息或者自定義過濾器的實(shí)例通過threading.local保存到到一個(gè)threadlocal中。

以上所述是小編給大家介紹的Python向日志輸出中添加上下文信息,希望對大家有所幫助,如果大家有任何疑問請給我留言,小編會及時(shí)回復(fù)大家的。在此也非常感謝大家對腳本之家網(wǎng)站的支持!

相關(guān)文章

  • 使用SimpleITK讀取NII格式三維圖像及注意事項(xiàng)說明

    使用SimpleITK讀取NII格式三維圖像及注意事項(xiàng)說明

    這篇文章主要介紹了使用SimpleITK讀取NII格式三維圖像及注意事項(xiàng)說明,具有很好的參考價(jià)值,希望對大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2022-12-12
  • Python?解析獲取?URL?參數(shù)及使用步驟

    Python?解析獲取?URL?參數(shù)及使用步驟

    這篇文章主要介紹了Python?解析獲取?URL?參數(shù)及使用,本文分步驟通過實(shí)例代碼給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2022-06-06
  • python?dowhy數(shù)據(jù)估計(jì)因果分析功能探索

    python?dowhy數(shù)據(jù)估計(jì)因果分析功能探索

    這篇文章主要為大家介紹了python?dowhy數(shù)據(jù)估計(jì)因果分析功能實(shí)例探索,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2024-02-02
  • Python中利用json庫進(jìn)行JSON數(shù)據(jù)處理詳解

    Python中利用json庫進(jìn)行JSON數(shù)據(jù)處理詳解

    JSON是一種輕量級的數(shù)據(jù)交換格式,易于人閱讀和編寫,同時(shí)也易于機(jī)器解析和生成,下面就跟隨小編一起來了解下如何使用Python實(shí)現(xiàn)JSON數(shù)據(jù)處理吧
    2025-02-02
  • Python存取XML的常見方法實(shí)例分析

    Python存取XML的常見方法實(shí)例分析

    這篇文章主要介紹了Python存取XML的常見方法,結(jié)合具體實(shí)例形式較為詳細(xì)的分析了Python存取xml的常用方法、優(yōu)缺點(diǎn)比較與相關(guān)注意事項(xiàng),需要的朋友可以參考下
    2017-03-03
  • Django 中的Timezone 處理操作

    Django 中的Timezone 處理操作

    這篇文章主要介紹了Django 中的Timezone 處理操作,具有很好的參考價(jià)值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2021-04-04
  • 一些常見Python簡單算法易錯(cuò)題及答案總結(jié)

    一些常見Python簡單算法易錯(cuò)題及答案總結(jié)

    這篇文章總結(jié)了Python編程中的25個(gè)常見問題及其解答,涵蓋字符串操作、列表操作、字典操作、排序算法、日期時(shí)間處理、文件操作、異常處理等多個(gè)方面,文中通過代碼介紹的非常詳細(xì),需要的朋友可以參考下
    2025-03-03
  • win10下Python3.6安裝、配置以及pip安裝包教程

    win10下Python3.6安裝、配置以及pip安裝包教程

    下面小編就為大家?guī)硪黄獁in10下Python3.6安裝、配置以及pip安裝包教程。小編覺得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧
    2017-10-10
  • win7 下搭建sublime的python開發(fā)環(huán)境的配置方法

    win7 下搭建sublime的python開發(fā)環(huán)境的配置方法

    Sublime Text具有漂亮的用戶界面和強(qiáng)大的功能,例如代碼縮略圖,Python的插件,代碼段等。還可自定義鍵綁定,菜單和工具欄。Sublime Text的主要功能包括:拼寫檢查,書簽,完整的 Python API,Goto功能,即時(shí)項(xiàng)目切換,多選擇,多窗口等等。
    2014-06-06
  • 淺談Tensorflow加載Vgg預(yù)訓(xùn)練模型的幾個(gè)注意事項(xiàng)

    淺談Tensorflow加載Vgg預(yù)訓(xùn)練模型的幾個(gè)注意事項(xiàng)

    這篇文章主要介紹了淺談Tensorflow加載Vgg預(yù)訓(xùn)練模型的幾個(gè)注意事項(xiàng)說明,具有很好的參考價(jià)值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2020-05-05

最新評論

清徐县| 双峰县| 罗甸县| 商河县| 马关县| 安吉县| 大田县| 浮山县| 阿瓦提县| 外汇| 乌拉特前旗| 镇雄县| 嵊泗县| 吉林市| 翁源县| 阳原县| 邯郸县| 绥滨县| 托克托县| 大余县| 抚顺县| 马关县| 苏州市| 大安市| 临漳县| 阿拉善盟| 富阳市| 克东县| 卢湾区| 赞皇县| 社旗县| 景宁| 汤原县| 当阳市| 武宁县| 佛冈县| 铜陵市| 堆龙德庆县| 罗源县| 孝感市| 堆龙德庆县|