Revert "Android: Add logging_ext.py module."

This reverts commit 9d96ef6894f46439e73300cdecf8ba30285e77f6.

Reason for revert: blocks rolls into clank on ToT bot.
BUG=758505

Original change's description:
> Android: Add logging_ext.py module.
> 
> Adds color in a better way to pylib code.
> Additionally added some more useful test logs (such as whether
> tests pass/fail as they are run and predicted time remaining).
> 
> Adding -quiet option which will make the logs as they were
> previously.
> 
> Change-Id: I0cec6334a38544ccbb4a4fde6df65b6b56c2547d
> Reviewed-on: https://chromium-review.googlesource.com/603916
> Commit-Queue: Michael Case <[email protected]>
> Reviewed-by: Andrew Grieve <[email protected]>
> Reviewed-by: John Budorick <[email protected]>
> Cr-Commit-Position: refs/heads/master@{#496922}

[email protected],[email protected],[email protected],[email protected]

Change-Id: I11ca3412b5b3516bfd7107aa3f0732e7331b9941
No-Presubmit: true
No-Tree-Checks: true
No-Try: true
Reviewed-on: https://chromium-review.googlesource.com/631724
Reviewed-by: Anthony Berent <[email protected]>
Commit-Queue: Anthony Berent <[email protected]>
Cr-Commit-Position: refs/heads/master@{#496992}
diff --git a/build/android/play_services/update.py b/build/android/play_services/update.py
index 8a43058..5297b3cf 100755
--- a/build/android/play_services/update.py
+++ b/build/android/play_services/update.py
@@ -22,8 +22,8 @@
 from devil.utils import cmd_helper
 from play_services import utils
 from pylib import constants
-from pylib import logging_ext
 from pylib.constants import host_paths
+from pylib.utils import logging_utils
 
 sys.path.append(os.path.join(host_paths.DIR_SOURCE_ROOT, 'build'))
 import find_depot_tools  # pylint: disable=import-error,unused-import
@@ -89,8 +89,10 @@
   AddBucketArguments(parser_upload)
 
   args = parser.parse_args(raw_args)
+  if args.verbose:
+    logging.basicConfig(level=logging.DEBUG)
+  logging_utils.ColorStreamHandler.MakeDefault(not _IsBotEnvironment())
   devil_chromium.Initialize()
-  logging_ext.Initialize(args.verbose_count)
   return args.func(args)
 
 
@@ -100,21 +102,25 @@
   allows to put arguments after the command: `foo.py upload --debug --force`
   instead of `foo.py --debug upload --force`
   '''
+
   parser.add_argument('--sdk-root',
                       help='base path to the Android SDK tools root',
                       default=constants.ANDROID_SDK_ROOT)
+
   parser.add_argument('-v', '--verbose',
-                      dest='verbose_count', default=0, action='count',
-                      help='Verbose level (multiple times for more)')
+                      action='store_true',
+                      help='print debug information')
 
 
 def AddBucketArguments(parser):
   parser.add_argument('--bucket',
                       help='name of the bucket where the files are stored',
                       default=GMS_CLOUD_STORAGE)
+
   parser.add_argument('--config',
                       help='JSON Configuration file',
                       default=CONFIG_DEFAULT_PATH)
+
   parser.add_argument('--dry-run',
                       action='store_true',
                       help=('run the script in dry run mode. Files will be '
@@ -122,6 +128,7 @@
                             'cloud storage. The bucket name will be as path '
                             'to that directory relative to the repository '
                             'root.'))
+
   parser.add_argument('-f', '--force',
                       action='store_true',
                       help='run even if the library is already up to date')
@@ -177,7 +184,7 @@
 
     license_sha1 = os.path.join(SHA1_DIRECTORY, LICENSE_FILE_NAME + '.sha1')
     _DownloadFromBucket(bucket_path, license_sha1, new_license,
-                        bool(args.verbose_count), args.dry_run)
+                        args.verbose, args.dry_run)
 
     if (not _IsBotEnvironment() and
         not _CheckLicenseAgreement(new_license, paths.license,
@@ -190,7 +197,7 @@
 
     new_lib_zip = os.path.join(tmp_root, ZIP_FILE_NAME)
     _DownloadFromBucket(bucket_path, new_lib_zip_sha1, new_lib_zip,
-                        bool(args.verbose_count), args.dry_run)
+                        args.verbose, args.dry_run)
 
     try:
       # Remove the deprecated sdk directory.
diff --git a/build/android/pylib/base/test_collection.py b/build/android/pylib/base/test_collection.py
index 19e1f0dc..de51027 100644
--- a/build/android/pylib/base/test_collection.py
+++ b/build/android/pylib/base/test_collection.py
@@ -74,3 +74,7 @@
     """Return the number of tests currently in the collection."""
     return len(self._tests)
 
+  def test_names(self):
+    """Return a list of the names of the tests currently in the collection."""
+    with self._lock:
+      return list(t.test for t in self._tests)
diff --git a/build/android/pylib/local/device/local_device_instrumentation_test_run.py b/build/android/pylib/local/device/local_device_instrumentation_test_run.py
index ee84437..8030d0f 100644
--- a/build/android/pylib/local/device/local_device_instrumentation_test_run.py
+++ b/build/android/pylib/local/device/local_device_instrumentation_test_run.py
@@ -21,7 +21,6 @@
 from devil.android.tools import system_app
 from devil.utils import reraiser_thread
 from incremental_install import installer
-from pylib import logging_ext
 from pylib import valgrind_tools
 from pylib.android import logdog_logcat_monitor
 from pylib.base import base_test_result
@@ -589,15 +588,6 @@
 
     if self._env.concurrent_adb:
       post_test_step_thread_group.JoinAll()
-
-    if result.GetType() == base_test_result.ResultType.PASS:
-      logging_ext.test_pass('%s %s', result.GetType(), test_display_name)
-    elif result.GetType() in (base_test_result.ResultType.NOTRUN,
-                              base_test_result.ResultType.SKIP):
-      pass
-    else:
-      logging_ext.test_fail('%s %s', result.GetType(), test_display_name)
-
     return results, None
 
   def _GetTestsFromRunner(self):
diff --git a/build/android/pylib/local/device/local_device_test_run.py b/build/android/pylib/local/device/local_device_test_run.py
index 6f8cbf2..94e9c82 100644
--- a/build/android/pylib/local/device/local_device_test_run.py
+++ b/build/android/pylib/local/device/local_device_test_run.py
@@ -6,7 +6,6 @@
 import logging
 import posixpath
 import signal
-import time
 import thread
 import threading
 
@@ -14,7 +13,6 @@
 from devil.android import crash_handler
 from devil.android import device_errors
 from devil.utils import signal_handler
-from pylib import logging_ext
 from pylib import valgrind_tools
 from pylib.base import base_test_result
 from pylib.base import test_run
@@ -53,15 +51,10 @@
     super(LocalDeviceTestRun, self).__init__(env, test_instance)
     self._tools = {}
 
-    # Fields used for logging time remaining estimates
-    self._start_tests_remaining = None
-    self._start_time = None
-    self._last_time_log = None
-    self._log_lock = threading.Lock()
-
   #override
   def RunTests(self):
     tests = self._GetTests()
+
     exit_now = threading.Event()
 
     @local_device_environment.handle_shard_failures
@@ -72,8 +65,6 @@
 
         result = None
         rerun = None
-        if isinstance(tests, test_collection.TestCollection):
-          self._MaybeLogTimeRemaining(tests)
         try:
           result, rerun = crash_handler.RetryOnSystemCrash(
               lambda d, t=test: self._RunTest(d, t),
@@ -129,9 +120,6 @@
           try:
             if self._ShouldShard():
               tc = test_collection.TestCollection(self._CreateShards(tests))
-              self._start_tests_remaining = len(tc)
-              self._start_time = time.time()
-              self._last_time_log = time.time()
               self._env.parallel_devices.pMap(
                   run_tests_on_device, tc, try_results).pGet(None)
             else:
@@ -161,41 +149,6 @@
 
     return results
 
-  def _MaybeLogTimeRemaining(self, tests):
-    """Log an estimate for how long remains to run the test suite.
-
-    If this function has not logged an estimate in the previous |LOG_COOLDOWN|
-    then it will log the number of tests remaining to run. This function will
-    also attempt to estimate the time remaining in the test run. It does this
-    by (1) looking to see if runtimes for tests were cached by a previous run,
-    (2) else attempt to extrapolate test runtimes from the tests run so far,
-    (3) or fall back to a hardcoded fix value.
-
-    Args:
-      tests: Test collection.
-    """
-    LOG_COOLDOWN = 60 # seconds
-
-    # Lock is to prevent multiple test shards from simultaneously printing
-    # time estimate.
-    with self._log_lock:
-      if time.time() - self._last_time_log < LOG_COOLDOWN:
-        return
-      self._last_time_log = time.time()
-
-    tests_remaining = len(tests)
-    if tests_remaining < self._start_tests_remaining:
-      avg_test_duration = (float(time.time() - self._start_time) /
-                           (self._start_tests_remaining - tests_remaining))
-    else:
-      avg_test_duration = 1.0
-
-    time_estimate = (avg_test_duration * tests_remaining /
-                     len(self._env.devices))
-    logging_ext.test_time('Tests remaining: %d, '
-                          'Estimated time remaining: %.2f seconds',
-                          tests_remaining, time_estimate)
-
   def _GetTestsToRetry(self, tests, try_results):
 
     def is_failure_result(test_result):
diff --git a/build/android/pylib/logging_ext/__init__.py b/build/android/pylib/logging_ext/__init__.py
deleted file mode 100644
index 62604c5..0000000
--- a/build/android/pylib/logging_ext/__init__.py
+++ /dev/null
@@ -1,105 +0,0 @@
-# Copyright 2017 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.
-
-"""Wrapper around logging to set some custom logging functionality."""
-
-import contextlib
-import logging
-import os
-import time
-import sys
-
-from pylib.constants import host_paths
-from pylib.utils import unicode_characters
-
-_COLORAMA_PATH = os.path.join(
-    host_paths.DIR_SOURCE_ROOT, 'third_party', 'colorama', 'src')
-
-with host_paths.SysPath(_COLORAMA_PATH, position=0):
-  import colorama
-
-TEST_FAIL = logging.INFO + 3
-TEST_PASS = logging.INFO + 2
-TEST_TIME = logging.INFO + 1
-
-
-def _set_log_level(verbose_count):
-  if verbose_count == -1: # Quiet
-    log_level = logging.WARNING
-  elif verbose_count == 0: # Default
-    log_level = logging.INFO + 1
-  elif verbose_count == 1:
-    log_level = logging.INFO
-  elif verbose_count >= 2:
-    log_level = logging.DEBUG
-  logging.getLogger().setLevel(log_level)
-
-
-class CustomFormatter(logging.Formatter):
-  """Custom log formatter."""
-  # pylint does not see members added dynamically in the constructor.
-  # pylint: disable=no-member
-  COLOR_MAP = {
-    logging.DEBUG: colorama.Fore.CYAN + colorama.Style.DIM,
-    logging.WARNING: colorama.Fore.YELLOW,
-    logging.ERROR: colorama.Back.RED,
-    logging.CRITICAL: colorama.Back.RED,
-    TEST_PASS: colorama.Fore.GREEN + colorama.Style.BRIGHT,
-    TEST_FAIL: colorama.Fore.RED + colorama.Style.BRIGHT,
-    TEST_TIME: colorama.Fore.MAGENTA + colorama.Style.BRIGHT,
-  }
-
-  # override
-  def __init__(self, color, fmt='%(threadName)-4s  %(message)s'):
-    logging.Formatter.__init__(self, fmt=fmt)
-    self._creation_time = time.time()
-    self._should_colorize = color
-
-  # override
-  def format(self, record):
-    msg = logging.Formatter.format(self, record)
-    if 'MainThread' in msg[:19]:
-      msg = msg.replace('MainThread', 'Main', 1)
-    timediff = time.time() - self._creation_time
-
-    level_tag = record.levelname[0]
-    if self._should_colorize:
-      level_tag = '%s%-1s%s' % (
-          self.COLOR_MAP.get(record.levelno, ''),
-          level_tag, colorama.Style.RESET_ALL)
-
-    return '%s %8.3fs %s' % (level_tag, timediff, msg)
-
-
-logging.addLevelName(TEST_TIME, unicode_characters.CLOCK)
-def test_time(message, *args, **kwargs):
-  logger = logging.getLogger()
-  if logger.isEnabledFor(TEST_TIME):
-    # pylint: disable=protected-access
-    logger._log(TEST_TIME, message, args, **kwargs)
-
-
-logging.addLevelName(TEST_FAIL, unicode_characters.CROSS)
-def test_fail(message, *args, **kwargs):
-  logger = logging.getLogger()
-  if logger.isEnabledFor(TEST_FAIL):
-    # pylint: disable=protected-access
-    logger._log(TEST_FAIL, message, args, **kwargs)
-
-
-logging.addLevelName(TEST_PASS, unicode_characters.CHECK)
-def test_pass(message, *args, **kwargs):
-  logger = logging.getLogger()
-  if logger.isEnabledFor(TEST_PASS):
-    # pylint: disable=protected-access
-    logger._log(TEST_PASS, message, args, **kwargs)
-
-
-def Initialize(log_level):
-  logging.getLogger().handlers = []
-  handler = logging.StreamHandler(stream=sys.stderr)
-  logging.getLogger().addHandler(handler)
-  handler.setFormatter(CustomFormatter(color=sys.stderr.isatty()))
-  _set_log_level(log_level)
-
diff --git a/build/android/pylib/utils/logging_utils.py b/build/android/pylib/utils/logging_utils.py
new file mode 100644
index 0000000..082f9e8
--- /dev/null
+++ b/build/android/pylib/utils/logging_utils.py
@@ -0,0 +1,111 @@
+# 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.
+
+import contextlib
+import logging
+import os
+
+from pylib.constants import host_paths
+
+_COLORAMA_PATH = os.path.join(
+    host_paths.DIR_SOURCE_ROOT, 'third_party', 'colorama', 'src')
+
+with host_paths.SysPath(_COLORAMA_PATH, position=0):
+  import colorama
+
+
+class _ColorFormatter(logging.Formatter):
+  # pylint does not see members added dynamically in the constructor.
+  # pylint: disable=no-member
+  color_map = {
+    logging.DEBUG: colorama.Fore.CYAN,
+    logging.WARNING: colorama.Fore.YELLOW,
+    logging.ERROR: colorama.Fore.RED,
+    logging.CRITICAL: colorama.Back.RED,
+  }
+
+  def __init__(self, wrapped_formatter=None):
+    """Wraps a |logging.Formatter| and adds color."""
+    super(_ColorFormatter, self).__init__(self)
+    self._wrapped_formatter = wrapped_formatter or logging.Formatter()
+
+  #override
+  def format(self, record):
+    message = self._wrapped_formatter.format(record)
+    return self.Colorize(message, record.levelno)
+
+  def Colorize(self, message, log_level):
+    try:
+      return self.color_map[log_level] + message + colorama.Style.RESET_ALL
+    except KeyError:
+      return message
+
+
+class ColorStreamHandler(logging.StreamHandler):
+  """Handler that can be used to colorize logging output.
+
+  Example using a specific logger:
+
+    logger = logging.getLogger('my_logger')
+    logger.addHandler(ColorStreamHandler())
+    logger.info('message')
+
+  Example using the root logger:
+
+    ColorStreamHandler.MakeDefault()
+    logging.info('message')
+
+  """
+  def __init__(self, force_color=False):
+    super(ColorStreamHandler, self).__init__()
+    self.force_color = force_color
+    self.setFormatter(logging.Formatter())
+
+  @property
+  def is_tty(self):
+    isatty = getattr(self.stream, 'isatty', None)
+    return isatty and isatty()
+
+  #override
+  def setFormatter(self, formatter):
+    if self.force_color or self.is_tty:
+      formatter = _ColorFormatter(formatter)
+    super(ColorStreamHandler, self).setFormatter(formatter)
+
+  @staticmethod
+  def MakeDefault(force_color=False):
+     """
+     Replaces the default logging handlers with a coloring handler. To use
+     a colorizing handler at the same time as others, either register them
+     after this call, or add the ColorStreamHandler on the logger using
+     Logger.addHandler()
+
+     Args:
+       force_color: Set to True to bypass the tty check and always colorize.
+     """
+     # If the existing handlers aren't removed, messages are duplicated
+     logging.getLogger().handlers = []
+     logging.getLogger().addHandler(ColorStreamHandler(force_color))
+
+
[email protected]
+def SuppressLogging(level=logging.ERROR):
+  """Momentarilly suppress logging events from all loggers.
+
+  TODO(jbudorick): This is not thread safe. Log events from other threads might
+  also inadvertently disappear.
+
+  Example:
+
+    with logging_utils.SuppressLogging():
+      # all but CRITICAL logging messages are suppressed
+      logging.info('just doing some thing') # not shown
+      logging.critical('something really bad happened') # still shown
+
+  Args:
+    level: logging events with this or lower levels are suppressed.
+  """
+  logging.disable(level)
+  yield
+  logging.disable(logging.NOTSET)
diff --git a/build/android/pylib/utils/unicode_characters.py b/build/android/pylib/utils/unicode_characters.py
deleted file mode 100644
index 0d56077..0000000
--- a/build/android/pylib/utils/unicode_characters.py
+++ /dev/null
@@ -1,9 +0,0 @@
-# Copyright 2017 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.
-
-"""Helper module that contains unicode character constants."""
-
-CHECK = u'\u2714'
-CLOCK = u'\u231A'
-CROSS = u'\u274C'
diff --git a/build/android/test_runner.py b/build/android/test_runner.py
index 7677329a..01a0ec53 100755
--- a/build/android/test_runner.py
+++ b/build/android/test_runner.py
@@ -31,9 +31,9 @@
 
 from devil import base_error
 from devil.utils import reraiser_thread
+from devil.utils import run_tests_helper
 
 from pylib import constants
-from pylib import logging_ext
 from pylib.base import base_test_result
 from pylib.base import environment_factory
 from pylib.base import test_instance_factory
@@ -41,6 +41,7 @@
 from pylib.results import json_results
 from pylib.results import report_results
 from pylib.utils import logdog_helper
+from pylib.utils import logging_utils
 
 from py_utils import contextlib_ext
 
@@ -178,6 +179,7 @@
       '--gs-results-bucket',
       help='Google Storage bucket to upload results to.')
 
+
   parser.add_argument(
       '--output-directory',
       dest='output_directory', type=os.path.realpath,
@@ -192,17 +194,20 @@
       '-v', '--verbose',
       dest='verbose_count', default=0, action='count',
       help='Verbose level (multiple times for more)')
-  parser.add_argument(
-      '-q', '--quiet',
-      dest='quiet', action='store_true',
-      help='Option for very quiet logging.')
 
   AddTestLauncherOptions(parser)
 
 
 def ProcessCommonOptions(args):
   """Processes and handles all common options."""
-  logging_ext.Initialize(-1 if args.quiet else args.verbose_count)
+  run_tests_helper.SetLogLevel(args.verbose_count, add_handler=False)
+  if args.verbose_count > 0:
+    handler = logging_utils.ColorStreamHandler()
+  else:
+    handler = logging.StreamHandler(sys.stdout)
+  handler.setFormatter(run_tests_helper.CustomFormatter())
+  logging.getLogger().addHandler(handler)
+
   constants.SetBuildType(args.build_type)
   if args.output_directory:
     constants.SetOutputDirectory(args.output_directory)
@@ -844,6 +849,7 @@
         continue
 
       all_raw_results.append(raw_results)
+
       iteration_results = base_test_result.TestRunResults()
       for r in reversed(raw_results):
         iteration_results.AddTestRunResults(r)
diff --git a/build/android/test_runner.pydeps b/build/android/test_runner.pydeps
index c4b3c43..573577e 100644
--- a/build/android/test_runner.pydeps
+++ b/build/android/test_runner.pydeps
@@ -163,7 +163,6 @@
 pylib/local/machine/__init__.py
 pylib/local/machine/local_machine_environment.py
 pylib/local/machine/local_machine_junit_test_run.py
-pylib/logging_ext/__init__.py
 pylib/monkey/__init__.py
 pylib/monkey/monkey_test_instance.py
 pylib/perf/__init__.py
@@ -183,11 +182,11 @@
 pylib/utils/google_storage_helper.py
 pylib/utils/instrumentation_tracing.py
 pylib/utils/logdog_helper.py
+pylib/utils/logging_utils.py
 pylib/utils/proguard.py
 pylib/utils/repo_utils.py
 pylib/utils/shared_preference_utils.py
 pylib/utils/time_profile.py
-pylib/utils/unicode_characters.py
 pylib/valgrind_tools.py
 test_runner.py
 tombstones.py