日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程语言 > python >内容正文

python

python和log有啥区别_细说 Python logging

發布時間:2024/9/19 python 36 豆豆
生活随笔 收集整理的這篇文章主要介紹了 python和log有啥区别_细说 Python logging 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

(可在我的博客文章)

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

初看log

在程序中, log 的用處寫代碼的你用你知道,log 有等級,DEBUG, INFO,...之類,還會記錄時間,log 發生的位置,在Python 中用的多的就是logging 這個標準庫中的包了.當打log 的時候究竟發生了什么? 是如何把不同級別的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

Level

Numeric value

CRITICAL

50

ERROR

40

WARNING

30

INFO

20

DEBUG

10

NOTSET

0

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

logger 流程,

整個過程,還是不是很詳細,貼個圖吧, 現在看還太早,也說不清真個過程到底發生了什么,先放著,回頭來看會比較好懂.

loger flow

讀代碼

代碼結構

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

├── config.py

├── handlers.py

└── __init__.py

_int.py中實現了基礎功能,主要的邏輯就在這個文件中

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 ''%(self.name, self.levelno,

self.pathname, self.lineno, self.msg)

def getMessage(self):

pass

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

Formatter Objects

Formatter 就是對Record 專門格式化的對象,它有一個format 方法,我們實現這個方法就能 做到不同的輸出,我的需求是做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

刪掉源代碼中的實現細節,這個類里面主要的是format 方法,這是默認最基本的Formater ,還有專門對exception ,時間做格式化的方法。具體是哪個,看方法名就很清楚了,具體每個方法怎么實現的,一眼也就懂了。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,

知道這些你就基本可以隨意定制化了

下面就是我實現的json 格式的Formater,支持控制臺顏色變化,當然前提是你的控制終端支持(Ubuntu14.04測試通過)

import re

import logging

import socket

import json

import traceback

import datetime

import time

try:

from collections import OrderedDict

except ImportError:

pass

RESERVED_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 target

def 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_ip

class 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_str

class 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)

配置

很多時候我們并不是這樣自己去實現一些Handler ,Formater ,之類的代碼,用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.。

總結

以上是生活随笔為你收集整理的python和log有啥区别_细说 Python logging的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。