Skip to content

Commit d7fbcb2

Browse files
authored
feat(OTel): Add first structlog events for OTel event pipeline (#7237)
1 parent 89ed1ea commit d7fbcb2

7 files changed

Lines changed: 79 additions & 2 deletions

File tree

api/core/workflows_services.py

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
from features.workflows.core.models import ChangeRequest
1515
from users.models import FFAdminUser
1616

17-
logger = structlog.get_logger()
17+
logger = structlog.get_logger("workflows")
1818

1919

2020
class ChangeRequestCommitService:
@@ -34,6 +34,15 @@ def commit(self, committed_by: "FFAdminUser") -> None:
3434

3535
self.change_request.committed_at = timezone.now()
3636
self.change_request.committed_by = committed_by
37+
38+
if environment := self.change_request.environment:
39+
logger.info(
40+
"change_request.committed",
41+
organisation__id=environment.project.organisation_id,
42+
environment__id=environment.id,
43+
feature_states__count=self.change_request.feature_states.count(),
44+
)
45+
3746
self.change_request.save()
3847

3948
def _publish_feature_states(self) -> None:

api/integrations/github/views.py

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
from urllib.parse import urlparse
77

88
import requests
9+
import structlog
910
from django.conf import settings
1011
from django.db.utils import IntegrityError
1112
from rest_framework import status, viewsets
@@ -54,6 +55,7 @@
5455
from projects.code_references.services import get_code_references_for_feature_flag
5556

5657
logger = logging.getLogger(__name__)
58+
code_references_logger = structlog.get_logger("code_references")
5759

5860

5961
def github_auth_required(func): # type: ignore[no-untyped-def]
@@ -383,6 +385,12 @@ def create_cleanup_issue(request, organisation_pk: int) -> Response: # type: ig
383385
except IntegrityError:
384386
pass
385387

388+
code_references_logger.info(
389+
"cleanup_issues.created",
390+
organisation__id=organisation_pk,
391+
issues_created__count=len(summaries),
392+
)
393+
386394
return Response(status=status.HTTP_204_NO_CONTENT)
387395

388396

api/projects/code_references/views.py

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
from typing import Any
22

3+
import structlog
34
from django.shortcuts import get_object_or_404
45
from drf_spectacular.utils import extend_schema
56
from rest_framework import generics, response
@@ -19,6 +20,8 @@
1920
FeatureFlagCodeReferencesRepositorySummary,
2021
)
2122

23+
logger = structlog.get_logger("code_references")
24+
2225

2326
class FeatureFlagCodeReferencesScanCreateAPIView(
2427
generics.CreateAPIView[FeatureFlagCodeReferencesScan]
@@ -33,7 +36,14 @@ class FeatureFlagCodeReferencesScanCreateAPIView(
3336
def perform_create( # type: ignore[override]
3437
self, serializer: FeatureFlagCodeReferencesScanSerializer
3538
) -> None:
36-
serializer.save(project_id=self.kwargs["project_pk"])
39+
instance = serializer.save(project_id=self.kwargs["project_pk"])
40+
feature_names = {ref["feature_name"] for ref in instance.code_references}
41+
logger.info(
42+
"scan.created",
43+
organisation__id=instance.project.organisation_id,
44+
code_references__count=len(instance.code_references),
45+
feature__count=len(feature_names),
46+
)
3747

3848

3949
@extend_schema(

api/tests/unit/features/workflows/core/test_unit_workflows_models.py

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
from flag_engine.segments.constants import EQUAL, PERCENTAGE_SPLIT
1010
from freezegun.api import FrozenDateTimeFactory
1111
from pytest_mock import MockerFixture
12+
from pytest_structlog import StructuredLogCapture
1213

1314
from audit.constants import (
1415
CHANGE_REQUEST_APPROVED_MESSAGE,
@@ -208,6 +209,28 @@ def test_change_request_commit__not_scheduled__sets_committed_at_and_version( #
208209
assert change_request_no_required_approvals.feature_states.first().live_from == now
209210

210211

212+
def test_change_request_commit__valid_request__emits_structlog_event(
213+
change_request_no_required_approvals: ChangeRequest,
214+
log: StructuredLogCapture,
215+
) -> None:
216+
# Given
217+
user = FFAdminUser.objects.create(email="committer@example.com")
218+
219+
# When
220+
change_request_no_required_approvals.commit(committed_by=user)
221+
222+
# Then
223+
environment = change_request_no_required_approvals.environment
224+
assert environment is not None
225+
assert {
226+
"event": "change_request.committed",
227+
"level": "info",
228+
"organisation__id": environment.project.organisation_id,
229+
"environment__id": environment.id,
230+
"feature_states__count": change_request_no_required_approvals.feature_states.count(),
231+
} in log.events
232+
233+
211234
def test_change_request_create__valid_environment__creates_audit_log( # type: ignore[no-untyped-def]
212235
environment, admin_user
213236
):

api/tests/unit/integrations/github/test_unit_github_cleanup_issue.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
import responses
77
from pytest_django.fixtures import SettingsWrapper
88
from pytest_mock import MockerFixture
9+
from pytest_structlog import StructuredLogCapture
910
from rest_framework import status
1011
from rest_framework.test import APIClient
1112

@@ -78,6 +79,7 @@ def test_create_cleanup_issue__valid_request__returns_204(
7879
admin_client_new: APIClient,
7980
organisation: Organisation,
8081
feature: Feature,
82+
log: StructuredLogCapture,
8183
) -> None:
8284
# Given
8385
github_issue_response_1: dict[str, Any] = {
@@ -135,6 +137,15 @@ def test_create_cleanup_issue__valid_request__returns_204(
135137
request_body_2 = json.loads(responses.calls[1].request.body)
136138
assert "lib/flags.py#L7" in request_body_2["body"]
137139

140+
assert log.events == [
141+
{
142+
"event": "cleanup_issues.created",
143+
"level": "info",
144+
"organisation__id": organisation.id,
145+
"issues_created__count": 2,
146+
},
147+
]
148+
138149

139150
@responses.activate
140151
@pytest.mark.usefixtures("set_github_pat", "mock_code_references")

api/tests/unit/projects/code_references/test_unit_projects_code_references_views.py

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import freezegun
2+
from pytest_structlog import StructuredLogCapture
23
from rest_framework.test import APIClient
34

45
from features.models import Feature
@@ -10,6 +11,7 @@
1011
def test_create_code_reference__valid_payload__returns_201_with_accepted_references(
1112
admin_client_new: APIClient,
1213
project: Project,
14+
log: StructuredLogCapture,
1315
) -> None:
1416
# Given / When
1517
response = admin_client_new.post(
@@ -63,6 +65,16 @@ def test_create_code_reference__valid_payload__returns_201_with_accepted_referen
6365
},
6466
]
6567

68+
assert log.events == [
69+
{
70+
"event": "scan.created",
71+
"level": "info",
72+
"organisation__id": project.organisation_id,
73+
"code_references__count": 3,
74+
"feature__count": 2,
75+
},
76+
]
77+
6678

6779
def test_create_code_reference__not_authenticated__returns_401(
6880
client: APIClient,

infrastructure/aws/staging/ecs-task-definition-task-processor.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -175,6 +175,10 @@
175175
{
176176
"name": "LOG_LEVEL",
177177
"value": "INFO"
178+
},
179+
{
180+
"name": "LOG_FORMAT",
181+
"value": "json"
178182
}
179183
],
180184
"secrets": [

0 commit comments

Comments
 (0)