Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/certbot/certbot.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'certbot/log.py')
-rw-r--r--certbot/log.py353
1 files changed, 0 insertions, 353 deletions
diff --git a/certbot/log.py b/certbot/log.py
deleted file mode 100644
index b883936f3..000000000
--- a/certbot/log.py
+++ /dev/null
@@ -1,353 +0,0 @@
-"""Logging utilities for Certbot.
-
-The best way to use this module is through `pre_arg_parse_setup` and
-`post_arg_parse_setup`. `pre_arg_parse_setup` configures a minimal
-terminal logger and ensures a detailed log is written to a secure
-temporary file if Certbot exits before `post_arg_parse_setup` is called.
-`post_arg_parse_setup` relies on the parsed command line arguments and
-does the full logging setup with terminal and rotating file handling as
-configured by the user. Any logged messages before
-`post_arg_parse_setup` is called are sent to the rotating file handler.
-Special care is taken by both methods to ensure all errors are logged
-and properly flushed before program exit.
-
-"""
-from __future__ import print_function
-import functools
-import logging
-import logging.handlers
-import os
-import sys
-import tempfile
-import traceback
-
-from acme import messages
-
-from certbot import compat
-from certbot import constants
-from certbot import errors
-from certbot import util
-
-# Logging format
-CLI_FMT = "%(message)s"
-FILE_FMT = "%(asctime)s:%(levelname)s:%(name)s:%(message)s"
-
-
-logger = logging.getLogger(__name__)
-
-
-def pre_arg_parse_setup():
- """Setup logging before command line arguments are parsed.
-
- Terminal logging is setup using
- `certbot.constants.QUIET_LOGGING_LEVEL` so Certbot is as quiet as
- possible. File logging is setup so that logging messages are
- buffered in memory. If Certbot exits before `post_arg_parse_setup`
- is called, these buffered messages are written to a temporary file.
- If Certbot doesn't exit, `post_arg_parse_setup` writes the messages
- to the normal log files.
-
- This function also sets `logging.shutdown` to be called on program
- exit which automatically flushes logging handlers and
- `sys.excepthook` to properly log/display fatal exceptions.
-
- """
- temp_handler = TempHandler()
- temp_handler.setFormatter(logging.Formatter(FILE_FMT))
- temp_handler.setLevel(logging.DEBUG)
- memory_handler = MemoryHandler(temp_handler)
-
- stream_handler = ColoredStreamHandler()
- stream_handler.setFormatter(logging.Formatter(CLI_FMT))
- stream_handler.setLevel(constants.QUIET_LOGGING_LEVEL)
-
- root_logger = logging.getLogger()
- root_logger.setLevel(logging.DEBUG) # send all records to handlers
- root_logger.addHandler(memory_handler)
- root_logger.addHandler(stream_handler)
-
- # logging.shutdown will flush the memory handler because flush() and
- # close() are explicitly called
- util.atexit_register(logging.shutdown)
- sys.excepthook = functools.partial(
- pre_arg_parse_except_hook, memory_handler,
- debug='--debug' in sys.argv, log_path=temp_handler.path)
-
-
-def post_arg_parse_setup(config):
- """Setup logging after command line arguments are parsed.
-
- This function assumes `pre_arg_parse_setup` was called earlier and
- the root logging configuration has not been modified. A rotating
- file logging handler is created and the buffered log messages are
- sent to that handler. Terminal logging output is set to the level
- requested by the user.
-
- :param certbot.interface.IConfig config: Configuration object
-
- """
- file_handler, file_path = setup_log_file_handler(
- config, 'letsencrypt.log', FILE_FMT)
- logs_dir = os.path.dirname(file_path)
-
- root_logger = logging.getLogger()
- memory_handler = stderr_handler = None
- for handler in root_logger.handlers:
- if isinstance(handler, ColoredStreamHandler):
- stderr_handler = handler
- elif isinstance(handler, MemoryHandler):
- memory_handler = handler
- msg = 'Previously configured logging handlers have been removed!'
- assert memory_handler is not None and stderr_handler is not None, msg
-
- root_logger.addHandler(file_handler)
- root_logger.removeHandler(memory_handler)
- temp_handler = memory_handler.target
- memory_handler.setTarget(file_handler)
- memory_handler.flush(force=True)
- memory_handler.close()
- temp_handler.close()
-
- if config.quiet:
- level = constants.QUIET_LOGGING_LEVEL
- else:
- level = -config.verbose_count * 10
- stderr_handler.setLevel(level)
- logger.debug('Root logging level set at %d', level)
- logger.info('Saving debug log to %s', file_path)
-
- sys.excepthook = functools.partial(
- post_arg_parse_except_hook, debug=config.debug, log_path=logs_dir)
-
-
-def setup_log_file_handler(config, logfile, fmt):
- """Setup file debug logging.
-
- :param certbot.interface.IConfig config: Configuration object
- :param str logfile: basename for the log file
- :param str fmt: logging format string
-
- :returns: file handler and absolute path to the log file
- :rtype: tuple
-
- """
- # TODO: logs might contain sensitive data such as contents of the
- # private key! #525
- util.set_up_core_dir(
- config.logs_dir, 0o700, compat.os_geteuid(), config.strict_permissions)
- log_file_path = os.path.join(config.logs_dir, logfile)
- try:
- handler = logging.handlers.RotatingFileHandler(
- log_file_path, maxBytes=2 ** 20,
- backupCount=config.max_log_backups)
- except IOError as error:
- raise errors.Error(util.PERM_ERR_FMT.format(error))
- # rotate on each invocation, rollover only possible when maxBytes
- # is nonzero and backupCount is nonzero, so we set maxBytes as big
- # as possible not to overrun in single CLI invocation (1MB).
- handler.doRollover() # TODO: creates empty letsencrypt.log.1 file
- handler.setLevel(logging.DEBUG)
- handler_formatter = logging.Formatter(fmt=fmt)
- handler.setFormatter(handler_formatter)
- return handler, log_file_path
-
-
-class ColoredStreamHandler(logging.StreamHandler):
- """Sends colored logging output to a stream.
-
- If the specified stream is not a tty, the class works like the
- standard `logging.StreamHandler`. Default red_level is
- `logging.WARNING`.
-
- :ivar bool colored: True if output should be colored
- :ivar bool red_level: The level at which to output
-
- """
- def __init__(self, stream=None):
- super(ColoredStreamHandler, self).__init__(stream)
- self.colored = (sys.stderr.isatty() if stream is None else
- stream.isatty())
- self.red_level = logging.WARNING
-
- def format(self, record):
- """Formats the string representation of record.
-
- :param logging.LogRecord record: Record to be formatted
-
- :returns: Formatted, string representation of record
- :rtype: str
-
- """
- out = super(ColoredStreamHandler, self).format(record)
- if self.colored and record.levelno >= self.red_level:
- return ''.join((util.ANSI_SGR_RED, out, util.ANSI_SGR_RESET))
- else:
- return out
-
-
-class MemoryHandler(logging.handlers.MemoryHandler):
- """Buffers logging messages in memory until the buffer is flushed.
-
- This differs from `logging.handlers.MemoryHandler` in that flushing
- only happens when flush(force=True) is called.
-
- """
- def __init__(self, target=None, capacity=10000):
- # capacity doesn't matter because should_flush() is overridden
- super(MemoryHandler, self).__init__(capacity, target=target)
-
- def close(self):
- """Close the memory handler, but don't set the target to None."""
- # This allows the logging module which may only have a weak
- # reference to the target handler to properly flush and close it.
- target = self.target
- super(MemoryHandler, self).close()
- self.target = target
-
- def flush(self, force=False): # pylint: disable=arguments-differ
- """Flush the buffer if force=True.
-
- If force=False, this call is a noop.
-
- :param bool force: True if the buffer should be flushed.
-
- """
- # This method allows flush() calls in logging.shutdown to be a
- # noop so we can control when this handler is flushed.
- if force:
- super(MemoryHandler, self).flush()
-
- def shouldFlush(self, record):
- """Should the buffer be automatically flushed?
-
- :param logging.LogRecord record: log record to be considered
-
- :returns: False because the buffer should never be auto-flushed
- :rtype: bool
-
- """
- return False
-
-
-class TempHandler(logging.StreamHandler):
- """Safely logs messages to a temporary file.
-
- The file is created with permissions 600. If no log records are sent
- to this handler, the temporary file is deleted when the handler is
- closed.
-
- :ivar str path: file system path to the temporary log file
-
- """
- def __init__(self):
- stream = tempfile.NamedTemporaryFile('w', delete=False)
- super(TempHandler, self).__init__(stream)
- self.path = stream.name
- self._delete = True
-
- def emit(self, record):
- """Log the specified logging record.
-
- :param logging.LogRecord record: Record to be formatted
-
- """
- self._delete = False
- super(TempHandler, self).emit(record)
-
- def close(self):
- """Close the handler and the temporary log file.
-
- The temporary log file is deleted if it wasn't used.
-
- """
- self.acquire()
- try:
- # StreamHandler.close() doesn't close the stream to allow a
- # stream like stderr to be used
- self.stream.close()
- if self._delete:
- os.remove(self.path)
- self._delete = False
- super(TempHandler, self).close()
- finally:
- self.release()
-
-
-def pre_arg_parse_except_hook(memory_handler, *args, **kwargs):
- """A simple wrapper around post_arg_parse_except_hook.
-
- The additional functionality provided by this wrapper is the memory
- handler will be flushed before Certbot exits. This allows us to
- write logging messages to a temporary file if we crashed before
- logging was fully configured.
-
- Since sys.excepthook isn't called on SystemExit exceptions, the
- memory handler will not be flushed in this case which prevents us
- from creating temporary log files when argparse exits because a
- command line argument was invalid or -h, --help, or --version was
- provided on the command line.
-
- :param MemoryHandler memory_handler: memory handler to flush
- :param tuple args: args for post_arg_parse_except_hook
- :param dict kwargs: kwargs for post_arg_parse_except_hook
-
- """
- try:
- post_arg_parse_except_hook(*args, **kwargs)
- finally:
- # flush() is called here so messages logged during
- # post_arg_parse_except_hook are also flushed.
- memory_handler.flush(force=True)
-
-
-def post_arg_parse_except_hook(exc_type, exc_value, trace, debug, log_path):
- """Logs fatal exceptions and reports them to the user.
-
- If debug is True, the full exception and traceback is shown to the
- user, otherwise, it is suppressed. sys.exit is always called with a
- nonzero status.
-
- :param type exc_type: type of the raised exception
- :param BaseException exc_value: raised exception
- :param traceback trace: traceback of where the exception was raised
- :param bool debug: True if the traceback should be shown to the user
- :param str log_path: path to file or directory containing the log
-
- """
- exc_info = (exc_type, exc_value, trace)
- # constants.QUIET_LOGGING_LEVEL or higher should be used to
- # display message the user, otherwise, a lower level like
- # logger.DEBUG should be used
- if debug or not issubclass(exc_type, Exception):
- assert constants.QUIET_LOGGING_LEVEL <= logging.ERROR
- logger.error('Exiting abnormally:', exc_info=exc_info)
- else:
- logger.debug('Exiting abnormally:', exc_info=exc_info)
- if issubclass(exc_type, errors.Error):
- sys.exit(exc_value)
- logger.error('An unexpected error occurred:')
- if messages.is_acme_error(exc_value):
- # Remove the ACME error prefix from the exception
- _, _, exc_str = str(exc_value).partition(':: ')
- logger.error(exc_str)
- else:
- traceback.print_exception(exc_type, exc_value, None)
- exit_with_log_path(log_path)
-
-
-def exit_with_log_path(log_path):
- """Print a message about the log location and exit.
-
- The message is printed to stderr and the program will exit with a
- nonzero status.
-
- :param str log_path: path to file or directory containing the log
-
- """
- msg = 'Please see the '
- if os.path.isdir(log_path):
- msg += 'logfiles in {0} '.format(log_path)
- else:
- msg += "logfile '{0}' ".format(log_path)
- msg += 'for more details.'
- sys.exit(msg)