diff --git a/NEWS.md b/NEWS.md index d5ef405b7a913..ab5410ff1014f 100644 --- a/NEWS.md +++ b/NEWS.md @@ -116,6 +116,11 @@ Standard library changes argument is the output of `bunchkaufman` or `lu` ([#50471]). * Structured matrices now retain either the axes of the parent (for `Symmetric`/`Hermitian`/`AbstractTriangular`/`UpperHessenberg`), or that of the principal diagonal (for banded matrices) ([#52480]). +#### Logging +* New `@create_log_macro` macro for creating new log macros like `@info`, `@warn` etc. For instance + `@create_log_macro MyLog 1500 :magenta` will create `@mylog` to be used like `@mylog "hello"` which + will show as `┌ MyLog: hello` etc. ([#52196]) + #### Printf #### Profile diff --git a/base/logging.jl b/base/logging.jl index 2d0f27dcff57a..4f20975c06982 100644 --- a/base/logging.jl +++ b/base/logging.jl @@ -162,12 +162,10 @@ const AboveMaxLevel = LogLevel( 1000001) # Global log limiting mechanism for super fast but inflexible global log limiting. const _min_enabled_level = Ref{LogLevel}(Debug) -# add to this to dict to introduce a log level for printing -# i.e. custom_log_levels[LogLevel(-500)] = ("MyLog", :magenta) -const custom_log_levels = Dict{LogLevel,Tuple{String,Symbol}}() +const custom_log_levels = Dict{LogLevel,Tuple{Symbol,Union{Symbol,Int}}}() function show(io::IO, level::LogLevel) - if level in keys(custom_log_levels) print(io, custom_log_levels[level][1]::String) + if level in keys(custom_log_levels) print(io, custom_log_levels[level][1]) elseif level == BelowMinLevel print(io, "BelowMinLevel") elseif level == Debug print(io, "Debug") elseif level == Info print(io, "Info") @@ -694,4 +692,33 @@ end _global_logstate = LogState(SimpleLogger()) +""" + @create_log_macro(name::Symbol, level::Int, color::Union{Int,Symbol}) + +Creates a custom log macro like `@info`, `@warn` etc. with a given `name`, `level` and +`color`. The macro created is named with the lowercase form of `name` but the given form +is used for the printing. + +See `Base.text_colors` for recognized color values. + +```julia-repl +julia> @create_log_macro(:MyLog, 200, :magenta) +@mylog (macro with 1 method) + +julia> @mylog "hello" +[ MyLog: hello +``` +""" +macro create_log_macro(name, level, color) + macro_name = Symbol(lowercase(string(name))) + macro_string = QuoteNode(name) + loglevel = LogLevel(level) + quote + custom_log_levels[$(esc(loglevel))] = ($(macro_string), $(esc(color))) + macro $(esc(macro_name))(exs...) + $logmsg_code(($@_sourceinfo)..., $(esc(loglevel)), exs...) + end + end +end + end # CoreLogging diff --git a/stdlib/Logging/docs/src/index.md b/stdlib/Logging/docs/src/index.md index 3b22e055d4365..4b1eb7bb19aa6 100644 --- a/stdlib/Logging/docs/src/index.md +++ b/stdlib/Logging/docs/src/index.md @@ -70,13 +70,15 @@ automatically extracted. Let's examine the user-defined data first: Often this log-level is unneeded as throwing an exception can convey all the required information. - You can also asign printing styles for custom log levels. For instance: - ``` - MyLog = LogLevel(-500) - custom_log_levels[MyLog] = ("MyLog", :magenta) - macro mylog(exs...) Base.CoreLogging.logmsg_code((Base.CoreLogging.@_sourceinfo)..., MyLog, exs...) end + You can also create logging macros for custom log levels. For instance: + ```julia-repl + julia> using Logging + + julia> @create_log_macro(:MyLog, 200, :magenta) + @mylog (macro with 1 method) - @mylog "foo" + julia> @mylog "hello" + [ MyLog: hello ``` * The *message* is an object describing the event. By convention diff --git a/stdlib/Logging/src/ConsoleLogger.jl b/stdlib/Logging/src/ConsoleLogger.jl index b27752b5711aa..1d45296c907d1 100644 --- a/stdlib/Logging/src/ConsoleLogger.jl +++ b/stdlib/Logging/src/ConsoleLogger.jl @@ -58,7 +58,7 @@ end showvalue(io, ex::Exception) = showerror(io, ex) function default_logcolor(level::LogLevel) - level in keys(custom_log_levels) ? custom_log_levels[level][2]::Symbol : + level in keys(custom_log_levels) ? custom_log_levels[level][2] : level < Info ? Base.debug_color() : level < Warn ? Base.info_color() : level < Error ? Base.warn_color() : diff --git a/stdlib/Logging/src/Logging.jl b/stdlib/Logging/src/Logging.jl index 287fc3b075079..7fe4f7db1cf39 100644 --- a/stdlib/Logging/src/Logging.jl +++ b/stdlib/Logging/src/Logging.jl @@ -21,6 +21,7 @@ for sym in [ Symbol("@warn"), Symbol("@error"), Symbol("@logmsg"), + Symbol("@create_log_macro"), :with_logger, :current_logger, :global_logger, @@ -79,6 +80,7 @@ export @warn, @error, @logmsg, + @create_log_macro, with_logger, current_logger, global_logger, diff --git a/stdlib/Logging/test/runtests.jl b/stdlib/Logging/test/runtests.jl index 7cbe0b465a0e5..ed637cc6c1f39 100644 --- a/stdlib/Logging/test/runtests.jl +++ b/stdlib/Logging/test/runtests.jl @@ -6,10 +6,6 @@ import Logging: min_enabled_level, shouldlog, handle_message @noinline func1() = backtrace() -# see "custom log macro" testset -CustomLog = LogLevel(-500) -macro customlog(exs...) Base.CoreLogging.logmsg_code((Base.CoreLogging.@_sourceinfo)..., esc(CustomLog), exs...) end - @testset "Logging" begin @testset "Core" begin @@ -280,17 +276,27 @@ end end @testset "custom log macro" begin - Logging.custom_log_levels[CustomLog] = ("CustomLog", :magenta) - @test_logs (CustomLog, "a") min_level=CustomLog @customlog "a" + @create_log_macro CustomLog -500 :magenta + + llevel = LogLevel(-500) + + @test_logs (llevel, "a") min_level=llevel @customlog "a" buf = IOBuffer() io = IOContext(buf, :displaysize=>(30,80), :color=>false) - logger = ConsoleLogger(io, CustomLog) + logger = ConsoleLogger(io, llevel) with_logger(logger) do @customlog "a" end @test occursin("CustomLog: a", String(take!(buf))) + + @create_log_macro CustomLog2 1500 1 + + with_logger(logger) do + @customlog2 "hello" + end + @test occursin("CustomLog2: a", String(take!(buf))) end end