Skip to content

Commit ed69b9d

Browse files
committed
Finish user side logging docs
1 parent 8361c32 commit ed69b9d

2 files changed

Lines changed: 50 additions & 29 deletions

File tree

docs/Logging.md

Lines changed: 50 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ Updates as of mid February
77

88
# Logging for Python in DUNE-DAQ
99

10-
Welcome, fellow logging enthusiast! This page provides a user's guide to how logging is done in Python in the context of DUNE-DAQ.
10+
Welcome, fellow beavers! This page provides a user's guide to how logging is done in Python in the context of DUNE-DAQ.
1111

1212
## Basics
1313

@@ -130,12 +130,15 @@ As shown above, initialising a logging instance with a specific handler is as ea
130130
The core philosophy of the logging framework in daqpytools is that each logger should only have _one_ instance of a specific type of logger. This means that while a single logger can have both a Rich and a Stream handler, a single logger cannot have _two_ Rich handlers to prevent duplicating messages.
131131

132132

133-
Please refer to the docstrings for the most up to date definitions on the options and what handlers may or may not be included.
133+
Please refer to the docstrings for the most up to date definitions on the options and what handlers may or may not be included. There are some exceptions which are clearly labelled in the document. Choosing which handler to trigger on a per-message instance is an advanced feature of logging in DUNE-DAQ, so please refer to the advanced section of this guide.
134134

135135

136-
### Subsec of existing handlers, filters, and walkthrough
136+
### Walkthrough of existing handlers and filters
137+
138+
As seen in the previous section, there are several handlers and filters that are present in the daqpytools that may readily be used. What follows will be a brief description of each handlers as well as a quick example, but for more complete docs please refer to the docstrings and the logging demonstrator.
139+
140+
Remember that by default, any messages received by the logger will be transmitted to _all_ available handlers that are attached to the logger.
137141

138-
As seen in the previous section, there are several handlers and filters that are present in the daqpytools that may readily be used. What follows will be a brief description of each handlers as well as a quick example, but for more complete docs please refer to the docstrings and the logging demonstrator
139142

140143
#### Rich handler
141144

@@ -213,7 +216,7 @@ The basic logic is as follows.
213216
5. After `time_limit` seconds after the last message, the filter gets reset, allowing messages to be sent once more
214217

215218

216-
For the throttle filter, a 'log record' is **uniquely** defined by the record's pathname and linenumber. Therefore, 50 records that contain the same 'messege' but defined in different line numbers in the script will not be erroneously filtered.
219+
For the throttle filter, a 'log record' is **uniquely** defined by the record's pathname and linenumber. Therefore, 50 records that contain the same 'message' but defined in different line numbers in the script will not be erroneously filtered.
217220

218221

219222
An example is as follows:
@@ -248,58 +251,76 @@ Which will behave as expected.
248251
**Note**
249252
By default, throttle filters obtained via `get_daq_logger` will be initialised with an `initial_treshold` of 30 and a `time_limit` of 30.
250253

254+
**Note**
255+
Similarly to the ERS Kafka handler, this filter is not enabled by default, hence requiring the use of HandlerTypes. See the Advanced section for more info.
251256

252257
### Advanced logging
253258

259+
The above walkthrough should be sufficient for the vast majority of logging. However, there are a few advanced features in daqpytools that would benefit the user. These mainly are targetted towards a high degree of customisability for the user, including the ability to choose which of the attached handlers will transmit a given message, and automatic routing of messages to certain handlers.
254260

255-
#### Context
261+
#### Choosing handlers with HandlerTypes
256262

257-
#### Handler streams
263+
Lets say you have a logger with an attached Rich handler and File handler as below, and that there are two messages you want to log. However, one of them should only be sent to the file, and the other one should be sent to the terminal via the rich handler.
258264

265+
```
266+
log = get_daq_logger("example", rich_handler=True, file_handler="logging.log")
267+
```
259268

260-
#### Log Handler Conf
269+
These can be done by using the `HandlerTypes` enum. The loggers defined here have a special ability to read which HandlerTypes are supplied and to only transmit to the required handlers.
261270

271+
There is a very specific syntax that must be followed involving the `extra` kwarg:
262272

263-
#### Using ERS
273+
```
274+
from daqpytools.logging.handlers import HandlerType
264275
276+
log.info("This will only be sent to the Rich", extra={"handlers": [HandlerType.Rich]})
277+
log.info("This will only be sent to the File", extra={"handlers": [HandlerType.File]})
278+
log.info("You can even send to both", extra={"handlers": [HandlerType.Rich, HandlerType.File]})
279+
```
265280

281+
Naturally, if tell the logger to transmit a message where the associated Handler is not attached will cause it to do nothing. For example above, using HandlerTypes.Stream will do nothing.
266282

267283

268284

285+
#### The need for handler streams
269286

287+
Within the DUNE DAQ ecosystem, there are several other configurations that interplay. These include the OpMon and ERS, which require several handlers for each configuration. These can be best thought of as 'streams' which interact with several other handlers, as shown in an example below.
270288

289+
![streams](img/streams.png)
271290

272-
## Basic info
273-
basically see your presentation
291+
The native implemention in drunc and most applications is referred to as the 'Base' stream, which will only require interactions with the Rich, File, and Stream handlers. **This is why the ERS Kafka Handler and the Throttle filter need to be 'activated' with HandlerTypes**.
274292

275-
- the basics of python logging
276-
- handlers
277-
- inheritance
278-
- log levels
293+
The ERS configuration is defined in OKS, [for example here](https://github.com/DUNE-DAQ/daqsystemtest/blob/974965be6e96aff969c69a380ed34aa96705e802/config/daqsystemtest/ccm.data.xml#L189), and are automatically parsed by daqpytools as they get used. A special feature of the ERS configuration is that the relevant Handlers are severity-level dependent; ERS Fatal and ERS info may have a different set of handler requirements
279294

280-
## User story
281-
And then talk about the features in daqpytools as a user
295+
#### Log Handler Conf
282296

283-
- Best practices on initialising loggers
284-
- How to deal with handlertypes
285-
- How to deal with handlerconf
286-
- integratoin with ers
287297

288-
## Advanced handlers (WIP as of Dec 2025)
298+
To deal with the constraints set above, a handler configuration dataclass called `LogHandlerConf` is constructed to define the relevant configurations and a system of filters is initialised with every handler. When passing a log record that needs to be processed via a specific stream, the log record and the handler configuration is passed to the relevant logger, after which it is processed.
289299

290-
Aside from the core set of loggers and handlers defined natively in drunc, there are several other configurations that interplay with drunc. These can be best thought of as 'streams' which in interact with several other handlers.
300+
An example of which is shown in the logging demonstrator, copied here.
291301

292-
<img width="510" height="561" alt="streams" src="https://github.com/user-attachments/assets/de95f47c-fe30-48f3-ac96-0f7c091bbc17" />
302+
```python
303+
handlerconf = LogHandlerConf()
293304

294-
The native implementation in drunc is referred to as the 'base' stream and interacts with the three core handlers already discussed.
295-
Other streams include the 'Opmon' stream interacting with its own set of handlers, and the 'ERS' stream that interacts with different handlers based on the log message's severity level.
305+
main_logger.warning("Handlerconf Base", extra=handlerconf.Base)
306+
main_logger.warning("Handlerconf Opmon", extra=handlerconf.Opmon)
307+
```
296308

297-
The 'ERS' configuration is configured in OKS ([for example here](https://github.com/DUNE-DAQ/daqsystemtest/blob/974965be6e96aff969c69a380ed34aa96705e802/config/daqsystemtest/ccm.data.xml#L189)), and are automatically parsed by drunc and daqpytools as they get used.
298309

299-
To deal with the constraints set above, a handler configuration dataclass is constructed to define the relevant configurations and a system of filters is initialised with every handler. When passing a log record that needs to be processed via a specific stream, the log record and the handler configuration is passed to the relevant logger, after which it is processed.
310+
#### Using ERS
311+
By default, the LogHandlerConf does not initialise the ERS stream because it requires the ERS environment variables to be defined. Should these variables exists, there are two ways to initialise the ERS stream with the LogHandlerConf.
300312

313+
```python
314+
# By default init_ers is false
315+
# When this is the case, ERS Streams are _not_ defined. Will survive without ERS envs being defined
316+
LHC_no_init = LogHandlerConf() == LogHandlerConf(init_ers=False)
301317

302-
## Dev story
318+
print(LHC_no_init.Base) # Success
319+
print(LHC_no_init.ERS) # Throws ERS stream not initialised. Call init_ERS() first
303320

321+
# Later on, when ERS envs are defined, can be initialised
322+
LHC_no_init.init_ERS()
323+
print(LHC_no_init.ERS) # Success
304324

325+
```
305326

docs/img/streams.png

276 KB
Loading

0 commit comments

Comments
 (0)