Skip to content

Commit

Permalink
[#8843] Add time series Apdex score chart
Browse files Browse the repository at this point in the history
  • Loading branch information
donghun-cho authored and emeroad committed May 26, 2022
1 parent 225f6d6 commit 5066c43
Show file tree
Hide file tree
Showing 18 changed files with 1,090 additions and 17 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -16,26 +16,37 @@

package com.navercorp.pinpoint.web.applicationmap.histogram;

import com.navercorp.pinpoint.common.server.util.time.Range;
import com.navercorp.pinpoint.common.trace.ServiceType;
import com.navercorp.pinpoint.web.applicationmap.rawdata.AgentHistogram;
import com.navercorp.pinpoint.web.applicationmap.rawdata.AgentHistogramList;
import com.navercorp.pinpoint.web.util.TimeWindow;
import com.navercorp.pinpoint.web.view.AgentResponseTimeViewModel;
import com.navercorp.pinpoint.web.view.TimeViewModel;
import com.navercorp.pinpoint.web.vo.Application;
import com.navercorp.pinpoint.common.server.util.time.Range;
import com.navercorp.pinpoint.web.vo.stat.SampledApdexScore;
import com.navercorp.pinpoint.web.vo.stat.chart.agent.AgentStatPoint;
import com.navercorp.pinpoint.web.vo.stat.chart.application.DoubleApplicationStatPoint;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.Objects;

/**
* most of the features have been delegated to AgentHistorgramList upon refactoring
* most of the features have been delegated to AgentHistogramList upon refactoring
* TODO: functionality reduced to creating views - need to be renamed or removed
*
* @author emeroad
*/
public class AgentTimeHistogram {

private static final Double DEFAULT_MIN_APDEX_SCORE = 2D;
private static final Double DEFAULT_MAX_APDEX_SCORE = -2D;
private static final String DEFAULT_AGENT_ID = "defaultAgentId";

private static final Comparator<AgentResponseTimeViewModel> AGENT_NAME_COMPARATOR
= Comparator.comparing(AgentResponseTimeViewModel::getAgentName);

Expand Down Expand Up @@ -83,4 +94,96 @@ private AgentResponseTimeViewModel createAgentResponseTimeViewModel(Application
private List<TimeViewModel> createResponseTimeViewModel(List<TimeHistogram> timeHistogramList, TimeHistogramFormat timeHistogramFormat) {
return new TimeViewModel.TimeViewModelBuilder(application, timeHistogramList).setTimeHistogramFormat(timeHistogramFormat).build();
}

public List<SampledApdexScore> getSampledAgentApdexScoreList(String agentName) {
AgentHistogram agentHistogram = selectAgentHistogram(agentName);
if (agentHistogram == null) {
return Collections.emptyList();
}

List<SampledApdexScore> result = new ArrayList<>();
for (TimeHistogram timeHistogram : agentHistogram.getTimeHistogram()) {
if (timeHistogram.getTotalCount() != 0) {
AgentStatPoint<Double> agentStatPoint = new AgentStatPoint<>(timeHistogram.getTimeStamp(), ApdexScore.toDoubleFromHistogram(timeHistogram));
result.add(new SampledApdexScore(agentStatPoint));
}
}
return result;
}

private AgentHistogram selectAgentHistogram(String agentName) {
for (AgentHistogram agentHistogram : agentHistogramList.getAgentHistogramList()) {
Application agentId = agentHistogram.getAgentId();
if (agentId.getName().equals(agentName)) {
return agentHistogram;
}
}
return null;
}

public List<DoubleApplicationStatPoint> getApplicationApdexScoreList(TimeWindow window) {
int size = (int) window.getWindowRangeCount();
List<Double> min = fillList(size, DEFAULT_MIN_APDEX_SCORE);
List<String> minAgentId = fillList(size, DEFAULT_AGENT_ID);
List<Double> max = fillList(size, DEFAULT_MAX_APDEX_SCORE);
List<String> maxAgentId = fillList(size, DEFAULT_AGENT_ID);

List<Histogram> sumHistogram = getDefaultHistograms(window, application.getServiceType());

for (AgentHistogram agentHistogram : agentHistogramList.getAgentHistogramList()) {
for (TimeHistogram timeHistogram : agentHistogram.getTimeHistogram()) {
if (timeHistogram.getTotalCount() != 0) {
int index = window.getWindowIndex(timeHistogram.getTimeStamp());
if (index < 0 || index >= size) {
continue;
}
double apdex = ApdexScore.toDoubleFromHistogram(timeHistogram);
String agentId = agentHistogram.getId();

updateMinMaxValue(index, apdex, agentId, min, minAgentId, max, maxAgentId);
sumHistogram.get(index).add(timeHistogram);
}
}
}

return createDoubleApplicationStatPoints(window, min, minAgentId, max, maxAgentId, sumHistogram);
}

private <T> List<T> fillList(int size, T defaultValue) {
return new ArrayList<>(Collections.nCopies(size, defaultValue));
}

private void updateMinMaxValue(int index, double apdex, String agentId,
List<Double> min, List<String> minAgentId, List<Double> max, List<String> maxAgentId) {
if (min.get(index) > apdex) {
min.set(index, apdex);
minAgentId.set(index, agentId);
}
if (max.get(index) < apdex) {
max.set(index, apdex);
maxAgentId.set(index, agentId);
}
}

private List<DoubleApplicationStatPoint> createDoubleApplicationStatPoints(TimeWindow window, List<Double> min, List<String> minAgentId, List<Double> max, List<String> maxAgentId, List<Histogram> sumHistogram) {
List<DoubleApplicationStatPoint> applicationStatPoints = new ArrayList<>();
for (long timestamp : window) {
int index = window.getWindowIndex(timestamp);
Histogram histogram = sumHistogram.get(index);
if (histogram.getTotalCount() != 0) {
double avg = ApdexScore.toDoubleFromHistogram(histogram);
DoubleApplicationStatPoint point = new DoubleApplicationStatPoint(timestamp, min.get(index), minAgentId.get(index), max.get(index), maxAgentId.get(index), avg);
applicationStatPoints.add(point);
}
}
return applicationStatPoints;
}

private List<Histogram> getDefaultHistograms(TimeWindow window, ServiceType serviceType) {
List<Histogram> sum = new ArrayList<>((int) window.getWindowRangeCount());
for (long timestamp : window) {
sum.add(new TimeHistogram(serviceType, timestamp));
}
return sum;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -26,18 +26,15 @@
import com.navercorp.pinpoint.common.server.util.time.Range;
import com.navercorp.pinpoint.web.vo.ResponseTime;

import org.apache.logging.log4j.Logger;
import org.apache.logging.log4j.LogManager;

import java.util.*;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;

/**
* @author emeroad
*/
public class AgentTimeHistogramBuilder {

private final Logger logger = LogManager.getLogger(this.getClass());

private final Application application;
private final Range range;
private final TimeWindow window;
Expand All @@ -48,6 +45,11 @@ public AgentTimeHistogramBuilder(Application application, Range range) {
this.window = new TimeWindow(range, TimeWindowDownSampler.SAMPLER);
}

public AgentTimeHistogramBuilder(Application application, Range range, TimeWindow window) {
this.application = Objects.requireNonNull(application, "application");
this.range = Objects.requireNonNull(range, "range");
this.window = Objects.requireNonNull(window, "window");
}

public AgentTimeHistogram build(List<ResponseTime> responseHistogramList) {
AgentHistogramList agentHistogramList = new AgentHistogramList(application, responseHistogramList);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,23 @@
import java.util.Objects;

/**
* https://en.wikipedia.org/wiki/Apdex
* <a href="https://en.wikipedia.org/wiki/Apdex">https://en.wikipedia.org/wiki/Apdex</a>
*/
public class ApdexScore {

private static final BigDecimal TWO = BigDecimal.valueOf(2);

private final double apdexScore;

public static double toDoubleFromHistogram(Histogram histogram) {
Objects.requireNonNull(histogram, "histogram");
final long satisfiedCount = histogram.getFastCount();
final long toleratingCount = histogram.getNormalCount();
final long totalCount = histogram.getTotalCount();

return calculateApdexScore(satisfiedCount, toleratingCount, totalCount);
}

public static ApdexScore newApdexScore(Histogram histogram) {
Objects.requireNonNull(histogram, "histogram");
final long satisfiedCount = histogram.getFastCount();
Expand All @@ -28,7 +37,7 @@ public ApdexScore(long satisfiedCount, long toleratingCount, long totalSamples)
this.apdexScore = calculateApdexScore(satisfiedCount, toleratingCount, totalSamples);
}

private double calculateApdexScore(long satisfiedCount, long toleratingCount, long totalSamples) {
private static double calculateApdexScore(long satisfiedCount, long toleratingCount, long totalSamples) {
// divide by zero
if (totalSamples == 0) {
return 0;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,177 @@
package com.navercorp.pinpoint.web.controller;

import com.navercorp.pinpoint.common.server.util.time.Range;
import com.navercorp.pinpoint.web.applicationmap.histogram.ApdexScore;
import com.navercorp.pinpoint.web.service.ApdexScoreService;
import com.navercorp.pinpoint.web.service.ApplicationFactory;
import com.navercorp.pinpoint.web.util.TimeWindow;
import com.navercorp.pinpoint.web.util.TimeWindowSampler;
import com.navercorp.pinpoint.web.util.TimeWindowSlotCentricSampler;
import com.navercorp.pinpoint.web.view.InspectorView;
import com.navercorp.pinpoint.web.vo.Application;
import com.navercorp.pinpoint.web.vo.stat.chart.InspectorData;
import com.navercorp.pinpoint.web.vo.stat.chart.StatChart;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import java.util.Objects;

@RestController
public class ApdexScoreController {
private static final TimeWindowSampler APDEX_SCORE_TIME_WINDOW_SAMPLER = new TimeWindowSlotCentricSampler(60 * 1000, 200);

private final ApplicationFactory applicationFactory;
private final ApdexScoreService apdexScoreService;

public ApdexScoreController(ApplicationFactory applicationFactory, ApdexScoreService apdexScoreService) {
this.applicationFactory = Objects.requireNonNull(applicationFactory, "applicationFactory");
this.apdexScoreService = Objects.requireNonNull(apdexScoreService, "apdexScoreService");
}

@GetMapping(value = "/getApdexScore")
public ApdexScore getApdexScore(
@RequestParam("applicationName") String applicationName,
@RequestParam("serviceTypeCode") Short serviceTypeCode,
@RequestParam("from") long from,
@RequestParam("to") long to) {
final Range range = Range.between(from, to);

Application application = applicationFactory.createApplication(applicationName, serviceTypeCode);

return apdexScoreService.selectApdexScoreData(application, range);
}

@GetMapping(value = "/getApdexScore", params = "serviceTypeName")
public ApdexScore getApdexScore(
@RequestParam("applicationName") String applicationName,
@RequestParam("serviceTypeName") String serviceTypeName,
@RequestParam("from") long from,
@RequestParam("to") long to) {
final Range range = Range.between(from, to);

Application application = applicationFactory.createApplicationByTypeName(applicationName, serviceTypeName);

return apdexScoreService.selectApdexScoreData(application, range);
}

@GetMapping(value = "/getApplicationStat/apdexScore/chart")
public StatChart getApplicationApdexScoreChart(
@RequestParam("applicationId") String applicationId,
@RequestParam("serviceTypeCode") Short serviceTypeCode,
@RequestParam("from") long from,
@RequestParam("to") long to) {
final Range range = Range.between(from, to);
TimeWindow timeWindow = new TimeWindow(range, APDEX_SCORE_TIME_WINDOW_SAMPLER);

Application application = applicationFactory.createApplication(applicationId, serviceTypeCode);

return apdexScoreService.selectApplicationChart(application, range, timeWindow);
}

@GetMapping(value = "/getApplicationStat/apdexScore/chart", params = "serviceTypeName")
public StatChart getApplicationApdexScoreChart(
@RequestParam("applicationId") String applicationId,
@RequestParam("serviceTypeName") String serviceTypeName,
@RequestParam("from") long from,
@RequestParam("to") long to) {
final Range range = Range.between(from, to);
TimeWindow timeWindow = new TimeWindow(range, APDEX_SCORE_TIME_WINDOW_SAMPLER);

Application application = applicationFactory.createApplicationByTypeName(applicationId, serviceTypeName);

return apdexScoreService.selectApplicationChart(application, range, timeWindow);
}

@GetMapping(value = "/getAgentStat/apdexScore/chart")
public StatChart getAgentApdexScoreChart(
@RequestParam("applicationId") String applicationId,
@RequestParam("serviceTypeCode") Short serviceTypeCode,
@RequestParam("agentId") String agentId,
@RequestParam("from") long from,
@RequestParam("to") long to) {
final Range range = Range.between(from, to);
TimeWindow timeWindow = new TimeWindow(range, APDEX_SCORE_TIME_WINDOW_SAMPLER);

Application application = applicationFactory.createApplication(applicationId, serviceTypeCode);

return apdexScoreService.selectAgentChart(application, range, timeWindow, agentId);
}

@GetMapping(value = "/getAgentStat/apdexScore/chart", params = "serviceTypeName")
public StatChart getAgentApdexScoreChart(
@RequestParam("applicationId") String applicationId,
@RequestParam("serviceTypeName") String serviceTypeName,
@RequestParam("agentId") String agentId,
@RequestParam("from") long from,
@RequestParam("to") long to) {
final Range range = Range.between(from, to);
TimeWindow timeWindow = new TimeWindow(range, APDEX_SCORE_TIME_WINDOW_SAMPLER);

Application application = applicationFactory.createApplicationByTypeName(applicationId, serviceTypeName);

return apdexScoreService.selectAgentChart(application, range, timeWindow, agentId);
}

@GetMapping(value = "/getApplicationStat/apdexScore/inspectorView")
public InspectorView getApplicationApdexScoreView(
@RequestParam("applicationName") String applicationName,
@RequestParam("serviceTypeCode") Short serviceTypeCode,
@RequestParam("from") long from,
@RequestParam("to") long to) {
final Range range = Range.between(from, to);
TimeWindow timeWindow = new TimeWindow(range, APDEX_SCORE_TIME_WINDOW_SAMPLER);

Application application = applicationFactory.createApplication(applicationName, serviceTypeCode);

InspectorData inspectorData = apdexScoreService.selectApplicationInspectorData(application, range, timeWindow);
return new InspectorView(inspectorData);
}

@GetMapping(value = "/getApplicationStat/apdexScore/inspectorView", params = "serviceTypeName")
public InspectorView getApplicationApdexScoreView(
@RequestParam("applicationName") String applicationName,
@RequestParam("serviceTypeName") String serviceTypeName,
@RequestParam("from") long from,
@RequestParam("to") long to) {
final Range range = Range.between(from, to);
TimeWindow timeWindow = new TimeWindow(range, APDEX_SCORE_TIME_WINDOW_SAMPLER);

Application application = applicationFactory.createApplicationByTypeName(applicationName, serviceTypeName);

InspectorData inspectorData = apdexScoreService.selectApplicationInspectorData(application, range, timeWindow);
return new InspectorView(inspectorData);
}

@GetMapping(value = "/getAgentStat/apdexScore/inspectorView")
public InspectorView getAgentApdexScoreView(
@RequestParam("applicationName") String applicationName,
@RequestParam("serviceTypeCode") Short serviceTypeCode,
@RequestParam("agentId") String agentId,
@RequestParam("from") long from,
@RequestParam("to") long to) {
final Range range = Range.between(from, to);
TimeWindow timeWindow = new TimeWindow(range, APDEX_SCORE_TIME_WINDOW_SAMPLER);

Application application = applicationFactory.createApplication(applicationName, serviceTypeCode);

InspectorData inspectorData = apdexScoreService.selectAgentInspectorData(application, range, timeWindow, agentId);
return new InspectorView(inspectorData);
}

@GetMapping(value = "/getAgentStat/apdexScore/inspectorView", params = "serviceTypeName")
public InspectorView getAgentApdexScoreView(
@RequestParam("applicationName") String applicationName,
@RequestParam("serviceTypeName") String serviceTypeName,
@RequestParam("agentId") String agentId,
@RequestParam("from") long from,
@RequestParam("to") long to) {
final Range range = Range.between(from, to);
TimeWindow timeWindow = new TimeWindow(range, APDEX_SCORE_TIME_WINDOW_SAMPLER);

Application application = applicationFactory.createApplicationByTypeName(applicationName, serviceTypeName);

InspectorData inspectorData = apdexScoreService.selectAgentInspectorData(application, range, timeWindow, agentId);
return new InspectorView(inspectorData);
}
}
Loading

0 comments on commit 5066c43

Please sign in to comment.