Fix `add_log_level()` re-registration value drift
The bound `StackLevelAdapter.<name>` emit method captured its level via a `_level: int = value` default-arg at bind time, so a later `add_log_level(name, <new-value>)` re-registration left the method emitting at the *stale* original level. Read `CUSTOM_LEVELS[name_up]` at call time instead — the method binds once but always tracks the current registered value. Also, - correct the `add_log_level()` docstring's "Idempotent" note to describe the call-time value lookup (was the misleading "no-op-ish refresh (won't clobber an already-bound method)"). - sync stale `_reap.py` doc/comment markers `tractor[` -> `_subactor[` to match the actual `_proctitle._def_prefix` proc-title prefix (doc-only drift; the code markers already referenced `_proctitle._def_prefix`). Review: PR #467 (copilot-pull-request-reviewer) https://github.com/goodboy/tractor/pull/467#pullrequestreview-4562945515 (this patch was generated in some part by [`claude-code`][claude-code-gh]) [claude-code-gh]: https://github.com/anthropics/claude-codecustom_log_levels_api
parent
3727ce9e6f
commit
88cc68edd3
|
|
@ -216,18 +216,18 @@ def _read_comm(pid: int) -> str:
|
|||
# regardless of cwd / venv path / launch context. Used by
|
||||
# `_is_tractor_subactor()` below.
|
||||
#
|
||||
# - cmdline `tractor[`: matches the `setproctitle`-set form
|
||||
# (`tractor[<aid.reprol()>]`) — set in
|
||||
# `_actor_child_main` for ALL backends, mutates argv via
|
||||
# libc so visible in `/proc/<pid>/cmdline`.
|
||||
# - cmdline `_subactor[` (`_proctitle._def_prefix`): matches
|
||||
# the `setproctitle`-set form (`_subactor[<aid.reprol()>]`)
|
||||
# — set in `_actor_child_main` for ALL backends, mutates
|
||||
# argv via libc so visible in `/proc/<pid>/cmdline`.
|
||||
# - cmdline `tractor._child`: matches the legacy
|
||||
# `python -m tractor._child --uid (...)` form. Catches
|
||||
# procs that died before `_actor_child_main` got to call
|
||||
# `setproctitle()` — argv from exec is still kernel-
|
||||
# visible at that point.
|
||||
# - comm `tractor[`: same proctitle-set form, but visible
|
||||
# - comm `_subactor[`: same proctitle-set form, but visible
|
||||
# via `/proc/<pid>/comm` (kernel-truncated to ~15 bytes,
|
||||
# `tractor[doggy:`). Critical for ZOMBIES — kernel
|
||||
# `_subactor[doggy:`). Critical for ZOMBIES — kernel
|
||||
# preserves `comm` past task-exit until parent reaps,
|
||||
# while `cmdline` for zombies often reads as empty.
|
||||
_TRACTOR_PROC_CMDLINE_MARKERS: tuple[str, ...] = (
|
||||
|
|
@ -253,7 +253,7 @@ def _is_tractor_subactor(pid: int) -> bool:
|
|||
|
||||
'''
|
||||
# 1. cmdline match — catches both `setproctitle`-set
|
||||
# `tractor[<aid>]` (live) AND legacy `python -m
|
||||
# `_subactor[<aid>]` (live) AND legacy `python -m
|
||||
# tractor._child` (any) form.
|
||||
cmdline: str = _read_cmdline(pid)
|
||||
if any(m in cmdline for m in _TRACTOR_PROC_CMDLINE_MARKERS):
|
||||
|
|
|
|||
|
|
@ -281,8 +281,11 @@ def add_log_level(
|
|||
`StackLevelAdapter` so `log.<name>('msg')` works (and so
|
||||
`get_logger()`'s per-level method audit passes).
|
||||
|
||||
Idempotent: re-registering an existing name is a no-op-ish
|
||||
refresh (won't clobber an already-bound method).
|
||||
Idempotent: re-registering an existing name refreshes its
|
||||
value + color in place. The bound `StackLevelAdapter.<name>`
|
||||
method reads the current `CUSTOM_LEVELS` value at call time, so
|
||||
it tracks the new level across re-registration (the method
|
||||
object is bound once, never re-bound/clobbered).
|
||||
|
||||
'''
|
||||
name_up: str = name.upper()
|
||||
|
|
@ -294,16 +297,18 @@ def add_log_level(
|
|||
BOLD_PALETTE['bold'][name_up] = f'bold_{color}'
|
||||
|
||||
if not hasattr(StackLevelAdapter, name_lo):
|
||||
# bind via default-arg so `value` is captured (not
|
||||
# late-bound); delegates to `.log()` exactly like the
|
||||
# hand-written level methods above.
|
||||
# read the level from `CUSTOM_LEVELS[name_up]` at CALL time
|
||||
# (NOT captured at bind time) so a later
|
||||
# `add_log_level(name, <new-value>)` re-registration stays
|
||||
# consistent with this once-bound method. The closure binds
|
||||
# *this* call's `name_up` local (stable, never reassigned),
|
||||
# so there's no late-binding hazard. Delegates to `.log()`
|
||||
# exactly like the hand-written level methods above.
|
||||
def _emit(
|
||||
self,
|
||||
msg: str,
|
||||
*,
|
||||
_level: int = value,
|
||||
) -> None:
|
||||
return self.log(_level, msg)
|
||||
return self.log(CUSTOM_LEVELS[name_up], msg)
|
||||
|
||||
_emit.__name__ = name_lo
|
||||
_emit.__qualname__ = f'StackLevelAdapter.{name_lo}'
|
||||
|
|
|
|||
Loading…
Reference in New Issue