Skip to content

Commit

Permalink
feat: log level names (#42)
Browse files Browse the repository at this point in the history
  • Loading branch information
Andrey Oskin committed Feb 19, 2024
1 parent e0dc981 commit ff8ebbc
Show file tree
Hide file tree
Showing 4 changed files with 56 additions and 9 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 and contributors"]
version = "0.5.2"
version = "0.5.3"

[deps]
Dates = "ade2ca70-3891-5945-98fb-dc099432e06a"
Expand Down
11 changes: 7 additions & 4 deletions src/jsonlogger.jl
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
struct JsonLogger{AM <: AbstractMode, IOT1 <: IO, IOT2 <: IO, DFT <: DateFormat} <: AbstractMiniLogger
struct JsonLogger{AM <: AbstractMode, IOT1 <: IO, IOT2 <: IO, DFT <: DateFormat, F} <: AbstractMiniLogger
io::IOT1
ioerr::IOT2
errlevel::LogLevel
Expand All @@ -12,6 +12,7 @@ struct JsonLogger{AM <: AbstractMode, IOT1 <: IO, IOT2 <: IO, DFT <: DateFormat}
flush_threshold::Int
lastflush::Base.RefValue{Int64}
lock::ReentrantLock # thread-safety of message_limits Dict
levelname::F
end

"""
Expand All @@ -29,6 +30,7 @@ Supported keyword arguments include:
* `squash_delimiter`: (default: "\\t"): defines which delimiter to use when squashing multilines messages.
* `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.
* `levelname` (default `string`): allows to redefine output of log level names. Should be function of the form `levelname(level::LogLevel)::String`
* `format`: defines which keywords should be used in the output. If defined, should be a string which defines the structure of the output json. It should use keywords, and allowed keywords are:
* `timestamp`: timestamp of the log message
* `level`: name of log level (Debug, Info, etc)
Expand All @@ -44,7 +46,7 @@ Format string should consists of comma separated tokens. In it's simplest form,
By default, `format` is `timestamp,level,basename,line,message`.
"""
function JsonLogger(; io = stdout, ioerr = stderr, errlevel = Error, minlevel = Info, append = false, message_limits = Dict{Any, Int}(), flush = true, format = "timestamp,level,basename,line,message", dtformat = dateformat"yyyy-mm-dd HH:MM:SS", flush_threshold = 0, squash_delimiter = "\t")
function JsonLogger(; io = stdout, ioerr = stderr, errlevel = Error, minlevel = Info, append = false, message_limits = Dict{Any, Int}(), flush = true, format = "timestamp,level,basename,line,message", dtformat = dateformat"yyyy-mm-dd HH:MM:SS", flush_threshold = 0, squash_delimiter = "\t", levelname = string)
tio = getio(io, append)
tioerr = io == ioerr ? tio : getio(ioerr, append)
lastflush = Dates.value(Dates.now())
Expand All @@ -60,7 +62,8 @@ function JsonLogger(; io = stdout, ioerr = stderr, errlevel = Error, minlevel =
squash_delimiter,
getflushthreshold(flush_threshold),
Ref(lastflush),
ReentrantLock())
ReentrantLock(),
levelname)
end

function handle_message(logger::JsonLogger, level, message, _module, group, id,
Expand All @@ -87,7 +90,7 @@ function handle_message(logger::JsonLogger, level, message, _module, group, id,
if val == "timestamp"
print(iob, "\"", tsnow(logger.dtformat), "\"")
elseif val == "level"
print(iob, "\"", string(level), "\"")
print(iob, "\"", logger.levelname(level), "\"")
elseif val == "filepath"
print(iob, "\"", filepath, "\"")
elseif val == "basename"
Expand Down
11 changes: 7 additions & 4 deletions src/minilogger.jl
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
struct MiniLogger{AM <: AbstractMode, IOT1 <: IO, IOT2 <: IO, DFT <: DateFormat} <: AbstractMiniLogger
struct MiniLogger{AM <: AbstractMode, IOT1 <: IO, IOT2 <: IO, DFT <: DateFormat, F} <: AbstractMiniLogger
io::IOT1
ioerr::IOT2
errlevel::LogLevel
Expand All @@ -12,6 +12,7 @@ struct MiniLogger{AM <: AbstractMode, IOT1 <: IO, IOT2 <: IO, DFT <: DateFormat}
flush_threshold::Int
lastflush::Base.RefValue{Int64}
lock::ReentrantLock # thread-safety of message_limits Dict
levelname::F
end

getmode(mode) = mode
Expand Down Expand Up @@ -49,6 +50,7 @@ Supported keyword arguments include:
* `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.
* `levelname` (default `string`): allows to redefine output of log level names. Should be function of the form `levelname(level::LogLevel)::String`
* `format` (default: "[{timestamp:func}] {level:func}: {message}"): format for output log message. It accepts following keywords, which should be provided in curly brackets:
* `timestamp`: timestamp of the log message
* `level`: name of log level (Debug, Info, etc)
Expand All @@ -66,7 +68,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(), squash_delimiter = "\t")
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", levelname = string)
tio = getio(io, append)
tioerr = io == ioerr ? tio : getio(ioerr, append)
lastflush = Dates.value(Dates.now())
Expand All @@ -82,7 +84,8 @@ function MiniLogger(; io = stdout, ioerr = stderr, errlevel = Error, minlevel =
squash_delimiter,
getflushthreshold(flush_threshold),
Ref(lastflush),
ReentrantLock())
ReentrantLock(),
levelname)
end


Expand Down Expand Up @@ -156,7 +159,7 @@ function handle_message(logger::MiniLogger, level, message, _module, group, id,
end
end
elseif val == "level"
levelstr = string(level)
levelstr = logger.levelname(level)
printwcolor(iob, levelstr, c)
elseif val == "basename"
printwcolor(iob, basename(filepath), c)
Expand Down
41 changes: 41 additions & 0 deletions test/test05_levelnames.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
module LevelNamesTest
using MiniLoggers
using Logging: LogLevel, BelowMinLevel, Debug, Info, Warn, Error, AboveMaxLevel
using ReTest

function levelname(level::LogLevel)
if level == Debug "MYDEBUG"
elseif level == Info "MYINFO"
else "LOG"
end
end

@testset "MiniLogger Level Names" begin
buf = IOBuffer()
iob = IOContext(buf, stdout)

with_logger(MiniLogger(io = iob, minlevel = MiniLoggers.Debug, format = "level", levelname = levelname)) do
@info ""
@debug ""
@warn ""
end

s = String(take!(buf))
@test s == "MYINFO\nMYDEBUG\nLOG\n"
end

@testset "JsonLogger Level Names" begin
buf = IOBuffer()
iob = IOContext(buf, stdout)

with_logger(JsonLogger(io = iob, minlevel = MiniLoggers.Debug, format = "level", levelname = levelname)) do
@info ""
@debug ""
@warn ""
end

s = String(take!(buf))
@test s == "{\"level\":\"MYINFO\"}\n{\"level\":\"MYDEBUG\"}\n{\"level\":\"LOG\"}\n"
end

end # module

0 comments on commit ff8ebbc

Please sign in to comment.