18
18
19
19
.. seealso::
20
20
21
- * :mod:`antsibull .config` to see how the antsibull scripts allow user defined configuration
21
+ * :mod:`antsibull_core .config` to see how the antsibull scripts allow user defined configuration
22
22
to configure logging after the bootstrap phase is over. This is the primary way that end
23
23
users interact with the logging subsystem.
24
24
31
31
logging output is disabled. That way the library doesn't spam the user's screen with log messages.
32
32
33
33
An application that wishes to use the log must import the log and then call
34
- antsibull .logging.initialize_app_logging() before any other parts of antsibull are imported. See
34
+ antsibull_core .logging.initialize_app_logging() before any other parts of antsibull are imported. See
35
35
the :ref:`Application Logging <application_logging>`_ section for more details.
36
36
37
37
38
38
Usage within a module
39
39
=====================
40
40
41
41
Our convention for logging with twiggy is that the name field reflects the Python package that the
42
- code is coming from (in this case, it is already set to ``antsibull`` by :mod:`antsibull .logging`.)
42
+ code is coming from (in this case, it is already set to ``antsibull`` by :mod:`antsibull_core .logging`.)
43
43
At the toplevel of a module, set up a logger which has a field named ``mod`` which reflects the
44
44
module name:
45
45
46
46
.. code-block:: python
47
47
48
48
# The antsibull log object with the name already set to `antsibull`.
49
- from logging import log
49
+ from logging import get_module_logger
50
50
51
- # mlog stands for module log. It's our convention to create a logger from the
52
- # antsibull.logging.log object in each module. `fields()` takes an arbitrary set of keyword
53
- # args and returns a new log object. Any log messages we emit with this log object (or its
54
- # children) will include the fields which were set on it. Our convention is to create mlog with
55
- # `fields(mod=__name__)` so that messages we make from mlog (or its children) have a field named
56
- # `mod` containing the name of the module.
51
+ # mlog stands for module log. It's our convention to create a logger in each module.
52
+ # The logger will containt the module name as the `mod` field.
53
+ mlog = get_module_logger(__name__)
57
54
58
- # `mod` and the value set to the module name.
59
- mlog = log.fields(mod=__name__)
55
+ # `fields()` takes an arbitrary set of keyword args and returns a new log object.
56
+ # Any log messages we emit with this log object (or its children) will include the fields
57
+ # which were set on it.
60
58
61
59
TRICKY_COMPUTED_GLOBAL = [a**a for a in range(1, 4)]
62
60
# Use mlog for logging interesting things that happen at the module level. Notice that we send
74
72
75
73
.. code-block:: python
76
74
77
- def test_function(argument1):
78
- # flog stands for function log. It's our convention to use this name.
79
- # Create a new one in any function you want to log from.
80
- # By creating this from mlog, we copy any fields and other settings that we made to mlog.
81
- # Our convention is to use the `func` field to hold the name of the function we're in.
82
- flog = mlog.fields(func='test_function')
75
+ def test_function(argument1):
76
+ # flog stands for function log. It's our convention to use this name.
77
+ # Create a new one in any function you want to log from.
78
+ # By creating this from mlog, we copy any fields and other settings that we made to mlog.
79
+ # Our convention is to use the `func` field to hold the name of the function we're in.
80
+ flog = mlog.fields(func='test_function')
83
81
84
- # This would output:
85
- # DEBUG:antsibull:func=test_function:mod=__main__|Enter
86
- flog.debug('Enter')
87
- value = do_something(argument1)
82
+ # This would output:
83
+ # DEBUG:antsibull:func=test_function:mod=__main__|Enter
84
+ flog.debug('Enter')
85
+ value = do_something(argument1)
88
86
89
- flog.debug('Leave')
87
+ flog.debug('Leave')
90
88
91
- class FooBar:
92
- def __init__(self):
93
- flog = mlog.fields(func='FooBar.__init__')
94
- flog.debug('Enter')
89
+ class FooBar:
90
+ def __init__(self):
91
+ flog = mlog.fields(func='FooBar.__init__')
92
+ flog.debug('Enter')
95
93
96
- self._x = initialize_x()
97
- self._y = initialize_y()
94
+ self._x = initialize_x()
95
+ self._y = initialize_y()
98
96
99
- self.position = self.calculate_position(self._x, self._y)
97
+ self.position = self.calculate_position(self._x, self._y)
100
98
101
- flog.debug('Leave')
99
+ flog.debug('Leave')
102
100
103
101
104
102
.. _logging_levels::
@@ -146,15 +144,15 @@ def __init__(self):
146
144
147
145
An antsibull command (:file:`antsibull/cli/*.py`) should import the ``log`` object from this module.
148
146
The log object will be configured for use within the library at first (silent) so the application
149
- should call :func:`antsibull .logging.initialize_app_logging` as soon as possible to tell the ``log``
147
+ should call :func:`antsibull_core .logging.initialize_app_logging` as soon as possible to tell the ``log``
150
148
that it is okay to emit messages.
151
149
152
150
The initial application logging configuration will log to stderr at the ``WARNING`` level or
153
151
higher. If the :envvar:`ANTIBULL_EARLY_DEBUG` environment variable is set, then it will log at
154
152
the ``DEBUG`` level rather than ``WARNING``.
155
153
156
154
The antsibull command should read the configuration settings, which may include user specified
157
- logging configuration and application defaults, and then call :twiggy: func:`twiggy.dict_config ` to
155
+ logging configuration and application defaults, and then call :func:`antsibull_core.logging.configure_logger ` to
158
156
finish the setup. At that point, logging calls will emit logs according to the user's
159
157
configuration.
160
158
@@ -163,10 +161,9 @@ def __init__(self):
163
161
.. code-block:: python
164
162
165
163
# File is antsibull/cli/antsibull_command.py
166
- import twiggy
167
164
# log is the toplevel log object. It is important to import this and initialize it prior to
168
165
# using the log so that sane defaults can be set.
169
- from .. logging import log , initialize_app_logging
166
+ from antsibull_core. logging import configure_logger, get_module_logger , initialize_app_logging
170
167
171
168
# By default, the log is configured to be useful within a library where the user may not have
172
169
# been given the chance to configure the log. Calling initialize_app_logging() reconfigures
@@ -178,7 +175,7 @@ def __init__(self):
178
175
from ..config import load_config
179
176
180
177
181
- mlog = log.fields(mod= __name__)
178
+ mlog = get_module_logger( __name__)
182
179
183
180
def run(args):
184
181
flog = mlog.fields(func='run')
@@ -189,12 +186,12 @@ def run(args):
189
186
with app_context.app_and_lib_context(context_data) as (app_ctx, dummy_):
190
187
# initialize_app_logging() sets the log's configuration with defaults appropriate for
191
188
# an application but this call takes that one step further. It takes the logging
192
- # configuration from the user's config file and hands it to twiggy.dict_config () so
189
+ # configuration from the user's config file and hands it to configure_logger () so
193
190
# that the user has ultimate control over what log level, what format, and which file
194
191
# the log is output as. See the twiggy documentation for information on the format of
195
- # the logging config. See the antsibull .app_context documentation if you want more
192
+ # the logging config. See the antsibull_core .app_context documentation if you want more
196
193
# information on the context object.
197
- twiggy.dict_config (app_ctx.logging_cfg.model_dump() )
194
+ configure_logger (app_ctx)
198
195
199
196
200
197
Once those steps are taken, any further logging calls will obey the user's configuration.
@@ -203,11 +200,16 @@ def run(args):
203
200
204
201
from __future__ import annotations
205
202
203
+ import abc
206
204
import os
205
+ import typing as t
207
206
208
207
import twiggy # type: ignore[import]
209
208
import twiggy .levels # type: ignore[import]
210
209
210
+ if t .TYPE_CHECKING :
211
+ from .schemas .context import AppContext
212
+
211
213
#: The standard log to use everywhere. The name of the logger for all of the antsibull libraries
212
214
#: is antsibull so that it is easy to setup an emitter for all of antsibull. For those used to
213
215
#: using the module's __name__ field as the name, the idiom we use here is to set the module name
@@ -229,7 +231,7 @@ def initialize_app_logging() -> None:
229
231
"""
230
232
Change log settings to make sense for an application.
231
233
232
- Merely importing the :mod:`antsibull .logging` module sets up the logger for use as part of
234
+ Merely importing the :mod:`antsibull_core .logging` module sets up the logger for use as part of
233
235
a library. Calling this function will initialize the logger for use in an application.
234
236
"""
235
237
# We want to see logs from the antsibull library, so the very first thing we do is turn the log
@@ -243,4 +245,75 @@ def initialize_app_logging() -> None:
243
245
twiggy .quick_setup (min_level = _level )
244
246
245
247
246
- __all__ = ("log" , "initialize_app_logging" )
248
+ class Logger (metaclass = abc .ABCMeta ):
249
+ @abc .abstractmethod
250
+ def debug (self , format_spec : str , * args , ** kwargs ) -> None :
251
+ pass
252
+
253
+ @abc .abstractmethod
254
+ def info (self , format_spec : str , * args , ** kwargs ) -> None :
255
+ pass
256
+
257
+ @abc .abstractmethod
258
+ def notice (self , format_spec : str , * args , ** kwargs ) -> None :
259
+ pass
260
+
261
+ @abc .abstractmethod
262
+ def warning (self , format_spec : str , * args , ** kwargs ) -> None :
263
+ pass
264
+
265
+ @abc .abstractmethod
266
+ def error (self , format_spec : str , * args , ** kwargs ) -> None :
267
+ pass
268
+
269
+ @abc .abstractmethod
270
+ def critical (self , format_spec : str , * args , ** kwargs ) -> None :
271
+ pass
272
+
273
+ @abc .abstractmethod
274
+ def trace (self ) -> None :
275
+ pass
276
+
277
+ @abc .abstractmethod
278
+ def fields (self , ** kwargs ) -> Logger :
279
+ pass
280
+
281
+
282
+ class TwiggyLogger (Logger ):
283
+ def __init__ (self , logger : twiggy .logger .Logger ) -> None :
284
+ self .logger = logger
285
+
286
+ def debug (self , format_spec : str , * args , ** kwargs ) -> None :
287
+ self .logger .debug (format_spec , * args , ** kwargs )
288
+
289
+ def info (self , format_spec : str , * args , ** kwargs ) -> None :
290
+ self .logger .info (format_spec , * args , ** kwargs )
291
+
292
+ def notice (self , format_spec : str , * args , ** kwargs ) -> None :
293
+ self .logger .notice (format_spec , * args , ** kwargs )
294
+
295
+ def warning (self , format_spec : str , * args , ** kwargs ) -> None :
296
+ self .logger .warning (format_spec , * args , ** kwargs )
297
+
298
+ def error (self , format_spec : str , * args , ** kwargs ) -> None :
299
+ self .logger .error (format_spec , * args , ** kwargs )
300
+
301
+ def critical (self , format_spec : str , * args , ** kwargs ) -> None :
302
+ self .logger .critical (format_spec , * args , ** kwargs )
303
+
304
+ def trace (self ) -> None :
305
+ self .logger .trace ()
306
+
307
+ def fields (self , ** kwargs ) -> Logger :
308
+ return TwiggyLogger (self .logger .fields (** kwargs ))
309
+
310
+
311
+ def get_module_logger (module_name : str ) -> Logger :
312
+ return TwiggyLogger (log .fields (mod = module_name ))
313
+
314
+
315
+ def configure_logger (app_ctx : AppContext ) -> None :
316
+ twiggy .dict_config (app_ctx .logging_cfg .model_dump ())
317
+
318
+
319
+ __all__ = ("log" , "initialize_app_logging" , "get_module_logger" , "Logger" )
0 commit comments