Skip to content

Instantly share code, notes, and snippets.

@Terrance
Last active April 23, 2025 22:05
Show Gist options
  • Save Terrance/2600d51594ec1672a2acb1f5530e88cd to your computer and use it in GitHub Desktop.
Save Terrance/2600d51594ec1672a2acb1f5530e88cd to your computer and use it in GitHub Desktop.
Script to parse Android logcat entries, show package names, highlight by level and mute noise.
#!/usr/bin/env python3
import argparse
from collections import defaultdict
from datetime import date, datetime
import os.path
from pathlib import Path
import re
import subprocess
import sys
LEVELS = {"V": 30, "I": 35, "W": 33, "E": 31}
NOISE_TAGS = set()
NOISE_PKGS = set()
NOISE_MSGS = set()
def style(text, *codes):
return "\33[{}m{}\33[0m".format(";".join(str(code) for code in codes), text)
def main(noise=False):
dumpsys = subprocess.run(("/system/bin/dumpsys", "package", "packages"), capture_output=True)
uids = defaultdict(list)
for line in dumpsys.stdout.decode().splitlines():
if line.startswith(" Package ["):
package = line.split("[")[1].split("]")[0]
elif line.startswith(" userId="):
uids[line.split("=", 1)[1]].append(package)
packages = {
uid: names[0] if len(names) == 1 else f"{sorted(names)[0]} +{len(names)}"
for uid, names in uids.items()
}
logcat = subprocess.Popen(("rish", "-c", "logcat -v uid"), stdin=subprocess.PIPE, stdout=subprocess.PIPE)
prev = today = date.today()
try:
while True:
line = logcat.stdout.readline()
if not line:
break
safe = line[:38].decode()
if not safe[0].isdigit():
sys.stdout.buffer.write(line)
continue
ts = datetime.strptime(safe[:18], "%m-%d %H:%M:%S.%f").replace(year=today.year)
if ts.day != prev.day:
prev = ts.date()
today = date.today()
time = ts.strftime("%H:%M:%S.%f")[:-3] if ts.day == today.day else ts.strftime("%d/%m %H:%M")
uid = safe[19:24].strip()
pkg = packages.get(uid, "")
pid = safe[25:30].strip()
tid = safe[31:36]
lvl = safe[37]
tag, msg = re.split(b" *: ?", line[39:-1], 1)
tag = tag.decode()
try:
msg = msg.decode()
except UnicodeDecodeError:
msg = repr(msg)[2:-1]
if tag in NOISE_TAGS or pkg in NOISE_PKGS or any(noise in msg for noise in NOISE_MSGS):
colour = 30
else:
colour = LEVELS.get(lvl, 0)
if not noise and colour == 30:
continue
print(style(f"{time} {uid:>5} {pkg:30.30} {pid:>5} {lvl} {tag:40.40} {msg}", colour))
except KeyboardInterrupt:
pass
if __name__ == "__main__":
parser = argparse.ArgumentParser()
parser.add_argument("-n", "--noise", action="store_true", help="include noise")
args = parser.parse_args()
main(args.noise)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment