Skip to content

Commit

Permalink
Move apmTimeseries to backend
Browse files Browse the repository at this point in the history
  • Loading branch information
sorenlouv committed Nov 23, 2018
1 parent 025e370 commit cc9e757
Show file tree
Hide file tree
Showing 27 changed files with 2,515 additions and 917 deletions.
2 changes: 1 addition & 1 deletion x-pack/plugins/apm/public/services/rest/apm.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import { ServiceAPIResponse } from 'x-pack/plugins/apm/server/lib/services/get_s
import { ServiceListAPIResponse } from 'x-pack/plugins/apm/server/lib/services/get_services';
import { TraceListAPIResponse } from 'x-pack/plugins/apm/server/lib/traces/get_top_traces';
import { TraceAPIResponse } from 'x-pack/plugins/apm/server/lib/traces/get_trace';
import { TimeSeriesAPIResponse } from 'x-pack/plugins/apm/server/lib/transactions/charts/get_timeseries_data';
import { TimeSeriesAPIResponse } from 'x-pack/plugins/apm/server/lib/transactions/charts';
import { ITransactionDistributionAPIResponse } from 'x-pack/plugins/apm/server/lib/transactions/distribution';
import { TransactionListAPIResponse } from 'x-pack/plugins/apm/server/lib/transactions/get_top_transactions';
import { TransactionAPIResponse } from 'x-pack/plugins/apm/server/lib/transactions/get_transaction';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
import React from 'react';
import { Request, RRRRender } from 'react-redux-request';
import { createSelector } from 'reselect';
import { TimeSeriesAPIResponse } from 'x-pack/plugins/apm/server/lib/transactions/charts/get_timeseries_data';
import { TimeSeriesAPIResponse } from 'x-pack/plugins/apm/server/lib/transactions/charts';
import { loadCharts } from '../../services/rest/apm';
import { IReduxState } from '../rootReducer';
import { getCharts } from '../selectors/chartSelectors';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import { get } from 'lodash';
import React from 'react';
import { Request, RRRRender } from 'react-redux-request';
import { createSelector } from 'reselect';
import { TimeSeriesAPIResponse } from 'x-pack/plugins/apm/server/lib/transactions/charts/get_timeseries_data';
import { TimeSeriesAPIResponse } from 'x-pack/plugins/apm/server/lib/transactions/charts';
import { loadCharts } from '../../services/rest/apm';
import { IReduxState } from '../rootReducer';
import { getCharts } from '../selectors/chartSelectors';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,56 +4,94 @@
* you may not use this file except in compliance with the Elastic License.
*/

import { TimeSeriesAPIResponse } from 'x-pack/plugins/apm/server/lib/transactions/charts/get_timeseries_data';
import { ApmTimeSeriesResponse } from 'x-pack/plugins/apm/server/lib/transactions/charts/get_timeseries_data/transform';
import { getResponseTimeSeries, getTpmSeries } from '../chartSelectors';

describe('chartSelectors', () => {
describe('getResponseTimeSeries', () => {
const chartsData = {
dates: [0, 1000, 2000, 3000, 4000, 5000],
const apmTimeseries = {
responseTimes: {
avg: [100, 200, 150, 250, 100, 50],
p95: [200, 300, 250, 350, 200, 150],
p99: [300, 400, 350, 450, 100, 50]
avg: [
{ x: 0, y: 100 },
{ x: 1000, y: 200 },
{ x: 2000, y: 150 },
{ x: 3000, y: 250 },
{ x: 4000, y: 100 },
{ x: 5000, y: 50 }
],
p95: [
{ x: 0, y: 200 },
{ x: 1000, y: 300 },
{ x: 2000, y: 250 },
{ x: 3000, y: 350 },
{ x: 4000, y: 200 },
{ x: 5000, y: 150 }
],
p99: [
{ x: 0, y: 300 },
{ x: 1000, y: 400 },
{ x: 2000, y: 350 },
{ x: 3000, y: 450 },
{ x: 4000, y: 100 },
{ x: 5000, y: 50 }
]
},
overallAvgDuration: 200
} as TimeSeriesAPIResponse;
} as ApmTimeSeriesResponse;

it('should match snapshot', () => {
expect(getResponseTimeSeries(chartsData)).toMatchSnapshot();
expect(getResponseTimeSeries(apmTimeseries)).toMatchSnapshot();
});

it('should return 3 series', () => {
expect(getResponseTimeSeries(chartsData).length).toBe(3);
expect(getResponseTimeSeries(apmTimeseries).length).toBe(3);
});
});

describe('getTpmSeries', () => {
const chartsData = {
const apmTimeseries = ({
dates: [0, 1000, 2000, 3000, 4000, 5000],
tpmBuckets: [
{
key: 'HTTP 2xx',
avg: 10,
values: [5, 10, 3, 8, 4, 9]
dataPoints: [
{ x: 0, y: 5 },
{ x: 1000, y: 10 },
{ x: 2000, y: 3 },
{ x: 3000, y: 8 },
{ x: 4000, y: 4 },
{ x: 5000, y: 9 }
]
},
{
key: 'HTTP 4xx',
avg: 2,
values: [1, 2, 3, 2, 3, 1]
dataPoints: [
{ x: 0, y: 1 },
{ x: 1000, y: 2 },
{ x: 2000, y: 3 },
{ x: 3000, y: 2 },
{ x: 4000, y: 3 },
{ x: 5000, y: 1 }
]
},
{
key: 'HTTP 5xx',
avg: 1,
values: [0, 1, 2, 1, 0, 2]
dataPoints: [
{ x: 0, y: 0 },
{ x: 1000, y: 1 },
{ x: 2000, y: 2 },
{ x: 3000, y: 1 },
{ x: 4000, y: 0 },
{ x: 5000, y: 2 }
]
}
]
} as TimeSeriesAPIResponse;
} as any) as ApmTimeSeriesResponse;

const transactionType = 'MyTransactionType';

it('should match snapshot', () => {
expect(getTpmSeries(chartsData, transactionType)).toMatchSnapshot();
expect(getTpmSeries(apmTimeseries, transactionType)).toMatchSnapshot();
});
});
});
89 changes: 56 additions & 33 deletions x-pack/plugins/apm/public/store/selectors/chartSelectors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,17 +6,17 @@

import d3 from 'd3';
import { difference, memoize, zipObject } from 'lodash';
import { TimeSeriesAPIResponse } from 'x-pack/plugins/apm/server/lib/transactions/charts/get_timeseries_data';
import mean from 'lodash.mean';
import { rgba } from 'polished';
import { TimeSeriesAPIResponse } from 'x-pack/plugins/apm/server/lib/transactions/charts';
import { AnomalyTimeSeriesResponse } from 'x-pack/plugins/apm/server/lib/transactions/charts/get_anomaly_data/transform';
import { ApmTimeSeriesResponse } from 'x-pack/plugins/apm/server/lib/transactions/charts/get_timeseries_data/transform';
import { StringMap } from 'x-pack/plugins/apm/typings/common';
import { Coordinate } from 'x-pack/plugins/apm/typings/timeseries';
import { colors } from '../../style/variables';
import { asDecimal, asMillis, tpmUnit } from '../../utils/formatters';
import { IUrlParams } from '../urlParams';

interface Coordinate {
x: number;
y?: number | null;
}

export const getEmptySerie = memoize(
(start = Date.now() - 3600000, end = Date.now()) => {
const dates = d3.time
Expand All @@ -38,17 +38,18 @@ export const getEmptySerie = memoize(

export function getCharts(
urlParams: IUrlParams,
charts: TimeSeriesAPIResponse
timeseriesResponse: TimeSeriesAPIResponse
) {
const { start, end, transactionType } = urlParams;
const noHits = charts.totalHits === 0;
const { apmTimeseries, anomalyTimeseries } = timeseriesResponse;
const noHits = apmTimeseries.totalHits === 0;
const tpmSeries = noHits
? getEmptySerie(start, end)
: getTpmSeries(charts, transactionType);
: getTpmSeries(apmTimeseries, transactionType);

const responseTimeSeries = noHits
? getEmptySerie(start, end)
: getResponseTimeSeries(charts);
: getResponseTimeSeries(apmTimeseries, anomalyTimeseries);

return {
noHits,
Expand All @@ -69,48 +70,79 @@ interface TimeSerie {
areaColor?: string;
}

export function getResponseTimeSeries(chartsData: TimeSeriesAPIResponse) {
const { dates, overallAvgDuration, anomalyTimeSeries } = chartsData;
const { avg, p95, p99 } = chartsData.responseTimes;
export function getResponseTimeSeries(
apmTimeseries: ApmTimeSeriesResponse,
anomalyTimeseries?: AnomalyTimeSeriesResponse
) {
const { overallAvgDuration } = apmTimeseries;
const { avg, p95, p99 } = apmTimeseries.responseTimes;

const series: TimeSerie[] = [
{
title: 'Avg.',
data: getChartValues(dates, avg),
data: avg,
legendValue: asMillis(overallAvgDuration),
type: 'line',
color: colors.apmBlue
},
{
title: '95th percentile',
titleShort: '95th',
data: getChartValues(dates, p95),
data: p95,
type: 'line',
color: colors.apmYellow
},
{
title: '99th percentile',
titleShort: '99th',
data: getChartValues(dates, p99),
data: p99,
type: 'line',
color: colors.apmOrange
}
];

if (anomalyTimeSeries) {
if (anomalyTimeseries) {
// insert after Avg. series
series.splice(1, 0, anomalyTimeSeries.anomalyBoundariesSeries);
series.splice(1, 0, anomalyTimeSeries.anomalyScoreSeries);
series.splice(
1,
0,
getAnomalyBoundariesSeries(anomalyTimeseries.anomalyBoundaries),
getAnomalyScoreSeries(anomalyTimeseries.anomalyScore)
);
}

return series;
}

function getAnomalyScoreSeries(data: Coordinate[]) {
return {
title: 'Anomaly score',
hideLegend: true,
hideTooltipValue: true,
data,
type: 'areaMaxHeight',
color: 'none',
areaColor: rgba(colors.apmRed, 0.1)
};
}

function getAnomalyBoundariesSeries(data: Coordinate[]) {
return {
title: 'Anomaly Boundaries',
hideLegend: true,
hideTooltipValue: true,
data,
type: 'area',
color: 'none',
areaColor: rgba(colors.apmBlue, 0.1)
};
}

export function getTpmSeries(
chartsData: TimeSeriesAPIResponse,
apmTimeseries: ApmTimeSeriesResponse,
transactionType?: string
) {
const { dates, tpmBuckets } = chartsData;
const { tpmBuckets } = apmTimeseries;
const bucketKeys = tpmBuckets.map(({ key }) => key);
const getColor = getColorByKey(bucketKeys);
const getTpmLegendTitle = (bucketKey: string) => {
Expand All @@ -123,10 +155,11 @@ export function getTpmSeries(
};

return tpmBuckets.map(bucket => {
const avg = mean(bucket.dataPoints.map(p => p.y));
return {
title: getTpmLegendTitle(bucket.key),
data: getChartValues(dates, bucket.values),
legendValue: `${asDecimal(bucket.avg)} ${tpmUnit(transactionType || '')}`,
data: bucket.dataPoints,
legendValue: `${asDecimal(avg)} ${tpmUnit(transactionType || '')}`,
type: 'line',
color: getColor(bucket.key)
};
Expand All @@ -153,13 +186,3 @@ function getColorByKey(keys: string[]) {

return (key: string) => assignedColors[key] || unassignedColors[key];
}

function getChartValues(
dates: number[] = [],
buckets: Array<number | null> = []
) {
return dates.map((x, i) => ({
x,
y: buckets[i]
}));
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`getAnomalySeries should match snapshot 1`] = `
Object {
"anomalyBoundaries": Array [
Object {
"x": 5000,
"y": 200,
"y0": 20,
},
Object {
"x": 15000,
"y": 100,
"y0": 20,
},
Object {
"x": 25000,
"y": 50,
"y0": 10,
},
Object {
"x": 30000,
"y": 50,
"y0": 10,
},
],
"anomalyScore": Array [
Object {
"x": 25000,
"x0": 15000,
},
Object {
"x": 35000,
"x0": 25000,
},
],
}
`;
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`anomalySeriesTransform should match snapshot 1`] = `
Object {
"anomalyBoundaries": Array [
Object {
"x": 10000,
"y": 200,
"y0": 20,
},
Object {
"x": 15000,
"y": 100,
"y0": 20,
},
Object {
"x": 25000,
"y": 50,
"y0": 10,
},
],
"anomalyScore": Array [
Object {
"x": 25000,
"x0": 15000,
},
Object {
"x": 35000,
"x0": 25000,
},
],
}
`;
Loading

0 comments on commit cc9e757

Please sign in to comment.