log.py
Go to the documentation of this file.
00001 #!/usr/bin/env python
00002 #
00003 # Copyright 2012 Facebook
00004 #
00005 # Licensed under the Apache License, Version 2.0 (the "License"); you may
00006 # not use this file except in compliance with the License. You may obtain
00007 # a copy of the License at
00008 #
00009 #     http://www.apache.org/licenses/LICENSE-2.0
00010 #
00011 # Unless required by applicable law or agreed to in writing, software
00012 # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
00013 # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
00014 # License for the specific language governing permissions and limitations
00015 # under the License.
00016 """Logging support for Tornado.
00017 
00018 Tornado uses three logger streams:
00019 
00020 * ``tornado.access``: Per-request logging for Tornado's HTTP servers (and
00021   potentially other servers in the future)
00022 * ``tornado.application``: Logging of errors from application code (i.e.
00023   uncaught exceptions from callbacks)
00024 * ``tornado.general``: General-purpose logging, including any errors
00025   or warnings from Tornado itself.
00026 
00027 These streams may be configured independently using the standard library's
00028 `logging` module.  For example, you may wish to send ``tornado.access`` logs
00029 to a separate file for analysis.
00030 """
00031 from __future__ import absolute_import, division, print_function, with_statement
00032 
00033 import logging
00034 import logging.handlers
00035 import sys
00036 
00037 from tornado.escape import _unicode
00038 from tornado.util import unicode_type, basestring_type
00039 
00040 try:
00041     import curses
00042 except ImportError:
00043     curses = None
00044 
00045 # Logger objects for internal tornado use
00046 access_log = logging.getLogger("tornado.access")
00047 app_log = logging.getLogger("tornado.application")
00048 gen_log = logging.getLogger("tornado.general")
00049 
00050 
00051 def _stderr_supports_color():
00052     color = False
00053     if curses and hasattr(sys.stderr, 'isatty') and sys.stderr.isatty():
00054         try:
00055             curses.setupterm()
00056             if curses.tigetnum("colors") > 0:
00057                 color = True
00058         except Exception:
00059             pass
00060     return color
00061 
00062 
00063 def _safe_unicode(s):
00064     try:
00065         return _unicode(s)
00066     except UnicodeDecodeError:
00067         return repr(s)
00068 
00069 
00070 class LogFormatter(logging.Formatter):
00071     """Log formatter used in Tornado.
00072 
00073     Key features of this formatter are:
00074 
00075     * Color support when logging to a terminal that supports it.
00076     * Timestamps on every log line.
00077     * Robust against str/bytes encoding problems.
00078 
00079     This formatter is enabled automatically by
00080     `tornado.options.parse_command_line` (unless ``--logging=none`` is
00081     used).
00082     """
00083     DEFAULT_FORMAT = '%(color)s[%(levelname)1.1s %(asctime)s %(module)s:%(lineno)d]%(end_color)s %(message)s'
00084     DEFAULT_DATE_FORMAT = '%y%m%d %H:%M:%S'
00085     DEFAULT_COLORS = {
00086         logging.DEBUG: 4,  # Blue
00087         logging.INFO: 2,  # Green
00088         logging.WARNING: 3,  # Yellow
00089         logging.ERROR: 1,  # Red
00090     }
00091 
00092     def __init__(self, color=True, fmt=DEFAULT_FORMAT,
00093                  datefmt=DEFAULT_DATE_FORMAT, colors=DEFAULT_COLORS):
00094         r"""
00095         :arg bool color: Enables color support.
00096         :arg string fmt: Log message format.
00097           It will be applied to the attributes dict of log records. The
00098           text between ``%(color)s`` and ``%(end_color)s`` will be colored
00099           depending on the level if color support is on.
00100         :arg dict colors: color mappings from logging level to terminal color
00101           code
00102         :arg string datefmt: Datetime format.
00103           Used for formatting ``(asctime)`` placeholder in ``prefix_fmt``.
00104 
00105         .. versionchanged:: 3.2
00106 
00107            Added ``fmt`` and ``datefmt`` arguments.
00108         """
00109         logging.Formatter.__init__(self, datefmt=datefmt)
00110         self._fmt = fmt
00111 
00112         self._colors = {}
00113         if color and _stderr_supports_color():
00114             # The curses module has some str/bytes confusion in
00115             # python3.  Until version 3.2.3, most methods return
00116             # bytes, but only accept strings.  In addition, we want to
00117             # output these strings with the logging module, which
00118             # works with unicode strings.  The explicit calls to
00119             # unicode() below are harmless in python2 but will do the
00120             # right conversion in python 3.
00121             fg_color = (curses.tigetstr("setaf") or
00122                         curses.tigetstr("setf") or "")
00123             if (3, 0) < sys.version_info < (3, 2, 3):
00124                 fg_color = unicode_type(fg_color, "ascii")
00125 
00126             for levelno, code in colors.items():
00127                 self._colors[levelno] = unicode_type(curses.tparm(fg_color, code), "ascii")
00128             self._normal = unicode_type(curses.tigetstr("sgr0"), "ascii")
00129         else:
00130             self._normal = ''
00131 
00132     def format(self, record):
00133         try:
00134             message = record.getMessage()
00135             assert isinstance(message, basestring_type)  # guaranteed by logging
00136             # Encoding notes:  The logging module prefers to work with character
00137             # strings, but only enforces that log messages are instances of
00138             # basestring.  In python 2, non-ascii bytestrings will make
00139             # their way through the logging framework until they blow up with
00140             # an unhelpful decoding error (with this formatter it happens
00141             # when we attach the prefix, but there are other opportunities for
00142             # exceptions further along in the framework).
00143             #
00144             # If a byte string makes it this far, convert it to unicode to
00145             # ensure it will make it out to the logs.  Use repr() as a fallback
00146             # to ensure that all byte strings can be converted successfully,
00147             # but don't do it by default so we don't add extra quotes to ascii
00148             # bytestrings.  This is a bit of a hacky place to do this, but
00149             # it's worth it since the encoding errors that would otherwise
00150             # result are so useless (and tornado is fond of using utf8-encoded
00151             # byte strings whereever possible).
00152             record.message = _safe_unicode(message)
00153         except Exception as e:
00154             record.message = "Bad message (%r): %r" % (e, record.__dict__)
00155 
00156         record.asctime = self.formatTime(record, self.datefmt)
00157 
00158         if record.levelno in self._colors:
00159             record.color = self._colors[record.levelno]
00160             record.end_color = self._normal
00161         else:
00162             record.color = record.end_color = ''
00163 
00164         formatted = self._fmt % record.__dict__
00165 
00166         if record.exc_info:
00167             if not record.exc_text:
00168                 record.exc_text = self.formatException(record.exc_info)
00169         if record.exc_text:
00170             # exc_text contains multiple lines.  We need to _safe_unicode
00171             # each line separately so that non-utf8 bytes don't cause
00172             # all the newlines to turn into '\n'.
00173             lines = [formatted.rstrip()]
00174             lines.extend(_safe_unicode(ln) for ln in record.exc_text.split('\n'))
00175             formatted = '\n'.join(lines)
00176         return formatted.replace("\n", "\n    ")
00177 
00178 
00179 def enable_pretty_logging(options=None, logger=None):
00180     """Turns on formatted logging output as configured.
00181 
00182     This is called automatically by `tornado.options.parse_command_line`
00183     and `tornado.options.parse_config_file`.
00184     """
00185     if options is None:
00186         from tornado.options import options
00187     if options.logging is None or options.logging.lower() == 'none':
00188         return
00189     if logger is None:
00190         logger = logging.getLogger()
00191     logger.setLevel(getattr(logging, options.logging.upper()))
00192     if options.log_file_prefix:
00193         channel = logging.handlers.RotatingFileHandler(
00194             filename=options.log_file_prefix,
00195             maxBytes=options.log_file_max_size,
00196             backupCount=options.log_file_num_backups)
00197         channel.setFormatter(LogFormatter(color=False))
00198         logger.addHandler(channel)
00199 
00200     if (options.log_to_stderr or
00201             (options.log_to_stderr is None and not logger.handlers)):
00202         # Set up color if we are in a tty and curses is installed
00203         channel = logging.StreamHandler()
00204         channel.setFormatter(LogFormatter())
00205         logger.addHandler(channel)
00206 
00207 
00208 def define_logging_options(options=None):
00209     if options is None:
00210         # late import to prevent cycle
00211         from tornado.options import options
00212     options.define("logging", default="info",
00213                    help=("Set the Python log level. If 'none', tornado won't touch the "
00214                          "logging configuration."),
00215                    metavar="debug|info|warning|error|none")
00216     options.define("log_to_stderr", type=bool, default=None,
00217                    help=("Send log output to stderr (colorized if possible). "
00218                          "By default use stderr if --log_file_prefix is not set and "
00219                          "no other logging is configured."))
00220     options.define("log_file_prefix", type=str, default=None, metavar="PATH",
00221                    help=("Path prefix for log files. "
00222                          "Note that if you are running multiple tornado processes, "
00223                          "log_file_prefix must be different for each of them (e.g. "
00224                          "include the port number)"))
00225     options.define("log_file_max_size", type=int, default=100 * 1000 * 1000,
00226                    help="max size of log files before rollover")
00227     options.define("log_file_num_backups", type=int, default=10,
00228                    help="number of log files to keep")
00229 
00230     options.add_parse_callback(enable_pretty_logging)


rosbridge_tools
Author(s): Jonathan Mace
autogenerated on Sat Dec 27 2014 11:25:59