Logger for geocoder metrics

This commit is contained in:
Mario de Frutos 2016-03-07 17:54:10 +01:00
parent 0b4d1bb17f
commit ac1627b5c5
3 changed files with 121 additions and 5 deletions

View File

@ -146,6 +146,18 @@ class InternalGeocoderConfig(ServiceConfig):
def service_type(self): def service_type(self):
return 'geocoder_internal' return 'geocoder_internal'
@property
def is_high_resolution(self):
return False
@property
def cost_per_hit(self):
return 0
@property
def geocoding_quota(self):
return None
class GeocoderConfig(ServiceConfig): class GeocoderConfig(ServiceConfig):
@ -168,6 +180,7 @@ class GeocoderConfig(ServiceConfig):
USERNAME_KEY = 'username' USERNAME_KEY = 'username'
ORGNAME_KEY = 'orgname' ORGNAME_KEY = 'orgname'
PERIOD_END_DATE = 'period_end_date' PERIOD_END_DATE = 'period_end_date'
LOG_PATH = '/var/log/postgresql/geocodings.log'
def __init__(self, redis_connection, username, orgname=None, def __init__(self, redis_connection, username, orgname=None,
heremaps_app_id=None, heremaps_app_code=None): heremaps_app_id=None, heremaps_app_code=None):
@ -256,7 +269,10 @@ class GeocoderConfig(ServiceConfig):
@property @property
def geocoding_quota(self): def geocoding_quota(self):
return self._geocoding_quota if self.heremaps_geocoder:
return self._geocoding_quota
else:
return None
@property @property
def soft_geocoding_limit(self): def soft_geocoding_limit(self):
@ -273,3 +289,18 @@ class GeocoderConfig(ServiceConfig):
@property @property
def heremaps_app_code(self): def heremaps_app_code(self):
return self._heremaps_app_code return self._heremaps_app_code
@property
def is_high_resolution(self):
return True
@property
def cost_per_hit(self):
if self.heremaps_geocoder:
return 1
else:
return 0
@property
def log_path(self):
return self.LOG_PATH

View File

@ -0,0 +1,74 @@
import abc
import json
import re
class LoggerFactory:
@classmethod
def build(self, service_config):
if re.match('geocoder_*', service_config.service_type):
return GeocoderLogger(service_config)
else:
return None
class Logger(object):
__metaclass__ = abc.ABCMeta
def __init__(self, file_path):
self._file_path = file_path
def dump_to_file(self, data):
with open(self._file_path, 'a') as logfile:
json.dump(data, logfile)
@abc.abstractproperty
def log(self, **data):
raise NotImplementedError('log method must be defined')
class GeocoderLogger(Logger):
def __init__(self, service_config):
super(GeocoderLogger, self).__init__(service_config.log_path)
self._service_config = service_config
def log(self, **data):
dump_data = self._dump_data(**data)
self.dump_to_file(dump_data)
def _dump_data(self, **data):
if data['success']:
cost = self._service_config.cost_per_hit
failed_rows = 0
successful_rows = 1
else:
cost = 0
failed_rows = 1
successful_rows = 0
if self._service_config.is_high_resolution:
kind = 'high-resolution'
else:
kind = 'internal'
return {
"batched": False,
"cache_hits": 0, # Always 0 because no cache involved
# https://github.com/CartoDB/cartodb/blob/master/app/models/geocoding.rb#L208-L211
"cost": cost,
"created_at": datetime.now().isoformat(),
"failed_rows": failed_rows,
"geocoder_type": self._service_config.service_type,
"kind": kind,
"processable_rows": 1,
"processed_rows": successful_rows,
"real_rows": successful_rows,
"success": data['success'],
"successful_rows": successful_rows,
"used_credits": 0,
"username": self._service_config.username,
"organization": self._service_config.organization
}

View File

@ -1,19 +1,20 @@
from user import UserMetricsService from user import UserMetricsService
from log import LoggerFactory
from datetime import date from datetime import date
import re import re
class QuotaService: class QuotaService:
""" Class to manage all the quota operation for """ Class to manage all the quota operation for
the Geocoder SQL API Extension """ the Dataservices SQL API Extension """
def __init__(self, user_service_config, redis_connection): def __init__(self, user_service_config, redis_connection):
self._user_service_config = user_service_config self._user_service_config = user_service_config
# TODO First step to extract to a factory if needed in the future
self._quota_checker = QuotaChecker(user_service_config, self._quota_checker = QuotaChecker(user_service_config,
redis_connection) redis_connection)
self._user_service = UserMetricsService( self._user_service = UserMetricsService(self._user_service_config,
self._user_service_config, redis_connection) redis_connection)
self._logger = LoggerFactory.build(user_service_config)
def check_user_quota(self): def check_user_quota(self):
return self._quota_checker.check() return self._quota_checker.check()
@ -22,16 +23,19 @@ class QuotaService:
self._user_service.increment_service_use( self._user_service.increment_service_use(
self._user_service_config.service_type, "success_responses", self._user_service_config.service_type, "success_responses",
amount=amount) amount=amount)
self._log_service_process("success")
def increment_empty_service_use(self, amount=1): def increment_empty_service_use(self, amount=1):
self._user_service.increment_service_use( self._user_service.increment_service_use(
self._user_service_config.service_type, "empty_responses", self._user_service_config.service_type, "empty_responses",
amount=amount) amount=amount)
self._log_service_process("empty")
def increment_failed_service_use(self, amount=1): def increment_failed_service_use(self, amount=1):
self._user_service.increment_service_use( self._user_service.increment_service_use(
self._user_service_config.service_type, "fail_responses", self._user_service_config.service_type, "fail_responses",
amount=amount) amount=amount)
self._log_service_process("fail")
def increment_total_service_use(self, amount=1): def increment_total_service_use(self, amount=1):
self._user_service.increment_service_use( self._user_service.increment_service_use(
@ -43,6 +47,13 @@ class QuotaService:
self._user_service_config.service_type, "isolines_generated", self._user_service_config.service_type, "isolines_generated",
amount=amount) amount=amount)
def _log_service_process(self, event):
if self._logger:
if event is 'success' or event is 'empty':
self._logger.log(success=True)
elif event is 'empty':
self._logger.log(success=False)
class QuotaChecker: class QuotaChecker: