Developer Tutorial: Adding SciMLLogging to Your Package
This tutorial is for Julia package developers who want to integrate SciMLLogging.jl into their packages to provide users with fine-grained verbosity control.
Overview
SciMLLogging.jl provides four main components for package developers:
- AbstractVerbositySpecifier- Base type for creating custom verbosity types
- @SciMLMessage- Macro for emitting conditional log messages
- Log levels - Predefined log levels (Silent,DebugLevel,InfoLevel,WarnLevel,ErrorLevel,CustomLevel(n)). These are the fields of theAbstractVerbositySpecifiers that determine which messages get logged, and at what log level.
- Verbosity preset levels - None,Minimal,Standard,Detailed,All. These represent predefined sets of log levels.
AbstractVerbositySpecifier
`AbstractVerbositySpecifier` is the base type that package developers implement a subtype of to create custom verbosity type for their packages.@SciMLMessage
In order to use the the @SciMLMessage macro, simply choose which of the fields of your AbstractVerbositySpecifier should control that particular message. Then when the macro is called, the field of the verbosity object corresponding with the option argument to the macro is used to control the logging of the message. 
Step 1: Design Your Verbosity Interface
First, decide what aspects of your package should be controllable by users. For example, a solver might have:
- Initialization messages
- Iteration progress
- Convergence information
- Error control information
Step 2: Create Your Verbosity Type
Define a struct that subtypes AbstractVerbositySpecifier:
using SciMLLogging
using ConcreteStructs: @concrete
@concrete struct MySolverVerbosity <: AbstractVerbositySpecifier
    initialization
    iterations
    convergence
    warnings
end
# Constructor with defaults
function MySolverVerbosity(;
        initialization = InfoLevel(),
        iterations = Silent(),
        convergence = InfoLevel(),
        warnings = WarnLevel()
)
    MySolverVerbosity(initialization, iterations, convergence, warnings)
end- Use @concretefrom ConcreteStructs.jl for better performance
- Each field represents a category of messages your package can emit
Step 3: Add Convenience Constructors
Make it easy for users to create verbosity instances. Perhaps include a constructor that can take a AbstractVerbosityPreset, and use it to set the rest of the fields, and a constructor that takes all keyword arguments:
# Preset-based constructor (optional)
function MySolverVerbosity(preset::AbstractVerbosityPreset)
    if preset isa None
        MySolverVerbosity(
            initialization = Silent(),
            iterations = Silent(),
            convergence = Silent(),
            warnings = Silent()
        )
    elseif preset isa All
        MySolverVerbosity(
            initialization = InfoLevel(),
            iterations = InfoLevel(),
            convergence = InfoLevel(),
            error_control = WarnLevel()
        )
    elseif preset isa Minimal
        MySolverVerbosity(
            initialization = Silent(),
            iterations = Silent(),
            convergence = ErrorLevel(),
            error_control = ErrorLevel()
        )
    else
        MySolverVerbosity()  # Default
    end
endStep 4: Integrate Messages Into Your Code
Use @SciMLMessage throughout your package code:
function my_solve(problem, verbose::MySolverVerbosity)
    @SciMLMessage("Initializing solver for $(typeof(problem))", verbose, :initialization)
    # Setup code here...
    for iteration in 1:maxiters
        # Solver iteration...
        @SciMLMessage(verbose, :iterations) do
            "Iteration $iteration: residual = $(compute_residual())"
        end
        if converged
            @SciMLMessage("Converged after $iteration iterations", verbose, :convergence)
            return solution
        end
        if should_warn_about_something()
            @SciMLMessage("Convergence is slow, consider adjusting parameters", verbose, :error_control)
        end
    end
    @SciMLMessage("Failed to converge after $maxiters iterations", verbose, :convergence)
    return nothing
endStep 5: Document for Users
Provide clear documentation for your users:
"""
    MySolverVerbosity(; kwargs...)
Controls verbosity output from MySolver functions.
# Keyword Arguments
- `initialization = InfoLevel()`: Messages about solver setup
- `iterations = Silent()`: Per-iteration progress messages
- `convergence = InfoLevel()`: Convergence/failure notifications
- `error_control = WarnLevel()`: Messages about solver error control
# Constructors
- `MySolverVerbosity()`: Default enabled verbosity
- `MySolverVerbosity(None())`: Disabled (zero overhead)
- `MySolverVerbosity(All())`: Enable all message categories
- `MySolverVerbosity(Minimal())`: Only errors and convergence
# Examplejulia
Default verbosity
verbose = MySolverVerbosity()
Custom verbosity - show everything except iterations
verbose = MySolverVerbosity(iterations = Silent())
Silent mode
verbose = MySolverVerbosity( initialization = Silent(), iterations = Silent(), convergence = Silent(), warnings = Silent() )
"""Complete Example
Here's a complete minimal example:
using SciMLLogging
using ConcreteStructs: @concrete
import SciMLLogging: AbstractVerbositySpecifier
@concrete struct ExampleVerbosity <: AbstractVerbositySpecifier
    progress
end
# Constructor with default
ExampleVerbosity(; progress = InfoLevel()) = ExampleVerbosity(progress)
function solve_example(n::Int, verbose::ExampleVerbosity)
    result = 0
    for i in 1:n
        result += i
        @SciMLMessage("Step $i: sum = $result", verbose, :progress)
    end
    return result
endsolve_example (generic function with 1 method)This example shows the minimal structure needed to integrate SciMLLogging into a package.