diff --git a/web/src/main/java/com/navercorp/pinpoint/web/applicationmap/ApplicationMapWithScatterDataV3.java b/web/src/main/java/com/navercorp/pinpoint/web/applicationmap/ApplicationMapWithScatterDataV3.java new file mode 100644 index 000000000000..39f40b624151 --- /dev/null +++ b/web/src/main/java/com/navercorp/pinpoint/web/applicationmap/ApplicationMapWithScatterDataV3.java @@ -0,0 +1,98 @@ +/* + * Copyright 2023 NAVER Corp. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.navercorp.pinpoint.web.applicationmap; + +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.annotation.JsonValue; +import com.navercorp.pinpoint.web.applicationmap.histogram.NodeHistogram; +import com.navercorp.pinpoint.web.applicationmap.link.Link; +import com.navercorp.pinpoint.web.applicationmap.nodes.Node; +import com.navercorp.pinpoint.web.applicationmap.nodes.NodeAgentHistogramList; +import com.navercorp.pinpoint.web.scatter.ScatterData; +import com.navercorp.pinpoint.web.view.histogram.ApplicationHistogramView; +import com.navercorp.pinpoint.web.view.histogram.HistogramView; +import com.navercorp.pinpoint.web.vo.Application; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; +import java.util.Map; + +public class ApplicationMapWithScatterDataV3 implements ApplicationMap { + + private final ApplicationMap applicationMap; + private final Map applicationScatterDataMap; + + public ApplicationMapWithScatterDataV3(ApplicationMap applicationMap, Map applicationScatterDataMap) { + this.applicationMap = applicationMap; + this.applicationScatterDataMap = applicationScatterDataMap; + } + + @Override + public Collection getNodes() { + return applicationMap.getNodes(); + } + + @Override + public Collection getLinks() { + return applicationMap.getLinks(); + } + + @JsonValue + public ApplicationMap getApplicationMap() { + return applicationMap; + } + + @JsonIgnore + public List getNodeHistogramData() { + final List result = new ArrayList<>(); + for (Node node : applicationMap.getNodes()) { + NodeHistogram nodeHistogram = node.getNodeHistogram(); + HistogramView histogramView = new HistogramView(null, nodeHistogram.getApplicationTimeHistogramList()); + + result.add(new ApplicationHistogramView(node.getNodeName().getName(), histogramView)); + } + return result; + } + + @JsonIgnore + public List getLinkHistogramData() { + final List result = new ArrayList<>(); + for (Link link : applicationMap.getLinks()) { + HistogramView histogramView = new HistogramView(null, link.getLinkApplicationTimeHistogramList()); + + result.add(new ApplicationHistogramView(link.getLinkName().getName(), histogramView)); + } + return result; + } + + @JsonIgnore + public List getAgentHistogramData() { + final List result = new ArrayList<>(); + for (Node node : applicationMap.getNodes()) { + if (node.getServerGroupList().getInstanceCount() > 0) { + result.add(new NodeAgentHistogramList(node.getNodeName().getName(), node.getNodeHistogram(), node.getServerGroupList())); + } + } + return result; + } + + @JsonIgnore + public Map getApplicationScatterDataMap() { + return applicationScatterDataMap; + } + +} diff --git a/web/src/main/java/com/navercorp/pinpoint/web/applicationmap/FilterMapWrap.java b/web/src/main/java/com/navercorp/pinpoint/web/applicationmap/FilterMapWrap.java index a613c41c68bf..5a8cd3d895dc 100644 --- a/web/src/main/java/com/navercorp/pinpoint/web/applicationmap/FilterMapWrap.java +++ b/web/src/main/java/com/navercorp/pinpoint/web/applicationmap/FilterMapWrap.java @@ -43,6 +43,15 @@ public FilterMapWrap(ApplicationMap applicationMap, TimeHistogramFormat timeHist } } + public void setV3Format(boolean v3Format) { + for(Node node : applicationMap.getNodes()) { + node.setV3Format(v3Format); + } + for(Link link : applicationMap.getLinks()) { + link.setV3Format(v3Format); + } + } + public void setLastFetchedTimestamp(Long lastFetchedTimestamp) { this.lastFetchedTimestamp = lastFetchedTimestamp; } diff --git a/web/src/main/java/com/navercorp/pinpoint/web/applicationmap/MapWrap.java b/web/src/main/java/com/navercorp/pinpoint/web/applicationmap/MapWrap.java index dea57cd3308b..13d859d34da4 100644 --- a/web/src/main/java/com/navercorp/pinpoint/web/applicationmap/MapWrap.java +++ b/web/src/main/java/com/navercorp/pinpoint/web/applicationmap/MapWrap.java @@ -41,6 +41,15 @@ public MapWrap(ApplicationMap applicationMap, final TimeHistogramFormat timeHist } } + public void setV3Format(boolean v3Format) { + for(Node node : applicationMap.getNodes()) { + node.setV3Format(v3Format); + } + for(Link link : applicationMap.getLinks()) { + link.setV3Format(v3Format); + } + } + @JsonProperty("applicationMapData") public ApplicationMap getApplicationMap() { return applicationMap; diff --git a/web/src/main/java/com/navercorp/pinpoint/web/applicationmap/histogram/AgentTimeHistogram.java b/web/src/main/java/com/navercorp/pinpoint/web/applicationmap/histogram/AgentTimeHistogram.java index 2554f1a5e6d9..54c6df832d95 100644 --- a/web/src/main/java/com/navercorp/pinpoint/web/applicationmap/histogram/AgentTimeHistogram.java +++ b/web/src/main/java/com/navercorp/pinpoint/web/applicationmap/histogram/AgentTimeHistogram.java @@ -32,7 +32,9 @@ import java.util.Collection; import java.util.Collections; import java.util.Comparator; +import java.util.HashMap; import java.util.List; +import java.util.Map; import java.util.Objects; /** @@ -79,6 +81,14 @@ public List createViewModel(TimeHistogramFormat time return result; } + public Map> getAgentTimeHistogramMap() { + Map> result = new HashMap<>(); + for (AgentHistogram agentHistogram : agentHistogramList.getAgentHistogramList()) { + result.put(agentHistogram.getAgentId().getName(), sortTimeHistogram(agentHistogram.getTimeHistogram())); + } + return result; + } + private List sortTimeHistogram(Collection timeMap) { List timeList = new ArrayList<>(timeMap); timeList.sort(TimeHistogram.TIME_STAMP_ASC_COMPARATOR); diff --git a/web/src/main/java/com/navercorp/pinpoint/web/applicationmap/histogram/ApplicationTimeHistogram.java b/web/src/main/java/com/navercorp/pinpoint/web/applicationmap/histogram/ApplicationTimeHistogram.java index 9da2bc8f6fa1..ad9d2d285164 100644 --- a/web/src/main/java/com/navercorp/pinpoint/web/applicationmap/histogram/ApplicationTimeHistogram.java +++ b/web/src/main/java/com/navercorp/pinpoint/web/applicationmap/histogram/ApplicationTimeHistogram.java @@ -16,11 +16,13 @@ package com.navercorp.pinpoint.web.applicationmap.histogram; +import com.navercorp.pinpoint.common.server.util.time.Range; import com.navercorp.pinpoint.web.view.TimeViewModel; import com.navercorp.pinpoint.web.vo.Application; -import com.navercorp.pinpoint.common.server.util.time.Range; -import java.util.*; +import java.util.Collections; +import java.util.List; +import java.util.Objects; /** * @author emeroad @@ -43,4 +45,8 @@ public ApplicationTimeHistogram(Application application, Range range, List createViewModel(TimeHistogramFormat timeHistogramFormat) { return new TimeViewModel.TimeViewModelBuilder(application, histogramList).setTimeHistogramFormat(timeHistogramFormat).build(); } + + public List getHistogramList() { + return histogramList; + } } \ No newline at end of file diff --git a/web/src/main/java/com/navercorp/pinpoint/web/applicationmap/histogram/NodeHistogram.java b/web/src/main/java/com/navercorp/pinpoint/web/applicationmap/histogram/NodeHistogram.java index 419f57a26e11..8ec856d3c1e1 100644 --- a/web/src/main/java/com/navercorp/pinpoint/web/applicationmap/histogram/NodeHistogram.java +++ b/web/src/main/java/com/navercorp/pinpoint/web/applicationmap/histogram/NodeHistogram.java @@ -19,10 +19,16 @@ import com.navercorp.pinpoint.common.server.util.time.Range; import com.navercorp.pinpoint.web.view.AgentResponseTimeViewModelList; import com.navercorp.pinpoint.web.view.TimeViewModel; +import com.navercorp.pinpoint.web.view.histogram.AgentHistogramView; +import com.navercorp.pinpoint.web.view.histogram.HistogramView; +import com.navercorp.pinpoint.web.view.histogram.TimeHistogramChartBuilder; +import com.navercorp.pinpoint.web.view.histogram.TimeHistogramType; +import com.navercorp.pinpoint.web.view.TimeSeries.TimeSeriesView; import com.navercorp.pinpoint.web.vo.Application; import com.navercorp.pinpoint.web.vo.ResponseTime; import com.navercorp.pinpoint.web.vo.ResponseTimeStatics; +import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.List; @@ -35,6 +41,7 @@ * agentHistogram * applicationTimeHistogram * agentTimeHistogram + * * @author emeroad */ public class NodeHistogram { @@ -105,9 +112,9 @@ public Map getAgentResponseStatisticsMap() { return null; } Map map = new HashMap<>(agentHistogramMap.size()); - agentHistogramMap.forEach((agentId, histogram) -> { - map.put(agentId, ResponseTimeStatics.fromHistogram(histogram)); - }); + agentHistogramMap.forEach((agentId, histogram) -> + map.put(agentId, ResponseTimeStatics.fromHistogram(histogram)) + ); return map; } @@ -115,10 +122,30 @@ public List getApplicationTimeHistogram(TimeHistogramFormat timeH return applicationTimeHistogram.createViewModel(timeHistogramFormat); } + public TimeSeriesView getApplicationTimeHistogram(TimeHistogramType timeHistogramType) { + TimeHistogramChartBuilder builder = new TimeHistogramChartBuilder(applicationTimeHistogram.getHistogramList()); + return builder.build(timeHistogramType); + } + + public List getApplicationTimeHistogramList() { + return applicationTimeHistogram.getHistogramList(); + } + public AgentResponseTimeViewModelList getAgentTimeHistogram(TimeHistogramFormat timeHistogramFormat) { return new AgentResponseTimeViewModelList(agentTimeHistogram.createViewModel(timeHistogramFormat)); } + public List getAgentHistogramViewList() { + Map> agentTimeHistogramMap = agentTimeHistogram.getAgentTimeHistogramMap(); + + List agentHistogramViewList = new ArrayList<>(); + for (String agentId : agentHistogramMap.keySet()) { + HistogramView histogramView = new HistogramView(agentHistogramMap.get(agentId), agentTimeHistogramMap.get(agentId)); + agentHistogramViewList.add(new AgentHistogramView(agentId, histogramView)); + } + return agentHistogramViewList; + } + public void setAgentTimeHistogram(AgentTimeHistogram agentTimeHistogram) { this.agentTimeHistogram = agentTimeHistogram; } @@ -165,7 +192,7 @@ private Histogram createApplicationLevelResponseTime(List response } return applicationHistogram; } - + public Range getRange() { return range; } diff --git a/web/src/main/java/com/navercorp/pinpoint/web/applicationmap/link/Link.java b/web/src/main/java/com/navercorp/pinpoint/web/applicationmap/link/Link.java index ef311e1b3bfc..a441fa025919 100644 --- a/web/src/main/java/com/navercorp/pinpoint/web/applicationmap/link/Link.java +++ b/web/src/main/java/com/navercorp/pinpoint/web/applicationmap/link/Link.java @@ -25,6 +25,7 @@ import com.navercorp.pinpoint.web.applicationmap.histogram.ApplicationTimeHistogram; import com.navercorp.pinpoint.web.applicationmap.histogram.ApplicationTimeHistogramBuilder; import com.navercorp.pinpoint.web.applicationmap.histogram.Histogram; +import com.navercorp.pinpoint.web.applicationmap.histogram.TimeHistogram; import com.navercorp.pinpoint.web.applicationmap.histogram.TimeHistogramFormat; import com.navercorp.pinpoint.web.applicationmap.nodes.Node; import com.navercorp.pinpoint.web.applicationmap.rawdata.AgentHistogramList; @@ -33,6 +34,9 @@ import com.navercorp.pinpoint.web.view.AgentResponseTimeViewModelList; import com.navercorp.pinpoint.web.view.LinkSerializer; import com.navercorp.pinpoint.web.view.TimeViewModel; +import com.navercorp.pinpoint.web.view.histogram.TimeHistogramChartBuilder; +import com.navercorp.pinpoint.web.view.histogram.TimeHistogramType; +import com.navercorp.pinpoint.web.view.TimeSeries.TimeSeriesView; import com.navercorp.pinpoint.web.vo.Application; import java.util.Collection; @@ -70,6 +74,7 @@ public class Link { private Histogram linkHistogram; private TimeHistogramFormat timeHistogramFormat = TimeHistogramFormat.V1; + private boolean v3Format = false; public Link(CreateType createType, Node fromNode, Node toNode, Range range) { this(LinkType.DETAILED, createType, fromNode, toNode, range); @@ -128,6 +133,13 @@ public void setTimeHistogramFormat(TimeHistogramFormat timeHistogramFormat) { this.timeHistogramFormat = timeHistogramFormat; } + public boolean isV3Format() { + return v3Format; + } + + public void setV3Format(boolean v3Format) { + this.v3Format = v3Format; + } public LinkCallDataMap getSourceLinkCallDataMap() { return sourceLinkCallDataMap; @@ -213,6 +225,23 @@ public ApplicationTimeHistogram getTargetApplicationTimeSeriesHistogramData() { return builder.build(targetLinkCallDataMap.getLinkDataList()); } + public TimeSeriesView getLinkApplicationTimeSeriesHistogram(TimeHistogramType timeHistogramType) { + TimeHistogramChartBuilder builder = new TimeHistogramChartBuilder(getLinkApplicationTimeHistogram().getHistogramList()); + return builder.build(timeHistogramType); + } + + public List getLinkApplicationTimeHistogramList() { + return getLinkApplicationTimeHistogram().getHistogramList(); + } + + public ApplicationTimeHistogram getLinkApplicationTimeHistogram() { + if (createType == CreateType.Source) { + return getSourceApplicationTimeSeriesHistogramData(); + } else { + return getTargetApplicationTimeSeriesHistogramData(); + } + } + public void addSource(LinkCallDataMap sourceLinkCallDataMap) { this.sourceLinkCallDataMap.addLinkDataMap(sourceLinkCallDataMap); } diff --git a/web/src/main/java/com/navercorp/pinpoint/web/applicationmap/link/LinkHistogramSummary.java b/web/src/main/java/com/navercorp/pinpoint/web/applicationmap/link/LinkHistogramSummary.java index d8e17b1d3bd9..d46b4fe0c2fd 100644 --- a/web/src/main/java/com/navercorp/pinpoint/web/applicationmap/link/LinkHistogramSummary.java +++ b/web/src/main/java/com/navercorp/pinpoint/web/applicationmap/link/LinkHistogramSummary.java @@ -21,6 +21,8 @@ import com.navercorp.pinpoint.web.applicationmap.histogram.TimeHistogramFormat; import com.navercorp.pinpoint.web.view.LinkHistogramSummarySerializer; import com.navercorp.pinpoint.web.view.TimeViewModel; +import com.navercorp.pinpoint.web.view.histogram.TimeHistogramType; +import com.navercorp.pinpoint.web.view.TimeSeries.TimeSeriesView; import java.util.List; import java.util.Objects; @@ -54,6 +56,10 @@ public List getTimeSeriesHistogram() { return link.getLinkApplicationTimeSeriesHistogram(); } + public TimeSeriesView getTimeHistogram(TimeHistogramType timeHistogramType) { + return link.getLinkApplicationTimeSeriesHistogram(timeHistogramType); + } + @Override public String toString() { return "LinkHistogramSummary{" + diff --git a/web/src/main/java/com/navercorp/pinpoint/web/applicationmap/nodes/Node.java b/web/src/main/java/com/navercorp/pinpoint/web/applicationmap/nodes/Node.java index d5a943dde5b3..f501e0f47516 100644 --- a/web/src/main/java/com/navercorp/pinpoint/web/applicationmap/nodes/Node.java +++ b/web/src/main/java/com/navercorp/pinpoint/web/applicationmap/nodes/Node.java @@ -47,6 +47,7 @@ public class Node { private boolean authorized = true; private TimeHistogramFormat timeHistogramFormat = TimeHistogramFormat.V1; + private boolean v3Format = false; public Node(Application application) { this(NodeType.DETAILED, application); @@ -125,6 +126,14 @@ public void setTimeHistogramFormat(TimeHistogramFormat timeHistogramFormat) { this.timeHistogramFormat = timeHistogramFormat; } + public boolean isV3Format() { + return v3Format; + } + + public void setV3Format(boolean v3Format) { + this.v3Format = v3Format; + } + @Override public String toString() { return "Node [" + application + "]"; diff --git a/web/src/main/java/com/navercorp/pinpoint/web/applicationmap/nodes/NodeAgentHistogramList.java b/web/src/main/java/com/navercorp/pinpoint/web/applicationmap/nodes/NodeAgentHistogramList.java new file mode 100644 index 000000000000..165378ca0dfd --- /dev/null +++ b/web/src/main/java/com/navercorp/pinpoint/web/applicationmap/nodes/NodeAgentHistogramList.java @@ -0,0 +1,42 @@ +package com.navercorp.pinpoint.web.applicationmap.nodes; + +import com.fasterxml.jackson.annotation.JsonProperty; +import com.navercorp.pinpoint.web.applicationmap.histogram.NodeHistogram; +import com.navercorp.pinpoint.web.view.histogram.AgentHistogramView; +import com.navercorp.pinpoint.web.vo.Application; + +import java.util.List; +import java.util.Objects; + +public class NodeAgentHistogramList { + private final String nodeName; + private final List agentHistogramList; + private final ServerGroupList serverGroupList; + + public NodeAgentHistogramList(String nodeName, NodeHistogram nodeHistogram, ServerGroupList serverGroupList) { + this.nodeName = Objects.requireNonNull(nodeName, "nodeName"); + Objects.requireNonNull(nodeHistogram, "nodeHistogram"); + this.agentHistogramList = nodeHistogram.getAgentHistogramViewList(); + this.serverGroupList = Objects.requireNonNull(serverGroupList, "serverGroupList"); + } + + public NodeAgentHistogramList(Application application, NodeHistogram nodeHistogram, ServerGroupList serverGroupList) { + this(NodeName.of(application).getName(), nodeHistogram, serverGroupList); + } + + @JsonProperty("key") + public String getNodeName() { + return nodeName; + } + + @JsonProperty("agentHistogramList") + public List getAgentHistogramList() { + return agentHistogramList; + } + + @JsonProperty("serverList") + public ServerGroupList getServerList() { + return serverGroupList; + } + +} diff --git a/web/src/main/java/com/navercorp/pinpoint/web/applicationmap/nodes/NodeHistogramSummary.java b/web/src/main/java/com/navercorp/pinpoint/web/applicationmap/nodes/NodeHistogramSummary.java index 07f488c7844d..32216cacb68e 100644 --- a/web/src/main/java/com/navercorp/pinpoint/web/applicationmap/nodes/NodeHistogramSummary.java +++ b/web/src/main/java/com/navercorp/pinpoint/web/applicationmap/nodes/NodeHistogramSummary.java @@ -17,9 +17,11 @@ package com.navercorp.pinpoint.web.applicationmap.nodes; import com.fasterxml.jackson.databind.annotation.JsonSerialize; -import com.navercorp.pinpoint.web.applicationmap.histogram.TimeHistogramFormat; import com.navercorp.pinpoint.web.applicationmap.histogram.NodeHistogram; +import com.navercorp.pinpoint.web.applicationmap.histogram.TimeHistogramFormat; import com.navercorp.pinpoint.web.view.NodeHistogramSummarySerializer; +import com.navercorp.pinpoint.web.view.histogram.TimeHistogramType; +import com.navercorp.pinpoint.web.view.TimeSeries.TimeSeriesView; import java.util.Objects; @@ -46,6 +48,10 @@ public NodeHistogram getNodeHistogram() { return nodeHistogram; } + public TimeSeriesView getNodeTimeHistogram(TimeHistogramType timeHistogramType) { + return nodeHistogram.getApplicationTimeHistogram(timeHistogramType); + } + public TimeHistogramFormat getTimeHistogramFormat() { return timeHistogramFormat; } diff --git a/web/src/main/java/com/navercorp/pinpoint/web/authorization/controller/ResponseTimeController.java b/web/src/main/java/com/navercorp/pinpoint/web/authorization/controller/ResponseTimeController.java new file mode 100644 index 000000000000..e70353954852 --- /dev/null +++ b/web/src/main/java/com/navercorp/pinpoint/web/authorization/controller/ResponseTimeController.java @@ -0,0 +1,183 @@ +/* + * Copyright 2023 NAVER Corp. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.navercorp.pinpoint.web.authorization.controller; + +import com.navercorp.pinpoint.common.server.util.time.Range; +import com.navercorp.pinpoint.web.applicationmap.link.LinkHistogramSummary; +import com.navercorp.pinpoint.web.applicationmap.nodes.NodeAgentHistogramList; +import com.navercorp.pinpoint.web.applicationmap.nodes.NodeHistogramSummary; +import com.navercorp.pinpoint.web.service.ApplicationFactory; +import com.navercorp.pinpoint.web.service.ResponseTimeHistogramService; +import com.navercorp.pinpoint.web.service.ResponseTimeHistogramServiceOption; +import com.navercorp.pinpoint.web.util.Limiter; +import com.navercorp.pinpoint.web.view.histogram.TimeHistogramType; +import com.navercorp.pinpoint.web.view.TimeSeries.TimeSeriesView; +import com.navercorp.pinpoint.web.vo.Application; +import com.navercorp.pinpoint.web.vo.ApplicationPair; +import com.navercorp.pinpoint.web.vo.ApplicationPairs; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.springframework.util.CollectionUtils; +import org.springframework.util.StringUtils; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; + +import javax.validation.constraints.NotBlank; +import javax.validation.constraints.PositiveOrZero; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Objects; + +@RestController +public class ResponseTimeController { + private final Logger logger = LogManager.getLogger(this.getClass()); + + private final ResponseTimeHistogramService responseTimeHistogramService; + private final Limiter dateLimit; + private final ApplicationFactory applicationFactory; + + public ResponseTimeController(ResponseTimeHistogramService responseTimeHistogramService, + Limiter dateLimit, ApplicationFactory applicationFactory) { + this.responseTimeHistogramService = Objects.requireNonNull(responseTimeHistogramService, "responseTimeHistogramService"); + this.dateLimit = Objects.requireNonNull(dateLimit, "dateLimit"); + this.applicationFactory = Objects.requireNonNull(applicationFactory, "applicationFactory"); + } + + @PostMapping(value = "/getResponseTimeHistogramDataV3") + public NodeAgentHistogramList postResponseTimeHistogramDataV3( + @RequestParam("applicationName") @NotBlank String applicationName, + @RequestParam("serviceTypeCode") Short serviceTypeCode, + @RequestParam("from") @PositiveOrZero long from, + @RequestParam("to") @PositiveOrZero long to, + @RequestBody ApplicationPairs applicationPairs, + @RequestParam(value = "useStatisticsAgentState", defaultValue = "false", required = false) + boolean useStatisticsAgentState) { + final Range range = Range.between(from, to); + dateLimit.limit(range); + Application application = applicationFactory.createApplication(applicationName, serviceTypeCode); + + ResponseTimeHistogramServiceOption option = buildOption(application, range, applicationPairs, useStatisticsAgentState); + NodeHistogramSummary nodeHistogramSummary = responseTimeHistogramService.selectNodeHistogramData(option); + + return new NodeAgentHistogramList(application, nodeHistogramSummary.getNodeHistogram(), nodeHistogramSummary.getServerGroupList()); + } + + @GetMapping(value = "/getWas/{type}/chart") + public TimeSeriesView getWasTimeHistogramChart( + @RequestParam("applicationName") @NotBlank String applicationName, + @RequestParam(value = "serviceTypeCode", required = false) Short serviceTypeCode, + @RequestParam(value = "serviceTypeName", required = false) @NotBlank String serviceTypeName, + @RequestParam("from") @PositiveOrZero long from, + @RequestParam("to") @PositiveOrZero long to, + @PathVariable("type") String type) { + final Range range = Range.between(from, to); + dateLimit.limit(range); + Application application = createApplication(applicationName, serviceTypeCode, serviceTypeName); + if (!application.getServiceType().isWas()) { + throw new IllegalArgumentException("application is not WAS. application:" + application + ", serviceTypeCode:" + serviceTypeCode); + } + TimeHistogramType timeHistogramType = TimeHistogramType.valueOf(type); + + ResponseTimeHistogramServiceOption option = buildOption(application, range, ApplicationPairs.empty(), false); + NodeHistogramSummary nodeHistogramSummary = responseTimeHistogramService.selectNodeHistogramData(option); + + return nodeHistogramSummary.getNodeTimeHistogram(timeHistogramType); + } + + @PostMapping(value = "/getNode/{type}/chart") + public TimeSeriesView postNodeTimeHistogramChart( + @RequestParam("applicationName") @NotBlank String applicationName, + @RequestParam(value = "serviceTypeCode", required = false) Short serviceTypeCode, + @RequestParam(value = "serviceTypeName", required = false) @NotBlank String serviceTypeName, + @RequestParam("from") @PositiveOrZero long from, + @RequestParam("to") @PositiveOrZero long to, + @PathVariable("type") String type, + @RequestBody ApplicationPairs applicationPairs) { + final Range range = Range.between(from, to); + dateLimit.limit(range); + Application application = createApplication(applicationName, serviceTypeCode, serviceTypeName); + TimeHistogramType timeHistogramType = TimeHistogramType.valueOf(type); + + ResponseTimeHistogramServiceOption option = buildOption(application, range, applicationPairs, true); + NodeHistogramSummary nodeHistogramSummary = responseTimeHistogramService.selectNodeHistogramData(option); + + return nodeHistogramSummary.getNodeTimeHistogram(timeHistogramType); + } + + @GetMapping(value = "/getLink/{type}/chart") + public TimeSeriesView getLinkTimeHistogramChart( + @RequestParam("fromApplicationName") @NotBlank String fromApplicationName, + @RequestParam(value = "fromServiceTypeCode", required = false) Short fromServiceTypeCode, + @RequestParam(value = "fromServiceTypeName", required = false) @NotBlank String fromServiceTypeName, + @RequestParam("toApplicationName") String toApplicationName, + @RequestParam(value = "toServiceTypeCode", required = false) Short toServiceTypeCode, + @RequestParam(value = "toServiceTypeName", required = false) @NotBlank String toServiceTypeName, + @RequestParam("from") @PositiveOrZero long from, + @RequestParam("to") @PositiveOrZero long to, + @PathVariable("type") String type) { + final Range range = Range.between(from, to); + dateLimit.limit(range); + + Application fromApplication = createApplication(fromApplicationName, fromServiceTypeCode, fromServiceTypeName); + Application toApplication = createApplication(toApplicationName, toServiceTypeCode, toServiceTypeName); + TimeHistogramType timeHistogramType = TimeHistogramType.valueOf(type); + + LinkHistogramSummary linkHistogramSummary = + responseTimeHistogramService.selectLinkHistogramData(fromApplication, toApplication, range); + + return linkHistogramSummary.getTimeHistogram(timeHistogramType); + } + + public ResponseTimeHistogramServiceOption buildOption(Application application, Range range, + ApplicationPairs applicationPairs, boolean useStatisticsAgentState) { + List fromApplications = mapApplicationPairsToApplications(applicationPairs.getFromApplications()); + List toApplications = mapApplicationPairsToApplications(applicationPairs.getToApplications()); + return new ResponseTimeHistogramServiceOption.Builder(application, range, fromApplications, toApplications) + .setUseStatisticsAgentState(useStatisticsAgentState).build(); + } + + private List mapApplicationPairsToApplications(List applicationPairs) { + if (CollectionUtils.isEmpty(applicationPairs)) { + return Collections.emptyList(); + } + List applications = new ArrayList<>(applicationPairs.size()); + for (ApplicationPair applicationPair : applicationPairs) { + String applicationName = applicationPair.getApplicationName(); + short serviceTypeCode = applicationPair.getServiceTypeCode(); + Application application = applicationFactory.createApplication(applicationName, serviceTypeCode); + applications.add(application); + } + return applications; + } + + private Application createApplication(String applicationName, Short serviceTypeCode, String serviceTypeName) { + if (StringUtils.hasLength(applicationName)) { + if (serviceTypeCode != null) { + return applicationFactory.createApplication(applicationName, serviceTypeCode); + } else if (serviceTypeName != null) { + return applicationFactory.createApplicationByTypeName(applicationName, serviceTypeName); + } + } + logger.error("can not create application. applicationName: {}, serviceTypeCode: {}, serviceTypeName: {}", applicationName, serviceTypeCode, serviceTypeName); + throw new IllegalArgumentException("can not create application. applicationName: " + serviceTypeName); + } +} diff --git a/web/src/main/java/com/navercorp/pinpoint/web/controller/FilteredMapController.java b/web/src/main/java/com/navercorp/pinpoint/web/controller/FilteredMapController.java index aa163ac8c2dd..43e79b8f0ac6 100644 --- a/web/src/main/java/com/navercorp/pinpoint/web/controller/FilteredMapController.java +++ b/web/src/main/java/com/navercorp/pinpoint/web/controller/FilteredMapController.java @@ -161,4 +161,79 @@ private static TimeHistogramFormat getTimeHistogramFormat(boolean useLoadHistogr return TimeHistogramFormat.V1; } } + + @GetMapping(value = "/getFilteredServerMapDataMadeOfDotGroupV3", params = "serviceTypeCode") + public FilterMapWrap getFilteredServerMapDataMadeOfDotGroupV3( + @RequestParam("applicationName") @NotBlank String applicationName, + @RequestParam("serviceTypeCode") short serviceTypeCode, + @RequestParam("from") @PositiveOrZero long from, + @RequestParam("to") @PositiveOrZero long to, + @RequestParam("originTo") long originTo, + @RequestParam("xGroupUnit") int xGroupUnit, + @RequestParam("yGroupUnit") int yGroupUnit, + @RequestParam(value = "filter", required = false) @NullOrNotBlank String filterText, + @RequestParam(value = "hint", required = false) @NullOrNotBlank String filterHint, + @RequestParam(value = "limit", required = false, defaultValue = "10000") @PositiveOrZero int limit, + @RequestParam(value = "v", required = false, defaultValue = "0") int viewVersion, + @RequestParam(value = "useStatisticsAgentState", defaultValue = "false", required = false) boolean useStatisticsAgentState) { + String serviceTypeName = registry.findServiceType(serviceTypeCode).getName(); + return getFilteredServerMapDataMadeOfDotGroupV3(applicationName, + serviceTypeName, + from, + to, + originTo, + xGroupUnit, + yGroupUnit, + filterText, + filterHint, + limit, + viewVersion, + useStatisticsAgentState); + } + + @GetMapping(value = "/getFilteredServerMapDataMadeOfDotGroupV3", params = "serviceTypeName") + public FilterMapWrap getFilteredServerMapDataMadeOfDotGroupV3( + @RequestParam("applicationName") String applicationName, + @RequestParam(value = "serviceTypeName", required = false) String serviceTypeName, + @RequestParam("from") long from, + @RequestParam("to") long to, + @RequestParam("originTo") long originTo, + @RequestParam("xGroupUnit") @Positive int xGroupUnit, + @RequestParam("yGroupUnit") @Positive int yGroupUnit, + @RequestParam(value = "filter", required = false) @NullOrNotBlank String filterText, + @RequestParam(value = "hint", required = false) @NullOrNotBlank String filterHint, + @RequestParam(value = "limit", required = false, defaultValue = "10000") @PositiveOrZero int limitParam, + @RequestParam(value = "v", required = false, defaultValue = "0") int viewVersion, + @RequestParam(value = "useStatisticsAgentState", defaultValue = "false", required = false) boolean useStatisticsAgentState) { + if (xGroupUnit <= 0) { + throw new IllegalArgumentException("xGroupUnit(" + xGroupUnit + ") must be positive number"); + } + if (yGroupUnit <= 0) { + throw new IllegalArgumentException("yGroupUnit(" + yGroupUnit + ") must be positive number"); + } + + final int limit = Math.min(limitParam, LimitUtils.MAX); + final Filter> filter = filterBuilder.build(filterText, filterHint); + final Range range = Range.between(from, to); + final LimitedScanResult> limitedScanResult = filteredMapService.selectTraceIdsFromApplicationTraceIndex(applicationName, range, limit); + + final long lastScanTime = limitedScanResult.getLimitedTime(); + // original range: needed for visual chart data sampling + final Range originalRange = Range.between(from, originTo); + // needed to figure out already scanned ranged + final Range scannerRange = Range.between(lastScanTime, to); + logger.debug("originalRange:{} scannerRange:{} ", originalRange, scannerRange); + final FilteredMapServiceOption option = new FilteredMapServiceOption.Builder(limitedScanResult.getScanData(), originalRange, xGroupUnit, yGroupUnit, filter, viewVersion).setUseStatisticsAgentState(useStatisticsAgentState).build(); + final ApplicationMap map = filteredMapService.selectApplicationMapWithScatterDataV3(option); + + if (logger.isDebugEnabled()) { + logger.debug("getFilteredServerMapData range scan(limit:{}) range:{} lastFetchedTimestamp:{}", limit, range.prettyToString(), DateTimeFormatUtils.format(lastScanTime)); + } + + FilterMapWrap mapWrap; + mapWrap = new FilterMapWrap(map, TimeHistogramFormat.V2); + mapWrap.setV3Format(true); + mapWrap.setLastFetchedTimestamp(lastScanTime); + return mapWrap; + } } diff --git a/web/src/main/java/com/navercorp/pinpoint/web/controller/MapController.java b/web/src/main/java/com/navercorp/pinpoint/web/controller/MapController.java index c097ba4206e4..3de0e3c0ea16 100644 --- a/web/src/main/java/com/navercorp/pinpoint/web/controller/MapController.java +++ b/web/src/main/java/com/navercorp/pinpoint/web/controller/MapController.java @@ -429,4 +429,54 @@ private Application createApplication(@Nullable String name, Short serviceTypeCo } return this.applicationFactory.createApplication(name, serviceTypeCode); } + + @GetMapping(value = "/getServerMapDataV3", params = "serviceTypeCode") + public MapWrap getServerMapDataV3( + @RequestParam("applicationName") @NotBlank String applicationName, + @RequestParam("serviceTypeCode") short serviceTypeCode, + @RequestParam("from") @PositiveOrZero long from, + @RequestParam("to") @PositiveOrZero long to, + @RequestParam(value = "callerRange", defaultValue = DEFAULT_SEARCH_DEPTH) int callerRange, + @RequestParam(value = "calleeRange", defaultValue = DEFAULT_SEARCH_DEPTH) int calleeRange, + @RequestParam(value = "bidirectional", defaultValue = "true", required = false) boolean bidirectional, + @RequestParam(value = "wasOnly", defaultValue = "false", required = false) boolean wasOnly, + @RequestParam(value = "useStatisticsAgentState", defaultValue = "false", required = false) + boolean useStatisticsAgentState) { + final Range range = Range.between(from, to); + this.dateLimit.limit(range); + + SearchOption searchOption = new SearchOption(callerRange, calleeRange, bidirectional, wasOnly); + assertSearchOption(searchOption); + + Application application = applicationFactory.createApplication(applicationName, serviceTypeCode); + + MapWrap mapWrap = selectApplicationMap(application, range, searchOption, NodeType.BASIC, LinkType.BASIC, useStatisticsAgentState, true); + mapWrap.setV3Format(true); + return mapWrap; + } + + @GetMapping(value = "/getServerMapDataV3", params = "serviceTypeName") + public MapWrap getServerMapDataV3( + @RequestParam("applicationName") @NotBlank String applicationName, + @RequestParam("serviceTypeName") @NotBlank String serviceTypeName, + @RequestParam("from") @PositiveOrZero long from, + @RequestParam("to") @PositiveOrZero long to, + @RequestParam(value = "callerRange", defaultValue = DEFAULT_SEARCH_DEPTH) int callerRange, + @RequestParam(value = "calleeRange", defaultValue = DEFAULT_SEARCH_DEPTH) int calleeRange, + @RequestParam(value = "bidirectional", defaultValue = "true", required = false) boolean bidirectional, + @RequestParam(value = "wasOnly", defaultValue = "false", required = false) boolean wasOnly, + @RequestParam(value = "useStatisticsAgentState", defaultValue = "false", required = false) + boolean useStatisticsAgentState + ) { + final Range range = Range.between(from, to); + this.dateLimit.limit(range); + + SearchOption searchOption = new SearchOption(callerRange, calleeRange, bidirectional, wasOnly); + assertSearchOption(searchOption); + + Application application = applicationFactory.createApplicationByTypeName(applicationName, serviceTypeName); + MapWrap mapWrap = selectApplicationMap(application, range, searchOption, NodeType.BASIC, LinkType.BASIC, useStatisticsAgentState, true); + mapWrap.setV3Format(true); + return mapWrap; + } } diff --git a/web/src/main/java/com/navercorp/pinpoint/web/service/FilteredMapService.java b/web/src/main/java/com/navercorp/pinpoint/web/service/FilteredMapService.java index 47d68df9da16..7d45aff513e5 100755 --- a/web/src/main/java/com/navercorp/pinpoint/web/service/FilteredMapService.java +++ b/web/src/main/java/com/navercorp/pinpoint/web/service/FilteredMapService.java @@ -37,4 +37,6 @@ public interface FilteredMapService { ApplicationMap selectApplicationMap(FilteredMapServiceOption option); ApplicationMap selectApplicationMapWithScatterData(FilteredMapServiceOption option); + + ApplicationMap selectApplicationMapWithScatterDataV3(FilteredMapServiceOption option); } diff --git a/web/src/main/java/com/navercorp/pinpoint/web/service/FilteredMapServiceImpl.java b/web/src/main/java/com/navercorp/pinpoint/web/service/FilteredMapServiceImpl.java index 067cd437cedc..980843e9852e 100755 --- a/web/src/main/java/com/navercorp/pinpoint/web/service/FilteredMapServiceImpl.java +++ b/web/src/main/java/com/navercorp/pinpoint/web/service/FilteredMapServiceImpl.java @@ -19,11 +19,13 @@ import com.navercorp.pinpoint.common.hbase.bo.ColumnGetCount; import com.navercorp.pinpoint.common.profiler.util.TransactionId; import com.navercorp.pinpoint.common.server.bo.SpanBo; +import com.navercorp.pinpoint.common.server.util.time.Range; import com.navercorp.pinpoint.loader.service.ServiceTypeRegistryService; import com.navercorp.pinpoint.web.applicationmap.ApplicationMap; import com.navercorp.pinpoint.web.applicationmap.ApplicationMapBuilder; import com.navercorp.pinpoint.web.applicationmap.ApplicationMapBuilderFactory; import com.navercorp.pinpoint.web.applicationmap.ApplicationMapWithScatterData; +import com.navercorp.pinpoint.web.applicationmap.ApplicationMapWithScatterDataV3; import com.navercorp.pinpoint.web.applicationmap.appender.histogram.DefaultNodeHistogramFactory; import com.navercorp.pinpoint.web.applicationmap.appender.histogram.datasource.ResponseHistogramsNodeHistogramDataSource; import com.navercorp.pinpoint.web.applicationmap.appender.histogram.datasource.WasNodeHistogramDataSource; @@ -40,10 +42,8 @@ import com.navercorp.pinpoint.web.service.map.FilteredMapBuilder; import com.navercorp.pinpoint.web.vo.Application; import com.navercorp.pinpoint.web.vo.LimitedScanResult; -import com.navercorp.pinpoint.common.server.util.time.Range; - -import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Service; import org.springframework.util.StopWatch; @@ -160,6 +160,28 @@ public ApplicationMap selectApplicationMapWithScatterData(FilteredMapServiceOpti return applicationMapWithScatterData; } + @Override + public ApplicationMap selectApplicationMapWithScatterDataV3(FilteredMapServiceOption option) { + StopWatch watch = new StopWatch(); + watch.start(); + + final List> filterList = selectFilteredSpan(option.getTransactionIdList(), option.getFilter(), option.getColumnGetCount()); + FilteredMapBuilder filteredMapBuilder = new FilteredMapBuilder(applicationFactory, registry, option.getOriginalRange(), option.getVersion()); + filteredMapBuilder.serverMapDataFilter(serverMapDataFilter); + filteredMapBuilder.addTransactions(filterList); + FilteredMap filteredMap = filteredMapBuilder.build(); + + ApplicationMap map = createMap(option, filteredMap); + + Map applicationScatterData = filteredMap.getApplicationScatterData(option.getOriginalRange().getFrom(), option.getOriginalRange().getTo(), option.getxGroupUnit(), option.getyGroupUnit()); + ApplicationMapWithScatterDataV3 applicationMapWithScatterDataV3 = new ApplicationMapWithScatterDataV3(map, applicationScatterData); + + watch.stop(); + logger.debug("Select filtered application map elapsed. {}ms", watch.getTotalTimeMillis()); + + return applicationMapWithScatterDataV3; + } + private List> selectFilteredSpan(List transactionIdList, Filter> filter, ColumnGetCount columnGetCount) { // filters out recursive calls by looking at each objects // do not filter here if we change to a tree-based collision check in the future. @@ -176,14 +198,14 @@ private ApplicationMap createMap(FilteredMapServiceOption option, FilteredMap fi applicationMapBuilder.linkType(LinkType.DETAILED); final WasNodeHistogramDataSource wasNodeHistogramDataSource = new ResponseHistogramsNodeHistogramDataSource(filteredMap.getResponseHistograms()); applicationMapBuilder.includeNodeHistogram(new DefaultNodeHistogramFactory(wasNodeHistogramDataSource)); - ServerGroupListDataSource serverGroupListDataSource = serverInstanceDatasourceService.getServerGroupListDataSource();; + ServerGroupListDataSource serverGroupListDataSource = serverInstanceDatasourceService.getServerGroupListDataSource(); if (option.isUseStatisticsAgentState()) { applicationMapBuilder.includeServerInfo(new StatisticsServerGroupListFactory(serverGroupListDataSource)); } else { applicationMapBuilder.includeServerInfo(new DefaultServerGroupListFactory(serverGroupListDataSource)); } ApplicationMap map = applicationMapBuilder.build(filteredMap.getLinkDataDuplexMap(), buildTimeoutMillis); - if(serverMapDataFilter != null) { + if (serverMapDataFilter != null) { map = serverMapDataFilter.dataFiltering(map); } diff --git a/web/src/main/java/com/navercorp/pinpoint/web/view/FilterMapWrapSerializer.java b/web/src/main/java/com/navercorp/pinpoint/web/view/FilterMapWrapSerializer.java index 5b75818210b1..fd03da173d0b 100644 --- a/web/src/main/java/com/navercorp/pinpoint/web/view/FilterMapWrapSerializer.java +++ b/web/src/main/java/com/navercorp/pinpoint/web/view/FilterMapWrapSerializer.java @@ -21,6 +21,7 @@ import com.fasterxml.jackson.databind.JsonSerializer; import com.fasterxml.jackson.databind.SerializerProvider; import com.navercorp.pinpoint.web.applicationmap.ApplicationMapWithScatterData; +import com.navercorp.pinpoint.web.applicationmap.ApplicationMapWithScatterDataV3; import com.navercorp.pinpoint.web.applicationmap.ApplicationMapWithScatterScanResult; import com.navercorp.pinpoint.web.applicationmap.FilterMapWrap; import com.navercorp.pinpoint.web.applicationmap.nodes.NodeName; @@ -58,24 +59,38 @@ public void serialize(FilterMapWrap wrap, JsonGenerator jgen, SerializerProvider if (wrap.getApplicationMap() instanceof ApplicationMapWithScatterData) { Map applicationScatterDataMap = ((ApplicationMapWithScatterData) wrap.getApplicationMap()).getApplicationScatterDataMap(); - jgen.writeFieldName("applicationScatterData"); - jgen.writeStartObject(); + writeScatterData(jgen, applicationScatterDataMap); + } - for (Map.Entry entry : applicationScatterDataMap.entrySet()) { - NodeName nodeName = NodeName.of(entry.getKey()); - jgen.writeFieldName(nodeName.getName()); + if (wrap.getApplicationMap() instanceof ApplicationMapWithScatterDataV3) { + ApplicationMapWithScatterDataV3 applicationMap = (ApplicationMapWithScatterDataV3) wrap.getApplicationMap(); + Map applicationScatterDataMap = applicationMap.getApplicationScatterDataMap(); - ScatterData scatterData = entry.getValue(); + writeScatterData(jgen, applicationScatterDataMap); + jgen.writeObjectField("nodeHistogramDataArray", applicationMap.getNodeHistogramData()); + jgen.writeObjectField("linkHistogramDataArray", applicationMap.getLinkHistogramData()); + jgen.writeObjectField("agentHistogramDataArray", applicationMap.getAgentHistogramData()); + } - jgen.writeStartObject(); - jgen.writeObjectField("from", scatterData.getFrom()); - jgen.writeObjectField("to", scatterData.getTo()); - jgen.writeObjectField("resultFrom", scatterData.getOldestAcceptedTime()); - jgen.writeObjectField("resultTo", scatterData.getLatestAcceptedTime()); - jgen.writeObjectField("scatter", scatterData); - jgen.writeEndObject(); - } + jgen.writeEndObject(); + } + private void writeScatterData(JsonGenerator jgen, Map applicationScatterDataMap) throws IOException { + jgen.writeFieldName("applicationScatterData"); + jgen.writeStartObject(); + + for (Map.Entry entry : applicationScatterDataMap.entrySet()) { + NodeName nodeName = NodeName.of(entry.getKey()); + jgen.writeFieldName(nodeName.getName()); + + ScatterData scatterData = entry.getValue(); + + jgen.writeStartObject(); + jgen.writeObjectField("from", scatterData.getFrom()); + jgen.writeObjectField("to", scatterData.getTo()); + jgen.writeObjectField("resultFrom", scatterData.getOldestAcceptedTime()); + jgen.writeObjectField("resultTo", scatterData.getLatestAcceptedTime()); + jgen.writeObjectField("scatter", scatterData); jgen.writeEndObject(); } diff --git a/web/src/main/java/com/navercorp/pinpoint/web/view/LinkSerializer.java b/web/src/main/java/com/navercorp/pinpoint/web/view/LinkSerializer.java index abc77fda1880..239c09b3a193 100644 --- a/web/src/main/java/com/navercorp/pinpoint/web/view/LinkSerializer.java +++ b/web/src/main/java/com/navercorp/pinpoint/web/view/LinkSerializer.java @@ -80,9 +80,14 @@ public void serialize(Link link, JsonGenerator jgen, SerializerProvider provider jgen.writeObjectField("histogram", histogram); - writeTimeSeriesHistogram(link, jgen); - if (LinkType.DETAILED == link.getLinkType()) { + //time histogram + if (!link.isV3Format()){ + writeTimeSeriesHistogram(link, jgen); + } + + //agent histogram + if (!link.isV3Format() && LinkType.DETAILED == link.getLinkType()) { // data showing how agents call each of their respective links writeAgentHistogram("sourceHistogram", link.getSourceList(), jgen); writeAgentHistogram("targetHistogram", link.getTargetList(), jgen); diff --git a/web/src/main/java/com/navercorp/pinpoint/web/view/NodeSerializer.java b/web/src/main/java/com/navercorp/pinpoint/web/view/NodeSerializer.java index 2db6c7e437ea..23b04c1bd072 100644 --- a/web/src/main/java/com/navercorp/pinpoint/web/view/NodeSerializer.java +++ b/web/src/main/java/com/navercorp/pinpoint/web/view/NodeSerializer.java @@ -147,15 +147,21 @@ private void writeHistogram(Node node, JsonGenerator jgen, SerializerProvider pr jgen.writeObjectField(ResponseTimeStatics.RESPONSE_STATISTICS, responseTimeStatics); if (applicationHistogram == null) { writeEmptyObject(jgen, "histogram"); - writeEmptyObject(jgen, "apdexScore"); } else { jgen.writeObjectField("histogram", applicationHistogram); + } + + if (applicationHistogram == null) { + writeEmptyObject(jgen, "apdexScore"); + } else { //jgen.writeObjectField("apdexScore", node.getApdexScore()); JsonSerializer beanSerializer = provider.findValueSerializer(node.getApdexScore().getClass()); JsonSerializer unwrapping = beanSerializer.unwrappingSerializer(NameTransformer.NOP); unwrapping.serialize(node.getApdexScore(), jgen, provider); } - if (NodeType.DETAILED == node.getNodeType()) { + + //agent histogram + if (!node.isV3Format() && NodeType.DETAILED == node.getNodeType()) { Map agentHistogramMap = nodeHistogram.getAgentHistogramMap(); if (agentHistogramMap == null) { writeEmptyObject(jgen, "agentHistogram"); @@ -168,18 +174,22 @@ private void writeHistogram(Node node, JsonGenerator jgen, SerializerProvider pr } else { jgen.writeBooleanField("hasAlert", false); // for go.js } - // FIXME isn't this all ServiceTypes that can be a node? - if (serviceType.isWas() || serviceType.isUser() || serviceType.isTerminal() || serviceType.isUnknown() || serviceType.isQueue() || serviceType.isAlias()) { - List applicationTimeSeriesHistogram = nodeHistogram.getApplicationTimeHistogram(node.getTimeHistogramFormat()); - if (applicationTimeSeriesHistogram == null) { - writeEmptyArray(jgen, "timeSeriesHistogram"); - } else { - jgen.writeObjectField("timeSeriesHistogram", applicationTimeSeriesHistogram); - } - if (NodeType.DETAILED == node.getNodeType()) { - AgentResponseTimeViewModelList agentTimeSeriesHistogram = nodeHistogram.getAgentTimeHistogram(node.getTimeHistogramFormat()); - jgen.writeObject(agentTimeSeriesHistogram); + //time histogram + if (!node.isV3Format()) { + // FIXME isn't this all ServiceTypes that can be a node? + if (serviceType.isWas() || serviceType.isUser() || serviceType.isTerminal() || serviceType.isUnknown() || serviceType.isQueue() || serviceType.isAlias()) { + List applicationTimeSeriesHistogram = nodeHistogram.getApplicationTimeHistogram(node.getTimeHistogramFormat()); + if (applicationTimeSeriesHistogram == null) { + writeEmptyArray(jgen, "timeSeriesHistogram"); + } else { + jgen.writeObjectField("timeSeriesHistogram", applicationTimeSeriesHistogram); + } + + if (NodeType.DETAILED == node.getNodeType()) { + AgentResponseTimeViewModelList agentTimeSeriesHistogram = nodeHistogram.getAgentTimeHistogram(node.getTimeHistogramFormat()); + jgen.writeObject(agentTimeSeriesHistogram); + } } } } diff --git a/web/src/main/java/com/navercorp/pinpoint/web/view/TimeSeries/TimeSeriesValueGroupView.java b/web/src/main/java/com/navercorp/pinpoint/web/view/TimeSeries/TimeSeriesValueGroupView.java new file mode 100644 index 000000000000..d442484e1700 --- /dev/null +++ b/web/src/main/java/com/navercorp/pinpoint/web/view/TimeSeries/TimeSeriesValueGroupView.java @@ -0,0 +1,30 @@ +/* + * Copyright 2023 NAVER Corp. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.navercorp.pinpoint.web.view.TimeSeries; + +import java.util.List; + +// duplicated from com.navercorp.pinpoint.metric.web.view.TimeSeriesValueGroupView +public interface TimeSeriesValueGroupView { + String getGroupName(); + + List getMetricValues(); + + String getChartType(); + + String getUnit(); + +} diff --git a/web/src/main/java/com/navercorp/pinpoint/web/view/TimeSeries/TimeSeriesValueView.java b/web/src/main/java/com/navercorp/pinpoint/web/view/TimeSeries/TimeSeriesValueView.java new file mode 100644 index 000000000000..a2719eb727a3 --- /dev/null +++ b/web/src/main/java/com/navercorp/pinpoint/web/view/TimeSeries/TimeSeriesValueView.java @@ -0,0 +1,26 @@ +/* + * Copyright 2023 NAVER Corp. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.navercorp.pinpoint.web.view.TimeSeries; + +import java.util.List; + +// duplicated from com.navercorp.pinpoint.metric.web.view.TimeSeriesValueView +public interface TimeSeriesValueView { + String getFieldName(); + List getTags(); + List getValues(); +} + diff --git a/web/src/main/java/com/navercorp/pinpoint/web/view/TimeSeries/TimeSeriesView.java b/web/src/main/java/com/navercorp/pinpoint/web/view/TimeSeries/TimeSeriesView.java new file mode 100644 index 000000000000..1b222a147a23 --- /dev/null +++ b/web/src/main/java/com/navercorp/pinpoint/web/view/TimeSeries/TimeSeriesView.java @@ -0,0 +1,30 @@ +/* + * Copyright 2023 NAVER Corp. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.navercorp.pinpoint.web.view.TimeSeries; + +import com.navercorp.pinpoint.web.view.TimeSeries.TimeSeriesValueGroupView; + +import java.util.List; + +// duplicated from com.navercorp.pinpoint.metric.web.view.TimeSeriesView +public interface TimeSeriesView { + String getTitle(); + + List getTimestamp(); + + List getMetricValueGroups(); + +} \ No newline at end of file diff --git a/web/src/main/java/com/navercorp/pinpoint/web/view/histogram/AgentHistogramView.java b/web/src/main/java/com/navercorp/pinpoint/web/view/histogram/AgentHistogramView.java new file mode 100644 index 000000000000..eeca8f9d1805 --- /dev/null +++ b/web/src/main/java/com/navercorp/pinpoint/web/view/histogram/AgentHistogramView.java @@ -0,0 +1,26 @@ +package com.navercorp.pinpoint.web.view.histogram; + +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonUnwrapped; + +import java.util.Objects; + +public class AgentHistogramView { + private final String agentId; + private final HistogramView histogramView; + + public AgentHistogramView(String agentId, HistogramView histogramView) { + this.agentId = Objects.requireNonNull(agentId, "agentId"); + this.histogramView = Objects.requireNonNull(histogramView, "histogramView"); + } + + @JsonProperty("agentId") + public String getAgentId() { + return agentId; + } + + @JsonUnwrapped + public HistogramView getHistogramView() { + return histogramView; + } +} diff --git a/web/src/main/java/com/navercorp/pinpoint/web/view/histogram/ApplicationHistogramView.java b/web/src/main/java/com/navercorp/pinpoint/web/view/histogram/ApplicationHistogramView.java new file mode 100644 index 000000000000..fc065263c246 --- /dev/null +++ b/web/src/main/java/com/navercorp/pinpoint/web/view/histogram/ApplicationHistogramView.java @@ -0,0 +1,26 @@ +package com.navercorp.pinpoint.web.view.histogram; + +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonUnwrapped; + +import java.util.Objects; + +public class ApplicationHistogramView { + private final String histogramId; + private final HistogramView histogramView; + + public ApplicationHistogramView(String histogramId, HistogramView histogramView) { + this.histogramId = Objects.requireNonNull(histogramId, "histogramId"); + this.histogramView = Objects.requireNonNull(histogramView, "histogramView"); + } + + @JsonProperty("key") + public String getHistogramId() { + return histogramId; + } + + @JsonUnwrapped() + public HistogramView getHistogramView() { + return histogramView; + } +} diff --git a/web/src/main/java/com/navercorp/pinpoint/web/view/histogram/HistogramView.java b/web/src/main/java/com/navercorp/pinpoint/web/view/histogram/HistogramView.java new file mode 100644 index 000000000000..5abdc671f4dd --- /dev/null +++ b/web/src/main/java/com/navercorp/pinpoint/web/view/histogram/HistogramView.java @@ -0,0 +1,47 @@ +package com.navercorp.pinpoint.web.view.histogram; + +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.navercorp.pinpoint.web.applicationmap.histogram.Histogram; +import com.navercorp.pinpoint.web.applicationmap.histogram.TimeHistogram; +import com.navercorp.pinpoint.web.view.TimeSeries.TimeSeriesView; +import com.navercorp.pinpoint.web.vo.ResponseTimeStatics; + +import java.util.List; +import java.util.Objects; + +@JsonInclude(JsonInclude.Include.NON_NULL) +public class HistogramView { + private final Histogram histogram; + private final List timeHistograms; + + public HistogramView(Histogram histogram, List timeHistograms) { + this.histogram = histogram; + this.timeHistograms = Objects.requireNonNull(timeHistograms, "timeHistograms"); + } + + @JsonProperty("histogram") + public Histogram getHistogram() { + return histogram; + } + + @JsonProperty("responseSummary") + public ResponseTimeStatics getResponseSummary() { + if (histogram == null) { + return null; + } + return ResponseTimeStatics.fromHistogram(histogram); + } + + @JsonProperty("loadChart") + public TimeSeriesView getLoadChart() { + TimeHistogramChartBuilder builder = new TimeHistogramChartBuilder(timeHistograms); + return builder.build(TimeHistogramType.load); + } + + @JsonProperty("loadStatisticsChart") + public TimeSeriesView getLoadStatisticsChart() { + TimeHistogramChartBuilder builder = new TimeHistogramChartBuilder(timeHistograms); + return builder.build(TimeHistogramType.loadStatistics); + } +} diff --git a/web/src/main/java/com/navercorp/pinpoint/web/view/histogram/TimeHistogramChart.java b/web/src/main/java/com/navercorp/pinpoint/web/view/histogram/TimeHistogramChart.java new file mode 100644 index 000000000000..d3424d9d3377 --- /dev/null +++ b/web/src/main/java/com/navercorp/pinpoint/web/view/histogram/TimeHistogramChart.java @@ -0,0 +1,34 @@ +package com.navercorp.pinpoint.web.view.histogram; + +import com.navercorp.pinpoint.web.view.TimeSeries.TimeSeriesValueGroupView; +import com.navercorp.pinpoint.web.view.TimeSeries.TimeSeriesView; + +import java.util.List; +import java.util.Objects; + +public class TimeHistogramChart implements TimeSeriesView { + private final String title; + private final List timestamp; + private final List metricValueGroups; + + public TimeHistogramChart(String title, List timestamp, List metricValueGroups) { + this.title = Objects.requireNonNull(title, "title"); + this.timestamp = Objects.requireNonNull(timestamp, "timestamp"); + this.metricValueGroups = Objects.requireNonNull(metricValueGroups, "metricValueGroups"); + } + + @Override + public String getTitle() { + return title; + } + + @Override + public List getTimestamp() { + return timestamp; + } + + @Override + public List getMetricValueGroups() { + return metricValueGroups; + } +} diff --git a/web/src/main/java/com/navercorp/pinpoint/web/view/histogram/TimeHistogramChartBuilder.java b/web/src/main/java/com/navercorp/pinpoint/web/view/histogram/TimeHistogramChartBuilder.java new file mode 100644 index 000000000000..404d93d02684 --- /dev/null +++ b/web/src/main/java/com/navercorp/pinpoint/web/view/histogram/TimeHistogramChartBuilder.java @@ -0,0 +1,79 @@ +package com.navercorp.pinpoint.web.view.histogram; + +import com.navercorp.pinpoint.common.trace.HistogramSchema; +import com.navercorp.pinpoint.web.applicationmap.histogram.Histogram; +import com.navercorp.pinpoint.web.applicationmap.histogram.TimeHistogram; +import com.navercorp.pinpoint.web.view.TimeSeries.TimeSeriesValueGroupView; +import com.navercorp.pinpoint.web.view.TimeSeries.TimeSeriesValueView; +import com.navercorp.pinpoint.web.vo.ResponseTimeStatics; + +import java.util.Collections; +import java.util.List; +import java.util.Objects; +import java.util.function.Function; +import java.util.stream.Collectors; + +public class TimeHistogramChartBuilder { + private final List timeHistograms; + + public TimeHistogramChartBuilder(List timeHistograms) { + this.timeHistograms = Objects.requireNonNull(timeHistograms, "timeHistograms"); + } + + public TimeHistogramChart build(TimeHistogramType timeHistogramType) { + if (timeHistogramType == TimeHistogramType.load) { + return buildLoadChart(); + } else if (timeHistogramType == TimeHistogramType.loadStatistics) { + return buildLoadStatisticsChart(); + } + return new TimeHistogramChart("empty", Collections.emptyList(), Collections.emptyList()); + } + + public TimeHistogramChart buildLoadChart() { + List metricValueList = createLoadValueList(timeHistograms); + return new TimeHistogramChart("Load", getTimeStampList(timeHistograms), + List.of(new TimeHistogramValueGroupView("load", "count", "area-step", metricValueList))); + } + + private List createLoadValueList(List histogramList) { + if (histogramList.isEmpty()) { + return Collections.emptyList(); + } + HistogramSchema schema = histogramList.get(0).getHistogramSchema(); + return List.of( + new TimeHistogramValueView(schema.getFastSlot().getSlotName(), getValueList(histogramList, Histogram::getFastCount)), + new TimeHistogramValueView(schema.getNormalSlot().getSlotName(), getValueList(histogramList, Histogram::getNormalCount)), + new TimeHistogramValueView(schema.getSlowSlot().getSlotName(), getValueList(histogramList, Histogram::getSlowCount)), + new TimeHistogramValueView(schema.getVerySlowSlot().getSlotName(), getValueList(histogramList, Histogram::getVerySlowCount)), + new TimeHistogramValueView(schema.getErrorSlot().getSlotName(), getValueList(histogramList, Histogram::getErrorCount)) + ); + } + + public TimeHistogramChart buildLoadStatisticsChart() { + List metricValueList = createLoadStatisticsValueList(timeHistograms); + return new TimeHistogramChart("Load Avg & Max", getTimeStampList(timeHistograms), + List.of(new TimeHistogramValueGroupView("loadStatistics", "ms", "area-step", metricValueList))); + } + + private List createLoadStatisticsValueList(List histogramList) { + return List.of( + new TimeHistogramValueView(ResponseTimeStatics.AVG_ELAPSED_TIME, getValueList(histogramList, Histogram::getAvgElapsed)), + new TimeHistogramValueView(ResponseTimeStatics.MAX_ELAPSED_TIME, getValueList(histogramList, Histogram::getMaxElapsed)) + ); + } + + + private List getTimeStampList(List histogramList) { + return histogramList.stream() + .map(TimeHistogram::getTimeStamp) + .collect(Collectors.toList()); + } + + public List getValueList(List histogramList, Function function) { + return histogramList + .stream() + .map(function) + .collect(Collectors.toList()); + } + +} diff --git a/web/src/main/java/com/navercorp/pinpoint/web/view/histogram/TimeHistogramType.java b/web/src/main/java/com/navercorp/pinpoint/web/view/histogram/TimeHistogramType.java new file mode 100644 index 000000000000..f388056ade99 --- /dev/null +++ b/web/src/main/java/com/navercorp/pinpoint/web/view/histogram/TimeHistogramType.java @@ -0,0 +1,6 @@ +package com.navercorp.pinpoint.web.view.histogram; + +public enum TimeHistogramType { + load, + loadStatistics +} diff --git a/web/src/main/java/com/navercorp/pinpoint/web/view/histogram/TimeHistogramValueGroupView.java b/web/src/main/java/com/navercorp/pinpoint/web/view/histogram/TimeHistogramValueGroupView.java new file mode 100644 index 000000000000..ae42f2641779 --- /dev/null +++ b/web/src/main/java/com/navercorp/pinpoint/web/view/histogram/TimeHistogramValueGroupView.java @@ -0,0 +1,42 @@ +package com.navercorp.pinpoint.web.view.histogram; + +import com.navercorp.pinpoint.web.view.TimeSeries.TimeSeriesValueGroupView; +import com.navercorp.pinpoint.web.view.TimeSeries.TimeSeriesValueView; + +import java.util.List; +import java.util.Objects; + +public class TimeHistogramValueGroupView implements TimeSeriesValueGroupView { + private final String groupName; + private final String unit; + private final String chartType; + private final List metricValues; + + public TimeHistogramValueGroupView(String groupName, String unit, String chartType, List metricValues) { + this.groupName = Objects.requireNonNull(groupName, "groupName"); + this.chartType = Objects.requireNonNull(chartType, "chartType"); + + this.metricValues = Objects.requireNonNull(metricValues, "metricValues"); + this.unit = Objects.requireNonNull(unit, "unit"); + } + + @Override + public String getGroupName() { + return groupName; + } + + @Override + public String getChartType() { + return chartType; + } + + @Override + public List getMetricValues() { + return metricValues; + } + + @Override + public String getUnit() { + return unit; + } +} diff --git a/web/src/main/java/com/navercorp/pinpoint/web/view/histogram/TimeHistogramValueView.java b/web/src/main/java/com/navercorp/pinpoint/web/view/histogram/TimeHistogramValueView.java new file mode 100644 index 000000000000..3f9e28db6baa --- /dev/null +++ b/web/src/main/java/com/navercorp/pinpoint/web/view/histogram/TimeHistogramValueView.java @@ -0,0 +1,33 @@ +package com.navercorp.pinpoint.web.view.histogram; + +import com.navercorp.pinpoint.web.view.TimeSeries.TimeSeriesValueView; + +import java.util.Collections; +import java.util.List; +import java.util.Objects; + +public class TimeHistogramValueView implements TimeSeriesValueView { + private final String fieldName; + + private final List valueList; + + public TimeHistogramValueView(String fieldName, List valueList) { + this.fieldName = Objects.requireNonNull(fieldName, "fieldName"); + this.valueList = Objects.requireNonNull(valueList, "valueList"); + } + + @Override + public String getFieldName() { + return fieldName; + } + + @Override + public List getTags() { + return Collections.emptyList(); + } + + @Override + public List getValues() { + return valueList; + } +} diff --git a/web/src/main/java/com/navercorp/pinpoint/web/vo/ApplicationPairs.java b/web/src/main/java/com/navercorp/pinpoint/web/vo/ApplicationPairs.java index 3c8b47237175..77f6cc8f76cd 100644 --- a/web/src/main/java/com/navercorp/pinpoint/web/vo/ApplicationPairs.java +++ b/web/src/main/java/com/navercorp/pinpoint/web/vo/ApplicationPairs.java @@ -24,6 +24,11 @@ * @author HyunGil Jeong */ public class ApplicationPairs { + public static final ApplicationPairs EMPTY = new ApplicationPairs(); + + public static ApplicationPairs empty() { + return EMPTY; + } @JsonProperty("from") private List fromApplications; diff --git a/web/src/test/java/com/navercorp/pinpoint/web/view/TimeHistogramChartTest.java b/web/src/test/java/com/navercorp/pinpoint/web/view/TimeHistogramChartTest.java new file mode 100644 index 000000000000..320261626c7a --- /dev/null +++ b/web/src/test/java/com/navercorp/pinpoint/web/view/TimeHistogramChartTest.java @@ -0,0 +1,84 @@ +/* + * Copyright 2014 NAVER Corp. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.navercorp.pinpoint.web.view; + +import com.navercorp.pinpoint.common.trace.BaseHistogramSchema; +import com.navercorp.pinpoint.common.trace.HistogramSchema; +import com.navercorp.pinpoint.web.applicationmap.histogram.TimeHistogram; +import com.navercorp.pinpoint.web.view.histogram.TimeHistogramChartBuilder; +import com.navercorp.pinpoint.web.view.histogram.TimeHistogramType; +import com.navercorp.pinpoint.web.view.TimeSeries.TimeSeriesValueGroupView; +import com.navercorp.pinpoint.web.view.TimeSeries.TimeSeriesValueView; +import com.navercorp.pinpoint.web.view.TimeSeries.TimeSeriesView; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.assertj.core.api.Assertions; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import java.util.List; + +/** + * @author emeroad + */ +public class TimeHistogramChartTest { + + private final Logger logger = LogManager.getLogger(this.getClass()); + + private List timeHistogramList; + + @BeforeEach + public void setUp() { + HistogramSchema schema = BaseHistogramSchema.NORMAL_SCHEMA; + TimeHistogram timeHistogram1 = new TimeHistogram(schema, 0); + TimeHistogram timeHistogram2 = new TimeHistogram(schema, 60000); + + timeHistogram1.addCallCount(schema.getFastSlot().getSlotTime(), 1); + timeHistogram1.addCallCount(schema.getNormalSlot().getSlotTime(), 2); + timeHistogram1.addCallCount(schema.getSlowSlot().getSlotTime(), 3); + timeHistogram1.addCallCount(schema.getVerySlowSlot().getSlotTime(), 4); + timeHistogram1.addCallCount(schema.getErrorSlot().getSlotTime(), 5); + + timeHistogram2.addCallCount(schema.getFastSlot().getSlotTime(), 6); + timeHistogram2.addCallCount(schema.getNormalSlot().getSlotTime(), 7); + timeHistogram2.addCallCount(schema.getSlowSlot().getSlotTime(), 8); + timeHistogram2.addCallCount(schema.getVerySlowSlot().getSlotTime(), 9); + timeHistogram2.addCallCount(schema.getErrorSlot().getSlotTime(), 10); + + timeHistogramList = List.of(timeHistogram1, timeHistogram2); + } + + @Test + public void LoadChartBuildTest() { + TimeHistogramChartBuilder builder = new TimeHistogramChartBuilder(timeHistogramList); + TimeSeriesView loadChart = builder.build(TimeHistogramType.load); + + Assertions.assertThat(loadChart.getTimestamp()).isEqualTo(List.of(0L, 60000L)); + List metricValueGroups = loadChart.getMetricValueGroups(); + Assertions.assertThat(metricValueGroups).isNotEmpty(); + + TimeSeriesValueGroupView groupView = metricValueGroups.get(0); + List metricValues = groupView.getMetricValues(); + Assertions.assertThat(metricValues.size()).isEqualTo(5); + + HistogramSchema schema = BaseHistogramSchema.NORMAL_SCHEMA; + Assertions.assertThat(metricValues.get(0).getFieldName()).isEqualTo(schema.getFastSlot().getSlotName()); + Assertions.assertThat(metricValues.get(0).getValues()).isEqualTo(List.of(1L, 6L)); + Assertions.assertThat(metricValues.get(4).getFieldName()).isEqualTo(schema.getErrorSlot().getSlotName()); + Assertions.assertThat(metricValues.get(4).getValues()).isEqualTo(List.of(5L, 10L)); + } +}