diff --git a/CHANGELOG.md b/CHANGELOG.md
index b330f8f..2fcb1e6 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -21,6 +21,7 @@
to and from dicts. (#87)
+ `peewee-moves` was updated to v2.0.1.
+ Documentation is now reStructuredText and is hosted by ReadTheDocs (#91)
++ Switched to using `loguru` for logging. (#94)
## v0.3.0 (2019-01-28)
diff --git a/requirements.txt b/requirements.txt
index 6ffe53f..aae8594 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -3,3 +3,4 @@ peewee==3.8.0
requests==2.21.0
celery[redis]==4.2.1
peewee-moves==2.0.1
+loguru==0.2.5
diff --git a/src/trendlines/__init__.py b/src/trendlines/__init__.py
index 0f282bf..c913555 100644
--- a/src/trendlines/__init__.py
+++ b/src/trendlines/__init__.py
@@ -1,5 +1,4 @@
# -*- coding: utf-8 -*-
-from trendlines._logging import setup_logging
-logger = setup_logging(to_console=True, to_file=False)
+from loguru import logger
from trendlines.app_factory import create_app
diff --git a/src/trendlines/_logging.py b/src/trendlines/_logging.py
index 59a0743..f11ae80 100644
--- a/src/trendlines/_logging.py
+++ b/src/trendlines/_logging.py
@@ -2,131 +2,56 @@
"""
"""
import logging
-import time
-import zlib
-from pathlib import Path
-from logging.handlers import RotatingFileHandler
+import sys
-LOG_FMT = (
- "%(asctime)s.%(msecs)03dZ"
- "|%(levelname)-8.8s"
- "|%(module)-18.18s"
- "|%(lineno)4d"
- "|%(funcName)-16.16s"
- "|%(message)s"
-)
-DATE_FMT = "%Y-%m-%dT%H-%M-%S"
-
-
-class CustomFormatter(logging.Formatter):
- # Record times in UTC. Because that's the smart thing to do.
- converter = time.gmtime
-
- def format(self, record):
- return super(CustomFormatter, self).format(record)
-
-
-def setup_logging(to_console=True, to_file=False, log_path=None):
+def setup_logging(logger, to_console=True, to_file=False, log_path=None):
"""
Setup logging for this project.
Parameters
----------
+ logger : :class:`loguru.Logger`
+ The Loguru logger object.
to_console : bool, optional
If ``True``, enable logging to the console.
to_file : bool, optional
If ``True``, enable logging to a file.
log_path : str, optional
The file to log to, if ``to_file`` is ``True``.
-
- Returns
- -------
- logger : :class:`logging.Logger`
- The created logger instance.
"""
- logger = logging.getLogger("trendlines")
- logger.setLevel(logging.DEBUG)
-
if to_console:
_setup_console_logging(logger)
if to_file:
- _setup_file_logging(logger, Path(log_path))
-
- return logger
-
-
-# Custom namer and rotator
-# Taken from https://docs.python.org/3/howto/logging-cookbook.html
-def _gzip_namer(name):
- return name + ".gz"
-
-
-def _gzip_rotator(source, dest):
- """
- Compress the source file into the dest.
-
- Keeps the most recent rotation uncompressed.
-
- Parameters
- ----------
- source : :class:`pathlib.Path`
- dest : :class:`pathlib.Path`
- """
- with open(str(source), 'rb') as open_source:
- data = open_source.read()
- compressed = zlib.compress(data)
- with open(str(dest), 'wb') as open_dest:
- open_dest.write(compressed)
-
- # Keep a copy of the most recent rotation uncompressed to make things
- # easier for users.
- source.replace(dest + ".1")
-
- source.unlink()
+ _setup_file_logging(logger, log_path)
def _setup_file_logging(logger, log_path):
"""
Parameters
----------
- logger :
+ logger : :class:`loguru.logger`
The logger to modify
log_path : :class:`pathlib.Path`
The file and path to log to.
"""
- name = "File Handler"
-
- # Make our log dir and file.
- log_path.mkdir(mode=0x0755, parents=True, exist_ok=True)
- log_path.chmod(0x0664)
+ logger.add(log_path,
+ rotation="1 week",
+ retention="20 weeks",
+ compression="tar.gz",
+ format='{time:YYYY-MM-DD HH:mm:ss.SSSZZ} | {level: <8} | {name}:{function}:{line} - {message}',
+ )
- # Create the handler and have it compress old files.
- handler = RotatingFileHandler(str(log_path), maxBytes=1e7) # ~10MB
- handler.rotator = _gzip_rotator
- handler.namer = _gzip_namer
-
- handler.setLevel(logging.DEBUG)
- handler.setFormatter(CustomFormatter(LOG_FMT, DATE_FMT))
- handler.set_name(name)
- if name not in [h.name for h in logger.handlers]:
- logger.addHandler(handler)
- logger.info("File logging initialized.")
+ logger.info("File logging initialized.")
def _setup_console_logging(logger):
"""
Parameters
----------
- logger : :class:`logging.Logger`
+ logger : :class:`loguru.logger`
+ The logger instance to modify.
"""
- name = "Console Handler"
-
- # Create the handler. Console handlers are easy.
- handler = logging.StreamHandler()
- handler.setLevel(logging.DEBUG)
- handler.setFormatter(CustomFormatter(LOG_FMT, DATE_FMT))
- handler.set_name(name)
- if name not in [h.name for h in logger.handlers]:
- logger.addHandler(handler)
- logger.info("Console logging initialized.")
+ # This is only here in case I decide I want to make changes. For now
+ # it's a noop.
+ logger.info("Console logging initialized.")
diff --git a/src/trendlines/app_factory.py b/src/trendlines/app_factory.py
index 9cb0a48..d2fb66e 100644
--- a/src/trendlines/app_factory.py
+++ b/src/trendlines/app_factory.py
@@ -18,7 +18,7 @@ def create_app():
"""
Primary application factory.
"""
- _logging.setup_logging()
+ _logging.setup_logging(logger)
logger.debug("Creating app.")
app = Flask(__name__)
diff --git a/tests/conftest.py b/tests/conftest.py
index 068a504..f77a97e 100644
--- a/tests/conftest.py
+++ b/tests/conftest.py
@@ -2,17 +2,30 @@
"""
Global PyTest fixtures.
"""
+import logging
from pathlib import Path
import pytest
+from _pytest.logging import caplog as _caplog
from trendlines import db
+from trendlines import logger
from trendlines.app_factory import create_app
from trendlines.app_factory import create_db
from trendlines.orm import DataPoint
from trendlines.orm import Metric
+@pytest.fixture
+def caplog(_caplog):
+ class PropogateHandler(logging.Handler):
+ def emit(self, record):
+ logging.getLogger(record.name).handle(record)
+ handler_id = logger.add(PropogateHandler(), format="{message}")
+ yield _caplog
+ logger.remove(handler_id)
+
+
@pytest.fixture
def app():
"""