From b9d44a979be974c2b86bc42a35e642b7ca7f56c7 Mon Sep 17 00:00:00 2001 From: Liad Noam Date: Wed, 20 Jan 2021 01:26:57 +0200 Subject: [PATCH 01/16] first logging commit --- app/__init__.py | 27 +++++++++ app/internal/custom_logging.py | 99 +++++++++++++++++++++++++++++++ app/internal/logger_util.py | 0 app/internal/logging_config.json | 10 ++++ app/internal/logging_system.py | 27 +++++++++ app/main.py | 26 ++++++-- database.db | Bin 0 -> 28672 bytes 7 files changed, 184 insertions(+), 5 deletions(-) create mode 100644 app/internal/custom_logging.py create mode 100644 app/internal/logger_util.py create mode 100644 app/internal/logging_config.json create mode 100644 app/internal/logging_system.py create mode 100644 database.db diff --git a/app/__init__.py b/app/__init__.py index e69de29b..3bd97917 100644 --- a/app/__init__.py +++ b/app/__init__.py @@ -0,0 +1,27 @@ + +from fastapi import FastAPI +from app.internal.custom_logging import CustomizeLogger +from pathlib import Path +# from fastapi import Request +# import uvicorn +import logging +# from fastapi.staticfiles import StaticFiles +# from fastapi.templating import Jinja2Templates + + + +# logger = logging.getLogger(__name__) + +config_path=Path(__file__).parent / 'internal' +config_path = config_path.absolute() / "logging_config.json" + + +def create_app() -> FastAPI: + app = FastAPI(title='Calendar', debug=False) + logger = CustomizeLogger.make_logger(config_path) + app.logger = logger + + return app + + +app = create_app() \ No newline at end of file diff --git a/app/internal/custom_logging.py b/app/internal/custom_logging.py new file mode 100644 index 00000000..37318b1c --- /dev/null +++ b/app/internal/custom_logging.py @@ -0,0 +1,99 @@ +# Custom Logger Using Loguru + +import logging +import sys +from pathlib import Path +from loguru import logger +import json + + +# class InterceptHandler(logging.Handler): +# loglevel_mapping = { +# 50: 'CRITICAL', +# 40: 'ERROR', +# 30: 'WARNING', +# 20: 'INFO', +# 10: 'DEBUG', +# 0: 'NOTSET', +# } + +# def emit(self, record): +# try: +# level = logger.level(record.levelname).name +# except AttributeError: +# level = self.loglevel_mapping[record.levelno] + +# frame, depth = logging.currentframe(), 2 +# while frame.f_code.co_filename == logging.__file__: +# frame = frame.f_back +# depth += 1 + +# log = logger.bind(request_id='app') +# log.opt( +# depth=depth, +# exception=record.exc_info +# ).log(level,record.getMessage()) + + +class CustomizeLogger: + + @classmethod + def make_logger(cls,config_path: Path): + + config = cls.load_logging_config(config_path) + logging_config = config.get('logger') + # print((Path(logging_config.get('path')) / Path(logging_config.get('filename'))).absolute(), '8888888888888888888888888888888') + # Path(logging_config.get('path')) / Path(logging_config.get('filename')), + + logger = cls.customize_logging( + (Path(logging_config.get('path')) / Path(logging_config.get('filename'))).absolute(), + level=logging_config.get('level'), + retention=logging_config.get('retention'), + rotation=logging_config.get('rotation'), + format=logging_config.get('format') + ) + return logger + + @classmethod + def customize_logging(cls, + filepath: Path, + level: str, + rotation: str, + retention: str, + format: str + ): + logger.remove() + logger.add( + sys.stdout, + enqueue=True, + backtrace=True, + level=level.upper(), + format=format + ) + logger.add( + str(filepath), + rotation=rotation, + retention=retention, + enqueue=True, + backtrace=True, + level=level.upper(), + format=format + ) + # logging.basicConfig(handlers=[InterceptHandler()], level=0) + # logging.getLogger("uvicorn.access").handlers = [InterceptHandler()] + # for _log in ['uvicorn', + # 'uvicorn.error', + # 'fastapi' + # ]: + # _logger = logging.getLogger(_log) + # _logger.handlers = [InterceptHandler()] + + return logger.bind(request_id=None, method=None) + + + @classmethod + def load_logging_config(cls, config_path): + config = None + with open(config_path) as config_file: + config = json.load(config_file) + return config \ No newline at end of file diff --git a/app/internal/logger_util.py b/app/internal/logger_util.py new file mode 100644 index 00000000..e69de29b diff --git a/app/internal/logging_config.json b/app/internal/logging_config.json new file mode 100644 index 00000000..dbd0840b --- /dev/null +++ b/app/internal/logging_config.json @@ -0,0 +1,10 @@ +{ + "logger": { + "path": "./var/log", + "filename": "calendar.log", + "level": "debug", + "rotation": "20 days", + "retention": "1 months", + "format": "{level: <8} {time:YYYY-MM-DD HH:mm:ss.SSS} request id: {extra[request_id]} - {name}:{function} - {message}" + } +} \ No newline at end of file diff --git a/app/internal/logging_system.py b/app/internal/logging_system.py new file mode 100644 index 00000000..080e7b76 --- /dev/null +++ b/app/internal/logging_system.py @@ -0,0 +1,27 @@ +import logging +from pathlib import Path, PurePath +import sys + + +class NoWitFolderFoundError(Exception): + pass + +log_file_path = r'log.log' +logger = logging.getLogger() +logger.setLevel(logging.DEBUG) + +LOG_FORMAT = "%(levelname)s %(asctime)s - %(message)s" +formatter = logging.Formatter(LOG_FORMAT, datefmt="%Y-%m-%d %H:%M:%S") + +file_handler = logging.FileHandler(log_file_path) +file_handler.setLevel(logging.DEBUG) +file_handler.setFormatter(formatter) + +stream_handler = logging.StreamHandler(sys.stdout) +stream_handler.setLevel(logging.INFO) + +logger.addHandler(file_handler) +logger.addHandler(stream_handler) + + +logger.debug('shimmi') diff --git a/app/main.py b/app/main.py index 3050a284..fe13372d 100644 --- a/app/main.py +++ b/app/main.py @@ -2,12 +2,18 @@ from fastapi.staticfiles import StaticFiles from fastapi.templating import Jinja2Templates +from app import app -app = FastAPI() -app.mount("/static", StaticFiles(directory="static"), name="static") +# app = FastAPI() -templates = Jinja2Templates(directory="templates") +app.mount("/static", StaticFiles(directory="app/static"), name="static") + +templates = Jinja2Templates(directory="app/templates") + +app.logger.debug("shimishinmiyeah") +app.logger.info("shimishinmiyeah") +app.logger.error("shimishinmiyeah") @app.get("/") @@ -22,11 +28,21 @@ def home(request: Request): def profile(request: Request): # Get relevant data from database - upcouming_events = range(5) + upcoming_events = range(5) current_username = "Chuck Norris" return templates.TemplateResponse("profile.html", { "request": request, "username": current_username, - "events": upcouming_events + "events": upcoming_events }) + + +@app.get('/custom-logger') +def customize_logger(request: Request): + request.app.logger.info("Here Is Your Info Log") + try: + a = 1 / 0 + except ZeroDivisionError: + request.app.logger.error("Here Is Your Error Log") + return {'data': "Successfully Implemented Custom Log"} diff --git a/database.db b/database.db new file mode 100644 index 0000000000000000000000000000000000000000..4d43a3a4d391d8be3fa4946b922dadf0ee7cad95 GIT binary patch literal 28672 zcmeI3O>7%Q7>0M_#EyR_{~PB|8%0R5n(Xe(?5@2QNOjUp8Z~u@n;?Qz1(Uec$c}03 zwz+^*%83KwgoFefDh_brR4!cL!~ubjkdP3Ep5TI1sV6|92j*MXNs5TGIg)&n-FMeB z^X}~T%#5Y+X0NO+mm4MZT5Y#lY^Yr7kz`pqud0$HO^H1u_Ezc?C$P7Y+&XDLld_le^_YybaABhi`kN^@u0!RP}AOR$R1dsp{Kmw2KmUp&G zd*!{&(yh`?qrO?*Zk~kZ3vO=BRhRM?+!xiu_VN|?VZxDxa-yxA@BLZ(&b!XL%rl~c((|73o8Y8X)zCZ z(=AU*Ew~GA!OhRRSJa#J(r(?W9!br029+sA-e|G$%0wgHvFqPtTBQKy;3{`slS7K4 z$alHN#Vhcn<0&dn3`atN@||M!DK4dIv0OP!<@I8{e!I52eK@aN-z;u5%C}1D+{((b zo68?XW<9^Oy6&pet(Hky@bmuP#KKqwl%B$1Hj7n3fNGcTwD$gmhSS zsEFnIX7OgD=6&AuV%%ijp)=KSd{ydI4Tx#h4O1;Hc9}eo#D)n8AOR$R1dsp{Kmter z2_OL^fCP{L68K*dQ01VcoJ)r5#d>YWlk=~_4; z2!zA%{{KW|P9i^$k4cq0OQMNi5}zgZ5{rqU_;2z1@w@Rtd@}YZ_EqfN*wxrf^zZ1m z(GQ~6qv>cc@>ArKNL`pQAps~OD%|j+gWC57PU>o))n6dGn3&NQy=mS(m@w^XU%@t zZ^6u38O^jQb4-2EzkyLEC=K{A7+L0Mmc`SQ>;3)>l-e1p_q8#IvE=nMbF!ABWh~PW z{m|>*z?kFMdQV$};Xpr#7DNo(uxxJY(>#_F)i>a8q;A8D?-j2kq+v?d=8a=>a7K sG?4&}$3bH;&}bAi5&;c|LA$#_Lm|*$5VWfcw6ha55CHAyfam}J06Eu*-v9sr literal 0 HcmV?d00001 From 0299b10d32d61af355ff5d4f64442b14effafe51 Mon Sep 17 00:00:00 2001 From: Liad Noam Date: Thu, 21 Jan 2021 21:31:05 +0200 Subject: [PATCH 02/16] Added docstring and annotations to logger, added logger tests --- app/__init__.py | 27 --------- app/internal/custom_logging.py | 99 ------------------------------- app/internal/logger_util.py | 0 app/internal/logging_config.json | 6 +- app/internal/logging_system.py | 27 --------- app/main.py | 12 ++++ database.db | Bin 28672 -> 0 bytes requirements.txt | 92 ++++++++++++++-------------- tests/conftest.py | 15 +++++ 9 files changed, 78 insertions(+), 200 deletions(-) delete mode 100644 app/internal/custom_logging.py delete mode 100644 app/internal/logger_util.py delete mode 100644 app/internal/logging_system.py delete mode 100644 database.db diff --git a/app/__init__.py b/app/__init__.py index 3bd97917..e69de29b 100644 --- a/app/__init__.py +++ b/app/__init__.py @@ -1,27 +0,0 @@ - -from fastapi import FastAPI -from app.internal.custom_logging import CustomizeLogger -from pathlib import Path -# from fastapi import Request -# import uvicorn -import logging -# from fastapi.staticfiles import StaticFiles -# from fastapi.templating import Jinja2Templates - - - -# logger = logging.getLogger(__name__) - -config_path=Path(__file__).parent / 'internal' -config_path = config_path.absolute() / "logging_config.json" - - -def create_app() -> FastAPI: - app = FastAPI(title='Calendar', debug=False) - logger = CustomizeLogger.make_logger(config_path) - app.logger = logger - - return app - - -app = create_app() \ No newline at end of file diff --git a/app/internal/custom_logging.py b/app/internal/custom_logging.py deleted file mode 100644 index 37318b1c..00000000 --- a/app/internal/custom_logging.py +++ /dev/null @@ -1,99 +0,0 @@ -# Custom Logger Using Loguru - -import logging -import sys -from pathlib import Path -from loguru import logger -import json - - -# class InterceptHandler(logging.Handler): -# loglevel_mapping = { -# 50: 'CRITICAL', -# 40: 'ERROR', -# 30: 'WARNING', -# 20: 'INFO', -# 10: 'DEBUG', -# 0: 'NOTSET', -# } - -# def emit(self, record): -# try: -# level = logger.level(record.levelname).name -# except AttributeError: -# level = self.loglevel_mapping[record.levelno] - -# frame, depth = logging.currentframe(), 2 -# while frame.f_code.co_filename == logging.__file__: -# frame = frame.f_back -# depth += 1 - -# log = logger.bind(request_id='app') -# log.opt( -# depth=depth, -# exception=record.exc_info -# ).log(level,record.getMessage()) - - -class CustomizeLogger: - - @classmethod - def make_logger(cls,config_path: Path): - - config = cls.load_logging_config(config_path) - logging_config = config.get('logger') - # print((Path(logging_config.get('path')) / Path(logging_config.get('filename'))).absolute(), '8888888888888888888888888888888') - # Path(logging_config.get('path')) / Path(logging_config.get('filename')), - - logger = cls.customize_logging( - (Path(logging_config.get('path')) / Path(logging_config.get('filename'))).absolute(), - level=logging_config.get('level'), - retention=logging_config.get('retention'), - rotation=logging_config.get('rotation'), - format=logging_config.get('format') - ) - return logger - - @classmethod - def customize_logging(cls, - filepath: Path, - level: str, - rotation: str, - retention: str, - format: str - ): - logger.remove() - logger.add( - sys.stdout, - enqueue=True, - backtrace=True, - level=level.upper(), - format=format - ) - logger.add( - str(filepath), - rotation=rotation, - retention=retention, - enqueue=True, - backtrace=True, - level=level.upper(), - format=format - ) - # logging.basicConfig(handlers=[InterceptHandler()], level=0) - # logging.getLogger("uvicorn.access").handlers = [InterceptHandler()] - # for _log in ['uvicorn', - # 'uvicorn.error', - # 'fastapi' - # ]: - # _logger = logging.getLogger(_log) - # _logger.handlers = [InterceptHandler()] - - return logger.bind(request_id=None, method=None) - - - @classmethod - def load_logging_config(cls, config_path): - config = None - with open(config_path) as config_file: - config = json.load(config_file) - return config \ No newline at end of file diff --git a/app/internal/logger_util.py b/app/internal/logger_util.py deleted file mode 100644 index e69de29b..00000000 diff --git a/app/internal/logging_config.json b/app/internal/logging_config.json index dbd0840b..aa118748 100644 --- a/app/internal/logging_config.json +++ b/app/internal/logging_config.json @@ -2,9 +2,9 @@ "logger": { "path": "./var/log", "filename": "calendar.log", - "level": "debug", + "level": "error", "rotation": "20 days", - "retention": "1 months", - "format": "{level: <8} {time:YYYY-MM-DD HH:mm:ss.SSS} request id: {extra[request_id]} - {name}:{function} - {message}" + "retention": "1 month", + "format": "{level: <8} {time:YYYY-MM-DD HH:mm:ss.SSS} - {name}:{function} - {message}" } } \ No newline at end of file diff --git a/app/internal/logging_system.py b/app/internal/logging_system.py deleted file mode 100644 index 080e7b76..00000000 --- a/app/internal/logging_system.py +++ /dev/null @@ -1,27 +0,0 @@ -import logging -from pathlib import Path, PurePath -import sys - - -class NoWitFolderFoundError(Exception): - pass - -log_file_path = r'log.log' -logger = logging.getLogger() -logger.setLevel(logging.DEBUG) - -LOG_FORMAT = "%(levelname)s %(asctime)s - %(message)s" -formatter = logging.Formatter(LOG_FORMAT, datefmt="%Y-%m-%d %H:%M:%S") - -file_handler = logging.FileHandler(log_file_path) -file_handler.setLevel(logging.DEBUG) -file_handler.setFormatter(formatter) - -stream_handler = logging.StreamHandler(sys.stdout) -stream_handler.setLevel(logging.INFO) - -logger.addHandler(file_handler) -logger.addHandler(stream_handler) - - -logger.debug('shimmi') diff --git a/app/main.py b/app/main.py index b48bdae5..be63f563 100644 --- a/app/main.py +++ b/app/main.py @@ -1,3 +1,5 @@ +from pathlib import Path + from fastapi import FastAPI, Request from fastapi.staticfiles import StaticFiles @@ -7,6 +9,8 @@ MEDIA_PATH, STATIC_PATH, templates) from app.routers import agenda, event, profile +from app.internal.logger_customizer import LoggerCustomizer + models.Base.metadata.create_all(bind=engine) @@ -14,12 +18,20 @@ app.mount("/static", StaticFiles(directory=STATIC_PATH), name="static") app.mount("/media", StaticFiles(directory=MEDIA_PATH), name="media") +# Configure logger +config_path=Path(__file__).parent / 'internal' +config_path = config_path.absolute() / "logging_config.json" +logger = LoggerCustomizer.make_logger(config_path, 'logger') +app.logger = logger + + app.include_router(profile.router) app.include_router(event.router) app.include_router(agenda.router) @app.get("/") +@app.logger.catch() async def home(request: Request): return templates.TemplateResponse("home.html", { "request": request, diff --git a/database.db b/database.db deleted file mode 100644 index 4d43a3a4d391d8be3fa4946b922dadf0ee7cad95..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 28672 zcmeI3O>7%Q7>0M_#EyR_{~PB|8%0R5n(Xe(?5@2QNOjUp8Z~u@n;?Qz1(Uec$c}03 zwz+^*%83KwgoFefDh_brR4!cL!~ubjkdP3Ep5TI1sV6|92j*MXNs5TGIg)&n-FMeB z^X}~T%#5Y+X0NO+mm4MZT5Y#lY^Yr7kz`pqud0$HO^H1u_Ezc?C$P7Y+&XDLld_le^_YybaABhi`kN^@u0!RP}AOR$R1dsp{Kmw2KmUp&G zd*!{&(yh`?qrO?*Zk~kZ3vO=BRhRM?+!xiu_VN|?VZxDxa-yxA@BLZ(&b!XL%rl~c((|73o8Y8X)zCZ z(=AU*Ew~GA!OhRRSJa#J(r(?W9!br029+sA-e|G$%0wgHvFqPtTBQKy;3{`slS7K4 z$alHN#Vhcn<0&dn3`atN@||M!DK4dIv0OP!<@I8{e!I52eK@aN-z;u5%C}1D+{((b zo68?XW<9^Oy6&pet(Hky@bmuP#KKqwl%B$1Hj7n3fNGcTwD$gmhSS zsEFnIX7OgD=6&AuV%%ijp)=KSd{ydI4Tx#h4O1;Hc9}eo#D)n8AOR$R1dsp{Kmter z2_OL^fCP{L68K*dQ01VcoJ)r5#d>YWlk=~_4; z2!zA%{{KW|P9i^$k4cq0OQMNi5}zgZ5{rqU_;2z1@w@Rtd@}YZ_EqfN*wxrf^zZ1m z(GQ~6qv>cc@>ArKNL`pQAps~OD%|j+gWC57PU>o))n6dGn3&NQy=mS(m@w^XU%@t zZ^6u38O^jQb4-2EzkyLEC=K{A7+L0Mmc`SQ>;3)>l-e1p_q8#IvE=nMbF!ABWh~PW z{m|>*z?kFMdQV$};Xpr#7DNo(uxxJY(>#_F)i>a8q;A8D?-j2kq+v?d=8a=>a7K sG?4&}$3bH;&}bAi5&;c|LA$#_Lm|*$5VWfcw6ha55CHAyfam}J06Eu*-v9sr diff --git a/requirements.txt b/requirements.txt index 9398c131..5fe8261f 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,44 +1,48 @@ -aiofiles==0.6.0 -arrow==0.17.0 -atomicwrites==1.4.0 -attrs==20.3.0 -certifi==2020.12.5 -chardet==4.0.0 -click==7.1.2 -colorama==0.4.4 -coverage==5.3.1 -fastapi==0.63.0 -h11==0.12.0 -h2==4.0.0 -hpack==4.0.0 -hyperframe==6.0.0 -idna==2.10 -importlib-metadata==3.3.0 -iniconfig==1.1.1 -Jinja2==2.11.2 -MarkupSafe==1.1.1 -packaging==20.8 -Pillow==8.1.0 -pluggy==0.13.1 -priority==1.3.0 -py==1.10.0 -pydantic==1.7.3 -pyparsing==2.4.7 -pytest==6.2.1 -pytest-cov==2.10.1 -python-dateutil==2.8.1 -python-dotenv==0.15.0 -python-multipart==0.0.5 -PyYAML==5.3.1 -requests==2.25.1 -six==1.15.0 -SQLAlchemy==1.3.22 -starlette==0.13.6 -toml==0.10.2 -typing-extensions==3.7.4.3 -urllib3==1.26.2 -uvicorn==0.13.3 -watchgod==0.6 -websockets==8.1 -wsproto==1.0.0 -zipp==3.4.0 +aiofiles==0.6.0 +arrow==0.17.0 +atomicwrites==1.4.0 +attrs==20.3.0 +autopep8==1.5.4 +certifi==2020.12.5 +chardet==4.0.0 +click==7.1.2 +colorama==0.4.4 +coverage==5.3.1 +fastapi==0.63.0 +h11==0.12.0 +h2==4.0.0 +hpack==4.0.0 +hyperframe==6.0.0 +idna==2.10 +importlib-metadata==3.3.0 +iniconfig==1.1.1 +Jinja2==2.11.2 +loguru==0.5.3 +MarkupSafe==1.1.1 +packaging==20.8 +Pillow==8.1.0 +pluggy==0.13.1 +priority==1.3.0 +py==1.10.0 +pycodestyle==2.6.0 +pydantic==1.7.3 +pyparsing==2.4.7 +pytest==6.2.1 +pytest-cov==2.10.1 +python-dateutil==2.8.1 +python-dotenv==0.15.0 +python-multipart==0.0.5 +PyYAML==5.3.1 +requests==2.25.1 +six==1.15.0 +SQLAlchemy==1.3.22 +starlette==0.13.6 +toml==0.10.2 +typing-extensions==3.7.4.3 +urllib3==1.26.2 +uvicorn==0.13.3 +watchgod==0.6 +websockets==8.1 +win32-setctime==1.0.3 +wsproto==1.0.0 +zipp==3.4.0 \ No newline at end of file diff --git a/tests/conftest.py b/tests/conftest.py index 9960301b..b152195b 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -1,4 +1,7 @@ +import logging from fastapi.testclient import TestClient +from loguru import logger +from _pytest.logging import caplog as _caplog import pytest from sqlalchemy import create_engine from sqlalchemy.orm import sessionmaker @@ -9,6 +12,7 @@ from app.routers import profile + SQLALCHEMY_TEST_DATABASE_URL = "sqlite:///./test.db" test_engine = create_engine( @@ -56,3 +60,14 @@ def profile_test_client(): with TestClient(app) as client: yield client app.dependency_overrides = {} + + +@pytest.fixture +def caplog(_caplog): + 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) \ No newline at end of file From 1408056bf1831e05218d5dbffad8376d31943869 Mon Sep 17 00:00:00 2001 From: Liad Noam Date: Thu, 21 Jan 2021 21:32:26 +0200 Subject: [PATCH 03/16] Added docstring and annotations to logger, added logger tests --- app/internal/logger_customizer.py | 104 ++++++++++++++++++++++++++++++ app/media/fake_user.png | Bin 0 -> 3556 bytes tests/test_logger.py | 54 ++++++++++++++++ 3 files changed, 158 insertions(+) create mode 100644 app/internal/logger_customizer.py create mode 100644 app/media/fake_user.png create mode 100644 tests/test_logger.py diff --git a/app/internal/logger_customizer.py b/app/internal/logger_customizer.py new file mode 100644 index 00000000..f721df02 --- /dev/null +++ b/app/internal/logger_customizer.py @@ -0,0 +1,104 @@ +import json +import sys +from typing import Union + +from pathlib import Path +from loguru import logger, _Logger as Logger + + +class LoggerConfigError(Exception): + pass + + +class LoggerCustomizer: + + @classmethod + def make_logger(cls, config: Union[Path, dict], logger_name: str) -> Logger: + """Creates a loguru logger from given configuration path or dict. + + Args: + config (Union[Path, dict]): Path to logger configuration file or dictionary of configuration + logger_name (str): Logger instance created from configuration + + Raises: + LoggerConfigError: Error raised when the configuration is invalid + + Returns: + Logger: Loguru logger instance + """ + if isinstance(config, Path): + config = cls.load_logging_config(config_path) + else: + config = config + + try: + logging_config = config.get(logger_name) + logs_path = logging_config.get('path') + log_file_path = logging_config.get('filename') + + logger = cls.customize_logging( + file_path=Path(logs_path) / Path(log_file_path), + level=logging_config.get('level'), + retention=logging_config.get('retention'), + rotation=logging_config.get('rotation'), + format=logging_config.get('format') + ) + except (TypeError, ValueError) as err: + raise LoggerConfigError( + f"You have an issue with your 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): [description] + 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 + + @classmethod + def load_logging_config(cls, config_path: Union[Path, dict]) -> dict: + """Loads logging configuration from file or dict + + Args: + config_path (Union[Path, dict]): Path to logging configuration file + + Returns: + dict: Configuration parsed as dictionary + """ + # config = None + with open(config_path) as config_file: + config = json.load(config_file) + return config \ No newline at end of file diff --git a/app/media/fake_user.png b/app/media/fake_user.png new file mode 100644 index 0000000000000000000000000000000000000000..bd856aaa02ed8fc75fb310971827e4734b8785d5 GIT binary patch literal 3556 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 literal 0 HcmV?d00001 diff --git a/tests/test_logger.py b/tests/test_logger.py new file mode 100644 index 00000000..f0b7d0ba --- /dev/null +++ b/tests/test_logger.py @@ -0,0 +1,54 @@ +import logging + +from fastapi.testclient import TestClient + +import pytest + +from app.main import app, logger +from app.internal.logger_customizer import LoggerCustomizer, LoggerConfigError + + +client = TestClient(app) +client.logger = logger + + +class TestLogger: + @staticmethod + def test_log_debug(caplog): + with caplog.at_level(logging.DEBUG): + client.logger.debug('Is it debugging now?') + assert 'Is it debugging now?' in caplog.text + + @staticmethod + def test_log_info(caplog): + with caplog.at_level(logging.INFO): + client.logger.info('App started') + assert 'App started' in caplog.text + + @staticmethod + def test_log_error(caplog): + with caplog.at_level(logging.ERROR): + client.logger.error('Something bad happened!') + assert 'Something bad happened!' in caplog.text + + @staticmethod + def test_log_error(caplog): + with caplog.at_level(logging.CRITICAL): + client.logger.critical("WE'RE DOOMED!") + assert "WE'RE DOOMED!" in caplog.text + + @staticmethod + def test_bad_configuration(): + bad_config = { + "logger": { + "path": "./var/log", + "filename": "calendar.log", + "level": "eror", + "rotation": "20 days", + "retention": "1 month", + "format": "{level: <8} {time:YYYY-MM-DD HH:mm:ss.SSS} - {name}:{function} - {message}" + } + } + with pytest.raises(LoggerConfigError): + LoggerCustomizer.make_logger(bad_config, 'logger') + # bad_config_logger.debug('hello') From c644bc022b8266e241241d40bfa6708bbe79f744 Mon Sep 17 00:00:00 2001 From: Liad Noam Date: Thu, 21 Jan 2021 21:57:37 +0200 Subject: [PATCH 04/16] Fixed an issue with variable name --- app/internal/logger_customizer.py | 208 +++++++++++++++--------------- 1 file changed, 104 insertions(+), 104 deletions(-) diff --git a/app/internal/logger_customizer.py b/app/internal/logger_customizer.py index f721df02..dfafd627 100644 --- a/app/internal/logger_customizer.py +++ b/app/internal/logger_customizer.py @@ -1,104 +1,104 @@ -import json -import sys -from typing import Union - -from pathlib import Path -from loguru import logger, _Logger as Logger - - -class LoggerConfigError(Exception): - pass - - -class LoggerCustomizer: - - @classmethod - def make_logger(cls, config: Union[Path, dict], logger_name: str) -> Logger: - """Creates a loguru logger from given configuration path or dict. - - Args: - config (Union[Path, dict]): Path to logger configuration file or dictionary of configuration - logger_name (str): Logger instance created from configuration - - Raises: - LoggerConfigError: Error raised when the configuration is invalid - - Returns: - Logger: Loguru logger instance - """ - if isinstance(config, Path): - config = cls.load_logging_config(config_path) - else: - config = config - - try: - logging_config = config.get(logger_name) - logs_path = logging_config.get('path') - log_file_path = logging_config.get('filename') - - logger = cls.customize_logging( - file_path=Path(logs_path) / Path(log_file_path), - level=logging_config.get('level'), - retention=logging_config.get('retention'), - rotation=logging_config.get('rotation'), - format=logging_config.get('format') - ) - except (TypeError, ValueError) as err: - raise LoggerConfigError( - f"You have an issue with your 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): [description] - 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 - - @classmethod - def load_logging_config(cls, config_path: Union[Path, dict]) -> dict: - """Loads logging configuration from file or dict - - Args: - config_path (Union[Path, dict]): Path to logging configuration file - - Returns: - dict: Configuration parsed as dictionary - """ - # config = None - with open(config_path) as config_file: - config = json.load(config_file) - return config \ No newline at end of file +import json +import sys +from typing import Union + +from pathlib import Path +from loguru import logger, _Logger as Logger + + +class LoggerConfigError(Exception): + pass + + +class LoggerCustomizer: + + @classmethod + def make_logger(cls, config_file_or_dict: Union[Path, dict], logger_name: str) -> Logger: + """Creates a loguru logger from given configuration path or dict. + + Args: + config_file_or_dict (Union[Path, dict]): Path to logger configuration file or dictionary of configuration + logger_name (str): Logger instance created from configuration + + Raises: + LoggerConfigError: Error raised when the configuration is invalid + + Returns: + Logger: Loguru logger instance + """ + config = cls.load_logging_config(config_file_or_dict) + + try: + logging_config = config.get(logger_name) + logs_path = logging_config.get('path') + log_file_path = logging_config.get('filename') + + logger = cls.customize_logging( + file_path=Path(logs_path) / Path(log_file_path), + level=logging_config.get('level'), + retention=logging_config.get('retention'), + rotation=logging_config.get('rotation'), + format=logging_config.get('format') + ) + except (TypeError, ValueError) as err: + raise LoggerConfigError( + f"You have an issue with your 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): [description] + 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 + + @classmethod + def load_logging_config(cls, config: Union[Path, dict]) -> dict: + """Loads logging configuration from file or dict + + Args: + config (Union[Path, dict]): Path to logging configuration file + + Returns: + dict: Configuration parsed as dictionary + """ + if isinstance(config, Path): + with open(config) as config_file: + used_config = json.load(config_file) + else: + used_config = config + + return used_config From ea9c0f4f48e3e190fa1277077847b7a59c87837b Mon Sep 17 00:00:00 2001 From: Liad Noam Date: Thu, 21 Jan 2021 23:32:11 +0200 Subject: [PATCH 05/16] Fixed requirements.txt duplicate rows. --- requirements.txt | 45 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) diff --git a/requirements.txt b/requirements.txt index 4b649244..814e0bd8 100644 --- a/requirements.txt +++ b/requirements.txt @@ -90,3 +90,48 @@ watchgod==0.6 websockets==8.1 wsproto==1.0.0 zipp==3.4.0 +=> Duplicates: +aiofiles==0.6.0 +arrow==0.17.0 +atomicwrites==1.4.0 +attrs==20.3.0 +certifi==2020.12.5 +chardet==4.0.0 +click==7.1.2 +colorama==0.4.4 +coverage==5.3.1 +fastapi==0.63.0 +h11==0.12.0 +h2==4.0.0 +hpack==4.0.0 +hyperframe==6.0.0 +idna==2.10 +importlib-metadata==3.3.0 +iniconfig==1.1.1 +Jinja2==2.11.2 +MarkupSafe==1.1.1 +packaging==20.8 +Pillow==8.1.0 +pluggy==0.13.1 +priority==1.3.0 +py==1.10.0 +pydantic==1.7.3 +pyparsing==2.4.7 +pytest==6.2.1 +pytest-cov==2.10.1 +python-dateutil==2.8.1 +python-dotenv==0.15.0 +python-multipart==0.0.5 +PyYAML==5.3.1 +requests==2.25.1 +six==1.15.0 +SQLAlchemy==1.3.22 +starlette==0.13.6 +toml==0.10.2 +typing-extensions==3.7.4.3 +urllib3==1.26.2 +uvicorn==0.13.3 +watchgod==0.6 +websockets==8.1 +wsproto==1.0.0 +zipp==3.4.0 \ No newline at end of file From c1e02210986c35704b2468d195fcea3a0315f4f3 Mon Sep 17 00:00:00 2001 From: Liad Noam Date: Thu, 21 Jan 2021 23:39:01 +0200 Subject: [PATCH 06/16] Fixed requirements.txt duplicate rows, again. --- requirements.txt | 91 +----------------------------------------------- 1 file changed, 1 insertion(+), 90 deletions(-) diff --git a/requirements.txt b/requirements.txt index 814e0bd8..bd78136d 100644 --- a/requirements.txt +++ b/requirements.txt @@ -43,95 +43,6 @@ watchgod==0.6 websockets==8.1 wsproto==1.0.0 zipp==3.4.0 -aiofiles==0.6.0 -arrow==0.17.0 -atomicwrites==1.4.0 -attrs==20.3.0 -certifi==2020.12.5 -chardet==4.0.0 -click==7.1.2 -colorama==0.4.4 -coverage==5.3.1 -fastapi==0.63.0 fastapi_mail==0.3.3.1 faker==5.6.2 -smtpdfix==0.2.6 -h11==0.12.0 -h2==4.0.0 -hpack==4.0.0 -hyperframe==6.0.0 -idna==2.10 -importlib-metadata==3.3.0 -iniconfig==1.1.1 -Jinja2==2.11.2 -MarkupSafe==1.1.1 -packaging==20.8 -Pillow==8.1.0 -pluggy==0.13.1 -priority==1.3.0 -py==1.10.0 -pydantic==1.7.3 -pyparsing==2.4.7 -pytest==6.2.1 -pytest-cov==2.10.1 -python-dateutil==2.8.1 -python-dotenv==0.15.0 -python-multipart==0.0.5 -PyYAML==5.3.1 -requests==2.25.1 -six==1.15.0 -SQLAlchemy==1.3.22 -starlette==0.13.6 -toml==0.10.2 -typing-extensions==3.7.4.3 -urllib3==1.26.2 -uvicorn==0.13.3 -watchgod==0.6 -websockets==8.1 -wsproto==1.0.0 -zipp==3.4.0 -=> Duplicates: -aiofiles==0.6.0 -arrow==0.17.0 -atomicwrites==1.4.0 -attrs==20.3.0 -certifi==2020.12.5 -chardet==4.0.0 -click==7.1.2 -colorama==0.4.4 -coverage==5.3.1 -fastapi==0.63.0 -h11==0.12.0 -h2==4.0.0 -hpack==4.0.0 -hyperframe==6.0.0 -idna==2.10 -importlib-metadata==3.3.0 -iniconfig==1.1.1 -Jinja2==2.11.2 -MarkupSafe==1.1.1 -packaging==20.8 -Pillow==8.1.0 -pluggy==0.13.1 -priority==1.3.0 -py==1.10.0 -pydantic==1.7.3 -pyparsing==2.4.7 -pytest==6.2.1 -pytest-cov==2.10.1 -python-dateutil==2.8.1 -python-dotenv==0.15.0 -python-multipart==0.0.5 -PyYAML==5.3.1 -requests==2.25.1 -six==1.15.0 -SQLAlchemy==1.3.22 -starlette==0.13.6 -toml==0.10.2 -typing-extensions==3.7.4.3 -urllib3==1.26.2 -uvicorn==0.13.3 -watchgod==0.6 -websockets==8.1 -wsproto==1.0.0 -zipp==3.4.0 \ No newline at end of file +smtpdfix==0.2.6 \ No newline at end of file From 5f14fc65c6cd7d9bfe9bec5da5a041b6ddb5a01d Mon Sep 17 00:00:00 2001 From: Liad Noam Date: Fri, 22 Jan 2021 02:35:01 +0200 Subject: [PATCH 07/16] Fixed merge suggestions in logger_customizer.py --- app/internal/logger_customizer.py | 2 +- app/internal/logging_config.json | 18 +++--- tests/conftest.py | 31 ++++++--- tests/test_logger.py | 102 ++++++++++++++---------------- 4 files changed, 79 insertions(+), 74 deletions(-) diff --git a/app/internal/logger_customizer.py b/app/internal/logger_customizer.py index dfafd627..22e6022c 100644 --- a/app/internal/logger_customizer.py +++ b/app/internal/logger_customizer.py @@ -100,5 +100,5 @@ def load_logging_config(cls, config: Union[Path, dict]) -> dict: used_config = json.load(config_file) else: used_config = config - + return used_config diff --git a/app/internal/logging_config.json b/app/internal/logging_config.json index aa118748..f4c77dc1 100644 --- a/app/internal/logging_config.json +++ b/app/internal/logging_config.json @@ -1,10 +1,10 @@ -{ - "logger": { - "path": "./var/log", - "filename": "calendar.log", - "level": "error", - "rotation": "20 days", - "retention": "1 month", - "format": "{level: <8} {time:YYYY-MM-DD HH:mm:ss.SSS} - {name}:{function} - {message}" - } +{ + "logger": { + "path": "./var/log", + "filename": "calendar.log", + "level": "error", + "rotation": "20 days", + "retention": "1 month", + "format": "{level: <8} {time:YYYY-MM-DD HH:mm:ss.SSS} - {name}:{function} - {message}" + } } \ No newline at end of file diff --git a/tests/conftest.py b/tests/conftest.py index c9c64c3f..fd0bb30e 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -1,24 +1,24 @@ -import logging -from fastapi.testclient import TestClient -from loguru import logger -from _pytest.logging import caplog as _caplog -import pytest -from sqlalchemy import create_engine -from sqlalchemy.orm import sessionmaker import datetime +import logging +from pathlib import Path import pytest +from _pytest.logging import caplog as _caplog +from loguru import logger +from faker import Faker + from app.database.database import Base, SessionLocal, engine from app.database.models import Event, User +from app.internal.logger_customizer import LoggerCustomizer from app.main import app from app.routers import profile -from faker import Faker from fastapi.testclient import TestClient from sqlalchemy import create_engine from sqlalchemy.orm import sessionmaker -pytest_plugins = "smtpdfix" +pytest_plugins = "smtpdfix" + SQLALCHEMY_TEST_DATABASE_URL = "sqlite:///./test.db" test_engine = create_engine( @@ -37,6 +37,17 @@ def client(): return TestClient(app) +@pytest.fixture(scope='module') +def client_with_logger(): + config_path = Path(__file__).parent.parent / 'app' / 'internal' + config_path = config_path / "logging_config.json" + logger = LoggerCustomizer.make_logger(config_path, 'logger') + + client = TestClient(app) + client.logger = logger + return client + + @pytest.fixture def session(): Base.metadata.create_all(bind=engine) @@ -100,4 +111,4 @@ def emit(self, record): handler_id = logger.add(PropagateHandler(), format="{message} {extra}") yield _caplog - logger.remove(handler_id) \ No newline at end of file + logger.remove(handler_id) diff --git a/tests/test_logger.py b/tests/test_logger.py index f0b7d0ba..ce32846b 100644 --- a/tests/test_logger.py +++ b/tests/test_logger.py @@ -1,54 +1,48 @@ -import logging - -from fastapi.testclient import TestClient - -import pytest - -from app.main import app, logger -from app.internal.logger_customizer import LoggerCustomizer, LoggerConfigError - - -client = TestClient(app) -client.logger = logger - - -class TestLogger: - @staticmethod - def test_log_debug(caplog): - with caplog.at_level(logging.DEBUG): - client.logger.debug('Is it debugging now?') - assert 'Is it debugging now?' in caplog.text - - @staticmethod - def test_log_info(caplog): - with caplog.at_level(logging.INFO): - client.logger.info('App started') - assert 'App started' in caplog.text - - @staticmethod - def test_log_error(caplog): - with caplog.at_level(logging.ERROR): - client.logger.error('Something bad happened!') - assert 'Something bad happened!' in caplog.text - - @staticmethod - def test_log_error(caplog): - with caplog.at_level(logging.CRITICAL): - client.logger.critical("WE'RE DOOMED!") - assert "WE'RE DOOMED!" in caplog.text - - @staticmethod - def test_bad_configuration(): - bad_config = { - "logger": { - "path": "./var/log", - "filename": "calendar.log", - "level": "eror", - "rotation": "20 days", - "retention": "1 month", - "format": "{level: <8} {time:YYYY-MM-DD HH:mm:ss.SSS} - {name}:{function} - {message}" - } - } - with pytest.raises(LoggerConfigError): - LoggerCustomizer.make_logger(bad_config, 'logger') - # bad_config_logger.debug('hello') +import logging + +import pytest + +from app.internal.logger_customizer import LoggerCustomizer, LoggerConfigError + + +class TestLogger: + + @staticmethod + def test_log_debug(caplog, client_with_logger): + with caplog.at_level(logging.DEBUG): + client_with_logger.logger.debug('Is it debugging now?') + assert 'Is it debugging now?' in caplog.text + + @staticmethod + def test_log_info(caplog, client_with_logger): + with caplog.at_level(logging.INFO): + client_with_logger.logger.info('App started') + assert 'App started' in caplog.text + + @staticmethod + def test_log_error(caplog, client_with_logger): + with caplog.at_level(logging.ERROR): + client_with_logger.logger.error('Something bad happened!') + assert 'Something bad happened!' in caplog.text + + @staticmethod + def test_log_critical(caplog, client_with_logger): + with caplog.at_level(logging.CRITICAL): + client_with_logger.logger.critical("WE'RE DOOMED!") + assert "WE'RE DOOMED!" in caplog.text + + @staticmethod + def test_bad_configuration(): + bad_config = { + "logger": { + "path": "./var/log", + "filename": "calendar.log", + "level": "eror", + "rotation": "20 days", + "retention": "1 month", + "format": ("{level: <8} {time:YYYY-MM-DD HH:mm:ss.SSS}" + "- {name}:{function} - {message}") + } + } + with pytest.raises(LoggerConfigError): + LoggerCustomizer.make_logger(bad_config, 'logger') From 41febf510aa5bb90db5a7f17451e1d7110953a65 Mon Sep 17 00:00:00 2001 From: Liad Noam Date: Fri, 22 Jan 2021 02:45:21 +0200 Subject: [PATCH 08/16] Fixed merge suggestions in logger_customizer.py --- app/internal/logger_customizer.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/app/internal/logger_customizer.py b/app/internal/logger_customizer.py index 22e6022c..dad2a78b 100644 --- a/app/internal/logger_customizer.py +++ b/app/internal/logger_customizer.py @@ -17,7 +17,8 @@ def make_logger(cls, config_file_or_dict: Union[Path, dict], logger_name: str) - """Creates a loguru logger from given configuration path or dict. Args: - config_file_or_dict (Union[Path, dict]): Path to logger configuration file or dictionary of configuration + config_file_or_dict (Union[Path, dict]): Path to logger configuration + file or dictionary of configuration logger_name (str): Logger instance created from configuration Raises: @@ -42,7 +43,7 @@ def make_logger(cls, config_file_or_dict: Union[Path, dict], logger_name: str) - ) except (TypeError, ValueError) as err: raise LoggerConfigError( - f"You have an issue with your logger configuration: {err!r}, fix it please") + f"You have an issue in the logger configuration: {err!r}, fix it please") return logger @@ -59,7 +60,8 @@ def customize_logging(cls, 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): [description] + rotation (str): Every how long the logs would be rotated(creation of new file) + retention (str): Amount of time in words defining long a log is kept format (str): The logging format Returns: From c07f18db9fb7c30e70513b836d91bb72ca76454c Mon Sep 17 00:00:00 2001 From: Liad Noam Date: Fri, 22 Jan 2021 03:06:15 +0200 Subject: [PATCH 09/16] Fixed linting in test_logging, conftest and logger_customizer, still issue about caplog flake8 --- app/internal/logger_customizer.py | 16 ++++++++++------ app/main.py | 2 +- tests/test_logger.py | 6 ++++-- 3 files changed, 15 insertions(+), 9 deletions(-) diff --git a/app/internal/logger_customizer.py b/app/internal/logger_customizer.py index dad2a78b..01368c36 100644 --- a/app/internal/logger_customizer.py +++ b/app/internal/logger_customizer.py @@ -13,12 +13,13 @@ class LoggerConfigError(Exception): class LoggerCustomizer: @classmethod - def make_logger(cls, config_file_or_dict: Union[Path, dict], logger_name: str) -> Logger: + def make_logger(cls, config_file_or_dict: Union[Path, dict], + logger_name: str) -> Logger: """Creates a loguru logger from given configuration path or dict. Args: - config_file_or_dict (Union[Path, dict]): Path to logger configuration - file or dictionary of configuration + config_file_or_dict (Union[Path, dict]): Path to logger + configuration file or dictionary of configuration logger_name (str): Logger instance created from configuration Raises: @@ -43,7 +44,8 @@ def make_logger(cls, config_file_or_dict: Union[Path, dict], logger_name: str) - ) except (TypeError, ValueError) as err: raise LoggerConfigError( - f"You have an issue in the logger configuration: {err!r}, fix it please") + f"You have an issue in the logger configuration: {err!r}, " + "fix it please") return logger @@ -60,8 +62,10 @@ def customize_logging(cls, 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 long a log is kept + 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: diff --git a/app/main.py b/app/main.py index 64703ce4..85b8dffc 100644 --- a/app/main.py +++ b/app/main.py @@ -19,7 +19,7 @@ app.mount("/media", StaticFiles(directory=MEDIA_PATH), name="media") # Configure logger -config_path=Path(__file__).parent / 'internal' +config_path = Path(__file__).parent / 'internal' config_path = config_path.absolute() / "logging_config.json" logger = LoggerCustomizer.make_logger(config_path, 'logger') app.logger = logger diff --git a/tests/test_logger.py b/tests/test_logger.py index ce32846b..e36c0721 100644 --- a/tests/test_logger.py +++ b/tests/test_logger.py @@ -40,8 +40,10 @@ def test_bad_configuration(): "level": "eror", "rotation": "20 days", "retention": "1 month", - "format": ("{level: <8} {time:YYYY-MM-DD HH:mm:ss.SSS}" - "- {name}:{function} - {message}") + "format": ("{level: <8} " + "{time:YYYY-MM-DD HH:mm:ss.SSS}" + "- {name}:{function}" + " - {message}") } } with pytest.raises(LoggerConfigError): From 973b4fcdc57f57b326bc3f065ad72c31d0662417 Mon Sep 17 00:00:00 2001 From: Liad Noam Date: Fri, 22 Jan 2021 23:04:26 +0200 Subject: [PATCH 10/16] Took config from global config file, ignored flake8 tests on caplog import in conftest.py other general fixes asked in PR code reviews. --- app/config.py.example | 14 ++++++++++++++ app/internal/logger_customizer.py | 3 ++- app/internal/logging_config.json | 10 ---------- app/main.py | 7 ++----- tests/conftest.py | 11 +++++------ tests/test_logger.py | 1 - 6 files changed, 23 insertions(+), 23 deletions(-) delete mode 100644 app/internal/logging_config.json diff --git a/app/config.py.example b/app/config.py.example index 7af0e1af..308b940c 100644 --- a/app/config.py.example +++ b/app/config.py.example @@ -22,3 +22,17 @@ email_conf = ConnectionConfig( MAIL_SSL=False, USE_CREDENTIALS=True, ) + +# LOGGER +LOGGER = { + "logger": { + "path": "./var/log", + "filename": "calendar.log", + "level": "error", + "rotation": "20 days", + "retention": "1 month", + "format": ("{level: <8} {time:YYYY-MM-DD HH:mm:ss.SSS}" + " - {name}:{function}" + " - {message}") + } +} \ No newline at end of file diff --git a/app/internal/logger_customizer.py b/app/internal/logger_customizer.py index 01368c36..8ef769e9 100644 --- a/app/internal/logger_customizer.py +++ b/app/internal/logger_customizer.py @@ -28,6 +28,7 @@ def make_logger(cls, config_file_or_dict: Union[Path, dict], Returns: Logger: Loguru logger instance """ + config = cls.load_logging_config(config_file_or_dict) try: @@ -44,7 +45,7 @@ def make_logger(cls, config_file_or_dict: Union[Path, dict], ) except (TypeError, ValueError) as err: raise LoggerConfigError( - f"You have an issue in the logger configuration: {err!r}, " + f"You have an issue with the logger configuration: {err!r}, " "fix it please") return logger diff --git a/app/internal/logging_config.json b/app/internal/logging_config.json deleted file mode 100644 index f4c77dc1..00000000 --- a/app/internal/logging_config.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "logger": { - "path": "./var/log", - "filename": "calendar.log", - "level": "error", - "rotation": "20 days", - "retention": "1 month", - "format": "{level: <8} {time:YYYY-MM-DD HH:mm:ss.SSS} - {name}:{function} - {message}" - } -} \ No newline at end of file diff --git a/app/main.py b/app/main.py index 85b8dffc..42f0324c 100644 --- a/app/main.py +++ b/app/main.py @@ -1,5 +1,3 @@ -from pathlib import Path - from fastapi import FastAPI, Request from fastapi.staticfiles import StaticFiles @@ -11,6 +9,7 @@ from app.internal.logger_customizer import LoggerCustomizer +from app import config models.Base.metadata.create_all(bind=engine) @@ -19,9 +18,7 @@ app.mount("/media", StaticFiles(directory=MEDIA_PATH), name="media") # Configure logger -config_path = Path(__file__).parent / 'internal' -config_path = config_path.absolute() / "logging_config.json" -logger = LoggerCustomizer.make_logger(config_path, 'logger') +logger = LoggerCustomizer.make_logger(config.LOGGER, "logger") app.logger = logger diff --git a/tests/conftest.py b/tests/conftest.py index fd0bb30e..b5f1b1a7 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -1,9 +1,8 @@ import datetime import logging -from pathlib import Path import pytest -from _pytest.logging import caplog as _caplog +from _pytest.logging import caplog as _caplog # noqa: F401 from loguru import logger from faker import Faker @@ -12,6 +11,8 @@ from app.internal.logger_customizer import LoggerCustomizer from app.main import app from app.routers import profile +from app import config + from fastapi.testclient import TestClient from sqlalchemy import create_engine from sqlalchemy.orm import sessionmaker @@ -39,9 +40,7 @@ def client(): @pytest.fixture(scope='module') def client_with_logger(): - config_path = Path(__file__).parent.parent / 'app' / 'internal' - config_path = config_path / "logging_config.json" - logger = LoggerCustomizer.make_logger(config_path, 'logger') + logger = LoggerCustomizer.make_logger(config.LOGGER, "logger") client = TestClient(app) client.logger = logger @@ -104,7 +103,7 @@ def profile_test_client(): @pytest.fixture -def caplog(_caplog): +def caplog(_caplog): # noqa: F811 class PropagateHandler(logging.Handler): def emit(self, record): logging.getLogger(record.name).handle(record) diff --git a/tests/test_logger.py b/tests/test_logger.py index e36c0721..92302e84 100644 --- a/tests/test_logger.py +++ b/tests/test_logger.py @@ -6,7 +6,6 @@ class TestLogger: - @staticmethod def test_log_debug(caplog, client_with_logger): with caplog.at_level(logging.DEBUG): From 8c07e192b609e91c24abe1db4b5b16664d67b6dd Mon Sep 17 00:00:00 2001 From: Liad Noam Date: Sat, 23 Jan 2021 23:56:14 +0200 Subject: [PATCH 11/16] Fixed tests, created new clientless logger fixture. Another fixture logger from external configuration --- app/internal/logger_customizer.py | 13 ++++++------- tests/conftest.py | 20 ++++++++++++++------ tests/test_logger.py | 22 ++++++++++++++-------- 3 files changed, 34 insertions(+), 21 deletions(-) diff --git a/app/internal/logger_customizer.py b/app/internal/logger_customizer.py index 8ef769e9..cab53c42 100644 --- a/app/internal/logger_customizer.py +++ b/app/internal/logger_customizer.py @@ -1,6 +1,6 @@ import json import sys -from typing import Union +from typing import Union, Dict from pathlib import Path from loguru import logger, _Logger as Logger @@ -13,12 +13,12 @@ class LoggerConfigError(Exception): class LoggerCustomizer: @classmethod - def make_logger(cls, config_file_or_dict: Union[Path, dict], + def make_logger(cls, config_file_or_dict: Union[Path, Dict], logger_name: str) -> Logger: """Creates a loguru logger from given configuration path or dict. Args: - config_file_or_dict (Union[Path, dict]): Path to logger + config_file_or_dict (Union[Path, Dict]): Path to logger configuration file or dictionary of configuration logger_name (str): Logger instance created from configuration @@ -30,7 +30,6 @@ def make_logger(cls, config_file_or_dict: Union[Path, dict], """ config = cls.load_logging_config(config_file_or_dict) - try: logging_config = config.get(logger_name) logs_path = logging_config.get('path') @@ -93,14 +92,14 @@ def customize_logging(cls, return logger @classmethod - def load_logging_config(cls, config: Union[Path, dict]) -> dict: + def load_logging_config(cls, config: Union[Path, Dict]) -> Dict: """Loads logging configuration from file or dict Args: - config (Union[Path, dict]): Path to logging configuration file + config (Union[Path, Dict]): Path to logging configuration file Returns: - dict: Configuration parsed as dictionary + Dict: Configuration parsed as dictionary """ if isinstance(config, Path): with open(config) as config_file: diff --git a/tests/conftest.py b/tests/conftest.py index b5f1b1a7..0487a9eb 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -6,12 +6,12 @@ from loguru import logger from faker import Faker +from app import config from app.database.database import Base, SessionLocal, engine from app.database.models import Event, User from app.internal.logger_customizer import LoggerCustomizer from app.main import app from app.routers import profile -from app import config from fastapi.testclient import TestClient from sqlalchemy import create_engine @@ -39,12 +39,20 @@ def client(): @pytest.fixture(scope='module') -def client_with_logger(): - logger = LoggerCustomizer.make_logger(config.LOGGER, "logger") +def logger_instance(): + _logger = LoggerCustomizer.make_logger(config.LOGGER, "logger") + + return _logger + + +@pytest.fixture(scope='module') +def logger_external_configuration(): + from pathlib import Path + config_path = Path(__file__).parent + config_path = config_path.absolute() / 'logging_config.json' + _logger = LoggerCustomizer.make_logger(config_path, "logger1") - client = TestClient(app) - client.logger = logger - return client + return _logger @pytest.fixture diff --git a/tests/test_logger.py b/tests/test_logger.py index 92302e84..436276d8 100644 --- a/tests/test_logger.py +++ b/tests/test_logger.py @@ -7,27 +7,33 @@ class TestLogger: @staticmethod - def test_log_debug(caplog, client_with_logger): + def test_configuration_file(caplog, logger_external_configuration): + with caplog.at_level(logging.ERROR): + logger_external_configuration.error('Testing configuration ERROR') + assert 'Testing configuration' in caplog.text + + @staticmethod + def test_log_debug(caplog, logger_instance): with caplog.at_level(logging.DEBUG): - client_with_logger.logger.debug('Is it debugging now?') + logger_instance.debug('Is it debugging now?') assert 'Is it debugging now?' in caplog.text @staticmethod - def test_log_info(caplog, client_with_logger): + def test_log_info(caplog, logger_instance): with caplog.at_level(logging.INFO): - client_with_logger.logger.info('App started') + logger_instance.info('App started') assert 'App started' in caplog.text @staticmethod - def test_log_error(caplog, client_with_logger): + def test_log_error(caplog, logger_instance): with caplog.at_level(logging.ERROR): - client_with_logger.logger.error('Something bad happened!') + logger_instance.error('Something bad happened!') assert 'Something bad happened!' in caplog.text @staticmethod - def test_log_critical(caplog, client_with_logger): + def test_log_critical(caplog, logger_instance): with caplog.at_level(logging.CRITICAL): - client_with_logger.logger.critical("WE'RE DOOMED!") + logger_instance.critical("WE'RE DOOMED!") assert "WE'RE DOOMED!" in caplog.text @staticmethod From e71328d7f5b918ed445b732ab278d4659c94dc9b Mon Sep 17 00:00:00 2001 From: Liad Noam Date: Sun, 24 Jan 2021 01:04:26 +0200 Subject: [PATCH 12/16] Finished logger_fixture, added logging config file to tests, added logger to config.py.example --- app/config.py.example | 4 ++-- tests/conftest.py | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/app/config.py.example b/app/config.py.example index 96c181b9..20601e7a 100644 --- a/app/config.py.example +++ b/app/config.py.example @@ -4,7 +4,7 @@ from fastapi_mail import ConnectionConfig # flake8: noqa -# general +# GENERAL DOMAIN = 'Our-Domain' # DATABASE @@ -18,7 +18,7 @@ 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//' diff --git a/tests/conftest.py b/tests/conftest.py index cd43c66b..3f6c578a 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -10,7 +10,7 @@ 'tests.invitation_fixture', 'tests.association_fixture', 'tests.client_fixture', - 'test.logger_fixture' + 'tests.logger_fixture', 'smtpdfix', ] From 88713de5de55380141e1ee5c0f8f23e1765a5480 Mon Sep 17 00:00:00 2001 From: Liad Noam Date: Sun, 24 Jan 2021 01:10:57 +0200 Subject: [PATCH 13/16] Added logger_fixture and config which werent added for some reason --- .vscode/settings.json | 5 +++++ tests/logger_fixture.py | 36 ++++++++++++++++++++++++++++++++++++ tests/logging_config.json | 10 ++++++++++ 3 files changed, 51 insertions(+) create mode 100644 .vscode/settings.json create mode 100644 tests/logger_fixture.py create mode 100644 tests/logging_config.json diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 00000000..09f7c786 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,5 @@ +{ + "cSpell.ignoreWords": [ + "smtpdfix" + ] +} \ No newline at end of file diff --git a/tests/logger_fixture.py b/tests/logger_fixture.py new file mode 100644 index 00000000..c947ad2f --- /dev/null +++ b/tests/logger_fixture.py @@ -0,0 +1,36 @@ +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.LOGGER, "logger") + + return _logger + + +@pytest.fixture(scope='module') +def logger_external_configuration(): + from pathlib import Path + config_path = Path(__file__).parent + config_path = config_path.absolute() / 'logging_config.json' + _logger = LoggerCustomizer.make_logger(config_path, "logger1") + + 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) diff --git a/tests/logging_config.json b/tests/logging_config.json new file mode 100644 index 00000000..ab90f6ba --- /dev/null +++ b/tests/logging_config.json @@ -0,0 +1,10 @@ +{ + "logger1": { + "path": "./var/log4", + "filename": "calendar3.log", + "level": "debug", + "rotation": "20 days", + "retention": "1 month", + "format": "{level: <8} {time:YYYY-MM-DD HH:mm:ss.SSS} - {name}:{function} - {message}" + } +} \ No newline at end of file From 91322d92e61254509f0d97cccaef3c0f4507b9a5 Mon Sep 17 00:00:00 2001 From: Liad Noam Date: Mon, 25 Jan 2021 00:40:59 +0200 Subject: [PATCH 14/16] Changed logging config to be received by separate parameters for simplicity, removed option to receive configuration as a dict, added .vscode to gitignore --- .gitignore | 3 ++ .vscode/settings.json | 5 --- app/config.py.example | 23 ++++++------ app/internal/logger_customizer.py | 58 +++++++++++-------------------- app/main.py | 9 +++-- tests/logger_fixture.py | 17 ++++----- tests/test_logger.py | 27 ++++---------- 7 files changed, 53 insertions(+), 89 deletions(-) delete mode 100644 .vscode/settings.json diff --git a/.gitignore b/.gitignore index 72930072..d9001003 100644 --- a/.gitignore +++ b/.gitignore @@ -134,3 +134,6 @@ dmypy.json # Pyre type checker .pyre/ + +# VSCode +.vscode/ diff --git a/.vscode/settings.json b/.vscode/settings.json deleted file mode 100644 index 09f7c786..00000000 --- a/.vscode/settings.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "cSpell.ignoreWords": [ - "smtpdfix" - ] -} \ No newline at end of file diff --git a/app/config.py.example b/app/config.py.example index 20601e7a..a6132cdf 100644 --- a/app/config.py.example +++ b/app/config.py.example @@ -22,7 +22,7 @@ WEATHER_API_KEY = os.getenv('WEATHER_API_KEY') 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", @@ -35,15 +35,12 @@ email_conf = ConnectionConfig( ) # LOGGER -LOGGER = { - "logger": { - "path": "./var/log", - "filename": "calendar.log", - "level": "error", - "rotation": "20 days", - "retention": "1 month", - "format": ("{level: <8} {time:YYYY-MM-DD HH:mm:ss.SSS}" - " - {name}:{function}" - " - {message}") - } -} \ No newline at end of file +LOG_PATH = "./var/log" +LOG_FILENAME = "calendar.log" +LOG_LEVEL = "error" +LOG_ROTATION_INTERVAL = "20 days" +LOG_RETENTION_INTERVAL = "1 month" +LOG_FORMAT = ("{level: <8}" + " {time:YYYY-MM-DD HH:mm:ss.SSS}" + " - {name}:{function}" + " - {message}") \ No newline at end of file diff --git a/app/internal/logger_customizer.py b/app/internal/logger_customizer.py index cab53c42..274f6c74 100644 --- a/app/internal/logger_customizer.py +++ b/app/internal/logger_customizer.py @@ -1,6 +1,4 @@ -import json import sys -from typing import Union, Dict from pathlib import Path from loguru import logger, _Logger as Logger @@ -13,14 +11,24 @@ class LoggerConfigError(Exception): class LoggerCustomizer: @classmethod - def make_logger(cls, config_file_or_dict: Union[Path, Dict], - logger_name: str) -> Logger: - """Creates a loguru logger from given configuration path or dict. + 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: - config_file_or_dict (Union[Path, Dict]): Path to logger - configuration file or dictionary of configuration - logger_name (str): Logger instance created from configuration + 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 @@ -28,19 +36,13 @@ def make_logger(cls, config_file_or_dict: Union[Path, Dict], Returns: Logger: Loguru logger instance """ - - config = cls.load_logging_config(config_file_or_dict) try: - logging_config = config.get(logger_name) - logs_path = logging_config.get('path') - log_file_path = logging_config.get('filename') - logger = cls.customize_logging( - file_path=Path(logs_path) / Path(log_file_path), - level=logging_config.get('level'), - retention=logging_config.get('retention'), - rotation=logging_config.get('rotation'), - format=logging_config.get('format') + 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( @@ -90,21 +92,3 @@ def customize_logging(cls, ) return logger - - @classmethod - def load_logging_config(cls, config: Union[Path, Dict]) -> Dict: - """Loads logging configuration from file or dict - - Args: - config (Union[Path, Dict]): Path to logging configuration file - - Returns: - Dict: Configuration parsed as dictionary - """ - if isinstance(config, Path): - with open(config) as config_file: - used_config = json.load(config_file) - else: - used_config = config - - return used_config diff --git a/app/main.py b/app/main.py index 789dea94..cc965bf9 100644 --- a/app/main.py +++ b/app/main.py @@ -6,9 +6,7 @@ from app.dependencies import ( MEDIA_PATH, STATIC_PATH, templates) from app.routers import agenda, event, profile, email, invitation - from app.internal.logger_customizer import LoggerCustomizer - from app import config models.Base.metadata.create_all(bind=engine) @@ -18,7 +16,12 @@ app.mount("/media", StaticFiles(directory=MEDIA_PATH), name="media") # Configure logger -logger = LoggerCustomizer.make_logger(config.LOGGER, "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 diff --git a/tests/logger_fixture.py b/tests/logger_fixture.py index c947ad2f..2684d336 100644 --- a/tests/logger_fixture.py +++ b/tests/logger_fixture.py @@ -10,17 +10,12 @@ @pytest.fixture(scope='module') def logger_instance(): - _logger = LoggerCustomizer.make_logger(config.LOGGER, "logger") - - return _logger - - -@pytest.fixture(scope='module') -def logger_external_configuration(): - from pathlib import Path - config_path = Path(__file__).parent - config_path = config_path.absolute() / 'logging_config.json' - _logger = LoggerCustomizer.make_logger(config_path, "logger1") + _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 diff --git a/tests/test_logger.py b/tests/test_logger.py index 436276d8..0c4cd97e 100644 --- a/tests/test_logger.py +++ b/tests/test_logger.py @@ -3,15 +3,10 @@ import pytest from app.internal.logger_customizer import LoggerCustomizer, LoggerConfigError +from app import config class TestLogger: - @staticmethod - def test_configuration_file(caplog, logger_external_configuration): - with caplog.at_level(logging.ERROR): - logger_external_configuration.error('Testing configuration ERROR') - assert 'Testing configuration' in caplog.text - @staticmethod def test_log_debug(caplog, logger_instance): with caplog.at_level(logging.DEBUG): @@ -38,18 +33,10 @@ def test_log_critical(caplog, logger_instance): @staticmethod def test_bad_configuration(): - bad_config = { - "logger": { - "path": "./var/log", - "filename": "calendar.log", - "level": "eror", - "rotation": "20 days", - "retention": "1 month", - "format": ("{level: <8} " - "{time:YYYY-MM-DD HH:mm:ss.SSS}" - "- {name}:{function}" - " - {message}") - } - } with pytest.raises(LoggerConfigError): - LoggerCustomizer.make_logger(bad_config, 'logger') + LoggerCustomizer.make_logger(config.LOG_PATH, + config.LOG_FILENAME, + 'eror', + config.LOG_ROTATION_INTERVAL, + config.LOG_RETENTION_INTERVAL, + config.LOG_FORMAT) From bca9c7c0704ada270b9933abb6b94708f78df697 Mon Sep 17 00:00:00 2001 From: Liad Noam Date: Tue, 26 Jan 2021 23:10:45 +0200 Subject: [PATCH 15/16] removed logging_config file which is no longer needed --- tests/logging_config.json | 10 ---------- 1 file changed, 10 deletions(-) delete mode 100644 tests/logging_config.json diff --git a/tests/logging_config.json b/tests/logging_config.json deleted file mode 100644 index ab90f6ba..00000000 --- a/tests/logging_config.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "logger1": { - "path": "./var/log4", - "filename": "calendar3.log", - "level": "debug", - "rotation": "20 days", - "retention": "1 month", - "format": "{level: <8} {time:YYYY-MM-DD HH:mm:ss.SSS} - {name}:{function} - {message}" - } -} \ No newline at end of file From 1a7fd84aaada9d444e739438a2a9e828d0bc10a3 Mon Sep 17 00:00:00 2001 From: Liad Noam Date: Wed, 27 Jan 2021 20:29:38 +0200 Subject: [PATCH 16/16] Fixing merge conflicts - missing whitespace on config.py.example --- app/config.py.example | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/config.py.example b/app/config.py.example index 9403bbe6..50b524b0 100644 --- a/app/config.py.example +++ b/app/config.py.example @@ -57,4 +57,4 @@ LOG_RETENTION_INTERVAL = "1 month" LOG_FORMAT = ("{level: <8}" " {time:YYYY-MM-DD HH:mm:ss.SSS}" " - {name}:{function}" - " - {message}") \ No newline at end of file + " - {message}")