From 758ae2ee6e546222fed099390fac2ca2b55cf06b Mon Sep 17 00:00:00 2001 From: Vincent Laugier Date: Mon, 15 Jul 2019 00:11:33 +0800 Subject: [PATCH 01/11] Add MultifilesLogger --- src/FileLogger.jl | 3 ++- src/IOLogging.jl | 4 +++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/src/FileLogger.jl b/src/FileLogger.jl index 7f98365..d1fb6ca 100644 --- a/src/FileLogger.jl +++ b/src/FileLogger.jl @@ -14,7 +14,8 @@ struct FileLogger <: _iologger flush::Bool append::Bool - FileLogger(logPaths::Dict{LogLevel, String} = Dict(Info => "default.log"); flush = true, append = true) = new(logPaths, Dict{LogLevel,IO}(), Dict{Any, Int}(), flush, append) + FileLogger(logPaths::Dict{LogLevel, String} = Dict(Info => "default.log"); flush = true, append = true) = + new(logPaths, Dict{LogLevel,IO}(), Dict{Any, Int}(), flush, append) end CoreLogging.min_enabled_level(logger::FileLogger) = minimum(collect(keys(logger.logPaths))) diff --git a/src/IOLogging.jl b/src/IOLogging.jl index bac34de..c83e3ba 100644 --- a/src/IOLogging.jl +++ b/src/IOLogging.jl @@ -4,7 +4,7 @@ using Base.CoreLogging using Base.CoreLogging: AbstractLogger, LogLevel, Debug, Info, Warn, Error, shouldlog, min_enabled_level, catch_exceptions, handle_message using Dates -export IOLogger, FileLogger +export IOLogger, FileLogger, MultifilesLogger """ Abstract supertype of all loggers contained in this package. @@ -109,5 +109,7 @@ function log!(io::T, level, message, _module, group, file, line; kwargs...) wher end include("FileLogger.jl") +include("MultifilesLogger.jl") + end # module From c1c266646cf4ed04f1e52d6f58f641f06deecc5a Mon Sep 17 00:00:00 2001 From: Vincent Laugier Date: Mon, 15 Jul 2019 00:12:42 +0800 Subject: [PATCH 02/11] Add MultifilesLogger --- src/MultifilesLogger.jl | 126 ++++++++++++++++++++++++++++++ test/runtests-MultifilesLogger.jl | 56 +++++++++++++ 2 files changed, 182 insertions(+) create mode 100644 src/MultifilesLogger.jl create mode 100644 test/runtests-MultifilesLogger.jl diff --git a/src/MultifilesLogger.jl b/src/MultifilesLogger.jl new file mode 100644 index 0000000..963f1c4 --- /dev/null +++ b/src/MultifilesLogger.jl @@ -0,0 +1,126 @@ +""" +A multiple files logger for logging to files depending on the module. +Flushes the necessary output stream after each write (i.e. after each logging event) by default and closes the files on finalizing. Opened files are by default appended to. Given `append = false`, they will be overwritten. +NOTE: Every module must be explicitely declared + + MultifilesLogger( + logs_paths = Dict("first.log" => + [(MyModule1.MySubModule1,Info),(MyModule1.MySubModule2,Info)], + "second.log" => [(MyModule2,Info)], + "Main.log" => [(Main,Info)] + ); + flush = true, append = true) + +Logs logging events with LogLevel greater than or equal to `Info` to "default.log", should no `logPaths` be given. In case two LogLevels are present, e.g. `Info` and `Error`, all logging events from `Info` up to (but excluding) `Error` will be logged to the file given by `Info`. `Error` and above will be logged to the file given by `Error`. It is possible to "clamp" logging events, by providing an upper bound that's logging to `/dev/null` on Unix/Mac or `NUL` on Windows. Beware, as the message will still be composed before writing to the actual file (no hotwiring). + +By default, exceptions occuring during logging are not caught. This is expected to change in the future, once it's decided how exceptions during logging should be handled. +""" +struct MultifilesLogger <: _iologger + logPaths::Dict{String, Vector{Tuple{Module,LogLevel}}} + logIOs::Dict{Module, Tuple{T,LogLevel}} where T <: IO + messageLimits::Dict{Any, Int} + flush::Bool + append::Bool + + MultifilesLogger( + logPaths::Dict{String, Vector{Tuple{Module,LogLevel}}}; + flush = true, + append = true) = (x = new(logPaths, + Dict{Module, Tuple{IO,LogLevel}}(), + Dict{Any, Int}(), + flush, + append); + createIOs!(x); + return x + ) +end + +# CoreLogging.min_enabled_level(logger::MultifilesLogger) = minimum(collect(keys(logger.logPaths))) +CoreLogging.min_enabled_level(logger::MultifilesLogger) = Info + +function createIOs!(logger::MultifilesLogger) + + append_arg = logger.append + logs_paths = logger.logPaths + + for (filepath,v) in logs_paths + # eg. (MyModule1.MySubModule1, Info) + for t in v + logger.logIOs[t[1]] = (open(filepath,append_arg ? "a" : "w"), + t[2] + ) + end + + end + +end + +function getIOLevelTuple(logger::MultifilesLogger, + _module::Module, + Wlevel::LogLevel) + + # If a module has no IO we try to find one + if !haskey(logger.logIOs,_module) + original_module = _module + # This stop condition is because 'parentmodule(Main) == Main' + while parentmodule(_module) != _module + _module = parentmodule(_module) + println("Check if the module[$(_module)] has a logger") + + for k in collect(keys(logger.logIOs)) + println("Is $(_module) == $(k): $(_module == k)") + end + + # If the parent module has a logger we use it + if haskey(logger.logIOs,_module) + println("Found one!") + break + end + end + + # Throw an exception if no logger could be found for the parents + if !haskey(logger.logIOs,_module) + throw(DomainError("There is no logger defined for [$(string(original_module))]" + * " and we were unable to find a logger in the parents modules," + * " not event a logger for the 'Main' module.")) + end + + end + + tuple_io_loglevel = logger.logIOs[_module] + return tuple_io_loglevel + +end + + +CoreLogging.handle_message(logger::MultifilesLogger, + level, + message, + _module, + group, + id, + file, + line; + maxlog = nothing, + kwargs...) = begin + # Should we log this? + if !checkLimits(logger, id, maxlog) + return + end + + + io_level_tuple = getIOLevelTuple(logger, _module, level) + + io = io_level_tuple[1] + loglevel_limit = io_level_tuple[2] + + # Check the the log level of the call is not below the limit + if level < loglevel_limit + return + end + + log!(io, level, string(message), _module, group, file, line; kwargs...) + + logger.flush ? flush(io) : nothing + nothing +end diff --git a/test/runtests-MultifilesLogger.jl b/test/runtests-MultifilesLogger.jl new file mode 100644 index 0000000..51d2a40 --- /dev/null +++ b/test/runtests-MultifilesLogger.jl @@ -0,0 +1,56 @@ +using Pkg +Pkg.activate(".") + +using Revise + +using IOLogging +using Logging +using Logging: Debug, Info, Warn, Error, BelowMinLevel, with_logger, min_enabled_level +using Test + + +# TODO make some real tests sets + +# Declare +module MyModule1 + module MySubModule1 + sayhello() = @info "MyModule1.MySubModule1 sayhello" + + module MySubSubModule1 + end + end + module MySubModule2 + sayhello() = @info "MyModule1.MySubModule2 sayhello" + end +end + +module MyModule2 + module MySubModule1 + sayhello() = @info "MyModule2.MySubModule1 sayhello" + end + module MySubModule2 + sayhello() = @info "MyModule2.MySubModule2 sayhello" + end +end + +parentmodule(MyModule1.MySubModule1.MySubSubModule1) +parentmodule(MyModule1) + +MyModule1.MySubModule1.sayhello() + +# Prepare the configuration +# +logs_paths = Dict("first.log" => + [(MyModule1.MySubModule1,Info),(MyModule1.MySubModule2,Info)], + "second.log" => [(MyModule2,Info)], + "Main.log" => [(Main,Info)] + ) + +multifilesLogger = MultifilesLogger( + logs_paths;flush = true, append = true) + + +global_logger(multifilesLogger) + +MyModule1.MySubModule1.sayhello() +MyModule2.MySubModule1.sayhello() From 120167c38c1da44cbebdd910ee18503033c0b10d Mon Sep 17 00:00:00 2001 From: Vincent Laugier Date: Mon, 15 Jul 2019 01:13:47 +0800 Subject: [PATCH 03/11] First attempt (failed) to create some proper utnit tests for MultifilesLogger --- src/IOLogging.jl | 1 + src/MultifilesLogger.jl | 12 ++------ test/runtests-MultifilesLogger.jl | 50 +++++++++++++++++++++++++++---- 3 files changed, 48 insertions(+), 15 deletions(-) diff --git a/src/IOLogging.jl b/src/IOLogging.jl index c83e3ba..0b3e83c 100644 --- a/src/IOLogging.jl +++ b/src/IOLogging.jl @@ -83,6 +83,7 @@ CoreLogging.handle_message(logger::T, end function log!(io::T, level, message, _module, group, file, line; kwargs...) where { T <: IO } + buffer = IOBuffer() context = IOContext(buffer, io) logTime = now() diff --git a/src/MultifilesLogger.jl b/src/MultifilesLogger.jl index 963f1c4..2045bbb 100644 --- a/src/MultifilesLogger.jl +++ b/src/MultifilesLogger.jl @@ -65,24 +65,18 @@ function getIOLevelTuple(logger::MultifilesLogger, # This stop condition is because 'parentmodule(Main) == Main' while parentmodule(_module) != _module _module = parentmodule(_module) - println("Check if the module[$(_module)] has a logger") - - for k in collect(keys(logger.logIOs)) - println("Is $(_module) == $(k): $(_module == k)") - end # If the parent module has a logger we use it if haskey(logger.logIOs,_module) - println("Found one!") break end end # Throw an exception if no logger could be found for the parents if !haskey(logger.logIOs,_module) - throw(DomainError("There is no logger defined for [$(string(original_module))]" - * " and we were unable to find a logger in the parents modules," - * " not event a logger for the 'Main' module.")) + throw(DomainError("There is no logger defined for module[$(string(original_module))]" + * " and we were also unable to find a logger in the parents modules," + * " not even a logger for the 'Main' module.")) end end diff --git a/test/runtests-MultifilesLogger.jl b/test/runtests-MultifilesLogger.jl index 51d2a40..d654fcc 100644 --- a/test/runtests-MultifilesLogger.jl +++ b/test/runtests-MultifilesLogger.jl @@ -33,11 +33,7 @@ module MyModule2 end end -parentmodule(MyModule1.MySubModule1.MySubSubModule1) -parentmodule(MyModule1) - -MyModule1.MySubModule1.sayhello() - +# # Prepare the configuration # logs_paths = Dict("first.log" => @@ -46,11 +42,53 @@ logs_paths = Dict("first.log" => "Main.log" => [(Main,Info)] ) +# Create the logger and set it as the global logger multifilesLogger = MultifilesLogger( logs_paths;flush = true, append = true) - global_logger(multifilesLogger) + +# First test with a module that is explicitely associated to a log file MyModule1.MySubModule1.sayhello() + +# Test the fallback mechanism when the module is not associated to a log file +# but one of its ancestors is. MyModule2.MySubModule1.sayhello() + + +# Failed attempt to create some unit tests +# mktempdir(@__DIR__) do dir +# cd(dir) do +# @testset "Assertions" begin +# logs_paths = Dict("first.log" => +# [(MyModule1.MySubModule1,Info),(MyModule1.MySubModule2,Info)], +# "second.log" => [(MyModule2,Info)], +# "Main.log" => [(Main,Info)] +# ) +# +# # Create the logger and set it as the global logger +# multifilesLogger = MultifilesLogger( +# logs_paths;flush = true, append = false) +# +# global_logger(multifilesLogger) +# +# # Let the time to create the log files +# sleep(2) +# +# # First test with a module that is explicitely associated to a log file +# MyModule1.MySubModule1.sayhello() +# +# # Test the fallback mechanism when the module is not associated to a log file +# # but one of its ancestors is. +# MyModule2.MySubModule1.sayhello() +# +# # Let the time to go check the content of the log files +# sleep(15) +# +# lines = readlines("first.log", keep = true) +# # println(lines[1]) +# # @test occursin("MyModule1.MySubModule1 sayhello", lines[1]) +# end +# end +# end From 045195830b9288a59a4c7b326abb56f36dae3747 Mon Sep 17 00:00:00 2001 From: Vincent Laugier Date: Mon, 15 Jul 2019 14:30:39 +0800 Subject: [PATCH 04/11] Put the configuration of every log file in a struct 'FileDefForMultifilesLogger'. Every log file now has its own 'append' attribute. --- src/IOLogging.jl | 2 +- src/MultifilesLogger.jl | 48 +++++++++++++++++++------------ test/runtests-MultifilesLogger.jl | 36 ++++++++++++----------- 3 files changed, 50 insertions(+), 36 deletions(-) diff --git a/src/IOLogging.jl b/src/IOLogging.jl index 0b3e83c..348fa8b 100644 --- a/src/IOLogging.jl +++ b/src/IOLogging.jl @@ -4,7 +4,7 @@ using Base.CoreLogging using Base.CoreLogging: AbstractLogger, LogLevel, Debug, Info, Warn, Error, shouldlog, min_enabled_level, catch_exceptions, handle_message using Dates -export IOLogger, FileLogger, MultifilesLogger +export IOLogger, FileLogger, MultifilesLogger, FileDefForMultifilesLogger """ Abstract supertype of all loggers contained in this package. diff --git a/src/MultifilesLogger.jl b/src/MultifilesLogger.jl index 2045bbb..cfb4e46 100644 --- a/src/MultifilesLogger.jl +++ b/src/MultifilesLogger.jl @@ -5,9 +5,9 @@ NOTE: Every module must be explicitely declared MultifilesLogger( logs_paths = Dict("first.log" => - [(MyModule1.MySubModule1,Info),(MyModule1.MySubModule2,Info)], - "second.log" => [(MyModule2,Info)], - "Main.log" => [(Main,Info)] + [(MyModule1.MySubModule1, Info, true),(MyModule1.MySubModule2, Info, true)], + "second.log" => [(MyModule2, Info, true)], + "Main.log" => [(Main, Info, true)] ); flush = true, append = true) @@ -15,21 +15,31 @@ Logs logging events with LogLevel greater than or equal to `Info` to "default.lo By default, exceptions occuring during logging are not caught. This is expected to change in the future, once it's decided how exceptions during logging should be handled. """ + +struct FileDefForMultifilesLogger + + filePath::String + append::Bool + modulesAndLogLevels::Vector{Tuple{Module,LogLevel}} + + # FileDefForMultifilesLogger(filePath::String, + # append::Bool, + # vectorsAndLogLevels::Vector{Tuple{Module,LogLevel}}) = + +end + struct MultifilesLogger <: _iologger - logPaths::Dict{String, Vector{Tuple{Module,LogLevel}}} + filesDefs::Vector{FileDefForMultifilesLogger} logIOs::Dict{Module, Tuple{T,LogLevel}} where T <: IO messageLimits::Dict{Any, Int} flush::Bool - append::Bool MultifilesLogger( - logPaths::Dict{String, Vector{Tuple{Module,LogLevel}}}; - flush = true, - append = true) = (x = new(logPaths, - Dict{Module, Tuple{IO,LogLevel}}(), + filesDefs::Vector{FileDefForMultifilesLogger}; + flush = true) = (x = new(filesDefs, + Dict{Module, Tuple{IO, LogLevel}}(), Dict{Any, Int}(), - flush, - append); + flush); createIOs!(x); return x ) @@ -40,15 +50,15 @@ CoreLogging.min_enabled_level(logger::MultifilesLogger) = Info function createIOs!(logger::MultifilesLogger) - append_arg = logger.append - logs_paths = logger.logPaths + for fileDef in logger.filesDefs - for (filepath,v) in logs_paths - # eg. (MyModule1.MySubModule1, Info) - for t in v - logger.logIOs[t[1]] = (open(filepath,append_arg ? "a" : "w"), - t[2] - ) + # Open one IO per Tuple{Module,LogLevel} + # NOTE: there can be several IOs pointing to the same file + for t in fileDef.modulesAndLogLevels + _module = t[1] + _loglevel = t[2] + logger.logIOs[_module] = (open(fileDef.filePath, fileDef.append ? "a" : "w"), + _loglevel) end end diff --git a/test/runtests-MultifilesLogger.jl b/test/runtests-MultifilesLogger.jl index d654fcc..d3bfe53 100644 --- a/test/runtests-MultifilesLogger.jl +++ b/test/runtests-MultifilesLogger.jl @@ -34,17 +34,26 @@ module MyModule2 end # -# Prepare the configuration +# Configure the log files # -logs_paths = Dict("first.log" => - [(MyModule1.MySubModule1,Info),(MyModule1.MySubModule2,Info)], - "second.log" => [(MyModule2,Info)], - "Main.log" => [(Main,Info)] - ) +fileDef1 = FileDefForMultifilesLogger("first.log", + true, # append + [(MyModule1.MySubModule1,Info),(MyModule1.MySubModule2,Info)], + ) + +fileDef2 = FileDefForMultifilesLogger("second.log", + true, # append + [(MyModule2,Info)], + ) + +fileDef3 = FileDefForMultifilesLogger("main.log", + true, # append + [(Main,Info)], + ) # Create the logger and set it as the global logger -multifilesLogger = MultifilesLogger( - logs_paths;flush = true, append = true) +multifilesLogger = + MultifilesLogger([fileDef1,fileDef2,fileDef3]) global_logger(multifilesLogger) @@ -61,15 +70,10 @@ MyModule2.MySubModule1.sayhello() # mktempdir(@__DIR__) do dir # cd(dir) do # @testset "Assertions" begin -# logs_paths = Dict("first.log" => -# [(MyModule1.MySubModule1,Info),(MyModule1.MySubModule2,Info)], -# "second.log" => [(MyModule2,Info)], -# "Main.log" => [(Main,Info)] -# ) -# +# # # # Create the logger and set it as the global logger -# multifilesLogger = MultifilesLogger( -# logs_paths;flush = true, append = false) +# multifilesLogger = +# MultifilesLogger([fileDef1,fileDef2,fileDef3]) # # global_logger(multifilesLogger) # From fc41f03e98531480879f3a346089edd18e44d699 Mon Sep 17 00:00:00 2001 From: Vincent Laugier Date: Mon, 15 Jul 2019 14:35:26 +0800 Subject: [PATCH 05/11] Add a test --- test/runtests-MultifilesLogger.jl | 2 ++ 1 file changed, 2 insertions(+) diff --git a/test/runtests-MultifilesLogger.jl b/test/runtests-MultifilesLogger.jl index d3bfe53..88d90ad 100644 --- a/test/runtests-MultifilesLogger.jl +++ b/test/runtests-MultifilesLogger.jl @@ -65,6 +65,8 @@ MyModule1.MySubModule1.sayhello() # but one of its ancestors is. MyModule2.MySubModule1.sayhello() +# Test the logging for the 'Main' module +@info "Should appear in main.log" # Failed attempt to create some unit tests # mktempdir(@__DIR__) do dir From 60d7d083a0d421baa1e95b9d9357d95f2634fb5e Mon Sep 17 00:00:00 2001 From: Vincent Laugier Date: Mon, 15 Jul 2019 14:51:14 +0800 Subject: [PATCH 06/11] Mechanism to update the 'logIOs' property in case a module falls back to the IO of a parent module --- src/MultifilesLogger.jl | 43 +++++++++++++++++++++++++++-------------- 1 file changed, 28 insertions(+), 15 deletions(-) diff --git a/src/MultifilesLogger.jl b/src/MultifilesLogger.jl index cfb4e46..d9550df 100644 --- a/src/MultifilesLogger.jl +++ b/src/MultifilesLogger.jl @@ -1,23 +1,32 @@ """ A multiple files logger for logging to files depending on the module. -Flushes the necessary output stream after each write (i.e. after each logging event) by default and closes the files on finalizing. Opened files are by default appended to. Given `append = false`, they will be overwritten. -NOTE: Every module must be explicitely declared - MultifilesLogger( - logs_paths = Dict("first.log" => - [(MyModule1.MySubModule1, Info, true),(MyModule1.MySubModule2, Info, true)], - "second.log" => [(MyModule2, Info, true)], - "Main.log" => [(Main, Info, true)] - ); - flush = true, append = true) + fileDef1 = FileDefForMultifilesLogger("first.log", + true, # append + [(MyModule1.MySubModule1,Info),(MyModule1.MySubModule2,Info)], + ) + + fileDef2 = FileDefForMultifilesLogger("second.log", + true, # append + [(MyModule2,Info)], + ) + + fileDef3 = FileDefForMultifilesLogger("main.log", + true, # append + [(Main,Info)], + ) + + # Create the logger and set it as the global logger + multifilesLogger = + MultifilesLogger([fileDef1,fileDef2,fileDef3]) + + global_logger(multifilesLogger) -Logs logging events with LogLevel greater than or equal to `Info` to "default.log", should no `logPaths` be given. In case two LogLevels are present, e.g. `Info` and `Error`, all logging events from `Info` up to (but excluding) `Error` will be logged to the file given by `Info`. `Error` and above will be logged to the file given by `Error`. It is possible to "clamp" logging events, by providing an upper bound that's logging to `/dev/null` on Unix/Mac or `NUL` on Windows. Beware, as the message will still be composed before writing to the actual file (no hotwiring). -By default, exceptions occuring during logging are not caught. This is expected to change in the future, once it's decided how exceptions during logging should be handled. """ struct FileDefForMultifilesLogger - + filePath::String append::Bool modulesAndLogLevels::Vector{Tuple{Module,LogLevel}} @@ -65,7 +74,7 @@ function createIOs!(logger::MultifilesLogger) end -function getIOLevelTuple(logger::MultifilesLogger, +function getIOLevelTuple!(logger::MultifilesLogger, _module::Module, Wlevel::LogLevel) @@ -76,8 +85,12 @@ function getIOLevelTuple(logger::MultifilesLogger, while parentmodule(_module) != _module _module = parentmodule(_module) - # If the parent module has a logger we use it + # If the parent module has an IO we use it and we update the + # logger.logIOs so that the unreferenced module now points to the + # same IO as the parent module. This is done for performance if haskey(logger.logIOs,_module) + # println("Update logger.logIOs") + logger.logIOs[original_module] = logger.logIOs[_module] break end end @@ -113,7 +126,7 @@ CoreLogging.handle_message(logger::MultifilesLogger, end - io_level_tuple = getIOLevelTuple(logger, _module, level) + io_level_tuple = getIOLevelTuple!(logger, _module, level) io = io_level_tuple[1] loglevel_limit = io_level_tuple[2] From 955cdc27d879b79bc0afe1625f1900a8fb568f97 Mon Sep 17 00:00:00 2001 From: Vincent Laugier Date: Mon, 15 Jul 2019 15:08:39 +0800 Subject: [PATCH 07/11] Some cleaning --- src/MultifilesLogger.jl | 21 ++++++++------------- 1 file changed, 8 insertions(+), 13 deletions(-) diff --git a/src/MultifilesLogger.jl b/src/MultifilesLogger.jl index d9550df..b87a61a 100644 --- a/src/MultifilesLogger.jl +++ b/src/MultifilesLogger.jl @@ -24,19 +24,6 @@ A multiple files logger for logging to files depending on the module. """ - -struct FileDefForMultifilesLogger - - filePath::String - append::Bool - modulesAndLogLevels::Vector{Tuple{Module,LogLevel}} - - # FileDefForMultifilesLogger(filePath::String, - # append::Bool, - # vectorsAndLogLevels::Vector{Tuple{Module,LogLevel}}) = - -end - struct MultifilesLogger <: _iologger filesDefs::Vector{FileDefForMultifilesLogger} logIOs::Dict{Module, Tuple{T,LogLevel}} where T <: IO @@ -54,6 +41,14 @@ struct MultifilesLogger <: _iologger ) end +struct FileDefForMultifilesLogger + + filePath::String + append::Bool + modulesAndLogLevels::Vector{Tuple{Module,LogLevel}} + +end + # CoreLogging.min_enabled_level(logger::MultifilesLogger) = minimum(collect(keys(logger.logPaths))) CoreLogging.min_enabled_level(logger::MultifilesLogger) = Info From 1764744b7f8e6a2336a0a1a656c577dbdbc486d1 Mon Sep 17 00:00:00 2001 From: Vincent Laugier Date: Mon, 15 Jul 2019 15:47:31 +0800 Subject: [PATCH 08/11] bugfix --- src/MultifilesLogger.jl | 12 ++++++------ test/runtests-MultifilesLogger.jl | 1 - 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/src/MultifilesLogger.jl b/src/MultifilesLogger.jl index b87a61a..1def133 100644 --- a/src/MultifilesLogger.jl +++ b/src/MultifilesLogger.jl @@ -1,3 +1,9 @@ +struct FileDefForMultifilesLogger + filePath::String + append::Bool + modulesAndLogLevels::Vector{Tuple{Module,LogLevel}} +end + """ A multiple files logger for logging to files depending on the module. @@ -41,13 +47,7 @@ struct MultifilesLogger <: _iologger ) end -struct FileDefForMultifilesLogger - - filePath::String - append::Bool - modulesAndLogLevels::Vector{Tuple{Module,LogLevel}} -end # CoreLogging.min_enabled_level(logger::MultifilesLogger) = minimum(collect(keys(logger.logPaths))) CoreLogging.min_enabled_level(logger::MultifilesLogger) = Info diff --git a/test/runtests-MultifilesLogger.jl b/test/runtests-MultifilesLogger.jl index 88d90ad..bd0c7b3 100644 --- a/test/runtests-MultifilesLogger.jl +++ b/test/runtests-MultifilesLogger.jl @@ -8,7 +8,6 @@ using Logging using Logging: Debug, Info, Warn, Error, BelowMinLevel, with_logger, min_enabled_level using Test - # TODO make some real tests sets # Declare From 7c271e09ef4221fcbd2f7883150bb2d98ecbb6b7 Mon Sep 17 00:00:00 2001 From: Vincent Laugier Date: Tue, 16 Jul 2019 17:52:37 +0800 Subject: [PATCH 09/11] Remove using 'Revise' Remove some comments --- test/runtests-MultifilesLogger.jl | 33 ------------------------------- 1 file changed, 33 deletions(-) diff --git a/test/runtests-MultifilesLogger.jl b/test/runtests-MultifilesLogger.jl index bd0c7b3..24fce60 100644 --- a/test/runtests-MultifilesLogger.jl +++ b/test/runtests-MultifilesLogger.jl @@ -1,8 +1,6 @@ using Pkg Pkg.activate(".") -using Revise - using IOLogging using Logging using Logging: Debug, Info, Warn, Error, BelowMinLevel, with_logger, min_enabled_level @@ -66,34 +64,3 @@ MyModule2.MySubModule1.sayhello() # Test the logging for the 'Main' module @info "Should appear in main.log" - -# Failed attempt to create some unit tests -# mktempdir(@__DIR__) do dir -# cd(dir) do -# @testset "Assertions" begin -# # -# # Create the logger and set it as the global logger -# multifilesLogger = -# MultifilesLogger([fileDef1,fileDef2,fileDef3]) -# -# global_logger(multifilesLogger) -# -# # Let the time to create the log files -# sleep(2) -# -# # First test with a module that is explicitely associated to a log file -# MyModule1.MySubModule1.sayhello() -# -# # Test the fallback mechanism when the module is not associated to a log file -# # but one of its ancestors is. -# MyModule2.MySubModule1.sayhello() -# -# # Let the time to go check the content of the log files -# sleep(15) -# -# lines = readlines("first.log", keep = true) -# # println(lines[1]) -# # @test occursin("MyModule1.MySubModule1 sayhello", lines[1]) -# end -# end -# end From 3ca61a81cd62d311bf6124d012129782ee6abae5 Mon Sep 17 00:00:00 2001 From: Vincent Laugier Date: Sat, 27 Jul 2019 23:27:42 +0800 Subject: [PATCH 10/11] - Reset FileLogger.jl to the state of the main branch. - Move the 'flush' property from 'MultifilesLogger' to FileDefForMultifilesLogger - Do not throw an error if we cannot find an IO for a given module --- src/FileLogger.jl | 3 +- src/MultifilesLogger.jl | 51 ++++++++++++++++++------------- test/runtests-MultifilesLogger.jl | 23 ++++++++++---- 3 files changed, 48 insertions(+), 29 deletions(-) diff --git a/src/FileLogger.jl b/src/FileLogger.jl index d1fb6ca..7f98365 100644 --- a/src/FileLogger.jl +++ b/src/FileLogger.jl @@ -14,8 +14,7 @@ struct FileLogger <: _iologger flush::Bool append::Bool - FileLogger(logPaths::Dict{LogLevel, String} = Dict(Info => "default.log"); flush = true, append = true) = - new(logPaths, Dict{LogLevel,IO}(), Dict{Any, Int}(), flush, append) + FileLogger(logPaths::Dict{LogLevel, String} = Dict(Info => "default.log"); flush = true, append = true) = new(logPaths, Dict{LogLevel,IO}(), Dict{Any, Int}(), flush, append) end CoreLogging.min_enabled_level(logger::FileLogger) = minimum(collect(keys(logger.logPaths))) diff --git a/src/MultifilesLogger.jl b/src/MultifilesLogger.jl index 1def133..d1da9cc 100644 --- a/src/MultifilesLogger.jl +++ b/src/MultifilesLogger.jl @@ -1,7 +1,16 @@ struct FileDefForMultifilesLogger filePath::String - append::Bool modulesAndLogLevels::Vector{Tuple{Module,LogLevel}} + flush::Bool + append::Bool + + FileDefForMultifilesLogger(filePath::String, + modulesAndLogLevels::Vector{Tuple{Module,LogLevel}} + ;flush::Bool = true, + append::Bool = true) = new(filePath, + modulesAndLogLevels, + flush, + append) end """ @@ -32,19 +41,18 @@ A multiple files logger for logging to files depending on the module. """ struct MultifilesLogger <: _iologger filesDefs::Vector{FileDefForMultifilesLogger} - logIOs::Dict{Module, Tuple{T,LogLevel}} where T <: IO + logIOs::Dict{Module, Tuple{T, LogLevel, Bool}} where T <: IO # The Bool attribute of + # the Tuple sets whether or + # not we flush after logging + # a message messageLimits::Dict{Any, Int} - flush::Bool - MultifilesLogger( - filesDefs::Vector{FileDefForMultifilesLogger}; - flush = true) = (x = new(filesDefs, - Dict{Module, Tuple{IO, LogLevel}}(), - Dict{Any, Int}(), - flush); - createIOs!(x); - return x - ) + MultifilesLogger(filesDefs::Vector{FileDefForMultifilesLogger}) = + (x = new(filesDefs, + Dict{Module, Tuple{IO, LogLevel, Bool}}(), + Dict{Any, Int}()); + createIOs!(x); + return x) end @@ -62,7 +70,8 @@ function createIOs!(logger::MultifilesLogger) _module = t[1] _loglevel = t[2] logger.logIOs[_module] = (open(fileDef.filePath, fileDef.append ? "a" : "w"), - _loglevel) + _loglevel, + fileDef.flush) end end @@ -76,7 +85,7 @@ function getIOLevelTuple!(logger::MultifilesLogger, # If a module has no IO we try to find one if !haskey(logger.logIOs,_module) original_module = _module - # This stop condition is because 'parentmodule(Main) == Main' + # This 'stop' condition is because 'parentmodule(Main) == Main' while parentmodule(_module) != _module _module = parentmodule(_module) @@ -90,12 +99,8 @@ function getIOLevelTuple!(logger::MultifilesLogger, end end - # Throw an exception if no logger could be found for the parents - if !haskey(logger.logIOs,_module) - throw(DomainError("There is no logger defined for module[$(string(original_module))]" - * " and we were also unable to find a logger in the parents modules," - * " not even a logger for the 'Main' module.")) - end + # If nothing was found we return missing + return missing end @@ -122,9 +127,13 @@ CoreLogging.handle_message(logger::MultifilesLogger, io_level_tuple = getIOLevelTuple!(logger, _module, level) + if ismissing(io_level_tuple) + return + end io = io_level_tuple[1] loglevel_limit = io_level_tuple[2] + _flush = io_level_tuple[3] # Check the the log level of the call is not below the limit if level < loglevel_limit @@ -133,6 +142,6 @@ CoreLogging.handle_message(logger::MultifilesLogger, log!(io, level, string(message), _module, group, file, line; kwargs...) - logger.flush ? flush(io) : nothing + _flush ? flush(io) : nothing nothing end diff --git a/test/runtests-MultifilesLogger.jl b/test/runtests-MultifilesLogger.jl index 24fce60..f24f49d 100644 --- a/test/runtests-MultifilesLogger.jl +++ b/test/runtests-MultifilesLogger.jl @@ -30,22 +30,25 @@ module MyModule2 end end +module MyModule3 + sayhello() = @info "MyModule3 sayhello" +end + # # Configure the log files # fileDef1 = FileDefForMultifilesLogger("first.log", - true, # append - [(MyModule1.MySubModule1,Info),(MyModule1.MySubModule2,Info)], + [(MyModule1.MySubModule1,Info), + (MyModule1.MySubModule2,Info)], ) fileDef2 = FileDefForMultifilesLogger("second.log", - true, # append [(MyModule2,Info)], ) fileDef3 = FileDefForMultifilesLogger("main.log", - true, # append - [(Main,Info)], + [(Main,Info)]; + append = false ) # Create the logger and set it as the global logger @@ -54,7 +57,6 @@ multifilesLogger = global_logger(multifilesLogger) - # First test with a module that is explicitely associated to a log file MyModule1.MySubModule1.sayhello() @@ -64,3 +66,12 @@ MyModule2.MySubModule1.sayhello() # Test the logging for the 'Main' module @info "Should appear in main.log" + +# Test what happens when there is no IO on the Main module and that a module +# is missing its IO +multifilesLogger = + MultifilesLogger([fileDef1,fileDef2]) + +global_logger(multifilesLogger) + +MyModule3.sayhello() From 25b7e26b7ce9940414cd76e2ad46d094f39b7add Mon Sep 17 00:00:00 2001 From: Vincent Laugier Date: Sat, 27 Jul 2019 23:31:43 +0800 Subject: [PATCH 11/11] Change the documentation of the struct --- src/MultifilesLogger.jl | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/MultifilesLogger.jl b/src/MultifilesLogger.jl index d1da9cc..3faea47 100644 --- a/src/MultifilesLogger.jl +++ b/src/MultifilesLogger.jl @@ -17,18 +17,16 @@ end A multiple files logger for logging to files depending on the module. fileDef1 = FileDefForMultifilesLogger("first.log", - true, # append [(MyModule1.MySubModule1,Info),(MyModule1.MySubModule2,Info)], ) fileDef2 = FileDefForMultifilesLogger("second.log", - true, # append [(MyModule2,Info)], ) fileDef3 = FileDefForMultifilesLogger("main.log", - true, # append - [(Main,Info)], + [(Main,Info)]; + append = false ) # Create the logger and set it as the global logger