alessandro trinca tornidor
feat: change logger library to structlog
c7d60e1
raw
history blame
4.17 kB
import logging
import os
import time
import gradio as gr
import structlog
import uvicorn
from asgi_correlation_id import CorrelationIdMiddleware
from asgi_correlation_id.context import correlation_id
from fastapi import FastAPI, Request, Response, APIRouter
from uvicorn.protocols.utils import get_path_with_query_string
from helpers.structlog_setup import setup_logging
from helpers import formatters
from dotenv import load_dotenv
from routes import router
load_dotenv()
# LOG_JSON_FORMAT = parse_obj_as(bool, os.getenv("LOG_JSON_FORMAT", False))
LOG_JSON_FORMAT = bool(os.getenv("LOG_JSON_FORMAT", False))
LOG_LEVEL = os.getenv("LOG_LEVEL", "INFO")
setup_logging(json_logs=LOG_JSON_FORMAT, log_level=LOG_LEVEL)
access_logger = structlog.stdlib.get_logger(__name__)
app = FastAPI(title="Example API", version="1.0.0")
@app.middleware("http")
async def logging_middleware(request: Request, call_next) -> Response:
structlog.contextvars.clear_contextvars()
# These context vars will be added to all log entries emitted during the request
request_id = correlation_id.get()
# print(f"request_id:{request_id}.")
structlog.contextvars.bind_contextvars(request_id=request_id)
start_time = time.perf_counter_ns()
# If the call_next raises an error, we still want to return our own 500 response,
# so we can add headers to it (process time, request ID...)
response = Response(status_code=500)
try:
response = await call_next(request)
except Exception:
# TODO: Validate that we don't swallow exceptions (unit test?)
structlog.stdlib.get_logger("api.error").exception("Uncaught exception")
raise
finally:
process_time = time.perf_counter_ns() - start_time
status_code = response.status_code
url = get_path_with_query_string(request.scope)
client_host = request.client.host
client_port = request.client.port
http_method = request.method
http_version = request.scope["http_version"]
# Recreate the Uvicorn access log format, but add all parameters as structured information
access_logger.info(
f"""{client_host}:{client_port} - "{http_method} {url} HTTP/{http_version}" {status_code}""",
http={
"url": str(request.url),
"status_code": status_code,
"method": http_method,
"request_id": request_id,
"version": http_version,
},
network={"client": {"ip": client_host, "port": client_port}},
duration=process_time,
)
response.headers["X-Process-Time"] = str(process_time / 10 ** 9)
return response
app.include_router(router)
logging.info("routes included, creating gradio app")
CUSTOM_GRADIO_PATH = "/"
def get_gradio_app():
with gr.Blocks() as gradio_app:
access_logger.info("start gradio app building...")
gr.Markdown(
"""
# Hello World!
Start typing below to _see_ the *output*.
Here a [link](https://huggingface.co/spaces/aletrn/gradio_with_fastapi).
"""
)
btn = gr.Button(value="Divide et Impera")
text_input = gr.Textbox(lines=1, placeholder="10",
label="write an integer to divide 100; 0 will raise ZeroDivisionError")
text_output = gr.Textbox(lines=1, placeholder=None, label="Text Output")
gr.Examples(
examples=[19, 1, -7, 0],
inputs=[text_input],
)
btn.click(
formatters.request_formatter,
inputs=[text_input, ],
outputs=[text_output]
)
return gradio_app
logging.info("mounting gradio app within FastAPI...")
gradio_app_md = get_gradio_app()
app.add_middleware(CorrelationIdMiddleware)
app = gr.mount_gradio_app(app, gradio_app_md, path=CUSTOM_GRADIO_PATH)
logging.info("gradio app mounted")
if __name__ == "__main__":
try:
uvicorn.run("app:app", host="127.0.0.1", port=7860, log_config=None, reload=True)
except Exception as ex:
logging.error(f"ex:{ex}.")
raise ex