Nove zavislosti, logovani, refaktoring
This commit is contained in:
+1
-1
@@ -36,4 +36,4 @@ COPY . /app
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
CMD ["uvicorn", "src.asgi:app", "--reload", "--reload-dir=/app", "--port=8000", "--host=0.0.0.0", "--log-level=debug"]
|
||||
CMD [ "gunicorn", "src.asgi:app", "--config=/app/config/gunicorn.py" ]
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
import os
|
||||
|
||||
# https://docs.gunicorn.org/en/stable/settings.html#settings
|
||||
|
||||
workers = os.cpu_count()
|
||||
bind = "0.0.0.0:8000"
|
||||
worker_class = "uvicorn.workers.UvicornWorker"
|
||||
worker_connections = 1000 # default
|
||||
threads = 1 # default
|
||||
|
||||
# restart workeru
|
||||
max_requests = 500000
|
||||
max_requests_jitter = 10000
|
||||
|
||||
timeout = 30 # default
|
||||
graceful_timeout = 30 # default
|
||||
|
||||
keepalive = 600 # s klientem udrzuje nginx
|
||||
@@ -3,7 +3,6 @@ version: "3.3"
|
||||
services:
|
||||
api:
|
||||
image: "docker.kvacek.cz/poletani/api:latest"
|
||||
command: "uvicorn src.asgi:app --port=8000 --host=0.0.0.0 --workers 4"
|
||||
network_mode: "host"
|
||||
restart: "always"
|
||||
env_file:
|
||||
|
||||
+6
-1
@@ -3,7 +3,10 @@ fastapi-jwt[authlib]==0.3.0
|
||||
bcrypt
|
||||
fastapi-mail==1.4.2
|
||||
strawberry-graphql[fastapi]==0.258.0
|
||||
uvicorn==0.34.0
|
||||
|
||||
gunicorn == 23.0.0
|
||||
uvicorn[standard] == 0.34.0
|
||||
|
||||
sqlalchemy[asyncio] == 2.0.37
|
||||
aiomysql==0.2.0
|
||||
alembic==1.14.1
|
||||
@@ -15,3 +18,5 @@ exif==1.6.1
|
||||
aiocache==0.12.3
|
||||
aiohttp==3.11.11
|
||||
lxml==5.3.0
|
||||
|
||||
colorlog
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import dataclasses
|
||||
from datetime import datetime
|
||||
from fastapi import BackgroundTasks, Depends, Security
|
||||
from fastapi_jwt import JwtAuthorizationCredentials
|
||||
from sqlalchemy import select
|
||||
@@ -9,6 +10,7 @@ from jwt import access_security
|
||||
|
||||
@dataclasses.dataclass
|
||||
class GraphQLContext(BaseContext):
|
||||
request_start_time: datetime
|
||||
user_id: int
|
||||
organization_ids: set[int]
|
||||
jwt_auth_credentials: JwtAuthorizationCredentials
|
||||
@@ -29,5 +31,6 @@ async def setup_graphql_context(credentials: JwtAuthorizationCredentials = Secur
|
||||
user_id=user_id,
|
||||
organization_ids=organization_ids,
|
||||
jwt_auth_credentials=credentials,
|
||||
background_tasks=Depends(BackgroundTasks)
|
||||
background_tasks=Depends(BackgroundTasks),
|
||||
request_start_time=datetime.now()
|
||||
)
|
||||
|
||||
@@ -1,37 +1,30 @@
|
||||
from datetime import datetime
|
||||
import strawberry
|
||||
from strawberry.extensions import SchemaExtension, ValidationCache
|
||||
|
||||
from strawberry.extensions import SchemaExtension, ValidationCache, ParserCache
|
||||
from graphql_schema.extensions.schema.error_logging import ErrorLogging
|
||||
from logger import log
|
||||
from .mutation import Mutation
|
||||
from .query import Query
|
||||
|
||||
|
||||
# Toto se da kdyztak pouzit jako extension do Schema
|
||||
# class SQLAlchemySession(Extension):
|
||||
# def on_request_start(self):
|
||||
# session = async_session()
|
||||
# print(self.execution_context.context)
|
||||
# self.execution_context.context["db"] = session
|
||||
#
|
||||
# async def on_request_end(self):
|
||||
# await self.execution_context.context["db"].close()
|
||||
|
||||
class LoggingExtension(SchemaExtension):
|
||||
def on_request_start(self):
|
||||
print("request start")
|
||||
|
||||
async def on_request_end(self):
|
||||
print("request end")
|
||||
|
||||
def on_request_end(self):
|
||||
# nastavuje se pri vytvareni GraphQL contextu
|
||||
request_start = self.execution_context.context.request_start_time
|
||||
request_end = datetime.now()
|
||||
|
||||
request_time_ms = round((request_end - request_start).microseconds / 1000)
|
||||
op_name = self.execution_context.operation_name
|
||||
log.info(f"Operation {op_name} took {request_time_ms}ms")
|
||||
|
||||
|
||||
schema = strawberry.Schema(
|
||||
query=Query,
|
||||
mutation=Mutation,
|
||||
extensions=[
|
||||
ErrorLogging(),
|
||||
LoggingExtension(),
|
||||
ErrorLogging(),
|
||||
ParserCache(),
|
||||
ValidationCache()
|
||||
],
|
||||
)
|
||||
|
||||
+16
-10
@@ -1,22 +1,28 @@
|
||||
import logging
|
||||
import sys
|
||||
from colorlog import ColoredFormatter
|
||||
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
log.setLevel(logging.DEBUG)
|
||||
|
||||
# Create handlers for logging to the standard output and a file
|
||||
stdoutHandler = logging.StreamHandler(stream=sys.stdout)
|
||||
|
||||
# Set the log levels on the handlers
|
||||
stdoutHandler.setLevel(logging.DEBUG)
|
||||
|
||||
# Create a log format using Log Record attributes
|
||||
fmt = logging.Formatter(
|
||||
"%(name)s: %(asctime)s | %(levelname)s | %(filename)s:%(lineno)s | %(process)d >>> %(message)s"
|
||||
|
||||
fmt = ColoredFormatter(
|
||||
"%(log_color)s[%(levelname)s]%(reset)s %(blue)s%(message)s%(reset)s %(light_black)s[%(filename)s:%(lineno)d]]%(reset)s",
|
||||
datefmt=None,
|
||||
reset=True,
|
||||
log_colors={
|
||||
'DEBUG': 'cyan',
|
||||
'INFO': 'green',
|
||||
'WARNING': 'yellow',
|
||||
'ERROR': 'red',
|
||||
'CRITICAL': 'red,bg_white',
|
||||
},
|
||||
secondary_log_colors={},
|
||||
style='%'
|
||||
)
|
||||
|
||||
# Set the log format on each handler
|
||||
stdoutHandler.setFormatter(fmt)
|
||||
|
||||
# Add each handler to the Logger object
|
||||
log.addHandler(stdoutHandler)
|
||||
|
||||
+17
-1
@@ -1,6 +1,9 @@
|
||||
import sentry_sdk
|
||||
from fastapi import FastAPI, HTTPException, APIRouter
|
||||
from graphql import GraphQLError
|
||||
from sentry_sdk.integrations.fastapi import FastApiIntegration
|
||||
from sentry_sdk.integrations.starlette import StarletteIntegration
|
||||
from sentry_sdk.integrations.strawberry import StrawberryIntegration
|
||||
from sqlalchemy.exc import NoResultFound
|
||||
from starlette.background import BackgroundTasks
|
||||
from starlette.middleware.cors import CORSMiddleware
|
||||
@@ -27,10 +30,23 @@ class App:
|
||||
|
||||
def create_app(self):
|
||||
if SENTRY_DSN:
|
||||
fast_api_params = dict(
|
||||
transaction_style="endpoint",
|
||||
failed_request_status_codes={403, *range(500, 599)},
|
||||
http_methods_to_capture=("GET", "POST"),
|
||||
)
|
||||
|
||||
sentry_sdk.init(
|
||||
dsn=SENTRY_DSN,
|
||||
enable_tracing=True,
|
||||
ignore_errors=[GraphQLError, HTTPException]
|
||||
traces_sample_rate=0.5,
|
||||
profiles_sample_rate=0.5,
|
||||
ignore_errors=[GraphQLError, HTTPException],
|
||||
integrations=[
|
||||
StrawberryIntegration(async_execution=True),
|
||||
StarletteIntegration(**fast_api_params),
|
||||
FastApiIntegration(**fast_api_params),
|
||||
]
|
||||
)
|
||||
|
||||
app = FastAPI(
|
||||
|
||||
Reference in New Issue
Block a user