This commit is contained in:
20
src/middlewares/__init__.py
Normal file
20
src/middlewares/__init__.py
Normal file
@ -0,0 +1,20 @@
|
||||
from logging import getLogger
|
||||
|
||||
from fastapi import FastAPI
|
||||
from fastapi.middleware.cors import CORSMiddleware
|
||||
|
||||
from .access_log_middleware import AccessLogMiddleware
|
||||
|
||||
logger = getLogger(__name__)
|
||||
|
||||
|
||||
def register_middlewares(app: FastAPI):
|
||||
app.add_middleware(AccessLogMiddleware)
|
||||
app.add_middleware(
|
||||
CORSMiddleware,
|
||||
allow_origins=['*'],
|
||||
allow_methods=['*'],
|
||||
allow_headers=['*'],
|
||||
)
|
||||
|
||||
return app
|
||||
108
src/middlewares/access_log_middleware.py
Normal file
108
src/middlewares/access_log_middleware.py
Normal file
@ -0,0 +1,108 @@
|
||||
from logging import getLogger
|
||||
from re import findall
|
||||
from time import perf_counter
|
||||
|
||||
from starlette.types import ASGIApp, Message, Receive, Scope, Send
|
||||
|
||||
from core.config import settings
|
||||
|
||||
LOCALHOST = '127.0.0.1'
|
||||
BROWSERS = {
|
||||
'firefox': 'Firefox',
|
||||
'yabrowser': 'Yandex',
|
||||
'samsungbrowser': 'Samsung Internet',
|
||||
'trident': 'Internet Explorer',
|
||||
'opera': 'Opera',
|
||||
'vivaldi': 'Vivaldi',
|
||||
'brave': 'Brave',
|
||||
'edg': 'Edge',
|
||||
'chrome': 'Chrome',
|
||||
'safari': 'Safari',
|
||||
'chromium': 'Chromium',
|
||||
'msie': 'Internet Explorer',
|
||||
}
|
||||
|
||||
|
||||
class AccessLogMiddleware:
|
||||
def __init__(self, app: ASGIApp):
|
||||
self.app = app
|
||||
self.logger = getLogger(__name__)
|
||||
|
||||
self.version = (
|
||||
b'Version',
|
||||
f'{settings.VERSION}'.encode(),
|
||||
)
|
||||
|
||||
async def detect_browser(self, headers: dict[bytes, bytes]):
|
||||
if b'user-agent' not in headers:
|
||||
return 'unknown'
|
||||
|
||||
user_agent = headers[b'user-agent'].decode().lower()
|
||||
|
||||
for k, v in BROWSERS.items():
|
||||
if findall(k, user_agent):
|
||||
return v
|
||||
|
||||
return 'unknown'
|
||||
|
||||
@staticmethod
|
||||
async def get_client_ip(
|
||||
headers: dict[bytes, bytes],
|
||||
default_ip: str = LOCALHOST,
|
||||
):
|
||||
if b'x-forwarded-for' not in headers:
|
||||
return default_ip
|
||||
|
||||
ips = headers[b'x-forwarded-for'].decode().split(',')
|
||||
|
||||
if len(ips) > 1:
|
||||
return ips[-1].strip()
|
||||
|
||||
return ips[0]
|
||||
|
||||
async def __call__(self, scope: Scope, receive: Receive, send: Send):
|
||||
if scope['type'] != 'http':
|
||||
return await self.app(scope, receive, send)
|
||||
|
||||
start_time = perf_counter()
|
||||
|
||||
async def send_wrapper(message: Message) -> None:
|
||||
if message['type'] != 'http.response.start':
|
||||
return await send(message)
|
||||
|
||||
headers = dict(scope.get('headers', []))
|
||||
|
||||
client_ip = await self.get_client_ip(headers, scope['client'][0])
|
||||
browser = await self.detect_browser(headers)
|
||||
|
||||
response_time = (perf_counter() - start_time) * 1000
|
||||
response_data = f'dur={response_time:.2f}'
|
||||
response = (
|
||||
b'Server-Timing',
|
||||
f'resp;{response_data};desc="Response Time"'.encode(),
|
||||
)
|
||||
|
||||
message['headers'] = message['headers'] + [response, self.version]
|
||||
|
||||
self.logger.info(
|
||||
'%s - %s %s %d [%0.2fms]',
|
||||
client_ip,
|
||||
scope['method'],
|
||||
scope['path'],
|
||||
message['status'],
|
||||
response_time,
|
||||
extra={
|
||||
'tags': {
|
||||
'method': scope['method'],
|
||||
'path': scope['path'],
|
||||
'status': message['status'],
|
||||
'response_time': response_time,
|
||||
'client_ip': client_ip,
|
||||
'browser': browser,
|
||||
},
|
||||
},
|
||||
)
|
||||
|
||||
await send(message)
|
||||
|
||||
return await self.app(scope, receive, send_wrapper)
|
||||
Reference in New Issue
Block a user