tornado中log使用与logging模块的关联影响详解

目录

【问题总结及代码】

参考一:参考点:logging基础内容了解

参考二:参考点:tornado.log 的 配置设置、日志自动分割、自定义格式化输出日志

参考三:参考点:自定义日志输出、日志分割

参考四:参考点:修改tornado.log 的 日志等级。 tornado.options

注意tornado中的日志级别, 一直改不了,才发现是 tornado 把 logging 设置的日志级别 就行了2次更改;


【问题总结及代码】

tornado原来是facebook开源的?

【问题】注意tornado中的日志级别, 一直改不了,才发现是 tornado 把 logging 设置的日志级别 就行了2次更改; 详见: 参考三

【问题】注意tornado中的日志, 一直改不了,才发现是 tornado 把 logging 设置的日志级别 就行了2次更改; 详见: 参考三

【采纳后,更新代码】:

import os
import tornado
import tornado.ioloop
import tornado.httpserver
import tornado.web
import tornado.log
import logging
from tornado.options import options, define

 
class Application(tornado.web.Application):
    def __init__(self):
        super(Application, self).__init__(handlers, **self.settings)
 
 
# 格式化日志输出格式
# 默认是这种的:[I 160807 09:27:17 web:1971] 200 GET / (::1) 7.00ms
# 格式化成这种的:[2016-08-07 09:38:01 执行文件名:执行函数名:执行行数 日志等级] 内容消息
class LogFormatter(tornado.log.LogFormatter):
    def __init__(self):
        super(LogFormatter, self).__init__(
            fmt='%(color)s[%(asctime)s %(filename)s:%(funcName)s:%(lineno)d %(levelname)s]%(end_color)s %(message)s',
            datefmt='%Y-%m-%d %H:%M:%S'
        )
 

def main():
    tornado.options.define("port", default="8888", help="run port", type=int)  # 设置默认端口port
    tornado.options.define("table", default=True, help="create table", type=bool)  # 设置默认创建数据库表

    print(tornado.options.options.logging, '打印出为 info ------------------')
    
    tornado.options.options.logging = "error" # 设置日志登记 
    tornado.options.define('log_file_prefix', default='./logs/DMS.log')  # 存储路径
    tornado.options.define('log_rotate_mode', default='time')  # 切割方式:按时间
    tornado.options.define('log_rotate_when', default='D')     # 切割单位:天
    tornado.options.define('log_rotate_interval', default=1)   # 间隔值:1天
    
    print(tornado.options.options.logging, '打印出为 error -------------------')

    
    tornado.options.parse_command_line()  # 解析配置,应用前面的配置项

    [i.setFormatter(LogFormatter()) for i in logging.getLogger().handlers] # 自定义格式化日志, 必须放在 配置解析 后
    
    if options.table:
        create_table.run()  # 引入自定义函数,省略
    
    http_server = tornado.httpserver.HTTPServer(Application())
    http_server.listen(tornado.options.options.port)  # 在这里应用之前的全局变量port
    tornado.ioloop.IOLoop.current().start()  # 启动监听
 
 
if __name__ == "__main__":
    main()

日志输出样式:

 

 

 


参考一:参考点:logging基础内容了解


这是一个提供日志功能的模块,它可以让你更敏捷的为你程序提供日志功能

 

一、常用日志记录场景及最佳解决方案:

日志记录方式 最佳记录日志方案

普通情况下,在控制台显示输出 print()
报告正常程序操作过程中发生的事件 logging.info()(或者更详细的logging.debug())
发出有关特定事件的警告 warnings.warn()或者logging.warning()
报告错误 弹出异常
在不引发异常的情况下报告错误 logging.error(), logging.exception()或者logging.critical()

 


二、日志等级:

logging模块定义了下表所示的日志级别,按事件严重程度由低到高排列(注意是全部大写!因为它们是常量。):

级别 级别数值 使用时机
DEBUG 10 详细信息,常用于调试。
INFO 20 程序正常运行过程中产生的一些信息。
WARNING 30 警告用户,虽然程序还在正常工作,但有可能发生错误。
ERROR 40 由于更严重的问题,程序已不能执行一些功能了。
CRITICAL 50 严重错误,程序已不能继续运行。

默认级别是WARNING,表示只有WARING和比WARNING更严重的事件才会被记录到日志内,低级别的信息会被忽略。因此,默认情况下,DEBUG和INFO会被忽略,WARING、ERROR和CRITICAL会被记录。

 


三、logging基本使用方法:

产生五种日志级别(WARING、ERROR和CRITICAL会直接输出()内的提示语至屏幕):
logging.info('info')
logging.debug('debug')
logging.warning('warning')
logging.error('error')
logging.critical('critical')


日志系统配置文件(定义日志一些规则):

logging.basicConfig(filename='test.log', level=logging.INFO, filemode='a', format='%(levelname)s:%(asctime)s:%(message)s')

filename:日志要保存至哪个文件中(定义了这个后日志将不再在屏幕上打印)
level:什么级别以上的日志需要保存
filemode:有"w"、"a"两种模式,同open一样,"a"追加,"w"覆盖
format:定义日志格式(后面提供日志元素表,建议通过":"将各种日志元素连接成合理的日志格式)


format定义格式时用的日志元素表:

日志元素 描述

%(asctime)s 日志产生的时间,默认格式为2003-07-08 16:49:45,896
%(created)f time.time()生成的日志创建时间戳
%(filename)s 生成日志的程序名
%(funcName)s 调用日志的函数名
%(levelname)s 日志级别 ('DEBUG', 'INFO', 'WARNING', 'ERROR', 'CRITICAL')
%(levelno)s 日志级别对应的数值
%(lineno)d 日志所针对的代码行号(如果可用的话)
%(module)s 生成日志的模块名
%(msecs)d 日志生成时间的毫秒部分
%(message)s 具体的日志信息
%(name)s 日志调用者
%(pathname)s 生成日志的文件的完整路径
%(process)d 生成日志的进程ID(如果可用)
%(processName)s 进程名(如果可用)
%(thread)d 生成日志的线程ID(如果可用)
%(threadName)s 线程名(如果可用)

 

四、logging高级用法(让日志即能写入文件又能在屏幕打印):

包含关系(左边包含右边):
记录器<——处理器<——格式化器

import logging
"""
logging模块采用了模块化设计,主要包含四种组件:

Loggers:记录器,提供应用程序代码能直接使用的接口;
Handlers:处理器,将记录器产生的日志发送至目的地;
Filters:过滤器,提供更好的粒度控制,决定哪些日志会被输出;
Formatters:格式化器,设置日志内容的组成结构和消息字段。

"""
# 创建一个记录器loggers,并设置默认等级
logger = logging.getLogger('jack') # ‘jack’位置定义了日志调用者的名字
logger.setLevel(logging.INFO)

# 创建两个处理器handlers(一个发往日志文件、一个发往屏幕),并分别设置他们的日志等级
stream = logging.StreamHandler() # 发往屏幕
stream.setLevel(logging.DEBUG) # 定义什么样级别以上的日志发往屏幕

file = logging.FileHandler('test.log') # 发往日志文件(需指定文件名称)
file.setLevel(logging.ERROR) # 定义什么样级别以上的日志发往日志文件

# 分别为两个处理器handlers创建格式化器formatters(可以让其在屏幕和日志文件中以不同的格式输出)
formatter_stream = logging.Formatter('%(levelname)s:%(asctime)s:%(message)s')
formatter_file = logging.Formatter('%(name)s:%(levelname)s:%(asctime)s:%(message)s')

# 为各个处理器handlers设置相应的格式化器
stream.setFormatter(formatter_stream)
file.setFormatter(formatter_file)

# 将所有的处理器handler加入自定义的记录器logger内
logger.addHandler(stream)
logger.addHandler(file)

# 测试日志功能
logger.debug('debug')
logger.info('info')
logger.warning('warn')
logger.error('error')
logger.critical('critical')

 


参考二:参考点:tornado.log 的 配置设置、日志自动分割、自定义格式化输出日志

 

logging的简单介绍和使用

python内置模块logging,用于记录日志,格式化输出。通过getLogger()获得的是单例且线程安全的(进程不安全),下文会简单介绍logging的常用方法和简单logging源码分析,以及tornado中的日志模块。

使用basicConfig()(给root handler添加基本配置),除了levelfilename参数外还有filemode(默认a),format(输出格式),handlers(给root logger添加handlers)

import logging

logging.basicConfig(level=logging.DEBUG, filename='main.log')
logging.debug('bug log')

logging的日志分为这几种级别:

CRITICAL = 50
FATAL = CRITICAL
ERROR = 40
WARNING = 30
WARN = WARNING
INFO = 20
DEBUG = 10
NOTSET = 0

logging.info()等实际上是根looger(root logger)的info()方法。

利用logger的相对应的infodebugwaring等可以输出日志,只有当方法级别>日志级别时才能够输出(如level=logging.INFO时,用logging.error('error')能够输出,而logging.debug('debug')无输出)。

值得一提的是还有一个logging.exception()方法,能够简单的记录Traceback信息(实际上是相当于logging.error(e, exc_info=True)),所有的输出方法最终都是调用了logger的_log()方法。

getLogger()

import logging

root_logger = logging.getLogger()
mine_logger = logging.getLogger('mine')

参数缺省时默认获取的是root_logger,是所有logger的最终父logger。
在未作任何配置时,root_logger的日志级别是warning

阅读getLogger()源码可以发现

root = RootLogger(WARNING)
Logger.root = root
Logger.manager = Manager(Logger.root)

def getLogger(name=None):
    """
    Return a logger with the specified name, creating it if necessary.

    If no name is specified, return the root logger.
    """
    if name:
        return Logger.manager.getLogger(name)
    else:
        return root

执行logging.getLogger('mine')方法,即Manager(Logger.root).getLogger('mine'),再来看一下ManagergetLogger()

    def getLogger(self, name):
        """
        Get a logger with the specified name (channel name), creating it
        if it doesn't yet exist. This name is a dot-separated hierarchical
        name, such as "a", "a.b", "a.b.c" or similar.

        If a PlaceHolder existed for the specified name [i.e. the logger
        didn't exist but a child of it did], replace it with the created
        logger and fix up the parent/child references which pointed to the
        placeholder to now point to the logger.
        """
        rv = None
        if not isinstance(name, str):
            raise TypeError('A logger name must be a string')
        _acquireLock()
        try:
            if name in self.loggerDict:
                rv = self.loggerDict[name]
                if isinstance(rv, PlaceHolder):
                    ph = rv
                    rv = (self.loggerClass or _loggerClass)(name)
                    rv.manager = self
                    self.loggerDict[name] = rv
                    self._fixupChildren(ph, rv)
                    self._fixupParents(rv)
            else:
                rv = (self.loggerClass or _loggerClass)(name)
                rv.manager = self
                self.loggerDict[name] = rv
                self._fixupParents(rv)
        finally:
            _releaseLock()
        return rv

getLogger的时候会先从Manager的loggerDict中查找,找不到时才生成新logger ,通过loggerDictPlaceHodler,将a,a.b,a.b.c类似的,子logger继承父logger的levelhandler等,最终继承root_handler。

Handlers, Formatter

每个logger都能有多个handler,默认的是StreamHandler,能够通过addHandler()removeHandler()来添加删除handler。

import logging

mine_logger = logging.getLogger('mine')
mine_logger.setLevel(logging.INFO)

console_handler = logging.StreamHandler()
file_handler = logging.FileHandler('./logs/test.log')

formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
console_handler.setFormatter(formatter)
file_handler.setFormatter(formatter)

mine_logger.addHandler(console_handler)
mine_logger.addHandler(file_handler)

mine_logger.info('info message')

新建了console_handlerfile_handler,通过addHandler()添加到了mine_logger上,这样mine_logger.info('info message')既能输出到文件又能打印在控制台。

ps:可以利用logging.Formatter来格式化输出,但每个handler只能有一种类formatter。

高级handlers

logging.handers下面有很多封装好的功能强大的handler。
RotatingFileHandler :根据大小切割日志。
TimedRotatingFileHandler :根据时间切割日志。
还有MemoryHandlerSocketHandler等等。

config

利用配置文件能够快速的对logging进行配置。

[loggers]
keys=root, common

[logger_root]
level=INFO
handlers=root_handler

[logger_common]
level=DEBUG
handlers=common_handler
qualname=common

[handlers]
keys=root_handler,common_handler

[handler_root_handler]
class=StreamHandler
level=INFO
formatter=form01
args=(sys.stdout,)

[handler_common_handler]
class=FileHandler
level=DEBUG
formatter=form01
args=('main.log',"a")

[formatters]
keys=form01

[formatter_form01]
format=[%(asctime)s %(levelname)s] file: %(filename)s, line:%(lineno)d %(process)d %(message)s 
datefmt=%Y-%m-%d %H:%M:%S
  •  
  •  

通过配置文件来定义logger,handler,formatter等。上述配置中handler都是指向logging模块自带的handler,也可以指向自定义的handler。

利用logging.config加载配置文件。

import logging
import logging.config

logging_config_path = 'logging.conf'
logging.config.fileConfig(logging_config_path)

root_logger = logging.getLogger('root')
root_logger.info('test message')


common_logger = logging.getLogger('common')
common_logger.info('test common message')

tornado中的log简单介绍

在tornado的log.py模块中,初始化了三种logger,都继承于logging模块的root logger。

'''Tornado uses three logger streams:

* ``tornado.access``: Per-request logging for Tornado's HTTP servers (and
  potentially other servers in the future)
* ``tornado.application``: Logging of errors from application code (i.e.
  uncaught exceptions from callbacks)
* ``tornado.general``: General-purpose logging, including any errors
  or warnings from Tornado itself.
'''

# Logger objects for internal tornado use
access_log = logging.getLogger("tornado.access")
app_log = logging.getLogger("tornado.application")
gen_log = logging.getLogger("tornado.general")

在启动tornado前利用tornado.options.parse_command_line()来初始化一些配置。
tornado.options.parse_command_line() 会默认调用enable_pretty_logging()方法,对日志输出做一些格式化和rotate并输出到日志文件,可以通过tornado.options.define()来传递这些参数。

def enable_pretty_logging(options=None, logger=None):
    """Turns on formatted logging output as configured.

    This is called automatically by `tornado.options.parse_command_line`
    and `tornado.options.parse_config_file`.
    """
    if options is None:
        import tornado.options
        options = tornado.options.options
    if options.logging is None or options.logging.lower() == 'none':
        return
    if logger is None:
        logger = logging.getLogger()
    logger.setLevel(getattr(logging, options.logging.upper()))
    if options.log_file_prefix:
        rotate_mode = options.log_rotate_mode
        if rotate_mode == 'size':
            channel = logging.handlers.RotatingFileHandler(
                filename=options.log_file_prefix,
                maxBytes=options.log_file_max_size,
                backupCount=options.log_file_num_backups)
        elif rotate_mode == 'time':
            channel = logging.handlers.TimedRotatingFileHandler(
                filename=options.log_file_prefix,
                when=options.log_rotate_when,
                interval=options.log_rotate_interval,
                backupCount=options.log_file_num_backups)
        else:
            error_message = 'The value of log_rotate_mode option should be ' +\
                            '"size" or "time", not "%s".' % rotate_mode
            raise ValueError(error_message)
        channel.setFormatter(LogFormatter(color=False))
        logger.addHandler(channel)

    if (options.log_to_stderr or
            (options.log_to_stderr is None and not logger.handlers)):
        # Set up color if we are in a tty and curses is installed
        channel = logging.StreamHandler()
        channel.setFormatter(LogFormatter())
        logger.addHandler(channel)
  •  
  •  

如现在我们对tornado的日志进行每间隔一天进行分割,并输出到文件中。

    tornado.options.define('log_file_prefix', default='./logs/test.log')
    tornado.options.define('log_rotate_mode', default='time')
    tornado.options.define('log_rotate_when', default='M')
    tornado.options.define('log_rotate_interval', default=1)
    tornado.options.parse_command_line() 

更多参数可以参考源码:

options.define("logging", default="info",
                   help=("Set the Python log level. If 'none', tornado won't touch the "
                         "logging configuration."),
                   metavar="debug|info|warning|error|none")
    options.define("log_to_stderr", type=bool, default=None,
                   help=("Send log output to stderr (colorized if possible). "
                         "By default use stderr if --log_file_prefix is not set and "
                         "no other logging is configured."))
    options.define("log_file_prefix", type=str, default=None, metavar="PATH",
                   help=("Path prefix for log files. "
                         "Note that if you are running multiple tornado processes, "
                         "log_file_prefix must be different for each of them (e.g. "
                         "include the port number)"))
    options.define("log_file_max_size", type=int, default=100 * 1000 * 1000,
                   help="max size of log files before rollover")
    options.define("log_file_num_backups", type=int, default=10,
                   help="number of log files to keep")

    options.define("log_rotate_when", type=str, default='midnight',
                   help=("specify the type of TimedRotatingFileHandler interval "
                         "other options:('S', 'M', 'H', 'D', 'W0'-'W6')"))
    options.define("log_rotate_interval", type=int, default=1,
                   help="The interval value of timed rotating")

    options.define("log_rotate_mode", type=str, default='size',
                   help="The mode of rotating files(time or size)")

对于access_logger,初始化后tornado会记录每次请求的信息,该记录log的操作是在handler中完成的。

在RequestHandler的finish()中会执行_log()方法,该方法会找到该handler所属Applicationlog_request()方法:

    def log_request(self, handler):
        """Writes a completed HTTP request to the logs.

        By default writes to the python root logger.  To change
        this behavior either subclass Application and override this method,
        or pass a function in the application settings dictionary as
        ``log_function``.
        """
        if "log_function" in self.settings:
            self.settings["log_function"](handler)
            return
        if handler.get_status() < 400:
            log_method = access_log.info
        elif handler.get_status() < 500:
            log_method = access_log.warning
        else:
            log_method = access_log.error
        request_time = 1000.0 * handler.request.request_time()
        log_method("%d %s %.2fms", handler.get_status(),
                   handler._request_summary(), request_time)
  •  

很直观的看出来在请求状态码不同时,输出的日志级别也不同,除了状态码,还记录了请求时间,以及ip,uri和方法,若要简单更改输出的日志格式,可以继承RequestHandler并重写_request_summary()方法。

    def _request_summary(self):
        return "%s %s (%s)" % (self.request.method, self.request.uri,
                               self.request.remote_ip)

ps: 也能够通过配置文件对tornado日志进行配置。


logging模块是线程安全的,但多进程时会出现问题,多进程解决方法参考:
- Python多进程log日志切分错误的解决方案
- python logging日志模块以及多进程日志

 

 

 


参考三:参考点:自定义日志输出、日志分割

1.需求
将http访问记录,程序自定义日志输出到文件,按天分割,保留最近30天的日志。

2.使用示例

init_logging("%s/QYK.%s.%s.log" % (log_path, tornado.options.options.mode, tornado.options.options.port))
def init_logging(log_file):
	# 使用TimedRotatingFileHandler处理器
    file_handler = TimedRotatingFileHandler(log_file, when="d", interval=1, backupCount=30)
	# 输出格式
    log_formatter = logging.Formatter(
        "%(asctime)s [%(threadName)-12.12s] [%(levelname)-5.5s] [%(lineno)d]  %(message)s"
    )
    file_handler.setFormatter(log_formatter)
	# 将处理器附加到根logger
    root_logger = logging.getLogger()
    root_logger.addHandler(file_handler)
logging.info('测试日志输出')

运行后日志文件内容:
tornado中log使用与logging模块的关联影响详解_第1张图片
tornado中会将logging的输出级别设置为info

3.http访问日志
tornado中http访问的日志是由access_log处理器完成的,access_log继承了根logger,因此,
access_log处理器也会将日志输出到示例中的日志文件中,如下所示:

import logging
import datetime

class Test1Handler(ApiHandler):
    async def get(self, *args, **kwargs):
        self.return_success_response()
        return

运行后日志文件内容:
tornado中log使用与logging模块的关联影响详解_第2张图片
查看tornado源码tornado\web.py,可以看到access_log的输出格式:

def log_request(self, handler):
    """Writes a completed HTTP request to the logs.

    By default writes to the python root logger.  To change
    this behavior either subclass Application and override this method,
    or pass a function in the application settings dictionary as
    ``log_function``.
    """
    if "log_function" in self.settings:
        self.settings["log_function"](handler)
        return
    if handler.get_status() < 400:
        log_method = access_log.info
    elif handler.get_status() < 500:
        log_method = access_log.warning
    else:
        log_method = access_log.error
    request_time = 1000.0 * handler.request.request_time()
    log_method("%d %s %.2fms", handler.get_status(),
               handler._request_summary(), request_time)

如何自定义http访问日志的输出格式呢?通过源码,可知有两种方法可以解决这个问题
1)我们可以在Application中自定义一个log_function即可

def log_func(handler):
    if handler.get_status() < 400:
        log_method = access_log.info
    elif handler.get_status() < 500:
        log_method = access_log.warning
    else:
        log_method = access_log.error
    request_time = 1000.0 * handler.request.request_time()
    log_method("%d %s %s (%s) %s %s %.2fms",
               handler.get_status(), handler.request.method,
               handler.request.uri, handler.request.remote_ip,
               handler.request.headers["User-Agent"],
               handler.request.arguments,
               request_time)


class QYKApplication(tornado.web.Application):
    def __init__(self, config, webpack_setting, _redis, session_class):
        settings = dict()
        settings["log_function"] = log_func

运行后日志文件内容:
tornado中log使用与logging模块的关联影响详解_第3张图片
在输出格式中,增加了访问url,ip,代理服务器,参数等信息
2)重写RequestHandler的_request_summary方法

def _request_summary(self):
    return "%s %s (%s) %s\n%s" % (self.request.method, self.request.uri,
    	self.request.remote_ip, self.request.headers["User-Agent"],
		self.request.arguments)

 


参考四:参考点:修改tornado.log 的 日志等级。 tornado.options

注意tornado中的日志级别, 一直改不了,才发现是 tornado 把 logging 设置的日志级别 就行了2次更改;

 

 

通过源码可以发现,tornado中也是大量使用了python中的logging模块来处理日志操作。

但tornado在处理日志的时候,特别是tornado.options.parse_command_line()时将根日志的级别设置为info,这点需要特别注意!

如果在tornado.options.parse_command_line()之前程序对logging的日志级别进行了设置,则很有可能会被tornado进行改写,这点需要特别注意。
通过下面的代码可以很容易就能看出tornado对跟logger的级别进行了调整:

 
  1. import os
    
    import ssl
    
    import logging
    
    from tornado.httpserver import HTTPServer
    
    from tornado.web import Application, RequestHandler
    
    from tornado.ioloop import IOLoop
    
    import tornado.options
    
    from tornado.options import define, options
    
    
    
    LOG = logging.getLogger(__name__)
    
    
    define("port", default=8000, help="run on the given port", type=int)
    
    class TestHandler(RequestHandler):
    
    def get(self):
    
    self.write("Hello, World!\n")
    
    
    application = Application([
    
    (r"/", TestHandler),],
    
    debug = True)
    
    
    if __name__ == "__main__":
    
        print "main 1 getEffectiveLevel = ", LOG.getEffectiveLevel()
    
        print "tornado default log level = "tornado.options.options.logging
    
        tornado.options.options.logging = "debug"
    
        tornado.options.parse_command_line()
    
    
        print "main getEffectiveLevel = ", LOG.getEffectiveLevel()
    
        # server = HTTPServer(application,ssl_options={
    
        # "certfile": os.path.join(os.path.abspath("."), "cert.pem"),
    
        # "keyfile": os.path.join(os.path.abspath("."), "privatekey.pem"),
        
        # })
    
        server = HTTPServer(applicatio)
    
        server.listen(options.port)
    
        IOLoop.instance().start()

     

 对根logger的日志级别进行更改主要是tornado的tornado.options所为,源码如下:

def parse_command_line(self, args=None):

if args is None:

args = sys.argv

remaining = []

for i in xrange(1, len(args)):

# All things after the last option are command line arguments

if not args[i].startswith("-"):

remaining = args[i:]

break

if args[i] == "--":

remaining = args[i + 1:]

break

arg = args[i].lstrip("-")

name, equals, value = arg.partition("=")

name = name.replace('-', '_')

if not name in self:

print_help()

raise Error('Unrecognized command line option: %r' % name)

option = self[name]

if not equals:

if option.type == bool:

value = "true"

else:

raise Error('Option %r requires a value' % name)

option.parse(value)

if self.help:

print_help()

sys.exit(0)


# Set up log level and pretty console logging by default

if self.logging != 'none':

logging.getLogger().setLevel(getattr(logging, self.logging.upper()))

enable_pretty_logging()


return remaining




# Default options

define("help", type=bool, help="show this help information")

define("logging", default="info",

help=("Set the Python log level. If 'none', tornado won't touch the "

"logging configuration."),

metavar="debug|info|warning|error|none")

   

可以看到,tornado.options在parse_command_line的时候,会判断self.logging 是否为none,非none时执行logging.getLogger().setLevel(getattr(logging, self.logging.upper())),也就是对根logger的level进行设置,在tornado.options被import的时候定义了一个logging,parse_command_line的时候将logging的根级别设置为info。
总之,在tornado应用中,应该特别注意logging级别设置同tornado.options.parse_command_line的先后顺序。
 

 

你可能感兴趣的:(Tornado,logging)