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)
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], )
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], )