"""Logic for cloudwatch logs.""" from copy import deepcopy import json from time import time import boto3 from datadog import api from datadog import initialize from oto import response from cloudwatch_logs import config from cloudwatch_logs.api import flask_cache def get_next_token(log_group, log_stream): """Get next token for to append to the log stream. Args: log_group (str): cloudwatch log group name. log_stream (str): name for cloudwatch stream, which is under log_group. Returns: str: Next token string. """ client = boto3.client('logs') last_stream = get_from_cache(log_stream) if not last_stream: # make aws call to get last logs to get the next sequence token. response_iterator = client.describe_log_streams( logGroupName=log_group, logStreamNamePrefix=log_stream, orderBy='LogStreamName', descending=True, limit=10 ) last_stream = response_iterator.get('logStreams')[0] \ .get('uploadSequenceToken') return last_stream def write_cloudwatch_log(data, sequence_token, log_group, log_stream): """Get next token for to append to the log stream. Args: data (dict): . sequence_token (str): . log_group (str): . log_stream (str): . Returns: Response: result. """ if data.get('To') and isinstance(data['To'][0], str): data['To'][0] = _mask_phone_number(data['To'][0]) # From number - will be our twilio number so should be ok without masking. try: params = { 'logGroupName': log_group, 'logStreamName': log_stream, 'logEvents': [{ 'timestamp': int(time() * 1000), 'message': json.dumps(data) }] } if sequence_token: params['sequenceToken'] = sequence_token client = boto3.client('logs') put_response = client.put_log_events(**params) set_to_cache(log_stream, put_response.get('nextSequenceToken')) return response.Response(put_response) except Exception as error_info: set_to_cache(log_stream, None) return response.create_fatal_response(str(error_info)) def write_datadog_log(data): """Get next token for to append to the log stream. Args: data (dict): raw message from twilio. Returns: Response: result. """ if data.get('To') and isinstance(data['To'][0], str): data['To'][0] = _mask_phone_number(data['To'][0]) # From number - will be our twilio number so should be ok without masking. try: initialize( api_key=config.DATADOG_API_KEY, app_key=config.DATADOG_APP_KEY) sms_status = data.get('SmsStatus')[0] or 'error' # this is to show a line graph for twilio working. tags = deepcopy(config.TWILIO_DATADOG_METRIC_TAGS) tags.append('status:' + sms_status) result = api.Metric.send( metric=config.TWILIO_DATADOG_METRIC, points=1, host=config.TWILIO_DATADOG_HOST, tags=tags, ) if sms_status not in config.TWILIO_SUCCESS_STATUS: title = config.TWILIO_EVENT_TITLE.format( to=data['To'][0], sender=data['From'][0], status=sms_status) api.Event.create( title=title, text=json.dumps(data), tags=tags, host=config.TWILIO_DATADOG_HOST, alert_type='error', source_type_name='API' ) result['error_event'] = data return response.Response(message=result) except Exception as error_info: return response.Response({'error': str(error_info)}) def _mask_phone_number(phone_number): """Get next token for to append to the log stream. Args: phone_number (str): Phone number with country code. Returns: str: Masked phone_number for privacy reasons. """ return phone_number[:-4] + 'XXXX' def get_from_cache(key): """Get cached value for key. Args: key (str): Cache key. Returns: str: Cached value. """ return flask_cache.get(key) def set_to_cache(key, value): """Set cache key with value. Args: key (str): Cache key. value (str): Value to be cached. """ flask_cache.set(key, value)