Last active
January 29, 2020 03:29
-
-
Save samthor/d5876b6f43fe97526a8aad24ec959366 to your computer and use it in GitHub Desktop.
Better logcat
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env python | |
import logging | |
import subprocess | |
import re | |
import sys | |
import datetime | |
RE_SPACES = re.compile('\s+') | |
RE_LOGCAT = re.compile('^(\w+)/(\w+)\((\d+?)\): (.*)$') # Android 9 or older? | |
RE_LOGCAT_NEW = re.compile('\s+(\d+)\s+([A-Z])\s+([^:]+): (.*)$') | |
WIDTH_ALIGN = 32 | |
class colors: | |
E = '\033[91m' | |
W = '\033[93m' | |
I = '\033[92m' | |
V = '\033[96m' | |
D = '\033[95m' | |
def to_string(raw): | |
# needed for Python 2 vs 3: subprocess returns bytes in 3, str in 2 | |
if type(raw) == str: | |
return raw | |
return raw.decode('utf-8') # str(b) doesn't work (it includes literal "b'...'") | |
def package_pids(): | |
m = {} | |
output = to_string(subprocess.check_output(['adb', 'shell', 'ps'])) | |
for line in output.split('\n'): | |
line = str(line) | |
line = RE_SPACES.sub('\t', line) | |
parts = line.split() | |
if len(parts) > 8: | |
pid, package_id = parts[1], parts[8] | |
try: | |
pid = int(pid) | |
except: | |
continue | |
m[pid] = package_id | |
return m | |
def ralign(text, width=WIDTH_ALIGN): | |
text = text.rjust(width) | |
if len(text) > width: | |
text = text[-WIDTH_ALIGN:] | |
if text[3] != '.': | |
text = '...' + text[3:] | |
else: | |
text = '..' + text[2:] | |
return text | |
def render(level, tag, pid, package, text): | |
starter = hasattr(colors, level) and getattr(colors, level) or '' | |
out = sys.stdout | |
out.write(ralign(package)) | |
out.write(starter + '\033[1m ') | |
out.write(level) | |
out.write(' ') | |
out.write(tag) | |
out.write('\033[0m ') | |
out.write(text) | |
out.write('\n') | |
def main(): | |
import argparse | |
parser = argparse.ArgumentParser() | |
parser.add_argument('-p', '--plain', dest='plain', action='store_true') | |
parser.add_argument('-s', '--system', dest='system', action='store_true', | |
help='show system processes') | |
parser.add_argument('-i', '--info', dest='info', action='store_true', | |
help='show level minimum info') | |
parser.add_argument('packages', metavar='package', type=str, nargs='*', | |
help='packages to filter to') | |
parser.set_defaults(style=True) | |
args = parser.parse_args() | |
procmap = {} | |
waituntil = datetime.datetime.now() | |
p = subprocess.Popen(['adb', 'logcat'], stdin=subprocess.PIPE, stdout=subprocess.PIPE, close_fds=True) | |
for line in p.stdout: | |
line = to_string(line) | |
match = RE_LOGCAT.match(line) | |
if match: | |
level, tag, pid, text = match.groups() | |
else: | |
match = RE_LOGCAT_NEW.search(line) | |
if not match: | |
continue | |
pid, level, tag, text = match.groups() | |
pid = int(pid) | |
# pid not found, update procmap | |
if pid not in procmap: | |
now = datetime.datetime.now() | |
if now > waituntil: | |
procmap = package_pids() | |
waituntil = now + datetime.timedelta(seconds=20) | |
package = procmap.get(pid, '') | |
# optionally filter debug lines | |
if args.info: | |
if level == 'D' or level == 'V': | |
continue | |
# optionally filter system processes | |
if not args.system: | |
if not package or package.startswith('/') or not '.' in package: | |
continue | |
# optionally filter to desired packages | |
if len(args.packages): | |
for p in args.packages: | |
if p in package: | |
break | |
else: | |
continue | |
# render: pretty or not | |
if args.plain: | |
parts = [level, tag, str(pid), package, text] | |
print('\t'.join(parts)) | |
continue | |
render(level, tag, pid, package, text) | |
if __name__ == "__main__": | |
try: | |
main() | |
except KeyboardInterrupt: | |
pass # ok |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment