1. 日志工具
代码检查,debug,调优都只能让代码确保当时是可靠的,一些复杂的关联错误,也可能让这些测试呀debug呀失准,,而只有日志才能长期的帮助我们监控项目的健壮性.这种时候就可以使用标准库logging为程序的运行做记录,在试运行之后通过分析logging记录的方式来debug.
在logging框架下首先我们需要初始化一个logger来处理log,之后通过添加handler,Formatter和config子属性来自定义我们的logger.
一个简单的例子
import logging import sys #日志的名字,会在每行的一开始写 logger = logging.getLogger("endlesscode") #格式化 formatter = logging.Formatter('%(name)-12s %(asctime)s %(levelname)-8s %(message)s', '%a, %d %b %Y %H:%M:%S',) #设定输出文件 file_handler = logging.FileHandler("src/test.log") #为handler设置输出格式 file_handler.setFormatter(formatter) #流控制,将信息输出到标准流输出 stream_handler = logging.StreamHandler(sys.stderr) #为logger设置handler logger.addHandler(file_handler) #发送信息到流 logger.addHandler(stream_handler) #设置报错等级 #logger.setLevel(logging.ERROR) #报错 logger.error("w") #移除handler logger.removeHandler(stream_handler) #报错 logger.error("f") w 其中
- level: 设置日志级别,默认为logging.WARNING 
- stream: 指定将日志的输出流,可以指定输出到sys.stderr,sys.stdout或者文件,默认输出到sys.stderr,当stream和filename同时指定时,stream被忽略 
1.1. 输出文本的格式化
| 元素 | 格式化字符串 | 描述 | 
|---|---|---|
| args | 不用格式化 | 参数会是一个元组 | 
| asctime | %(asctime)s | 可读的时间 | 
| created | %(created)f | 记录的创建时间 | 
| filename | %(filename)s | 文件名 | 
| funcName | %(funcName)s | 函数名 | 
| levelname | %(levelname)s | 错误,警报等的名字 | 
| levelno | %(levelno)s | 错误,警报等,是预警等级 | 
| lineno | %(lineno)d | 报错行数 | 
| module | %(module)s | 报错模块 | 
| msecs | %(msecs)d | 毫秒级的出错时间 | 
| message | %(message)s | 错误信息 | 
| name | %(name)s | log的名字 | 
| pathname | %(pathname)s | 报错文件所在path | 
| process | %(process)d | 进程id | 
| processName | %(processName)s | 进程名 | 
| relativeCreated | %(relativeCreated)d | 微秒级的报错时间 | 
| thread | %(thread)d | 线程id | 
| threadName | %(threadName)s | 线程名 | 
1.2. 日志回滚
日志也不是一直记录就好,也要考录时效性和存储空间的限制,回滚机制便是解决这个问题的
from logging.handlers import RotatingFileHandler #定义一个RotatingFileHandler,最多备份5个日志文件,每个日志文件最大10M Rthandler = RotatingFileHandler('src/myapp.log', maxBytes=10*1024*1024,backupCount=5) Rthandler.setLevel(logging.INFO) formatter = logging.Formatter('%(name)-12s: %(levelname)-8s %(message)s') Rthandler.setFormatter(formatter) logging.getLogger('').addHandler(Rthandler) 1.3. 几种handler
- StreamHandler(stream=None) 流输出 
- FileHandler(filename, mode='a', encoding=None, delay=False) 写入文件 
- WatchedFileHandler(filename[, mode[, encoding[, delay]]]) 监控log文件 
- RotatingFileHandler(filename, mode='a', maxBytes=0, backupCount=0, encoding=None, delay=0) 轮替日志,根据日志文件的大小来循环 
- TimedRotatingFileHandler(filename, when='h', interval=1, backupCount=0, encoding=None, delay=False, utc=False, atTime=None) 轮替日志,根据时间来循环,interval参数可选的值有: - "S"-Seconds
- 'M'-Minutes
- 'H'-Hours
- 'D'-Days
- 'W0'~'W6'-Weekday (0=Monday)
- 'midnight'-半夜循环
 
- SocketHandler(host, port) 把log送到网上的socket 
- DatagramHandler(host, port) 把log送到网上的UDP sockets 
- SysLogHandler(address=('localhost', SYSLOG_UDP_PORT), facility=LOG_USER, socktype=socket.SOCK_DGRAM) log送到unix系统log 
- SMTPHandler(mailhost, fromaddr, toaddrs, subject, credentials=None, secure=None, timeout=1.0) log送到电子邮箱 
- MemoryHandler(capacity, flushLevel=ERROR, target=None) log存入内存 
- HTTPHandler(host, url, method='GET', secure=False, credentials=None, context=None) log通过http网络送到服务器 
1.4. 使用.conf设置文件设置logging行为
当然可以在程序中设置log了,但为了改起来方便也可以写在别的文件中然后用config.fileConfig(path)来设置,配置文件的形式是这样:
[loggers] keys=root,simpleExample  [handlers] keys=consoleHandler  [formatters] keys=simpleFormatter  [logger_root] level=DEBUG handlers=consoleHandler  [logger_simpleExample] level=DEBUG handlers=consoleHandler qualname=simpleExample propagate=0  [handler_consoleHandler] class=StreamHandler level=DEBUG formatter=simpleFormatter args=(sys.stdout,)  [formatter_simpleFormatter] format=%(asctime)s - %(name)s - %(levelname)s - %(message)s datefmt=%a, %d %b %Y %H:%M:%S 要注意的是如果用这种方式那么,使用rotation file handler时,不要同时声明file handler,否则rotation发生时,doRollover 函数的os.rename 会报错(「另一个程序正在使用此文件,进程无法访问).当然,可以写另一个py文件专门用来初始化,要用的时候import进来就好了.
1.5. 使用字典配置logging行为
上面一种看起来比较晦涩难懂难以维护,更加pythonic的做法是使用字典进行配置
import sys import logging import logging.config LOGGING_CONFIG = dict(     version=1,     loggers={         "<模块>":{             "level": "INFO",             "handlers": ["model_console"]         },         "<服务>": {             "level": "INFO",             "handlers": ["server_console"]         }     },     handlers={         "model_console": {             "class": "logging.StreamHandler",             "formatter": "model",             "stream": sys.stdout         },         "server_console": {             "class": "logging.StreamHandler",             "formatter": "server",             "stream": sys.stdout         }     },     formatters={         "model": {             "format": "%(asctime)s :: %(name)s  :: %(levelname)s :: %(process)d :: "+             "%(module)s - line %(lineno)d - funcname: %(funcName)s - params: %(params)s :: %(message)s",             "datefmt": "[%Y-%m-%d %H:%M:%S %z]",             "class": "logging.Formatter"         },         "server": {             "format": "%(asctime)s :: %(name)s :: %(levelname)s :: %(host)s :: " +                       "%(request)s :: %(message)s",             "datefmt": "[%Y-%m-%d %H:%M:%S %z]",             "class": "logging.Formatter"         }     } ) logging.config.dictConfig(LOGGING_CONFIG) model_logger = logging.getLogger('<模块>') server_logger = logging.getLogger('<服务>')  def a():     model_logger.info("qwer",extra= dict(params = ["123",12])) a() [2018-06-06 17:35:01 +0800] :: <模块>  :: INFO :: 27392 :: <ipython-input-7-0aa5e3aa1d5b> - line 6 - funcname: a - params: ['123', 12] :: qwer 1.6. 结构化log
现在很多场景尤其是ELK体系下的log要求使用json格式.我们可以借助了第三方工具structlog用于结构化log数据
结构化log需要配置两块:
- structlog部分需要配置
import structlog  structlog.configure(     processors=[         structlog.stdlib.filter_by_level, # 判断是否接受某个level的log消息         structlog.stdlib.add_logger_name, # 增加字段logger         structlog.stdlib.add_log_level, #增加字段level         structlog.stdlib.PositionalArgumentsFormatter(),         structlog.processors.TimeStamper(fmt="iso"), #增加字段timestamp且使用iso格式输出         structlog.processors.StackInfoRenderer(),         structlog.processors.format_exc_info,# 捕获异常的栈信息         structlog.processors.StackInfoRenderer(), # 详细栈信息         structlog.processors.JSONRenderer() #json格式输出,第一个参数会被放入event字段     ],     context_class=dict,     logger_factory=structlog.stdlib.LoggerFactory(),     wrapper_class=structlog.stdlib.BoundLogger,     cache_logger_on_first_use=True, ) - 配置标准库的log
import logging import sys handler = logging.StreamHandler(sys.stdout) root_logger = logging.getLogger() root_logger.addHandler(handler)  root_logger.setLevel(logging.INFO) # 设置最低log等级 之后使用structlog.get_logger构造logger,就可以使用它来输出结构化的log了
w 0w 1w 2w 3我们还可以使用structlog.processors.format_exc_info来自动捕获异常信息,使用structlog.processors.StackInfoRenderer()来获取栈信息
w 4w 5


 
		 
		 
		

还没有评论,来说两句吧...