Index: appengine/swarming/swarming_bot/logging_utils.py |
diff --git a/appengine/swarming/swarming_bot/logging_utils.py b/appengine/swarming/swarming_bot/logging_utils.py |
deleted file mode 100644 |
index efddb9c99c8da8360341115449ff09bc87aa0568..0000000000000000000000000000000000000000 |
--- a/appengine/swarming/swarming_bot/logging_utils.py |
+++ /dev/null |
@@ -1,168 +0,0 @@ |
-# Copyright 2014 The Swarming Authors. All rights reserved. |
-# Use of this source code is governed by the Apache v2.0 license that can be |
-# found in the LICENSE file. |
- |
-"""Utility relating to logging. |
- |
-TODO(maruel): Merge buffering and output related code from client/utils/tools.py |
-in a single file. |
-""" |
- |
-import codecs |
-import logging |
-import logging.handlers |
-import os |
-import sys |
-import tempfile |
-import time |
- |
- |
-# This works around file locking issue on Windows specifically in the case of |
-# long lived child processes. |
-# |
-# Python opens files with inheritable handle and without file sharing by |
-# default. This causes the RotatingFileHandler file handle to be duplicated in |
-# the subprocesses even if the log file is not used in it. Because of this |
-# handle in the child process, when the RotatingFileHandler tries to os.rename() |
-# the file in the parent process, it fails with: |
-# WindowsError: [Error 32] The process cannot access the file because |
-# it is being used by another process |
-if sys.platform == 'win32': |
- import msvcrt # pylint: disable=F0401 |
- import _subprocess # pylint: disable=F0401 |
- |
- # TODO(maruel): Make it work in cygwin too if necessary. This would have to |
- # use ctypes.cdll.kernel32 instead of _subprocess and msvcrt. |
- |
- def _duplicate(handle): |
- target_process = _subprocess.GetCurrentProcess() |
- return _subprocess.DuplicateHandle( |
- _subprocess.GetCurrentProcess(), handle, target_process, |
- 0, False, _subprocess.DUPLICATE_SAME_ACCESS).Detach() |
- |
- |
- class NoInheritRotatingFileHandler(logging.handlers.RotatingFileHandler): |
- def _open(self): |
- """Opens the current file without handle inheritance.""" |
- if self.encoding is None: |
- with open(self.baseFilename, self.mode) as stream: |
- newosf = _duplicate(msvcrt.get_osfhandle(stream.fileno())) |
- new_fd = msvcrt.open_osfhandle(newosf, os.O_APPEND) |
- return os.fdopen(new_fd, self.mode) |
- return codecs.open(self.baseFilename, self.mode, self.encoding) |
- |
- |
-else: # Not Windows. |
- |
- |
- NoInheritRotatingFileHandler = logging.handlers.RotatingFileHandler |
- |
- |
-class CaptureLogs(object): |
- """Captures all the logs in a context.""" |
- def __init__(self, prefix, root=None): |
- handle, self._path = tempfile.mkstemp(prefix=prefix, suffix='.log') |
- os.close(handle) |
- self._handler = logging.FileHandler(self._path, 'w') |
- self._handler.setLevel(logging.DEBUG) |
- formatter = UTCFormatter( |
- '%(process)d %(asctime)s: %(levelname)-5s %(message)s') |
- self._handler.setFormatter(formatter) |
- self._root = root or logging.getLogger() |
- self._root.addHandler(self._handler) |
- assert self._root.isEnabledFor(logging.DEBUG) |
- |
- def read(self): |
- """Returns the current content of the logs. |
- |
- This also closes the log capture so future logs will not be captured. |
- """ |
- self._disconnect() |
- assert self._path |
- try: |
- with open(self._path, 'rb') as f: |
- return f.read() |
- except IOError as e: |
- return 'Failed to read %s: %s' % (self._path, e) |
- |
- def close(self): |
- """Closes and delete the log.""" |
- self._disconnect() |
- if self._path: |
- try: |
- os.remove(self._path) |
- except OSError as e: |
- logging.error('Failed to delete log file %s: %s', self._path, e) |
- self._path = None |
- |
- def __enter__(self): |
- return self |
- |
- def __exit__(self, _exc_type, _exc_value, _traceback): |
- self.close() |
- |
- def _disconnect(self): |
- if self._handler: |
- self._root.removeHandler(self._handler) |
- self._handler.close() |
- self._handler = None |
- |
- |
-class UTCFormatter(logging.Formatter): |
- converter = time.gmtime |
- |
- def formatTime(self, record, datefmt=None): |
- """Change is ',' to '.'.""" |
- ct = self.converter(record.created) |
- if datefmt: |
- return time.strftime(datefmt, ct) |
- else: |
- t = time.strftime("%Y-%m-%d %H:%M:%S", ct) |
- return "%s.%03d" % (t, record.msecs) |
- |
- |
-def find_stderr(root=None): |
- """Returns the logging.handler streaming to stderr, if any.""" |
- for log in (root or logging.getLogger()).handlers: |
- if getattr(log, 'stream', None) is sys.stderr: |
- return log |
- |
- |
-def prepare_logging(filename, root=None): |
- """Prepare logging for scripts. |
- |
- Makes it log in UTC all the time. Prepare a rotating file based log. |
- """ |
- assert not find_stderr(root) |
- formatter = UTCFormatter( |
- '%(process)d %(asctime)s: %(levelname)-5s %(message)s') |
- |
- # It is a requirement that the root logger is set to DEBUG, so the messages |
- # are not lost. It defaults to WARNING otherwise. |
- logger = root or logging.getLogger() |
- logger.setLevel(logging.DEBUG) |
- |
- stderr = logging.StreamHandler() |
- stderr.setFormatter(formatter) |
- # Default to ERROR. |
- stderr.setLevel(logging.ERROR) |
- logger.addHandler(stderr) |
- |
- # Setup up logging to a constant file so we can debug issues where |
- # the results aren't properly sent to the result URL. |
- if filename: |
- try: |
- rotating_file = NoInheritRotatingFileHandler( |
- filename, maxBytes=10 * 1024 * 1024, backupCount=5) |
- rotating_file.setLevel(logging.DEBUG) |
- rotating_file.setFormatter(formatter) |
- logger.addHandler(rotating_file) |
- except Exception: |
- # May happen on cygwin. Do not crash. |
- logging.exception('Failed to open %s', filename) |
- |
- |
-def set_console_level(level, root=None): |
- """Reset the console (stderr) logging level.""" |
- handler = find_stderr(root) |
- handler.setLevel(level) |