Skip to content

Improve error reporting to users #3244

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions docs/faq.rst
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@ If you add a subproject to a project,
that documentation will also be served under the parent project's subdomain.

For example,
Kombu is a subproject of celery,
Kombu is a subproject of Celery,
so you can access it on the `celery.readthedocs.io` domain:

http://celery.readthedocs.io/projects/kombu/en/latest/
Expand Down Expand Up @@ -204,4 +204,4 @@ file* field.
What commit of Read the Docs is in production?
----------------------------------------------

We deploy readthedocs.org from the `rel` branch in our GitHub repository. You can see the latest commits that have been deployed by looking on GitHub: https://github.com/rtfd/readthedocs.org/commits/rel
We deploy readthedocs.org from the `rel` branch in our GitHub repository. You can see the latest commits that have been deployed by looking on GitHub: https://github.com/rtfd/readthedocs.org/commits/rel
6 changes: 3 additions & 3 deletions docs/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -92,18 +92,18 @@ Information about development is also available:
:maxdepth: 2
:caption: Developer Documentation

install
api/index
changelog
install
architecture
tests
docs
architecture
development/standards
development/buildenvironments
symlinks
settings
i18n
issue-labels
api/index

.. _business-docs:

Expand Down
4 changes: 4 additions & 0 deletions readthedocs/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
"""Read the Docs"""

# Import the Celery application before anything else happens
from readthedocs.worker import app # noqa
4 changes: 2 additions & 2 deletions readthedocs/builds/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -155,7 +155,7 @@ def save(self, *args, **kwargs): # pylint: disable=arguments-differ
try:
self.project.sync_supported_versions()
except Exception:
log.error('failed to sync supported versions', exc_info=True)
log.exception('failed to sync supported versions')
broadcast(type='app', task=tasks.symlink_project, args=[self.project.pk])
return obj

Expand Down Expand Up @@ -224,7 +224,7 @@ def clean_build_path(self):
path, self))
rmtree(path)
except OSError:
log.error('Build path cleanup failed', exc_info=True)
log.exception('Build path cleanup failed')

def get_github_url(self, docroot, filename, source_suffix='.rst', action='view'):
"""
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,4 +53,4 @@ def handle(self, *args, **options):
update_search(version.pk, commit,
delete_non_commit_files=False)
except Exception:
log.error('Reindex failed for %s', version, exc_info=True)
log.exception('Reindex failed for {}'.format(version))
2 changes: 1 addition & 1 deletion readthedocs/core/management/commands/set_metadata.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,4 +23,4 @@ def handle(self, *args, **options):
try:
broadcast(type='app', task=tasks.update_static_metadata, args=[p.pk])
except Exception:
log.error('Build failed for %s', p, exc_info=True)
log.exception('Build failed for %s', p)
3 changes: 2 additions & 1 deletion readthedocs/core/management/commands/update_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,4 +33,5 @@ def handle(self, *args, **options):
project_data = api.project(slug).get()
p = APIProject(**project_data)
log.info("Building %s", p)
tasks.update_docs.run(pk=p.pk, docker=docker)
update_docs = tasks.UpdateDocsTask()
update_docs.run(pk=p.pk, docker=docker)
25 changes: 16 additions & 9 deletions readthedocs/core/management/commands/update_repos.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,9 +54,11 @@ def handle(self, *args, **options):
for version in Version.objects.filter(project__slug=slug,
active=True,
uploaded=False):
tasks.update_docs.run(pk=version.project_id,
record=False,
version_pk=version.pk)
tasks.UpdateDocsTask().run(
pk=version.project_id,
record=False,
version_pk=version.pk
)
else:
p = Project.all_objects.get(slug=slug)
log.info("Building %s", p)
Expand All @@ -66,12 +68,17 @@ def handle(self, *args, **options):
log.info("Updating all versions")
for version in Version.objects.filter(active=True,
uploaded=False):
tasks.update_docs.run(pk=version.project_id,
record=record,
force=force,
version_pk=version.pk)
tasks.UpdateDocsTask().run(
pk=version.project_id,
record=record,
force=force,
version_pk=version.pk
)
else:
log.info("Updating all docs")
for project in Project.objects.all():
tasks.update_docs.run(pk=project.pk, record=record,
force=force)
tasks.UpdateDocsTask().run(
pk=project.pk,
record=record,
force=force
)
3 changes: 2 additions & 1 deletion readthedocs/core/management/commands/update_versions.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
from django.core.management.base import BaseCommand

from readthedocs.builds.models import Version
from readthedocs.projects.tasks import update_docs
from readthedocs.projects.tasks import UpdateDocsTask


class Command(BaseCommand):
Expand All @@ -13,5 +13,6 @@ class Command(BaseCommand):

def handle(self, *args, **options):
for version in Version.objects.filter(active=True, built=False):
update_docs = UpdateDocsTask()
update_docs.run(version.project_id, record=False,
version_pk=version.pk)
5 changes: 3 additions & 2 deletions readthedocs/core/tasks.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,19 +3,20 @@
from __future__ import absolute_import
import logging

from celery import task
from django.conf import settings
from django.core.mail import EmailMultiAlternatives
from django.template.loader import get_template
from django.template import TemplateDoesNotExist

from readthedocs.worker import app


log = logging.getLogger(__name__)

EMAIL_TIME_LIMIT = 30


@task(queue='web', time_limit=EMAIL_TIME_LIMIT)
@app.task(queue='web', time_limit=EMAIL_TIME_LIMIT)
def send_email_task(recipient, subject, template, template_html, context=None):
"""Send multipart email

Expand Down
13 changes: 10 additions & 3 deletions readthedocs/core/utils/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,9 +49,15 @@ def broadcast(type, task, args, kwargs=None, callback=None): # pylint: disable=
task_sig = task.s(*args, **kwargs).set(queue=server)
tasks.append(task_sig)
if callback:
task_promise = chord(tasks)(callback).get()
task_promise = chord(tasks, callback).apply_async()
else:
task_promise = group(*tasks).apply_async()
# Celery's Group class does some special handling when an iterable with
# len() == 1 is passed in. This will be hit if there is only one server
# defined in the above queue lists
if len(tasks) > 1:
task_promise = group(*tasks).apply_async()
else:
task_promise = group(tasks).apply_async()
return task_promise


Expand All @@ -77,7 +83,7 @@ def trigger_build(project, version=None, record=True, force=False, basic=False):
will be prefixed with ``build-`` to unify build queue names.
"""
# Avoid circular import
from readthedocs.projects.tasks import update_docs
from readthedocs.projects.tasks import UpdateDocsTask
from readthedocs.builds.models import Build

if project.skip:
Expand Down Expand Up @@ -121,6 +127,7 @@ def trigger_build(project, version=None, record=True, force=False, basic=False):
options['soft_time_limit'] = time_limit
options['time_limit'] = int(time_limit * 1.2)

update_docs = UpdateDocsTask()
update_docs.apply_async(kwargs=kwargs, **options)

return build
Expand Down
5 changes: 3 additions & 2 deletions readthedocs/core/utils/tasks/retrieve.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
"""Utilities for retrieving task data."""

from __future__ import absolute_import
from djcelery import celery as celery_app
from celery.result import AsyncResult


Expand All @@ -20,14 +19,16 @@ def get_task_data(task_id):

meta data has no ``'task_name'`` key set.
"""
from readthedocs.worker import app

result = AsyncResult(task_id)
state, info = result.state, result.info
if state == 'PENDING':
raise TaskNotFound(task_id)
if 'task_name' not in info:
raise TaskNotFound(task_id)
try:
task = celery_app.tasks[info['task_name']]
task = app.tasks[info['task_name']]
except KeyError:
raise TaskNotFound(task_id)
return task, state, info
10 changes: 5 additions & 5 deletions readthedocs/core/views/hooks.py
Original file line number Diff line number Diff line change
Expand Up @@ -178,7 +178,7 @@ def github_build(request): # noqa: D205
ssh_search_url = ssh_url.replace('git@', '').replace('.git', '')
branches = [data['ref'].replace('refs/heads/', '')]
except (ValueError, TypeError, KeyError):
log.error('Invalid GitHub webhook payload', exc_info=True)
log.exception('Invalid GitHub webhook payload')
return HttpResponse('Invalid request', status=400)
try:
repo_projects = get_project_from_url(http_search_url)
Expand All @@ -198,7 +198,7 @@ def github_build(request): # noqa: D205
projects = repo_projects | ssh_projects
return _build_url(http_search_url, projects, branches)
except NoProjectException:
log.error('Project match not found: url=%s', http_search_url)
log.exception('Project match not found: url=%s', http_search_url)
return HttpResponseNotFound('Project not found')
else:
return HttpResponse('Method not allowed, POST is required', status=405)
Expand All @@ -222,7 +222,7 @@ def gitlab_build(request): # noqa: D205
search_url = re.sub(r'^https?://(.*?)(?:\.git|)$', '\\1', url)
branches = [data['ref'].replace('refs/heads/', '')]
except (ValueError, TypeError, KeyError):
log.error('Invalid GitLab webhook payload', exc_info=True)
log.exception('Invalid GitLab webhook payload')
return HttpResponse('Invalid request', status=400)
log.info(
'GitLab webhook search: url=%s branches=%s',
Expand Down Expand Up @@ -281,7 +281,7 @@ def bitbucket_build(request):
data['repository']['full_name']
)
except (TypeError, ValueError, KeyError):
log.error('Invalid Bitbucket webhook payload', exc_info=True)
log.exception('Invalid Bitbucket webhook payload')
return HttpResponse('Invalid request', status=400)

log.info(
Expand Down Expand Up @@ -320,7 +320,7 @@ def generic_build(request, project_id_or_slug=None):
try:
project = Project.objects.get(slug=project_id_or_slug)
except (Project.DoesNotExist, ValueError):
log.error(
log.exception(
"(Incoming Generic Build) Repo not found: %s",
project_id_or_slug)
return HttpResponseNotFound(
Expand Down
13 changes: 10 additions & 3 deletions readthedocs/doc_builder/environments.py
Original file line number Diff line number Diff line change
Expand Up @@ -418,10 +418,17 @@ def update_build(self, state=None):
# BuildEnvironmentException or BuildEnvironmentWarning
if isinstance(self.failure,
(BuildEnvironmentException, BuildEnvironmentWarning)):
self.build['error'] = str(self.failure)
self.build['error'] = ugettext_noop(
"A failure in building the documentation as occured: {}".format(
str(self.failure)
)
)
else:
self.build['error'] = ugettext_noop(
"An unexpected error occurred")
"A failure in our code has occured. The failure is: {}".format(
str(self.failure)
)
)

# Attempt to stop unicode errors on build reporting
for key, val in list(self.build.items()):
Expand All @@ -433,7 +440,7 @@ def update_build(self, state=None):
except HttpClientError as e:
log.error("Unable to post a new build: %s", e.content)
except Exception:
log.error("Unknown build exception", exc_info=True)
log.exception("Unknown build exception")


class LocalEnvironment(BuildEnvironment):
Expand Down
2 changes: 1 addition & 1 deletion readthedocs/oauth/services/github.py
Original file line number Diff line number Diff line change
Expand Up @@ -289,5 +289,5 @@ def get_token_for_project(cls, project, force_local=False):
if tokens.exists():
token = tokens[0].token
except Exception:
log.error('Failed to get token for project', exc_info=True)
log.exception('Failed to get token for project')
return token
3 changes: 1 addition & 2 deletions readthedocs/oauth/tasks.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@

from __future__ import absolute_import
from django.contrib.auth.models import User
from djcelery import celery as celery_app

from readthedocs.core.utils.tasks import PublicTask
from readthedocs.core.utils.tasks import permission_check
Expand All @@ -22,4 +21,4 @@ def run_public(self, user_id):
service.sync()


sync_remote_repositories = celery_app.tasks[SyncRemoteRepositories.name]
sync_remote_repositories = SyncRemoteRepositories()
1 change: 1 addition & 0 deletions readthedocs/projects/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
default_app_config = 'readthedocs.projects.apps.ProjectsConfig'
12 changes: 12 additions & 0 deletions readthedocs/projects/apps.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
"""Project app config"""

from django.apps import AppConfig


class ProjectsConfig(AppConfig):
name = 'readthedocs.projects'

def ready(self):
from readthedocs.projects.tasks import UpdateDocsTask
from readthedocs.worker import app
app.tasks.register(UpdateDocsTask)
10 changes: 5 additions & 5 deletions readthedocs/projects/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -315,29 +315,29 @@ def save(self, *args, **kwargs): # pylint: disable=arguments-differ
latest.identifier = self.default_branch
latest.save()
except Exception:
log.error('Failed to update latest identifier', exc_info=True)
log.exception('Failed to update latest identifier')

# Add exceptions here for safety
try:
self.sync_supported_versions()
except Exception:
log.error('failed to sync supported versions', exc_info=True)
log.exception('failed to sync supported versions')
try:
if not first_save:
broadcast(type='app', task=tasks.symlink_project, args=[self.pk])
except Exception:
log.error('failed to symlink project', exc_info=True)
log.exception('failed to symlink project')
try:
if not first_save:
broadcast(type='app', task=tasks.update_static_metadata, args=[self.pk])
except Exception:
log.error('failed to update static metadata', exc_info=True)
log.exception('failed to update static metadata')
try:
branch = self.default_branch or self.vcs_repo().fallback_branch
if not self.versions.filter(slug=LATEST).exists():
self.versions.create_latest(identifier=branch)
except Exception:
log.error('Error creating default branches', exc_info=True)
log.exception('Error creating default branches')

def get_absolute_url(self):
return reverse('projects_detail', args=[self.slug])
Expand Down
Loading