Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 42 additions & 2 deletions src/cluster.jl
Original file line number Diff line number Diff line change
Expand Up @@ -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)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I don't think I like that we expect the handler to fully take over the role of printing stuff as well. We may very well change it in the future to better support logging or something, so I would prefer that the callback instead be a transform that takes in the string and returns a (possibly different) string for Distributed to handle in its own way.

In which case we could have:

Suggested change
const worker_output_hook = Ref{Union{Nothing, Function}}(nothing)
worker_output_hook::Function = identity

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Good call. I switched it to a transform that just returns the string, so Distributed still owns the println (and a throwing hook now falls back to the default prefix instead of eating output).

Two deliberate tweaks from the exact suggestion:

  • (ident, line) not just line. The test harness needs ident to label a line with which test that worker is running.
  • defaults to nothing, not identity. So unset means "use the normal From worker N: prefix" rather than printing raw un-prefixed lines. Default behavior stays unchanged.


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)
Expand Down
2 changes: 1 addition & 1 deletion src/managers.jl
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
Loading