Skip to content

Commit 0638625

Browse files
vtjnashJoe Petviashvili
authored andcommitted
logging: redirect closed streams to stderr/stdout (JuliaLang#40423)
This has the additional benefit of making the initial logger respect changes to redirect_stderr/stdout, until the user explicitly sets another stream as the logging destination. Fix JuliaLang#26798 Fix JuliaLang#38482 Replaces JuliaLang#26920, which provided the idea and most of the implementation Co-authored-by: Joe Petviashvili <joe@gnom.us>
1 parent 8d50300 commit 0638625

File tree

4 files changed

+65
-15
lines changed

4 files changed

+65
-15
lines changed

base/logging.jl

Lines changed: 14 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -611,21 +611,25 @@ attached to the task.
611611
"""
612612
current_logger() = current_logstate().logger
613613

614+
const closed_stream = IOBuffer(UInt8[])
615+
close(closed_stream)
614616

615617
#-------------------------------------------------------------------------------
616618
# SimpleLogger
617619
"""
618-
SimpleLogger(stream=stderr, min_level=Info)
620+
SimpleLogger([stream,] min_level=Info)
619621
620622
Simplistic logger for logging all messages with level greater than or equal to
621-
`min_level` to `stream`.
623+
`min_level` to `stream`. If stream is closed then messages with log level
624+
greater or equal to `Warn` will be logged to `stderr` and below to `stdout`.
622625
"""
623626
struct SimpleLogger <: AbstractLogger
624627
stream::IO
625628
min_level::LogLevel
626629
message_limits::Dict{Any,Int}
627630
end
628-
SimpleLogger(stream::IO=stderr, level=Info) = SimpleLogger(stream, level, Dict{Any,Int}())
631+
SimpleLogger(stream::IO, level=Info) = SimpleLogger(stream, level, Dict{Any,Int}())
632+
SimpleLogger(level=Info) = SimpleLogger(closed_stream, level)
629633

630634
shouldlog(logger::SimpleLogger, level, _module, group, id) =
631635
get(logger.message_limits, id, 1) > 0
@@ -644,7 +648,11 @@ function handle_message(logger::SimpleLogger, level::LogLevel, message, _module,
644648
remaining > 0 || return
645649
end
646650
buf = IOBuffer()
647-
iob = IOContext(buf, logger.stream)
651+
stream = logger.stream
652+
if !isopen(stream)
653+
stream = level < Warn ? stdout : stderr
654+
end
655+
iob = IOContext(buf, stream)
648656
levelstr = level == Warn ? "Warning" : string(level)
649657
msglines = split(chomp(string(message)::String), '\n')
650658
println(iob, "", levelstr, ": ", msglines[1])
@@ -656,10 +664,10 @@ function handle_message(logger::SimpleLogger, level::LogLevel, message, _module,
656664
println(iob, "", key, " = ", val)
657665
end
658666
println(iob, "└ @ ", _module, " ", filepath, ":", line)
659-
write(logger.stream, take!(buf))
667+
write(stream, take!(buf))
660668
nothing
661669
end
662670

663-
_global_logstate = LogState(SimpleLogger(Core.stderr, CoreLogging.Info))
671+
_global_logstate = LogState(SimpleLogger())
664672

665673
end # CoreLogging

stdlib/Logging/src/ConsoleLogger.jl

Lines changed: 17 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
# This file is a part of Julia. License is MIT: https://julialang.org/license
22

33
"""
4-
ConsoleLogger(stream=stderr, min_level=Info; meta_formatter=default_metafmt,
4+
ConsoleLogger([stream,] min_level=Info; meta_formatter=default_metafmt,
55
show_limited=true, right_justify=0)
66
77
Logger with formatting optimized for readability in a text console, for example
@@ -30,12 +30,19 @@ struct ConsoleLogger <: AbstractLogger
3030
right_justify::Int
3131
message_limits::Dict{Any,Int}
3232
end
33-
function ConsoleLogger(stream::IO=stderr, min_level=Info;
33+
function ConsoleLogger(stream::IO, min_level=Info;
3434
meta_formatter=default_metafmt, show_limited=true,
3535
right_justify=0)
3636
ConsoleLogger(stream, min_level, meta_formatter,
3737
show_limited, right_justify, Dict{Any,Int}())
3838
end
39+
function ConsoleLogger(min_level=Info;
40+
meta_formatter=default_metafmt, show_limited=true,
41+
right_justify=0)
42+
ConsoleLogger(closed_stream, min_level, meta_formatter,
43+
show_limited, right_justify, Dict{Any,Int}())
44+
end
45+
3946

4047
shouldlog(logger::ConsoleLogger, level, _module, group, id) =
4148
get(logger.message_limits, id, 1) > 0
@@ -110,12 +117,16 @@ function handle_message(logger::ConsoleLogger, level::LogLevel, message, _module
110117
# Generate a text representation of the message and all key value pairs,
111118
# split into lines.
112119
msglines = [(indent=0, msg=l) for l in split(chomp(string(message)::String), '\n')]
113-
dsize = displaysize(logger.stream)::Tuple{Int,Int}
120+
stream = logger.stream
121+
if !isopen(stream)
122+
stream = level < Warn ? stdout : stderr
123+
end
124+
dsize = displaysize(stream)::Tuple{Int,Int}
114125
nkwargs = length(kwargs)::Int
115126
if nkwargs > hasmaxlog
116127
valbuf = IOBuffer()
117128
rows_per_value = max(1, dsize[1] ÷ (nkwargs + 1 - hasmaxlog))
118-
valio = IOContext(IOContext(valbuf, logger.stream),
129+
valio = IOContext(IOContext(valbuf, stream),
119130
:displaysize => (rows_per_value, dsize[2] - 5),
120131
:limit => logger.show_limited)
121132
for (key, val) in kwargs
@@ -136,7 +147,7 @@ function handle_message(logger::ConsoleLogger, level::LogLevel, message, _module
136147
color, prefix, suffix = logger.meta_formatter(level, _module, group, id, filepath, line)::Tuple{Union{Symbol,Int},String,String}
137148
minsuffixpad = 2
138149
buf = IOBuffer()
139-
iob = IOContext(buf, logger.stream)
150+
iob = IOContext(buf, stream)
140151
nonpadwidth = 2 + (isempty(prefix) || length(msglines) > 1 ? 0 : length(prefix)+1) +
141152
msglines[end].indent + termlength(msglines[end].msg) +
142153
(isempty(suffix) ? 0 : length(suffix)+minsuffixpad)
@@ -164,6 +175,6 @@ function handle_message(logger::ConsoleLogger, level::LogLevel, message, _module
164175
println(iob)
165176
end
166177

167-
write(logger.stream, take!(buf))
178+
write(stream, take!(buf))
168179
nothing
169180
end

stdlib/Logging/src/Logging.jl

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,9 @@ for sym in [
2929
@eval const $sym = Base.CoreLogging.$sym
3030
end
3131

32+
using Base.CoreLogging:
33+
closed_stream
34+
3235
export
3336
AbstractLogger,
3437
LogLevel,
@@ -56,7 +59,7 @@ include("ConsoleLogger.jl")
5659
# handle_message, shouldlog, min_enabled_level, catch_exceptions,
5760

5861
function __init__()
59-
global_logger(ConsoleLogger(stderr))
62+
global_logger(ConsoleLogger())
6063
end
6164

6265
end

test/corelogging.jl

Lines changed: 30 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -341,15 +341,43 @@ end
341341
String(take!(io))
342342
end
343343

344+
function genmsg_out(level, message, _module, filepath, line; kws...)
345+
fname = tempname()
346+
f = open(fname, "w")
347+
logger = SimpleLogger()
348+
redirect_stdout(f) do
349+
handle_message(logger, level, message, _module, :group, :id,
350+
filepath, line; kws...)
351+
end
352+
close(f)
353+
buf = read(fname)
354+
rm(fname)
355+
String(buf)
356+
end
357+
358+
function genmsg_err(level, message, _module, filepath, line; kws...)
359+
fname = tempname()
360+
f = open(fname, "w")
361+
logger = SimpleLogger()
362+
redirect_stderr(f) do
363+
handle_message(logger, level, message, _module, :group, :id,
364+
filepath, line; kws...)
365+
end
366+
close(f)
367+
buf = read(fname)
368+
rm(fname)
369+
String(buf)
370+
end
371+
344372
# Simple
345-
@test genmsg(Info, "msg", Main, "some/path.jl", 101) ==
373+
@test genmsg_out(Info, "msg", Main, "some/path.jl", 101) ==
346374
"""
347375
┌ Info: msg
348376
└ @ Main some/path.jl:101
349377
"""
350378

351379
# Multiline message
352-
@test genmsg(Warn, "line1\nline2", Main, "some/path.jl", 101) ==
380+
@test genmsg_err(Warn, "line1\nline2", Main, "some/path.jl", 101) ==
353381
"""
354382
┌ Warning: line1
355383
│ line2

0 commit comments

Comments
 (0)