mirror of
https://github.com/grillazz/fastapi-sqlalchemy-asyncpg.git
synced 2025-08-26 16:40:40 +03:00
Merge pull request #87 from grillazz/75-revisit-logging
refactor base model crud meths
This commit is contained in:
commit
ba11bf78f8
22
README.md
22
README.md
@ -50,6 +50,27 @@ Data set is coming form https://github.com/catherinedevlin/opensourceshakespeare
|
||||
Next models were generated with https://github.com/agronholm/sqlacodegen
|
||||
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.
|
||||
- don’t 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
|
||||
|
||||

|
||||
|
||||
Hope you enjoy it.
|
||||
|
||||
### Change Log
|
||||
@ -59,6 +80,7 @@ Hope you enjoy it.
|
||||
- 12 NOV 2022 ruff implemented to project as linting tool
|
||||
- 14 FEB 2023 bump project to Python 3.11
|
||||
- 10 APR 2023 implement logging with rich
|
||||
- 28 APR 2023 Rainbow logs with rich :rainbow:
|
||||
|
||||
### Local development with poetry
|
||||
|
||||
|
@ -4,7 +4,7 @@ from asyncpg import UniqueViolationError
|
||||
from fastapi import HTTPException, status
|
||||
from sqlalchemy.exc import SQLAlchemyError, IntegrityError
|
||||
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()
|
||||
@ -53,16 +53,19 @@ class Base:
|
||||
except SQLAlchemyError as 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 kwargs:
|
||||
:param db:
|
||||
:param kwargs
|
||||
:return:
|
||||
"""
|
||||
for k, v in kwargs.items():
|
||||
setattr(self, k, v)
|
||||
await self.save(db_session)
|
||||
try:
|
||||
for k, v in kwargs.items():
|
||||
setattr(self, k, v)
|
||||
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):
|
||||
try:
|
||||
|
81
config.ini
81
config.ini
@ -1,55 +1,48 @@
|
||||
[loggers]
|
||||
keys = root, sqlalchemy
|
||||
keys = root, sqlalchemy.engine.Engine, uvicorn.access
|
||||
|
||||
[handlers]
|
||||
keys = console, console_rich, error_file, access_file
|
||||
keys = stream, sqlalchemy, uvicorn
|
||||
|
||||
[formatters]
|
||||
keys = generic, generic_rich, access
|
||||
keys = default
|
||||
|
||||
[logger_root]
|
||||
; Logging level for all loggers
|
||||
level = NOTSET
|
||||
handlers = console_rich, error_file
|
||||
level = INFO
|
||||
propagate = 0
|
||||
handlers = stream
|
||||
|
||||
[logger_sqlalchemy]
|
||||
handlers =
|
||||
qualname = sqlalchemy.engine
|
||||
[logger_sqlalchemy.engine.Engine]
|
||||
level = INFO
|
||||
propagate = 0
|
||||
handlers = sqlalchemy
|
||||
qualname = sqlalchemy.engine.Engine
|
||||
|
||||
[handler_console]
|
||||
class = logging.StreamHandler
|
||||
level = NOTSET
|
||||
formatter = generic
|
||||
stram = ext://sys.stdout
|
||||
[logger_uvicorn.access]
|
||||
level = INFO
|
||||
propagate = 0
|
||||
handlers = uvicorn
|
||||
qualname = uvicorn.access
|
||||
|
||||
[handler_error_file]
|
||||
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]
|
||||
[handler_stream]
|
||||
class = app.logging.RichConsoleHandler
|
||||
args = (100, "blue")
|
||||
kwargs = {"omit_repeated_times":False, "show_time": False, "enable_link_path": True, "tracebacks_show_locals": True}
|
||||
level = NOTSET
|
||||
kwargs = {"omit_repeated_times":True, "show_time": False, "enable_link_path": False, "tracebacks_show_locals": True}
|
||||
args = (100, "white")
|
||||
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
BIN
static/logz.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 273 KiB |
Loading…
x
Reference in New Issue
Block a user