Skip to content

Commit

Permalink
Generalize metadata formatting to an arbitrary function
Browse files Browse the repository at this point in the history
  • Loading branch information
c42f committed Jan 5, 2018
1 parent 7d2d507 commit 305e519
Show file tree
Hide file tree
Showing 2 changed files with 71 additions and 66 deletions.
71 changes: 39 additions & 32 deletions stdlib/Logging/src/ConsoleLogger.jl
Original file line number Diff line number Diff line change
@@ -1,30 +1,34 @@
# This file is a part of Julia. License is MIT: https://julialang.org/license

"""
ConsoleLogger(stream=STDERR, min_level=Info; min_level_prefix=Logging.Warn, show_limited=true)
ConsoleLogger(stream=STDERR, min_level=Info; meta_formatter=default_metafmt, show_limited=true)
Logger with formatting optimized for readability in a text console, for example
interactive work with the Julia REPL.
Log levels less than `min_level` are filtered out.
Message formatting can be controlled by setting keyword arguments.
`min_level_prefix` controls whether the name of the log level will prefix the
message (for example `@warn "blah"` being formatted as "Warning: blah"). The
printing of large data structures in key value pairs is limited to what can be
reasonably shown on the display when `show_limited=true`, by setting the
`:limit` `IOContext` key during formatting.
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.
* `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.
"""
struct ConsoleLogger <: AbstractLogger
stream::IO
min_level::LogLevel
min_level_prefix::LogLevel
meta_formatter
show_limited::Bool
message_limits::Dict{Any,Int}
end
function ConsoleLogger(stream::IO=STDERR, min_level=Info;
min_level_prefix=Logging.Warn, show_limited=true)
ConsoleLogger(stream, min_level, min_level_prefix, show_limited, Dict{Any,Int}())
meta_formatter=default_metafmt, show_limited=true)
ConsoleLogger(stream, min_level, meta_formatter,
show_limited, Dict{Any,Int}())
end

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

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

function handle_message(logger::ConsoleLogger, level, message, _module, group, id,
filepath, line; maxlog=nothing, kwargs...)
if maxlog != nothing && maxlog isa Integer
remaining = get!(logger.message_limits, id, maxlog)
logger.message_limits[id] = remaining - 1
remaining > 0 || return
end
levelstr = string(level)
level != Warn || (levelstr = "Warning")
color = level < Info ? Base.debug_color() :
level < Warn ? Base.info_color() :
level < Error ? Base.warn_color() :
Expand All @@ -59,33 +66,30 @@ function handle_message(logger::ConsoleLogger, level, message, _module, group, i
iob = IOContext(iob, :limit=>true)
end
msglines = split(chomp(string(message)), '\n')
prefixwithlevel = level >= logger.min_level_prefix
dsize = displaysize(logger.stream)
width = dsize[2]
locationstr = string(levelstr, " @ ", _module, " ", basename(filepath), ":", line)
singlelinewidth = 2 + length(msglines[1]) +
(prefixwithlevel ? length(levelstr) + 2 : 0) +
length(locationstr)
if length(msglines) + length(kwargs) == 1 && singlelinewidth <= width
print_with_color(color, iob, "[ ", bold=true)
if prefixwithlevel
print_with_color(color, iob, levelstr, ": ", bold=true)
end
print(iob, msglines[1])
print(iob, " "^(width - singlelinewidth))
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
print_with_color(color, iob, "", bold=true)
if prefixwithlevel
print_with_color(color, iob, levelstr, ": ", bold=true)
end
println(iob, msglines[1])
println(iob)
for i in 2:length(msglines)
print_with_color(color, iob, "", bold=true)
println(iob, msglines[i])
end
valbuf = IOBuffer()
rows_per_value = max(1, dsize[1]÷(length(kwargs)+1))
valio = IOContext(IOContext(valbuf, iob),
:displaysize=>(1 + dsize[1]÷(length(kwargs)+1),dsize[2]-5))
:displaysize=>(rows_per_value,dsize[2]-5))
for (key,val) in pairs(kwargs)
print_with_color(color, iob, "", bold=true)
print(iob, " ", key, " =")
Expand All @@ -102,9 +106,12 @@ function handle_message(logger::ConsoleLogger, level, message, _module, group, i
end
end
print_with_color(color, iob, "", bold=true)
print(iob, " "^(max(1, width - 1 - 1 - length(locationstr))))
npad = width - 2 - length(suffix)
end
if length(suffix) > 0
print(iob, " "^npad)
print_with_color(:light_black, iob, suffix, bold=false)
end
print_with_color(:light_black, iob, locationstr, bold=false)
print(iob, "\n")
write(logger.stream, take!(buf))
nothing
Expand Down
66 changes: 32 additions & 34 deletions stdlib/Logging/test/runtests.jl
Original file line number Diff line number Diff line change
Expand Up @@ -20,69 +20,69 @@ import Logging: min_enabled_level, shouldlog, handle_message
@test shouldlog(logger, Logging.Info, Base, :group, :asdf) === false

# Log formatting
function genmsg(level, message, _module, filepath, line; color=false, min_level_prefix=Logging.Warn, show_limited=true, kws...)
function genmsg(level, message, _module, filepath, line; color=false,
meta_formatter=Logging.default_metafmt, show_limited=true, kws...)
buf = IOBuffer()
io = IOContext(buf, :displaysize=>(30,75), :color=>color)
logger = ConsoleLogger(io, Logging.Debug,
min_level_prefix=min_level_prefix,
meta_formatter=meta_formatter,
show_limited=show_limited)
Logging.handle_message(logger, level, message, _module, :group, :id,
Logging.handle_message(logger, level, message, _module, :a_group, :an_id,
filepath, line; kws...)
s = String(take!(buf))
String(take!(buf))
end

# Single line formatting, default metadata
@test genmsg(Logging.Debug, "msg", Main, "some/path.jl", 101) ==
"""
[ msg Debug @ Main path.jl:101
[ Debug: msg @ Main path.jl:101
"""
@test genmsg(Logging.Info, "msg", Main, "some/path.jl", 101) ==
"""
[ msg Info @ Main path.jl:101
[ Info: msg @ Main path.jl:101
"""
@test genmsg(Logging.Warn, "msg", Main, "some/path.jl", 101) ==
"""
[ Warning: msg Warning @ Main path.jl:101
[ Warning: msg @ Main path.jl:101
"""
@test genmsg(Logging.Error, "msg", Main, "some/path.jl", 101) ==
"""
[ Error: msg Error @ Main path.jl:101
[ Error: msg @ Main path.jl:101
"""

# Disabling / enabling the level prefix
@test genmsg(Logging.Debug, "msg", Main, "some/path.jl", 101, min_level_prefix=Logging.BelowMinLevel) ==
# Configurable metadata formatting
@test genmsg(Logging.Debug, "msg", Main, "some/path.jl", 101,
meta_formatter=(level, _module, group, id, file, line)->
("Foo!", "$level $_module $group $id $file $line")) ==
"""
[ Debug: msg Debug @ Main path.jl:101
"""
@test genmsg(Logging.Error, "msg", Main, "some/path.jl", 101, min_level_prefix=Logging.AboveMaxLevel) ==
"""
[ msg Error @ Main path.jl:101
[ Foo! msg Debug Main a_group an_id some/path.jl 101
"""

# Empty message
# Empty message string
@test genmsg(Logging.Error, "", Main, "some/path.jl", 101) ==
"""
[ Error: Error @ Main path.jl:101
[ Error: @ Main path.jl:101
"""
@test genmsg(Logging.Error, "", Main, "some/path.jl", 101, a=1) ==
replace("""
┌ Error: EOL
│ a = 1
Error @ Main path.jl:101
@ Main path.jl:101
""", "EOL"=>"")

# Long line
# Long message line
@test genmsg(Logging.Error, "msg msg msg msg msg msg msg msg msg msg msg msg msg", Main, "some/path.jl", 101) ==
"""
┌ Error: msg msg msg msg msg msg msg msg msg msg msg msg msg
Error @ Main path.jl:101
@ Main path.jl:101
"""

# Multiline messages
@test genmsg(Logging.Warn, "line1\nline2", Main, "some/path.jl", 101) ==
"""
┌ Warning: line1
│ line2
Warning @ Main path.jl:101
@ Main path.jl:101
"""

# Keywords
Expand All @@ -91,21 +91,21 @@ import Logging: min_enabled_level, shouldlog, handle_message
┌ Error: msg
│ a = 1
│ b = "asdf"
Error @ Base other.jl:101
@ Base other.jl:101
"""
# Exceptions use showerror
@test genmsg(Logging.Info, "msg", Base, "other.jl", 101, exception=DivideError()) ==
"""
┌ msg
Info: msg
│ exception = DivideError: integer division error
Info @ Base other.jl:101
@ Base other.jl:101
"""
# Attaching backtraces
bt = func1()
@test startswith(genmsg(Logging.Info, "msg", Base, "other.jl", 101,
exception=(DivideError(),bt)),
"""
┌ msg
Info: msg
│ exception =
│ DivideError: integer division error
│ Stacktrace:
Expand All @@ -116,7 +116,7 @@ import Logging: min_enabled_level, shouldlog, handle_message
@test genmsg(Logging.Info, "msg", Main, "some/path.jl", 101,
a=fill(1.00001, 100,100), b=fill(2.00002, 10,10)) ==
replace("""
┌ msg
Info: msg
│ a =
│ 100×100 Array{Float64,2}:
│ 1.00001 1.00001 1.00001 1.00001 … 1.00001 1.00001 1.00001
Expand All @@ -125,7 +125,6 @@ import Logging: min_enabled_level, shouldlog, handle_message
│ ⋮ ⋱ EOL
│ 1.00001 1.00001 1.00001 1.00001 1.00001 1.00001 1.00001
│ 1.00001 1.00001 1.00001 1.00001 1.00001 1.00001 1.00001
│ 1.00001 1.00001 1.00001 1.00001 1.00001 1.00001 1.00001
│ b =
│ 10×10 Array{Float64,2}:
│ 2.00002 2.00002 2.00002 2.00002 … 2.00002 2.00002 2.00002
Expand All @@ -134,14 +133,13 @@ import Logging: min_enabled_level, shouldlog, handle_message
│ ⋮ ⋱ EOL
│ 2.00002 2.00002 2.00002 2.00002 2.00002 2.00002 2.00002
│ 2.00002 2.00002 2.00002 2.00002 2.00002 2.00002 2.00002
│ 2.00002 2.00002 2.00002 2.00002 2.00002 2.00002 2.00002
└ Info @ Main path.jl:101
└ @ Main path.jl:101
""", "EOL"=>"") # EOL hack to work around git whitespace errors
# Limiting the amount which is printed
@test genmsg(Logging.Info, "msg", Main, "some/path.jl", 101,
a=fill(1.00001, 10,10), show_limited=false) ==
"""
┌ msg
Info: msg
│ a =
│ 10×10 Array{Float64,2}:
│ 1.00001 1.00001 1.00001 1.00001 1.00001 1.00001 1.00001 1.00001 1.00001 1.00001
Expand All @@ -154,17 +152,17 @@ import Logging: min_enabled_level, shouldlog, handle_message
│ 1.00001 1.00001 1.00001 1.00001 1.00001 1.00001 1.00001 1.00001 1.00001 1.00001
│ 1.00001 1.00001 1.00001 1.00001 1.00001 1.00001 1.00001 1.00001 1.00001 1.00001
│ 1.00001 1.00001 1.00001 1.00001 1.00001 1.00001 1.00001 1.00001 1.00001 1.00001
Info @ Main path.jl:101
@ Main path.jl:101
"""


# Basic color test
@test genmsg(Logging.Info, "line1\nline2", Main, "some/path.jl", 101, color=true) ==
"""
\e[1m\e[36m┌ \e[39m\e[22mline1
\e[1m\e[36m┌ \e[39m\e[22m\e[1m\e[36mInfo: \e[39m\e[22mline1
\e[1m\e[36m│ \e[39m\e[22mline2
\e[1m\e[36m└ \e[39m\e[22m \e[90mInfo @ Main path.jl:101\e[39m
\e[1m\e[36m└ \e[39m\e[22m \e[90m @ Main path.jl:101\e[39m
"""

end

end

0 comments on commit 305e519

Please sign in to comment.