API: Add request logging

Log all the parts of a request if the config flag is set. The logged
fields are all server side anyways, so nothing is being exposed to
clients.

Signed-off-by: kingbri <bdashore3@proton.me>
This commit is contained in:
kingbri 2024-07-22 21:33:10 -04:00
parent 522999ebb4
commit 3826815edb
4 changed files with 61 additions and 28 deletions

View file

@ -51,10 +51,7 @@ def from_args(args: dict):
cur_logging_config = logging_config() cur_logging_config = logging_config()
GLOBAL_CONFIG["logging"] = { GLOBAL_CONFIG["logging"] = {
**cur_logging_config, **cur_logging_config,
**{ **{k.replace("log_", ""): logging_override[k] for k in logging_override},
k.replace("log_", ""): logging_override[k]
for k in logging_override
},
} }
developer_override = args.get("developer") developer_override = args.get("developer")

View file

@ -1,9 +1,10 @@
"""Common utility functions""" """Common utility functions"""
import asyncio import asyncio
import json
import socket import socket
import traceback import traceback
from fastapi import HTTPException, Request from fastapi import Depends, HTTPException, Request
from loguru import logger from loguru import logger
from pydantic import BaseModel from pydantic import BaseModel
from typing import Optional from typing import Optional
@ -108,3 +109,32 @@ async def add_request_id(request: Request):
request.state.id = uuid4().hex request.state.id = uuid4().hex
return request return request
async def log_request(request: Request):
"""FastAPI depends to log a request to the user."""
log_message = [f"Information for {request.method} request {request.state.id}:"]
log_message.append(f"URL: {request.url}")
log_message.append(f"Headers: {dict(request.headers)}")
if request.method != "GET":
body_bytes = await request.body()
if body_bytes:
body = json.loads(body_bytes.decode("utf-8"))
log_message.append(f"Body: {dict(body)}")
logger.info("\n".join(log_message))
def get_global_depends():
"""Returns global dependencies for a FastAPI app."""
depends = [Depends(add_request_id)]
if config.logging_config().get("requests"):
depends.append(Depends(log_request))
return depends

View file

@ -31,6 +31,10 @@ logging:
# Enable generation parameter logging (default: False) # Enable generation parameter logging (default: False)
generation_params: False generation_params: False
# Enable request logging (default: False)
# NOTE: Only use this for debugging!
requests: False
# Options for sampling # Options for sampling
sampling: sampling:
# Override preset name. Find this in the sampler-overrides folder (default: None) # Override preset name. Find this in the sampler-overrides folder (default: None)

View file

@ -1,42 +1,44 @@
import uvicorn import uvicorn
from fastapi import Depends, FastAPI from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware from fastapi.middleware.cors import CORSMiddleware
from loguru import logger from loguru import logger
from common.logger import UVICORN_LOG_CONFIG from common.logger import UVICORN_LOG_CONFIG
from common.networking import add_request_id from common.networking import get_global_depends
from endpoints.OAI.router import router as OAIRouter from endpoints.OAI.router import router as OAIRouter
app = FastAPI(
title="TabbyAPI",
summary="An OAI compatible exllamav2 API that's both lightweight and fast",
description=(
"This docs page is not meant to send requests! Please use a service "
"like Postman or a frontend UI."
),
dependencies=[Depends(add_request_id)],
)
# ALlow CORS requests
app.add_middleware(
CORSMiddleware,
allow_origins=["*"],
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)
def setup_app(): def setup_app():
"""Includes the correct routers for startup""" """Includes the correct routers for startup"""
app = FastAPI(
title="TabbyAPI",
summary="An OAI compatible exllamav2 API that's both lightweight and fast",
description=(
"This docs page is not meant to send requests! Please use a service "
"like Postman or a frontend UI."
),
dependencies=get_global_depends(),
)
# ALlow CORS requests
app.add_middleware(
CORSMiddleware,
allow_origins=["*"],
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)
app.include_router(OAIRouter) app.include_router(OAIRouter)
return app
def export_openapi(): def export_openapi():
"""Function to return the OpenAPI JSON from the API server""" """Function to return the OpenAPI JSON from the API server"""
setup_app() app = setup_app()
return app.openapi() return app.openapi()
@ -49,7 +51,7 @@ async def start_api(host: str, port: int):
logger.info(f"Chat completions: http://{host}:{port}/v1/chat/completions") logger.info(f"Chat completions: http://{host}:{port}/v1/chat/completions")
# Setup app # Setup app
setup_app() app = setup_app()
config = uvicorn.Config( config = uvicorn.Config(
app, app,