Skip to content

Commit

Permalink
Merge pull request #25488 from JuliaLang/cjf/ConsoleLogger-formatting
Browse files Browse the repository at this point in the history
Log printing tweaks
  • Loading branch information
StefanKarpinski authored Jan 11, 2018
2 parents 4440b0f + 52c4c45 commit cbae7a2
Show file tree
Hide file tree
Showing 2 changed files with 247 additions and 159 deletions.
141 changes: 89 additions & 52 deletions stdlib/Logging/src/ConsoleLogger.jl
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
# This file is a part of Julia. License is MIT: https://julialang.org/license

"""
ConsoleLogger(stream=STDERR, min_level=Info; meta_formatter=default_metafmt, show_limited=true)
ConsoleLogger(stream=STDERR, 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
interactive work with the Julia REPL.
Expand All @@ -11,24 +12,29 @@ Log levels less than `min_level` are filtered out.
Message formatting can be controlled by setting keyword arguments:
* `meta_formatter` is a function which takes the log event metadata
`(level, _module, group, id, file, line)` and produces a prefix and suffix
for the log message. The default is to prefix with the log level and add a
suffix containing the module, file and line location.
`(level, _module, group, id, file, line)` and returns a color (as would be
passed to print_with_color), prefix and suffix for the log message. The
default is to prefix with the log level and a suffix containing the module,
file and line location.
* `show_limited` limits the printing of large data structures to something
which can fit on the screen by setting the `:limit` `IOContext` key during
formatting.
* `right_justify` is the integer column which log metadata is right justified
at. The default is zero (metadata goes on its own line).
"""
struct ConsoleLogger <: AbstractLogger
stream::IO
min_level::LogLevel
meta_formatter
show_limited::Bool
right_justify::Int
message_limits::Dict{Any,Int}
end
function ConsoleLogger(stream::IO=STDERR, min_level=Info;
meta_formatter=default_metafmt, show_limited=true)
meta_formatter=default_metafmt, show_limited=true,
right_justify=0)
ConsoleLogger(stream, min_level, meta_formatter,
show_limited, Dict{Any,Int}())
show_limited, right_justify, Dict{Any,Int}())
end

shouldlog(logger::ConsoleLogger, level, _module, group, id) =
Expand All @@ -44,9 +50,39 @@ function showvalue(io, e::Tuple{Exception,Any})
end
showvalue(io, ex::Exception) = showvalue(io, (ex,catch_backtrace()))

function default_logcolor(level)
level < Info ? Base.debug_color() :
level < Warn ? Base.info_color() :
level < Error ? Base.warn_color() :
Base.error_color()
end

function default_metafmt(level, _module, group, id, file, line)
((level == Warn ? "Warning" : string(level))*':',
"@ $_module $(basename(file)):$line")
color = default_logcolor(level)
prefix = (level == Warn ? "Warning" : string(level))*':'
suffix = (Info <= level < Warn) ? "" : "@ $_module $(basename(file)):$line"
color,prefix,suffix
end

# Length of a string as it will appear in the terminal (after ANSI color codes
# are removed)
function termlength(str)
N = 0
in_esc = false
for c in str
if in_esc
if c == 'm'
in_esc = false
end
else
if c == '\e'
in_esc = true
else
N += 1
end
end
end
return N
end

function handle_message(logger::ConsoleLogger, level, message, _module, group, id,
Expand All @@ -56,63 +92,64 @@ function handle_message(logger::ConsoleLogger, level, message, _module, group, i
logger.message_limits[id] = remaining - 1
remaining > 0 || return
end
color = level < Info ? Base.debug_color() :
level < Warn ? Base.info_color() :
level < Error ? Base.warn_color() :
Base.error_color()
buf = IOBuffer()
iob = IOContext(buf, logger.stream)
if logger.show_limited
iob = IOContext(iob, :limit=>true)
end
msglines = split(chomp(string(message)), '\n')

# 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)), '\n')]
dsize = displaysize(logger.stream)
width = dsize[2]
prefix,suffix = logger.meta_formatter(level, _module, group, id, filepath, line)
length(prefix) == 0 || (prefix = prefix*" ")
length(suffix) == 0 || (suffix = " "*suffix)
singlelinewidth = 2 + length(msglines[1]) + length(prefix) + length(suffix)
issingleline = length(msglines) + length(kwargs) == 1 && singlelinewidth <= width
print_with_color(color, iob, issingleline ? "[ " : "", bold=true)
if length(prefix) > 0
print_with_color(color, iob, prefix, bold=true)
end
print(iob, msglines[1])
if issingleline
npad = (width - singlelinewidth)
else
println(iob)
for i in 2:length(msglines)
print_with_color(color, iob, "", bold=true)
println(iob, msglines[i])
end
if !isempty(kwargs)
valbuf = IOBuffer()
rows_per_value = max(1, dsize[1]÷(length(kwargs)+1))
valio = IOContext(IOContext(valbuf, iob),
valio = IOContext(IOContext(valbuf, logger.stream),
:displaysize=>(rows_per_value,dsize[2]-5))
if logger.show_limited
valio = IOContext(valio, :limit=>true)
end
for (key,val) in pairs(kwargs)
print_with_color(color, iob, "", bold=true)
print(iob, " ", key, " =")
showvalue(valio, val)
vallines = split(String(take!(valbuf)), '\n')
if length(vallines) == 1
println(iob, " ", vallines[1])
push!(msglines, (indent=2,msg=SubString("$key = $(vallines[1])")))
else
println(iob)
for line in vallines
print_with_color(color, iob, "", bold=true)
println(iob, line)
end
push!(msglines, (indent=2,msg=SubString("$key =")))
append!(msglines, ((indent=3,msg=line) for line in vallines))
end
end
print_with_color(color, iob, "", bold=true)
npad = width - 2 - length(suffix)
end
if length(suffix) > 0
print(iob, " "^npad)
print_with_color(:light_black, iob, suffix, bold=false)

# Format lines as text with appropriate indentation and with a box
# decoration on the left.
color,prefix,suffix = logger.meta_formatter(level, _module, group, id, filepath, line)
minsuffixpad = 2
buf = IOBuffer()
iob = IOContext(buf, logger.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)
justify_width = min(logger.right_justify, dsize[2])
if nonpadwidth > justify_width && !isempty(suffix)
push!(msglines, (indent=0,msg=SubString("")))
minsuffixpad = 0
nonpadwidth = 2 + length(suffix)
end
for (i,(indent,msg)) in enumerate(msglines)
boxstr = length(msglines) == 1 ? "[ " :
i == 1 ? "" :
i < length(msglines) ? "" :
""
print_with_color(color, iob, boxstr, bold=true)
if i == 1 && !isempty(prefix)
print_with_color(color, iob, prefix, " ", bold=true)
end
print(iob, " "^indent, msg)
if i == length(msglines) && !isempty(suffix)
npad = max(0, justify_width - nonpadwidth) + minsuffixpad
print(iob, " "^npad)
print_with_color(:light_black, iob, suffix)
end
println(iob)
end
print(iob, "\n")

write(logger.stream, take!(buf))
nothing
end
Expand Down
Loading

0 comments on commit cbae7a2

Please sign in to comment.