Robin Claude Sonnet 4.6 commited on
Commit ·
41904b3
1
Parent(s): 9a7f7d0
feat: add request/response logging with elapsed time
Browse files- app/logger.py: centralized logging config (stdout, timestamp)
- main.py: log input (text_len, labels, threshold) and output
(entities, elapsed ms) for every /extract call
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- app/logger.py +15 -0
- app/main.py +20 -1
app/logger.py
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import logging
|
| 2 |
+
import sys
|
| 3 |
+
|
| 4 |
+
_FMT = "%(asctime)s [%(levelname)s] %(name)s - %(message)s"
|
| 5 |
+
_DATE_FMT = "%Y-%m-%d %H:%M:%S"
|
| 6 |
+
|
| 7 |
+
logging.basicConfig(
|
| 8 |
+
level=logging.INFO,
|
| 9 |
+
format=_FMT,
|
| 10 |
+
datefmt=_DATE_FMT,
|
| 11 |
+
stream=sys.stdout,
|
| 12 |
+
)
|
| 13 |
+
|
| 14 |
+
def get_logger(name: str) -> logging.Logger:
|
| 15 |
+
return logging.getLogger(name)
|
app/main.py
CHANGED
|
@@ -1,18 +1,23 @@
|
|
|
|
|
| 1 |
from contextlib import asynccontextmanager
|
| 2 |
|
| 3 |
-
from fastapi import FastAPI
|
| 4 |
|
| 5 |
from app.config import NER_API_BASE_URL
|
|
|
|
| 6 |
from app.models import ExtractRequest, ExtractResponse
|
| 7 |
from app.ner import NERService
|
| 8 |
|
|
|
|
| 9 |
ner_service: NERService | None = None
|
| 10 |
|
| 11 |
|
| 12 |
@asynccontextmanager
|
| 13 |
async def lifespan(app: FastAPI):
|
| 14 |
global ner_service
|
|
|
|
| 15 |
ner_service = NERService(NER_API_BASE_URL)
|
|
|
|
| 16 |
yield
|
| 17 |
ner_service = None
|
| 18 |
|
|
@@ -27,5 +32,19 @@ def health():
|
|
| 27 |
|
| 28 |
@app.post("/api/v1/extract", response_model=ExtractResponse)
|
| 29 |
def extract(req: ExtractRequest):
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 30 |
entities = ner_service.extract(req.text, req.labels, req.threshold)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 31 |
return ExtractResponse(entities=entities)
|
|
|
|
| 1 |
+
import time
|
| 2 |
from contextlib import asynccontextmanager
|
| 3 |
|
| 4 |
+
from fastapi import FastAPI, Request
|
| 5 |
|
| 6 |
from app.config import NER_API_BASE_URL
|
| 7 |
+
from app.logger import get_logger
|
| 8 |
from app.models import ExtractRequest, ExtractResponse
|
| 9 |
from app.ner import NERService
|
| 10 |
|
| 11 |
+
logger = get_logger("ner.api")
|
| 12 |
ner_service: NERService | None = None
|
| 13 |
|
| 14 |
|
| 15 |
@asynccontextmanager
|
| 16 |
async def lifespan(app: FastAPI):
|
| 17 |
global ner_service
|
| 18 |
+
logger.info("Loading NER service, upstream=%s", NER_API_BASE_URL)
|
| 19 |
ner_service = NERService(NER_API_BASE_URL)
|
| 20 |
+
logger.info("NER service ready")
|
| 21 |
yield
|
| 22 |
ner_service = None
|
| 23 |
|
|
|
|
| 32 |
|
| 33 |
@app.post("/api/v1/extract", response_model=ExtractResponse)
|
| 34 |
def extract(req: ExtractRequest):
|
| 35 |
+
logger.info(
|
| 36 |
+
"extract request | text_len=%d labels=%s threshold=%s",
|
| 37 |
+
len(req.text),
|
| 38 |
+
req.labels,
|
| 39 |
+
req.threshold,
|
| 40 |
+
)
|
| 41 |
+
t0 = time.perf_counter()
|
| 42 |
entities = ner_service.extract(req.text, req.labels, req.threshold)
|
| 43 |
+
elapsed_ms = (time.perf_counter() - t0) * 1000
|
| 44 |
+
|
| 45 |
+
logger.info(
|
| 46 |
+
"extract response | entities=%s elapsed=%.1fms",
|
| 47 |
+
[{"text": e.text, "label": e.label, "score": e.score} for e in entities],
|
| 48 |
+
elapsed_ms,
|
| 49 |
+
)
|
| 50 |
return ExtractResponse(entities=entities)
|