Skip to content
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

Distributed error reporting: Task to ping telemetry with all the tracked errors #12357

Merged
Show file tree
Hide file tree
Changes from 3 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
5 changes: 5 additions & 0 deletions kolibri/core/analytics/tasks.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@

from kolibri.core.analytics.utils import DEFAULT_SERVER_URL
from kolibri.core.analytics.utils import ping_once
from kolibri.core.errorreports.tasks import ping_error_reports
from kolibri.core.tasks.decorators import register_task
from kolibri.core.tasks.exceptions import JobRunning
from kolibri.core.tasks.main import job_storage
Expand All @@ -25,6 +26,10 @@
def _ping(started, server, checkrate):
try:
ping_once(started, server=server)
try:
ping_error_reports.enqueue()
thesujai marked this conversation as resolved.
Show resolved Hide resolved
except JobRunning:
pass
thesujai marked this conversation as resolved.
Show resolved Hide resolved
except ConnectionError:
logger.warning(
"Ping failed (could not connect). Trying again in {} minutes.".format(
Expand Down
65 changes: 65 additions & 0 deletions kolibri/core/errorreports/tasks.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
import logging

from django.db import connection
from django.http import JsonResponse

from .models import ErrorReports
from kolibri.core.discovery.utils.network.client import NetworkClient
from kolibri.core.discovery.utils.network.errors import NetworkLocationConnectionFailure
from kolibri.core.discovery.utils.network.errors import NetworkLocationResponseFailure
from kolibri.core.discovery.utils.network.errors import NetworkLocationResponseTimeout
from kolibri.core.tasks.decorators import register_task

logger = logging.getLogger(__name__)

DEFAULT_SERVER_URL = "https://telemetry.learningequality.org"
thesujai marked this conversation as resolved.
Show resolved Hide resolved

DEFAULT_PING_JOB_ID = "10" # Unsure about this value
thesujai marked this conversation as resolved.
Show resolved Hide resolved

client = NetworkClient(DEFAULT_SERVER_URL)
thesujai marked this conversation as resolved.
Show resolved Hide resolved


def serialize_error_reports_to_json_response(errors):
errors_list = []
for error in errors:
errors_list.append(
{
"error_from": error.error_from,
"error_message": error.error_message,
"traceback": error.traceback,
"first_occurred": error.first_occurred,
"last_occurred": error.last_occurred,
"sent": error.sent,
thesujai marked this conversation as resolved.
Show resolved Hide resolved
"no_of_errors": error.no_of_errors,
}
)
return JsonResponse(errors_list, safe=False)
thesujai marked this conversation as resolved.
Show resolved Hide resolved


def markErrorsAsSent(errors):
thesujai marked this conversation as resolved.
Show resolved Hide resolved
for error in errors:
error.mark_as_sent()
thesujai marked this conversation as resolved.
Show resolved Hide resolved


@register_task(job_id=DEFAULT_PING_JOB_ID)
def ping_error_reports():
thesujai marked this conversation as resolved.
Show resolved Hide resolved
try:
errors = ErrorReports.get_unsent_errors()
errors_json = serialize_error_reports_to_json_response(errors)
client.post(
"/api/errorreports/",
data=errors_json.content,
headers={"Content-Type": "application/json"},
)
markErrorsAsSent(errors)
except NetworkLocationConnectionFailure:
logger.warning("Reporting Error failed (could not connect).")
raise
except NetworkLocationResponseTimeout:
logger.warning("Reporting Error failed (connection timed out).")
raise
except NetworkLocationResponseFailure as e:
logger.warning("Reporting Error failed ({})!".format(e))
raise
finally:
connection.close()
63 changes: 63 additions & 0 deletions kolibri/core/errorreports/test/test_tasks.py
thesujai marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
from unittest.mock import patch

from django.test import TestCase

from kolibri.core.discovery.utils.network.errors import NetworkLocationConnectionFailure
from kolibri.core.discovery.utils.network.errors import NetworkLocationResponseFailure
from kolibri.core.discovery.utils.network.errors import NetworkLocationResponseTimeout


@patch("kolibri.core.errorreports.tasks.client.post")
@patch("kolibri.core.errorreports.tasks.ErrorReports.get_unsent_errors")
@patch("kolibri.core.errorreports.tasks.markErrorsAsSent")
class PingErrorReportsTestCase(TestCase):
def test_ping_error_reports(self, markErrorsAsSent, get_unsent_errors, post):
from kolibri.core.errorreports.tasks import ping_error_reports

ping_error_reports()

self.assertTrue(get_unsent_errors.called)
self.assertTrue(post.called)
self.assertTrue(markErrorsAsSent.called)

def test_ping_error_reports_connection_error(
self, markErrorsAsSent, get_unsent_errors, post
):
from kolibri.core.errorreports.tasks import ping_error_reports

get_unsent_errors.side_effect = NetworkLocationConnectionFailure

with self.assertRaises(NetworkLocationConnectionFailure):
ping_error_reports()

self.assertTrue(get_unsent_errors.called)
self.assertFalse(post.called)
self.assertFalse(markErrorsAsSent.called)

def test_ping_error_reports_timeout(
self, markErrorsAsSent, get_unsent_errors, post
):
from kolibri.core.errorreports.tasks import ping_error_reports

get_unsent_errors.side_effect = NetworkLocationResponseTimeout

with self.assertRaises(NetworkLocationResponseTimeout):
ping_error_reports()

self.assertTrue(get_unsent_errors.called)
self.assertFalse(post.called)
self.assertFalse(markErrorsAsSent.called)

def test_ping_error_reports_response_failure(
self, markErrorsAsSent, get_unsent_errors, post
):
from kolibri.core.errorreports.tasks import ping_error_reports

get_unsent_errors.side_effect = NetworkLocationResponseFailure

with self.assertRaises(NetworkLocationResponseFailure):
ping_error_reports()

self.assertTrue(get_unsent_errors.called)
self.assertFalse(post.called)
self.assertFalse(markErrorsAsSent.called)