diff --git a/base/logging.jl b/base/logging.jl index 066523771f5fb..2404c1107e58c 100644 --- a/base/logging.jl +++ b/base/logging.jl @@ -611,21 +611,25 @@ attached to the task. """ current_logger() = current_logstate().logger +const closed_stream = IOBuffer(UInt8[]) +close(closed_stream) #------------------------------------------------------------------------------- # SimpleLogger """ - SimpleLogger(stream=stderr, min_level=Info) + SimpleLogger([stream,] min_level=Info) Simplistic logger for logging all messages with level greater than or equal to -`min_level` to `stream`. +`min_level` to `stream`. If stream is closed then messages with log level +greater or equal to `Warn` will be logged to `stderr` and below to `stdout`. """ struct SimpleLogger <: AbstractLogger stream::IO min_level::LogLevel message_limits::Dict{Any,Int} end -SimpleLogger(stream::IO=stderr, level=Info) = SimpleLogger(stream, level, Dict{Any,Int}()) +SimpleLogger(stream::IO, level=Info) = SimpleLogger(stream, level, Dict{Any,Int}()) +SimpleLogger(level=Info) = SimpleLogger(closed_stream, level) shouldlog(logger::SimpleLogger, level, _module, group, id) = get(logger.message_limits, id, 1) > 0 @@ -644,7 +648,11 @@ function handle_message(logger::SimpleLogger, level::LogLevel, message, _module, remaining > 0 || return end buf = IOBuffer() - iob = IOContext(buf, logger.stream) + stream = logger.stream + if !isopen(stream) + stream = level < Warn ? stdout : stderr + end + iob = IOContext(buf, stream) levelstr = level == Warn ? "Warning" : string(level) msglines = split(chomp(string(message)::String), '\n') println(iob, "┌ ", levelstr, ": ", msglines[1]) @@ -656,10 +664,10 @@ function handle_message(logger::SimpleLogger, level::LogLevel, message, _module, println(iob, "│ ", key, " = ", val) end println(iob, "└ @ ", _module, " ", filepath, ":", line) - write(logger.stream, take!(buf)) + write(stream, take!(buf)) nothing end -_global_logstate = LogState(SimpleLogger(Core.stderr, CoreLogging.Info)) +_global_logstate = LogState(SimpleLogger()) end # CoreLogging diff --git a/stdlib/Logging/src/ConsoleLogger.jl b/stdlib/Logging/src/ConsoleLogger.jl index 2a96b08eb5ed2..04c56c6dbfaae 100644 --- a/stdlib/Logging/src/ConsoleLogger.jl +++ b/stdlib/Logging/src/ConsoleLogger.jl @@ -1,7 +1,7 @@ # This file is a part of Julia. License is MIT: https://julialang.org/license """ - ConsoleLogger(stream=stderr, min_level=Info; meta_formatter=default_metafmt, + ConsoleLogger([stream,] min_level=Info; meta_formatter=default_metafmt, show_limited=true, right_justify=0) Logger with formatting optimized for readability in a text console, for example @@ -30,12 +30,19 @@ struct ConsoleLogger <: AbstractLogger right_justify::Int message_limits::Dict{Any,Int} end -function ConsoleLogger(stream::IO=stderr, min_level=Info; +function ConsoleLogger(stream::IO, min_level=Info; meta_formatter=default_metafmt, show_limited=true, right_justify=0) ConsoleLogger(stream, min_level, meta_formatter, show_limited, right_justify, Dict{Any,Int}()) end +function ConsoleLogger(min_level=Info; + meta_formatter=default_metafmt, show_limited=true, + right_justify=0) + ConsoleLogger(closed_stream, min_level, meta_formatter, + show_limited, right_justify, Dict{Any,Int}()) +end + shouldlog(logger::ConsoleLogger, level, _module, group, id) = get(logger.message_limits, id, 1) > 0 @@ -110,12 +117,16 @@ function handle_message(logger::ConsoleLogger, level::LogLevel, message, _module # Generate a text representation of the message and all key value pairs, # split into lines. msglines = [(indent=0, msg=l) for l in split(chomp(string(message)::String), '\n')] - dsize = displaysize(logger.stream)::Tuple{Int,Int} + stream = logger.stream + if !isopen(stream) + stream = level < Warn ? stdout : stderr + end + dsize = displaysize(stream)::Tuple{Int,Int} nkwargs = length(kwargs)::Int if nkwargs > hasmaxlog valbuf = IOBuffer() rows_per_value = max(1, dsize[1] ÷ (nkwargs + 1 - hasmaxlog)) - valio = IOContext(IOContext(valbuf, logger.stream), + valio = IOContext(IOContext(valbuf, stream), :displaysize => (rows_per_value, dsize[2] - 5), :limit => logger.show_limited) for (key, val) in kwargs @@ -136,7 +147,7 @@ function handle_message(logger::ConsoleLogger, level::LogLevel, message, _module color, prefix, suffix = logger.meta_formatter(level, _module, group, id, filepath, line)::Tuple{Union{Symbol,Int},String,String} minsuffixpad = 2 buf = IOBuffer() - iob = IOContext(buf, logger.stream) + iob = IOContext(buf, stream) nonpadwidth = 2 + (isempty(prefix) || length(msglines) > 1 ? 0 : length(prefix)+1) + msglines[end].indent + termlength(msglines[end].msg) + (isempty(suffix) ? 0 : length(suffix)+minsuffixpad) @@ -164,6 +175,6 @@ function handle_message(logger::ConsoleLogger, level::LogLevel, message, _module println(iob) end - write(logger.stream, take!(buf)) + write(stream, take!(buf)) nothing end diff --git a/stdlib/Logging/src/Logging.jl b/stdlib/Logging/src/Logging.jl index b44b8ae67473c..a9d86d9bd4418 100644 --- a/stdlib/Logging/src/Logging.jl +++ b/stdlib/Logging/src/Logging.jl @@ -29,6 +29,9 @@ for sym in [ @eval const $sym = Base.CoreLogging.$sym end +using Base.CoreLogging: + closed_stream + export AbstractLogger, LogLevel, @@ -56,7 +59,7 @@ include("ConsoleLogger.jl") # handle_message, shouldlog, min_enabled_level, catch_exceptions, function __init__() - global_logger(ConsoleLogger(stderr)) + global_logger(ConsoleLogger()) end end diff --git a/test/corelogging.jl b/test/corelogging.jl index 89f69be438810..698209661456b 100644 --- a/test/corelogging.jl +++ b/test/corelogging.jl @@ -341,15 +341,43 @@ end String(take!(io)) end + function genmsg_out(level, message, _module, filepath, line; kws...) + fname = tempname() + f = open(fname, "w") + logger = SimpleLogger() + redirect_stdout(f) do + handle_message(logger, level, message, _module, :group, :id, + filepath, line; kws...) + end + close(f) + buf = read(fname) + rm(fname) + String(buf) + end + + function genmsg_err(level, message, _module, filepath, line; kws...) + fname = tempname() + f = open(fname, "w") + logger = SimpleLogger() + redirect_stderr(f) do + handle_message(logger, level, message, _module, :group, :id, + filepath, line; kws...) + end + close(f) + buf = read(fname) + rm(fname) + String(buf) + end + # Simple - @test genmsg(Info, "msg", Main, "some/path.jl", 101) == + @test genmsg_out(Info, "msg", Main, "some/path.jl", 101) == """ ┌ Info: msg └ @ Main some/path.jl:101 """ # Multiline message - @test genmsg(Warn, "line1\nline2", Main, "some/path.jl", 101) == + @test genmsg_err(Warn, "line1\nline2", Main, "some/path.jl", 101) == """ ┌ Warning: line1 │ line2