Skip to content

Commit

Permalink
Merge pull request #28 from Arkoniak/squash_delimiter
Browse files Browse the repository at this point in the history
squashed values
  • Loading branch information
Arkoniak authored Aug 14, 2021
2 parents 960dd72 + d771065 commit eb001a3
Show file tree
Hide file tree
Showing 3 changed files with 49 additions and 26 deletions.
2 changes: 1 addition & 1 deletion Project.toml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
name = "MiniLoggers"
uuid = "93f3dd0f-005d-4452-894a-a31841fa4078"
authors = ["Andrey Oskin"]
version = "0.3.2"
version = "0.4.0"

[deps]
Dates = "ade2ca70-3891-5945-98fb-dc099432e06a"
Expand Down
47 changes: 25 additions & 22 deletions src/minilogger.jl
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ struct MiniLogger{AM <: AbstractMode, IOT1 <: IO, IOT2 <: IO, DFT <: DateFormat}
format::Vector{Token}
dtformat::DFT
mode::AM
squash_delimiter::String
flush_threshold::Int
lastflush::Base.RefValue{Int64}
end
Expand Down Expand Up @@ -59,8 +60,9 @@ Supported keyword arguments include:
* `append` (default: `false`): defines whether to append to output stream or to truncate file initially. Used only if `io` or `ioerr` is a file path.
* `message_mode` (default: `:squash`): choose how message is transformed before being printed out. Following modes are supported:
* `:notransformations`: message printed out as is, without any extra transformations
* `:squash`: message is squashed to a single line, i.e. all `\\n` are changed to ` ` and `\\r` are removed.
* `:squash`: message is squashed to a single line, i.e. all `\\n` are changed to `squash_delimiter` and `\\r` are removed.
* `:markdown`: message is treated as if it is written in markdown
* `squash_delimiter`: (default: "\\t"): defines which delimiter to use in squash mode.
* `flush` (default: `true`): whether to `flush` IO stream for each log message. Flush behaviour also affected by `flush_threshold` argument.
* `flush_threshold::Union{Integer, TimePeriod}` (default: 0): if this argument is nonzero and `flush` is `true`, then `io` is flushed only once per `flush_threshold` milliseconds. I.e. if time between two consecutive log messages is less then `flush_threshold`, then second message is not flushed and will have to wait for the next log event.
* `dtformat` (default: "yyyy-mm-dd HH:MM:SS"): if `datetime` parameter is used in `format` argument, this dateformat is applied for output timestamps.
Expand All @@ -81,7 +83,7 @@ Colour information is applied recursively without override, so `{{line} {module:
If part of the format is not a recognised keyword, then it is just used as is, for example `{foo:red}` means that output log message contain word "foo" printed in red.
"""
function MiniLogger(; io = stdout, ioerr = stderr, errlevel = Error, minlevel = Info, append = false, message_limits = Dict{Any, Int}(), flush = true, format = "[{timestamp:func}] {level:func}: {message}", dtformat = dateformat"yyyy-mm-dd HH:MM:SS", flush_threshold = 0, message_mode = Squash())
function MiniLogger(; io = stdout, ioerr = stderr, errlevel = Error, minlevel = Info, append = false, message_limits = Dict{Any, Int}(), flush = true, format = "[{timestamp:func}] {level:func}: {message}", dtformat = dateformat"yyyy-mm-dd HH:MM:SS", flush_threshold = 0, message_mode = Squash(), squash_delimiter = "\t")
tio = getio(io, append)
tioerr = io == ioerr ? tio : getio(ioerr, append)
lastflush = Dates.value(Dates.now())
Expand All @@ -94,6 +96,7 @@ function MiniLogger(; io = stdout, ioerr = stderr, errlevel = Error, minlevel =
tokenize(format),
dtformat,
getmode(message_mode),
squash_delimiter,
getflushthreshold(flush_threshold),
Ref(lastflush))
end
Expand All @@ -106,33 +109,34 @@ min_enabled_level(logger::MiniLogger) = logger.minlevel
catch_exceptions(logger::MiniLogger) = true

# Formatting of values in key value pairs
showvalue(io, msg) = show(io, "text/plain", msg)
function showvalue(io, e::Tuple{Exception,Any})
squash(msg, logger, mode) = string(msg)
function squash(msg, logger, mode::Squash)
smsg = string(msg)
smsg = replace(smsg, "\r" => "")
smsg = replace(smsg, "\n" => logger.squash_delimiter)
end

showvalue(io, msg, logger, mode) = print(io, squash(msg, logger, mode))

function showvalue(io, e::Tuple{Exception,Any}, logger, mode)
ex, bt = e
Base.showerror(io, ex, bt; backtrace = bt!==nothing)
end
showvalue(io, ex::Exception) = Base.showerror(io, ex)
showvalue(io, ex::AbstractVector{Union{Ptr{Nothing}, Base.InterpreterIP}}) = Base.show_backtrace(io, ex)
showvalue(io, ex::Exception, logger, mode) = Base.showerror(io, ex)
showvalue(io, ex::AbstractVector{Union{Ptr{Nothing}, Base.InterpreterIP}}, logger, mode) = Base.show_backtrace(io, ex)

# Here we are fighting with multiple dispatch.
# If message is `Exception` or `Tuple{Exception, Any}` or anything else
# then we want to ignore third argument.
# But if it is any other sort of message we want to dispatch result
# on the type of message transformation
function _showmessage(io, msg, ::Squash)
msglines = split(chomp(string(msg)), '\n')
print(io, replace(msglines[1], "\r" => ""))
for i in 2:length(msglines)
print(io, " ", replace(msglines[i], "\r" => ""))
end
end
_showmessage(io, msg, ::MDown) = show(io, MIME"text/plain"(), Markdown.parse(msg))
_showmessage(io, msg, ::NoTransformations) = print(io, msg)
_showmessage(io, msg, logger, mode) = print(io, squash(msg, logger, mode))
_showmessage(io, msg, logger, ::MDown) = show(io, MIME"text/plain"(), Markdown.parse(msg))

showmessage(io, msg, mode) = _showmessage(io, msg, mode)
showmessage(io, e::Tuple{Exception,Any}, _) = showvalue(io, e)
showmessage(io, ex::Exception, _) = showvalue(io, ex)
showmessage(io, ex::AbstractVector{Union{Ptr{Nothing}, Base.InterpreterIP}}, _) = Base.show_backtrace(io, ex)
showmessage(io, msg, logger, mode) = _showmessage(io, msg, logger, mode)
showmessage(io, e::Tuple{Exception,Any}, logger, mode) = showvalue(io, e, logger, mode)
showmessage(io, ex::Exception, logger, mode) = showvalue(io, ex, logger, mode)
showmessage(io, ex::AbstractVector{Union{Ptr{Nothing}, Base.InterpreterIP}}, logger, mode) = Base.show_backtrace(io, ex)

tsnow(dtf) = Dates.format(Dates.now(), dtf)

Expand Down Expand Up @@ -187,7 +191,7 @@ function handle_message(logger::MiniLogger, level, message, _module, group, id,
if val == "timestamp"
printwcolor(iob, tsnow(logger.dtformat), c)
elseif val == "message"
showmessage(iob, message, logger.mode)
showmessage(iob, message, logger, logger.mode)

iscomma = false
for (key, val) in kwargs
Expand All @@ -197,7 +201,7 @@ function handle_message(logger::MiniLogger, level, message, _module, group, id,
else
iscomma && print(iob, ", ")
print(iob, key, " = ")
showvalue(iob, val)
showvalue(iob, val, logger, logger.mode)
iscomma = true
end
end
Expand Down Expand Up @@ -235,4 +239,3 @@ function handle_message(logger::MiniLogger, level, message, _module, group, id,
end
nothing
end

26 changes: 23 additions & 3 deletions test/test02_loggerformat.jl
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ end
@info "foo\nbar"

s = String(take!(io))
@test s == "foo bar\n"
@test s == "foo\tbar\n"
end

logger = MiniLogger(io = io, format = "{message}", message_mode = :notransformations)
Expand All @@ -45,6 +45,26 @@ end
end
end

@testset "squashing values" begin
io = IOBuffer()
logger = MiniLogger(io = io, format = "{message}", squash_delimiter = "XXX")
val = "foo\nbar"
with_logger(logger) do
@info "" x = val
s = String(take!(io))

@test s == "x = fooXXXbar\n"
end

val = "bar\n\rfoo"
with_logger(logger) do
@info "" x = val
s = String(take!(io))

@test s == "x = barXXXfoo\n"
end
end

@testset "markdown support" begin
io = IOBuffer()
ioc = IOContext(io, :color => true)
Expand All @@ -64,7 +84,7 @@ end
@info "foo\r\nbar"

s = String(take!(io))
@test s == "foo bar\n"
@test s == "foo\tbar\n"
end
end

Expand All @@ -78,7 +98,7 @@ end
@info "values: " x y

s = String(take!(io))
@test s == "values: x = 1, y = \"asd\"\n"
@test s == "values: x = 1, y = asd\n"
end
end

Expand Down

2 comments on commit eb001a3

@Arkoniak
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

@JuliaRegistrator
Copy link

Choose a reason for hiding this comment

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

Registration pull request created: JuliaRegistries/General/42867

After the above pull request is merged, it is recommended that a tag is created on this repository for the registered package version.

This will be done automatically if the Julia TagBot GitHub Action is installed, or can be done manually through the github interface, or via:

git tag -a v0.4.0 -m "<description of version>" eb001a3f8838f2c0722d2414927a74f956057085
git push origin v0.4.0

Please sign in to comment.