-
Notifications
You must be signed in to change notification settings - Fork 9
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
3 changed files
with
169 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,104 @@ | ||
# Licensed under a 3-clause BSD style license - see LICENSE.rst | ||
# -*- coding: utf-8 -*- | ||
""" | ||
============ | ||
desiutil.log | ||
============ | ||
Utility functions to dump log messages. We can have something specific for | ||
DESI in the future but for now we use the standard Python. | ||
""" | ||
from __future__ import absolute_import, division, print_function | ||
import logging | ||
|
||
desi_logger = None | ||
|
||
# Just for convenience to avoid importing logging, we duplicate the logging levels | ||
DEBUG = logging.DEBUG # Detailed information, typically of interest only when diagnosing problems. | ||
INFO = logging.INFO # Confirmation that things are working as expected. | ||
WARNING = logging.WARNING # An indication that something unexpected happened, or indicative of some problem | ||
# in the near future (e.g. "disk space low"). The software is still working as expected. | ||
ERROR = logging.ERROR # Due to a more serious problem, the software has not been able to perform some function. | ||
CRITICAL = logging.CRITICAL # A serious error, indicating that the program itself may be unable to continue running. | ||
|
||
# see example of usage in test/test_log.py | ||
|
||
|
||
def get_logger(level=None, timestamp=False): | ||
"""Returns a default DESI logger. | ||
Parameters | ||
---------- | ||
level : :class:`int`, optional | ||
Debugging level. | ||
timestamp : :class:`bool`, optional | ||
If set, include a time stamp in the log message. | ||
Returns | ||
------- | ||
:class:`logging.Logger` | ||
A logging object configured with the DESI defaults. | ||
Notes | ||
----- | ||
* If environment variable :envvar:`DESI_LOGLEVEL` exists and has value | ||
DEBUG, INFO, WARNING or ERROR (upper or lower case), it overules the level | ||
argument. | ||
* If :envvar:`DESI_LOGLEVEL` is not set and `level` is ``None``, | ||
the default level is set to INFO. | ||
""" | ||
from os import environ | ||
from sys import stdout | ||
global desi_logger | ||
try: | ||
desi_level = environ["DESI_LOGLEVEL"] | ||
except KeyError: | ||
desi_level = None | ||
if desi_level is not None and (desi_level != ""): | ||
# | ||
# Forcing the level to the value of DESI_LOGLEVEL, | ||
# ignoring the requested logging level. | ||
# | ||
desi_level = desi_level.upper() | ||
dico = {"DEBUG": DEBUG, | ||
"INFO": INFO, | ||
"WARNING": WARNING, | ||
"ERROR": ERROR} | ||
try: | ||
level = dico[desi_level] | ||
except KeyError: | ||
# Amusingly I would need the logger to dump a warning here | ||
# but this recursion can be problematic. | ||
message = ("Ignore DESI_LOGLEVEL='{0}' " + | ||
"(only recognize {1}).").format(desi_level, | ||
', '.join(dico.keys())) | ||
print(message) | ||
|
||
if level is None: | ||
level = INFO | ||
|
||
if desi_logger is not None : | ||
if level is not None : | ||
desi_logger.setLevel(level) | ||
return desi_logger | ||
|
||
desi_logger = logging.getLogger("DESI") | ||
|
||
desi_logger.setLevel(level) | ||
|
||
while len(desi_logger.handlers) > 0: | ||
h = desi_logger.handlers[0] | ||
desi_logger.removeHandler(h) | ||
|
||
ch = logging.StreamHandler(stdout) | ||
|
||
if timestamp: | ||
formatter = logging.Formatter('%(levelname)s:%(filename)s:%(lineno)s:%(funcName)s:%(asctime)s: %(message)s', datefmt='%Y-%m-%dT%H:%M:%S') | ||
else: | ||
formatter = logging.Formatter('%(levelname)s:%(filename)s:%(lineno)s:%(funcName)s: %(message)s') | ||
|
||
ch.setFormatter(formatter) | ||
|
||
desi_logger.addHandler(ch) | ||
|
||
return desi_logger |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,59 @@ | ||
# Licensed under a 3-clause BSD style license - see LICENSE.rst | ||
# -*- coding: utf-8 -*- | ||
"""Test desiutil.log. | ||
""" | ||
from __future__ import absolute_import, print_function | ||
import unittest | ||
import desiutil.log as l | ||
|
||
class TestLog(unittest.TestCase): | ||
"""Test desispec.log | ||
""" | ||
|
||
def setUp(self): | ||
"""Reset the cached logging object for each test. | ||
""" | ||
from os import environ | ||
try: | ||
self.desi_level = environ['DESI_LOGLEVEL'] | ||
except KeyError: | ||
self.desi_level = None | ||
l.desi_logger = None | ||
|
||
def test_log(self): | ||
"""Test basic logging functionality. | ||
""" | ||
for level in (None, l.DEBUG, l.INFO, l.WARNING, l.ERROR): | ||
logger = l.get_logger(level) | ||
print("With the requested debugging level={0}:".format(level)) | ||
if self.desi_level is not None and (self.desi_level != "" ): | ||
print(" (but overuled by env. DESI_LOGLEVEL='{0}')".format(self.desi_level)) | ||
print("--------------------------------------------------") | ||
logger.debug("This is a debugging message.") | ||
logger.info("This is an informational message.") | ||
logger.warning("This is a warning message.") | ||
logger.error("This is an error message.") | ||
logger.critical("This is a critical error message.") | ||
|
||
def test_log_with_timestamp(self): | ||
"""Test logging with timestamps. | ||
""" | ||
for level in (None, l.DEBUG, l.INFO, l.WARNING, l.ERROR): | ||
logger = l.get_logger(level, timestamp=True) | ||
print("With the requested debugging level={0}:".format(level)) | ||
if self.desi_level is not None and (self.desi_level != "" ) : | ||
print(" (but overuled by env. DESI_LOGLEVEL='{0}'):".format(self.desi_level)) | ||
print("--------------------------------------------------") | ||
logger.debug("This is a debugging message.") | ||
logger.info("This is an informational message.") | ||
logger.warning("This is a warning message.") | ||
logger.error("This is an error message.") | ||
logger.critical("This is a critical error message.") | ||
|
||
|
||
def test_suite(): | ||
"""Allows testing of only this module with the command:: | ||
python setup.py test -m <modulename> | ||
""" | ||
return unittest.defaultTestLoader.loadTestsFromName(__name__) |