Skip to content

Commit 8ae93e8

Browse files
committed
WS6-3
codetracer-python-recorder/README.md: codetracer-python-recorder/codetracer_python_recorder/cli.py: codetracer-python-recorder/codetracer_python_recorder/session.py: codetracer-python-recorder/tests/python/unit/test_auto_start.py: codetracer-python-recorder/tests/python/unit/test_cli.py: codetracer-python-recorder/tests/python/unit/test_session_helpers.py: design-docs/US0028 - Configurable Python trace filters.md: design-docs/adr/0009-configurable-trace-filters.md: design-docs/adr/0010-codetracer-python-recorder-benchmarking.md: design-docs/codetracer-python-benchmarking-implementation-plan.md: design-docs/configurable-trace-filters-implementation-plan.md: design-docs/configurable-trace-filters-implementation-plan.status.md: design-docs/py-api-001.md: docs/onboarding/trace-filters.md: nix/flake.nix: Signed-off-by: Tzanko Matev <[email protected]>
1 parent 0cfb9f4 commit 8ae93e8

15 files changed

+188
-28
lines changed

codetracer-python-recorder/README.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -40,12 +40,12 @@ python -m codetracer_python_recorder \
4040
integration with the DB backend importer.
4141
- `--activation-path` – optional gate that postpones tracing until the interpreter
4242
executes this file (defaults to the target script).
43-
- `--trace-filter` – path to a filter file. Provide multiple times or use `//`
43+
- `--trace-filter` – path to a filter file. Provide multiple times or use `::`
4444
separators within a single argument to build a chain. When present, the recorder
4545
prepends the project default `.codetracer/trace-filter.toml` (if found near the
4646
target script) so later entries override the defaults. The
47-
`CODETRACER_TRACE_FILTER` environment variable accepts the same syntax when using
48-
the auto-start hook.
47+
`CODETRACER_TRACE_FILTER` environment variable accepts the same `::`-separated
48+
syntax when using the auto-start hook.
4949

5050
All additional arguments are forwarded to the target script unchanged. The CLI
5151
reuses whichever interpreter launches it so wrappers such as `uv run`, `pipx`,
@@ -54,7 +54,7 @@ or activated virtual environments behave identically to `python script.py`.
5454
## Trace filter configuration
5555
- Filter files are TOML with `[meta]`, `[scope]`, and `[[scope.rules]]` tables. Rules evaluate in declaration order and can tweak both execution (`exec`) and value decisions (`value_default`).
5656
- Supported selector domains: `pkg`, `file`, `obj` for scopes; `local`, `global`, `arg`, `ret`, `attr` for value policies. Match types default to `glob` and also accept `regex` or `literal` (e.g. `local:regex:^(metric|masked)_\w+$`).
57-
- Default discovery: `.codetracer/trace-filter.toml` next to the traced script. Chain additional files via CLI (`--trace-filter path_a --trace-filter path_b`), environment variable (`CODETRACER_TRACE_FILTER=path_a//path_b`), or Python helpers (`trace(..., trace_filter=[path_a, path_b])`). Later entries override earlier ones when selectors overlap.
57+
- Default discovery: `.codetracer/trace-filter.toml` next to the traced script. Chain additional files via CLI (`--trace-filter path_a --trace-filter path_b`), environment variable (`CODETRACER_TRACE_FILTER=path_a::path_b`), or Python helpers (`trace(..., trace_filter=[path_a, path_b])`). Later entries override earlier ones when selectors overlap.
5858
- Runtime metadata captures the active chain under `trace_metadata.json -> trace_filter`, including per-kind redaction counters. See `docs/onboarding/trace-filters.md` for the full DSL reference and examples.
5959

6060
Example snippet:

codetracer-python-recorder/codetracer_python_recorder/cli.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ def _parse_args(argv: Sequence[str]) -> RecorderCLIConfig:
7171
action="append",
7272
help=(
7373
"Path to a trace filter file. Provide multiple times to chain filters; "
74-
"specify multiple paths within a single argument using '//' separators. "
74+
"specify multiple paths within a single argument using '::' separators. "
7575
"Filters load after any project default '.codetracer/trace-filter.toml' so "
7676
"later entries override earlier ones; the CODETRACER_TRACE_FILTER "
7777
"environment variable accepts the same syntax for env auto-start."

codetracer-python-recorder/codetracer_python_recorder/session.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@ def start(
7676
the referenced file.
7777
trace_filter:
7878
Optional filter specification. Accepts a path-like object, an iterable
79-
of path-like objects, or a string containing ``//``-separated paths.
79+
of path-like objects, or a string containing ``::``-separated paths.
8080
Paths are expanded to absolute locations and must exist.
8181
policy:
8282
Optional mapping of runtime policy overrides forwarded to
@@ -224,7 +224,7 @@ def _extract_filter_segments(
224224

225225

226226
def _split_filter_spec(value: str) -> list[str]:
227-
parts = [segment.strip() for segment in value.split("//")]
227+
parts = [segment.strip() for segment in value.split("::")]
228228
return [segment for segment in parts if segment]
229229

230230

codetracer-python-recorder/tests/python/unit/test_auto_start.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ def fake_stop_backend() -> None:
4343

4444
monkeypatch.setenv(auto_start.ENV_TRACE_PATH, str(trace_dir))
4545
monkeypatch.setenv(
46-
auto_start.ENV_TRACE_FILTER, f"{default_filter}//{override_filter}"
46+
auto_start.ENV_TRACE_FILTER, f"{default_filter}::{override_filter}"
4747
)
4848

4949
monkeypatch.setattr(session, "_start_backend", fake_start_backend)

codetracer-python-recorder/tests/python/unit/test_cli.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -132,12 +132,12 @@ def test_parse_args_collects_trace_filters(tmp_path: Path) -> None:
132132
"--trace-filter",
133133
str(filter_a),
134134
"--trace-filter",
135-
f"{filter_b}//{filter_a}",
135+
f"{filter_b}::{filter_a}",
136136
str(script),
137137
]
138138
)
139139

140-
assert config.trace_filter == (str(filter_a), f"{filter_b}//{filter_a}")
140+
assert config.trace_filter == (str(filter_a), f"{filter_b}::{filter_a}")
141141

142142

143143
def test_parse_args_enables_io_capture_fd_mirroring(tmp_path: Path) -> None:

codetracer-python-recorder/tests/python/unit/test_session_helpers.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -140,7 +140,7 @@ def test_normalize_trace_filter_expands_sequence(tmp_path: Path) -> None:
140140
overrides.write_text("# overrides\n", encoding="utf-8")
141141

142142
result = session._normalize_trace_filter(
143-
[default, f"{overrides}//{default}", overrides]
143+
[default, f"{overrides}::{default}", overrides]
144144
)
145145

146146
assert result == [

design-docs/US0028 - Configurable Python trace filters.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ As a **Python team lead**, I want **a powerful configuration language to filter
3030
- When I inspect a trace event
3131
- Then only the allowed variables are serialized, with excluded variables redacted
3232
- [ ] Scenario: Merge multiple filter files
33-
- Given I provide a base filter `filters/common.trace` and user-specific overrides `filters/local.trace` combined as `filters/common.trace//filters/local.trace`
33+
- Given I provide a base filter `filters/common.trace` and user-specific overrides `filters/local.trace` combined as `filters/common.trace::filters/local.trace`
3434
- When I launch the recorder
3535
- Then the merged configuration applies deterministic precedence and validation before tracing starts
3636
- [ ] Scenario: Default filter protects secrets
@@ -47,7 +47,7 @@ As a **Python team lead**, I want **a powerful configuration language to filter
4747
- **Value Capture Controls**: Within each scope rule, evaluate a top-down list of value patterns (locals, globals, arguments, return payloads, and optionally nested attributes). Patterns resolve to allow-or-deny decisions, with denied values redacted while preserving variable names to indicate omission.
4848
- **I/O Capture Toggle**: Expose a filter flag to enable/disable interceptors for stdout, stderr, stdin, and file descriptors, aligning with the concurrent IO capture effort.
4949
- **Configuration Source**: Filters live in a human-editable file (default path: `<project_root>/.codetracer/trace-filter.cfg`) and can be overridden via CLI/API parameters for alternate locations.
50-
- **Filter Composition**: Support chained composition using the `filter_a//filter_b` syntax, where later filters extend or override earlier ones with clear conflict resolution rules.
50+
- **Filter Composition**: Support chained composition using the `filter_a::filter_b` syntax, where later filters extend or override earlier ones with clear conflict resolution rules.
5151
- **Default Policy**: Ship a curated default filter that aggressively redacts common secrets (tokens, passwords, keys) and excludes sensitive system modules. This fallback activates when no project filter is found.
5252

5353
## Unified Scope Selector Format
@@ -308,7 +308,7 @@ The recorder validates filter files against the schema below. Keys not listed ar
308308
- `reason` *(string, optional)*: Document why the pattern exists.
309309

310310
### Composition Semantics
311-
- Filters may be combined via `filter_a//filter_b`. Evaluation walks the chain left → right; later filters override earlier ones when keys conflict.
311+
- Filters may be combined via `filter_a::filter_b`. Evaluation walks the chain left → right; later filters override earlier ones when keys conflict.
312312
- `inherit` defaults carry the value from the previous filter in the chain; if no prior value exists, validation fails with a descriptive error.
313313
- `scope.rules` arrays merge by appending, so rules contributed by later filters execute after earlier ones and can override them through ordered evaluation.
314314
- Nested `value_patterns` arrays also append, preserving the expectation that later entries refine or replace earlier decisions.

design-docs/adr/0009-configurable-trace-filters.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
We must let maintainers author deterministic filters that:
1818
- Enable or disable tracing for specific packages, files, or fully qualified code objects with glob/regex support.
1919
- Allow or redact captured values (locals, globals, arguments, return payloads) per scope while keeping variable names visible.
20-
- Compose multiple filter files (`baseline//overrides`) with predictable default inheritance.
20+
- Compose multiple filter files (`baseline::overrides`) with predictable default inheritance.
2121

2222
The solution has to load human-authored TOML, enforce schema validation, and add minimal overhead to the monitoring callbacks. Policy errors must surface as structured `RecorderError` instances.
2323

@@ -26,7 +26,7 @@ The solution has to load human-authored TOML, enforce schema validation, and add
2626
- Parse TOML using `serde` + `toml` with `deny_unknown_fields`.
2727
- Support the selector grammar `<kind> ":" [<match_type> ":"] <pattern>` for both scope rules (`pkg`, `file`, `obj`) and value patterns (`local`, `global`, `arg`, `ret`, `attr`).
2828
- Compile globs with `globset::GlobMatcher`, regexes with `regex::Regex`, and literals as exact byte comparisons. Keep compiled matchers alongside original text for diagnostics.
29-
- Resolve `inherit` defaults while chaining multiple files (split on `//`). Later files append to the ordered rule list; `value_patterns` are likewise appended.
29+
- Resolve `inherit` defaults while chaining multiple files (split on `::`). Later files append to the ordered rule list; `value_patterns` are likewise appended.
3030
2. **Expose filter loading at session bootstrap.**
3131
- Extend `TraceSessionBootstrap` to locate the default project filter (`<cwd>/.codetracer/trace-filter.toml` up the directory tree) and accept optional override specs from CLI, Python API, or env (`CODETRACER_TRACE_FILTER`).
3232
- Parse each provided file once per `start_tracing` call. Propagate `RecorderError` on IO or schema failures with context about the offending selector.
@@ -39,7 +39,7 @@ The solution has to load human-authored TOML, enforce schema validation, and add
3939
- Augment `capture_call_arguments`, `record_visible_scope`, and `record_return_value` to accept a `ValuePolicy`. Encode real values for `Allow` and emit a reusable redaction record (`ValueRecord::Error { msg: "<redacted>" }`) for `Deny`.
4040
- Preserve variable names even when redacted; mark redaction hits via diagnostics counters so we can surface them later.
4141
4. **Surface configuration from Python.**
42-
- Extend `codetracer_python_recorder.session.start` with a `trace_filter` keyword accepting a string or pathlike. Accept the same parameter on the CLI as `--trace-filter`, honouring `filter_a//filter_b` composition or repeated flags.
42+
- Extend `codetracer_python_recorder.session.start` with a `trace_filter` keyword accepting a string or pathlike. Accept the same parameter on the CLI as `--trace-filter`, honouring `filter_a::filter_b` composition or repeated flags.
4343
- Teach the auto-start helper to respect `CODETRACER_TRACE_FILTER` with the same semantics.
4444
- Provide `codetracer_python_recorder.codetracer_python_recorder.configure_trace_filter(path_spec: str | None)` to preload/clear filters for embedding scenarios.
4545
5. **Diagnostics and metadata.**
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
# 0010 – Codetracer Python Recorder Benchmarking
2+
3+
## Status
4+
Proposed – pending review and implementation sequencing (target: post-configurable-trace-filter release).
5+
6+
## Context
7+
- The Rust-backed `codetracer-python-recorder` now exposes configurable trace filters (WS1–WS6) and baseline micro/perf smoke benchmarks, but these are developer-only workflows with no CI visibility or historical tracking.
8+
- Performance regressions are difficult to detect: Criterion runs produce local reports, the Python smoke benchmark is opt-in, and CI currently exercises only functional correctness.
9+
- Product direction demands confidence that new features (filters, IO capture, PyO3 integration, policy changes) do not introduce unacceptable overhead or redaction slippage across representative workloads.
10+
- We require an auditable, automated benchmarking strategy that integrates with existing tooling (`just`, `uv`, Nix flake, GitHub Actions/Jenkins) and surfaces trends to the team without burdening release cadence.
11+
12+
## Decision
13+
We will build a first-class benchmarking suite for `codetracer-python-recorder` with three pillars:
14+
15+
1. **Deterministic harness coverage**
16+
- Preserve the existing Criterion microbench (`benches/trace_filter.rs`) and Python smoke benchmark, expanding them into a common `bench` workspace with reusable fixtures and scenario definitions (baseline, glob, regex, IO-heavy, auto-start).
17+
- Introduce additional Rust benches for runtime hot paths (scope resolution, redaction policy application, telemetry writes) under `codetracer-python-recorder/benches/`.
18+
- Add Python benchmarks (Pytest plugins + `pytest-benchmark` or custom timers) for end-to-end CLI runs, session API usage, and cross-process start/stop costs.
19+
20+
2. **Automated execution & artefacts**
21+
- Create a dedicated `just bench-all` (or extend `just bench`) command that orchestrates all benchmarks, produces structured JSON summaries (`target/perf/*.json`), and archives raw outputs (Criterion reports, flamegraphs when enabled).
22+
- Provide a stable JSON schema capturing metadata (git SHA, platform, interpreter versions), scenario descriptors, statistics (p50/p95/mean, variance), and thresholds.
23+
- Ship a lightweight renderer (`scripts/render_bench_report.py`) that compares current results against the latest baseline stored in CI artefacts.
24+
25+
3. **CI integration & historical tracking**
26+
- Add a continuous benchmark job (nightly and pull-request optional) that executes the suite inside the Nix shell (ensuring gnuplot/nodeps), uploads artefacts to GitHub Actions artefacts for long-term storage, and posts summary comments in PRs.
27+
- Maintain baseline snapshots in-repo (`codetracer-python-recorder/benchmarks/baselines/*.json`) refreshed on release branches after running on dedicated hardware.
28+
- Gate merges when regressions exceed configured tolerances (e.g., >5% slowdowns on primary scenarios) unless explicitly approved.
29+
30+
Supporting practices:
31+
- Store benchmark configuration alongside code (`benchconfig.toml`) to keep scenarios versioned and reviewable.
32+
- Ensure opt-in developer tooling (`just bench`) remains fast by allowing subset filters (e.g., `JUST_BENCH_SCENARIOS=filters,session`).
33+
34+
## Rationale
35+
- **Consistency:** Centralising definitions and outputs ensures that local runs and CI share identical workflows, reducing “works on my machine” drift.
36+
- **Observability:** Structured artefacts + historical storage let us graph trends, spot regressions early, and correlate with feature work.
37+
- **Scalability:** By codifying thresholds and baselines, we can expand the suite without rethinking CI each time (e.g., adding memory benchmarks).
38+
- **Maintainability:** Versioned configuration and scripts avoid ad-hoc shell pipelines and make it easy for contributors to extend benchmarks.
39+
40+
## Consequences
41+
Positive:
42+
- Faster detection of performance regressions and validation of expected improvements.
43+
- Shared language for performance goals (scenarios, metrics, thresholds) across Rust and Python components.
44+
- Developers gain confidence via `just bench` parity with CI, plus local comparison tooling.
45+
46+
Negative / Risks:
47+
- Running the full suite may increase CI time; we mitigate by scheduling nightly runs and allowing PR opt-in toggles.
48+
- Maintaining baselines requires disciplined updates whenever we intentionally change performance characteristics.
49+
- Additional scripts and artefacts introduce upkeep; we must document workflows and automate cleanup.
50+
51+
Mitigations:
52+
- Provide partial runs (`just bench --scenarios filters`, `pytest ... -k benchmark`) for quick iteration.
53+
- Automate baseline updates via a `scripts/update_bench_baseline.py` helper with reviewable diffs.
54+
- Document the suite in `docs/onboarding/trace-filters.md` (updated) and a new benchmarking guide.
55+
56+
## References
57+
- `codetracer-python-recorder/benches/trace_filter.rs` (current microbench harness).
58+
- `codetracer-python-recorder/tests/python/perf/test_trace_filter_perf.py` (Python smoke benchmark).
59+
- `Justfile` (`bench` recipe) and `nix/flake.nix` (dev shell dependencies, now including gnuplot).
60+
- Storage backend for historical data (settled: GitHub Actions artefacts).

0 commit comments

Comments
 (0)