Exemple #1
0
 def test_milestone_ranges(self):
     numbers = get_milestone_range("XS", "SM")
     self.assertEqual(numbers[0], 1e1)
     self.assertEqual(numbers[-1], 5e4)
 def test_milestone_ranges(self):
     numbers = get_milestone_range('XS', 'SM')
     self.assertEqual(numbers[0], 1e1)
     self.assertEqual(numbers[-1], 5e4)
Exemple #3
0
class LoggingMixin(object):
    """Log requests to Redis

    This draws inspiration from the code that can be found at: https://github.com/aschn/drf-tracking/blob/master/rest_framework_tracking/mixins.py

    The big distinctions, however, are that this code uses Redis for greater
    speed, and that it logs significantly less information.

    We want to know:
     - How many queries in last X days, total?
     - How many queries ever, total?
     - How many queries total made by user X?
     - How many queries per day made by user X?
    """
    milestones = get_milestone_range('SM', 'XXXL')

    def initial(self, request, *args, **kwargs):
        super(LoggingMixin, self).initial(request, *args, **kwargs)

        d = date.today().isoformat()
        user = request.user
        endpoint = request.resolver_match.url_name

        r = redis.StrictRedis(
            host=settings.REDIS_HOST,
            port=settings.REDIS_PORT,
            db=settings.REDIS_DATABASES['STATS'],
        )
        pipe = r.pipeline()

        # Global and daily tallies for all URLs.
        pipe.incr('api:v3.count')
        pipe.incr('api:v3.d:%s.count' % d)

        # Use a sorted set to store the user stats, with the score representing
        # the number of queries the user made total or on a given day.
        pipe.zincrby('api:v3.user.counts', user.pk)
        pipe.zincrby('api:v3.user.d:%s.counts' % d, user.pk)

        # Use a sorted set to store all the endpoints with score representing
        # the number of queries the endpoint received total or on a given day.
        pipe.zincrby('api:v3.endpoint.counts', endpoint)
        pipe.zincrby('api:v3.endpoint.d:%s.counts' % d, endpoint)

        results = pipe.execute()
        total_count = results[0]
        user_count = results[2]

        if total_count in MILESTONES_FLAT:
            Event.objects.create(
                description="API has logged %s total requests." % total_count)
        if user.is_authenticated():
            if user_count in self.milestones:
                Event.objects.create(
                    description="User '%s' has placed their %s API request." %
                    (user.username, intcomma(ordinal(user_count))),
                    user=user,
                )
            if user_count == 5:
                email = emails['new_api_user']
                send_mail(
                    email['subject'],
                    email['body'] % user.first_name or 'there',
                    email['from'],
                    [user.email],
                )
Exemple #4
0
class LoggingMixin(object):
    """Log requests to Redis

    This draws inspiration from the code that can be found at:
      https://github.com/aschn/drf-tracking/blob/master/rest_framework_tracking/mixins.py

    The big distinctions, however, are that this code uses Redis for greater
    speed, and that it logs significantly less information.

    We want to know:
     - How many queries in last X days, total?
     - How many queries ever, total?
     - How many queries total made by user X?
     - How many queries per day made by user X?
    """

    milestones = get_milestone_range("SM", "XXXL")

    def initial(self, request, *args, **kwargs):
        super(LoggingMixin, self).initial(request, *args, **kwargs)

        # For logging the timing in self.finalize_response
        self.requested_at = now()

    def finalize_response(self, request, response, *args, **kwargs):
        response = super(LoggingMixin,
                         self).finalize_response(request, response, *args,
                                                 **kwargs)

        if not response.exception:
            # Don't log things like 401, 403, etc.,
            # noinspection PyBroadException
            try:
                results = self._log_request(request)
                self._handle_events(results, request.user)
            except Exception as e:
                logger.exception("Unable to log API response timing info: %s",
                                 e.message)
        return response

    def _get_response_ms(self):
        """
        Get the duration of the request response cycle in milliseconds.
        In case of negative duration 0 is returned.
        """
        response_timedelta = now() - self.requested_at
        response_ms = int(response_timedelta.total_seconds() * 1000)

        return max(response_ms, 0)

    def _log_request(self, request):
        d = date.today().isoformat()
        user = request.user
        endpoint = resolve(request.path_info).url_name
        response_ms = self._get_response_ms()

        r = make_redis_interface("STATS")
        pipe = r.pipeline()

        # Global and daily tallies for all URLs.
        pipe.incr("api:v3.count")
        pipe.incr("api:v3.d:%s.count" % d)
        pipe.incr("api:v3.timing", response_ms)
        pipe.incr("api:v3.d:%s.timing" % d, response_ms)

        # Use a sorted set to store the user stats, with the score representing
        # the number of queries the user made total or on a given day.
        user_pk = user.pk or "AnonymousUser"
        pipe.zincrby("api:v3.user.counts", 1, user_pk)
        pipe.zincrby("api:v3.user.d:%s.counts" % d, 1, user_pk)

        # Use a sorted set to store all the endpoints with score representing
        # the number of queries the endpoint received total or on a given day.
        pipe.zincrby("api:v3.endpoint.counts", 1, endpoint)
        pipe.zincrby("api:v3.endpoint.d:%s.counts" % d, 1, endpoint)

        # We create a per-day key in redis for timings. Inside the key we have
        # members for every endpoint, with score of the total time. So to get
        # the average for an endpoint you need to get the number of requests
        # and the total time for the endpoint and divide.
        timing_key = "api:v3.endpoint.d:%s.timings" % d
        pipe.zincrby(timing_key, response_ms, endpoint)

        results = pipe.execute()
        return results

    def _handle_events(self, results, user):
        total_count = results[0]
        user_count = results[4]

        if total_count in MILESTONES_FLAT:
            Event.objects.create(
                description="API has logged %s total requests." % total_count)
        if user.is_authenticated:
            if user_count in self.milestones:
                Event.objects.create(
                    description="User '%s' has placed their %s API request." %
                    (user.username, intcomma(ordinal(user_count))),
                    user=user,
                )
            if user_count == SEND_API_WELCOME_EMAIL_COUNT:
                email = emails["new_api_user"]
                send_mail(
                    email["subject"],
                    email["body"] % user.first_name or "there",
                    email["from"],
                    [user.email],
                )
Exemple #5
0
 def test_milestone_ranges(self):
     numbers = get_milestone_range('XS', 'SM')
     self.assertEqual(numbers[0], 1e1)
     self.assertEqual(numbers[-1], 5e4)