alessandro trinca tornidor
feat: change logger library to structlog
c7d60e1
raw
history blame contribute delete
No virus
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