Source code for webapp.timeseries_graph

"""
Plot anomalies for a metric
"""
import logging
import traceback
import datetime
import time

from flask import request

import settings

from skyline_functions import filesafe_metricname
from functions.metrics.get_metric_id_from_base_name import get_metric_id_from_base_name
from functions.metrics.get_base_name_from_labelled_metrics_name import get_base_name_from_labelled_metrics_name
from functions.graphite.get_metrics_timeseries import get_metrics_timeseries
from functions.victoriametrics.get_victoriametrics_metric import get_victoriametrics_metric
from functions.redis.get_metric_timeseries import get_metric_timeseries
from functions.plots.plot_timeseries import plot_timeseries
from functions.timeseries.determine_data_frequency import determine_data_frequency
from functions.timeseries.downsample import downsample_timeseries

skyline_app = 'webapp'
skyline_app_logger = '%sLog' % skyline_app
logger = logging.getLogger(skyline_app_logger)
skyline_app_logfile = '%s/%s.log' % (settings.LOG_PATH, skyline_app)
logfile = '%s/%s.log' % (settings.LOG_PATH, skyline_app)


[docs]def timeseries_graph(): """ Create a plot of the metric and return the path and filename :param metric: the name of the metric :param from_timestamp: the from timestamp :param until_timestamp: the until timestamp :param data_source: what data_source to use :type metric: str :type from_timestamp: int :type until_timestamp: int :type data_source: str :return: path and file :rtype: str """ function_str = 'timeseries_graph' graph_image = None metric = None try: metric = request.args.get('metric') logger.info('%s :: with metric: %s' % (function_str, str(metric))) except Exception as err: logger.error('%s :: no metric passed - %s' % (function_str, err)) raise try: from_timestamp = request.args.get('from_timestamp') logger.info('%s :: with from_timestamp: %s' % (function_str, str(from_timestamp))) except Exception as err: logger.error('%s :: no from_timestamp passed - %s' % (function_str, err)) raise if ":" in from_timestamp: try: from_timestamp = int(time.mktime(datetime.datetime.strptime(from_timestamp, '%Y-%m-%d %H:%M').timetuple())) except Exception as err: logger.error('%s :: failed to determine from_timestamp from %s - %s' % ( function_str, str(from_timestamp), err)) raise try: until_timestamp = request.args.get('until_timestamp') logger.info('%s :: with until_timestamp: %s' % (function_str, str(until_timestamp))) except Exception as err: logger.error('%s :: no until_timestamp passed - %s' % (function_str, err)) raise if ":" in until_timestamp: try: until_timestamp = int(time.mktime(datetime.datetime.strptime(until_timestamp, '%Y-%m-%d %H:%M').timetuple())) except Exception as err: logger.error('%s :: failed to determine until_timestamp from %s - %s' % ( function_str, str(until_timestamp), err)) raise try: data_source = request.args.get('data_source') logger.info('%s :: with data_source: %s' % (function_str, str(data_source))) except Exception as err: logger.error('%s :: no data_source passed - %s' % (function_str, err)) raise downsample_at = 0 try: downsample_at_str = request.args.get('downsample_at') if downsample_at_str: downsample_at = int(float(downsample_at_str)) logger.info('%s :: with downsample_at: %s' % (function_str, str(downsample_at))) except Exception as err: downsample_at = 0 downsample_by = 'mean' try: downsample_by = request.args.get('downsample_by') logger.info('%s :: with downsample_by: %s' % (function_str, str(downsample_by))) except Exception as err: downsample_by = 'mean' logger.info('%s - metric: %s, from_timestamp: %s, until_timestamp: %s, data_source: %s, downsample_at: %s, downsample_by: %s' % ( function_str, str(metric), str(from_timestamp), str(until_timestamp), str(data_source), str(downsample_at), str(downsample_by))) labelled_metric_name = None labelled_metric_base_name = None use_metric = str(metric) file_metric_name = str(metric) if metric.startswith('labelled_metrics.'): if data_source == 'graphite': logger.error('%s :: data_source passed was graphite, graphite is not a data_source for labelled_metrics' % function_str) return graph_image labelled_metric_name = str(metric) try: metric_name = get_base_name_from_labelled_metrics_name(skyline_app, metric) if metric_name: labelled_metric_base_name = str(metric_name) except Exception as err: logger.error('error :: %s :: get_base_name_from_labelled_metrics_name failed for %s - %s' % ( function_str, metric, err)) raise if '_tenant_id="' in metric: labelled_metric_base_name = str(metric) try: metric_id = get_metric_id_from_base_name(skyline_app, metric) if metric_id: file_metric_name = 'labelled_metrics.%s' % str(metric_id) use_metric = str(file_metric_name) except Exception as err: logger.error('error :: %s :: get_metric_id_from_base_name failed for %s - %s' % ( function_str, metric, err)) raise if not until_timestamp: until_timestamp = int(time.time()) logger.info('%s :: no until_timestamp set, using until_timestamp: %s' % (function_str, str(until_timestamp))) if not from_timestamp: from_timestamp = int(until_timestamp - settings.FULL_DURATION) logger.info('%s :: no from_timestamp set, using from_timestamp: %s' % (function_str, str(from_timestamp))) timeseries = [] if data_source == 'graphite': metrics_functions = {} metrics_functions[metric] = {} metrics_functions[metric]['functions'] = None try: metrics_timeseries = get_metrics_timeseries(skyline_app, metrics_functions, int(from_timestamp), int(until_timestamp), log=False) except Exception as err: logger.error(traceback.format_exc()) logger.error('error :: %s :: get_metrics_timeseries failed - %s' % ( function_str, err)) raise try: timeseries = metrics_timeseries[metric]['timeseries'] # Truncate the first and last timestamp, just in case they are not # filled buckets timeseries = timeseries[1:-1] except Exception as err: logger.error(traceback.format_exc()) logger.error('error :: %s :: failed retrieve timeseries from Graphite for %s - %s' % ( function_str, metric, err)) raise if data_source == 'victoriametrics': try: # get_victoriametrics_metric automatically applies the rate and # step required no downsampling or nonNegativeDerivative is # required. timeseries = get_victoriametrics_metric( skyline_app, labelled_metric_base_name, int(from_timestamp), int(until_timestamp), 'list', 'object') except Exception as err: logger.error('error :: %s :: get_victoriametrics_metric failed - %s' % ( function_str, err)) raise if data_source == 'redis': try: timeseries = get_metric_timeseries(skyline_app, metric, int(from_timestamp), int(until_timestamp)) except Exception as err: logger.error('error :: %s :: get_metric_timeseries failed - %s' % ( function_str, err)) raise if downsample_at and downsample_by: logger.info('%s :: downsampling at %s seconds by %s - timeseries length before downsampling: %s' % ( function_str, str(downsample_at), str(downsample_by), str(len(timeseries)))) try: resolution = determine_data_frequency(skyline_app, timeseries) logger.info('%s :: determined resolution: %s' % ( function_str, str(resolution))) timeseries = downsample_timeseries( skyline_app, timeseries, resolution, downsample_at, downsample_by, 'end') except Exception as err: logger.error('error :: %s :: failed to downsample timeseries - %s' % ( function_str, err)) raise logger.info('%s :: timeseries length after downsampling: %s' % ( function_str, str(len(timeseries)))) sane_metricname = filesafe_metricname(file_metric_name) save_to_file = '%s/timeseries_graph.%s.%s.%s.png' % ( settings.SKYLINE_TMP_DIR, sane_metricname, str(from_timestamp), str(until_timestamp)) start_date = time.strftime('%Y-%m-%d %H:%M:%S %Z', time.localtime(int(from_timestamp))) end_date = time.strftime('%Y-%m-%d %H:%M:%S %Z', time.localtime(int(until_timestamp))) title = '%s\n%s - %s to %s' % (str(use_metric), data_source, start_date, end_date) plot_parameters = { 'title': title, 'line_color': 'blue', 'bg_color': 'black', 'figsize': (8, 4) } try: success, graph_image = plot_timeseries( skyline_app, metric, timeseries, save_to_file, plot_parameters) if success: logger.info('%s :: plotted %s' % (function_str, str(graph_image))) except Exception as err: logger.error('error :: %s :: plot_timeseries failed - %s' % ( function_str, err)) raise return graph_image