Skip to content

Commit 18a8d71

Browse files
authored
Merge pull request #8705 from readthedocs/humitos/migrate-to-structlog
structlog: migrate application code to better logging
2 parents 2dc1c94 + 41b325e commit 18a8d71

Some content is hidden

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

99 files changed

+1232
-1108
lines changed

common

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)