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>

Files changed (2) hide show
  1. app/logger.py +15 -0
  2. 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)