Skip to content

Commit 0b5cda9

Browse files
authored
Fix #344: add request id to logs (#398)
* Fix #344: add request id to logs * @grahamalama review
1 parent e9db0d4 commit 0b5cda9

File tree

7 files changed

+59
-3
lines changed

7 files changed

+59
-3
lines changed

jbi/app.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
import logging
55
import time
66
from pathlib import Path
7+
from secrets import token_hex
78
from typing import Any, Awaitable, Callable
89

910
import sentry_sdk
@@ -47,6 +48,16 @@ def traces_sampler(sampling_context: dict[str, Any]) -> float:
4748
app.mount("/static", StaticFiles(directory=SRC_DIR / "static"), name="static")
4849

4950

51+
@app.middleware("http")
52+
async def request_id(
53+
request: Request, call_next: Callable[[Request], Awaitable[Response]]
54+
) -> Response:
55+
"""Read the request id from headers. This is set by NGinx."""
56+
request.state.rid = request.headers.get("X-Request-Id", token_hex(16))
57+
response = await call_next(request)
58+
return response
59+
60+
5061
@app.middleware("http")
5162
async def request_summary(
5263
request: Request, call_next: Callable[[Request], Awaitable[Response]]

jbi/log.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ class RequestSummary(BaseModel):
5555
t: int
5656
time: str
5757
status_code: int
58+
rid: str
5859

5960

6061
def format_request_summary_fields(
@@ -73,4 +74,5 @@ def format_request_summary_fields(
7374
t=int((current_time - request_time) * 1000.0),
7475
time=datetime.fromtimestamp(current_time).isoformat(),
7576
status_code=status_code,
77+
rid=request.state.rid,
7678
).dict()

jbi/models.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -323,6 +323,7 @@ def lookup_action(self, actions: Actions) -> Action:
323323
class BugzillaWebhookRequest(BaseModel):
324324
"""Bugzilla Webhook Request Object"""
325325

326+
rid: str = "" # This field has a default since it's not parsed from body.
326327
webhook_id: int
327328
webhook_name: str
328329
event: BugzillaWebhookEvent
@@ -366,6 +367,7 @@ class JiraContext(Context):
366367
class RunnerContext(Context, extra=Extra.forbid):
367368
"""Logging context from runner"""
368369

370+
rid: str
369371
operation: Operation
370372
event: BugzillaWebhookEvent
371373
action: Optional[Action]
@@ -375,6 +377,7 @@ class RunnerContext(Context, extra=Extra.forbid):
375377
class ActionContext(Context, extra=Extra.forbid):
376378
"""Logging context from actions"""
377379

380+
rid: str
378381
operation: Operation
379382
event: BugzillaWebhookEvent
380383
jira: JiraContext

jbi/router.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -64,13 +64,15 @@ def version(version_json=Depends(get_version)):
6464

6565
@router.post("/bugzilla_webhook")
6666
def bugzilla_webhook(
67-
request: BugzillaWebhookRequest = Body(..., embed=False),
67+
request: Request,
68+
webhook_request: BugzillaWebhookRequest = Body(..., embed=False),
6869
actions: Actions = Depends(get_actions),
6970
settings: Settings = Depends(get_settings),
7071
):
7172
"""API endpoint that Bugzilla Webhook Events request"""
73+
webhook_request.rid = request.state.rid
7274
try:
73-
result = execute_action(request, actions, settings)
75+
result = execute_action(webhook_request, actions, settings)
7476
return result
7577
except IgnoreInvalidRequestError as exception:
7678
return {"error": str(exception)}

jbi/runner.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ def execute_action(
3838
bug, event = request.bug, request.event
3939
webhook_comment: Optional[BugzillaWebhookComment] = bug.comment
4040
runner_context = RunnerContext(
41+
rid=request.rid,
4142
bug=bug,
4243
event=event,
4344
operation=Operation.HANDLE,
@@ -75,6 +76,7 @@ def execute_action(
7576
linked_issue_key: Optional[str] = bug.extract_from_see_also()
7677

7778
action_context = ActionContext(
79+
rid=request.rid,
7880
bug=bug,
7981
event=event,
8082
operation=Operation.IGNORE,

tests/fixtures/factories.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
from secrets import token_hex
2+
13
from jbi import Operation
24
from jbi.models import (
35
Action,
@@ -100,6 +102,7 @@ def comment_factory(**overrides):
100102
def action_context_factory(**overrides):
101103
return ActionContext.parse_obj(
102104
{
105+
"rid": token_hex(16),
103106
"operation": Operation.IGNORE,
104107
"bug": bug_factory(),
105108
"event": webhook_event_factory(),

tests/unit/test_app.py

Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,11 +13,17 @@ def test_request_summary_is_logged(caplog):
1313
with caplog.at_level(logging.INFO):
1414
with TestClient(app) as anon_client:
1515
# https://fastapi.tiangolo.com/advanced/testing-events/
16-
anon_client.get("/")
16+
anon_client.get(
17+
"/",
18+
headers={
19+
"X-Request-Id": "foo-bar",
20+
},
21+
)
1722

1823
summary = caplog.records[-1]
1924

2025
assert summary.name == "request.summary"
26+
assert summary.rid == "foo-bar"
2127
assert summary.method == "GET"
2228
assert summary.path == "/"
2329
assert summary.querystring == "{}"
@@ -71,3 +77,30 @@ def test_errors_are_reported_to_sentry(
7177
)
7278

7379
assert mocked.called, "Sentry captured the exception"
80+
81+
82+
def test_request_id_is_passed_down_to_logger_contexts(
83+
caplog,
84+
webhook_create_example: BugzillaWebhookRequest,
85+
mocked_jira,
86+
mocked_bugzilla,
87+
):
88+
mocked_bugzilla.get_bug.return_value = webhook_create_example.bug
89+
mocked_jira.create_issue.return_value = {
90+
"key": "JBI-1922",
91+
}
92+
with caplog.at_level(logging.DEBUG):
93+
with TestClient(app) as anon_client:
94+
anon_client.post(
95+
"/bugzilla_webhook",
96+
data=webhook_create_example.json(),
97+
headers={
98+
"X-Request-Id": "foo-bar",
99+
},
100+
)
101+
102+
runner_logs = [r for r in caplog.records if r.name == "jbi.runner"]
103+
assert runner_logs[0].rid == "foo-bar"
104+
105+
action_logs = [r for r in caplog.records if r.name == "jbi.actions.default"]
106+
assert action_logs[0].rid == "foo-bar"

0 commit comments

Comments
 (0)