我们使用不同的Handler可以把日志输出到不同的地方,比如使用StreamHandler把日志输出到控制台上;使用FileHandler把日志输出到文件里。那我们同样可以把日志输出到数据库中,只需要自己实现一个Handler
日志输出到数据库
DatabaseLogHandler的实现:
import sys
import logging
import pymysql
import traceback
class DatabaseLogHandler(logging.Handler):
def __init__(self):
self.db = pymysql.connect(
host='127.0.0.1',
port=3306,
user='xxx',
passwd='xxx',
db='xxx')
self.table = 'xxx'
self.cursor = self.db.cursor()
self.db.commit()
logging.Handler.__init__(self)
def emit(self, record):
trace = None
if sys.exc_info()[2]:
trace = traceback.format_exc()
kwargs = {
'logger_name': record.name,
'level': record.levelname,
'level_no': record.levelno,
'msg': record.getMessage(),
'trace': trace,
'file_name': record.filename,
'func_name': record.funcName,
'path_name': record.pathname,
'line_no': record.lineno
}
keys = list(kwargs.keys())
for i in range(len(keys)):
keys[i] = '`' + keys[i] + '`'
keys = ','.join(keys)
values = ','.join(['%s'] * len(kwargs))
sql_insert = f"INSERT INTO {self.table}({keys}) VALUES ({values})"
try:
self.cursor.execute(sql_insert, tuple(kwargs.values()))
self.db.commit()
except Exception as e:
pass
DatabaseLogHandler的使用:
from logging import getLogger, INFO
from xxx import DatabaseLogHandler
logger = getLogger('api_log')
logger.setLevel(INFO)
dblog = DatabaseLogHandler()
logger.addHandler(dblog)
if __name__ == '__main__':
logger.info('日志的msg内容')
try:
1 / 0
except Exception as e:
logger.error(e)
效果:
与flask结合使用?
?model
from stock_flask.extensions import db
from stock_flask.models.base_model import BaseModel
class LogInfo(BaseModel):
"""日志信息"""
name = db.Column(db.String(20), comment='模块')
level = db.Column(db.String(10), comment='日志等级')
file = db.Column(db.String(30), comment='文件名')
func = db.Column(db.String(30), comment='方法名')
thread = db.Column(db.String(30), comment='线程')
message = db.Column(db.String(255), comment='日志信息')
traceback = db.Column(db.Text, comment='异常堆栈')
log
import os
import sys
import logging
import traceback
from logging.handlers import TimedRotatingFileHandler
from flask import current_app
from stock_flask.models.log_model import LogInfo
from stock_flask.settings import BASE_DIR
LOG_FILE_PATH = os.path.join(BASE_DIR, "run_time_log")
LOG_LEVEL = logging.INFO
class DatabaseLogHandler(logging.Handler):
def emit(self, record: logging.LogRecord) -> None:
tb = None
if sys.exc_info()[2]:
tb = traceback.format_exc()
log_info = {
"name": record.name,
"level": record.levelname,
"file": record.filename,
"func": record.funcName,
"thread": record.threadName,
"message": record.getMessage(),
"traceback": tb
}
with current_app.app_context():
LogInfo.insert(log_info)
class Log:
@staticmethod
def create_logger(name):
# 创建日志收集器
log_format = "%(asctime)s-%(name)s-%(levelname)s-%(filename)s-%(funcName)s-%(lineno)d: %(message)s"
formatter = logging.Formatter(fmt=log_format)
log = logging.getLogger(name)
log.setLevel(LOG_LEVEL)
# 输出到控制台
sh = logging.StreamHandler()
sh.setLevel(LOG_LEVEL)
sh.setFormatter(formatter)
log.addHandler(sh)
# 输出到日志文件(按时间轮转)
fh = TimedRotatingFileHandler(filename=LOG_FILE_PATH,
when="D",
interval=1,
backupCount=7,
encoding="utf8")
fh.setLevel(LOG_LEVEL)
fh.setFormatter(formatter)
log.addHandler(fh)
# 输出到数据库
dbh = DatabaseLogHandler()
dbh.setLevel(LOG_LEVEL)
log.addHandler(dbh)
return log
log = Log.create_logger('system')
|