Skip to content

Commit 1110839

Browse files
committed
structlog: migrate application code to better logging
Initial work to migrate our application to structlog for better logging, making them compatible with New Relic's integration. There are more work that can be done here to make it better. For example, more usage of `log.bind` to avoid repetition. Besides, rewriting some log message and reduce them to have more Canonical Log Lines (https://www.structlog.org/en/stable/logging-best-practices.html#canonical-log-lines) instead.
1 parent 2dc1c94 commit 1110839

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

97 files changed

+1059
-1095
lines changed

readthedocs/analytics/utils.py

+3-3
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44

55
import hashlib
66
import ipaddress
7-
import logging
7+
import structlog
88

99
import requests
1010
from django.conf import settings
@@ -13,7 +13,7 @@
1313
from user_agents import parse
1414

1515

16-
log = logging.getLogger(__name__) # noqa
16+
log = structlog.get_logger(__name__) # noqa
1717

1818

1919
def get_client_ip(request):
@@ -79,7 +79,7 @@ def send_to_analytics(data):
7979
data['ua'] = anonymize_user_agent(data['ua'])
8080

8181
resp = None
82-
log.debug('Sending data to analytics: %s', data)
82+
log.debug('Sending data to analytics.', data=data)
8383
try:
8484
resp = requests.post(
8585
'https://www.google-analytics.com/collect',

readthedocs/api/v2/client.py

+5-5
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
"""Simple client to access our API with Slumber credentials."""
22

3-
import logging
3+
import structlog
44

55
import requests
66
from django.conf import settings
@@ -10,7 +10,7 @@
1010

1111
from .adapters import TimeoutHostHeaderSSLAdapter, TimeoutHTTPAdapter
1212

13-
log = logging.getLogger(__name__)
13+
log = structlog.get_logger(__name__)
1414

1515

1616
class DrfJsonSerializer(serialize.JsonSerializer):
@@ -63,9 +63,9 @@ def setup_api():
6363
}
6464
if settings.SLUMBER_USERNAME and settings.SLUMBER_PASSWORD:
6565
log.debug(
66-
'Using slumber v2 with user %s, pointed at %s',
67-
settings.SLUMBER_USERNAME,
68-
settings.SLUMBER_API_HOST,
66+
'Using slumber v2.',
67+
username=settings.SLUMBER_USERNAME,
68+
api_host=settings.SLUMBER_API_HOST,
6969
)
7070
session.auth = (settings.SLUMBER_USERNAME, settings.SLUMBER_PASSWORD)
7171
else:

readthedocs/api/v2/mixins.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
import logging
1+
import structlog
22

3-
log = logging.getLogger(__name__)
3+
log = structlog.get_logger(__name__)
44

55

66
class CachedResponseMixin:

readthedocs/api/v2/utils.py

+9-9
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
"""Utility functions that are used by both views and celery tasks."""
22

33
import itertools
4-
import logging
4+
import structlog
55

66
from rest_framework.pagination import PageNumberPagination
77

@@ -17,7 +17,7 @@
1717
)
1818
from readthedocs.builds.models import RegexAutomationRule, Version
1919

20-
log = logging.getLogger(__name__)
20+
log = structlog.get_logger(__name__)
2121

2222

2323
def sync_versions_to_db(project, versions, type): # pylint: disable=redefined-builtin
@@ -86,9 +86,9 @@ def sync_versions_to_db(project, versions, type): # pylint: disable=redefined-b
8686
)
8787

8888
log.info(
89-
'(Sync Versions) Updated Version: [%s=%s] ',
90-
version_name,
91-
version_id,
89+
'Re-syncing versions: version updated.',
90+
version_verbose_name=version_name,
91+
version_id=version_id,
9292
)
9393
else:
9494
# New Version
@@ -116,8 +116,9 @@ def sync_versions_to_db(project, versions, type): # pylint: disable=redefined-b
116116
latest_version.save()
117117
if added:
118118
log.info(
119-
'(Sync Versions) Added Versions: versions_count=%d versions=[%s]',
120-
len(added), ' '.join(itertools.islice(added, 100)),
119+
'Re-syncing versions: versions added.',
120+
count=len(added),
121+
versions=','.join(itertools.islice(added, 100)),
121122
)
122123
return added
123124

@@ -214,8 +215,7 @@ def delete_versions_from_db(project, tags_data, branches_data):
214215
_, deleted = to_delete_qs.delete()
215216
versions_count = deleted.get('builds.Version', 0)
216217
log.info(
217-
'(Sync Versions) Deleted Versions: project=%s versions_count=%s',
218-
project.slug, versions_count,
218+
'Re-syncing versions: versions deleted.', project_slug=project.slug, count=versions_count,
219219
)
220220

221221

readthedocs/api/v2/views/footer_views.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
"""Endpoint to generate footer HTML."""
22

3-
import logging
3+
import structlog
44
import re
55
from functools import lru_cache
66

@@ -24,7 +24,7 @@
2424
parse_version_failsafe,
2525
)
2626

27-
log = logging.getLogger(__name__)
27+
log = structlog.get_logger(__name__)
2828

2929

3030
def get_version_compare_data(project, base_version=None):

readthedocs/api/v2/views/integrations.py

+29-17
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
import hashlib
44
import hmac
55
import json
6-
import logging
6+
import structlog
77
import re
88

99
from django.shortcuts import get_object_or_404
@@ -29,7 +29,7 @@
2929
from readthedocs.integrations.models import HttpExchange, Integration
3030
from readthedocs.projects.models import Feature, Project
3131

32-
log = logging.getLogger(__name__)
32+
log = structlog.get_logger(__name__)
3333

3434
GITHUB_EVENT_HEADER = 'HTTP_X_GITHUB_EVENT'
3535
GITHUB_SIGNATURE_HEADER = 'HTTP_X_HUB_SIGNATURE'
@@ -86,8 +86,9 @@ def post(self, request, project_slug):
8686
raise NotFound('Project not found')
8787
if not self.is_payload_valid():
8888
log.warning(
89-
'Invalid payload for project: %s and integration: %s',
90-
project_slug, self.integration_type
89+
'Invalid payload for project and integration.',
90+
project_slug=project_slug,
91+
integration_type=self.integration_type,
9192
)
9293
return Response(
9394
{'detail': self.invalid_payload_msg},
@@ -184,9 +185,9 @@ def get_response_push(self, project, branches):
184185
to_build, not_building = build_branches(project, branches)
185186
if not_building:
186187
log.info(
187-
'Skipping project branches: project=%s branches=%s',
188-
project,
189-
branches,
188+
'Skipping project branches.',
189+
project_slug=project.slug,
190+
branches=branches,
190191
)
191192
triggered = bool(to_build)
192193
return {
@@ -338,8 +339,8 @@ def is_payload_valid(self):
338339
secret = self.get_integration().secret
339340
if not secret:
340341
log.info(
341-
'Skipping payload signature validation. project=%s',
342-
self.project.slug,
342+
'Skipping payload signature validation.',
343+
project_slug=self.project.slug,
343344
)
344345
return True
345346
if not signature:
@@ -405,7 +406,11 @@ def handle_webhook(self):
405406

406407
# Sync versions when a branch/tag was created/deleted
407408
if event in (GITHUB_CREATE, GITHUB_DELETE):
408-
log.info('Triggered sync_versions: project=%s event=%s', self.project, event)
409+
log.info(
410+
'Triggered sync_versions.',
411+
project_slug=self.project.slug,
412+
webhook_event=event,
413+
)
409414
return self.sync_versions_response(self.project)
410415

411416
# Handle pull request events
@@ -441,7 +446,7 @@ def handle_webhook(self):
441446
# already have the CREATE/DELETE events. So we don't trigger the sync twice.
442447
return self.sync_versions_response(self.project, sync=False)
443448

444-
log.info('Triggered sync_versions: project=%s events=%s', self.project, events)
449+
log.info('Triggered sync_versions.', project_slug=self.project.slug, events=events)
445450
return self.sync_versions_response(self.project)
446451

447452
# Trigger a build for all branches in the push
@@ -512,8 +517,8 @@ def is_payload_valid(self):
512517
secret = self.get_integration().secret
513518
if not secret:
514519
log.info(
515-
'Skipping payload signature validation. project=%s',
516-
self.project.slug,
520+
'Skipping payload signature validation.',
521+
project_slug=self.project.slug,
517522
)
518523
return True
519524
if not token:
@@ -554,8 +559,12 @@ def handle_webhook(self):
554559
after = data.get('after')
555560
# Tag/branch created/deleted
556561
if GITLAB_NULL_HASH in (before, after):
557-
log.info('Triggered sync_versions: project=%s before=%s after=%s',
558-
self.project, before, after)
562+
log.info(
563+
'Triggered sync_versions.',
564+
project_slug=self.project.slug,
565+
before=before,
566+
after=after,
567+
)
559568
return self.sync_versions_response(self.project)
560569
# Normal push to master
561570
try:
@@ -652,8 +661,11 @@ def handle_webhook(self):
652661
# will be triggered with the normal push.
653662
if branches:
654663
return self.get_response_push(self.project, branches)
655-
log.info('Triggered sync_versions: project=%s event=%s',
656-
self.project, event)
664+
log.info(
665+
'Triggered sync_versions.',
666+
project_slug=self.project.slug,
667+
webhook_event=event,
668+
)
657669
return self.sync_versions_response(self.project)
658670
except KeyError:
659671
raise ParseError('Invalid request')

readthedocs/api/v2/views/model_views.py

+4-4
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
"""Endpoints for listing Projects, Versions, Builds, etc."""
22

33
import json
4-
import logging
4+
import structlog
55

66
from allauth.socialaccount.models import SocialAccount
77
from django.conf import settings
@@ -40,7 +40,7 @@
4040
RemoteProjectPagination,
4141
)
4242

43-
log = logging.getLogger(__name__)
43+
log = structlog.get_logger(__name__)
4444

4545

4646
class PlainTextBuildRenderer(BaseRenderer):
@@ -269,8 +269,8 @@ def retrieve(self, *args, **kwargs):
269269
data['commands'] = json.loads(json_resp)
270270
except Exception:
271271
log.exception(
272-
'Failed to read build data from storage. path=%s.',
273-
storage_path,
272+
'Failed to read build data from storage.',
273+
path=storage_path,
274274
)
275275
return Response(data)
276276

readthedocs/api/v2/views/task_views.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
"""Endpoints relating to task/job status, etc."""
22

3-
import logging
3+
import structlog
44

55
from django.urls import reverse
66
from redis import ConnectionError
@@ -12,7 +12,7 @@
1212
from readthedocs.oauth import tasks
1313

1414

15-
log = logging.getLogger(__name__)
15+
log = structlog.get_logger(__name__)
1616

1717
SUCCESS_STATES = ('SUCCESS',)
1818
FAILURE_STATES = (

readthedocs/audit/apps.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
"""Audit module."""
22

3-
import logging
3+
import structlog
44

55
from django.apps import AppConfig
66

7-
log = logging.getLogger(__name__)
7+
log = structlog.get_logger(__name__)
88

99

1010
class AuditConfig(AppConfig):

readthedocs/builds/apps.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
1-
import logging
1+
import structlog
22

33
from django.apps import AppConfig
44
from django.utils.translation import ugettext_lazy as _
55

6-
log = logging.getLogger(__name__)
6+
log = structlog.get_logger(__name__)
77

88

99
class Config(AppConfig):

readthedocs/builds/automation_actions.py

+5-4
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,12 @@
88
- action_arg: An additional argument to apply the action
99
"""
1010

11-
import logging
11+
import structlog
1212

1313
from readthedocs.core.utils import trigger_build
1414
from readthedocs.projects.constants import PRIVATE, PUBLIC
1515

16-
log = logging.getLogger(__name__)
16+
log = structlog.get_logger(__name__)
1717

1818

1919
def activate_version(version, match_result, action_arg, *args, **kwargs):
@@ -72,8 +72,9 @@ def delete_version(version, match_result, action_arg, *args, **kwargs):
7272
"""Delete a version if isn't marked as the default version."""
7373
if version.project.default_version == version.slug:
7474
log.info(
75-
"Skipping deleting default version. project=%s version=%s",
76-
version.project.slug, version.slug,
75+
"Skipping deleting default version.",
76+
project_slug=version.project.slug,
77+
version_slug=version.slug,
7778
)
7879
return
7980
version.delete()

readthedocs/builds/filters.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
1-
import logging
1+
import structlog
22

33
from django.forms.widgets import HiddenInput
44
from django.utils.translation import ugettext_lazy as _
55
from django_filters import CharFilter, ChoiceFilter, FilterSet
66

77
from readthedocs.builds.constants import BUILD_STATE_FINISHED
88

9-
log = logging.getLogger(__name__)
9+
log = structlog.get_logger(__name__)
1010

1111

1212
class BuildListFilter(FilterSet):

readthedocs/builds/managers.py

+3-3
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
"""Build and Version class model Managers."""
22

3-
import logging
3+
import structlog
44

55
from django.core.exceptions import ObjectDoesNotExist
66
from django.db import models
@@ -18,7 +18,7 @@
1818
from readthedocs.builds.querysets import VersionQuerySet
1919
from readthedocs.core.utils.extend import get_override_class
2020

21-
log = logging.getLogger(__name__)
21+
log = structlog.get_logger(__name__)
2222

2323

2424
__all__ = ['VersionManager']
@@ -78,7 +78,7 @@ def get_object_or_log(self, **kwargs):
7878
try:
7979
return super().get(**kwargs)
8080
except ObjectDoesNotExist:
81-
log.warning('Version not found for given kwargs. %s', kwargs)
81+
log.warning('Version not found for given kwargs.', kwargs=kwargs)
8282

8383

8484
class InternalVersionManager(VersionManager):

0 commit comments

Comments
 (0)