Merge pull request #87 from grillazz/75-revisit-logging

refactor base model crud meths
This commit is contained in:
Jakub Miazek 2023-04-28 13:59:27 +02:00 committed by GitHub
commit ba11bf78f8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 69 additions and 51 deletions

View File

@ -50,6 +50,27 @@ Data set is coming form https://github.com/catherinedevlin/opensourceshakespeare
Next models were generated with https://github.com/agronholm/sqlacodegen Next models were generated with https://github.com/agronholm/sqlacodegen
And after some tweaking I got desired result And after some tweaking I got desired result
### Rainbow logs with rich :rainbow:
To deliver better user(developer) experience when watching logs with tons of information
from few emitters (which are really needy on development stage) project is using [rich](https://github.com/Textualize/rich) library.
Event with [rich](https://github.com/Textualize/rich) superpowers reading logs is not easy.
Found [rich](https://github.com/Textualize/rich) really nice -
but it took time to learn how to integrate it as logger object properly and keep it as singleton.
To address below needs:
- it is hard to find what I am looking for even with glasses on.
- dont want to hire ELK to be able to use logs.
- want to move fast enough with debugging.
Below steps were done to integrate [rich](https://github.com/Textualize/rich) into project.
1. Configure emitters with [config.ini](https://github.com/grillazz/fastapi-sqlalchemy-asyncpg/blob/main/config.ini)
2. Eliminate duplicates i.e. sqlalchemy echo by separate handlers
3. Keep logger as singleton pattern to avoid multiple instances
4. add uvicorn parameter --log-config config.ini
![sample-logs-with-rich](/static/logz.png)
Hope you enjoy it. Hope you enjoy it.
### Change Log ### Change Log
@ -59,6 +80,7 @@ Hope you enjoy it.
- 12 NOV 2022 ruff implemented to project as linting tool - 12 NOV 2022 ruff implemented to project as linting tool
- 14 FEB 2023 bump project to Python 3.11 - 14 FEB 2023 bump project to Python 3.11
- 10 APR 2023 implement logging with rich - 10 APR 2023 implement logging with rich
- 28 APR 2023 Rainbow logs with rich :rainbow:
### Local development with poetry ### Local development with poetry

View File

@ -4,7 +4,7 @@ from asyncpg import UniqueViolationError
from fastapi import HTTPException, status from fastapi import HTTPException, status
from sqlalchemy.exc import SQLAlchemyError, IntegrityError from sqlalchemy.exc import SQLAlchemyError, IntegrityError
from sqlalchemy.ext.asyncio import AsyncSession from sqlalchemy.ext.asyncio import AsyncSession
from sqlalchemy.ext.declarative import as_declarative, declared_attr from sqlalchemy.orm import as_declarative, declared_attr
@as_declarative() @as_declarative()
@ -53,16 +53,19 @@ class Base:
except SQLAlchemyError as ex: except SQLAlchemyError as ex:
raise HTTPException(status_code=status.HTTP_422_UNPROCESSABLE_ENTITY, detail=repr(ex)) from ex raise HTTPException(status_code=status.HTTP_422_UNPROCESSABLE_ENTITY, detail=repr(ex)) from ex
async def update(self, db_session: AsyncSession, **kwargs): async def update(self, db: AsyncSession, **kwargs):
""" """
:param db_session: :param db:
:param kwargs: :param kwargs
:return: :return:
""" """
try:
for k, v in kwargs.items(): for k, v in kwargs.items():
setattr(self, k, v) setattr(self, k, v)
await self.save(db_session) return await db.commit()
except SQLAlchemyError as ex:
raise HTTPException(status_code=status.HTTP_422_UNPROCESSABLE_ENTITY, detail=repr(ex)) from ex
async def save_or_update(self, db: AsyncSession): async def save_or_update(self, db: AsyncSession):
try: try:

View File

@ -1,55 +1,48 @@
[loggers] [loggers]
keys = root, sqlalchemy keys = root, sqlalchemy.engine.Engine, uvicorn.access
[handlers] [handlers]
keys = console, console_rich, error_file, access_file keys = stream, sqlalchemy, uvicorn
[formatters] [formatters]
keys = generic, generic_rich, access keys = default
[logger_root] [logger_root]
; Logging level for all loggers level = INFO
level = NOTSET propagate = 0
handlers = console_rich, error_file handlers = stream
[logger_sqlalchemy] [logger_sqlalchemy.engine.Engine]
handlers = level = INFO
qualname = sqlalchemy.engine propagate = 0
handlers = sqlalchemy
qualname = sqlalchemy.engine.Engine
[handler_console] [logger_uvicorn.access]
class = logging.StreamHandler level = INFO
level = NOTSET propagate = 0
formatter = generic handlers = uvicorn
stram = ext://sys.stdout qualname = uvicorn.access
[handler_error_file] [handler_stream]
class = logging.FileHandler
formatter = generic
level = WARNING
args = ('/tmp/error.log','w')
[handler_access_file]
class = logging.FileHandler
formatter = access
args = ('/tmp/access.log',)
[formatter_generic]
format = [%(process)d|%(name)-12s|%(filename)s:%(lineno)d] %(levelname)-7s %(message)s
datefmt = %H:%M:%S
class = logging.Formatter
[formatter_access]
format = %(message)s
class = logging.Formatter
[formatter_generic_rich]
format = [%(process)d %(name)s] %(message)s
datefmt = %H:%M:%S
class = logging.Formatter
[handler_console_rich]
class = app.logging.RichConsoleHandler class = app.logging.RichConsoleHandler
args = (100, "blue") kwargs = {"omit_repeated_times":True, "show_time": False, "enable_link_path": False, "tracebacks_show_locals": True}
kwargs = {"omit_repeated_times":False, "show_time": False, "enable_link_path": True, "tracebacks_show_locals": True} args = (100, "white")
level = NOTSET formatter = default
stream = ext://sys.stdout
[handler_sqlalchemy]
class = app.logging.RichConsoleHandler
kwargs = {"omit_repeated_times":True, "show_time": False, "enable_link_path": False, "tracebacks_show_locals": True}
args = (100, "magenta")
formatter = default
[handler_uvicorn]
class = app.logging.RichConsoleHandler
kwargs = {"omit_repeated_times":True, "show_time": False, "enable_link_path": False, "tracebacks_show_locals": True}
args = (100, "yellow")
formatter = default
[formatter_default]
format = [%(process)d|%(name)-12s] %(message)s
class = logging.Formatter

BIN
static/logz.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 273 KiB