api_client.py (5710B)
1 #Embedded file name: C:\BuildAgent\work\20d73f1b24eed06e\eve\release\V24.01\packages\datadog\api\api_client.py 2 import logging 3 import time 4 from datadog.api import _api_version, _max_timeouts, _backoff_period 5 from datadog.api.exceptions import ClientError, ApiError, HttpBackoff, HttpTimeout, HTTPError, ApiNotInitialized 6 from datadog.api.http_client import resolve_http_client 7 from datadog.util.compat import json, is_p3k 8 from eveprefs import boot 9 log = logging.getLogger('datadog.api') 10 11 class APIClient(object): 12 _backoff_period = _backoff_period 13 _max_timeouts = _max_timeouts 14 _backoff_timestamp = None 15 _timeout_counter = 0 16 _api_version = _api_version 17 _http_client = None 18 19 @classmethod 20 def _get_http_client(cls): 21 if not cls._http_client: 22 cls._http_client = resolve_http_client() 23 return cls._http_client 24 25 @classmethod 26 def submit(cls, method, path, body = None, attach_host_name = False, response_formatter = None, error_formatter = None, **params): 27 try: 28 if not cls._should_submit(): 29 _, backoff_time_left = cls._backoff_status() 30 raise HttpBackoff(backoff_time_left) 31 from datadog.api import _api_key, _application_key, _api_host, _mute, _host_name, _proxies, _max_retries, _timeout, _cacert 32 if _api_key is None: 33 raise ApiNotInitialized("API key is not set. Please run 'initialize' method first.") 34 params['api_key'] = _api_key 35 if _application_key: 36 params['application_key'] = _application_key 37 if attach_host_name and body: 38 if 'series' in body: 39 for obj_params in body['series']: 40 if obj_params.get('host', '') == '': 41 obj_params['host'] = _host_name 42 43 elif body.get('host', '') == '': 44 body['host'] = _host_name 45 if 'tags' in params and isinstance(params['tags'], list): 46 params['tags'] = ','.join(params['tags']) 47 headers = {} 48 if isinstance(body, dict): 49 body = json.dumps(body) 50 headers['Content-Type'] = 'application/json' 51 url = '{api_host}/api/{api_version}/{path}'.format(api_host=_api_host, api_version=cls._api_version, path=path.lstrip('/')) 52 start_time = time.time() 53 result = cls._get_http_client().request(method=method, url=url, headers=headers, params=params, data=body, timeout=_timeout, max_retries=_max_retries, proxies=_proxies, verify=_cacert) 54 duration = round((time.time() - start_time) * 1000.0, 4) 55 log.info('%s %s %s (%sms)' % (result.status_code, 56 method, 57 url, 58 duration)) 59 cls._timeout_counter = 0 60 content = result.content 61 if content: 62 try: 63 if is_p3k(): 64 response_obj = json.loads(content.decode('utf-8')) 65 else: 66 response_obj = json.loads(content) 67 except ValueError: 68 raise ValueError('Invalid JSON response: {0}'.format(content)) 69 70 if response_obj and 'errors' in response_obj: 71 raise ApiError(response_obj) 72 else: 73 response_obj = None 74 if response_formatter is None: 75 return response_obj 76 return response_formatter(response_obj) 77 except HTTPError as e: 78 if e.status_code == 408 and boot.region == 'optic': 79 pass 80 else: 81 raise 82 except HttpTimeout: 83 cls._timeout_counter += 1 84 raise 85 except ClientError as e: 86 if _mute: 87 log.error(str(e)) 88 if error_formatter is None: 89 return {'errors': e.args[0]} 90 else: 91 return error_formatter({'errors': e.args[0]}) 92 else: 93 raise 94 except ApiError as e: 95 if _mute: 96 for error in e.args[0]['errors']: 97 log.error(str(error)) 98 99 if error_formatter is None: 100 return e.args[0] 101 else: 102 return error_formatter(e.args[0]) 103 else: 104 raise 105 106 @classmethod 107 def _should_submit(cls): 108 now = time.time() 109 should_submit = False 110 if not cls._backoff_timestamp and cls._timeout_counter >= cls._max_timeouts: 111 log.info('Max number of datadog timeouts exceeded, backing off for {0} seconds'.format(cls._backoff_period)) 112 cls._backoff_timestamp = now 113 should_submit = False 114 elif cls._backoff_timestamp: 115 backed_off_time, backoff_time_left = cls._backoff_status() 116 if backoff_time_left < 0: 117 log.info('Exiting backoff state after {0} seconds, will try to submit metrics again'.format(backed_off_time)) 118 cls._backoff_timestamp = None 119 cls._timeout_counter = 0 120 should_submit = True 121 else: 122 log.info("In backoff state, won't submit metrics for another {0} seconds".format(backoff_time_left)) 123 should_submit = False 124 else: 125 should_submit = True 126 return should_submit 127 128 @classmethod 129 def _backoff_status(cls): 130 now = time.time() 131 backed_off_time = now - cls._backoff_timestamp 132 backoff_time_left = cls._backoff_period - backed_off_time 133 return (round(backed_off_time, 2), round(backoff_time_left, 2))