X Tutup
Skip to content

feat(logging): structured logging, colorama removal, OutputFormatter#1017

Merged
tony merged 15 commits intomasterfrom
logging
Mar 8, 2026
Merged

feat(logging): structured logging, colorama removal, OutputFormatter#1017
tony merged 15 commits intomasterfrom
logging

Conversation

@tony
Copy link
Member

@tony tony commented Mar 7, 2026

Summary

  • Add structured logging with extra context (tmux_session, tmux_window, tmux_pane, tmux_config_path) across all workspace, CLI, and utility modules
  • Add TmuxpLoggerAdapter for persistent identity context on session/window/pane objects, portable across Python 3.10–3.13+
  • Add NullHandler in library __init__.py and logging.getLogger(__name__) declarations in all source modules
  • Remove colorama runtime and type-stub dependencies; replace with stdlib ANSI constants
  • Add OutputFormatter.emit_object() for single-object JSON output; route ls --json and debug-info --json through it
  • Add Colors.format_rule() with Unicode box-drawing characters
  • Route all raw print() calls through tmuxp_echo() for consistent output channels
  • Fix default CLI log level from INFO to WARNING so normal usage is not noisy
  • Fix get_pane() exception catch type to match sibling methods

Changes by area

Logging infrastructure (log.py)

  • TmuxpLoggerAdapter: LoggerAdapter subclass that merges extra dicts portably (manual process() override for Python < 3.13)
  • setup_logger(): Defaults to "tmuxp" logger (not root), skips NullHandler when checking for existing handlers, selects DebugLogFormatter at DEBUG level
  • tmuxp_echo(): Adds stacklevel=2 for accurate caller attribution

Workspace modules

  • builder.py: Session/window/pane lifecycle logging via TmuxpLoggerAdapter
  • finders.py: Structured DEBUG for local file search, WARNING for ambiguous multi-file directories
  • freezer.py, importers.py, loader.py, validation.py: DEBUG logging with session/config context

CLI and output

  • _output.py: OutputFormatter.emit_object() for JSON-mode commands
  • cli/__init__.py: Fix setup_logger call site, route print() through tmuxp_echo()
  • load.py: Add missing log_level to CLILoadNamespace, fix log-file handler setup
  • ls.py, debug_info.py: Route --json through OutputFormatter

Dependencies

  • pyproject.toml / uv.lock: Remove colorama and colorama-stubs

Design decisions

  • Portable LoggerAdapter over merge_extra=True: Python 3.13 added merge_extra to LoggerAdapter.__init__, but tmuxp supports 3.10+, so process() manually merges — forward-compatible with the newer API
  • setup_logger defaults to "tmuxp" not root: Prevents handler setup from affecting unrelated libraries when tmuxp is the application entry point
  • Separate logger.warning + tmuxp_echo for ambiguity warnings: Structured log record goes to aggregators; tmuxp_echo output goes to the terminal for user visibility

Stacking

This PR is the base for:

Verification

Verify no print() calls remain in util.py:

$ rg 'print\(' src/tmuxp/util.py

Verify all source modules have logger declarations:

$ rg -L 'logger = logging.getLogger' src/tmuxp/ --glob '*.py' --files-without-match

Verify no f-strings in log calls:

$ rg 'logger\.\w+\(f"' src/tmuxp/

Test plan

  • test_builder_logs_session_created — verifies INFO record with tmux_session extra
  • test_builder_logs_window_and_pane_creation — verifies DEBUG records with tmux_window/tmux_pane extra
  • test_find_workspace_file_logs_warning_on_multiple — verifies WARNING on ambiguous workspace dirs
  • test_freeze_logs_debug — verifies freeze session/window DEBUG records
  • test_import_teamocil_logs_debug / test_import_tmuxinator_logs_debug — verifies import DEBUG with session name
  • test_get_pane_logs_debug_on_failure — verifies structured DEBUG on pane lookup failure
  • test_oh_my_zsh_auto_title_logs_warning — verifies WARNING when DISABLE_AUTO_TITLE unset
  • uv run ruff check . — clean
  • uv run mypy — clean
  • uv run py.test -x — all pass

@tony

This comment has been minimized.

@tony tony force-pushed the logging branch 3 times, most recently from 4a55334 to 56173ab Compare March 8, 2026 13:21
@codecov

This comment has been minimized.

@tony tony force-pushed the logging branch 3 times, most recently from 25ea629 to 7da5d3a Compare March 8, 2026 16:16
@tony

This comment has been minimized.

@tony

This comment has been minimized.

@tony

This comment has been minimized.

tony added 5 commits March 8, 2026 14:42
…l modules

why: Enable structured logging with `extra` context for filtering, testing,
and aggregation. Library modules need NullHandler per Python best practices.
what:
- Add `logging.getLogger(__name__)` to every module
- Add NullHandler in library `__init__.py`
- Add TmuxpLoggerAdapter with process() override for Python <3.13 compat
- Simplify tmuxp_echo to pure print wrapper (decoupled from logging)
- Add setup_log_file() for centralized --log-file handler setup
- Fix setup_logger to target "tmuxp" logger, skip NullHandler check
- Change default CLI log level from INFO to WARNING
- Fix timestamp format bug: %H:%m:%S -> %H:%M:%S
- Add future annotations and fix import ordering in _compat.py
…oss all modules

why: Structured `extra` keys (tmux_session, tmux_window, tmux_pane,
tmux_config_path) enable filtering, aggregation, and test assertions
on log records rather than string matching.
what:
- Add structured DEBUG/INFO/WARNING/ERROR log calls to workspace, CLI,
  and utility modules with appropriate extra keys
- Route all raw print() calls through tmuxp_echo() in CLI commands
- Fix get_pane() exception catch type to match sibling methods
- Change before_script failure log from DEBUG to ERROR
- Remove catch-log-reraise in plugin version check
why: Verify structured extra keys on log records using caplog.records,
per AGENTS.md guidelines — assert on attributes, not string matching.
what:
- Add caplog tests for builder, freezer, finders, loader, validation,
  importers, and plugin version_check
- Add log-level filtering test for --log-file
- Add ANSI-free assertions to JSON/NDJSON output tests (ls, search)
- Add non-TTY stderr ANSI-free test for load command
why: colorama wraps fixed ANSI escape string constants the stdlib can
provide directly. Removing it shrinks the dependency tree.
what:
- Replace all colorama Fore/Style references in log.py with raw ANSI
  escapes via _ansi_colors from tmuxp._internal.colors
- Remove colorama and types-colorama from pyproject.toml
….format_rule

why: ls and debug-info bypassed OutputFormatter with raw sys.stdout.write,
breaking the 2-channel output architecture for machine-readable output.
what:
- Add OutputFormatter.emit_object() for single top-level JSON objects
- Route ls --json/--ndjson and debug-info --json through emit_object()
- Add Colors.format_rule() for Unicode box-drawing horizontal rules
- Add unit tests for emit_object in JSON, NDJSON, and HUMAN modes
tony added 2 commits March 8, 2026 15:41
why: CLAUDE.md requires all functions and methods to have working doctests.
what:
- Add Examples section to TmuxpLoggerAdapter demonstrating extra merging
why: Developers need guidance on the 2-channel output architecture
(logger for diagnostics, tmuxp_echo/OutputFormatter for user output).
what:
- Add "Output channels" section with diagnostics vs user-facing rules
- Document print() prohibition in command/business logic
- Expand "Avoid" entry for print() with structured extra guidance
@tony tony changed the title py(logging): Add structured logging with extra context across all modules feat(logging): structured logging, colorama removal, OutputFormatter Mar 8, 2026
@tony
Copy link
Member Author

tony commented Mar 8, 2026

Code review

No issues found. Checked for bugs and CLAUDE.md compliance.

🤖 Generated with Claude Code

- If this code review was useful, please react with 👍. Otherwise, react with 👎.

tony added 6 commits March 8, 2026 16:55
why: PR 1 needs changelog entries for the logging work.
what:
- Add bug fixes: CLI log level, get_pane() exception, OutputFormatter routing
- Add development section: structured logging, colorama removal, print→tmuxp_echo
why: logger.exception() dumps tracebacks at ERROR level (visible at
default WARNING) alongside tmuxp_echo() user-friendly messages, causing
users to see double output.
what:
- Change plugin load failed from exception to debug with exc_info
- Change plugin import failed from exception to debug with exc_info
- Change workspace build failed from exception to debug with exc_info
why: The structured logging migration removed the user-visible [Loading]
message that shows which workspace file is being loaded.
what:
- Add tmuxp_echo with [Loading] and privacy-masked workspace path
- Uses PrivatePath (already imported) and cli_colors (already available)
why: Inlining tmux stdout in the log message string defeats structured
log aggregation and filtering.
what:
- Move display-message output from format arg to extra tmux_stdout key
why: All functions must have working doctests per project conventions.
what:
- Add Examples section with string and integer pass-through tests
why: The bare error message loses the script path, making it harder to
diagnose which before_script failed in structured log systems.
what:
- Add tmux_config_path extra with the before_script path to error log
tony added 2 commits March 8, 2026 16:55
why: The raise...from e chain on the next lines already preserves the
exception traceback; logging it too is redundant per project standards.
what:
- Remove exc_info=True from pane lookup debug log before reraise
why: The assertion checked for "Loading" or "Loaded" but the restored
user-facing message now uses "[Loading]" format.
what:
- Update assertion to match the restored [Loading] message format
@tony tony merged commit 13906cc into master Mar 8, 2026
13 checks passed
@tony tony deleted the logging branch March 8, 2026 22:00
tony added a commit that referenced this pull request Mar 8, 2026
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.

1 participant

X Tutup