|
1 | | -""" |
2 | | - log!(problem::Problem, algorithm::Algorithm, state::State; context::Symbol) -> nothing |
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 | | -# ------- |
| 1 | +# LoggingAction interface |
| 2 | +# ----------------------- |
52 | 3 | """ |
53 | 4 | LoggingAction |
54 | 5 |
|
55 | 6 | Abstract supertype for defining an action that generates a log record. |
56 | 7 |
|
57 | 8 | ## 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. |
| 9 | +Any concrete subtype should at least implement the following method to handle the logging event: |
60 | 10 |
|
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. |
| 11 | +- [`handle_message!(action, problem, algorithm, state, args...; kwargs...)`](@ref handle_message!) |
64 | 12 | """ |
65 | 13 | abstract type LoggingAction end |
66 | 14 |
|
67 | | -logdata!(::LoggingAction, ::Problem, ::Algorithm, ::State) = missing |
68 | | -loglevel(::LoggingAction) = Logging.Info |
69 | | -logid(action::LoggingAction) = objectid(action) |
| 15 | +@doc """ |
| 16 | + handle_message!(action::LoggingAction, problem::Problem, algorithm::Algorithm, state::State; kwargs...) |
70 | 17 |
|
71 | | -struct LogCallback{F} <: LoggingAction |
72 | | - f::F |
73 | | -end |
| 18 | +Entry-point for defining an implementation of how to handle a logging event for a given [`LoggingAction`](@ref). |
| 19 | +""" handle_message!(::LoggingAction, ::Algorithm, ::Problem, ::State; kwargs...) |
74 | 20 |
|
75 | | -logdata!(action::LogCallback, problem, algorithm, state) = |
76 | | - action.f(problem, algorithm, state) |
| 21 | +# Concrete LoggingActions |
| 22 | +# ----------------------- |
| 23 | +""" |
| 24 | + LogGroup(actions::Vector{<:LoggingAction}) |
77 | 25 |
|
| 26 | +Concrete [`LoggingAction`](@ref) that can be used to sequentially perform each of the `actions`. |
| 27 | +""" |
78 | 28 | struct LogGroup{A <: LoggingAction} <: LoggingAction |
79 | 29 | actions::Vector{A} |
80 | 30 | end |
81 | 31 |
|
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) |
| 32 | +function handle_message!( |
| 33 | + action::LogGroup, problem::Problem, algorithm::Algorithm, state::State; kwargs... |
| 34 | + ) |
| 35 | + for child in action.actions |
| 36 | + handle_message!(child, algorithm, problem, state; kwargs...) |
| 37 | + end |
| 38 | + return nothing |
86 | 39 | end |
87 | 40 |
|
88 | | -struct LogLvl{F, A <: LoggingAction} <: LoggingAction |
89 | | - action::A |
90 | | - lvl::LogLevel |
| 41 | +""" |
| 42 | + CallbackAction(callback) |
| 43 | +
|
| 44 | +Concrete [`LoggingAction`](@ref) that handles a logging event through an arbitrary callback function. |
| 45 | +The callback function must have the following signature: |
| 46 | +```julia |
| 47 | +callback(algorithm, problem, state; kwargs...) = ... |
| 48 | +``` |
| 49 | +Here `args...` and `kwargs...` are optional and can be filled out with context-specific information. |
| 50 | +""" |
| 51 | +struct CallbackAction{F} <: LoggingAction |
| 52 | + callback::F |
91 | 53 | end |
92 | 54 |
|
93 | | -loglevel(alg::LogLvl) = alg.lvl |
| 55 | +function handle_message!( |
| 56 | + action::CallbackAction, problem::Problem, algorithm::Algorithm, state::State; kwargs... |
| 57 | + ) |
| 58 | + action.callback(algorithm, problem, state; kwargs...) |
| 59 | + return nothing |
| 60 | +end |
94 | 61 |
|
95 | | -struct LogIf{F, A <: LoggingAction} <: LoggingAction |
| 62 | +struct IfAction{F, A <: LoggingAction} <: LoggingAction |
96 | 63 | predicate::F |
97 | 64 | action::A |
98 | 65 | end |
99 | 66 |
|
100 | | -# first cheap check through the level |
101 | | -loglevel(alg::LogIf) = loglevel(alg.action) |
| 67 | +function handle_message!( |
| 68 | + action::IfAction, problem::Problem, algorithm::Algorithm, state::State; kwargs... |
| 69 | + ) |
| 70 | + return action.predicate(problem, algorithm, state; kwargs...) ? |
| 71 | + handle_message(action.action, problem, algorithm, state; kwargs...) : |
| 72 | + nothing |
| 73 | +end |
102 | 74 |
|
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 |
| 75 | +# Algorithm Logger |
| 76 | +# ---------------- |
| 77 | +""" |
| 78 | + AlgorithmLogger(context => action, ...) |
106 | 79 |
|
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) |
| 80 | +Logging transformer that handles the logic of dispatching logging events to logging actions. |
| 81 | +By default, the following events trigger a logging action with the given `context`: |
115 | 82 |
|
| 83 | +| context | event | |
| 84 | +| --------- | ----------------------------------- | |
| 85 | +| :Start | The solver will start. | |
| 86 | +| :Init | The solver has been initialized. | |
| 87 | +| :PreStep | The solver is about to take a step. | |
| 88 | +| :PostStep | The solver has taken a step. | |
| 89 | +| :Stop | The solver has finished. | |
| 90 | +
|
| 91 | +Specific algorithms can associate other events with other contexts. |
| 92 | +
|
| 93 | +See also the scoped value [`AlgorithmsInterface.algorithm_logger`](@ref). |
| 94 | +""" |
| 95 | +struct AlgorithmLogger |
| 96 | + actions::Dict{Symbol, LogAction} |
116 | 97 | end |
| 98 | +AlgorithmLogger(args...) = AlgorithmLogger(Dict{Symbol, LogAction}(args...)) |
117 | 99 |
|
118 | | -struct FormatterGroup{K, V} |
119 | | - rules::Dict{K, V} |
| 100 | +""" |
| 101 | + const LOGGING_ENABLED = Ref(true) |
| 102 | +
|
| 103 | +Global toggle for enabling and disabling all logging features. |
| 104 | +""" |
| 105 | +const LOGGING_ENABLED = Ref(true) |
| 106 | + |
| 107 | +""" |
| 108 | + const algorithm_logger = ScopedValue(AlgorithmLogger()) |
| 109 | +
|
| 110 | +Scoped value for handling the logging events of arbitrary algorithms. |
| 111 | +""" |
| 112 | +const ALGORITHM_LOGGER = ScopedValue(AlgorithmLogger()) |
| 113 | + |
| 114 | +# @inline here to enable the cheap global check |
| 115 | +@inline function log!(problem::Problem, algorithm::Algorithm, state::State, context::Symbol; kwargs...) |
| 116 | + if LOGGING_ENABLED[] |
| 117 | + logger::AlgorithmLogger = ALGORITHM_LOGGER[] |
| 118 | + handle_message(logger, problem, algorithm, state, context; kwargs...) |
| 119 | + end |
| 120 | + return nothing |
120 | 121 | end |
121 | 122 |
|
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...) |
| 123 | +# @noinline to keep the algorithm function bodies small |
| 124 | +@noinline function handle_message( |
| 125 | + alglogger::AlgorithmLogger, problem::Problem, algorithm::Algorithm, state::State, context::Symbol; |
| 126 | + kwargs... |
| 127 | + ) |
| 128 | + action::LoggingAction = @something(get(alglogger.actions, context, nothing), return nothing) |
| 129 | + try |
| 130 | + handle_message!(action, problem, algorithm, state, args...; kwargs...) |
| 131 | + catch err |
| 132 | + bt = catch_backtrace() |
| 133 | + @error "Error during the handling of a logging action" action exception = (err, bt) |
| 134 | + end |
126 | 135 | return nothing |
127 | 136 | end |
0 commit comments