Skip to content

Commit 78b6906

Browse files
committed
android: fix logs forwarding latency
Instead of executing an adb shell command for each log line, spawn one adb shell per android device and write one log command per log to its stdin. This considerably reduce the forwarding latency.
1 parent 62d6e59 commit 78b6906

File tree

1 file changed

+28
-15
lines changed

1 file changed

+28
-15
lines changed

avatar/pandora_server.py

Lines changed: 28 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -20,10 +20,10 @@
2020
import grpc
2121
import grpc.aio
2222
import logging
23-
import os
2423
import portpicker
2524
import re
2625
import shlex
26+
import subprocess
2727
import threading
2828
import types
2929

@@ -118,6 +118,7 @@ class AndroidPandoraServer(PandoraServer[AndroidDevice]):
118118
_port: int
119119
_logger: logging.Logger
120120
_handler: logging.Handler
121+
_adb_shell: subprocess.Popen[bytes]
121122

122123
def start(self) -> PandoraClient:
123124
"""Sets up and starts the Pandora server on the Android device."""
@@ -140,27 +141,36 @@ def start(self) -> PandoraClient:
140141

141142
# Forward all logging to ADB logs
142143
adb = self.device.adb
144+
self._adb_shell = subprocess.Popen(['adb', '-s', adb.serial, 'shell'], stdin=subprocess.PIPE)
145+
146+
# This regex match all ANSI escape sequences (colors, style, ..).
147+
ansi_escape = re.compile(r'\x1B(?:[@-Z\\-_]|\[[0-?]*[ -/]*[@-~])')
143148

144149
class AdbLoggingHandler(logging.Handler):
150+
LOGGING_TO_ANDROID_LEVELS = {
151+
logging.FATAL: 'f',
152+
logging.ERROR: 'e',
153+
logging.WARN: 'w',
154+
logging.INFO: 'i',
155+
logging.DEBUG: 'd',
156+
logging.NOTSET: 'd',
157+
}
158+
159+
def __init__(self, adb_shell: subprocess.Popen[bytes]) -> None:
160+
self.adb_shell = adb_shell
161+
145162
def emit(self, record: logging.LogRecord) -> None:
146163
if record.levelno <= logging.DEBUG:
147164
return
148-
ansi_escape = re.compile(r'\x1B(?:[@-Z\\-_]|\[[0-?]*[ -/]*[@-~])')
149-
msg = self.format(record)
150-
msg = ansi_escape.sub('', msg)
151-
level = {
152-
logging.FATAL: 'f',
153-
logging.ERROR: 'e',
154-
logging.WARN: 'w',
155-
logging.INFO: 'i',
156-
logging.DEBUG: 'd',
157-
logging.NOTSET: 'd',
158-
}[record.levelno]
159-
for msg in msg.splitlines():
160-
os.system(f'adb -s {adb.serial} shell "log -t Avatar -p {level} {shlex.quote(msg)}"')
165+
# Format and remove all ANSI escape sequences.
166+
msg = ansi_escape.sub('', self.format(record))
167+
level = AdbLoggingHandler.LOGGING_TO_ANDROID_LEVELS[record.levelno]
168+
assert self.adb_shell.stdin
169+
self.adb_shell.stdin.write(f'log -t Avatar -p {level} {shlex.quote(msg)}\n'.encode('utf-8'))
170+
self.adb_shell.stdin.flush()
161171

162172
self._logger = logging.getLogger()
163-
self._handler = AdbLoggingHandler()
173+
self._handler = AdbLoggingHandler(self._adb_shell)
164174
self._logger.addHandler(self._handler)
165175

166176
return PandoraClient(f'localhost:{self._port}', 'android')
@@ -176,6 +186,9 @@ def stop(self) -> None:
176186

177187
# Remove ADB logging handler
178188
self._logger.removeHandler(self._handler)
189+
assert self._adb_shell.stdin
190+
self._adb_shell.stdin.close()
191+
self._adb_shell.wait()
179192

180193
self.device.adb.forward(['--remove', f'tcp:{self._port}']) # type: ignore
181194
self._instrumentation.join()

0 commit comments

Comments
 (0)