package com.nhn.hippo.web.service; import java.util.ArrayList; import java.util.HashSet; import java.util.List; import java.util.Set; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.util.StopWatch; import com.nhn.hippo.web.calltree.server.AgentIdNodeSelector; import com.nhn.hippo.web.calltree.server.ApplicationIdNodeSelector; import com.nhn.hippo.web.calltree.server.NodeSelector; import com.nhn.hippo.web.calltree.server.ServerCallTree; import com.nhn.hippo.web.dao.ApplicationIndexDao; import com.nhn.hippo.web.dao.ApplicationTraceIndexDao; import com.nhn.hippo.web.dao.TraceDao; import com.nhn.hippo.web.vo.Application; import com.nhn.hippo.web.vo.BusinessTransactions; import com.nhn.hippo.web.vo.ClientStatistics; import com.nhn.hippo.web.vo.TraceId; import com.profiler.common.bo.SpanBo; import com.profiler.common.bo.SpanEventBo; /** * @author netspider */ @Service public class FlowChartServiceImpl implements FlowChartService { private Logger logger = LoggerFactory.getLogger(this.getClass()); @Autowired private TraceDao traceDao; @Autowired private ApplicationIndexDao applicationIndexDao; @Autowired private ApplicationTraceIndexDao applicationTraceIndexDao; @Override public List selectAllApplicationNames() { return applicationIndexDao.selectAllApplicationNames(); } /** * DetailView에서 사용함. 하나의 Span을 선택했을때 Draw되는 데이터를 생성하는 함수이다 makes call tree * of transaction detail view */ @Override public ServerCallTree selectServerCallTree(TraceId traceId) { StopWatch watch = new StopWatch(); watch.start(); List transaction = this.traceDao.selectSpans(traceId); Set endPoints = createUniqueEndpoint(transaction); ServerCallTree tree = createServerCallTree(transaction, new AgentIdNodeSelector()); // subSpan에서 record할 데이터만 골라낸다. List spanEventBoList = findRecordStatisticsSpanEventData(transaction, endPoints); tree.addSpanEventList(spanEventBoList); tree.build(); watch.stop(); logger.info("Fetch single transaction serverCallTree elapsed. {}ms", watch.getLastTaskTimeMillis()); return tree; } /** * filtered application map */ @Override public ServerCallTree selectServerCallTree(Set traceIdSet) { StopWatch watch = new StopWatch(); watch.start(); List> transactionList = this.traceDao.selectAllSpans(traceIdSet); List transaction = new ArrayList(); for (List t : transactionList) { for (SpanBo span : t) { transaction.add(span); } } Set endPoints = createUniqueEndpoint(transaction); ServerCallTree tree = createServerCallTree(transaction, new ApplicationIdNodeSelector()); // subSpan에서 record할 데이터만 골라낸다. List spanEventBoList = findRecordStatisticsSpanEventData(transaction, endPoints); tree.addSpanEventList(spanEventBoList); tree.build(); watch.stop(); logger.info("Fetch single transaction serverCallTree elapsed. {}ms", watch.getLastTaskTimeMillis()); return tree; } private List findRecordStatisticsSpanEventData(List transaction, Set endPoints) { List filterSpanEventBo = new ArrayList(); for (SpanBo eachTransaction : transaction) { List spanEventBoList = eachTransaction.getSpanEventBoList(); if (spanEventBoList == null) { continue; } for (SpanEventBo spanEventBo : spanEventBoList) { // 통계정보로 잡지 않을 데이터는 스킵한다. if (!spanEventBo.getServiceType().isRecordStatistics()) { continue; } // remove subspan of the rpc client if (!endPoints.contains(spanEventBo.getEndPoint())) { // this is unknown cloud filterSpanEventBo.add(spanEventBo); } } } return filterSpanEventBo; } /** * ServerCallTree를 생성하고 low SpanBo데이터를 tree에 추가한다. * * @param transaction * @return */ private ServerCallTree createServerCallTree(List transaction, NodeSelector nodeSelector) { ServerCallTree serverCallTree = new ServerCallTree(nodeSelector); serverCallTree.addSpanList(transaction); // TODO 이 메소드는 transaction하나만 조회하는 페이지에서 사용되기 때문에 이렇게 한다. // 나중에 방식을 변경할 필요가 있을지도... for (SpanBo span : transaction) { if (!span.isRoot()) { continue; } ClientStatistics stat = new ClientStatistics(span.getApplicationId(), span.getServiceType().getCode()); stat.getHistogram().addSample(span.getElapsed()); serverCallTree.addClientStatistics(stat); } return serverCallTree; } /** * Trace uuid를 구성하는 SpanBo의 집합에서 endpoint 값을 유니크한 값으로 뽑아온다. * * @param transaction * @return */ private Set createUniqueEndpoint(List transaction) { // markRecursiveCall(transaction); Set endPointSet = new HashSet(); for (SpanBo eachTransaction : transaction) { endPointSet.add(eachTransaction.getEndPoint()); } return endPointSet; } @Override public Set selectTraceIdsFromApplicationTraceIndex(String applicationName, long from, long to) { if (applicationName == null) { throw new NullPointerException("applicationName"); } if (logger.isTraceEnabled()) { logger.trace("scan(selectTraceIdsFromApplicationTraceIndex) {}, {}, {}", new Object[] { applicationName, from, to }); } List> traceIdList = this.applicationTraceIndexDao.scanTraceIndex(applicationName, from, to); Set result = new HashSet(); for (List list : traceIdList) { for (TraceId traceId : list) { result.add(traceId); logger.trace("traceid:{}", traceId); } } return result; } @Override public BusinessTransactions selectBusinessTransactions(Set traceIds, String applicationName, long from, long to) { List> traceList = this.traceDao.selectSpans(traceIds); BusinessTransactions businessTransactions = new BusinessTransactions(); for (List trace : traceList) { for (SpanBo spanBo : trace) { // 해당 application으로 인입된 요청만 보여준다. if (applicationName.equals(spanBo.getApplicationId())) { businessTransactions.add(spanBo); } } } return businessTransactions; } }