Skip to content

Feature/logging system #112

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

Merged
merged 21 commits into from
Jan 27, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
b9d44a9
first logging commit
Jan 19, 2021
87ff7bc
took main from develop logging commit
Jan 19, 2021
0299b10
Added docstring and annotations to logger, added logger tests
Jan 21, 2021
1408056
Added docstring and annotations to logger, added logger tests
Jan 21, 2021
3cd2e37
Removed merge conflicts
Jan 21, 2021
c644bc0
Fixed an issue with variable name
Jan 21, 2021
ea9c0f4
Fixed requirements.txt duplicate rows.
Jan 21, 2021
c1e0221
Fixed requirements.txt duplicate rows, again.
Jan 21, 2021
5f14fc6
Fixed merge suggestions in logger_customizer.py
Jan 22, 2021
41febf5
Fixed merge suggestions in logger_customizer.py
Jan 22, 2021
c07f18d
Fixed linting in test_logging, conftest and logger_customizer, still …
Jan 22, 2021
973b4fc
Took config from global config file,
Jan 22, 2021
8c07e19
Fixed tests, created new clientless logger fixture. Another fixture l…
Jan 23, 2021
551936d
Updated conftest and separated logger fixtures to new file, fix mergi…
Jan 23, 2021
310bd58
Fix requirements for merging from develop
Jan 23, 2021
e71328d
Finished logger_fixture, added logging config file to tests, added lo…
Jan 23, 2021
88713de
Added logger_fixture and config which werent added for some reason
Jan 23, 2021
91322d9
Changed logging config to be received by separate parameters for simp…
Jan 24, 2021
bca9c7c
removed logging_config file which is no longer needed
Jan 26, 2021
26f0a40
Fixing merge conflicts
Jan 27, 2021
1a7fd84
Fixing merge conflicts - missing whitespace on config.py.example
Jan 27, 2021
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: 0 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,6 @@ dmypy.json
# Pyre type checker
.pyre/


# VScode
.vscode/
app/.vscode/
Expand Down
17 changes: 14 additions & 3 deletions app/config.py.example
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ class Settings(BaseSettings):
env_file = ".env"


# general
# GENERAL
DOMAIN = 'Our-Domain'

# DATABASE
Expand All @@ -29,11 +29,11 @@ AVATAR_SIZE = (120, 120)
# API-KEYS
WEATHER_API_KEY = os.getenv('WEATHER_API_KEY')

# export
# EXPORT
ICAL_VERSION = '2.0'
PRODUCT_ID = '-//Our product id//'

# email
# EMAIL
email_conf = ConnectionConfig(
MAIL_USERNAME=os.getenv("MAIL_USERNAME") or "user",
MAIL_PASSWORD=os.getenv("MAIL_PASSWORD") or "password",
Expand All @@ -47,3 +47,14 @@ email_conf = ConnectionConfig(

# PATHS
STATIC_ABS_PATH = os.path.abspath("static")

# LOGGER
LOG_PATH = "./var/log"
LOG_FILENAME = "calendar.log"
LOG_LEVEL = "error"
LOG_ROTATION_INTERVAL = "20 days"
LOG_RETENTION_INTERVAL = "1 month"
LOG_FORMAT = ("<level>{level: <8}</level>"
" <green>{time:YYYY-MM-DD HH:mm:ss.SSS}</green>"
" - <cyan>{name}</cyan>:<cyan>{function}</cyan>"
" - <level>{message}</level>")
94 changes: 94 additions & 0 deletions app/internal/logger_customizer.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
import sys

from pathlib import Path
from loguru import logger, _Logger as Logger


class LoggerConfigError(Exception):
pass


class LoggerCustomizer:

@classmethod
def make_logger(cls, log_path: Path,
log_filename: str,
log_level: str,
log_rotation_interval: str,
log_retention_interval: str,
log_format: str) -> Logger:
"""Creates a logger from given configurations

Args:
log_path (Path): Path where the log file is located
log_filename (str):

log_level (str): The level we want to start logging from
log_rotation_interval (str): Every how long the logs
would be rotated
log_retention_interval (str): Amount of time in words defining
how long the log will be kept
log_format (str): The logging format

Raises:
LoggerConfigError: Error raised when the configuration is invalid

Returns:
Logger: Loguru logger instance
"""
try:
logger = cls.customize_logging(
file_path=Path(log_path) / Path(log_filename),
level=log_level,
retention=log_retention_interval,
rotation=log_rotation_interval,
format=log_format
)
except (TypeError, ValueError) as err:
raise LoggerConfigError(
f"You have an issue with the logger configuration: {err!r}, "
"fix it please")

return logger

@classmethod
def customize_logging(cls,
file_path: Path,
level: str,
rotation: str,
retention: str,
format: str
) -> Logger:
"""Used to customize the logger instance

Args:
file_path (Path): Path where the log file is located
level (str): The level wanted to start logging from
rotation (str): Every how long the logs would be
rotated(creation of new file)
retention (str): Amount of time in words defining how
long a log is kept
format (str): The logging format

Returns:
Logger: Instance of a logger mechanism
"""
logger.remove()
logger.add(
sys.stdout,
enqueue=True,
backtrace=True,
level=level.upper(),
format=format
)
logger.add(
str(file_path),
rotation=rotation,
retention=retention,
enqueue=True,
backtrace=True,
level=level.upper(),
format=format
)

return logger
13 changes: 13 additions & 0 deletions app/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,8 @@
from app.routers import (
agenda, dayview, email, event, invitation, profile, search, telegram)
from app.telegram.bot import telegram_bot
from app.internal.logger_customizer import LoggerCustomizer
from app import config


def create_tables(engine, psql_environment):
Expand All @@ -27,6 +29,16 @@ def create_tables(engine, psql_environment):
app.mount("/static", StaticFiles(directory=STATIC_PATH), name="static")
app.mount("/media", StaticFiles(directory=MEDIA_PATH), name="media")

# Configure logger
logger = LoggerCustomizer.make_logger(config.LOG_PATH,
config.LOG_FILENAME,
config.LOG_LEVEL,
config.LOG_ROTATION_INTERVAL,
config.LOG_RETENTION_INTERVAL,
config.LOG_FORMAT)
app.logger = logger


app.include_router(profile.router)
app.include_router(event.router)
app.include_router(agenda.router)
Expand All @@ -40,6 +52,7 @@ def create_tables(engine, psql_environment):


@app.get("/")
@app.logger.catch()
async def home(request: Request):
return templates.TemplateResponse("home.html", {
"request": request,
Expand Down
3 changes: 2 additions & 1 deletion requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ iniconfig==1.1.1
Jinja2==2.11.2
joblib==1.0.0
lazy-object-proxy==1.5.2
loguru==0.5.3
mypy==0.790
mypy-extensions==0.4.3
MarkupSafe==1.1.1
Expand Down Expand Up @@ -86,4 +87,4 @@ watchgod==0.6
websockets==8.1
word-forms==2.1.0
wsproto==1.0.0
zipp==3.4.0
zipp==3.4.0
1 change: 1 addition & 0 deletions tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
'tests.association_fixture',
'tests.client_fixture',
'tests.asyncio_fixture',
'tests.logger_fixture',
'smtpdfix',
]

Expand Down
31 changes: 31 additions & 0 deletions tests/logger_fixture.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import logging

import pytest
from _pytest.logging import caplog as _caplog # noqa: F401
from loguru import logger

from app import config
from app.internal.logger_customizer import LoggerCustomizer


@pytest.fixture(scope='module')
def logger_instance():
_logger = LoggerCustomizer.make_logger(config.LOG_PATH,
config.LOG_FILENAME,
config.LOG_LEVEL,
config.LOG_ROTATION_INTERVAL,
config.LOG_RETENTION_INTERVAL,
config.LOG_FORMAT)

return _logger


@pytest.fixture
def caplog(_caplog): # noqa: F811
class PropagateHandler(logging.Handler):
def emit(self, record):
logging.getLogger(record.name).handle(record)

handler_id = logger.add(PropagateHandler(), format="{message} {extra}")
yield _caplog
logger.remove(handler_id)
42 changes: 42 additions & 0 deletions tests/test_logger.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import logging

import pytest

from app.internal.logger_customizer import LoggerCustomizer, LoggerConfigError
from app import config


class TestLogger:
@staticmethod
def test_log_debug(caplog, logger_instance):
with caplog.at_level(logging.DEBUG):
logger_instance.debug('Is it debugging now?')
assert 'Is it debugging now?' in caplog.text

@staticmethod
def test_log_info(caplog, logger_instance):
with caplog.at_level(logging.INFO):
logger_instance.info('App started')
assert 'App started' in caplog.text

@staticmethod
def test_log_error(caplog, logger_instance):
with caplog.at_level(logging.ERROR):
logger_instance.error('Something bad happened!')
assert 'Something bad happened!' in caplog.text

@staticmethod
def test_log_critical(caplog, logger_instance):
with caplog.at_level(logging.CRITICAL):
logger_instance.critical("WE'RE DOOMED!")
assert "WE'RE DOOMED!" in caplog.text

@staticmethod
def test_bad_configuration():
with pytest.raises(LoggerConfigError):
LoggerCustomizer.make_logger(config.LOG_PATH,
config.LOG_FILENAME,
'eror',
config.LOG_ROTATION_INTERVAL,
config.LOG_RETENTION_INTERVAL,
config.LOG_FORMAT)