Commits

Can Xue committed d3efa41

添加简化日志配置的工具包 logkit。

  • Participants
  • Parent commits cdbee1a

Comments (0)

Files changed (1)

kahgean/logkit.py

+# -*- coding: utf-8 -*-
+# Copyright (C) 2012 Xue Can <xuecan@gmail.com> and contributors.
+# Licensed under the MIT license: http://www.opensource.org/licenses/mit-license
+
+import os
+import sys
+import logging
+import logging.handlers
+
+if os.name=='nt':
+    # 对于 Windows,我们需要 colorama 支持在 stderr 以不同颜色显示日志
+    try:
+        from colorama import AnsiToWin32
+    except ImportError:
+        AnsiToWin32 = None
+
+# Ansi 颜色控制码
+CSI_PATTERN = '\033[%dm'
+(FORE_BLACK, FORE_RED, FORE_GREEN, FORE_YELLOW, FORE_BLUE, FORE_MAGENTA,
+ FORE_CYAN, FORE_WHITE, _, FORE_RESET) = range(30, 40)
+(BACK_BLACK, BACK_RED, BACK_GREEN, BACK_YELLOW, BACK_BLUE, BACK_MAGENTA,
+ BACK_CYAN, BACK_WHITE, _, BACK_RESET) = range(40, 50)
+(RESET_ALL, BRIGHT, DIM, NORMAL) = (0, 1, 2, 22)
+
+CSI = lambda code: CSI_PATTERN % code
+
+# 默认的日志格式和日志时间格式
+DEFAULT_FORMAT = '[%(asctime)s %(name)8.8s %(levelname)1.1s] %(message)s'
+DEFAULT_DATE_FORMAT = '%y%m%d %H:%M:%S'
+
+
+class ColoredFormatter(logging.Formatter):
+    
+    def __init__(self, colored, fmt=None, datefmt=None):
+        logging.Formatter.__init__(self, fmt, datefmt)
+        self.colored = bool(colored)
+                
+    def format(self, record):
+        # 正常的日志信息
+        record.message = record.getMessage()
+        if self.usesTime():
+            record.asctime = self.formatTime(record, self.datefmt)
+        s = self._fmt % record.__dict__
+        # 颜色控制
+        if self.colored:
+            if record.levelno == logging.DEBUG:
+                prefix = CSI(BRIGHT)+CSI(FORE_CYAN)
+            elif record.levelno == logging.INFO:
+                prefix = CSI(BRIGHT)+CSI(FORE_GREEN)
+            elif record.levelno == logging.WARN:
+                prefix = CSI(BRIGHT)+CSI(FORE_YELLOW)
+            elif record.levelno == logging.ERROR:
+                prefix = CSI(BRIGHT)+CSI(FORE_RED)
+            elif record.levelno == logging.CRITICAL:
+                prefix = CSI(BRIGHT)+CSI(BACK_RED)+CSI(FORE_YELLOW)
+            else:
+                prefix = CSI(RESET_ALL)
+            s = prefix + s + CSI(RESET_ALL)
+        # 如果需要,加上异常信息
+        if record.exc_info:
+            # Cache the traceback text to avoid converting it multiple times
+            # (it's constant anyway)
+            if not record.exc_text:
+                record.exc_text = self.formatException(record.exc_info)
+        if record.exc_text:
+            if result[-1:] != "\n":
+                s = s + "\n"
+            try:
+                s = s + record.exc_text
+            except UnicodeError:
+                # Sometimes filenames have non-ASCII chars, which can lead
+                # to errors when s is Unicode and record.exc_text is str
+                # See issue 8924
+                s = s + record.exc_text.decode(sys.getfilesystemencoding())
+        return s
+
+
+def add_options(options):
+    """配合 options 模块使用,方便添加和本模块有关的配置参数"""
+    options.add_option('--log-level', help='logging level (default: info)')
+    options.add_option('--log-format', help='')
+    options.add_option('--log-date-format', help='')
+    options.add_option('--log-to-stderr', help='')
+    options.add_option('--log-filename', help='')
+    options.add_option('--log-server', help='')
+
+
+def deal_with_options(options):
+    """配合 add_options() 使用,分析参数并进行相应操作"""
+    root_logger = logging.getLogger()
+    # 将日志等级设置在 root 日志器上
+    level_name = options.get('log_level') or 'info'
+    root_logger.setLevel(level_name.upper())
+
+    # 读取日志格式设置信息
+    format = options.get('log_format') or DEFAULT_FORMAT
+    date_format = options.get('date_format') or DEFAULT_DATE_FORMAT
+
+    # 如果配置了 log_filename,则启用基于文件的日志处理器
+    log_filename = options.get('log_filename')
+    if log_filename:
+        handler = logging.FileHandler(log_filename, encoding='utf8')
+        formatter = ColoredFormatter(False, format, date_format)
+        handler.setFormatter(formatter)
+        root_logger.addHandler(handler)
+    
+    # 如果配置了 log_server,则启用基于 socket 的日志处理器
+    log_server = options.get('log_server')
+    if log_server:
+        info = log_server.split(':', 1)
+        if len(info) == 2:
+            # 形如 address:port 的情况
+            host, port = info
+            port = int(info)
+        else:
+            host = info[0]
+            port = logging.handlers.DEFAULT_TCP_LOGGING_PORT
+        handler = SocketHandler(host, port)
+        root_logger.addHandler(handler)
+
+    # 如果允许在 stderr 写入日志(或者是设置了 log_to_stderr 为 True,或者
+    # log_to_stderr 为 None 但 root 日志器没有配置任何日志处理器),则尝试
+    # 使用颜色表示不同的等级。在 Windows 下,需要借助第三方模块 colorama
+    # 来转换 ANSI 颜色控制序列
+    log_to_stderr = options.get('log_to_stderr')
+    if log_to_stderr or (log_to_stderr is None and not root_logger.handlers):
+        stderr = sys.stderr
+        colored = True
+        if not stderr.isatty():
+            colored = False
+        else:
+            if os.name == 'nt':
+                if not AnsiToWin32:
+                    colored = False
+                else:
+                    stderr = AnsiToWin32(stderr).stream
+        handler = logging.StreamHandler(stderr)
+        formatter = ColoredFormatter(colored, format, date_format)
+        handler.setFormatter(formatter)
+        root_logger.addHandler(handler)