Logging In Rogue
Many Rogue and PyRogue modules emit useful log messages for transport bring-up, transaction debugging, protocol state, and application behavior. The important user-facing point is that these messages can be routed into the normal Python logging path, but C++-backed Rogue loggers still have their own Rogue-side filter threshold.
For new Python code, the recommended interface is to use the unified PyRogue
logging path so both Python-side loggers and C++-backed Rogue loggers are
configured through Python logging.
Internally, many C++-backed Rogue modules still use rogue.Logging. In the
recommended unified mode, those messages are forwarded into Python logging so
they appear in the same logging path as the rest of the application.
For a quick reference of common logger names and patterns, see Logger Names.
How To Think About It
For new PyRogue applications, the practical rule is:
turn on unified logging
configure the Python logger and the Rogue C++ filter for the subsystem you care about
The easiest way to do that is pyrogue.setLogLevel(...). It updates both the
Python logger level and the Rogue C++ filter for the normalized logger name.
If you are working in older code or a standalone Rogue transport script, first identify which side emitted the message:
Pure Python / PyRogue class: configure it with Python
logging.C++-backed Rogue object: configure it with
rogue.Logging.
If you are not sure, the practical rule is:
self._log = pyrogue.logInit(...)means Python logging.rogue::Logging::create("...")means Rogue C++ logging.
Python Logging In PyRogue
PyRogue nodes and many Python helper classes create loggers with
pyrogue.logInit() in [python/pyrogue/_Node.py](/Users/bareese/rogue/python/pyrogue/_Node.py).
That function ultimately returns a standard Python logger from
logging.getLogger(...).
Logger Naming Rules
The logger name starts with pyrogue and then appends type/class/path
information. Common patterns are:
pyrogue.Root.<RootSubclass>.<path>pyrogue.Device.<DeviceSubclass>.<path>pyrogue.Variable.<VariableSubclass>.<path>pyrogue.Command.<CommandSubclass>.<path>
Because the full node path is usually included after tree attachment, Python logger names are often predictable once you know the object path.
Examples from current code:
A Device named
Top.MyDevicewill typically log under a name beginning withpyrogue.Device.<ClassName>.Top.MyDevice.The file reader uses
pyrogue.FileReaderin [python/pyrogue/utilities/fileio/_FileReader.py](/Users/bareese/rogue/python/pyrogue/utilities/fileio/_FileReader.py).
How To Enable Python Logging
Use the standard Python logging API:
Recommended Usage
For a normal PyRogue application:
import logging
import pyrogue as pr
pr.setUnifiedLogging(True)
pr.setLogLevel('pyrogue', 'INFO')
pr.setLogLevel('udp', 'DEBUG')
This does three things:
Rogue C++ log messages are forwarded into Python
logging.The native Rogue stdout sink is disabled to avoid duplicate output.
In a running
Root, forwarded C++ messages can flow intoRoot.SystemLog.
Important: setUnifiedLogging(True) changes where Rogue C++ log records are
delivered, but it does not by itself lower the Rogue C++ filter threshold.
If you only call:
pr.setUnifiedLogging(True)
logging.getLogger('pyrogue.SrpV3').setLevel(logging.DEBUG)
then the Python logger is ready for debug output, but the Rogue C++ logger may
still be filtering at Error. In that case no forwarded DEBUG records
will be emitted. Use pyrogue.setLogLevel('SrpV3', 'DEBUG') or call
rogue.Logging.setFilter(...) explicitly.
If you are already constructing a Root, unifyLogs=True is a convenient
wrapper around the same behavior:
class MyRoot(pr.Root):
def __init__(self, **kwargs):
super().__init__(name='MyRoot', unifyLogs=True, **kwargs)
SystemLog Integration
Root mirrors Python log records into:
Root.SystemLogRoot.SystemLogLast
With unified logging enabled, forwarded C++ log records can appear there as well. Forwarded C++ records also carry additional metadata fields such as:
rogueCpprogueTidroguePidrogueLoggerrogueTimestamprogueComponent
This powers:
The PyDM System Log widget
pyrogue.interfaces.SystemLogMonitorSQL log capture via
SqlLogger
Example forwarded SystemLog entry:
{
"created": 1772812345.123456,
"name": "pyrogue.udp.Client",
"message": "UDP write call failed for 10.0.0.5: Connection refused",
"levelName": "WARNING",
"levelNumber": 30,
"rogueCpp": true,
"rogueTid": 48123,
"roguePid": 90210,
"rogueLogger": "pyrogue.udp.Client",
"rogueTimestamp": 1772812345.123456,
"rogueComponent": "udp"
}
Legacy C++ Logging
Rogue C++ code implements its own logging framework. In previous Rogue
releases, this logging had to be configured separately from Python
logging. That direct rogue.Logging API is still supported, and users
may still encounter it in older code, existing applications, standalone
transport scripts, or low-level debugging examples.
Today it is mainly useful for:
older code
standalone transport/protocol scripts
low-level debugging outside a PyRogue
Root
Example:
import rogue
rogue.Logging.setFilter('udp', rogue.Logging.Debug)
Useful direct C++ logging calls:
import rogue
rogue.Logging.setLevel(rogue.Logging.Warning)
rogue.Logging.setFilter('udp', rogue.Logging.Debug)
rogue.Logging.setFilter('pyrogue.packetizer', rogue.Logging.Debug)
Severity constants are:
rogue.Logging.Critical= 50rogue.Logging.Error= 40rogue.Logging.Thread= 35rogue.Logging.Warning= 30rogue.Logging.Info= 20rogue.Logging.Debug= 10
Filter matching is prefix-based. These are equivalent:
rogue.Logging.setFilter('udp', rogue.Logging.Debug)
rogue.Logging.setFilter('pyrogue.udp', rogue.Logging.Debug)
Logger Naming Rules
Each C++ logger is created with rogue::Logging::create("name").
Rogue then prepends pyrogue. internally, so:
Logging::create("udp.Client")becomespyrogue.udp.ClientLogging::create("packetizer.Controller")becomespyrogue.packetizer.Controller
Some logger names are static, and some are built dynamically at runtime.
Examples currently in the codebase:
pyrogue.udp.Clientpyrogue.udp.Serverpyrogue.rssi.controllerpyrogue.packetizer.Controllerpyrogue.SrpV0pyrogue.SrpV3pyrogue.xilinx.xvcpyrogue.xilinx.jtagpyrogue.fileio.StreamWriterpyrogue.prbs.txpyrogue.prbs.rx
Dynamic example:
pyrogue.stream.TcpCore.<addr>.Client.<port>pyrogue.stream.TcpCore.<addr>.Server.<port>
That dynamic pattern is constructed in [src/rogue/interfaces/stream/TcpCore.cpp](/Users/bareese/rogue/src/rogue/interfaces/stream/TcpCore.cpp).
How To Enable C++ Logging
Global level:
import rogue
rogue.Logging.setLevel(rogue.Logging.Debug)
rogue.Logging.setFilter('udp', rogue.Logging.Debug)
rogue.Logging.setFilter('pyrogue.udp', rogue.Logging.Debug)
If you need the forwarding pieces directly, the unified helper is equivalent to:
rogue.Logging.setForwardPython(True)
rogue.Logging.setEmitStdout(False)
Logger Names
If you already have the object, the most direct way to discover the logger name is to ask it:
print(my_obj.logName())
print(pyrogue.logName(my_obj))
For a broader reference of common logger names and patterns, see Logger Names.
Practical Recipes
Debug one Python subtree:
import pyrogue as pr
pr.setLogLevel('pyrogue.Device.MyDevice', 'DEBUG')
Debug one C++ subsystem in a PyRogue app:
import logging
import pyrogue as pr
pr.setUnifiedLogging(True)
pr.setLogLevel('xilinx', 'DEBUG')
Debug one C++ subsystem in a standalone script:
import rogue
rogue.Logging.setFilter('xilinx', rogue.Logging.Debug)
API Reference
Python: rogue.Logging
C++: rogue::Logging
Current Rough Edges
Module-specific docs now include local logging notes with the exact logger name or pattern for that module. Use those pages first when you already know which module you are debugging.