Description
Every instrument instance in a process shares one and the same logging.Logger object, so any per-instrument logging configuration silently applies to all instruments.
InstrumentBase.__init__ creates the logger with a module constant:
# src/qcodes/instrument/instrument_base.py
self.log: InstrumentLoggerAdapter = get_instrument_logger(self, __name__)
__name__ here is the fixed string "qcodes.instrument.instrument_base", so logging.getLogger() returns the same cached Logger for every instrument of every driver class. InstrumentLoggerAdapter wraps it per instrument, but the underlying Logger — which is what owns the level, the handlers and propagate — is global.
The same applies to VISA traffic: VisaInstrument and ip_to_visa both use the constant VISA_LOGGER (qcodes.instrument.instrument_base.com.visa).
Steps to reproduce
import logging
from qcodes.instrument_drivers.mock_instruments import DummyInstrument
a = DummyInstrument("inst_a")
b = DummyInstrument("inst_b")
print("same logger object :", a.log.logger is b.log.logger)
print("logger name :", a.log.logger.name)
a.log.logger.setLevel(logging.DEBUG)
print("after a.setLevel(DEBUG), b level:",
logging.getLevelName(b.log.logger.getEffectiveLevel()))
a.log.logger.propagate = False
print("after a.propagate=False, b.propagate:", b.log.logger.propagate)
Output:
same logger object : True
logger name : qcodes.instrument.instrument_base
after a.setLevel(DEBUG), b level: DEBUG
after a.propagate=False, b.propagate: False
Expected behaviour
It should be possible to configure logging for one instrument, or for one driver class, without affecting unrelated instruments. Concretely, a.log.logger.setLevel(logging.DEBUG) should not change the level of b.
Actual behaviour
a and b are the same Logger object, so setLevel, addHandler and propagate set on one instrument apply to every instrument in the process.
Why the existing filtering does not cover this
InstrumentLoggerAdapter adds an instrument_name field to each record and logger.filter_instrument filters on it. That solves handler-side filtering of an already configured handler, which is a different problem. It does not give per-instrument or per-driver log levels, per-instrument handler routing (e.g. one file per instrument), or any integration with standard logging configuration (logging.config.dictConfig, or the logger_levels section of qcodesrc.json) — all of which are keyed on logger names.
Because there is only one logger name, none of that is expressible today. For example there is currently no way to write "turn on DEBUG for my AMI430 driver" in qcodesrc.json; the closest available option raises the level for every instrument at once.
Suggested direction
Give instruments an opt-in logger name derived from the driver class and the instrument's name_parts, keeping it as a descendant of the current shared logger so that existing configuration is still inherited and the default behaviour is unchanged.
System
qcodes branch: main
qcodes commit: f6b9dd6
Description
Every instrument instance in a process shares one and the same
logging.Loggerobject, so any per-instrument logging configuration silently applies to all instruments.InstrumentBase.__init__creates the logger with a module constant:__name__here is the fixed string"qcodes.instrument.instrument_base", sologging.getLogger()returns the same cachedLoggerfor every instrument of every driver class.InstrumentLoggerAdapterwraps it per instrument, but the underlyingLogger— which is what owns the level, the handlers andpropagate— is global.The same applies to VISA traffic:
VisaInstrumentandip_to_visaboth use the constantVISA_LOGGER(qcodes.instrument.instrument_base.com.visa).Steps to reproduce
Output:
Expected behaviour
It should be possible to configure logging for one instrument, or for one driver class, without affecting unrelated instruments. Concretely,
a.log.logger.setLevel(logging.DEBUG)should not change the level ofb.Actual behaviour
aandbare the sameLoggerobject, sosetLevel,addHandlerandpropagateset on one instrument apply to every instrument in the process.Why the existing filtering does not cover this
InstrumentLoggerAdapteradds aninstrument_namefield to each record andlogger.filter_instrumentfilters on it. That solves handler-side filtering of an already configured handler, which is a different problem. It does not give per-instrument or per-driver log levels, per-instrument handler routing (e.g. one file per instrument), or any integration with standard logging configuration (logging.config.dictConfig, or thelogger_levelssection ofqcodesrc.json) — all of which are keyed on logger names.Because there is only one logger name, none of that is expressible today. For example there is currently no way to write "turn on DEBUG for my AMI430 driver" in
qcodesrc.json; the closest available option raises the level for every instrument at once.Suggested direction
Give instruments an opt-in logger name derived from the driver class and the instrument's
name_parts, keeping it as a descendant of the current shared logger so that existing configuration is still inherited and the default behaviour is unchanged.System
qcodes branch: main
qcodes commit: f6b9dd6