diff --git a/README.md b/README.md index 50cb55a..ab09cd1 100644 --- a/README.md +++ b/README.md @@ -79,6 +79,7 @@ This package introduces 6 new loggers. The `TeeLogger`, the `TransformerLogger`, 3 types of filtered logger, and the `FileLogger`. All of them just wrap existing loggers. - The `TeeLogger` sends the logs to multiple different loggers. + - The `FirstMatchLogger` also sends the logs to multiple different loggers but to only at most one logger for each log. - The `TransformerLogger` applies a function to modify log messages before passing them on. - The 3 filter loggers are used to control if a message is written or not - The `MinLevelLogger` only allowes messages to pass that are above a given level of severity @@ -100,6 +101,12 @@ It is up to those loggers to determine if they will accept it. Which they do using their methods for `shouldlog` and `min_enabled_level`. Or you can do, by wrapping them in a filtered logger as discussed below. +## `FirstMatchLogger` + +The `FirstMatchLogger` sends the log messages to multiple places, like `TeeLogger`. +Unlike, `TeeLogger`, it only sends a log to at most one logger that can handle the +log message. The first match in the list of loggers is used. + ## `FileLogger` The `FileLogger` does logging to file. It is just a convience wrapper around the base julia `SimpleLogger`, diff --git a/src/LoggingExtras.jl b/src/LoggingExtras.jl index 68d8e14..e85a30c 100644 --- a/src/LoggingExtras.jl +++ b/src/LoggingExtras.jl @@ -8,7 +8,7 @@ import Base.CoreLogging: AbstractLogger, SimpleLogger, handle_message, shouldlog, min_enabled_level, catch_exceptions -export TeeLogger, TransformerLogger, FileLogger, +export TeeLogger, FirstMatchLogger, TransformerLogger, FileLogger, ActiveFilteredLogger, EarlyFilteredLogger, MinLevelLogger diff --git a/src/tee.jl b/src/tee.jl index ba46409..0687286 100644 --- a/src/tee.jl +++ b/src/tee.jl @@ -1,4 +1,6 @@ -struct TeeLogger{T<:NTuple{<:Any, AbstractLogger}} <: AbstractLogger +abstract type AbstractTeeLogger <: AbstractLogger end + +struct TeeLogger{T<:NTuple{<:Any, AbstractLogger}} <: AbstractTeeLogger loggers::T end @@ -26,14 +28,38 @@ function handle_message(demux::TeeLogger, args...; kwargs...) end end -function shouldlog(demux::TeeLogger, args...) +function shouldlog(demux::AbstractTeeLogger, args...) any(comp_shouldlog(logger, args...) for logger in demux.loggers) end -function min_enabled_level(demux::TeeLogger) +function min_enabled_level(demux::AbstractTeeLogger) minimum(min_enabled_level(logger) for logger in demux.loggers) end -function catch_exceptions(demux::TeeLogger) +function catch_exceptions(demux::AbstractTeeLogger) any(catch_exceptions(logger) for logger in demux.loggers) end + + +struct FirstMatchLogger{T<:NTuple{<:Any,AbstractLogger}} <: AbstractTeeLogger + loggers::T +end + +""" + FirstMatchLogger(loggers...) + +For each log message, invoke the first logger in `loggers` that +matches with it; i.e., `min_enabled_level` is less than or equal to +the log level and `shouldlog` returns `true`. +""" +function FirstMatchLogger(loggers::Vararg{AbstractLogger}) + return FirstMatchLogger(loggers) +end + +function handle_message(logger::FirstMatchLogger, args...; kwargs...) + for logger in logger.loggers + if comp_handle_message_check(logger, args...; kwargs...) + return handle_message(logger, args...; kwargs...) + end + end +end diff --git a/test/runtests.jl b/test/runtests.jl index 0fe61e0..46f71cc 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -31,6 +31,31 @@ using Base.CoreLogging: BelowMinLevel, Debug, Info, Warn, Error end +@testset "FirstMatch" begin + @testset "contructor" begin + @testset "mixed types" begin + @test FirstMatchLogger(TestLogger(), NullLogger()) isa FirstMatchLogger + end + + @testset "errors if given nonloggers" begin + @test_throws Exception FirstMatchLogger(stdout, stderr) + end + end + @testset "basic use with compositional levels" begin + testlogger_info = TestLogger(min_level=Info) + testlogger_warn = TestLogger(min_level=Warn) + + with_logger(FirstMatchLogger(testlogger_warn, testlogger_info)) do + @info "info1" + @warn "warn1" + @info "info2" + end + @test length(testlogger_info.logs) == 2 + @test length(testlogger_warn.logs) == 1 + end +end + + @testset "File" begin mktempdir() do dir filepath = joinpath(dir, "log")