1#!/usr/bin/env python3 2# 3# Copyright 2018 - The Android Open Source Project 4# 5# Licensed under the Apache License, Version 2.0 (the "License"); 6# you may not use this file except in compliance with the License. 7# You may obtain a copy of the License at 8# 9# http://www.apache.org/licenses/LICENSE-2.0 10# 11# Unless required by applicable law or agreed to in writing, software 12# distributed under the License is distributed on an "AS IS" BASIS, 13# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14# See the License for the specific language governing permissions and 15# limitations under the License. 16 17import logging 18import re 19 20from acts.libs.proc.process import Process 21from acts.libs.logging import log_stream 22from acts.libs.logging.log_stream import LogStyles 23 24TIMESTAMP_REGEX = r'((?:\d+-)?\d+-\d+ \d+:\d+:\d+.\d+)' 25 26 27class TimestampTracker(object): 28 """Stores the last timestamp outputted by the Logcat process.""" 29 30 def __init__(self): 31 self._last_timestamp = None 32 33 @property 34 def last_timestamp(self): 35 return self._last_timestamp 36 37 def read_output(self, message): 38 """Reads the message and parses all timestamps from it.""" 39 all_timestamps = re.findall(TIMESTAMP_REGEX, message) 40 if len(all_timestamps) > 0: 41 self._last_timestamp = all_timestamps[0] 42 43 44def _get_log_level(message): 45 """Returns the log level for the given message.""" 46 if message.startswith('-') or len(message) < 37: 47 return logging.ERROR 48 else: 49 log_level = message[36] 50 if log_level in ('V', 'D'): 51 return logging.DEBUG 52 elif log_level == 'I': 53 return logging.INFO 54 elif log_level == 'W': 55 return logging.WARNING 56 elif log_level == 'E': 57 return logging.ERROR 58 return logging.NOTSET 59 60 61def _log_line_func(log, timestamp_tracker): 62 """Returns a lambda that logs a message to the given logger.""" 63 64 def log_line(message): 65 timestamp_tracker.read_output(message) 66 log.log(_get_log_level(message), message) 67 68 return log_line 69 70 71def _on_retry(serial, extra_params, timestamp_tracker): 72 def on_retry(_): 73 begin_at = '"%s"' % (timestamp_tracker.last_timestamp or 1) 74 additional_params = extra_params or '' 75 76 return 'adb -s %s logcat -T %s -v year %s' % ( 77 serial, begin_at, additional_params) 78 79 return on_retry 80 81 82def create_logcat_keepalive_process(serial, logcat_dir, extra_params=''): 83 """Creates a Logcat Process that automatically attempts to reconnect. 84 85 Args: 86 serial: The serial of the device to read the logcat of. 87 logcat_dir: The directory used for logcat file output. 88 extra_params: Any additional params to be added to the logcat cmdline. 89 90 Returns: 91 A acts.libs.proc.process.Process object. 92 """ 93 logger = log_stream.create_logger( 94 'adblog_%s' % serial, log_name=serial, subcontext=logcat_dir, 95 log_styles=(LogStyles.LOG_DEBUG | LogStyles.TESTCASE_LOG)) 96 process = Process('adb -s %s logcat -T 1 -v year %s' % 97 (serial, extra_params)) 98 timestamp_tracker = TimestampTracker() 99 process.set_on_output_callback(_log_line_func(logger, timestamp_tracker)) 100 process.set_on_terminate_callback( 101 _on_retry(serial, extra_params, timestamp_tracker)) 102 return process 103