diff --git a/common/djangoapps/enrollment/views.py b/common/djangoapps/enrollment/views.py index 796e183b94..05da454dd2 100644 --- a/common/djangoapps/enrollment/views.py +++ b/common/djangoapps/enrollment/views.py @@ -9,7 +9,7 @@ from django.core.exceptions import ObjectDoesNotExist from django.utils.decorators import method_decorator from opaque_keys import InvalidKeyError from course_modes.models import CourseMode -from lms.djangoapps.commerce.utils import audit_log +from openedx.core.lib.log_utils import audit_log from openedx.core.djangoapps.user_api.preferences.api import update_email_opt_in from openedx.core.lib.api.permissions import ApiKeyHeaderPermission, ApiKeyHeaderPermissionIsAuthenticated from rest_framework import status diff --git a/lms/djangoapps/commerce/api/v0/views.py b/lms/djangoapps/commerce/api/v0/views.py index e7b2042cc6..d3f6da98a3 100644 --- a/lms/djangoapps/commerce/api/v0/views.py +++ b/lms/djangoapps/commerce/api/v0/views.py @@ -13,7 +13,6 @@ from rest_framework.views import APIView from commerce.constants import Messages from commerce.exceptions import InvalidResponseError from commerce.http import DetailResponse, InternalRequestErrorResponse -from commerce.utils import audit_log from course_modes.models import CourseMode from courseware import courses from embargo import api as embargo_api @@ -22,6 +21,7 @@ from enrollment.views import EnrollmentCrossDomainSessionAuth from openedx.core.djangoapps.commerce.utils import ecommerce_api_client from openedx.core.djangoapps.user_api.preferences.api import update_email_opt_in from openedx.core.lib.api.authentication import OAuth2AuthenticationAllowInactiveUser +from openedx.core.lib.log_utils import audit_log from student.models import CourseEnrollment from util.json_request import JsonResponse diff --git a/lms/djangoapps/commerce/tests/test_utils.py b/lms/djangoapps/commerce/tests/test_utils.py index f2c405b333..1f751495de 100644 --- a/lms/djangoapps/commerce/tests/test_utils.py +++ b/lms/djangoapps/commerce/tests/test_utils.py @@ -1,11 +1,12 @@ """Tests of commerce utilities.""" from django.test import TestCase +from django.test.client import RequestFactory from django.test.utils import override_settings from mock import patch -from commerce.utils import audit_log, EcommerceService from commerce.models import CommerceConfiguration -from django.test.client import RequestFactory +from commerce.utils import EcommerceService +from openedx.core.lib.log_utils import audit_log from student.tests.factories import UserFactory @@ -19,7 +20,7 @@ def update_commerce_config(enabled=False, checkout_page='/test_basket/'): class AuditLogTests(TestCase): """Tests of the commerce audit logging helper.""" - @patch('commerce.utils.log') + @patch('openedx.core.lib.log_utils.log') def test_log_message(self, mock_log): """Verify that log messages are constructed correctly.""" audit_log('foo', qux='quux', bar='baz') diff --git a/lms/djangoapps/commerce/utils.py b/lms/djangoapps/commerce/utils.py index b36554400e..5bb1808088 100644 --- a/lms/djangoapps/commerce/utils.py +++ b/lms/djangoapps/commerce/utils.py @@ -1,5 +1,4 @@ """Utilities to assist with commerce tasks.""" -import logging from urlparse import urljoin from django.conf import settings @@ -7,37 +6,6 @@ from django.conf import settings from commerce.models import CommerceConfiguration from openedx.core.djangoapps.site_configuration import helpers as configuration_helpers -log = logging.getLogger(__name__) - - -def audit_log(name, **kwargs): - """DRY helper used to emit an INFO-level log message. - - Messages logged with this function are used to construct an audit trail. Log messages - should be emitted immediately after the event they correspond to has occurred and, if - applicable, after the database has been updated. These log messages use a verbose - key-value pair syntax to make it easier to extract fields when parsing the application's - logs. - - This function is variadic, accepting a variable number of keyword arguments. - - Arguments: - name (str): The name of the message to log. For example, 'payment_received'. - - Keyword Arguments: - Indefinite. Keyword arguments are strung together as comma-separated key-value - pairs ordered alphabetically by key in the resulting log message. - - Returns: - None - """ - # Joins sorted keyword argument keys and values with an "=", wraps each value - # in quotes, and separates each pair with a comma and a space. - payload = u', '.join(['{k}="{v}"'.format(k=k, v=v) for k, v in sorted(kwargs.items())]) - message = u'{name}: {payload}'.format(name=name, payload=payload) - - log.info(message) - class EcommerceService(object): """ Helper class for ecommerce service integration. """ diff --git a/lms/djangoapps/verify_student/views.py b/lms/djangoapps/verify_student/views.py index 9c37e8dc83..10f69d65a5 100644 --- a/lms/djangoapps/verify_student/views.py +++ b/lms/djangoapps/verify_student/views.py @@ -30,7 +30,7 @@ from eventtracking import tracker from opaque_keys import InvalidKeyError from opaque_keys.edx.keys import CourseKey, UsageKey -from commerce.utils import audit_log, EcommerceService +from commerce.utils import EcommerceService from course_modes.models import CourseMode from courseware.url_helpers import get_redirect_url from edx_rest_api_client.exceptions import SlumberBaseException @@ -42,6 +42,7 @@ from openedx.core.djangoapps.user_api.accounts.api import update_account_setting from openedx.core.djangoapps.user_api.errors import UserNotFound, AccountValidationError from openedx.core.djangoapps.credit.api import set_credit_requirement_status from openedx.core.djangoapps.site_configuration import helpers as configuration_helpers +from openedx.core.lib.log_utils import audit_log from student.models import CourseEnrollment from shoppingcart.models import Order, CertificateItem from shoppingcart.processors import ( diff --git a/openedx/core/lib/log_utils.py b/openedx/core/lib/log_utils.py new file mode 100644 index 0000000000..4011c80cb8 --- /dev/null +++ b/openedx/core/lib/log_utils.py @@ -0,0 +1,36 @@ +""" +Helper functions for logging. +""" +import logging + +log = logging.getLogger(__name__) + + +def audit_log(name, **kwargs): + """ + DRY helper used to emit an INFO-level log message. + + Messages logged with this function are used to construct an audit trail. Log messages + should be emitted immediately after the event they correspond to has occurred and, if + applicable, after the database has been updated. These log messages use a verbose + key-value pair syntax to make it easier to extract fields when parsing the application's + logs. + + This function is variadic, accepting a variable number of keyword arguments. + + Arguments: + name (str): The name of the message to log. For example, 'payment_received'. + + Keyword Arguments: + Indefinite. Keyword arguments are strung together as comma-separated key-value + pairs ordered alphabetically by key in the resulting log message. + + Returns: + None + """ + # Joins sorted keyword argument keys and values with an "=", wraps each value + # in quotes, and separates each pair with a comma and a space. + payload = u', '.join(['{k}="{v}"'.format(k=k, v=v) for k, v in sorted(kwargs.items())]) + message = u'{name}: {payload}'.format(name=name, payload=payload) + + log.info(message)