日志功能描述:
写python项目时,需要用到日志类,需求为:日志信息可配置,提供几种类型不同的配置,并且日志既可以写到文本也可以写到数据库中。
实现时日志类直接使用python的logging,配置信息写到配置文件logging_data.conf,并使用logging.config.fileConfig(log_config_path)加载配置。写日志到数据库参考了log4mongo-1.6.0.tar.gz的写法,地址在:https://pypi.python.org/pypi/log4mongo/ ,同时每当在数据库写日志时,同时需要插入一些额外信息,比如:projectId runningId algorithmId,所以使用了python的logging.LoggerAdapter把额外信息添加进去。
以下是编写的配置和代码:
配置文件:logging_data.conf
[loggers]
keys=root,input,output,computer
[handlers]
keys=consoleHandler,inputfileHandler,outfileHandler,computerfileHandler,mysqlHandler
[formatters]
keys=fmt
[logger_root]
level=DEBUG
handlers=consoleHandler
[logger_input]
level=DEBUG
qualname=input
handlers=consoleHandler,inputfileHandler,mysqlHandler
propagate=0
[logger_output]
level=DEBUG
qualname=output
handlers=consoleHandler,outfileHandler,mysqlHandler
propagate=0
[logger_computer]
level=DEBUG
qualname=computer
handlers=consoleHandler,computerfileHandler,mysqlHandler
propagate=0
[handler_consoleHandler]
class=StreamHandler
level=DEBUG
formatter=fmt
args=(sys.stdout,)
[handler_inputfileHandler]
class=logging.handlers.RotatingFileHandler
level=DEBUG
formatter=fmt
args=('../../logs/input.log','a',20000,5,)
[handler_outfileHandler]
class=logging.handlers.RotatingFileHandler
level=DEBUG
formatter=fmt
args=('../../logs/out.log','a',20000,5,)
[handler_computerfileHandler]
class=logging.handlers.RotatingFileHandler
level=DEBUG
formatter=fmt
args=('../../logs/computer.log','a',20000,5,)
[handler_mysqlHandler]
class=MysqlHandler.MysqlHandler
#level=WARNING
level=DEBUG
args=("10.17.87.226","root","mysql123","aiadm")
[formatter_fmt]
format=%(asctime)s - %(filename)s:%(lineno)d - %(levelname)s - %(name)s - %(message)s
datefmt=[%Y-%m-%d %H:%M:%S]
从配置中可知:有三种日志配置,分别是input,output,computer,而写日志到数据库则使用了自己编写的MysqlHandler.MysqlHandler
日志包装类 logwrapper.py
#!/usr/bin/env python
#coding=UTF-8
import logging
import logging.config
import os,sys
try:
import thread
import threading
except ImportError:
thread = None
if thread:
_lock = threading.RLock()
else:
_lock = None
def _acquireLock():
"""
Acquire the module-level lock for serializing access to shared data.
This should be released with _releaseLock().
"""
if _lock:
_lock.acquire()
def _releaseLock():
"""
Release the module-level lock acquired by calling _acquireLock().
"""
if _lock:
_lock.release()
class LoggerAdapter(logging.LoggerAdapter):
def process(self,msg,kwargs):
try:
kwargs["extra"] = dict(kwargs["extra"],**self.extra)
except:
kwargs["extra"] = self.extra
return msg,kwargs
def setExtra(self,extra):
self.extra = extra
class logwrapper:
def __init__(self):
cur_dir = os.path.dirname(os.path.abspath(__file__))
log_config_path = cur_dir + "/logging_data.conf"
sys.path.append(cur_dir)
logging.config.fileConfig(log_config_path)
self.logger_dict = {}
def getLogger(self,name,extra = {}):
rv = None
_acquireLock()
try:
if name in self.logger_dict:
rv = self.logger_dict[name]
else:
logger = logging.getLogger(name)
rv = LoggerAdapter(logger, extra)
self.logger_dict[name] = rv
finally:
_releaseLock()
return rv
log_manager = logwrapper()
def getLogger(name=None):
return log_manager.getLogger(name)
写日志到数据库的类MysqlHander.py
import logging
import MySQLdb
import datetime
import sys
sys.path.append("..")
import data_service.mysql_context.mySQLWrap as mySQLWrap
_mysql_obj = None
class MysqlHandler(logging.Handler):
def __init__(self,host,user,passwd,db,charset="utf8",timeout=10,level=logging.NOTSET,reuse=True):
logging.Handler.__init__(self,level)
self.host = host
self.user = user
self.passwd = passwd
self.db = db
self.charset = charset
self.timeout = timeout
self.reuse = reuse
self.mysql_obj = None
self.connect_state = False
self._connect()
def _connect(self):
global _mysql_obj
if self.reuse and _mysql_obj:
self.mysql_obj = _mysql_obj
else:
self.mysql_obj = mySQLWrap.MySQLWrap()
ret = self.mysql_obj.connectDatabase(host=self.host,user=self.user,passwd=self.passwd,db=self.db,charset=self.charset,timeout=self.timeout)
if ret == False:
_mysql_obj = None
self.connect_state = False
self.printMysqlError(self.mysql_obj.getErrorStr(),"MysqlHandler mysql connect error")
else:
_mysql_obj = self.mysql_obj
self.connect_state = True
def emit(self,record):
if self.connect_state == False:
return
CRITICAL = 50
FATAL = CRITICAL
ERROR = 40
WARNING = 30
WARN = WARNING
INFO = 20
DEBUG = 10
NOTSET = 0
_level_names = {
'CRITICAL': CRITICAL,
'ERROR': ERROR,
'WARN': WARNING,
'WARNING': WARNING,
'INFO': INFO,
'DEBUG': DEBUG,
'NOTSET': NOTSET,
}
level = _level_names[record.levelname]
log_time = datetime.datetime.now().strftime("%Y-%m-%d %H:%M:%S")
log_desc = record.getMessage()
keys = set(record.__dict__)
project_id = "NULL"
running_id = "NULL"
algorithm_id = "NULL"
ext1 = ext2 = ext3 = ext4 = ext5 = ext6 = ext7 = ext8 = ""
if keys:
for key in keys:
if key == "project_id":
project_id = record.__dict__["project_id"]
elif key == "running_id":
running_id = record.__dict__["running_id"]
elif key == "Algorithm_id":
algorithm_id = record.__dict__["Algorithm_id"]
elif key == "ext1":
ext1 = record.__dict__["ext1"]
elif key == "ext2":
ext2 = record.__dict__["ext2"]
elif key == "ext3":
ext3 = record.__dict__["ext3"]
elif key == "ext4":
ext4 = record.__dict__["ext4"]
elif key == "ext5":
ext5 = record.__dict__["ext5"]
elif key == "ext6":
ext6 = record.__dict__["ext6"]
elif key == "ext7":
ext7 = record.__dict__["ext7"]
elif key == "ext8":
ext8 = record.__dict__["ext8"]
sql = '''INSERT INTO AI_ALGORITHM_LOGS(running_id,
project_id,Algorithm_id,log_time,level,log_desc,ext1,ext2,ext3,ext4,ext5,ext6,ext7,ext8)
VALUES('%s','%s','%s','%s','%d',"%s",'%s','%s','%s','%s','%s','%s','%s','%s')'''\
%(str(running_id),str(project_id),str(algorithm_id),log_time,level,log_desc,ext1,ext2,ext3,ext4,ext5,ext6,ext7,ext8)
#print sql
ret = self.mysql_obj.exeSQLcmd(sql)
if ret == False:
self.printMysqlError(self.mysql_obj.getErrorStr(),"MysqlHandler insert mysql log error")
def printMysqlError(self,str,msg):
print "%s %s" %(msg,str)
def close(self):
self.mysql_obj.closeDatabase()
def __del__(self):
self.mysql_obj.closeDatabase()
def __exit__(self, exc_type, exc_val, exc_tb):
self.mysql_obj.closeDatabase()
其中projectId runningId algorithmId是每条日志都必须到数据库插入的数据,而ext1 -> ext8则是可能插入的一些额外信息。mySQLWrap.MySQLWrap是自己封装的使用mysqldb操作数据库的一个类。
测试使用logtest.py
#!/usr/bin/env python
#coding=UTF-8
import logwrapper
logwrapper = logwrapper.getLogger("input")
logwrapper.setExtra({"project_id":2,"running_id":0,"Algorithm_id":1})
#handler = MysqlHandler.MysqlHandler(host="10.17.87.226",user="root",passwd="mysql123",db="aiadm")
#logger.addHandler(handler)
logger.info("mysql logger %d,%s",1,"hello",extra={"ext3":"extrss","ext4":"extss444"})
logger.info("mysql logger %d,%s",1,"hello",extra={"ext1":"extrss","ext2":"extss444"})
logger.info("test input info logger %d,%s",1,"hello")
logger.warning("test input warning logger %d,%s",1,"hello")
logger.error("test input logger %d,%s",1,"hello")
logger.critical("test input logger %d,%s",1,"hello")
try:
1/0
except:
logger.exception("except")
logger = logwrapper.getLogger("out")
logger.debug("test out debug logger %d,%s",1,"hello")
logger.info("test out info logger %d,%s",1,"hello")