|
| 1 | +""" |
| 2 | + log!(problem::Problem, algorithm::Algorithm, state::State; context::Symbol) -> nothing |
1 | 3 |
|
| 4 | +Generate a log record for the given `(problem, algorithm, state)`, using the logging action associated to `context`. |
| 5 | +By default, the following events trigger a logging action with the given `context`: |
| 6 | +
|
| 7 | +| context | event | |
| 8 | +| --------- | ------------------------------------- | |
| 9 | +| :Start | The solver has been initialized. | |
| 10 | +| :PreStep | The solver is about to take a step. | |
| 11 | +| :PostStep | The solver has taken a step. | |
| 12 | +| :Stop | The solver has finished. | |
| 13 | +
|
| 14 | +Specific algorithms can associate other events with other contexts. |
| 15 | +
|
| 16 | +See also [`register_action!`](@ref) to associate custom actions with these contexts. |
| 17 | +""" |
| 18 | +function log!(problem::Problem, algorithm::Algorithm, state::State; context)::Nothing |
| 19 | + action = @something(logging_action(problem, algorithm, state, context), return nothing) |
| 20 | + # TODO: filter out `nothing` logdata |
| 21 | + @logmsg( |
| 22 | + loglevel(action), logdata!(action, problem, algorithm, state), |
| 23 | + _id = logid(action), _group = loggroup(algorithm) |
| 24 | + ) |
| 25 | + return nothing |
| 26 | +end |
| 27 | + |
| 28 | +""" |
| 29 | + logging_action(problem::Problem, algorithm::Algorithm, state::State, context::Symbol) |
| 30 | + -> Union{Nothing,LoggingAction} |
| 31 | +
|
| 32 | +Obtain the registered logging action associated to an event identified by `context`. |
| 33 | +The default implementation assumes a dictionary-like object `state.logging_action`, which holds |
| 34 | +the different registered actions. |
| 35 | +""" |
| 36 | +function logging_action(::Problem, ::Algorithm, state::State, context::Symbol) |
| 37 | + return get(state.logging_actions, context, nothing) |
| 38 | +end |
| 39 | + |
| 40 | +@doc """ |
| 41 | + loggroup(algorithm::Algorithm) -> Symbol |
| 42 | + loggroup(::Type{<:Algorithm}) -> Symbol |
| 43 | +
|
| 44 | +Generate a group id to attach to all log records for a given algorithm. |
| 45 | +""" loggroup |
| 46 | + |
| 47 | +loggroup(algorithm::Algorithm) = loggroup(typeof(algorithm)) |
| 48 | +loggroup(Alg::Type{<:Algorithm}) = Base.nameof(Alg) |
| 49 | + |
| 50 | +# Sources |
| 51 | +# ------- |
| 52 | +""" |
| 53 | + LoggingAction |
| 54 | +
|
| 55 | +Abstract supertype for defining an action that generates a log record. |
| 56 | +
|
| 57 | +## Methods |
| 58 | +Any concrete subtype should at least implement the following method: |
| 59 | +- [`logdata!(action, problem, algorithm, state)`](@ref logdata!) : generate the data for the log record. |
| 60 | +
|
| 61 | +Addionally, the following methods can be specialized to alter the default behavior: |
| 62 | +- [`loglevel(action) = Logging.Info`](@ref loglevel) : specify the logging level of the generated record. |
| 63 | +- [`logid(action) = objectid(action)`](@ref logid) : specify a unique identifier to associate with the record. |
| 64 | +""" |
| 65 | +abstract type LoggingAction end |
| 66 | + |
| 67 | +logdata!(::LoggingAction, ::Problem, ::Algorithm, ::State) = missing |
| 68 | +loglevel(::LoggingAction) = Logging.Info |
| 69 | +logid(action::LoggingAction) = objectid(action) |
| 70 | + |
| 71 | +struct LogCallback{F} <: LoggingAction |
| 72 | + f::F |
| 73 | +end |
| 74 | + |
| 75 | +logdata!(action::LogCallback, problem, algorithm, state) = |
| 76 | + action.f(problem, algorithm, state) |
| 77 | + |
| 78 | +struct LogGroup{A <: LoggingAction} <: LoggingAction |
| 79 | + actions::Vector{A} |
| 80 | +end |
| 81 | + |
| 82 | +loglevel(alg::LogGroup) = maximum(loglevel, alg.actions) |
| 83 | + |
| 84 | +logdata!(action::LogGroup, problem, algorithm, state) = map(action.actions) do action |
| 85 | + return logdata!(action, problem, algorithm, state) |
| 86 | +end |
| 87 | + |
| 88 | +struct LogLvl{F, A <: LoggingAction} <: LoggingAction |
| 89 | + action::A |
| 90 | + lvl::LogLevel |
| 91 | +end |
| 92 | + |
| 93 | +loglevel(alg::LogLvl) = alg.lvl |
| 94 | + |
| 95 | +struct LogIf{F, A <: LoggingAction} <: LoggingAction |
| 96 | + predicate::F |
| 97 | + action::A |
| 98 | +end |
| 99 | + |
| 100 | +# first cheap check through the level |
| 101 | +loglevel(alg::LogIf) = loglevel(alg.action) |
| 102 | + |
| 103 | +# second check through the predicate |
| 104 | +logdata!(action::LogIf, problem::Problem, algorithm::Algorithm, state::State) = |
| 105 | + action.predicate(problem, algorithm, state) ? logdata!(action.action, problem, algorithm, state) : nothing |
| 106 | + |
| 107 | +# Sinks |
| 108 | +# ----- |
| 109 | +struct StringFormat{F <: PrintF.Format} |
| 110 | + format::F |
| 111 | +end |
| 112 | +function (formatter::StringFormat)(io::IO, lvl, msg, _module, group, id, file = nothing, line = nothing; indent::Integer = 0, kwargs...) |
| 113 | + iob = IOBuffer() |
| 114 | + return ioc = IOContext(iob, io) |
| 115 | + |
| 116 | +end |
| 117 | + |
| 118 | +struct FormatterGroup{K, V} |
| 119 | + rules::Dict{K, V} |
| 120 | +end |
| 121 | + |
| 122 | +function (formatter::AlgorithmFormatter)(io::IO, log) |
| 123 | + rule = get(formatter.rules, log.id, nothing) |
| 124 | + isnothing(rule) ? println(io, log) : |
| 125 | + rule(io, log.level, log.message, log._module, log.group, log.id, log.file, log.line; log.kwargs...) |
| 126 | + return nothing |
| 127 | +end |
0 commit comments