Configuration and Logging

The configurable settings are stored in a dictionary in aca_view.config.SETTINGS. You can inspect it in the python terminal:

>>> import aca_view.config
>>> aca_view.config.SETTINGS['aca_widget']['window_size']
16

and you can edit, save it:

>>> import aca_view.config
>>> aca_view.config.SETTINGS['aca_widget']['window_size'] = 14
>>> aca_view.config.SETTINGS.save()

and retrieve it in subsequent sessions:

>>> import aca_view.config
>>> aca_view.config.SETTINGS['aca_widget']['window_size']
14

ACA-view also includes a very simple settings editor.

How to Configure the Logging Module

Logging in aca_view grew to enable these two requirements:

  • to serialize log messages from child processes into a single log.

  • to be able to switch on/off certain messages, depending on the case.

For this reason, configuration is not trivial. To configure the logging module, one follows the logging.config module documentation. What follows is an elaborate configuration which includes logging configuration. I have used it to log function execution times. It differs from the default in the following:

  • specifies a formatter, called ‘time’, that will be used to write log entries in a way that is easy to parse,

  • creates a logging.FileHandler, called ‘time’, to keep the log messages from the timing loggers,

  • it sets the log level of the aca_view logger to DEBUG

  • it sets the log level of all descendants (aca_view.*) to WARNING, except aca_view.timing which is se to DEBUG

  • set the handler of the aca_view.timing logger to ‘time’.

{
  "refresh_rate": 40,
  "sampling_rate": 2.05,
  "color_scheme": "qdarkstyle",
  "data_service": {
    "max_workers": 4,
    "multi_process": true
  },
  "logging": {
    "version": 1,
    "formatters": {
      "default": {
        "format": "%(levelname)-8s %(module)25s.%(funcName)-20s - %(message)s"
      },
      "times": {
        "format": "%(relativeCreated)d, %(message)s"
      }
    },
    "handlers": {
      "console": {
        "class": "logging.StreamHandler",
        "formatter": "default",
        "level": "WARNING"
      },
      "file": {
        "class": "logging.FileHandler",
        "formatter": "default",
        "filename": "/Users/javierg/SAO/aca_view.log",
        "mode": "w",
        "level": "DEBUG"
      },
      "time": {
        "class": "logging.FileHandler",
        "formatter": "times",
        "filename": "/Users/javierg/SAO/aca_view_timing.log",
        "mode": "w",
        "level": "DEBUG"
      }
    },
    "loggers": {
      "aca_view": {
        "level": "DEBUG",
        "handlers": [
          "console",
          "file"
        ],
        "propagate": false
      },
      "aca_view.main": {
        "level": "WARNING"
      },
      "aca_view.threading": {
        "level": "WARNING"
      },
      "aca_view.telemetry_traversal": {
        "level": "WARNING"
      },
      "aca_view.widgets": {
        "level": "WARNING"
      },
      "aca_view.data_service": {
        "level": "WARNING"
      },
      "aca_view.timing": {
        "level": "DEBUG",
        "handlers": ["time"]
      },
      "maude": {
        "level": "WARNING"
      }
    },
    "root": {
      "level": "WARNING",
      "handlers": [
        "console"
      ]
    }
  }
}

API

Configuratio for the ACA-view application.

The setting are stored in a QSettings instance. This is the process by which settings are loaded:

  • When ACA-view starts, it imports aca_view.config, which instantiates aca_view.config.SETTINGS. In the constructor, the settings are:

    • First set from a hardwired private data member.

    • Updated from the settings in persistent store. The exact location of this store is OS-dependent.

  • The command-line arguments are then parsed by calling aca_view.config.init. This function can modify values in aca_view.config.SETTINGS.

  • The configuration of the data service is a special case which is handled by aca_view.data.config.validate_config. This function is called when parsing the command-line arguments, and its return value is stored in aca_view.config.COMMAND_LINE_OPTIONS. The reason for this is that we do not want to accidentally include these values in the permanent store.

Warning

Changes in aca_view.config.SETTINGS are not automatically saved in the persistent store. To persist changes one has to call QSettings.save.

Depending on how sub-processes are created, this can mean that sub-processes do not get any temporary changes to aca_view.config.SETTINGS. This is not a problem in most cases. The main case where we use sub-processes is within the dat service, and the data service configuration is passed explicitly to the subprocesses.

In any case, the behavior of the configuration in multi-processes is not something I have paid much attention.

aca_view.config.COMMAND_LINE_OPTIONS = {}

Custom (non-persistent) settings derived from command line.

class aca_view.config.QSettings(organization='ska.cxc', application='aca_view', defaults=None, load=True)[source]

Store settings in an instance of QtCore.QSettings.

QtCore.QSettings stores config variables in a list of key/value pairs. It mimics a hierarchical structure by using keys with slashes in them like directory names in linux. The nodes in this hierarchical structure are called “groups”.

In this class we map a python dictionary to a QtCore.QSettings structure, where each key in dictionary corresponds to a group. A nested key like “a/b/c” corresponds to a dictionary {‘a’: {‘b’: {‘c’: <some value>}}}.

QtCore.QSettings stores values using the QVariant class. This is efficient but not very readable. Some basic types are stored in text format, by-passing the QVariant class.

Note

Some confusion can arise because there are three levels of settings:

  • the settings in this dictionary class (what you see)

  • the settings in the QtCore.QSettings inside this class (self._q_settings)

  • the settings in the persistent store (a file)

When this class is instanciated, it starts with default values, then QtCore.QSettings loads the persistent settings, and the two are combined. As a result, one can always reconstruct the settings from file at any time.

Some times it is useful for debugging to see what is in _q_settings. For example:

aca_view.config.SETTINGS._q_settings.allKeys()
aca_view.config.SETTINGS._q_settings.value('aca_widget/window_size/item_type')
clear(deep=True)[source]

Clear current values. Optionally also clear the values in QtCore.QSettings.

Note that this does not affect the persistent store. To actually wipe out all settings one has to do:

SETTINGS.clear(deep=True) SETTINGS.save()

load()[source]

Clear current values, load defaults and load from persistent config.

save(prune=True)[source]

Save the in-memory dictionary to the persistent store, optionally pruning deleted branches.

aca_view.config.init()[source]

Initialize the configuration.

The settings are already loaded from the persistent store without calling this function. This does the following:

It is assumed that this is called before the logging framework has been configured. For this reason, this function does not try to write to any log, and instead it returns a list of pairs (log level, message).

aca_view.config.parse_command_line()[source]

Parse command-line arguments and set the corresponding values in config.SETTINGS