ATLAS Offline Software
Toggle main menu visibility
Loading...
Searching...
No Matches
PhysicsAnalysis
Columnar
ColumnarToolWrapperPython
python
logging_bridge.py
Go to the documentation of this file.
1
# Copyright (C) 2002-2026 CERN for the benefit of the ATLAS collaboration
2
#
3
# @author Giordon Stark
4
5
"""Bridge C++ message output to Python logging module.
6
7
Installs a callback that routes C++ IMessagePrinter output to a Python
8
logger named "atlas", integrating with standard logging configuration
9
(handlers, formatters, levels).
10
11
The bridge is installed automatically on package import with a default
12
StreamHandler and INFO level, so C++ messages are visible out of the box.
13
To suppress them::
14
15
import logging
16
logging.getLogger("atlas").setLevel(logging.WARNING)
17
18
To see DEBUG/VERBOSE messages::
19
20
import logging
21
logging.getLogger("atlas").setLevel(logging.DEBUG)
22
23
To customize the format, replace the default handler::
24
25
import logging, sys
26
logger = logging.getLogger("atlas")
27
logger.handlers.clear()
28
handler = logging.StreamHandler(sys.stderr)
29
handler.setFormatter(logging.Formatter("[%(tool)s] %(levelname)s %(message)s"))
30
logger.addHandler(handler)
31
32
Tool context is passed via the `extra` dict, so formatters can reference
33
`%(tool)s` to display the tool name.
34
35
In Athena/AthAnalysis builds (non-standalone), IMessagePrinter is not available
36
and the C++ bridge cannot be installed. The logger is still created and usable
37
for Python-side logging, but C++ messages will not be routed through it.
38
"""
39
40
import
logging
41
import
sys
42
import
atexit
43
44
from
ColumnarToolWrapperPython.python_tool_handle
import
set_python_printer, MsgLevel
45
46
47
# Mapping from C++ MSG::Level to Python logging levels.
48
# C++ levels (0-6) map to Python levels preserving severity ordering.
49
_LEVEL_MAP = {
50
MsgLevel.NIL: logging.NOTSET,
51
MsgLevel.VERBOSE: logging.DEBUG - 5,
52
MsgLevel.DEBUG: logging.DEBUG,
53
MsgLevel.INFO: logging.INFO,
54
MsgLevel.WARNING: logging.WARNING,
55
MsgLevel.ERROR: logging.ERROR,
56
MsgLevel.FATAL: logging.CRITICAL,
57
}
58
59
60
# Format approximating the C++ default MessagePrinter output:
61
# "ToolSvc.myTool INFO message text"
62
_DEFAULT_FORMAT =
"%(tool)-25s%(levelname)-8s%(message)s"
63
64
65
def
install_printer
(name="atlas"):
66
"""Install a Python logger as the C++ message printer.
67
68
Routes all C++ IMessagePrinter messages (from tools, ASG framework, etc.)
69
to a Python logger with the given name. Called automatically by the package
70
on import with a default StreamHandler and INFO level, matching the C++
71
default stdout printer behaviour.
72
73
In Athena/AthAnalysis (non-standalone) builds, IMessagePrinter is not
74
available. The logger is still created and configured, but C++ messages
75
will not be routed through it.
76
77
Tool name is passed via the `extra` dict, so formatters can use `%(tool)s`.
78
79
Parameters
80
----------
81
name:
82
Logger name (default: "atlas").
83
84
Returns
85
-------
86
logging.Logger
87
The installed logger. Configure with handlers and setLevel as needed.
88
89
Notes
90
-----
91
Only one printer can be active at a time. Calling install_printer again
92
replaces the previous one. Call remove_printer() to restore default
93
stdout printing.
94
95
Cleanup is automatically registered via atexit, so you don't need to
96
call remove_printer() explicitly at interpreter shutdown.
97
"""
98
logger = logging.getLogger(name)
99
100
# Add a default handler if none exists yet, so messages are visible
101
# out of the box without any user configuration.
102
if
not
logger.handlers:
103
handler = logging.StreamHandler(sys.stderr)
104
handler.setFormatter(logging.Formatter(_DEFAULT_FORMAT))
105
logger.addHandler(handler)
106
107
# Set level to INFO to match the C++ default MsgStream threshold, but
108
# only if the caller has not already configured a level (NOTSET means
109
# no explicit level was set yet).
110
if
logger.level == logging.NOTSET:
111
logger.setLevel(logging.INFO)
112
113
# Don't propagate to the root logger — avoids double printing when
114
# the user has logging.basicConfig() or other root handlers configured.
115
logger.propagate =
False
116
117
def
printer_callback(level_int, tool_name, text):
118
"""Callback invoked by C++ for each message."""
119
# Look up Python logging level for the C++ message level (int key)
120
py_level = _LEVEL_MAP.get(level_int, logging.INFO)
121
# Pass tool name via extra dict for formatter access
122
logger.log(py_level, text.rstrip(), extra={
"tool"
: tool_name})
123
124
try
:
125
set_python_printer(printer_callback)
126
# Ensure cleanup at interpreter shutdown
127
atexit.register(remove_printer)
128
except
RuntimeError:
129
# Non-standalone (Athena/AthAnalysis) build: IMessagePrinter is not
130
# available. The logger is still usable for Python-side logging.
131
logger.debug(
132
"C++ message bridge unavailable (not a standalone build); "
133
"C++ tool messages will not be routed through Python logging."
134
)
135
136
return
logger
137
138
139
def
remove_printer
():
140
"""Remove the Python printer and restore default stdout printing."""
141
set_python_printer()
# Call with no arguments to reset
python.logging_bridge.install_printer
install_printer(name="atlas")
Definition
logging_bridge.py:65
python.logging_bridge.remove_printer
remove_printer()
Definition
logging_bridge.py:139
Generated on
for ATLAS Offline Software by
1.17.0