Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/SciMLLogging.jl
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ include("utils.jl")

# Export public API
export AbstractVerbositySpecifier, AbstractVerbosityPreset, AbstractMessageLevel
export InfoLevel, WarnLevel, ErrorLevel, CustomLevel, Silent
export DebugLevel, InfoLevel, WarnLevel, ErrorLevel, CustomLevel, Silent
export @SciMLMessage
export verbosity_to_int, verbosity_to_bool
export SciMLLogger
Expand Down
82 changes: 40 additions & 42 deletions src/utils.jl
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ function message_level(verbose::AbstractVerbositySpecifier, option)

if opt_level isa Silent
return nothing
elseif opt_level isa DebugLevel
return Logging.Debug
elseif opt_level isa InfoLevel
return Logging.Info
elseif opt_level isa WarnLevel
Expand All @@ -30,31 +32,40 @@ function message_level(verbose::AbstractVerbositySpecifier, option)
end

function emit_message(
f::Function, verbose::AbstractVerbositySpecifier, level, file, line,
f::Function, level, file, line,
_module)
message = f()
@static if LOGGING_BACKEND == "core"
Core.println(message)
else
Base.@logmsg level message _file=file _line=line _module=_module
end

if level == Logging.Error
throw(ErrorException(message))
end
end

function emit_message(message::String, verbose::AbstractVerbositySpecifier,
function emit_message(message::String,
level, file, line, _module)
@static if LOGGING_BACKEND == "core"
Core.println(message)
else
Base.@logmsg level message _file=file _line=line _module=_module
end

if level == Logging.Error
throw(ErrorException(message))
end
end

function emit_message(message::String, verbose::AbstractVerbositySpecifier,

function emit_message(message::String,
level::Nothing, file, line, _module)
end

function emit_message(
f::Function, verbose::AbstractVerbositySpecifier, level::Nothing, file, line, _module)
f::Function, level::Nothing, file, line, _module)
end


Expand Down Expand Up @@ -89,7 +100,7 @@ macro SciMLMessage(f_or_message, verb, option)
line = __source__.line
file = string(__source__.file)
_module = __module__
expr = :(emit_message($(esc(f_or_message)), $(esc(verb)), message_level($(esc(verb)), $(esc(option))), $file, $line, $_module))
expr = :(emit_message($(esc(f_or_message)), message_level($(esc(verb)), $(esc(option))), $file, $line, $_module))
return expr
end

Expand All @@ -99,7 +110,7 @@ macro SciMLMessage(f_or_message, verb, option, group)
file = string(__source__.file)
_module = __module__
return :(emit_message(
$(esc(f_or_message)), $(esc(verb)), message_level($(esc(verb)), $(esc(option))), $file, $line, $_module))
$(esc(f_or_message)), message_level($(esc(verb)), $(esc(option))), $file, $line, $_module))
end

"""
Expand All @@ -110,20 +121,23 @@ end
are mapped to an integer.

- Silent() => 0
- InfoLevel() => 1
- WarnLevel() => 2
- ErrorLevel() => 3
- DebugLevel() => 1
- InfoLevel() => 2
- WarnLevel() => 3
- ErrorLevel() => 4
- CustomLevel(i) => i
"""
function verbosity_to_int(verb::AbstractMessageLevel)
if verb isa Silent
return 0
elseif verb isa InfoLevel
elseif verb isa DebugLevel
return 1
elseif verb isa WarnLevel
elseif verb isa InfoLevel
return 2
elseif verb isa ErrorLevel
elseif verb isa WarnLevel
return 3
elseif verb isa ErrorLevel
return 4
elseif verb isa CustomLevel
return verb.level
else
Expand All @@ -146,53 +160,33 @@ function verbosity_to_bool(verb::AbstractMessageLevel)
end
end

"""
set_logging_backend(backend::String)

Set the logging backend preference. Valid options are:
- "logging": Use Julia's standard Logging system (default)
- "core": Use Core.println for simple output

Note: You must restart Julia for this preference change to take effect.
"""
function set_logging_backend(backend::String)
if backend in ["logging", "core"]
@set_preferences!("logging_backend" => backend)
@info("Logging backend set to '$backend'. Restart Julia for changes to take effect!")
else
throw(ArgumentError("Invalid backend '$backend'. Valid options are: 'logging', 'core'"))
end
end

"""
get_logging_backend() -> String

Get the current logging backend preference.
"""
function get_logging_backend()
return @load_preference("logging_backend", "logging")
end

"""
SciMLLogger(; kwargs...)

Create a logger that routes messages to REPL and/or files based on log level.

# Keyword Arguments
- `debug_repl = false`: Show debug messages in REPL
- `info_repl = true`: Show info messages in REPL
- `warn_repl = true`: Show warnings in REPL
- `error_repl = true`: Show errors in REPL
- `debug_file = nothing`: File path for debug messages
- `info_file = nothing`: File path for info messages
- `warn_file = nothing`: File path for warnings
- `error_file = nothing`: File path for errors
"""
function SciMLLogger(; info_repl = true, warn_repl = true, error_repl = true,
info_file = nothing, warn_file = nothing, error_file = nothing)
function SciMLLogger(; debug_repl = false, info_repl = true, warn_repl = true, error_repl = true,
debug_file = nothing, info_file = nothing, warn_file = nothing, error_file = nothing)
debug_sink = isnothing(debug_file) ? NullLogger() : FileLogger(debug_file)
info_sink = isnothing(info_file) ? NullLogger() : FileLogger(info_file)
warn_sink = isnothing(warn_file) ? NullLogger() : FileLogger(warn_file)
error_sink = isnothing(error_file) ? NullLogger() : FileLogger(error_file)

repl_filter = EarlyFilteredLogger(current_logger()) do log
if log.level == Logging.Debug && debug_repl
return true
end

if log.level == Logging.Info && info_repl
return true
end
Expand All @@ -208,6 +202,10 @@ function SciMLLogger(; info_repl = true, warn_repl = true, error_repl = true,
return false
end

debug_filter = EarlyFilteredLogger(debug_sink) do log
log.level == Logging.Debug
end

info_filter = EarlyFilteredLogger(info_sink) do log
log.level == Logging.Info
end
Expand All @@ -220,5 +218,5 @@ function SciMLLogger(; info_repl = true, warn_repl = true, error_repl = true,
log.level == Logging.Error
end

TeeLogger(repl_filter, info_filter, warn_filter, error_filter)
TeeLogger(repl_filter, debug_filter, info_filter, warn_filter, error_filter)
end
10 changes: 10 additions & 0 deletions src/verbosity.jl
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ Abstract base type for all verbosity log levels in SciMLLogging.

Log levels determine the severity/importance of messages. Concrete subtypes include:
- `Silent`: No output
- `DebugLevel`: Debug messages
- `InfoLevel`: Informational messages
- `WarnLevel`: Warning messages
- `ErrorLevel`: Error messages
Expand All @@ -20,6 +21,15 @@ no messages will be emitted for that category.
"""
struct Silent <: AbstractMessageLevel end

"""
DebugLevel <: AbstractMessageLevel

Debug log level. Messages at this level provide detailed debugging information,
typically more verbose than informational messages. Useful for development and
troubleshooting.
"""
struct DebugLevel <: AbstractMessageLevel end

"""
InfoLevel <: AbstractMessageLevel

Expand Down
32 changes: 13 additions & 19 deletions test/basics.jl
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,9 @@ end

@test_logs (:warn, "Test1") @SciMLMessage("Test1", verbose, :test1)
@test_logs (:info, "Test2") @SciMLMessage("Test2", verbose, :test2)
@test_logs (:error, "Test3") @SciMLMessage("Test3", verbose, :test3)
@test_logs (:error, "Test3") @test_throws "Test3" begin
@SciMLMessage("Test3", verbose, :test3)
end
@test_logs min_level = Logging.Debug @SciMLMessage("Test4", verbose, :test4)

x = 30
Expand All @@ -74,8 +76,15 @@ end
# All preset should log info level messages
@test_logs (:info, "All preset test") @SciMLMessage("All preset test", verbose_all, :test1)

# Minimal preset should only log errors
@test_logs (:error, "Minimal preset test") @SciMLMessage("Minimal preset test", verbose_minimal, :test1)
# Minimal preset should only log errors and throw for error messages
@test_logs (:error, "Minimal preset test") @test_throws ErrorException("Minimal preset test") begin
@SciMLMessage("Minimal preset test", verbose_minimal, :test1)
end

# Test that minimal preset throws for test3 (which is ErrorLevel)
@test_logs (:error, "Minimal error on test3") @test_throws ErrorException("Minimal error on test3") begin
@SciMLMessage("Minimal error on test3", verbose_minimal, :test3)
end

# None preset should not log anything
@test_logs min_level = Logging.Debug @SciMLMessage("None preset test", verbose_none, :test1)
Expand All @@ -94,21 +103,6 @@ end
@test_logs min_level = Logging.Debug @SciMLMessage("Should not appear", verbose_off, :test2)
end

@testset "Backwards compatibility" begin
verbose = TestVerbosity()

# Test 4-argument version for backwards compatibility
# The group argument should be ignored but the macro should still work
@test_logs (:warn, "Backwards compat test") @SciMLMessage("Backwards compat test", verbose, :test1, :ignored_group)
@test_logs (:info, "Backwards compat test 2") @SciMLMessage("Backwards compat test 2", verbose, :test2, :another_ignored_group)

# Test function-based version with 4 arguments
x = 42
@test_logs (:error, "Backwards function test: 42") @SciMLMessage(verbose, :test3, :ignored_group) do
"Backwards function test: $x"
end
end

@testset "Nested @SciMLMessage macros" begin
verbose = TestVerbosity()

Expand All @@ -131,4 +125,4 @@ end
"Outer result: $counter"
end
end
end
end
Loading