Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(515)

Unified Diff: infra_libs/logs/logs.py

Issue 2213143002: Add infra_libs as a bootstrap dependency. (Closed) Base URL: https://chromium.googlesource.com/infra/infra.git@master
Patch Set: Removed the ugly import hack Created 4 years, 4 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: infra_libs/logs/logs.py
diff --git a/infra_libs/logs/logs.py b/infra_libs/logs/logs.py
deleted file mode 100644
index 2a32786d825f49164ea6ca0c515281d650b3f678..0000000000000000000000000000000000000000
--- a/infra_libs/logs/logs.py
+++ /dev/null
@@ -1,295 +0,0 @@
-# Copyright 2014 The Chromium Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style license that can be
-# found in the LICENSE file.
-
-"""Utilities for logging.
-
-Example usage:
-
-.. code-block:: python
-
- import argparse
- import logging
- import infra_libs.logs
-
- parser = argparse.ArgumentParser()
- infra_libs.logs.add_argparse_options(parser)
-
- options = parser.parse_args()
- infra_libs.logs.process_argparse_options(options)
-
- LOGGER = logging.getLogger(__name__)
- LOGGER.info('test message')
-
-The last line should print something like::
-
- [I2014-06-27T11:42:32.418716-07:00 7082 logs:71] test message
-
-"""
-
-import datetime
-import getpass
-import inspect
-import logging
-import logging.handlers
-import os
-import re
-import socket
-import sys
-import tempfile
-import textwrap
-
-import pytz
-
-from infra_libs.ts_mon.common.metrics import CumulativeMetric
-
-log_metric = CumulativeMetric(
- 'proc/log_lines', description="Number of log lines, per severity level.")
-
-if sys.platform.startswith('win'): # pragma: no cover
- DEFAULT_LOG_DIRECTORIES = os.pathsep.join([
- 'E:\\chrome-infra-logs',
- 'C:\\chrome-infra-logs',
- ])
-else:
- DEFAULT_LOG_DIRECTORIES = '/var/log/chrome-infra'
-
-
-class InfraFilter(logging.Filter): # pragma: no cover
- """Adds fields used by the infra-specific formatter.
-
- Fields added:
-
- - 'iso8601': timestamp
- - 'severity': one-letter indicator of log level (first letter of levelname).
- - 'fullModuleName': name of the module including the package name.
-
- Args:
- timezone (str): timezone in which timestamps should be printed.
- module_name_blacklist (str): do not print log lines from modules whose name
- matches this regular expression.
- """
- def __init__(self, timezone, module_name_blacklist=None):
- super(InfraFilter, self).__init__()
- self.module_name_blacklist = None
-
- if module_name_blacklist:
- self.module_name_blacklist = re.compile(module_name_blacklist)
-
- self.tz = pytz.timezone(timezone)
-
- def filter(self, record):
- dt = datetime.datetime.fromtimestamp(record.created, tz=pytz.utc)
- record.iso8601 = self.tz.normalize(dt).isoformat()
- record.severity = record.levelname[0]
- log_metric.increment(fields={'level': record.severity})
- record.fullModuleName = self._full_module_name() or record.module
- if self.module_name_blacklist:
- if self.module_name_blacklist.search(record.fullModuleName):
- return False
- return True
-
- def _full_module_name(self):
- frame = inspect.currentframe()
- try:
- while frame is not None:
- try:
- name = frame.f_globals['__name__']
- except KeyError:
- continue
-
- if name not in (__name__, 'logging'):
- return name
- frame = frame.f_back
- finally:
- del frame
-
- return None
-
-
-class InfraFormatter(logging.Formatter): # pragma: no cover
- """Formats log messages in a standard way.
-
- This object processes fields added by :class:`InfraFilter`.
- """
- def __init__(self):
- super(InfraFormatter, self).__init__(
- '[%(severity)s%(iso8601)s %(process)d %(thread)d '
- '%(fullModuleName)s:%(lineno)s] %(message)s')
-
-
-def add_handler(logger, handler=None, timezone='UTC',
- level=logging.WARNING,
- module_name_blacklist=None): # pragma: no cover
- """Configures and adds a handler to a logger the standard way for infra.
-
- Args:
- logger (logging.Logger): logger object obtained from `logging.getLogger`.
-
- Keyword Args:
- handler (logging.Handler): handler to add to the logger. defaults to
- logging.StreamHandler.
- timezone (str): timezone to use for timestamps.
- level (int): logging level. Could be one of DEBUG, INFO, WARNING, CRITICAL
- module_name_blacklist (str): do not print log lines from modules whose name
- matches this regular expression.
-
- Example usage::
-
- import logging
- import infra_libs.logs
- logger = logging.getLogger('foo')
- infra_libs.logs.add_handler(logger, timezone='US/Pacific')
- logger.info('test message')
-
- The last line should print something like::
-
- [I2014-06-27T11:42:32.418716-07:00 7082 logs:71] test message
-
- """
- handler = handler or logging.StreamHandler()
- handler.addFilter(InfraFilter(timezone,
- module_name_blacklist=module_name_blacklist))
- handler.setFormatter(InfraFormatter())
- handler.setLevel(level=level)
- logger.addHandler(handler)
-
- # Formatters only get messages that pass this filter: let everything through.
- logger.setLevel(level=logging.DEBUG)
-
-
-def default_program_name():
- # Use argv[0] as the program name, except when it's '__main__.py' which is the
- # case when we were invoked by run.py. In this case look at the main module's
- # __package__ variable which is set by runpy.
- ret = os.path.basename(sys.argv[0])
- if ret == '__main__.py':
- package = sys.modules['__main__'].__package__
- if package is not None:
- return package.split('.')[-1]
- return ret
-
-
-def add_argparse_options(parser,
- default_level=logging.WARNING): # pragma: no cover
- """Adds logging related options to an argparse.ArgumentParser.
-
- See also: :func:`process_argparse_options`
- """
-
- parser = parser.add_argument_group('Logging Options')
- g = parser.add_mutually_exclusive_group()
- g.set_defaults(log_level=default_level)
- g.add_argument('--logs-quiet', '--quiet',
- action='store_const', const=logging.ERROR,
- dest='log_level', help='Make the output quieter (ERROR).')
- g.add_argument('--logs-warning', '--warning',
- action='store_const', const=logging.WARNING,
- dest='log_level',
- help='Set the output to an average verbosity (WARNING).')
- g.add_argument('--logs-verbose', '--verbose',
- action='store_const', const=logging.INFO,
- dest='log_level', help='Make the output louder (INFO).')
- g.add_argument('--logs-debug', '--debug',
- action='store_const', const=logging.DEBUG,
- dest='log_level', help='Make the output really loud (DEBUG).')
- parser.add_argument('--logs-black-list', metavar='REGEX',
- help='hide log lines emitted by modules whose name '
- 'matches\nthis regular expression.')
- parser.add_argument(
- '--logs-directory',
- default=DEFAULT_LOG_DIRECTORIES,
- help=textwrap.fill(
- 'directory into which to write logs (default: %%(default)s). If '
- 'this directory does not exist or is not writable, the temporary '
- 'directory (%s) will be used instead. If this is explicitly set '
- 'to the empty string, logs will not be written at all. May be set '
- 'to multiple directories separated by the "%s" character, in '
- 'which case the first one that exists and is writable is used.' % (
- tempfile.gettempdir(), os.pathsep), width=56))
- parser.add_argument(
- '--logs-program-name',
- default=default_program_name(),
- help='the program name used to name the log files created in '
- '--logs-directory (default: %(default)s).')
- parser.add_argument(
- '--logs-debug-file',
- action='store_true',
- help='by default only INFO, WARNING and ERROR log files are written to '
- 'disk. This flag causes a DEBUG log to be written as well.')
-
-
-def process_argparse_options(options, logger=None): # pragma: no cover
- """Handles logging argparse options added in 'add_argparse_options'.
-
- Configures 'logging' module.
-
- Args:
- options: return value of argparse.ArgumentParser.parse_args.
- logger (logging.Logger): logger to apply the configuration to.
-
- Example usage::
-
- import argparse
- import sys
- import infra_libs.logs
-
- parser = argparse.ArgumentParser()
- infra_libs.logs.add_argparse_options(parser)
-
- options = parser.parse_args(sys.path[1:])
- infra_libs.logs.process_argparse_options(options)
- """
-
- if logger is None:
- logger = logging.root
-
- add_handler(logger, level=options.log_level,
- module_name_blacklist=options.logs_black_list)
-
- if options.logs_directory:
- _add_file_handlers(options, logger)
-
-
-def _add_file_handlers(options, logger): # pragma: no cover
- # Test whether we can write to the log directory. If not, write to a
- # temporary directory instead. One of the DEFAULT_LOG_DIRECTORIES are created
- # on the real production machines by puppet, so /tmp should only be used when
- # running locally on developers' workstations.
- logs_directory = tempfile.gettempdir()
- for directory in options.logs_directory.split(os.pathsep):
- if not os.path.isdir(directory):
- continue
-
- try:
- with tempfile.TemporaryFile(dir=directory):
- pass
- except OSError:
- pass
- else:
- logs_directory = directory
- break
-
- # Log files are named with this pattern:
- # <program>.<hostname>.<username>.log.<level>.YYYYMMDD-HHMMSS.<pid>
- pattern = "%s.%s.%s.log.%%s.%s.%d" % (
- options.logs_program_name,
- socket.getfqdn().split('.')[0],
- getpass.getuser(),
- datetime.datetime.utcnow().strftime('%Y%m%d-%H%M%S'),
- os.getpid())
-
- file_levels = [logging.INFO, logging.WARNING, logging.ERROR]
- if options.logs_debug_file:
- file_levels.append(logging.DEBUG)
-
- for level in file_levels:
- add_handler(
- logger,
- handler=logging.handlers.RotatingFileHandler(
- filename=os.path.join(
- logs_directory, pattern % logging.getLevelName(level)),
- maxBytes=10 * 1024 * 1024,
- backupCount=10,
- delay=True),
- level=level)

Powered by Google App Engine
This is Rietveld 408576698