Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Pcvl 769 logging user documentation #452

Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions docs/source/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,7 @@ If you are using Perceval for academic work, please cite the `Perceval white pap
reference/processor
reference/postselect
reference/pdisplay
reference/logging
reference/qiskit_converter
reference/stategenerator
reference/scaleway_session
Expand Down
2 changes: 1 addition & 1 deletion docs/source/legacy.rst
Original file line number Diff line number Diff line change
Expand Up @@ -193,7 +193,7 @@ The :code:`Simulator` is also optimized to simulate a whole input distribution i
|{_:0},0,{_:0},0>: 0.020250000000000004
|{_:0},0,{_:1},0>: 0.002250000000000002
}
>>> simulator.set_min_detected_photon_filter(1)
>>> simulator.set_min_detected_photons_filter(1)
>>> probs = simulator.probs_svd(input_distribution)
>>> print("physical performance:", probs["physical_perf"])
>>> print("output distribution:", probs["results"])
Expand Down
200 changes: 200 additions & 0 deletions docs/source/reference/logging.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,200 @@
Logging
=======

To log with Perceval you can either use a built-in Perceval logger or the python one. By default, our logger will be used.

To log the perceval messages with the python logger, use this method:

.. code-block:: python

from perceval.utils import use_python_logger
use_python_logger()

To switch back to using perceval logger, use:

.. code-block:: python

from perceval.utils import use_perceval_logger
use_perceval_logger()

.. note:: If you use the python logger, use directly the module logging of python to configure it (except for channel configuration)

This module defines functions and classes which implement a flexible event logging system for any Perceval script.

It is build to work similarly as the python logging module.

Logger
------

A logger instance is created the first time Perceval is imported.

In order to use it you'll just need to import it:

.. code-block:: python

from perceval.utils import logger

To log message, you can use it the same way as the python logger:

.. code-block:: python

from perceval.utils import logger
logger.info('I log something as info')


Saving log in file
^^^^^^^^^^^^^^^^^^

By default the log are not saved in a file.

If order to activate / deactivate the logging in a file you can use the following methods:

.. code-block:: python

from perceval.utils import logger
logger.enable_file()
logger.disable_file()

If this feature is activated, the log file will be in the perceval persistent data folder and the path of the file will be printed at the beginning of your perceval script.

Levels
------

You can use the logger to log message at different level. Each level represent a different type of message.

The level are listed by ascending order of importance in the following table.

.. list-table::
:header-rows: 1
:stub-columns: 1
:width: 100%
:align: center

* - Log level
- Perceval call
- Usage
* - DEBUG
- ``level.debug``
- Detailed information, typically of interest only when diagnosing problems.
* - INFO
- ``level.info``
- Confirmation that things are working as expected.
* - WARNING
- ``level.warn``
- An indication that something unexpected happened, or indicative of some problem in the near future. The software is still working as expected.
* - ERROR
- ``level.error``
- Due to a more serious problem, the software has not been able to perform some function.
* - CRITICAL
- ``level.critical``
- A serious error, indicating that the program itself is unable to continue running normally or has crashed.

Example
^^^^^^^

.. code-block:: python

from perceval.utils import logger
logger.info('I log something as info')
logger.critical('I log something as critical')

Channels
--------

You can also log in a specific channel. A channel is like a category.
MarionQuandela marked this conversation as resolved.
Show resolved Hide resolved
Each channel can have its own configuration, which means each channel can have a different level.
If the channel is not specified, the message will be log in the ``user`` channel.

.. note:: If you are a Perceval user, you should only write log in the channel ``user``.

.. list-table::
:header-rows: 1
:stub-columns: 1
:width: 100%
:align: center

* - Channel
- Default level
- Usage
* - ``general``
- off
- General info: Technical info, track the usage of features
* - ``resources``
- off
- Usage info about our backends or remote platform GPU (exqalibur)
* - ``user``
- warning
- Channel to use as a Perceval user & warnings (such as deprecated methods or arguments)

Example
^^^^^^^

.. code-block:: python

from perceval.utils.logging import logger, channel
logger.info('I log something as info in channel user', channel.user)

To set a level for a channel, use the following method:

.. code-block:: python

from perceval.utils import logger
logger.set_level(level, channel)

Example
^^^^^^^

.. code-block:: python

from perceval.utils import logger, logging
logger.set_level(logging.level.info, logging.channel.general)

Logger configuration
--------------------

For logging to be useful, it needs to be configured, meaning setting the levels for each channel and if log are saved in a file.
Setting a level for a channel means that any log with a less important level will not be displayed/saved.

In most cases, only the user & general channel needs to be so configured, since all relevant messages will be logged here.

Example
^^^^^^^

.. code-block:: python

from perceval.utils.logging import logger, channel, level
logger.enable_file()
logger.set_level(level.info, channel.resources)
logger.set_level(level.err, channel.general)

.. note:: The logger configuration can be saved on your hard drive so you don't have to configure the logger each time you use perceval. When saved, it is written to a file in Perceval persistent data folder.

In order to configure it you have to use the :class:`LoggerConfig`.

.. automodule:: perceval.utils.logging.config
:members:

After configuring your LoggerConfig, you can apply it to the current logger:

.. code-block:: python

from perceval.utils import logger, logging
logger_config = logging.LoggerConfig()
logger_config.enable_file()
logger_config.set_level(logging.level.info, logging.channel.user)
logger.apply_config(logger_config)

Log format
----------

On the console the log will appear with the format:

[log_level] message

In the file, the log will be save to the format:

[yyyy-mm-dd HH:MM:SS.fff]channel_first_letter[level_first_letter] message

Log exceptions
--------------
If the general channel level is at least on critical and save in file is enable, uncaught exception will be logged
7 changes: 7 additions & 0 deletions docs/source/tools.rst
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,13 @@ Perceval offers a high level function, ``pcvl.pdisplay()``, to display circuits

See :ref:`pdisplay`

Logging messages
----------------

Perceval offers a log management package, ``pcvl.utils.logger``, to log messages.

See :ref:`Logging`


Matrices
^^^^^^^^
Expand Down
2 changes: 1 addition & 1 deletion perceval/utils/logging/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@

def _my_excepthook(excType, excValue, this_traceback):
# only works for the main thread
logger.error("Uncaught exception!", channel=channel.general,
logger.critical("Uncaught exception!", channel=channel.general,
exc_info=(excType, excValue, this_traceback))


Expand Down
44 changes: 44 additions & 0 deletions perceval/utils/logging/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,11 @@
_USE_PYTHON_LOGGER = "use_python_logger"
_CHANNEL_NAMES = ["user", "general", "resources"]


class LoggerConfig(dict):
"""This class represent the logger configuration as a dictionary and can be used to save it into persistent data.
On class initialization, the configuration will be loaded from persistent data.
"""
def __init__(self):
super().__init__()
self.reset()
Expand All @@ -50,6 +54,11 @@ def _init_channel(self, channel: exqalibur_logging.channel, level: exqalibur_log
self[_CHANNELS][channel.name]["level"] = level.name

def reset(self):
"""Reset the logger configuration to its default value, which is:
- Disable file
- Channel user at level warning
- Channels general & resources off
"""
self[_USE_PYTHON_LOGGER] = False
self[_ENABLE_FILE] = False
self[_CHANNELS] = {}
Expand All @@ -74,13 +83,48 @@ def _load_from_persistent_data(self):
warnings.warn(UserWarning(f"Incorrect logger config, try to reset and save it. {e}"))

def set_level(self, level: exqalibur_logging.level, channel: exqalibur_logging.channel):
"""Set the level of a channel in the configuration

Warning: this will not change the current logger level but only the level of the channel in the current LoggerConfig instance


:param level: _description_
:param channel: _description_
"""
self[_CHANNELS][channel.name]["level"] = level.name

def use_python_logger(self):
"""Set the config to use the python logger

Warning: this will not change the current logger level but only the level of the channel in the current LoggerConfig instance
"""
self[_USE_PYTHON_LOGGER] = True

def use_perceval_logger(self):
"""Set the config to use the perceval logger

Warning: this will not change the current logger level, but only the level of the channel in the current LoggerConfig instance
"""
self[_USE_PYTHON_LOGGER] = False

def python_logger_is_enabled(self):
return self[_USE_PYTHON_LOGGER]

def enable_file(self):
"""Enable to save the log into a file in the configuration

Warning: this will not change the current logger file saving, but only the file saving of the current LoggerConfig instance
"""
self[_ENABLE_FILE] = True

def disable_file(self):
"""Disable to save the log into a file in the configuration

Warning: this will not change the current logger file saving, but only the file saving of the current LoggerConfig instance
"""
self[_ENABLE_FILE] = False

def save(self):
"""Save the current logger configuration in the persistent data
"""
self._persistent_data.save_config({_LOGGING: dict(self)})
19 changes: 17 additions & 2 deletions perceval/utils/logging/loggers.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,10 @@


class ALogger(ABC):
@abstractmethod
def apply_config(self, config: LoggerConfig):
pass

@abstractmethod
def enable_file(self):
pass
Expand Down Expand Up @@ -102,8 +106,7 @@ def initialize(self):
else:
exq_log.initialize()

self._config = LoggerConfig()
self._configure_logger()
self.apply_config(LoggerConfig())

def _configure_logger(self):
if _ENABLE_FILE in self._config and self._config[_ENABLE_FILE]:
Expand All @@ -128,6 +131,13 @@ def _configure_logger(self):
exq_log.level.__members__[level],
exq_log.channel.__members__[channel])

def apply_config(self, config: LoggerConfig):
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To avoid the inability to change the logger type, from inside a given logger, you can define an apply_logger_config() function outside of the logger classes. It would remove both user warnings.

if config.python_logger_is_enabled():
warnings.warn(UserWarning("Cannot change type of logger this way, use instead perceval.utils.use_python_logger"))
self._config = config
self._configure_logger()


def get_log_file_path(self):
return path.join(PersistentData().directory, "logs", "perceval.log")

Expand Down Expand Up @@ -199,6 +209,11 @@ def _get_levelno(self, channel: str):
else:
return 60

def apply_config(self, config: LoggerConfig):
if not config.python_logger_is_enabled():
warnings.warn(UserWarning("Cannot change type of logger this way, use instead perceval.utils.use_perceval_logger"))
self._config = config

def _message_has_to_be_logged(self, record) -> bool:
if "channel" in record.__dict__:
if record.levelno < self._level[record.channel]:
Expand Down
Loading