Skip to content

Commit

Permalink
Merge cfcfe06 into 2fda6cf
Browse files Browse the repository at this point in the history
  • Loading branch information
dougthor42 committed Feb 25, 2019
2 parents 2fda6cf + cfcfe06 commit a6604df
Show file tree
Hide file tree
Showing 6 changed files with 35 additions and 96 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Expand Up @@ -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)
Expand Down
1 change: 1 addition & 0 deletions requirements.txt
Expand Up @@ -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
3 changes: 1 addition & 2 deletions 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
111 changes: 18 additions & 93 deletions src/trendlines/_logging.py
Expand Up @@ -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='<green>{time:YYYY-MM-DD HH:mm:ss.SSSZZ}</green> | <level>{level: <8}</level> | <cyan>{name}</cyan>:<cyan>{function}</cyan>:<cyan>{line}</cyan> - <level>{message}</level>',
)

# 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.")
2 changes: 1 addition & 1 deletion src/trendlines/app_factory.py
Expand Up @@ -18,7 +18,7 @@ def create_app():
"""
Primary application factory.
"""
_logging.setup_logging()
_logging.setup_logging(logger)

logger.debug("Creating app.")
app = Flask(__name__)
Expand Down
13 changes: 13 additions & 0 deletions tests/conftest.py
Expand Up @@ -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():
"""
Expand Down

0 comments on commit a6604df

Please sign in to comment.