96 lines
2.7 KiB
Python
96 lines
2.7 KiB
Python
# Copyright 2020 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.
|
|
|
|
"""Creates and manages test runner log file objects.
|
|
|
|
Provides a context manager object for use in a with statement
|
|
and a module level FileStreamFor function for use by clients.
|
|
"""
|
|
|
|
import collections
|
|
import multiprocessing
|
|
import os
|
|
|
|
from symbolizer import RunSymbolizer
|
|
|
|
SYMBOLIZED_SUFFIX = '.symbolized'
|
|
|
|
_RunnerLogEntry = collections.namedtuple(
|
|
'_RunnerLogEntry', ['name', 'log_file', 'path', 'symbolize'])
|
|
|
|
# Module singleton variable.
|
|
_instance = None
|
|
|
|
|
|
class RunnerLogManager(object):
|
|
""" Runner logs object for use in a with statement."""
|
|
|
|
def __init__(self, log_dir, build_ids_files):
|
|
global _instance
|
|
if _instance:
|
|
raise Exception('Only one RunnerLogManager can be instantiated')
|
|
|
|
self._log_dir = log_dir
|
|
self._build_ids_files = build_ids_files
|
|
self._runner_logs = []
|
|
|
|
if self._log_dir and not os.path.isdir(self._log_dir):
|
|
os.makedirs(self._log_dir)
|
|
|
|
_instance = self
|
|
|
|
def __enter__(self):
|
|
return self
|
|
|
|
def __exit__(self, exc_type, exc_value, traceback):
|
|
pool = multiprocessing.Pool(4)
|
|
for log_entry in self._runner_logs:
|
|
pool.apply_async(_FinalizeLog, (log_entry, self._build_ids_files))
|
|
pool.close()
|
|
pool.join()
|
|
_instance = None
|
|
|
|
|
|
def _FileStreamFor(self, name, symbolize):
|
|
if any(elem.name == name for elem in self._runner_logs):
|
|
raise Exception('RunnerLogManager can only open "%s" once' % name)
|
|
|
|
path = os.path.join(self._log_dir, name) if self._log_dir else os.devnull
|
|
log_file = open(path, 'w')
|
|
|
|
self._runner_logs.append(_RunnerLogEntry(name, log_file, path, symbolize))
|
|
|
|
return log_file
|
|
|
|
|
|
def _FinalizeLog(log_entry, build_ids_files):
|
|
log_entry.log_file.close()
|
|
|
|
if log_entry.symbolize:
|
|
input_file = open(log_entry.path, 'r')
|
|
output_file = open(log_entry.path + SYMBOLIZED_SUFFIX, 'w')
|
|
proc = RunSymbolizer(input_file, output_file, build_ids_files)
|
|
proc.wait()
|
|
output_file.close()
|
|
input_file.close()
|
|
|
|
|
|
def IsEnabled():
|
|
"""Returns True if the RunnerLogManager has been created, or False if not."""
|
|
|
|
return _instance is not None and _instance._log_dir is not None
|
|
|
|
|
|
def FileStreamFor(name, symbolize=False):
|
|
"""Opens a test runner file stream in the test runner log directory.
|
|
|
|
If no test runner log directory is specified, output is discarded.
|
|
|
|
name: log file name
|
|
symbolize: if True, make a symbolized copy of the log after closing it.
|
|
|
|
Returns an opened log file object."""
|
|
|
|
return _instance._FileStreamFor(name, symbolize) if IsEnabled() else open(
|
|
os.devnull, 'w')
|