138 lines
5.3 KiB
Python
138 lines
5.3 KiB
Python
#!/usr/bin/python
|
|
|
|
import matplotlib
|
|
import matplotlib.pyplot as plt
|
|
from matplotlib import gridspec
|
|
import numpy as np
|
|
import datetime
|
|
import json
|
|
import pandas
|
|
import argparse
|
|
import os
|
|
matplotlib.style.use('ggplot')
|
|
|
|
# sacrificial plot for single legend
|
|
matplotlib.rcParams['figure.figsize'] = 1, 1
|
|
randFig = plt.figure()
|
|
randAx = plt.subplot()
|
|
randAx.plot(0, 0, color='green', label='good', marker='+')
|
|
randAx.plot(0, 0, color='red', label='failed', marker='x')
|
|
randAx.plot(0, 0, color='black', label='sent', linestyle='--')
|
|
randAx.plot(0, 0, color='green', label='50th quantile')
|
|
randAx.plot(0, 0, color='orange', label='90th quantile')
|
|
randAx.plot(0, 0, color='red', label='99th quantile')
|
|
handles, labels = randAx.get_legend_handles_labels()
|
|
|
|
# big ol' plotting method
|
|
def plot_section(all_data, title, outputPath):
|
|
# group calls by the endpoint/method
|
|
actions = all_data.groupby('action')
|
|
h = len(actions.groups.keys())
|
|
matplotlib.rcParams['figure.figsize'] = 20, 3 * h
|
|
|
|
fig = plt.figure()
|
|
fig.legend(handles, labels, ncol=6, fontsize=16, framealpha=0, loc='upper center')
|
|
if title is not None:
|
|
fig.suptitle(title, fontsize=20, y=0.93)
|
|
gs = gridspec.GridSpec(h, 3)
|
|
|
|
# figure out left and right datetime bounds
|
|
started = all_data['sent'].min()
|
|
stopped = all_data['finished'].max()
|
|
|
|
i = 0
|
|
# plot one row of charts for each endpoint/method combination
|
|
for section in actions.groups.keys():
|
|
# setup the tree charts
|
|
ax = fig.add_subplot(gs[i, 0])
|
|
ax.set_title(section)
|
|
ax.set_xlim(started, stopped)
|
|
ax2 = fig.add_subplot(gs[i, 2])
|
|
ax2.set_xlim(started, stopped)
|
|
ax3 = fig.add_subplot(gs[i, 1])
|
|
ax3.set_xlim(started, stopped)
|
|
|
|
# find the maximum y value and set it across all three charts
|
|
calls = actions.get_group(section)
|
|
tookMax = calls['took'].max()
|
|
ax.set_ylim(0, tookMax+tookMax*0.1)
|
|
ax2.set_ylim(0, tookMax+tookMax*0.1)
|
|
ax3.set_ylim(0, tookMax+tookMax*0.1)
|
|
|
|
groups = calls.groupby('type')
|
|
if groups.groups.get('error', False) is not False:
|
|
bad = groups.get_group('error')
|
|
ax.plot_date(bad['finished'], bad['took'], color='red', marker='x', label='error')
|
|
|
|
bad_rate = bad.set_index('finished')
|
|
bad_rate['rate'] = [0] * len(bad_rate.index)
|
|
bad_rate = bad_rate.resample('5S').count()
|
|
bad_rate['rate'] = bad_rate['rate'].divide(5)
|
|
rateMax = bad_rate['rate'].max()
|
|
ax2.plot_date(bad_rate.index, bad_rate['rate'], linestyle='-', marker='', color='red', label='error')
|
|
if groups.groups.get('good', False) is not False:
|
|
good = groups.get_group('good')
|
|
ax.plot_date(good['finished'], good['took'], color='green', marker='+', label='good')
|
|
|
|
good_rate = good.set_index('finished')
|
|
good_rate['rate'] = [0] * len(good_rate.index)
|
|
good_rate = good_rate.resample('5S').count()
|
|
good_rate['rate'] = good_rate['rate'].divide(5)
|
|
rateMax = good_rate['rate'].max()
|
|
ax2.plot_date(good_rate.index, good_rate['rate'], linestyle='-', marker='', color='green', label='good')
|
|
ax.set_ylabel('Latency (ms)')
|
|
|
|
# calculate the request rate
|
|
sent_rate = pandas.DataFrame(calls['sent'])
|
|
sent_rate = sent_rate.set_index('sent')
|
|
sent_rate['rate'] = [0] * len(sent_rate.index)
|
|
sent_rate = sent_rate.resample('5S').count()
|
|
sent_rate['rate'] = sent_rate['rate'].divide(5)
|
|
if sent_rate['rate'].max() > rateMax:
|
|
rateMax = sent_rate['rate'].max()
|
|
ax2.plot_date(sent_rate.index, sent_rate['rate'], linestyle='--', marker='', color='black', label='sent')
|
|
ax2.set_ylim(0, rateMax+rateMax*0.1)
|
|
ax2.set_ylabel('Rate (per second)')
|
|
|
|
# calculate and plot latency quantiles
|
|
calls = calls.set_index('finished')
|
|
calls = calls.sort_index()
|
|
quan = pandas.DataFrame(calls['took'])
|
|
for q, c in [[.5, 'green'], [.9, 'orange'], [.99, 'red']]:
|
|
quanN = quan.rolling(500, center=True).quantile(q)
|
|
ax3.plot(quanN['took'].index, quanN['took'], color=c)
|
|
|
|
ax3.set_ylabel('Latency quantiles (ms)')
|
|
|
|
i += 1
|
|
|
|
# format x axes
|
|
for ax in fig.axes:
|
|
matplotlib.pyplot.sca(ax)
|
|
plt.xticks(rotation=30, ha='right')
|
|
majorFormatter = matplotlib.dates.DateFormatter('%H:%M:%S')
|
|
ax.xaxis.set_major_formatter(majorFormatter)
|
|
|
|
# save image
|
|
gs.update(wspace=0.275, hspace=0.5)
|
|
fig.savefig(outputPath, bbox_inches='tight')
|
|
|
|
# and the main event
|
|
parser = argparse.ArgumentParser()
|
|
parser.add_argument('chartData', type=str, help='Path to file containing JSON chart output from load-generator')
|
|
parser.add_argument('--output', type=str, help='Path to save output to', default='latency-chart.png')
|
|
parser.add_argument('--title', type=str, help='Chart title')
|
|
args = parser.parse_args()
|
|
|
|
with open(args.chartData) as data_file:
|
|
stuff = []
|
|
for l in data_file.readlines():
|
|
stuff.append(json.loads(l))
|
|
|
|
df = pandas.DataFrame(stuff)
|
|
df['finished'] = pandas.to_datetime(df['finished']).astype(datetime.datetime)
|
|
df['sent'] = pandas.to_datetime(df['sent']).astype(datetime.datetime)
|
|
df['took'] = df['took'].divide(1000000)
|
|
|
|
plot_section(df, args.title, args.output)
|