Skip to content

Commit 2b8bf69

Browse files
Merge pull request #6 from leplatrem/add-mozlog
Log every request with MozLog
2 parents 1947d11 + f16020c commit 2b8bf69

File tree

6 files changed

+113
-3
lines changed

6 files changed

+113
-3
lines changed

poetry.lock

Lines changed: 19 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pyproject.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ gunicorn = "^20.1.0"
1414
prometheus-client = "^0.13.1"
1515
python-bugzilla = "^3.2.0"
1616
atlassian-python-api = "^3.20.1"
17+
dockerflow = "2022.1.0"
1718

1819
[tool.poetry.dev-dependencies]
1920
pre-commit = "^2.17.0"

src/app/api.py

Lines changed: 39 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,18 @@
1+
import logging
2+
import time
3+
from datetime import datetime
4+
15
import uvicorn # type: ignore
26
from fastapi import FastAPI, Request
37
from fastapi.responses import RedirectResponse
48

59
from src.app.environment import get_settings
10+
from src.app.log import configure_logging
611
from src.app.monitor import api_router as monitor_router
712
from src.jbi.router import api_router as jbi_router
813

14+
settings = get_settings()
15+
916
app = FastAPI(
1017
title="Jira Bugzilla Integration (JBI)",
1118
description="JBI v2 Platform",
@@ -26,8 +33,38 @@ def root(request: Request):
2633
return RedirectResponse(url="./docs")
2734

2835

36+
@app.middleware("http")
37+
async def request_summary(request: Request, call_next):
38+
summary_logger = logging.getLogger("request.summary")
39+
previous_time = time.time()
40+
41+
infos = {
42+
"agent": request.headers.get("User-Agent"),
43+
"path": request.url.path,
44+
"method": request.method,
45+
"lang": request.headers.get("Accept-Language"),
46+
"querystring": dict(request.query_params),
47+
"errno": 0,
48+
}
49+
50+
response = await call_next(request)
51+
52+
current = time.time()
53+
duration = int((current - previous_time) * 1000.0)
54+
isotimestamp = datetime.fromtimestamp(current).isoformat()
55+
infos = {"time": isotimestamp, "code": response.status_code, "t": duration, **infos}
56+
57+
summary_logger.info("", extra=infos)
58+
59+
return response
60+
61+
62+
@app.on_event("startup")
63+
def startup_event():
64+
configure_logging()
65+
66+
2967
if __name__ == "__main__":
30-
settings = get_settings()
3168
uvicorn.run(
32-
"app:app", host="0.0.0.0", port=settings.port, reload=settings.app_reload
69+
"app:app", host=settings.host, port=settings.port, reload=settings.app_reload
3370
)

src/app/environment.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66

77

88
class Settings(BaseSettings):
9+
host: str = "0.0.0.0"
910
port: str = "80"
1011
app_reload: bool = True
1112

@@ -20,6 +21,9 @@ class Settings(BaseSettings):
2021
jira_password: str
2122
bugzilla_api_key: str
2223

24+
# Logging
25+
log_level: str = "info"
26+
2327

2428
@lru_cache()
2529
def get_settings() -> Settings:

src/app/log.py

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
import logging
2+
import logging.config
3+
import sys
4+
5+
from src.app.environment import get_settings
6+
7+
settings = get_settings()
8+
9+
10+
def configure_logging():
11+
logging_config = {
12+
"version": 1,
13+
"formatters": {
14+
"mozlog_json": {
15+
"()": "dockerflow.logging.JsonLogFormatter",
16+
"logger_name": "jbi",
17+
},
18+
},
19+
"handlers": {
20+
"console": {
21+
"level": settings.log_level.upper(),
22+
"class": "logging.StreamHandler",
23+
"formatter": "mozlog_json",
24+
"stream": sys.stdout,
25+
}
26+
},
27+
"loggers": {
28+
"request.summary": {"handlers": ["console"], "level": "INFO"},
29+
},
30+
}
31+
32+
logging.config.dictConfig(logging_config)

tests/unit/app/test_api.py

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,8 @@
1+
from fastapi.testclient import TestClient
2+
3+
from src.app.api import app
4+
5+
16
def test_read_root(anon_client):
27
"""The site root redirects to the Swagger docs"""
38
resp = anon_client.get("/")
@@ -6,3 +11,16 @@ def test_read_root(anon_client):
611
prev_resp = resp.history[0]
712
assert prev_resp.status_code == 307 # Temporary Redirect
813
assert prev_resp.headers["location"] == "./docs"
14+
15+
16+
def test_request_summary_is_logged(caplog):
17+
with TestClient(app) as anon_client:
18+
# https://fastapi.tiangolo.com/advanced/testing-events/
19+
anon_client.get("/__lbheartbeat__")
20+
21+
summary = caplog.records[-1]
22+
23+
assert summary.name == "request.summary"
24+
assert summary.method == "GET"
25+
assert summary.path == "/__lbheartbeat__"
26+
assert summary.querystring == {}

0 commit comments

Comments
 (0)