diff --git a/Project.toml b/Project.toml index 2d1d938..09b08d0 100644 --- a/Project.toml +++ b/Project.toml @@ -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" diff --git a/src/jsonlogger.jl b/src/jsonlogger.jl index 127829e..f8e5789 100644 --- a/src/jsonlogger.jl +++ b/src/jsonlogger.jl @@ -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 @@ -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 """ @@ -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) @@ -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()) @@ -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, @@ -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" diff --git a/src/minilogger.jl b/src/minilogger.jl index f5f4594..1384762 100644 --- a/src/minilogger.jl +++ b/src/minilogger.jl @@ -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 @@ -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 @@ -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) @@ -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()) @@ -82,7 +84,8 @@ function MiniLogger(; io = stdout, ioerr = stderr, errlevel = Error, minlevel = squash_delimiter, getflushthreshold(flush_threshold), Ref(lastflush), - ReentrantLock()) + ReentrantLock(), + levelname) end @@ -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) diff --git a/test/test05_levelnames.jl b/test/test05_levelnames.jl new file mode 100644 index 0000000..3e9618b --- /dev/null +++ b/test/test05_levelnames.jl @@ -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