Journald Analyst
Instructions
Step 1: Set Up the Environment First
Always initialize a local Python environment with uv before reading or transforming journald logs.
- Create the virtual environment:
uv venv source .venv/bin/activate - Install the required libraries:
uv pip install polars orjson - Create reusable scripts in the current working directory. Do not rely on ad hoc shell one-liners for repeatable analysis.
Step 2: Initial Discovery with orjson
Use orjson for fast line-by-line inspection and schema sampling before building larger polars workflows.
- Sample the data: Create a script such as
sample_journal.pyto inspect a few records.import orjson with open("journal.json", "rb") as f: for _ in range(5): line = f.readline() if not line: break print(orjson.dumps(orjson.loads(line), option=orjson.OPT_INDENT_2).decode()) - Count service identifiers: Create a script such as
count_services.pyto see which services are logging.import orjson from collections import Counter counts = Counter() with open("journal.json", "rb") as f: for line in f: event = orjson.loads(line) counts[event.get("SYSLOG_IDENTIFIER", "unknown")] += 1 for service, count in counts.most_common(): print(f"{count:7} {service}") - Consult references: Use
references/journald_format.mdfor field definitions andreferences/journald_security_research.mdfor Polars-based hunting patterns.
Step 3: Targeted Analysis with polars
- Check for existing Parquet files: Before scanning
journal.json, check for.parquetfiles. If they exist, usepolars.scan_parquet(). - Filter by Priority: Prioritize critical logs by filtering
PRIORITYvalues (e.g., "0" to "4"). - Use lazy scans for scale: Prefer
polars.scan_ndjson()for large log files. - Persist JSON data as Parquet: Materialize filtered datasets to Parquet early (e.g.,
df.sink_parquet("auth_logs.parquet")). - Document findings: Maintain an
analyst_log-YY-MM-DD_HH-MM.mdfile for every session.
Working Agreements
- Python environment: ALWAYS create a virtual environment with
uv venvand install dependencies withuv pip install polars orjson. - Tool re-use: ALWAYS search for and re-use existing scripts in the current directory.
- Data-First retrieval: ALWAYS check for and use existing
.parquetfiles. - Script retention: Always create and retain scripts such as
analyze_*.pyin the current project directory. NEVER delete generated helper scripts or analysis code. - Python style: Prefer
orjsonfor streaming JSON parsing andpolarsfor filtering and aggregations.
Examples
Example 1: Hunting for SSH Brute Force
User says: "Check for failed logins." Action:
- Filter to
SYSLOG_IDENTIFIER == "sshd"andmessagecontaining "Failed password". - Group by
hostor IP (if extracted from message) and count.
Example 2: Tracking Sudo Usage
User says: "Show me all sudo commands executed." Action:
- Filter to
SYSLOG_IDENTIFIER == "sudo". - Extract and display
timestamp,message, andhost.
Troubleshooting
Error: "Invalid JSON"
Cause: The log file might be truncated or contains non-JSON lines.
Solution: Use an orjson script with try-except blocks to skip malformed lines.
Error: "Missing fields"
Cause: Not all journald entries contain the same fields (e.g., SYSLOG_IDENTIFIER might be missing).
Solution: Use pl.col("field").fill_null("unknown") or filter for existence.