Skip to content

Commit

Permalink
add log module to desiutil
Browse files Browse the repository at this point in the history
  • Loading branch information
weaverba137 committed Feb 28, 2017
1 parent 81cb07d commit 07751e6
Show file tree
Hide file tree
Showing 3 changed files with 169 additions and 2 deletions.
8 changes: 6 additions & 2 deletions doc/changes.rst
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,14 @@ Change Log
1.9.3 (unreleased)
------------------

* Added new :mod:`desiutil.sklearn` module and :class:`distutils.sklearn.GaussianMixtureModel` class to save and sample from a Gaussian mixture model.
* Added new functions for creating all-sky maps (`PR #52`_) with an accompanying tutorial notebook in `doc/nb/`.
* Added new :mod:`desiutil.sklearn` module and
:class:`distutils.sklearn.GaussianMixtureModel` class to save and
sample from a Gaussian mixture model.
* Added new functions for creating all-sky maps (`PR #52`_) with an
accompanying tutorial notebook in `doc/nb/`.
* Add option to :command:`fix_permissions.sh` to remove group-writeability for
"official" data. Also, make sure that files and directories are group-readable.
* Moved logging infrastructure from desispec.

.. _`PR #52`: https://github.com/desihub/desiutil/pull/52

Expand Down
104 changes: 104 additions & 0 deletions py/desiutil/log.py
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
59 changes: 59 additions & 0 deletions py/desiutil/test/test_log.py
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__)

0 comments on commit 07751e6

Please sign in to comment.