From da43f39a7e25ea543c39087811de3198caca4b72 Mon Sep 17 00:00:00 2001 From: Ian Butterworth Date: Thu, 16 Jul 2026 10:53:46 -0400 Subject: [PATCH 1/2] managers: pass worker ident as a string to output redirection `redirect_worker_output` is documented to receive `ident` as an `AbstractString`, and the `cluster.jl` call site already passes `"$pid"`. The `connect` path in `managers.jl` passed the raw `Int` pid instead, so any consumer that treated `ident` as a string (e.g. `tryparse(Int, ident)`) would misbehave. Pass `"$pid"` here too so the two call sites agree. Co-Authored-By: Claude Opus 4.8 (1M context) --- src/managers.jl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/managers.jl b/src/managers.jl index e065466..241f8de 100644 --- a/src/managers.jl +++ b/src/managers.jl @@ -631,7 +631,7 @@ function connect(manager::ClusterManager, pid::Int, config::WorkerConfig) if config.io !== nothing let pid = pid - redirect_worker_output(pid, notnothing(config.io)) + redirect_worker_output("$pid", notnothing(config.io)) end end From 14f81291c8a60ca4c6a1d78cf372ba45017b7e47 Mon Sep 17 00:00:00 2001 From: Ian Butterworth Date: Thu, 16 Jul 2026 10:53:46 -0400 Subject: [PATCH 2/2] cluster: add internal worker_output_hook for customizing worker output Add an internal, unstable `worker_output_hook` Ref that, when set, transforms each line of a worker's output into the string that should be printed for it. `Distributed` still owns the actual printing, so a hook may return a `StyledStrings`-styled line and it will render correctly on IOs that do or do not support color. When the hook is unset (the default) the standard prefix is used. A hook that throws falls back to the default prefix so worker output is never swallowed. The default prefix indent is also reduced from six spaces to a single space. The hook is deliberately not exported and carries no compatibility guarantees; it exists so test harnesses can label worker output with richer context (e.g. which test a worker is running). Co-Authored-By: Claude Opus 4.8 (1M context) --- src/cluster.jl | 44 ++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 42 insertions(+), 2 deletions(-) diff --git a/src/cluster.jl b/src/cluster.jl index 094c461..7f9ecfc 100644 --- a/src/cluster.jl +++ b/src/cluster.jl @@ -294,15 +294,55 @@ function start_worker(out::IO, cookie::AbstractString=readline(stdin); close_std end +""" + worker_output_hook + +Internal, unstable hook for customizing how a worker's output lines are displayed. + +`worker_output_hook[]` may be set to a transform function +`f(ident::AbstractString, line::AbstractString) -> AbstractString` that maps a line of +worker output to the string that should be printed for it. This lets test harnesses or +other callers provide richer context about what each worker is doing (e.g. which test is +running) instead of the default `" From worker \$ident:\\t\$line"` prefix. The returned +string is printed as-is, so it may carry `StyledStrings` styling; `Distributed` still owns +the actual printing. + +When `worker_output_hook[]` is `nothing` (the default) the standard prefix is used. + +This is an internal implementation detail with no compatibility guarantees; it may be +changed or removed at any time. + +# Example +```julia +Distributed.worker_output_hook[] = (ident, line) -> "[worker \$ident] \$line" +``` +""" +const worker_output_hook = Ref{Union{Nothing, Function}}(nothing) + +const _worker_output_prefix = " From worker " + function redirect_worker_output(ident, stream) t = @async while !eof(stream) line = readline(stream) - if startswith(line, " From worker ") + if startswith(line, _worker_output_prefix) # stdout's of "additional" workers started from an initial worker on a host are not available # on the master directly - they are routed via the initial worker's stdout. println(line) else - println(" From worker $(ident):\t$line") + hook = worker_output_hook[] + out = nothing + if hook !== nothing + try + out = hook(ident, line)::AbstractString + catch + out = nothing # a broken hook must not swallow worker output + end + end + if out === nothing + println("$(_worker_output_prefix)$(ident):\t$line") + else + println(out) + end end end errormonitor(t)