python 日志封装

日志功能描述:

写python项目时,需要用到日志类,需求为:日志信息可配置,提供几种类型不同的配置,并且日志既可以写到文本也可以写到数据库中。 实现时日志类直接使用python的logging,配置信息写到配置文件logging_data.conf,并使用logging.config.fileConfig(log_config_path)加载配置。写日志到数据库参考了log4mongo-1.6.0.tar.gz的写法,同时每当在数据库写日志时,同时需要插入一些额外信息,比如: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")