Log日志
1 Logging
日志是对程序执行过程中进行事件的记录追踪方式
1.1 logging输出配置
basicConfig() # 基础配置
fileConfig() # 通过读取配置文件配置
dictConfig() # 通过字典配置logger handler filter等
listen() # 通过监听网络端口 接收配置文件数据进行配置
1.2 配置实例-basicConfig
# Pyhton内置模块logging使用
import logging
Log_Level='日志等级'
Log_Format = '日志输出格式'
File_Name = '日志保存的文件名'
File_Mode = '日志打开|写入方式' # 没有filemode 默认为追加
Date_fmt = '自定义输出的日期格式'
# 日志配置信息
logging.basicConfig(level=Log_Level,
format=Log_Format,
filename=File_Name,
filemode=File_Mode,
datefmt=Date_fmt,
)
# basicConfig其他可选参数
# stream:相当于创建StreamHandler 指定控制台输出 与 filename冲突
# filename:相当于创建FileHandler 指定文本输出
level参数
日志输出等级 >= 配置的等级
- DEBUG:程序运行的详细信息 每次请求 每个操作都会记录
- INFO:日常程序运行正常信息
- WARNING:警告信息-程序|程序功能不受影响
- ERROR:严重错误-程序部分功能失效
- CRITICAL:严重问题-程序崩溃
format参数
规范日志输出的具体内容
%(levelno)s: 输出日志级别数值
%(levelname)s: 输出日志级别名称
%(pathname)s: 输出执行程序的路径
%(filename)s:输出执行程序的文件名称
%(funcName)s:输出日志的当前函数
%(lineno)d:输出日志的行号
%(asctime)s:输出日志的时间
%(thread)d:输出线程id
%(threadName)s: 输出线程名称
%(process)d: 输出进程id
%(message)s: 输出日志信息
datefmt参数
datefmt = '%Y/%m/%d %I:%M:%S %p' # 2020/12/22 11:46:36 AM
1.3 Logger
Logger 日志器:暴露程序的使用的接口信息 即产生一个关联程序的日志接口
-创建:logger_obj = logging.getLogger(name='logger_name') 默认名root
-设置总的log输出等级:logger_obj.setLevel(logging.INFO)
-关联Handler处理器指定输出: logger_obj.addHandler(handler_name)
-关联Filter:logger_obj.addFilter(filter_name)
1.4 Handler
Handler 处理器:处理logger产生的日志记录到指定的位置
StreamHandler # 控制台日志输出
-创建: sh = logging.StreamHandler(stream=None) 默认stream=sys.stdout
-设置日志输出等级:sh.setLevel(logging.INFO)
-设置日志输出格式:sh.setFormatter('Formatter_name')
-设置日志输出的过滤规则: sh.addFilter('Filter_name')
FileHandler # 文本输出
-创建:logging.FileHandler(filename='日志文件名', mode='写入模式', encoding='编码模式')
RotatingFileHandler # 支持磁盘文件回滚输出
-创建: logging.handlers.RotatingFileHandler(filename='',
mode='',
maxBytes=0,
backupCount=0)
# maxBytes:决定日志文件的长度 设置maxBytes&&backupCount >0 可实现回滚
# backupCount:决定备份的文件数
import logging.handlers
logger = logging.getLogger()
rh = logging.handlers.RotatingFileHandler(filename='./test.log',
mode='a',
maxBytes=10,
backupCount=3,
encoding=None,
delay=0)
rh.setLevel(level=logging.ERROR)
rh.setFormatter(logging.Formatter("%(name)s:%(asctime)s-%(levelname)s-%(message)s"))
logger.addHandler(rh)
# 测试数据
logging.debug('debug message')
logging.info('info message')
logging.warning('warning message')
logging.error('error message')
logging.critical('critical message')

Formatter 格式化器: 输出特定格式的日志信息
-创建: f = logging.Formatter(fmt='', datefmt='', style='')
-fmt 默认消息格式化字符串->%(message)s
-datefmt 默认日期格式化字符串->'%Y-%m-%d %H:%M:%S'
-style 默认取值'%'格式 可选{ | $
1.6 Filter
Filter 过滤器: 过滤输出特定的日志信息
-创建:
class MyFilter(logging.Filter):
def filter(self, record):
print('record', 'LogRecord')
# 每个日志信息都保存在LogRecord实例
pass
-关联:与logger全局关联 通过过滤规则完成日志输出 | 与logger下handler局部关联
-logger_obj.addFilter(MyFilter())
-handler_obj.addFilter(MyFilter())
1.7 日志输出 -basciConfig
1.7.1 控制台输出
# 控制台输出(默认)-stream
import logging
Log_Level = logging.INFO
Log_Format = '%(asctime)s-{%(process)d-[%(threadName)s:%(thread)d]} %(pathname)s-%(filename)s[line:%(lineno)d]--->%(funcName)s- %(levelname)s - %(message)s'
Date_fmt = '%Y/%m/%d %I:%M:%S %p'
logging.basicConfig(level=Log_Level,
format=Log_Format,
datefmt=Date_fmt,
)
# 测试数据
logging.debug('debug message')
logging.info('info message')
logging.warning('warning message')
logging.error('error message')
logging.critical('critical message')

1.7.2 文本输出
# 提供filename参数
import logging
Log_Level = logging.INFO
Log_Format = '%(asctime)s-{%(process)d-[%(threadName)s:%(thread)d]} %(pathname)s-%(filename)s[line:%(lineno)d]--->%(funcName)s- %(levelname)s - %(message)s'
File_Name = './log1.log'
File_Mode = 'w' # 没有filemode 默认为追加a
logging.basicConfig(level=Log_Level,
format=Log_Format,
filename=File_Name,
filemode=File_Mode,
Date_fmt = '%Y/%m/%d %I:%M:%S %p',
)
# 测试数据
logging.debug('debug message')
logging.info('info message')
logging.warning('warning message')
logging.error('error message')
logging.critical('critical message')

1.7.3 控制台+文本 输出
import logging
# 显式创建logger记录器(默认会创建一个root logger并引用默认日志等级warn)
import sys
logger = logging.getLogger()
logger.setLevel(logging.INFO) # 记录器记录的Log等级
# 创建FileHandler 指定日志信息输出到日志文件
logfile = './log1.log'
fh = logging.FileHandler(logfile, mode='w')
fh.setLevel(logging.DEBUG) # 设置输出到文本的log等级
# 创建StreamHandler 指定日志信息输出到控制台
kh = logging.StreamHandler(stream=sys.stdout)
kh.setLevel(logging.WARNING) # 设置输出到控制台的log等级
# 创建Formatter 统一输出格式
_formatter = logging.Formatter("%(asctime)s - %(levelname)s: %(message)s")
# 设置handler输出格式
fh.setFormatter(_formatter)
kh.setFormatter(_formatter)
# 创建Filter 过滤输出需要的日志信息
class MyFilter(logging.Filter):
def filter(self, record):
# 过滤输出指定日志等级 warn error
# record:为日志信息
if record.__dict__.get('levelname') in ['WARNING', 'ERROR']:
return True
else:
return False
# handler关联filter(局部控制输出的log日志等级信息)
# kh.addFilter(MyFilter())
# logger关联handler
logger.addHandler(fh)
logger.addHandler(kh)
# # logger关联过滤器(全局控制输出log日志等级)
# logger.addFilter(MyFilter())
# 测试数据
logging.debug('debug message')
logging.info('info message')
logging.warning('warning message')
logging.error('error message')
logging.critical('critical message')

1.8 日志输出-fileConfig
# logconfig.conf
[loggers]
keys=root
# default:root-logger
[handlers]
keys= kh, fh
[formatters]
keys=FhFormatter, KhFormatter
# logger-setting_detail
[logger_root]
level=INFO
handlers= kh, fh
#kh_handler_setting
[handler_kh]
class = StreamHandler
level = WARNING
formatter = KhFormatter
args=()
# fh_handler_setting
[handler_fh]
class = FileHandler
level = DEBUG
formatter = FhFormatter
args=('./log3.log', 'w')
# formatter_setting
[formatter_FhFormatter]
format=%(name)s:%(asctime)s - %(levelname)s - %(message)s
datefmt = %Y/%m/%d %H:%M:%S
[formatter_KhFormatter]
format=%(name)s:%(asctime)s - %(levelname)s - %(message)s
import logging.config
# 通过配置文件logconfig.conf控制日志输出
# xx.conf配置文件不能出现中文 否则报错gbk
logging.config.fileConfig('logconfig.conf')
# 创建默认名root的logger
logger = logging.getLogger()
# 测试数据
logging.debug('debug message')
logging.info('info message')
logging.warning('warning message')
logging.error('error message')
logging.critical('critical message')

1.9 日志输出-dictConfig
1.9.1 dictConfig: dict
import logging.config
# logging配置信息 可以是dict | json | yaml
config_dict = {
'version': 1,
'formatters': {
'FhFormat': {
'class': 'logging.Formatter',
'format': '%(asctime)s %(name)-15s %(levelname)-8s %(processName)-10s %(message)s'
},
'ChFormat': {
'class': 'logging.Formatter',
'format': '%(asctime)s-%(levelname)s-%(message)s'
}
},
'handlers': {
'ch': {
'class': 'logging.StreamHandler',
'level': 'INFO',
'formatter': 'ChFormat'
},
'fh': {
'class': 'logging.FileHandler',
'level': 'WARNING',
'filename': './log4.log',
'mode': 'w',
'formatter': 'FhFormat',
},
},
# 可指定多个logger
# 'loggers': {
# 'foo': {
# 'handlers': ['foofile']
# }
# },
'root': {
'level': 'DEBUG',
'handlers': ['ch', 'fh', ]
},
}
# 通过字典配置logging输出
logging.config.dictConfig(config_dict)
# 测试数据
logging.debug('debug message')
logging.info('info message')
logging.warning('warning message')
logging.error('error message')
logging.critical('critical message')

1.9.2 dictConfig: json
{
"version": 1,
"formatters": {
"FhFormat": {
"class": "logging.Formatter",
"format": "%(asctime)s %(name)-15s %(levelname)-8s %(processName)-10s %(message)s"
},
"ChFormat": {
"class": "logging.Formatter",
"format": "%(asctime)s-%(levelname)s-%(message)s"
}
},
"handlers": {
"ch": {
"class": "logging.StreamHandler",
"level": "INFO",
"formatter": "ChFormat"
},
"fh": {
"class": "logging.FileHandler",
"level": "WARNING",
"filename": "./log4.log",
"mode": "w",
"formatter": "FhFormat"
}
},
"root": {
"level": "DEBUG",
"handlers": [
"ch",
"fh"
]
}
}
# 通过xx.json文件进行配置logging日志输出
with open(xx.json, 'r') as f
_config = json.load(f)
logging.config.dictConfig(_config)
1.9.3 dictConfig:yaml
version: 1
formatters:
FhFormat:
class: logging.Formatter
format: "%(asctime)s %(name)-15s %(levelname)-8s %(processName)-10s %(message)s"
ChFormat:
class: logging.Formatter
format: "%(asctime)s-%(levelname)s-%(message)s"
handlers:
ch:
class: logging.StreamHandler
level: INFO
formatter: ChFormat
fh:
class: logging.FileHandler
level: WARNING
filename: ./log4.log
mode: w
formatter: FhFormat
root:
level: DEBUG
handlers: [ch, fh]
# 通过ymal文件进行日志文件输出配置(pip install pyyaml)
with open('logconfig.yaml', 'r') as f:
_config = yaml.load(f, Loader=yaml.SafeLoader)
logging.config.dictConfig(_config)
1.10 日志输出-listen()
# 相当于服务端日志平台 通过监听端口 接收特定配置文件更改logging日志输出配置
import logging.config, logging.handlers
import time
# 通过监听特定端口加载logging配置信息
t = logging.config.listen(8888)
print(t.name, t.port)
t.start()
# 创建默认的logger
logger = logging.getLogger()
try:
while True:
# 测试数据刷新
logging.debug('debug message')
logging.info('info message')
logging.warning('warning message')
logging.error('error message')
logging.critical('critical message')
time.sleep(5)
print('='*50)
except:
# 停止监听
logging.config.stopListening()
t.join()
# 发送方 发送特定配置文件给服务端实质是tcp连接
# 这里引用上面的logconfig.conf配置文件
import socket, sys, struct
try:
with open(sys.argv[1], 'rb') as f:
data_to_send = f.read()
except:
print('缺少执行参数请使用: python xx.py xx.conf')
# 连接的服务端ip:port
HOST = 'localhost'
PORT = 8888
# 创建tcp套接字
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
print('connecting...')
# 连接
s.connect((HOST, PORT))
print('sending config...')
# 发送二进制的配置文件内容
s.send(struct.pack('>L', len(data_to_send)))
s.send(data_to_send)
# 关闭套接字
s.close()
print('complete')

2 Traceback
2.1 traceback
Traceback模块
追踪输出完成的异常信息
-异常出现的代码行
-详细的异常类型
-异常信息输出详细|简略
实质是通过traceback_obj对象获取异常信息
exc_type, exc_value, exc_traceback_obj = sys.exc_info()
traceback.print_tb(exc_traceback_ojb)
pip install traceback # 安装模块
2.2 traceback实例
import sys
import traceback
def test():
raise Exception('异常信息')
try:
test()
except Exception as e:
print(sys.exc_info())
print('-'*100)
exc_type, exc_val, exc_traceback_obj = sys.exc_info()
print(exc_type, exc_val, exc_traceback_obj)
print('-' * 100)
traceback.print_tb(exc_traceback_obj)
print('-' * 100)
traceback.print_exception(exc_type, exc_val, exc_traceback_obj, limit=2, file=sys.stdout)
print('-' * 100)
traceback.print_exc(limit=1, file=sys.stdout)

2.3 logging + traceback
traceback.format_exc() :字符串形式输出异常信息
traceback.print_exc(): 打印输出异常信息
traceback.print_exc(file=open('error.log', 'a')) # 异常信息存储到指定文件
2.4 logging+traceback实例
import logging
import traceback
logging.basicConfig(level=logging.INFO,
format='%(asctime)s -%(levelname)s -%(message)s',
filename='./log2.log',
filemode='w')
try:
res = 5/0
logging.info('info:res-success')
except:
# 打印异常信息详情
traceback.print_exc()
# 格式化异常信息打印输出到日志
logging.error(f'error: {str(traceback.format_exc())}')

3 flask + logging+ traceback
import logging
import traceback
from flask import Flask
from flask_restful import Api, Resource, fields
app = Flask(__name__)
api = Api(app)
class Test(Resource):
def get(self):
res = 5/0
return {'name': 'fsh', 'job': 'python trainee'}
# 路由配置
def route_init():
api.add_resource(Test, '/')
# 日志处理
Log_format = '%(asctime)s - %(levelname)s - %(message)s'
logging.basicConfig(filename='./flask.log', level=logging.INFO, format=Log_format)
try:
route_init()
except Exception as e:
logging.info('find-error')
logging.exception(f'error:{str(e)}')
logging.exception(f'traceback:{str(traceback.format_exc())}')
if __name__ == '__main__':
app.run()

4 gunicorn+flask+ logging
gunicorn 全名green unicorn 高并发的Python WSGI Unix Http服务器
pip install gunicorn
# gunicorn-conf.py
# 添加logs目录
from multiprocessing import cpu_count
workers = (cpu_count() * 2 + 1) if cpu_count() < 10 else 20 # 开启的进程数
worker_class = 'eventlet' # 工作模式默认同步(sync) |(异步:gevent/eventlet)
threads = 2 # 设置每个进程的线程数
loglevel = 'info' # 设置日志等级与accesslog相关
bind = 'localhost:9999' # 监听的ip:port
pidfile = 'logs/gunicorn.pid' # 进程号保存路径
accesslog = 'logs/access.log' # 请求日志
errorlog = 'logs/error.log' # 运行产生的具体日志信息
from flask import Flask
app = Flask(__name__)
@app.route('/')
def test():
return {"a": 'test_data'}
if __name__ == '__main__':
app.run()
# terminal
# 第一个app:flask实例所在文件; 第二个app:flask实例名
# 通过配置文件启动gunicorn
gunicorn -c gunicorn-conf.py app:app

5 帮助文档