Created
February 20, 2026 22:09
-
-
Save scramblr/66fca5c0539f5eeac4f5cd345132d3bf to your computer and use it in GitHub Desktop.
Android Studio Chat Log & Extension Log File Parser, Recovery, and Formatting
This file contains hidden or 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
| # This python script is designed to fix up and make all of the text inside of the "Chat" window in Android Studio human readable. | |
| # If you're ever in a situation where you need to find your Android Studio Gemini Chat history, or Claude, or whatever extension's | |
| # history that integrates with Android Studio's Chat function, this script will clean it up and usually make it 100x more readable. | |
| # | |
| # This script will produce two files when you run it: | |
| # 1. <original_filename>.pretty.log — This will have the same log structure, all embedded JSON, but expanded & prettified. | |
| # 2. <original_filename>.json — Reformats everything into a big JSON array. Each entry gets a timestamp, logger, | |
| # method, level, message, and nested json_data (when a JSON blob was already present) | |
| # | |
| # Sample Usage: | |
| # prompt> cd wherescriptshide | |
| # prompt> python android-studio-chatlogfix.py "C:/Users/user/AppData/Local/Google/AndroidStudio2025.3.1/log/gemini-backend.log" | |
| # Parsed 1227 entries. | |
| # Pretty log: C:\Users\user\AppData\Local\Google\AndroidStudio2025.3.1\log\gemini-backend.pretty.log | |
| # JSON: C:\Users\user\AppData\Local\Google\AndroidStudio2025.3.1\log\gemini-backend.json | |
| # | |
| # Android Studio Extensions Examples: | |
| # *** Typical Windows Location: %USERPROFILE%\AppData\Local\Google\AndroidStudio2025.3.1\log\ *** | |
| # | |
| # JetBrains AI / Gemini / Studio Bot logs | |
| # - gemini-backend.log | |
| # - ai-agent.log | |
| # - ai-conversation.log | |
| # - gemini-client.log | |
| # - assistant-events.log | |
| # - chat-service.log | |
| # | |
| # Kotlin LLM Code Completion / Code Insight Logs: | |
| # - ml-completion.log | |
| # - suggestion.log | |
| # - generation.log | |
| # | |
| # Google “Studio Bot” | |
| # - .../log/google-assistant/ | |
| # - .../log/ml/ | |
| # - .../log/studio-bot/ | |
| # | |
| # | |
| # Even if you don't see it listed above, changes are if it's a .log file in the Android Studio log directory, this script will help. | |
| # YMMV. | |
| # | |
| # Let me know if you have any issues and feel free to open Issues or just fix it and push/pull! | |
| # | |
| # Peace! | |
| # -scramblr | |
| # | |
| # format_log.py | |
| import re | |
| import json | |
| import sys | |
| from pathlib import Path | |
| def _find_json_blob(text: str): | |
| """Find the start and end index of the first top-level JSON object in text.""" | |
| start = text.find('{') | |
| if start == -1: | |
| return None | |
| depth = 0 | |
| in_string = False | |
| escape_next = False | |
| for i in range(start, len(text)): | |
| c = text[i] | |
| if escape_next: | |
| escape_next = False | |
| continue | |
| if c == '\\' and in_string: | |
| escape_next = True | |
| continue | |
| if c == '"': | |
| in_string = not in_string | |
| continue | |
| if in_string: | |
| continue | |
| if c == '{': | |
| depth += 1 | |
| elif c == '}': | |
| depth -= 1 | |
| if depth == 0: | |
| return start, i + 1 | |
| return None | |
| def prettify_json_in_text(text: str) -> str: | |
| """Find the first JSON object in text and replace it with pretty-printed version.""" | |
| span = _find_json_blob(text) | |
| if span is None: | |
| return text | |
| start, end = span | |
| raw = text[start:end] | |
| try: | |
| obj = json.loads(raw) | |
| pretty = json.dumps(obj, indent=2) | |
| return text[:start] + pretty + text[end:] | |
| except json.JSONDecodeError: | |
| return text | |
| # Header: "Feb 18, 2026 6:21:11 PM com.example.MyClass myMethod" | |
| _HEADER_RE = re.compile( | |
| r'^(?P<timestamp>\w+ \d+, \d+ \d+:\d+:\d+ [AP]M)' | |
| r'\s+(?P<logger>\S+)\s+(?P<method>\S+)\s*$', | |
| re.MULTILINE | |
| ) | |
| def parse_log_entries(text: str) -> list[dict]: | |
| """Parse a Java util.logging log into a list of entry dicts.""" | |
| matches = list(_HEADER_RE.finditer(text)) | |
| entries = [] | |
| for i, m in enumerate(matches): | |
| header_end = m.end() | |
| body_end = matches[i + 1].start() if i + 1 < len(matches) else len(text) | |
| body = text[header_end:body_end].strip() | |
| # Split level from message: body starts with "INFO: ..." or similar | |
| level = "UNKNOWN" | |
| message = body | |
| level_match = re.match(r'^([A-Z]+):\s*(.*)', body, re.DOTALL) | |
| if level_match: | |
| level = level_match.group(1) | |
| message = level_match.group(2).strip() | |
| logger = m.group("logger") # keep full qualified name | |
| entry = { | |
| "timestamp": m.group("timestamp"), | |
| "logger": logger, | |
| "method": m.group("method"), | |
| "level": level, | |
| "message": message, | |
| } | |
| span = _find_json_blob(message) | |
| if span is not None: | |
| raw = message[span[0]:span[1]] | |
| try: | |
| entry["json_data"] = json.loads(raw) | |
| except json.JSONDecodeError: | |
| pass | |
| entries.append(entry) | |
| return entries | |
| def format_pretty_log(entries: list[dict]) -> str: | |
| """Reconstruct the log as a human-readable string with JSON prettified.""" | |
| lines = [] | |
| for e in entries: | |
| lines.append(f"{e['timestamp']} {e['logger']} {e['method']}") | |
| pretty_msg = prettify_json_in_text(e["message"]) | |
| lines.append(f"{e['level']}: {pretty_msg}") | |
| lines.append("") # blank line between entries | |
| return "\n".join(lines) | |
| def format_structured_json(entries: list[dict]) -> str: | |
| """Serialize log entries as a pretty-printed JSON array.""" | |
| return json.dumps(entries, indent=2, ensure_ascii=False) | |
| if __name__ == "__main__": | |
| if len(sys.argv) < 2: | |
| print("Usage: python format_log.py <log_file>") | |
| sys.exit(1) | |
| input_path = Path(sys.argv[1]) | |
| if not input_path.exists(): | |
| print(f"Error: file not found: {input_path}", file=sys.stderr) | |
| sys.exit(1) | |
| text = input_path.read_text(encoding="utf-8", errors="replace") | |
| entries = parse_log_entries(text) | |
| if not entries: | |
| print("Warning: no log entries found.", file=sys.stderr) | |
| stem = input_path.stem | |
| parent = input_path.parent | |
| pretty_path = parent / f"{stem}.pretty.log" | |
| json_path = parent / f"{stem}.json" | |
| pretty_path.write_text(format_pretty_log(entries), encoding="utf-8") | |
| json_path.write_text(format_structured_json(entries), encoding="utf-8") | |
| print(f"Parsed {len(entries)} entries.") | |
| print(f" Pretty log: {pretty_path}") | |
| print(f" JSON: {json_path}") |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment