Skip to content

feat(verbosity): unify rust + python worker logging via TRYKE_LOG#94

Merged
thejchap merged 3 commits intomainfrom
claude/unify-verbosity-logging
May 3, 2026
Merged

feat(verbosity): unify rust + python worker logging via TRYKE_LOG#94
thejchap merged 3 commits intomainfrom
claude/unify-verbosity-logging

Conversation

@thejchap
Copy link
Copy Markdown
Owner

@thejchap thejchap commented May 2, 2026

Context

Verbosity in tryke today is fragmented across four independent knobs that don't talk to each other: -v/-q flags drive rust logging, RUST_LOG is silently dropped, TRYKE_WORKER_LOG configures python workers but is undocumented and not auto-set, and a Verbosity reporter enum is derived from log level via an awkward hand-rolled match.

In practice that means:

  • tryke -v test lights up rust logs but not python worker logs. Diagnosing a flaky worker requires knowing about the undocumented TRYKE_WORKER_LOG env var and setting it manually.
  • The VS Code extension's RUST_LOG=tryke=<level> (issue fix(logging): honor RUST_LOG so the VS Code server output isn't empty #91) was being dropped entirely because env_logger::Builder::new() ignores RUST_LOG.

Approach

Adopt a single user-facing knob (-v/-q flags or TRYKE_LOG env) that propagates to both the rust process and every python worker it spawns. RUST_LOG stays as a power-user override for the rust side only — its per-module filter syntax (tryke=info,hyper=warn) doesn't map onto a python log level, so it deliberately doesn't propagate to workers. This mirrors uv's ""RUST_LOG forwarded to PEP 517 build backends"" model and avoids pytest-xdist's well-known footgun where worker verbosity diverges from the parent.

Precedence

Rust log filter (consumed by env_logger):

  1. RUST_LOG (wins natively via Builder::from_env).
  2. TRYKE_LOG.
  3. -v/-q flag.
  4. Default warn.

Python worker log (set as TRYKE_LOG on the worker's env):

  1. TRYKE_LOG if set.
  2. CLI flag, only when explicitly more verbose than warn (i.e., the user passed at least one -v).
  3. Otherwise off — preserves the long-standing ""no chatter unless asked"" default.

Concrete changes

  • New resolvers tryke_config::rust_log_default + worker_log_level mirroring resolve_python's shape (8 unit tests covering the precedence chain).
  • WorkerPool::new / with_python_path / WorkerProcess::spawn take a log::LevelFilter; the spawn Command sets TRYKE_LOG on the child env when level > Off. Backwards-incompatible for direct callers — the user OK'd this.
  • tryke/main.rs resolves the rust default via the new helper and feeds env_logger::Builder::from_env(default_filter_or(...)) so RUST_LOG wins natively. This subsumes fix(logging): honor RUST_LOG so the VS Code server output isn't empty #91, which can be closed when this lands.
  • Verbosity::from_level_filter replaces the hand-rolled log::Level match (which collapsed three log levels into one reporter level).
  • python/tryke/worker.py:_configure_logging_from_env reads TRYKE_LOG first; TRYKE_WORKER_LOG is retained as a deprecated alias for one cycle with a one-time deprecation log line.
  • docs/guides/configuration.md documents the full precedence chain end-to-end with examples.
  • CONTRIBUTING.md: cargo run -- test (delayed copilot follow-up from refactor(tests): extract test_python_bin into tryke_testing #93).

Doesn't do

  • Doesn't decouple reporter Verbosity from log level. That's pytest's model (-v = UI detail, --log-level = diagnostic filter) and is the right end-state, but it's a separate UX change worth its own PR.
  • Doesn't add a custom python TRACE level. Mapping TRACEDEBUG (the current behavior) is fine.

Test plan

Unit:

  • tryke_config precedence-chain tests for rust_log_default and worker_log_level (env wins, CLI fallback, garbage values, default behavior).
  • tryke_runner::worker::worker_log_env_value returns None for Off and lowercase level names otherwise.

Integration:

  • All 611 existing tests pass with the additional LevelFilter::Off argument threaded through.

End-to-end (manual):

tryke test                                     # rust at warn, workers off
tryke -v test                                  # rust + workers both at info
RUST_LOG=tryke=debug tryke test                # rust debug, workers off
TRYKE_LOG=info tryke test                      # both at info
TRYKE_LOG=info RUST_LOG=tryke=warn tryke test  # rust warn, workers info
TRYKE_WORKER_LOG=info tryke test               # workers info + deprecation warning

Related

The previous design had four independent verbosity knobs that didn't
talk to each other:

  - `-v`/`-q` flags drove rust `log` filter only
  - `RUST_LOG` was ignored entirely (env_logger::Builder::new())
  - `TRYKE_WORKER_LOG` configured python workers, undocumented and
    not auto-set
  - the `Verbosity` reporter enum was derived from log level via
    a hand-rolled match that collapsed three log levels into one

Concretely, a user running `tryke -v test` to debug a flaky worker
saw rust info logs but no python logs — they had to know about the
undocumented `TRYKE_WORKER_LOG` env var and set it manually. The
VS Code extension's `RUST_LOG=tryke=info` was being dropped on the
floor entirely, so the "Tryke Server" output panel was empty.

This change introduces a single user-facing knob (`-v`/`-q` flags or
`TRYKE_LOG` env) that propagates to both the rust process and every
python worker spawned. Mirrors uv's "RUST_LOG forwarded to PEP 517
build backends" model and avoids pytest-xdist's well-known footgun
where worker verbosity diverges from the parent.

Resolution chain (rust):
  RUST_LOG > TRYKE_LOG > -v/-q > warn

Resolution chain (python worker):
  TRYKE_LOG > -v/-q (only if more verbose than warn) > off

`RUST_LOG` is honored as a power-user override for the rust side
(its per-module filter syntax doesn't map onto a python log level,
so it deliberately doesn't propagate to workers).

Concrete changes:

  - `tryke_config::rust_log_default` and `worker_log_level` resolvers
    mirror `resolve_python`'s shape; tested across the precedence chain
  - `WorkerPool::new` / `with_python_path` / `WorkerProcess::spawn`
    take a `log::LevelFilter`; the spawn `Command` sets `TRYKE_LOG`
    on the child env when level > Off
  - `tryke/main.rs` resolves the rust default via the new helper and
    feeds `env_logger::Builder::from_env(default_filter_or(...))` so
    `RUST_LOG` wins natively (subsumes #91)
  - `Verbosity::from_level_filter` replaces the awkward log::Level
    match in main.rs with a single helper on the reporter side
  - `TRYKE_WORKER_LOG` is retained as a deprecated alias for one
    cycle with a one-time deprecation log line
  - configuration guide documents the precedence chain end-to-end
  - CONTRIBUTING.md: `cargo run -- test` (delayed copilot follow-up
    from #93)

This is backwards-incompatible for direct callers of `WorkerPool` /
`WorkerProcess::spawn` — they all gain a `LevelFilter` parameter.
Most consumers already pass `LevelFilter::Off` (test helpers). The
external CLI surface is unchanged.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings May 2, 2026 23:19
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR unifies Tryke's logging configuration so a single user-facing verbosity setting can drive both Rust-side logging and spawned Python worker logging, while still preserving RUST_LOG as a Rust-only power-user override. It also threads the resolved worker log level through the runner/server/watch paths and documents the new precedence rules.

Changes:

  • Added config helpers to resolve Rust default logging and Python worker log propagation from TRYKE_LOG and CLI verbosity.
  • Threaded LevelFilter through worker pool, test execution, watch mode, and server startup so spawned workers can receive TRYKE_LOG.
  • Updated worker/docs/reporter plumbing to support the new logging model and documented the precedence chain.

Reviewed changes

Copilot reviewed 15 out of 16 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
python/tryke/worker.py Reads TRYKE_LOG first, keeps TRYKE_WORKER_LOG as a deprecated alias, and configures worker-side logging.
docs/guides/configuration.md Documents the new logging model, precedence, and examples.
crates/tryke_server/src/server.rs Threads resolved worker log level into server-created worker pools.
crates/tryke_server/src/handler.rs Updates tests/helpers for the new WorkerPool::new signature.
crates/tryke_runner/src/worker.rs Adds child env propagation of TRYKE_LOG and tests the env-value mapping helper.
crates/tryke_runner/src/pool.rs Propagates log level through pool lifecycle and worker spawning paths.
crates/tryke_reporter/src/text.rs Adds Verbosity::from_level_filter to centralize reporter verbosity mapping.
crates/tryke_reporter/Cargo.toml Adds log dependency needed for reporter verbosity mapping.
crates/tryke_config/src/lib.rs Introduces rust_log_default and worker_log_level helpers plus unit tests.
crates/tryke_config/Cargo.toml Adds log dependency for config-level log filter parsing.
crates/tryke/src/watch.rs Passes resolved worker log level into watch-mode worker pools.
crates/tryke/src/main.rs Resolves logging precedence, initializes env_logger from env/defaults, and threads worker logging through commands.
crates/tryke/src/execution.rs Passes resolved worker log level into test execution worker pools.
Cargo.lock Records new crate dependencies.
CONTRIBUTING.md Updates cargo invocation example.
AGENTS.md Documents the new repository logging contract.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread crates/tryke/src/main.rs Outdated
// verbosity than the `Warn` default.
let worker_log = tryke_config::worker_log_level(tryke_log.as_deref(), cli_filter);

let verbosity = Verbosity::from_level_filter(cli_filter);
Comment thread python/tryke/worker.py Outdated
Comment on lines +889 to +893
if use_legacy:
_log.warning(
"TRYKE_WORKER_LOG is deprecated; use TRYKE_LOG instead "
"(it propagates to both rust and python workers)"
)
Comment thread python/tryke/worker.py Outdated
Comment on lines +858 to +859
``-v`` / ``-q`` or the ``TRYKE_LOG`` env var asks for cross-language
verbosity, so users typically don't set this directly.
Comment thread CONTRIBUTING.md
Three real findings from copilot review on the redesign:

- Reporter `Verbosity` derived from raw CLI filter, not the resolved
  cross-language level — so `TRYKE_LOG=info tryke test` would light up
  logs but leave the text reporter in `Normal` mode, contradicting the
  documented "single knob" promise. Use the resolved `rust_default`
  (which already incorporates `TRYKE_LOG`) as the source. `RUST_LOG`
  is deliberately not consulted here: it's a rust-internal filter, not
  a user-facing UI knob.

- The python worker emitted the `TRYKE_WORKER_LOG` deprecation
  warning per worker process, so multi-worker pools and watch-mode
  restarts spammed it N times. Move the alias resolution + warning
  to the rust side so it fires exactly once per `tryke` invocation,
  regardless of pool size or restart count. Python still honors the
  legacy env for the standalone-worker case but no longer warns.

- Worker docstring claimed `-q` triggered cross-language verbosity;
  the resolver only propagates explicit `-v` (more verbose than the
  `warn` default). Fix the docstring.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Copy link
Copy Markdown
Owner Author

@thejchap thejchap left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Folded copilot feedback into 69a2a0b: reporter Verbosity now follows the resolved cross-language level (so TRYKE_LOG=info lifts the text reporter to Verbose), TRYKE_WORKER_LOG deprecation warning moved to the rust side so it fires once per invocation rather than once per worker, and the python docstring no longer claims -q propagates.

Declining the CONTRIBUTING.md lowercase-verb nit — the surrounding prose in that file is uniformly lowercase (run, we use, install it once, etc.), so capitalizing only line 31 would be inconsistent. Worth a separate sweep if the project wants to adopt the AGENTS.md capitalization rule for docs.

Backwards compat isn't a goal here — `TRYKE_LOG` is the only env var.
Removes the rust-side legacy lookup + deprecation warning, the
python-side fallback, and the docs paragraph describing the alias.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings May 2, 2026 23:54
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 15 out of 16 changed files in this pull request and generated 1 comment.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread python/tryke/worker.py
on stdout.
"""
level_name = os.environ.get("TRYKE_WORKER_LOG", "").strip().upper()
level_name = os.environ.get("TRYKE_LOG", "").strip().upper()
@thejchap thejchap merged commit ec7b1a8 into main May 3, 2026
27 checks passed
@thejchap thejchap deleted the claude/unify-verbosity-logging branch May 3, 2026 00:56
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

requires-python with lower and upper limits

2 participants