Source code for cup.log

#!/usr/bin/python
# -*- coding: utf-8 -*
# #############################################################################
#
#  Copyright (c) 2014 Baidu.com,  Inc. All Rights Reserved
#
# #############################################################################

"""
:author:
    Guannan Ma
:create_date:
    2014
:last_date:
    2014
:descrition:
    common log related module
"""

__all__ = [
    'debug', 'info', 'warn', 'critical',
    'init_comlog', 'setloglevel',
    'ROTATION', 'INFINITE'
]


import os
import sys
import threading
import logging
# import traceback
from logging import handlers

import cup
from cup import err
from cup import platforms


ROTATION = 0
INFINITE = 1

ROTATION_COUNTS = 30

DEBUG = logging.DEBUG

INFO = logging.INFO

ERROR = logging.ERROR

CRITICAL = logging.CRITICAL


class _Singleton(object):  # pylint: disable=R0903
    """
    Singleton你的类.
    """
    _LOCK = threading.Lock()

    def __init__(self, cls):
        self.__instance = None
        self.__cls = cls

    def __call__(self, *args, **kwargs):
        self._LOCK.acquire()
        if self.__instance is None:
            self.__instance = self.__cls(*args, **kwargs)
        self._LOCK.release()
        return self.__instance


# pylint: disable=R0903
@_Singleton
class _LoggerMan(object):
    _instance = None
    _pylogger = None
    _maxsize = 0
    _b_rotation = False
    _logfile = ''
    _logtype = ROTATION

    def __init__(self):
        pass

    def _getlogger(self):
        if self._pylogger is None:
            raise err.LoggerException(
                'The Cup logger has not been initalized Yet. '
                'Call init_comlog first'
            )
        return self._pylogger

    def _setlogger(self, logger):
        if self._pylogger is not None:
            raise err.LoggerException(
                """WARNING!!! The cup logger has been initalized already\
                .Plz do NOT init_comlog twice""")
        self._pylogger = logger

    def _reset_logger(self, logger):
        del self._pylogger
        self._pylogger = logger

    def is_initalized(self):
        """
            Initialized or not
        """
        if self._pylogger is None:
            return False
        else:
            return True

    def _config_filelogger(
        self, loglevel, strlogfile, logtype, maxsize, bprint_console
    ):  # too many arg pylint: disable=R0913
        if not os.path.exists(strlogfile):
            try:
                os.mknod(strlogfile)
            except IOError:
                raise err.LoggerException(
                    'logfile does not exist. '
                    'try to create it. but file creation failed'
                )
        self._logfile = strlogfile
        self._logtype = logtype
        self._pylogger.setLevel(loglevel)
        self._maxsize = maxsize
        # '%(asctime)s - %(levelname)s - %(filename)s:%(lineno)s - %(message)s'
        formatter = logging.Formatter(
            '%(levelname)s: %(asctime)s %(message)s'
        )
        if bprint_console:
            streamhandler = logging.StreamHandler()
            streamhandler.setLevel(loglevel)
            streamhandler.setFormatter(formatter)
            self._pylogger.addHandler(streamhandler)
        fdhandler = None
        if logtype == ROTATION:
            fdhandler = handlers.RotatingFileHandler(
                self._logfile, 'a', maxsize, ROTATION_COUNTS, encoding='utf-8'
            )
        else:
            fdhandler = logging.FileHandler(
                self._logfile, 'a', encoding='utf-8'
            )
        fdhandler.setFormatter(formatter)
        fdhandler.setLevel(loglevel)
        self._pylogger.addHandler(fdhandler)


def _line(back=0):
    return sys._getframe(back + 1).f_lineno  # traceback pylint:disable=W0212


def _file(back=0):
    # pylint:disable=W0212
    return os.path.basename(sys._getframe(back + 1).f_code.co_filename)


# def _func(back=0):
#     # traceback functionality. pylint:disable=W0212
#     return sys._getframe(back + 1).f_code.co_name \


def _proc_thd_id():
    # return str(os.getpid()) # traceback functionality. pylint:disable=W0212
    return str(os.getpid()) + ':' + str(threading.current_thread().ident)


def _log_file_func_info(msg, back_trace_len=0):
    tempmsg = ' * [%s] [%s:%s]' % (
        _proc_thd_id(), _file(2 + back_trace_len),
        _line(2 + back_trace_len)
    )

    msg = '%s%s' % (tempmsg, msg)
    if isinstance(msg, unicode):
        return msg
    else:
        return msg.decode('utf8')


[docs]def init_comlog( loggername, loglevel=logging.INFO, logfile='cup.log', logtype=ROTATION, maxlogsize=1073741824, bprint_console=False ): # too many arg pylint: disable=R0913 """ 初始化日志函数。用法如下:: :param loggername: 这个logger的名字. :param loglevel: 一共四种 logging.DEBUG logging.INFO logging.WARN logging.CRITICAL :param logfile: log文件的位置,如不存在,会尝试创建该文件 :param logtype: 支持两种leo.log.ROTATION cup.log.INFINITE ROTATION会在文件大小达到maxlogsize的时候进行switch. 一共会switch 30个文件, 然后在这30个文件里面ROTATION的写 INFINITE会一直写log文件 :param maxlogsize: logfile的最大文件大小(单位byte).超过会进行覆盖写或者switch. :param b_printcmd: 打印日志到logfile的同时,是否打印到stdout. 请注意,打印日志时要么打印unicode字符,要么打印Python默认的UTF8的字符 *E.g.* :: import logging from cup import log log.init_comlog( 'test', logging.DEBUG, '/home/work/test/test.log', log.ROTATION, 1024, False ) log.info('test xxx') log.critical('test critical') """ loggerman = _LoggerMan() if loggerman.is_initalized() is False: loggerman._setlogger(logging.getLogger(loggername)) if os.path.exists(logfile) is False: if platforms.is_linux(): os.mknod(logfile) else: with open(logfile, 'w+') as fhandle: fhandle.write('----Windows File Creation ----\n') elif os.path.isfile(logfile) is False: raise err.LoggerException( 'The log file exists. But it\'s not regular file' ) loggerman._config_filelogger( loglevel, logfile, logtype, maxlogsize, bprint_console ) # too many arg pylint: disable=w0212 info('-' * 20 + 'Log Initialized Successfully' + '-' * 20) else: print '[%s:%s] init_comlog has been already initalized' % \ (_file(1), _line(1)) return
def reinit_comlog( loggername, loglevel, logfile, logtype, maxlogsize, bprint_console ): # too many arg pylint: disable=R0913 """ 重新设置comlog, 参与意义同init_comlog. reinit_comlog会重新设置整个进程的参数, 但请注意loggername一定不能与 原来的loggername相同,否则可能出现打印两次的情况 """ loggerman = _LoggerMan() loggerman._reset_logger(logging.getLogger(loggername)) if os.path.exists(logfile) is False: if platforms.is_linux(): os.mknod(logfile) else: with open(logfile, 'w+') as fhandle: fhandle.write('----Windows File Creation ----\n') elif os.path.isfile(logfile) is False: raise err.LoggerException( 'The log file exists. But it\'s not regular file' ) loggerman._config_filelogger( loglevel, logfile, logtype, maxlogsize, bprint_console ) # too many arg pylint: disable=w0212 info('-' * 20 + 'Log Reinitialized Successfully' + '-' * 20) return def _fail_handle(msg, e): # print 'cup.log.info print to file failed. %s' % str(e) if not isinstance(msg, unicode): msg = msg.decode('utf8') print '%s\nerror:%s' % (msg, e)
[docs]def info(msg, back_trace_len=0): """ logging.INFO的日志打印 """ try: msg = _log_file_func_info(msg, back_trace_len) loggerman = _LoggerMan() loggerman._getlogger().info(msg) except err.LoggerException: return except Exception as e: _fail_handle(msg, e)
[docs]def debug(msg, back_trace_len=0): """ :param msg: logging.DEBUG级别的日志打印。 :param back_trace_len: 为扩展预留的参数, 正常使用可忽略。 """ try: msg = _log_file_func_info(msg, back_trace_len) loggerman = _LoggerMan() loggerman._getlogger().debug(msg) except err.LoggerException: return except Exception as e: _fail_handle(msg, e)
[docs]def warn(msg, back_trace_len=0): """ logging.WARN级别的日志打印 """ try: msg = _log_file_func_info(msg, back_trace_len) loggerman = _LoggerMan() loggerman._getlogger().warn(msg) except err.LoggerException: return except Exception as e: _fail_handle(msg, e)
def error(msg, back_trace_len=0): """ logging.ERROR级别的日志打印 """ try: msg = _log_file_func_info(msg, back_trace_len) loggerman = _LoggerMan() loggerman._getlogger().error(msg) except err.LoggerException: return except Exception as error: _fail_handle(msg, error)
[docs]def critical(msg, back_trace_len=0): """ logging.CRITICAL级别的日志打印 """ try: msg = _log_file_func_info(msg, back_trace_len) loggerman = _LoggerMan() loggerman._getlogger().critical(msg) except err.LoggerException: return except Exception as e: _fail_handle(msg, e)
[docs]def setloglevel(logginglevel): """ 进程运行时更改loglevel :param logginglevel: 四种, 同init_comlog的loglevel """ loggerman = _LoggerMan() loggerman._getlogger().setLevel(logginglevel)
if __name__ == '__main__': cup.log.debug('中文') cup.log.init_comlog( 'test', cup.log.DEBUG, './test.log', cup.log.ROTATION, 102400000, False ) cup.log.init_comlog( 'test', cup.log.DEBUG, './test.log', cup.log.ROTATION, 102400000, False ) cup.log.info('test info') cup.log.debug('test debug') cup.log.debug('中文') cup.log.reinit_comlog( 're-test', cup.log.DEBUG, './re.test.log', cup.log.ROTATION, 102400000, False ) cup.log.info('re:test info') cup.log.debug('re:test debug') cup.log.debug('re:中文') # vi:set tw=0 ts=4 sw=4 nowrap fdm=indent