Skip to content

All instruments share a single logging.Logger, so per-instrument log configuration leaks between instruments #8522

Description

@ymampaey

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

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions