国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 編程 > Python > 正文

說一說Python logging

2020-01-04 17:32:32
字體:
來源:轉載
供稿:網(wǎng)友
這篇文章主要和大家聊一聊Python logging,Python logging是什么,Python logging的作用是什么,感興趣的小伙伴們可以參考一下
 

最近有個需求是把以前字符串輸出的log 改為json 格式,看了別人的例子,還是有些比較茫然,索性就把logging 整個翻了一邊,做點小總結.

初看log

在程序中, log 的用處寫代碼的你用你知道,log 有等級,DEBUG, INFO,...之類,還會記錄時間,log 發(fā)生的位置,在Python 中用的多的就是logging 這個標準庫中的包了.當打log 的時候究竟發(fā)生了什么? 是如何把不同級別的log 輸出到不同文件里,還能在控制臺輸出.......

最簡單的用法

import logging
logging.basicConfig(format='%(levelname)s:%(message)s', level=logging.DEBUG)
logging.debug('This message should go to the log file')
logging.info('So should this')
logging.warning('And this, too')
1,第一行導入包 2,第二行利用basicConfig 對輸出的格式,和輸出級別做了限制 3, 后面分別輸出了三條不同級別的 log

Logging Levels

Python,logging

共有幾個等級, 每個等級對應一個Int 型整數(shù) ,每個等級都會有一個方法與之對應,這樣輸出的內容就有了不同的等級.

logger 流程,

Python,logging

整個過程,還是不是很詳細,貼個圖吧, 現(xiàn)在看還太早,也說不清真?zhèn)€過程到底發(fā)生了什么,先放著,回頭來看會比較好懂. loger flow

讀代碼

代碼結構

logging 在源碼中有三個文件,結構如下:

├── config.py
├── handlers.py
└── __init__.py
_int.py中實現(xiàn)了基礎功能,主要的邏輯就在這個文件中 handlers.py 是一些Handlers (用處后面會明白)用起來很方便的. config.py 是對配置做處理的方法.

objects

LogRecord Objects

每一次log 都會實例化一個Record 對象,這個對象有很多屬性,最后對LogRecord 做一下format 就輸出了,格式化的log ,里面就基本就是這個對象的屬性了。

 

class LogRecord(object):  def __init__(self, name, level, pathname, lineno,         msg, args, exc_info, func=None):    ct = time.time()    self.name = name    self.msg = msg    if (args and len(args) == 1 and isinstance(args[0], collections.Mapping)      and args[0]):      args = args[0]    self.args = args    self.levelname = getLevelName(level)    self.levelno = level    self.pathname = pathname    try:      self.filename = os.path.basename(pathname)      self.module = os.path.splitext(self.filename)[0]    except (TypeError, ValueError, AttributeError):      self.filename = pathname      self.module = "Unknown module"    self.exc_info = exc_info    self.exc_text = None   # used to cache the traceback text    self.lineno = lineno    self.funcName = func    self.created = ct    self.msecs = (ct - long(ct)) * 1000    self.relativeCreated = (self.created - _startTime) * 1000    if logThreads and thread:      self.thread = thread.get_ident()      self.threadName = threading.current_thread().name    else:      self.thread = None      self.threadName = None    if not logMultiprocessing:      self.processName = None    else:      self.processName = 'MainProcess'      mp = sys.modules.get('multiprocessing')      if mp is not None:        try:          self.processName = mp.current_process().name        except StandardError:          pass    if logProcesses and hasattr(os, 'getpid'):      self.process = os.getpid()    else:      self.process = None  def __str__(self):    return '<LogRecord: %s, %s, %s, %s, "%s">'%(self.name, self.levelno,      self.pathname, self.lineno, self.msg)  def getMessage(self):     pass

看代碼就發(fā)現(xiàn), 這個類沒做什么事情,就是一個model 而已, 有一個得到msg 的方法

Formatter Objects

Formatter 就是對Record 專門格式化的對象,它有一個format 方法,我們實現(xiàn)這個方法就能 做到不同的輸出,我的需求是做json 格式的log 其實關鍵就在寫一個Formatter 就好了

class Formatter(object):  converter = time.localtime  def __init__(self, fmt=None, datefmt=None):    if fmt:      self._fmt = fmt    else:      self._fmt = "%(message)s"    self.datefmt = datefmt  def formatTime(self, record, datefmt=None):    pass  def formatException(self, ei):    pass  def usesTime(self):    return self._fmt.find("%(asctime)") >= 0  def format(self, record):    pass

刪掉源代碼中的實現(xiàn)細節(jié),這個類里面主要的是format 方法,這是默認最基本的Formater ,還有專門對exception ,時間做格式化的方法。具體是哪個,看方法名就很清楚了,具體每個方法怎么實現(xiàn)的,一眼也就懂了。fmt 是制定格式化的,具體怎么指定在最基礎的用法中就有例子,datefmt 是對時間格式的指定。

Filter Objects

這個類是Logger 和Handler 的基類,主要有一個Filter 方法,和一個filters 屬性

Handler Objects

叫Handler 的類還真的不少,在SocketServer 中也有看到,具體的功能都在Handler 中.在這里,組合所有的Formatter ,和控制log 的輸出的方向,繼承自Filter.

 def __init__(self, level=NOTSET):    Filterer.__init__(self)    self._name = None    self.level = _checkLevel(level)    self.formatter = None    _addHandlerRef(self)    self.createLock()

在init方法中看到,Handler 也有一個屬性,通過把自身的屬性和LogRecord 的level對比來決定是否處理這個LogRecord 的。每個Handler 都有一個Formatter 屬性,其實就是上面介紹的Formatter 。Handler 就是來控制LogRecord 和Formatter 的,它還可以控制輸出的方式,在后面會有,StreamHandler,FileHandler等。通過名稱也就能明白具體能干什么,這就是編程取名的智慧。

Logger Objects

這個類通常會通過getLogger()或者getLogger(name)來得到,不會直接new 一個出來.它會有info(msg, *args, kwargs) ,warn(msg, args, *kwargs)等方法,

  def __init__(self, name, level=NOTSET):    Filterer.__init__(self)    self.name = name    self.level = _checkLevel(level)    self.parent = Noneou    self.handlers = []    self.disabled = 0

從init方法中能看到handlers 屬性,這是一個list ,每個LogRecord 通過Handlers 不同的handlers 就能以不同的格式輸出到不同的地方了。每個Logger 可以通過addHandler(hdlr)方法來添加各種Handler, 知道這些你就基本可以隨意定制化了 下面就是我實現(xiàn)的json 格式的Formater,支持控制臺顏色變化,當然前提是你的控制終端支持(Ubuntu14.04測試通過)

 

import reimport loggingimport socketimport jsonimport tracebackimport datetimeimport timetry:  from collections import OrderedDictexcept ImportError:  passRESERVED_ATTRS = (  'args', 'asctime', 'created', 'exc_info', 'exc_text', 'filename',  'funcName', 'levelname', 'levelno', 'lineno', 'module',  'msecs', 'message', 'msg', 'name', 'pathname', 'process',  'processName', 'relativeCreated', 'stack_info', 'thread', 'threadName')RESERVED_ATTR_HASH = dict(zip(RESERVED_ATTRS, RESERVED_ATTRS))COLORS ={  'HEADER' : '/033[95m',  'INFO' : '/033[94m',  'DEBUG' : '/033[92m',  'WARNING' : '/033[93m',  'ERROR' : '/033[91m',  'ENDC' : '/033[0m',}def merge_record_extra(record, target, reserved=RESERVED_ATTR_HASH):  for key, value in record.__dict__.items():    if (key not in reserved      and not (hasattr(key, "startswith")           and key.startswith('_'))):      target[key] = value  return targetdef get_host_info():  host_name = ''  local_ip = ''  try:    host_name = socket.gethostname()    local_ip = socket.gethostbyname(host_name)  except Exception, e:    pass  return host_name, local_ipclass JsonFormatterBase(logging.Formatter):  def __init__(self, *args, **kwargs):    logging.Formatter.__init__(self, *args, **kwargs)    self._required_fields = self.parse()    self._skip_fields = dict(zip(self._required_fields,self._required_fields))    self._skip_fields.update(RESERVED_ATTR_HASH)  def parse(self):    standard_formatters = re.compile(r'/((.+?)/)', re.IGNORECASE)    return standard_formatters.findall(self._fmt)  def add_fields(self, record ):    log_record = {}    for field in self._required_fields:      log_record[field] = record.__dict__.get(field)    host_name , local_ip = get_host_info()    log_record[u'@hostName'] = host_name    log_record[u'@localIp'] = local_ip    return log_record    #merge_record_extra(record, log_record, reserved=self._skip_fields)  def process_log_record(self, log_record):    """    Override this method to implement custom logic    on the possibly ordered dictionary.    """    try:      new_record = OrderedDict()    except Exception, e:      return log_record    key_list = [      'asctime',      'levelname',      '@hostName',      '@localIp',      'threadName',      'thread',      'name',      'pathname',      'lineno',      'message',    ]    for k in key_list:      new_record[k] = log_record.get(k)    new_record.update(log_record)    return new_record  def jsonify_log_record(self, log_record):    """Returns a json string of the log record."""    return json.dumps(log_record, ensure_ascii=False)  def format_col(self, message_str, level_name):    """    是否需要顏色    """    return message_str  def formatTime(self, record, datefmt=None):    ct = self.converter(record.created)    if datefmt:      s = time.strftime(datefmt, ct)    else:      t = time.strftime("%Y-%m-%d %H:%M:%S", ct)      s = "%s.%03d" % (t, record.msecs)    return s  def format(self, record):    if isinstance(record.msg, dict):      record.message = record.msg    elif isinstance(record.msg, list) or isinstance(record.msg, tuple):      record.message = record.msg    elif isinstance(record.msg, basestring):      record.message = record.getMessage().split('/n')    elif isinstance(record.msg, Exception):      record.message = traceback.format_exc(record.msg).split('/n')    else :      record.message = repr(record.msg)    if "asctime" in self._required_fields:      record.asctime = self.formatTime(record, self.datefmt)    #    # if record.exc_info and not message_dict.get('exc_info'):    #   message_dict['message'] = traceback.format_exception(*record.exc_info)    log_record = self.add_fields(record)    log_record = self.process_log_record(log_record)    message_str = self.jsonify_log_record(log_record)    message_str = self.format_col(message_str, level_name=record.levelname)    return message_strclass ConsoleFormater(JsonFormatterBase):  def __init__(self, *args, **kwargs):    JsonFormatterBase.__init__(self, *args, **kwargs)  def format_col(self, message_str, level_name):    if level_name in COLORS.keys():      message_str = COLORS.get(level_name) + message_str + COLORS.get('ENDC')    return message_str  def jsonify_log_record(self, log_record):    return json.dumps(log_record, ensure_ascii=False, indent=4)class JsonFileFormater(JsonFormatterBase):  def __init__(self, *args, **kewars):    JsonFormatterBase.__init__(self, *args, **kewars)  def jsonify_log_record(self, log_record):    return json.dumps(log_record, ensure_ascii=False)

配置

很多時候我們并不是這樣自己去實現(xiàn)一些Handler ,F(xiàn)ormater ,之類的代碼,用logging 提供的config 就能做到了,如何寫config下面舉個例子解釋下,

SC_LOGGING_CONF = {  "version": 1,  "disable_existing_loggers": False,  "formatters": {    "simple": {      "format": "%(asctime)s [%(levelname)s] [%(threadName)s:%(thread)d] [%(name)s:%(lineno)d] - %(message)s"    }  },  "handlers": {    "console": {      "class": "logging.StreamHandler",      "level": "DEBUG",      "formatter": "simple",      "stream": "ext://sys.stdout"    },    "info_file_handler": {      "class": "logging.handlers.RotatingFileHandler",      "level": "INFO",      "formatter": "simple",      "filename": PATH + "info-" + date.today().isoformat() + ".log",      "maxBytes": 10485760,      "backupCount": 20,      "encoding": "utf8"    },    "error_file_handler": {      "class": "logging.handlers.RotatingFileHandler",      "level": "ERROR",      "formatter": "simple",      "filename": PATH + "errors-" + date.today().isoformat() + ".log",      "maxBytes": 10485760,      "backupCount": 20,      "encoding": "utf8"    }  },    "": {      "level": "INFO",      "handlers": ["console", "info_file_handler", "error_file_handler"]    }  }}

首先定義了一個formater 叫simaple , 然后定義了三個Handler ,分別是輸出到控制臺,輸出到文件和info,error的。

 logging.config.dictConfig(CONFIG.SC_LOGGING_CONF)
通過這句就能讓這些配置產生效果了,這也是config.py做的事情,不需要寫很多代碼也能定制個性化的log.。

以上就是本文的全部內容,希望對大家的學習有所幫助。


發(fā)表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發(fā)表
主站蜘蛛池模板: 收藏| 闽清县| 镇巴县| 类乌齐县| 新乐市| 城步| 呼玛县| 湘乡市| 会东县| 鹤壁市| 商河县| 宜宾市| 兴文县| 盐亭县| 孙吴县| 手游| 青铜峡市| 吴川市| 蓬莱市| 安阳县| 繁峙县| 阿克| 石狮市| 抚远县| 图木舒克市| 万载县| 柳林县| 德兴市| 垦利县| 湖口县| 莱芜市| 当阳市| 屯昌县| 平武县| 仪征市| 积石山| 本溪市| 天峨县| 明星| 辽源市| 南乐县|