[유치수] [NOBTS] refactoring.

git-svn-id: http://svn.bds.nhncorp.com/pe/hippo-web/trunk@1564 84d0f5b1-2673-498c-a247-62c4ff18d310
This commit is contained in:
Chisu Yu
2013-04-25 08:02:36 +00:00
parent fd41cfa008
commit 159be16772
33 changed files with 444 additions and 3319 deletions
@@ -1,24 +1,16 @@
package com.nhn.hippo.web.controller;
import java.util.Set;
import javax.servlet.http.HttpServletResponse;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.util.StopWatch;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import com.nhn.hippo.web.applicationmap.ApplicationMap;
import com.nhn.hippo.web.calltree.server.ServerCallTree;
import com.nhn.hippo.web.service.ApplicationMapService;
import com.nhn.hippo.web.service.FlowChartService;
import com.nhn.hippo.web.vo.TraceId;
/**
*
@@ -26,12 +18,7 @@ import com.nhn.hippo.web.vo.TraceId;
*/
@Controller
public class ApplicationMapController extends BaseController {
private final Logger logger = LoggerFactory.getLogger(this.getClass());
@Autowired
private FlowChartService flow;
@Autowired
private ApplicationMapService applicationMapService;
@@ -52,64 +39,4 @@ public class ApplicationMapController extends BaseController {
long from = to - period;
return getServerMapData2(model, response, applicationName, serviceType, from, to);
}
@RequestMapping(value = "/getServerMapData", method = RequestMethod.GET)
public String getServerMapData(Model model, HttpServletResponse response, @RequestParam("application") String applicationName, @RequestParam("from") long from, @RequestParam("to") long to) {
// TODO 제거 하거나, interceptor로 할것.
StopWatch watch = new StopWatch();
watch.start("scanTraceindex");
Set<TraceId> traceIdList = flow.selectTraceIdsFromApplicationTraceIndex(applicationName, from, to);
watch.stop();
logger.info("Fetch traceIdList elapsed : {}ms, {} traces", watch.getLastTaskTimeMillis(), traceIdList.size());
watch.start("selectServerCallTree");
ServerCallTree callTree = flow.selectServerCallTree(traceIdList, applicationName, from, to);
watch.stop();
logger.info("Fetch calltree time : {}ms", watch.getLastTaskTimeMillis());
model.addAttribute("nodes", callTree.getNodes());
model.addAttribute("links", callTree.getLinks());
addResponseHeader(response);
return "servermap";
}
@RequestMapping(value = "/getLastServerMapData", method = RequestMethod.GET)
public String getLastServerMapData(Model model, HttpServletResponse response, @RequestParam("application") String applicationName, @RequestParam("period") long period) {
long to = getQueryEndTime();
long from = to - period;
return getServerMapData(model, response, applicationName, from, to);
}
@Deprecated
@RequestMapping(value = "/flowserverByHost", method = RequestMethod.GET)
public String flowServerByHost(Model model, @RequestParam("host") String[] hosts, @RequestParam("from") long from, @RequestParam("to") long to) {
String[] agentIds = flow.selectAgentIds(hosts);
// TODO 제거 하거나, interceptor로 할것.
StopWatch watch = new StopWatch();
watch.start("scanTraceindex");
Set<TraceId> traceIds = flow.selectTraceIdsFromTraceIndex(agentIds, from, to);
watch.stop();
logger.info("time:{} {}", watch.getLastTaskTimeMillis(), traceIds.size());
watch.start("selectServerCallTree");
ServerCallTree callTree = flow.selectServerCallTree(traceIds);
watch.stop();
logger.info("time:{}", watch.getLastTaskTimeMillis());
model.addAttribute("nodes", callTree.getNodes());
model.addAttribute("links", callTree.getLinks());
logger.debug("callTree:{}", callTree);
return "flowserver";
}
}
@@ -80,13 +80,20 @@ public class BusinessTransactionController extends BaseController {
return getBusinessTransactionsData(model, response, applicationName, from, to);
}
@RequestMapping(value = "/selectTransaction", method = RequestMethod.GET)
public ModelAndView selectTransaction(@RequestParam("traceId") String traceIdParam, @RequestParam("focusTimestamp") long focusTimestamp) {
/**
* 선택한 하나의 Transaction 정보 조회.
*
* @param traceIdParam
* @param focusTimestamp
* @return
*/
@RequestMapping(value = "/transactionInfo", method = RequestMethod.GET)
public ModelAndView transactionInfo(@RequestParam("traceId") String traceIdParam, @RequestParam("focusTimestamp") long focusTimestamp) {
logger.debug("traceId:{}", traceIdParam);
final TraceId traceId = new TraceId(traceIdParam);
ModelAndView mv = new ModelAndView("selectTransaction");
ModelAndView mv = new ModelAndView("transactionInfo");
try {
// select spans
@@ -15,9 +15,8 @@ import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import com.nhn.hippo.web.service.FlowChartService;
import com.nhn.hippo.web.service.SpanService;
import com.nhn.hippo.web.vo.RequestMetadataQuery;
import com.nhn.hippo.web.service.ScatterChartService;
import com.nhn.hippo.web.vo.TransactionMetadataQuery;
import com.nhn.hippo.web.vo.scatter.Dot;
import com.profiler.common.bo.SpanBo;
@@ -31,10 +30,7 @@ public class ScatterChartController extends BaseController {
private final Logger logger = LoggerFactory.getLogger(this.getClass());
@Autowired
private FlowChartService flow;
@Autowired
private SpanService spanService;
private ScatterChartService scatter;
@RequestMapping(value = "/scatterpopup", method = RequestMethod.GET)
public String scatterPopup(Model model, HttpServletResponse response, @RequestParam("application") String applicationName, @RequestParam("from") long from, @RequestParam("to") long to, @RequestParam("period") long period, @RequestParam("usePeriod") boolean usePeriod) {
@@ -45,29 +41,6 @@ public class ScatterChartController extends BaseController {
model.addAttribute("usePeriod", usePeriod);
return "scatterPopup";
}
@RequestMapping(value = "/scatterView", method = RequestMethod.GET)
public String getScatterView(Model model, HttpServletResponse response, @RequestParam("application") String applicationName, @RequestParam("from") long from, @RequestParam("to") long to, @RequestParam("limit") int limit) {
StopWatch watch = new StopWatch();
watch.start("selectScatterData");
List<Dot> scatterData = flow.selectScatterData(applicationName, from, to, limit);
watch.stop();
logger.info("Fetch scatterData time : {}ms", watch.getLastTaskTimeMillis());
model.addAttribute("scatter", scatterData);
addResponseHeader(response);
return "scatter_view";
}
@RequestMapping(value = "/lastScatterView", method = RequestMethod.GET)
public String getLastScatterView(Model model, HttpServletResponse response, @RequestParam("application") String applicationName, @RequestParam("period") long period, @RequestParam("limit") int limit) {
long to = getQueryEndTime();
long from = to - period;
return getScatterView(model, response, applicationName, from, to, limit);
}
/**
*
@@ -82,11 +55,11 @@ public class ScatterChartController extends BaseController {
* @return
*/
@RequestMapping(value = "/getScatterData", method = RequestMethod.GET)
public String getScatterData(Model model, HttpServletResponse response, @RequestParam("application") String applicationName, @RequestParam("from") long from, @RequestParam("to") long to, @RequestParam("limit") int limit, @RequestParam(value="_callback", required=false) String jsonpCallback) {
public String getScatterData(Model model, HttpServletResponse response, @RequestParam("application") String applicationName, @RequestParam("from") long from, @RequestParam("to") long to, @RequestParam("limit") int limit, @RequestParam(value = "_callback", required = false) String jsonpCallback) {
StopWatch watch = new StopWatch();
watch.start("selectScatterData");
List<Dot> scatterData = flow.selectScatterData(applicationName, from, to, limit);
List<Dot> scatterData = scatter.selectScatterData(applicationName, from, to, limit);
watch.stop();
logger.info("Fetch scatterData time : {}ms", watch.getLastTaskTimeMillis());
@@ -94,7 +67,7 @@ public class ScatterChartController extends BaseController {
model.addAttribute("scatter", scatterData);
addResponseHeader(response);
if (jsonpCallback == null) {
return "scatter_json";
} else {
@@ -114,7 +87,7 @@ public class ScatterChartController extends BaseController {
* @return
*/
@RequestMapping(value = "/getLastScatterData", method = RequestMethod.GET)
public String getLastScatterData(Model model, HttpServletResponse response, @RequestParam("application") String applicationName, @RequestParam("period") long period, @RequestParam("limit") int limit, @RequestParam(value="_callback", required=false) String jsonpCallback) {
public String getLastScatterData(Model model, HttpServletResponse response, @RequestParam("application") String applicationName, @RequestParam("period") long period, @RequestParam("limit") int limit, @RequestParam(value = "_callback", required = false) String jsonpCallback) {
long to = getQueryEndTime();
long from = to - period;
return getScatterData(model, response, applicationName, from, to, limit, jsonpCallback);
@@ -139,7 +112,7 @@ public class ScatterChartController extends BaseController {
long to = getQueryEndTime();
List<Dot> scatterData = flow.selectScatterData(applicationName, from, to, limit);
List<Dot> scatterData = scatter.selectScatterData(applicationName, from, to, limit);
watch.stop();
logger.info("Fetch scatterData time : {}ms", watch.getLastTaskTimeMillis());
@@ -167,13 +140,13 @@ public class ScatterChartController extends BaseController {
* @param response
* @return
*/
@RequestMapping(value = "/requestmetadata", method = RequestMethod.POST)
public String requestmetadata(Model model, HttpServletRequest request, HttpServletResponse response) {
@RequestMapping(value = "/transactionmetadata", method = RequestMethod.POST)
public String transactionmetadata(Model model, HttpServletRequest request, HttpServletResponse response) {
String TRACEID = "tr";
String TIME = "ti";
String RESPONSE_TIME = "re";
RequestMetadataQuery query = new RequestMetadataQuery();
TransactionMetadataQuery query = new TransactionMetadataQuery();
int index = 0;
while (true) {
@@ -190,11 +163,11 @@ public class ScatterChartController extends BaseController {
}
if (query.size() > 0) {
List<SpanBo> metadata = spanService.selectRequestMetadata(query);
List<SpanBo> metadata = scatter.selectTransactionMetadata(query);
model.addAttribute("metadata", metadata);
}
addResponseHeader(response);
return "requestmetadata";
return "transactionmetadata";
}
}
@@ -1,16 +0,0 @@
package com.nhn.hippo.web.dao;
import java.util.List;
import java.util.Map;
import com.nhn.hippo.web.vo.ClientStatistics;
/**
*
* @author netspider
*
*/
@Deprecated
public interface ClientStatisticsDao {
public List<Map<String, ClientStatistics>> selectClient(String applicationName, short serviceType, long from, long to);
}
@@ -1,23 +0,0 @@
package com.nhn.hippo.web.dao;
import java.util.List;
import java.util.Map;
import com.nhn.hippo.web.vo.TerminalStatistics;
/**
*
* @author netspider
*
*/
@Deprecated
public interface TerminalStatisticsDao {
/**
*
* @param applicationName
* @param from
* @param to
* @return key=applicationname
*/
public List<Map<String, TerminalStatistics>> selectTerminal(String applicationName, long from, long to);
}
@@ -21,7 +21,6 @@ import com.profiler.common.hbase.HBaseTables;
import com.profiler.common.hbase.HbaseOperations2;
import com.profiler.common.util.ApplicationMapStatisticsUtils;
import com.profiler.common.util.TimeSlot;
import com.profiler.common.util.TimeUtils;
/**
*
@@ -21,7 +21,6 @@ import com.profiler.common.hbase.HBaseTables;
import com.profiler.common.hbase.HbaseOperations2;
import com.profiler.common.util.ApplicationMapStatisticsUtils;
import com.profiler.common.util.TimeSlot;
import com.profiler.common.util.TimeUtils;
/**
*
@@ -1,68 +0,0 @@
package com.nhn.hippo.web.dao.hbase;
import java.sql.Date;
import java.text.SimpleDateFormat;
import java.util.List;
import java.util.Map;
import org.apache.hadoop.hbase.client.Scan;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.data.hadoop.hbase.RowMapper;
import org.springframework.stereotype.Repository;
import com.nhn.hippo.web.dao.ClientStatisticsDao;
import com.nhn.hippo.web.vo.ClientStatistics;
import com.profiler.common.hbase.HBaseTables;
import com.profiler.common.hbase.HbaseOperations2;
import com.profiler.common.util.ClientStatUtils;
import com.profiler.common.util.TimeSlot;
/**
*
* @author netspider
*
*/
@Deprecated
@Repository
public class HbaseClientStatisticsDao implements ClientStatisticsDao {
private Logger logger = LoggerFactory.getLogger(this.getClass());
private int scanCacheSize = 40;
@Autowired
private HbaseOperations2 hbaseOperations2;
@Autowired
@Qualifier("clientStatisticsMapper")
private RowMapper<Map<String, ClientStatistics>> clientStatisticsMapper;
@Override
public List<Map<String, ClientStatistics>> selectClient(String applicationName, short serviceType, long from, long to) {
Scan scan = createScan(applicationName, serviceType, from, to);
return hbaseOperations2.find(HBaseTables.CLIENT_STATISTICS, scan, clientStatisticsMapper);
}
private Scan createScan(String applicationName, short serviceType, long from, long to) {
long startTime = TimeSlot.getStatisticsRowSlot(from);
// hbase의 scanner를 사용하여 검색시 endTime은 검색 대상에 포함되지 않기 때문에, +1을 해줘야 된다.
long endTime = TimeSlot.getStatisticsRowSlot(to) + 1;
if (logger.isDebugEnabled()) {
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("HH:mm:ss,SSS");
logger.debug("scan startTime:{} endTime:{}", simpleDateFormat.format(new Date(startTime)), simpleDateFormat.format(new Date(endTime)));
}
byte[] startKey = ClientStatUtils.makeRowKey(applicationName, serviceType, startTime);
byte[] endKey = ClientStatUtils.makeRowKey(applicationName, serviceType, endTime);
Scan scan = new Scan();
scan.setCaching(this.scanCacheSize);
scan.setStartRow(startKey);
scan.setStopRow(endKey);
scan.addFamily(HBaseTables.CLIENT_STATISTICS_CF_COUNTER);
scan.setId("clientStatisticsScan");
return scan;
}
}
@@ -1,68 +0,0 @@
package com.nhn.hippo.web.dao.hbase;
import java.sql.Date;
import java.text.SimpleDateFormat;
import java.util.List;
import java.util.Map;
import org.apache.hadoop.hbase.client.Scan;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.data.hadoop.hbase.RowMapper;
import org.springframework.stereotype.Repository;
import com.nhn.hippo.web.dao.TerminalStatisticsDao;
import com.nhn.hippo.web.vo.TerminalStatistics;
import com.profiler.common.hbase.HBaseTables;
import com.profiler.common.hbase.HbaseOperations2;
import com.profiler.common.util.TerminalSpanUtils;
import com.profiler.common.util.TimeSlot;
/**
*
* @author netspider
*
*/
@Deprecated
@Repository
public class HbaseTerminalStatisticsDao implements TerminalStatisticsDao {
private Logger logger = LoggerFactory.getLogger(this.getClass());
private int scanCacheSize = 40;
@Autowired
private HbaseOperations2 hbaseOperations2;
@Autowired
@Qualifier("terminalStatisticsMapper")
private RowMapper<Map<String, TerminalStatistics>> terminalStatisticsMapper;
@Override
public List<Map<String, TerminalStatistics>> selectTerminal(String applicationName, long from, long to) {
Scan scan = createScan(applicationName, from, to);
return hbaseOperations2.find(HBaseTables.TERMINAL_STATISTICS, scan, terminalStatisticsMapper);
}
private Scan createScan(String applicationName, long from, long to) {
long startTime = TimeSlot.getStatisticsRowSlot(from);
// hbase의 scanner를 사용하여 검색시 endTime은 검색 대상에 포함되지 않기 때문에, +1을 해줘야 된다.
long endTime = TimeSlot.getStatisticsRowSlot(to) + 1;
if (logger.isDebugEnabled()) {
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("HH:mm:ss,SSS");
logger.debug("scan startTime:{} endTime:{}", simpleDateFormat.format(new Date(startTime)), simpleDateFormat.format(new Date(endTime)));
}
byte[] startKey = TerminalSpanUtils.makeRowKey(applicationName, startTime);
byte[] endKey = TerminalSpanUtils.makeRowKey(applicationName, endTime);
Scan scan = new Scan();
scan.setCaching(this.scanCacheSize);
scan.setStartRow(startKey);
scan.setStopRow(endKey);
scan.addFamily(HBaseTables.TERMINAL_STATISTICS_CF_COUNTER);
scan.setId("terminalStatisticsScan");
return scan;
}
}
@@ -1,71 +0,0 @@
package com.nhn.hippo.web.mapper;
import java.util.HashMap;
import java.util.Map;
import org.apache.hadoop.hbase.KeyValue;
import org.apache.hadoop.hbase.client.Result;
import org.apache.hadoop.hbase.util.Bytes;
import org.springframework.data.hadoop.hbase.RowMapper;
import org.springframework.stereotype.Component;
import com.nhn.hippo.web.vo.ClientStatistics;
import com.profiler.common.hbase.HBaseTables;
import com.profiler.common.util.ClientStatUtils;
/**
*
* @author netspider
*
*/
@Component
public class ClientStatisticsMapper implements RowMapper<Map<String, ClientStatistics>> {
/**
* <pre>
* rowkey = applicationName + serviceType + timeslot
* cf = Count
* cq = Slot
* </pre>
*/
@Override
public Map<String, ClientStatistics> mapRow(Result result, int rowNum) throws Exception {
KeyValue[] keyList = result.raw();
// key is destApplicationName.
Map<String, ClientStatistics> stat = new HashMap<String, ClientStatistics>();
for (KeyValue kv : keyList) {
if (kv.getFamilyLength() != HBaseTables.CLIENT_STATISTICS_CF_COUNTER.length) {
continue;
}
byte[] qualifier = kv.getQualifier();
String destApplicationName = ClientStatUtils.getApplicationNameFromRowKey(kv.getRow());
short destServiceType = ClientStatUtils.getApplicationServiceTypeFromRowKey(kv.getRow());
long requestCount = Bytes.toLong(kv.getValue());
short histogramSlot = ClientStatUtils.getHistogramSlotFromColumnName(qualifier);
boolean isError = histogramSlot == (short) -1;
if (stat.containsKey(destApplicationName)) {
ClientStatistics statistics = stat.get(destApplicationName);
if (isError) {
statistics.getHistogram().incrErrorCount(requestCount);
} else {
statistics.getHistogram().addSample(histogramSlot, requestCount);
}
} else {
ClientStatistics statistics = new ClientStatistics(destApplicationName, destServiceType);
if (isError) {
statistics.getHistogram().incrErrorCount(requestCount);
} else {
statistics.getHistogram().addSample(histogramSlot, requestCount);
}
stat.put(destApplicationName, statistics);
}
}
return stat;
}
}
@@ -1,113 +0,0 @@
package com.nhn.hippo.web.mapper;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import org.apache.hadoop.hbase.KeyValue;
import org.apache.hadoop.hbase.client.Result;
import org.apache.hadoop.hbase.util.Bytes;
import org.springframework.data.hadoop.hbase.RowMapper;
import org.springframework.stereotype.Component;
import com.nhn.hippo.web.vo.TerminalStatistics;
import com.profiler.common.hbase.HBaseTables;
import com.profiler.common.util.TerminalSpanUtils;
/**
*
*/
@Component
public class TerminalStatisticsMapper implements RowMapper<Map<String, TerminalStatistics>> {
/**
* <pre>
* rowkey = applicationName + timeslot
* cf = Cnt, ErrCnt
* cn = ServiceType + Slot + ApplicationName
*
* output format
* {
* hippo={
* From=TOMCAT11, To=hippo, ToSvcType=2101, Histogram={ "1000" : 3, "3000" : 0, "5000" : 0 }
* },
* dev={
* From=TOMCAT11, To=dev, ToSvcType=8100, Histogram={ "100" : 1, "300" : 0, "500" : 0 }
* },
* MEMCACHED={
* From=TOMCAT11, To=MEMCACHED, ToSvcType=8050, Histogram={ "100" : 1, "300" : 0, "500" : 0 }
* },
* section.cafe.naver.com={
* From=TOMCAT11, To=section.cafe.naver.com, ToSvcType=9050, Histogram={ "1000" : 2, "3000" : 0, "5000" : 0 }
* },
* www.naver.com={
* From=TOMCAT11, To=www.naver.com, ToSvcType=9050, Histogram={ "1000" : 2, "3000" : 0, "5000" : 0 }
* }
* }
*
* </pre>
*/
@Override
public Map<String, TerminalStatistics> mapRow(Result result, int rowNum) throws Exception {
KeyValue[] keyList = result.raw();
// key is destApplicationName.
Map<String, TerminalStatistics> stat = new HashMap<String, TerminalStatistics>();
// key is destApplicationName
Map<String, Set<String>> destAppHostMap = new HashMap<String, Set<String>>();
for (KeyValue kv : keyList) {
if (kv.getFamilyLength() != HBaseTables.TERMINAL_STATISTICS_CF_COUNTER.length) {
continue;
}
byte[] qualifier = kv.getQualifier();
String srcApplicationName = TerminalSpanUtils.getApplicationNameFromRowKey(kv.getRow());
String destApplicationName = TerminalSpanUtils.getDestApplicationNameFromColumnName(qualifier);
long requestCount = Bytes.toLong(kv.getValue());
short destServiceType = TerminalSpanUtils.getDestServiceTypeFromColumnName(qualifier);
short histogramSlot = TerminalSpanUtils.getHistogramSlotFromColumnName(qualifier);
String host = TerminalSpanUtils.getHost(qualifier);
boolean isError = histogramSlot == (short) -1;
// hostname은 일단 따로 보관.
if (host != null) {
if (destAppHostMap.containsKey(destApplicationName)) {
destAppHostMap.get(destApplicationName).add(host);
} else {
Set<String> set = new HashSet<String>();
set.add(host);
destAppHostMap.put(destApplicationName, set);
}
}
if (stat.containsKey(destApplicationName)) {
TerminalStatistics statistics = stat.get(destApplicationName);
if (isError) {
statistics.getHistogram().incrErrorCount(requestCount);
} else {
statistics.getHistogram().addSample(histogramSlot, requestCount);
}
} else {
TerminalStatistics statistics = new TerminalStatistics(srcApplicationName, destApplicationName, destServiceType);
if (isError) {
statistics.getHistogram().incrErrorCount(requestCount);
} else {
statistics.getHistogram().addSample(histogramSlot, requestCount);
}
stat.put(destApplicationName, statistics);
}
}
// statistics에 dest host정보 삽입.
for (Entry<String, TerminalStatistics> entry : stat.entrySet()) {
entry.getValue().addHosts(destAppHostMap.get(entry.getKey()));
}
return stat;
}
}
@@ -7,73 +7,17 @@ import com.nhn.hippo.web.calltree.server.ServerCallTree;
import com.nhn.hippo.web.vo.Application;
import com.nhn.hippo.web.vo.BusinessTransactions;
import com.nhn.hippo.web.vo.TraceId;
import com.nhn.hippo.web.vo.scatter.Dot;
/**
* @author netspider
*/
public interface FlowChartService {
/**
* select agentIds from application name
*
* @param hosts
* @return
*/
public String[] selectAgentIdsFromApplicationName(String applicationName);
/**
* select traceIds from TraceIndex table
*
* @param agentIds
* @param from
* @param to
* @return
*/
public Set<TraceId> selectTraceIdsFromTraceIndex(String[] agentIds, long from, long to);
/**
* select traceIds from ApplicationTraceIndex table
*
* @param agentIds
* @param from
* @param to
* @return
*/
public Set<TraceId> selectTraceIdsFromApplicationTraceIndex(String applicationName, long from, long to);
/**
* select call tree
*
* @param traceIds
* @return
*/
public ServerCallTree selectServerCallTree(Set<TraceId> traceIds);
/**
*
* @param traceIds
* @param applicationName
* @param from
* @param to
* @return
*/
public ServerCallTree selectServerCallTree(Set<TraceId> traceIds, String applicationName, long from, long to);
/**
* select all application names
*
* @return all of application names
*/
public List<Application> selectAllApplicationNames();
public String[] selectAgentIds(String[] hosts);
public ServerCallTree selectServerCallTree(TraceId traceId);
public List<Dot> selectScatterData(String applicationName, long from, long to);
public List<Dot> selectScatterData(String applicationName, long from, long to, int limit);
public BusinessTransactions selectBusinessTransactions(Set<TraceId> traceIds, String applicationName, long from, long to);
}
@@ -1,11 +1,8 @@
package com.nhn.hippo.web.service;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import org.slf4j.Logger;
@@ -15,23 +12,14 @@ 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.ServerCallTree;
import com.nhn.hippo.web.dao.AgentInfoDao;
import com.nhn.hippo.web.dao.ApplicationIndexDao;
import com.nhn.hippo.web.dao.ApplicationTraceIndexDao;
import com.nhn.hippo.web.dao.ClientStatisticsDao;
import com.nhn.hippo.web.dao.TerminalStatisticsDao;
import com.nhn.hippo.web.dao.TraceDao;
import com.nhn.hippo.web.dao.TraceIndexDao;
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.TerminalStatistics;
import com.nhn.hippo.web.vo.TraceId;
import com.nhn.hippo.web.vo.scatter.Dot;
import com.profiler.common.ServiceType;
import com.profiler.common.bo.AgentInfoBo;
import com.profiler.common.bo.SpanBo;
import com.profiler.common.bo.SpanEventBo;
@@ -46,86 +34,17 @@ public class FlowChartServiceImpl implements FlowChartService {
@Autowired
private TraceDao traceDao;
@Autowired
private TraceIndexDao traceIndexDao;
@Autowired
private ApplicationIndexDao applicationIndexDao;
@Autowired
private ApplicationTraceIndexDao applicationTraceIndexDao;
@Autowired
private TerminalStatisticsDao terminalStatisticsDao;
@Autowired
private ClientStatisticsDao clientStatisticsDao;
@Autowired
private AgentInfoDao agentInfoDao;
@Override
public List<Application> selectAllApplicationNames() {
return applicationIndexDao.selectAllApplicationNames();
}
@Override
public String[] selectAgentIdsFromApplicationName(String applicationName) {
return applicationIndexDao.selectAgentIds(applicationName);
}
@Override
public Set<TraceId> selectTraceIdsFromTraceIndex(String[] agentIds, long from, long to) {
if (agentIds == null) {
throw new NullPointerException("agentIds");
}
if (agentIds.length == 1) {
// single scan
if (logger.isTraceEnabled()) {
logger.trace("scan {}, {}, {}", new Object[] { agentIds[0], from, to });
}
List<List<TraceId>> bytes = this.traceIndexDao.scanTraceIndex(agentIds[0], from, to);
Set<TraceId> result = new HashSet<TraceId>();
for (List<TraceId> list : bytes) {
for (TraceId traceId : list) {
result.add(traceId);
logger.trace("traceid:{}", traceId);
}
}
return result;
} else {
// multi scan 가능한 동일 open htable 에서 액세스함.
List<List<List<TraceId>>> multiScan = this.traceIndexDao.multiScanTraceIndex(agentIds, from, to);
Set<TraceId> result = new HashSet<TraceId>();
for (List<List<TraceId>> list : multiScan) {
for (List<TraceId> scan : list) {
for (TraceId traceId : scan) {
result.add(traceId);
}
}
}
return result;
}
}
@Deprecated
@Override
public ServerCallTree selectServerCallTree(Set<TraceId> traceIds) {
final ServerCallTree tree = new ServerCallTree(new ApplicationIdNodeSelector());
List<List<SpanBo>> traces = this.traceDao.selectSpans(traceIds);
for (List<SpanBo> transaction : traces) {
// List<SpanBo> processed = refine(transaction);
// markRecursiveCall(transaction);
for (SpanBo eachTransaction : transaction) {
tree.addSpan(eachTransaction);
}
}
return tree.build();
}
/**
* DetailView에서 사용함. 하나의 Span을 선택했을때 Draw되는 데이터를 생성하는 함수이다 makes call tree
* of transaction detail view
@@ -145,7 +64,7 @@ public class FlowChartServiceImpl implements FlowChartService {
tree.addSpanEventList(spanEventBoList);
tree.build();
watch.stop();
logger.info("Fetch single transaction serverCallTree elapsed. {}ms", watch.getLastTaskTimeMillis());
@@ -216,150 +135,6 @@ public class FlowChartServiceImpl implements FlowChartService {
return endPointSet;
}
private Set<String> selectApplicationHosts(String applicationId) {
String[] agentIds = applicationIndexDao.selectAgentIds(applicationId);
Set<String> hostnames = new HashSet<String>();
for (String agentId : agentIds) {
// TODO 조회 시간대에 따라서 agent info row timestamp를 변경하여 조회해야하는지는 모르겠음.
AgentInfoBo info = agentInfoDao.findAgentInfoBeforeStartTime(agentId, System.currentTimeMillis());
hostnames.add(info.getHostname());
}
return hostnames;
}
/**
* 메인화면에서 사용. 시간별로 TimeSlot을 조회하여 서버 맵을 그릴 때 사용한다. makes call tree of main
* view
*/
@Override
public ServerCallTree selectServerCallTree(Set<TraceId> traceIds, String applicationName, long from, long to) {
StopWatch watch = new StopWatch();
watch.start();
final Map<String, ServiceType> terminalQueryParams = new HashMap<String, ServiceType>();
final Map<String, ServiceType> clientQueryParams = new HashMap<String, ServiceType>();
final Set<String> hostnameQueryParams = new HashSet<String>();
final ServerCallTree tree = new ServerCallTree(new ApplicationIdNodeSelector());
// fetch non-terminal spans
List<List<SpanBo>> traces = this.traceDao.selectSpans(traceIds);
int totalNonTerminalSpansCount = 0;
Set<String> nonTerminalEndPoints = new HashSet<String>();
// processing spans
for (List<SpanBo> transaction : traces) {
totalNonTerminalSpansCount += transaction.size();
// List<SpanBo> processed = refine(transaction);
// markRecursiveCall(transaction);
for (SpanBo eachTransaction : transaction) {
tree.addSpan(eachTransaction);
// make hostname query params
hostnameQueryParams.add(eachTransaction.getApplicationId());
// make query param
terminalQueryParams.put(eachTransaction.getApplicationId(), eachTransaction.getServiceType());
// make client query param
if (eachTransaction.isRoot()) {
// TODO 여기에서 service type을 CLIENT로 지정해버려서 client유형별로 조회 불가능. 나중에 고쳐야함.
clientQueryParams.put(eachTransaction.getApplicationId(), ServiceType.CLIENT);
}
nonTerminalEndPoints.add(eachTransaction.getEndPoint());
}
}
// fetch terminal info
for (Entry<String, ServiceType> param : terminalQueryParams.entrySet()) {
ServiceType svcType = param.getValue();
if (!svcType.isRpcClient() && !svcType.isUnknown() && !svcType.isTerminal()) {
long start = System.currentTimeMillis();
List<Map<String, TerminalStatistics>> terminals = terminalStatisticsDao.selectTerminal(param.getKey(), from, to);
logger.info(" Fetch terminals of {} : {}ms", param.getKey(), System.currentTimeMillis() - start);
for (Map<String, TerminalStatistics> terminal : terminals) {
for (Entry<String, TerminalStatistics> entry : terminal.entrySet()) {
// TODO 임시방편
TerminalStatistics terminalStatistics = entry.getValue();
// 이 요청의 destination이 수집된 trace정보에 없으면 unknown cloud로 처리한다.
if (!nonTerminalEndPoints.contains(terminalStatistics.getTo())) {
if (ServiceType.findServiceType(terminalStatistics.getToServiceType()).isRpcClient()) {
terminalStatistics.setToServiceType(ServiceType.UNKNOWN_CLOUD.getCode());
}
tree.addTerminalStatistics(terminalStatistics);
}
}
}
}
}
logger.debug("client query params=" + clientQueryParams);
// fetch client info
for (Entry<String, ServiceType> param : clientQueryParams.entrySet()) {
List<Map<String, ClientStatistics>> clients = clientStatisticsDao.selectClient(param.getKey(), param.getValue().getCode(), from, to);
for (Map<String, ClientStatistics> client : clients) {
for (Entry<String, ClientStatistics> clientEntry : client.entrySet()) {
logger.debug("fetched client=" + clientEntry);
tree.addClientStatistics(clientEntry.getValue());
}
}
}
logger.debug("hostname query params=" + hostnameQueryParams);
// fetch hostnames
for (String applicationId : hostnameQueryParams) {
tree.addApplicationHosts(applicationId, selectApplicationHosts(applicationId));
}
tree.build();
watch.stop();
logger.info("Fetch serverCallTree elapsed. {}ms", watch.getLastTaskTimeMillis());
return tree;
}
@Deprecated
private SpanBo findChildSpan(final List<SpanBo> list, final SpanBo parent) {
for (int i = 0; i < list.size(); i++) {
SpanBo child = list.get(i);
if (child.getParentSpanId() == parent.getSpanId()) {
return child;
}
}
return null;
}
/**
* server map이 recursive call을 표현할 수 있게 되어 필요 없음.
*
* @param list
*/
@Deprecated
private void markRecursiveCall(final List<SpanBo> list) {
/*
* for (int i = 0; i < list.size(); i++) { SpanBo a = list.get(i); for
* (int j = 0; j < list.size(); j++) { if (i == j) continue; SpanBo b =
* list.get(j); if (a.getServiceName().equals(b.getServiceName()) &&
* a.getSpanId() == b.getParentSpanId()) {
* a.increaseRecursiveCallCount(); } } }
*/
}
@Override
public Set<TraceId> selectTraceIdsFromApplicationTraceIndex(String applicationName, long from, long to) {
if (applicationName == null) {
@@ -381,55 +156,13 @@ public class FlowChartServiceImpl implements FlowChartService {
return result;
}
@Deprecated
@Override
public String[] selectAgentIds(String[] hosts) {
// List<HbaseColumn> column = new ArrayList<HBaseQuery.HbaseColumn>();
// column.add(new HbaseColumn("Agents", "AgentID"));
//
// HBaseQuery query = new HBaseQuery(HBaseTables.APPLICATION_INDEX,
// null, null, column);
// Iterator<Map<String, byte[]>> iterator = client.getHBaseData(query);
//
// if (logger.isDebugEnabled()) {
// while (iterator.hasNext()) {
// logger.debug("selectedAgentId={}", iterator.next());
// }
// logger.debug("!!!==============WARNING==============!!!");
// logger.debug("!!! selectAgentIds IS NOT IMPLEMENTED !!!");
// logger.debug("!!!===================================!!!");
// }
return hosts;
}
@Override
public List<Dot> selectScatterData(String applicationName, long from, long to) {
List<List<Dot>> scanTrace = applicationTraceIndexDao.scanTraceScatter(applicationName, from, to);
List<Dot> list = new ArrayList<Dot>();
for (List<Dot> l : scanTrace) {
for (Dot dot : l) {
list.add(dot);
}
}
return list;
}
@Override
public List<Dot> selectScatterData(String applicationName, long from, long to, int limit) {
return applicationTraceIndexDao.scanTraceScatter2(applicationName, from, to, limit);
}
@Override
public BusinessTransactions selectBusinessTransactions(Set<TraceId> traceIds, String applicationName, long from, long to) {
List<List<SpanBo>> traceList = this.traceDao.selectSpans(traceIds);
BusinessTransactions businessTransactions = new BusinessTransactions();
for (List<SpanBo> trace : traceList) {
for (SpanBo spanBo : trace ) {
for (SpanBo spanBo : trace) {
// 해당 application으로 인입된 요청만 보여준다.
if (applicationName.equals(spanBo.getApplicationId())) {
businessTransactions.add(spanBo);
@@ -1,10 +1,10 @@
package com.nhn.hippo.web.service;
import java.util.List;
import com.nhn.hippo.web.calltree.span.SpanAlign;
import com.nhn.hippo.web.vo.callstacks.RecordSet;
import java.util.List;
/**
*
*/
@@ -1,6 +1,12 @@
package com.nhn.hippo.web.service;
import java.util.ArrayList;
import java.util.List;
import org.apache.commons.lang.ObjectUtils;
import org.springframework.stereotype.Service;
import com.nhn.hippo.web.calltree.span.SpanAlign;
import com.nhn.hippo.web.vo.callstacks.Record;
import com.nhn.hippo.web.vo.callstacks.RecordSet;
@@ -11,11 +17,6 @@ import com.profiler.common.bo.SpanEventBo;
import com.profiler.common.util.AnnotationUtils;
import com.profiler.common.util.ApiDescription;
import com.profiler.common.util.ApiDescriptionParser;
import org.apache.commons.lang.ObjectUtils;
import org.springframework.stereotype.Service;
import java.util.ArrayList;
import java.util.List;
/**
*
@@ -0,0 +1,13 @@
package com.nhn.hippo.web.service;
import java.util.List;
import com.nhn.hippo.web.vo.TransactionMetadataQuery;
import com.nhn.hippo.web.vo.scatter.Dot;
import com.profiler.common.bo.SpanBo;
public interface ScatterChartService {
public List<Dot> selectScatterData(String applicationName, long from, long to, int limit);
public List<SpanBo> selectTransactionMetadata(TransactionMetadataQuery query);
}
@@ -0,0 +1,71 @@
package com.nhn.hippo.web.service;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.nhn.hippo.web.dao.ApplicationTraceIndexDao;
import com.nhn.hippo.web.dao.TraceDao;
import com.nhn.hippo.web.vo.TransactionMetadataQuery;
import com.nhn.hippo.web.vo.scatter.Dot;
import com.profiler.common.bo.SpanBo;
/**
* @author netspider
*/
@Service
public class ScatterChartServiceImpl implements ScatterChartService {
@Autowired
private ApplicationTraceIndexDao applicationTraceIndexDao;
@Autowired
private TraceDao traceDao;
@Override
public List<Dot> selectScatterData(String applicationName, long from, long to, int limit) {
return applicationTraceIndexDao.scanTraceScatter2(applicationName, from, to, limit);
}
/**
* scatter chart에서 선택한 점에 대한 정보를 조회 하는 메소드.
*/
@Override
public List<SpanBo> selectTransactionMetadata(TransactionMetadataQuery query) {
List<List<SpanBo>> selectedSpans = traceDao.selectSpans(query.getTraceIds());
List<SpanBo> result = new ArrayList<SpanBo>(query.size());
// 조회된 녀석들 중에서 UUID, starttime, responseTime이 같은것들만 골라냄.
for (List<SpanBo> spans : selectedSpans) {
for (SpanBo span : spans) {
// check UUID and time
if (query.isExists(span.getMostTraceId(), span.getLeastTraceId(), span.getCollectorAcceptTime(), span.getElapsed())) {
result.add(span);
}
}
}
// TODO 일단 임시로...
Collections.sort(result, new Comparator<SpanBo>() {
@Override
public int compare(SpanBo o1, SpanBo o2) {
if (o1.getException() != 0 && o2.getException() != 0) {
return o2.getElapsed() - o1.getElapsed();
} else if (o1.getException() != 0) {
return -1;
} else if (o2.getException() != 0) {
return 1;
} else {
return o2.getElapsed() - o1.getElapsed();
}
}
});
return result;
}
}
@@ -3,15 +3,11 @@ package com.nhn.hippo.web.service;
import java.util.List;
import com.nhn.hippo.web.calltree.span.SpanAlign;
import com.nhn.hippo.web.vo.RequestMetadataQuery;
import com.nhn.hippo.web.vo.TraceId;
import com.profiler.common.bo.SpanBo;
/**
*
*/
public interface SpanService {
List<SpanAlign> selectSpan(TraceId traceId);
List<SpanBo> selectRequestMetadata(RequestMetadataQuery query);
}
@@ -1,12 +1,8 @@
package com.nhn.hippo.web.service;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import com.nhn.hippo.web.vo.TraceId;
import com.profiler.common.AnnotationKey;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
@@ -18,7 +14,8 @@ import com.nhn.hippo.web.dao.AgentInfoDao;
import com.nhn.hippo.web.dao.ApiMetaDataDao;
import com.nhn.hippo.web.dao.SqlMetaDataDao;
import com.nhn.hippo.web.dao.TraceDao;
import com.nhn.hippo.web.vo.RequestMetadataQuery;
import com.nhn.hippo.web.vo.TraceId;
import com.profiler.common.AnnotationKey;
import com.profiler.common.bo.AgentInfoBo;
import com.profiler.common.bo.AnnotationBo;
import com.profiler.common.bo.ApiMetaDataBo;
@@ -36,379 +33,335 @@ import com.profiler.common.util.SqlParser;
@Service
public class SpanServiceImpl implements SpanService {
private Logger logger = LoggerFactory.getLogger(this.getClass());
private Logger logger = LoggerFactory.getLogger(this.getClass());
@Autowired
private TraceDao traceDao;
@Autowired
private TraceDao traceDao;
@Autowired
private SqlMetaDataDao sqlMetaDataDao;
@Autowired
private SqlMetaDataDao sqlMetaDataDao;
@Autowired
private ApiMetaDataDao apiMetaDataDao;
@Autowired
private ApiMetaDataDao apiMetaDataDao;
@Autowired
private AgentInfoDao agentInfoDao;
@Autowired
private AgentInfoDao agentInfoDao;
private SqlParser sqlParser = new SqlParser();
private OutputParameterParser outputParameterParser = new OutputParameterParser();
private SqlParser sqlParser = new SqlParser();
private OutputParameterParser outputParameterParser = new OutputParameterParser();
@Override
public List<SpanAlign> selectSpan(TraceId traceId) {
@Override
public List<SpanAlign> selectSpan(TraceId traceId) {
List<SpanBo> spans = traceDao.selectSpanAndAnnotation(traceId);
if (spans == null || spans.isEmpty()) {
return Collections.emptyList();
}
List<SpanBo> spans = traceDao.selectSpanAndAnnotation(traceId);
if (spans == null || spans.isEmpty()) {
return Collections.emptyList();
}
List<SpanAlign> order = order(spans);
transitionApiId(order);
transitionDynamicApiId(order);
transitionSqlId(order);
// TODO root span not found시 row data라도 보여줘야 됨.
List<SpanAlign> order = order(spans);
transitionApiId(order);
transitionDynamicApiId(order);
transitionSqlId(order);
// TODO root span not found시 row data라도 보여줘야 됨.
return order;
}
return order;
}
private void transitionAnnotation(List<SpanAlign> spans, AnnotationReplacementCallback annotationReplacementCallback) {
for (SpanAlign spanAlign : spans) {
List<AnnotationBo> annotationBoList;
if (spanAlign.isSpan()) {
annotationBoList = spanAlign.getSpanBo().getAnnotationBoList();
annotationReplacementCallback.replacement(spanAlign, annotationBoList);
} else {
annotationBoList = spanAlign.getSpanEventBo().getAnnotationBoList();
annotationReplacementCallback.replacement(spanAlign, annotationBoList);
}
}
}
private void transitionAnnotation(List<SpanAlign> spans, AnnotationReplacementCallback annotationReplacementCallback) {
for (SpanAlign spanAlign : spans) {
List<AnnotationBo> annotationBoList;
if (spanAlign.isSpan()) {
annotationBoList = spanAlign.getSpanBo().getAnnotationBoList();
annotationReplacementCallback.replacement(spanAlign, annotationBoList);
} else {
annotationBoList = spanAlign.getSpanEventBo().getAnnotationBoList();
annotationReplacementCallback.replacement(spanAlign, annotationBoList);
}
}
}
private void transitionSqlId(final List<SpanAlign> spans) {
this.transitionAnnotation(spans, new AnnotationReplacementCallback() {
@Override
public void replacement(SpanAlign spanAlign, List<AnnotationBo> annotationBoList) {
AnnotationBo sqlIdAnnotation = findAnnotation(annotationBoList, AnnotationKey.SQL_ID.getCode());
if (sqlIdAnnotation == null) {
return;
}
AgentInfoBo agentInfoBo = null;
try {
agentInfoBo = findAgentInfoBoBeforeStartTime(spanAlign);
logger.info("{} Agent StartTime found:{}", agentInfoBo.getAgentId(), agentInfoBo);
} catch (AgentIdNotFoundException ex) {
AnnotationBo agentInfoNotFound = new AnnotationBo();
agentInfoNotFound.setKey(AnnotationKey.SQL.getCode());
agentInfoNotFound.setValue("SQL-ID not found. Cause:agentInfo not found. agentId:" + ex.getAgentId() + " startTime:" + ex.getStartTime());
annotationBoList.add(agentInfoNotFound);
return;
}
// TODO 일단 시간까지 조회는 하지 말고 하자.
// 미리 sqlMetaDataList를 indentifier로 필터치는 로직이 더 좋을것으로 생각됨.
int hashCode = (Integer) sqlIdAnnotation.getValue();
List<SqlMetaDataBo> sqlMetaDataList = sqlMetaDataDao.getSqlMetaData(agentInfoBo.getAgentId(), agentInfoBo.getIdentifier(), hashCode, agentInfoBo.getTimestamp());
int size = sqlMetaDataList.size();
if (size == 0) {
AnnotationBo api = new AnnotationBo();
api.setKey(AnnotationKey.SQL.getCode());
api.setValue("SQL-ID not found hashCode:" + hashCode);
annotationBoList.add(api);
} else if (size == 1) {
AnnotationBo sqlParamAnnotationBo = findAnnotation(annotationBoList, AnnotationKey.SQL_PARAM.getCode());
final SqlMetaDataBo sqlMetaDataBo = sqlMetaDataList.get(0);
if (sqlParamAnnotationBo == null) {
AnnotationBo sqlMeta = new AnnotationBo();
sqlMeta.setKey(AnnotationKey.SQL_METADATA.getCode());
sqlMeta.setValue(sqlMetaDataBo.getSql());
annotationBoList.add(sqlMeta);
AnnotationBo checkFail = checkIdentifier (spanAlign, sqlMetaDataBo);
if (checkFail != null) {
// 실패
annotationBoList.add(checkFail);
return;
}
AnnotationBo sql = new AnnotationBo();
sql.setKey(AnnotationKey.SQL.getCode());
sql.setValue(sqlMetaDataBo.getSql());
annotationBoList.add(sql);
} else {
logger.debug("sqlMetaDataBo:{}", sqlMetaDataBo);
String outputParams = (String) sqlParamAnnotationBo.getValue();
List<String> parsedOutputParams = outputParameterParser.parseOutputParameter(outputParams);
logger.debug("outputPrams:{}, parsedOutputPrams:{}", outputParams, parsedOutputParams);
String originalSql = sqlParser.combineOutputParams(sqlMetaDataBo.getSql(), parsedOutputParams);
logger.debug("outputPrams{}, originalSql:{}", outputParams, originalSql);
AnnotationBo sqlMeta = new AnnotationBo();
sqlMeta.setKey(AnnotationKey.SQL_METADATA.getCode());
sqlMeta.setValue(sqlMetaDataBo.getSql());
annotationBoList.add(sqlMeta);
AnnotationBo checkFail = checkIdentifier (spanAlign, sqlMetaDataBo);
if (checkFail != null) {
// 실패
annotationBoList.add(checkFail);
return;
}
AnnotationBo sql = new AnnotationBo();
sql.setKey(AnnotationKey.SQL.getCode());
sql.setValue(originalSql);
annotationBoList.add(sql);
}
} else {
// TODO 보완해야됨.
AnnotationBo api = new AnnotationBo();
api.setKey(AnnotationKey.SQL.getCode());
api.setValue(collisionSqlHashCodeMessage(hashCode, sqlMetaDataList));
annotationBoList.add(api);
}
}
private AnnotationBo checkIdentifier(SpanAlign spanAlign, SqlMetaDataBo sqlMetaDataBo) {
short agentIdentifier = getAgentIdentifier(spanAlign);
short sqlIdentifier = sqlMetaDataBo.getIdentifier();
if (agentIdentifier == sqlIdentifier) {
return null;
}
AnnotationBo identifierCheckFail = new AnnotationBo();
identifierCheckFail.setKey(AnnotationKey.SQL.getCode());
identifierCheckFail.setValue("invalid SqlMetaInfo:" + sqlMetaDataBo);
return identifierCheckFail;
}
});
}
private AnnotationBo findAnnotation(List<AnnotationBo> annotationBoList, int key) {
for (AnnotationBo annotationBo : annotationBoList) {
if (key == annotationBo.getKey()) {
return annotationBo;
}
}
return null;
}
private String collisionSqlHashCodeMessage(int hashCode, List<SqlMetaDataBo> sqlMetaDataList) {
// TODO 이거 체크하는 테스트를 따로 만들어야 될듯 하다. 왠간하면 확율상 hashCode 충돌 케이스를 쉽게 만들수 없음.
StringBuilder sb = new StringBuilder(64);
sb.append("Collision Sql hashCode:");
sb.append(hashCode);
sb.append('\n');
for (int i = 0; i < sqlMetaDataList.size(); i++) {
if (i != 0) {
sb.append("or\n");
}
SqlMetaDataBo sqlMetaDataBo = sqlMetaDataList.get(i);
sb.append(sqlMetaDataBo.getSql());
}
return sb.toString();
}
private String getAgentId(SpanAlign spanAlign) {
if (spanAlign.isSpan()) {
return spanAlign.getSpanBo().getAgentId();
} else {
return spanAlign.getSpanEventBo().getAgentId();
}
}
private short getAgentIdentifier(SpanAlign spanAlign) {
if (spanAlign.isSpan()) {
return spanAlign.getSpanBo().getAgentIdentifier();
} else {
return spanAlign.getSpanEventBo().getAgentIdentifier();
}
}
private void transitionDynamicApiId(List<SpanAlign> spans) {
this.transitionAnnotation(spans, new AnnotationReplacementCallback() {
@Override
public void replacement(SpanAlign spanAlign, List<AnnotationBo> annotationBoList) {
AnnotationBo apiIdAnnotation = findAnnotation(annotationBoList, AnnotationKey.API_DID.getCode());
if (apiIdAnnotation == null) {
return;
}
AgentInfoBo agentInfoBo = null;
try {
agentInfoBo = findAgentInfoBoBeforeStartTime(spanAlign);
logger.info("{} Agent StartTime found:{}", agentInfoBo.getAgentId(), agentInfoBo);
} catch (AgentIdNotFoundException ex) {
AnnotationBo agentInfoNotFound = new AnnotationBo();
agentInfoNotFound.setKey(AnnotationKey.ERROR_API_METADATA_AGENT_INFO_NOT_FOUND.getCode());
agentInfoNotFound.setValue("API-DynamicID not found. Cause:agentInfo not found. agentId:" + ex.getAgentId() + " startTime:" + ex.getStartTime());
annotationBoList.add(agentInfoNotFound);
return;
}
int apiId = (Integer) apiIdAnnotation.getValue();
List<ApiMetaDataBo> apiMetaDataList = apiMetaDataDao.getApiMetaData(agentInfoBo.getAgentId(), agentInfoBo.getIdentifier(), apiId, agentInfoBo.getTimestamp());
int size = apiMetaDataList.size();
if (size == 0) {
AnnotationBo api = new AnnotationBo();
api.setKey(AnnotationKey.ERROR_API_METADATA_NOT_FOUND.getCode());
api.setValue("API-DynamicID not found. api:" + apiId);
annotationBoList.add(api);
} else if (size == 1) {
ApiMetaDataBo apiMetaDataBo = apiMetaDataList.get(0);
AnnotationBo apiMetaData = new AnnotationBo();
apiMetaData.setKey(AnnotationKey.API_METADATA.getCode());
apiMetaData.setValue(apiMetaDataBo);
annotationBoList.add(apiMetaData);
AnnotationBo checkFail = checkIdentifier (spanAlign, apiMetaDataBo);
if (checkFail != null) {
// 실패
annotationBoList.add(checkFail);
return;
}
AnnotationBo apiAnnotation = new AnnotationBo();
apiAnnotation.setKey(AnnotationKey.API.getCode());
String apiInfo = getApiInfo(apiMetaDataBo);
apiAnnotation.setValue(apiInfo);
annotationBoList.add(apiAnnotation);
} else {
AnnotationBo apiAnnotation = new AnnotationBo();
apiAnnotation.setKey(AnnotationKey.ERROR_API_METADATA_DID_COLLSION.getCode());
String collisonMessage = collisionApiDidMessage(apiId, apiMetaDataList);
apiAnnotation.setValue(collisonMessage);
annotationBoList.add(apiAnnotation);
}
}
private AnnotationBo checkIdentifier(SpanAlign spanAlign, ApiMetaDataBo apiMetaDataBo) {
short agentIdentifier = getAgentIdentifier(spanAlign);
short sqlIdentifier = apiMetaDataBo.getIdentifier();
if (agentIdentifier == sqlIdentifier) {
return null;
}
AnnotationBo identifierCheckFail = new AnnotationBo();
identifierCheckFail.setKey(AnnotationKey.ERROR_API_METADATA_IDENTIFIER_CHECK_ERROR.getCode());
identifierCheckFail.setValue("invalid ApiMetaInfo:" + apiMetaDataBo);
return identifierCheckFail;
}
});
}
private AgentInfoBo findAgentInfoBoBeforeStartTime(SpanAlign spanAlign) {
String agentId = getAgentId(spanAlign);
long startTime = spanAlign.getSpanBo().getStartTime();
AgentInfoBo agentInfoBeforeStartTime = agentInfoDao.findAgentInfoBeforeStartTime(agentId, startTime);
if (agentInfoBeforeStartTime == null) {
throw new AgentIdNotFoundException(agentId, startTime);
}
return agentInfoBeforeStartTime;
}
private String collisionApiDidMessage(int apidId, List<ApiMetaDataBo> apiMetaDataList) {
// TODO 이거 체크하는 테스트를 따로 만들어야 될듯 하다. 왠간하면 확율상 hashCode 충돌 케이스를 쉽게 만들수 없음.
StringBuilder sb = new StringBuilder(64);
sb.append("Collision Api DynamicId:");
sb.append(apidId);
sb.append('\n');
for (int i = 0; i < apiMetaDataList.size(); i++) {
if (i != 0) {
sb.append("or\n");
}
ApiMetaDataBo apiMetaDataBo = apiMetaDataList.get(i);
sb.append(getApiInfo(apiMetaDataBo));
}
return sb.toString();
}
private String getApiInfo(ApiMetaDataBo apiMetaDataBo) {
if (apiMetaDataBo.getLineNumber() != -1) {
return apiMetaDataBo.getApiInfo() + ":" + apiMetaDataBo.getLineNumber();
} else {
return apiMetaDataBo.getApiInfo();
}
}
private void transitionApiId(List<SpanAlign> spans) {
this.transitionAnnotation(spans, new AnnotationReplacementCallback() {
@Override
public void replacement(SpanAlign spanAlign, List<AnnotationBo> annotationBoList) {
AnnotationBo apiIdAnnotation = findAnnotation(annotationBoList, AnnotationKey.API_ID.getCode());
if (apiIdAnnotation == null) {
return;
}
MethodMapping methodMapping = ApiMappingTable.findMethodMapping((Integer) apiIdAnnotation.getValue());
if (methodMapping == null) {
return;
}
String className = methodMapping.getClassMapping().getClassName();
String methodName = methodMapping.getMethodName();
String[] parameterType = methodMapping.getParameterType();
String[] parameterName = methodMapping.getParameterName();
String args = ApiUtils.mergeParameterVariableNameDescription(parameterType, parameterName);
AnnotationBo api = new AnnotationBo();
api.setKey(AnnotationKey.API.getCode());
api.setValue(className + "." + methodName + args);
annotationBoList.add(api);
}
});
}
public static interface AnnotationReplacementCallback {
void replacement(SpanAlign spanAlign, List<AnnotationBo> annotationBoList);
}
private List<SpanAlign> order(List<SpanBo> spans) {
SpanAligner2 spanAligner = new SpanAligner2(spans);
return spanAligner.sort();
}
@Override
public List<SpanBo> selectRequestMetadata(RequestMetadataQuery query) {
List<List<SpanBo>> selectedSpans = traceDao.selectSpans(query.getTraceIds());
List<SpanBo> result = new ArrayList<SpanBo>(query.size());
// 조회된 녀석들 중에서 UUID, starttime, responseTime이 같은것들만 골라냄.
for (List<SpanBo> spans : selectedSpans) {
for (SpanBo span : spans) {
// check UUID and time
if (query.isExists(span.getMostTraceId(), span.getLeastTraceId(), span.getCollectorAcceptTime(), span.getElapsed())) {
result.add(span);
}
}
}
// TODO 일단 임시로...
Collections.sort(result, new Comparator<SpanBo>() {
private void transitionSqlId(final List<SpanAlign> spans) {
this.transitionAnnotation(spans, new AnnotationReplacementCallback() {
@Override
public int compare(SpanBo o1, SpanBo o2) {
if (o1.getException() != 0 && o2.getException() != 0) {
return o2.getElapsed() - o1.getElapsed();
} else if (o1.getException() != 0) {
return -1;
} else if (o2.getException() != 0) {
return 1;
} else {
return o2.getElapsed() - o1.getElapsed();
public void replacement(SpanAlign spanAlign, List<AnnotationBo> annotationBoList) {
AnnotationBo sqlIdAnnotation = findAnnotation(annotationBoList, AnnotationKey.SQL_ID.getCode());
if (sqlIdAnnotation == null) {
return;
}
AgentInfoBo agentInfoBo = null;
try {
agentInfoBo = findAgentInfoBoBeforeStartTime(spanAlign);
logger.info("{} Agent StartTime found:{}", agentInfoBo.getAgentId(), agentInfoBo);
} catch (AgentIdNotFoundException ex) {
AnnotationBo agentInfoNotFound = new AnnotationBo();
agentInfoNotFound.setKey(AnnotationKey.SQL.getCode());
agentInfoNotFound.setValue("SQL-ID not found. Cause:agentInfo not found. agentId:" + ex.getAgentId() + " startTime:" + ex.getStartTime());
annotationBoList.add(agentInfoNotFound);
return;
}
// TODO 일단 시간까지 조회는 하지 말고 하자.
// 미리 sqlMetaDataList를 indentifier로 필터치는 로직이 더 좋을것으로 생각됨.
int hashCode = (Integer) sqlIdAnnotation.getValue();
List<SqlMetaDataBo> sqlMetaDataList = sqlMetaDataDao.getSqlMetaData(agentInfoBo.getAgentId(), agentInfoBo.getIdentifier(), hashCode, agentInfoBo.getTimestamp());
int size = sqlMetaDataList.size();
if (size == 0) {
AnnotationBo api = new AnnotationBo();
api.setKey(AnnotationKey.SQL.getCode());
api.setValue("SQL-ID not found hashCode:" + hashCode);
annotationBoList.add(api);
} else if (size == 1) {
AnnotationBo sqlParamAnnotationBo = findAnnotation(annotationBoList, AnnotationKey.SQL_PARAM.getCode());
final SqlMetaDataBo sqlMetaDataBo = sqlMetaDataList.get(0);
if (sqlParamAnnotationBo == null) {
AnnotationBo sqlMeta = new AnnotationBo();
sqlMeta.setKey(AnnotationKey.SQL_METADATA.getCode());
sqlMeta.setValue(sqlMetaDataBo.getSql());
annotationBoList.add(sqlMeta);
AnnotationBo checkFail = checkIdentifier(spanAlign, sqlMetaDataBo);
if (checkFail != null) {
// 실패
annotationBoList.add(checkFail);
return;
}
AnnotationBo sql = new AnnotationBo();
sql.setKey(AnnotationKey.SQL.getCode());
sql.setValue(sqlMetaDataBo.getSql());
annotationBoList.add(sql);
} else {
logger.debug("sqlMetaDataBo:{}", sqlMetaDataBo);
String outputParams = (String) sqlParamAnnotationBo.getValue();
List<String> parsedOutputParams = outputParameterParser.parseOutputParameter(outputParams);
logger.debug("outputPrams:{}, parsedOutputPrams:{}", outputParams, parsedOutputParams);
String originalSql = sqlParser.combineOutputParams(sqlMetaDataBo.getSql(), parsedOutputParams);
logger.debug("outputPrams{}, originalSql:{}", outputParams, originalSql);
AnnotationBo sqlMeta = new AnnotationBo();
sqlMeta.setKey(AnnotationKey.SQL_METADATA.getCode());
sqlMeta.setValue(sqlMetaDataBo.getSql());
annotationBoList.add(sqlMeta);
AnnotationBo checkFail = checkIdentifier(spanAlign, sqlMetaDataBo);
if (checkFail != null) {
// 실패
annotationBoList.add(checkFail);
return;
}
AnnotationBo sql = new AnnotationBo();
sql.setKey(AnnotationKey.SQL.getCode());
sql.setValue(originalSql);
annotationBoList.add(sql);
}
} else {
// TODO 보완해야됨.
AnnotationBo api = new AnnotationBo();
api.setKey(AnnotationKey.SQL.getCode());
api.setValue(collisionSqlHashCodeMessage(hashCode, sqlMetaDataList));
annotationBoList.add(api);
}
}
private AnnotationBo checkIdentifier(SpanAlign spanAlign, SqlMetaDataBo sqlMetaDataBo) {
short agentIdentifier = getAgentIdentifier(spanAlign);
short sqlIdentifier = sqlMetaDataBo.getIdentifier();
if (agentIdentifier == sqlIdentifier) {
return null;
}
AnnotationBo identifierCheckFail = new AnnotationBo();
identifierCheckFail.setKey(AnnotationKey.SQL.getCode());
identifierCheckFail.setValue("invalid SqlMetaInfo:" + sqlMetaDataBo);
return identifierCheckFail;
}
});
return result;
}
}
private AnnotationBo findAnnotation(List<AnnotationBo> annotationBoList, int key) {
for (AnnotationBo annotationBo : annotationBoList) {
if (key == annotationBo.getKey()) {
return annotationBo;
}
}
return null;
}
private String collisionSqlHashCodeMessage(int hashCode, List<SqlMetaDataBo> sqlMetaDataList) {
// TODO 이거 체크하는 테스트를 따로 만들어야 될듯 하다. 왠간하면 확율상 hashCode 충돌 케이스를 쉽게 만들수 없음.
StringBuilder sb = new StringBuilder(64);
sb.append("Collision Sql hashCode:");
sb.append(hashCode);
sb.append('\n');
for (int i = 0; i < sqlMetaDataList.size(); i++) {
if (i != 0) {
sb.append("or\n");
}
SqlMetaDataBo sqlMetaDataBo = sqlMetaDataList.get(i);
sb.append(sqlMetaDataBo.getSql());
}
return sb.toString();
}
private String getAgentId(SpanAlign spanAlign) {
if (spanAlign.isSpan()) {
return spanAlign.getSpanBo().getAgentId();
} else {
return spanAlign.getSpanEventBo().getAgentId();
}
}
private short getAgentIdentifier(SpanAlign spanAlign) {
if (spanAlign.isSpan()) {
return spanAlign.getSpanBo().getAgentIdentifier();
} else {
return spanAlign.getSpanEventBo().getAgentIdentifier();
}
}
private void transitionDynamicApiId(List<SpanAlign> spans) {
this.transitionAnnotation(spans, new AnnotationReplacementCallback() {
@Override
public void replacement(SpanAlign spanAlign, List<AnnotationBo> annotationBoList) {
AnnotationBo apiIdAnnotation = findAnnotation(annotationBoList, AnnotationKey.API_DID.getCode());
if (apiIdAnnotation == null) {
return;
}
AgentInfoBo agentInfoBo = null;
try {
agentInfoBo = findAgentInfoBoBeforeStartTime(spanAlign);
logger.info("{} Agent StartTime found:{}", agentInfoBo.getAgentId(), agentInfoBo);
} catch (AgentIdNotFoundException ex) {
AnnotationBo agentInfoNotFound = new AnnotationBo();
agentInfoNotFound.setKey(AnnotationKey.ERROR_API_METADATA_AGENT_INFO_NOT_FOUND.getCode());
agentInfoNotFound.setValue("API-DynamicID not found. Cause:agentInfo not found. agentId:" + ex.getAgentId() + " startTime:" + ex.getStartTime());
annotationBoList.add(agentInfoNotFound);
return;
}
int apiId = (Integer) apiIdAnnotation.getValue();
List<ApiMetaDataBo> apiMetaDataList = apiMetaDataDao.getApiMetaData(agentInfoBo.getAgentId(), agentInfoBo.getIdentifier(), apiId, agentInfoBo.getTimestamp());
int size = apiMetaDataList.size();
if (size == 0) {
AnnotationBo api = new AnnotationBo();
api.setKey(AnnotationKey.ERROR_API_METADATA_NOT_FOUND.getCode());
api.setValue("API-DynamicID not found. api:" + apiId);
annotationBoList.add(api);
} else if (size == 1) {
ApiMetaDataBo apiMetaDataBo = apiMetaDataList.get(0);
AnnotationBo apiMetaData = new AnnotationBo();
apiMetaData.setKey(AnnotationKey.API_METADATA.getCode());
apiMetaData.setValue(apiMetaDataBo);
annotationBoList.add(apiMetaData);
AnnotationBo checkFail = checkIdentifier(spanAlign, apiMetaDataBo);
if (checkFail != null) {
// 실패
annotationBoList.add(checkFail);
return;
}
AnnotationBo apiAnnotation = new AnnotationBo();
apiAnnotation.setKey(AnnotationKey.API.getCode());
String apiInfo = getApiInfo(apiMetaDataBo);
apiAnnotation.setValue(apiInfo);
annotationBoList.add(apiAnnotation);
} else {
AnnotationBo apiAnnotation = new AnnotationBo();
apiAnnotation.setKey(AnnotationKey.ERROR_API_METADATA_DID_COLLSION.getCode());
String collisonMessage = collisionApiDidMessage(apiId, apiMetaDataList);
apiAnnotation.setValue(collisonMessage);
annotationBoList.add(apiAnnotation);
}
}
private AnnotationBo checkIdentifier(SpanAlign spanAlign, ApiMetaDataBo apiMetaDataBo) {
short agentIdentifier = getAgentIdentifier(spanAlign);
short sqlIdentifier = apiMetaDataBo.getIdentifier();
if (agentIdentifier == sqlIdentifier) {
return null;
}
AnnotationBo identifierCheckFail = new AnnotationBo();
identifierCheckFail.setKey(AnnotationKey.ERROR_API_METADATA_IDENTIFIER_CHECK_ERROR.getCode());
identifierCheckFail.setValue("invalid ApiMetaInfo:" + apiMetaDataBo);
return identifierCheckFail;
}
});
}
private AgentInfoBo findAgentInfoBoBeforeStartTime(SpanAlign spanAlign) {
String agentId = getAgentId(spanAlign);
long startTime = spanAlign.getSpanBo().getStartTime();
AgentInfoBo agentInfoBeforeStartTime = agentInfoDao.findAgentInfoBeforeStartTime(agentId, startTime);
if (agentInfoBeforeStartTime == null) {
throw new AgentIdNotFoundException(agentId, startTime);
}
return agentInfoBeforeStartTime;
}
private String collisionApiDidMessage(int apidId, List<ApiMetaDataBo> apiMetaDataList) {
// TODO 이거 체크하는 테스트를 따로 만들어야 될듯 하다. 왠간하면 확율상 hashCode 충돌 케이스를 쉽게 만들수 없음.
StringBuilder sb = new StringBuilder(64);
sb.append("Collision Api DynamicId:");
sb.append(apidId);
sb.append('\n');
for (int i = 0; i < apiMetaDataList.size(); i++) {
if (i != 0) {
sb.append("or\n");
}
ApiMetaDataBo apiMetaDataBo = apiMetaDataList.get(i);
sb.append(getApiInfo(apiMetaDataBo));
}
return sb.toString();
}
private String getApiInfo(ApiMetaDataBo apiMetaDataBo) {
if (apiMetaDataBo.getLineNumber() != -1) {
return apiMetaDataBo.getApiInfo() + ":" + apiMetaDataBo.getLineNumber();
} else {
return apiMetaDataBo.getApiInfo();
}
}
private void transitionApiId(List<SpanAlign> spans) {
this.transitionAnnotation(spans, new AnnotationReplacementCallback() {
@Override
public void replacement(SpanAlign spanAlign, List<AnnotationBo> annotationBoList) {
AnnotationBo apiIdAnnotation = findAnnotation(annotationBoList, AnnotationKey.API_ID.getCode());
if (apiIdAnnotation == null) {
return;
}
MethodMapping methodMapping = ApiMappingTable.findMethodMapping((Integer) apiIdAnnotation.getValue());
if (methodMapping == null) {
return;
}
String className = methodMapping.getClassMapping().getClassName();
String methodName = methodMapping.getMethodName();
String[] parameterType = methodMapping.getParameterType();
String[] parameterName = methodMapping.getParameterName();
String args = ApiUtils.mergeParameterVariableNameDescription(parameterType, parameterName);
AnnotationBo api = new AnnotationBo();
api.setKey(AnnotationKey.API.getCode());
api.setValue(className + "." + methodName + args);
annotationBoList.add(api);
}
});
}
public static interface AnnotationReplacementCallback {
void replacement(SpanAlign spanAlign, List<AnnotationBo> annotationBoList);
}
private List<SpanAlign> order(List<SpanBo> spans) {
SpanAligner2 spanAligner = new SpanAligner2(spans);
return spanAligner.sort();
}
}
@@ -1,51 +0,0 @@
package com.nhn.hippo.web.vo;
import com.profiler.common.util.TraceIdUtils;
/**
* UI로 이 객체 대신 SpanBO를 던진다.
*
* @author netspider
*
*/
@Deprecated
public class RequestMetadata {
private final String traceId;
private final long startTime;
private final int elapsed;
private final String application;
public RequestMetadata(long mostTraceId, long leastTraceId, long startTime, int elapsed, String application) {
this.traceId = TraceIdUtils.formatString(mostTraceId, leastTraceId);
this.startTime = startTime;
this.elapsed = elapsed;
this.application = application;
}
public String getTraceId() {
return traceId;
}
public long getStartTime() {
return startTime;
}
public int getElapsed() {
return elapsed;
}
public String getApplication() {
return application;
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append(traceId);
sb.append(startTime);
sb.append(elapsed);
sb.append(application);
return sb.toString();
}
}
@@ -13,12 +13,12 @@ import java.util.Set;
* @author netspider
*
*/
public class RequestMetadataQuery {
public class TransactionMetadataQuery {
private final Map<QueryCondition, Object> queryConditions;
public RequestMetadataQuery() {
queryConditions = new HashMap<RequestMetadataQuery.QueryCondition, Object>();
public TransactionMetadataQuery() {
queryConditions = new HashMap<TransactionMetadataQuery.QueryCondition, Object>();
}
public void addQueryCondition(String traceId, long time, int responseTime) {
@@ -1,32 +0,0 @@
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %>
{
"graphdata" : {
"nodes" : [
<c:forEach items="${nodes}" var="node" varStatus="status">
{
"name" : "${node}",
"agentIds" : [
<c:forEach items="${node.agentIds}" var="agentId" varStatus="status2">
"${agentId}"
<c:if test="${!status2.last}">,</c:if>
</c:forEach>
],
"serviceType" : "${node.serviceType}",
"terminal" : "${node.serviceType.terminal}"
} <c:if test="${!status.last}">,</c:if>
</c:forEach>
],
"links" : [
<c:forEach items="${links}" var="link" varStatus="status">
{
"source" : ${link.from.sequence},
"target" : ${link.to.sequence},
"value" : ${link.requestCount},
"histogram" : ${link.histogram}
} <c:if test="${!status.last}">,</c:if>
</c:forEach>
]
}
}
@@ -1,331 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<title>HIPPO</title>
<meta charset="utf-8">
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link href="/common/css/bootstrap/bootstrap.css" rel="stylesheet">
<link href="/common/css/bootstrap/bootstrap-responsive.css" rel="stylesheet"/>
<link href="/common/css/hippo/hippo.css" rel="stylesheet"/>
<link href="/common/css/datepicker.css" rel="stylesheet"/>
<link href="/select2/select2-customized.css" rel="stylesheet"/>
<!-- Le HTML5 shim, for IE6-8 support of HTML5 elements -->
<!--[if lt IE 9]>
<script src="http://html5shim.googlecode.com/svn/trunk/html5.js"></script>
<![endif]-->
<script type="text/javascript" src="/common/js/jquery/jquery-1.7.1.min.js"></script>
<script type="text/javascript" src="/common/js/jquery/jquery-ui-1.8.18.custom.min.js"></script>
<script type="text/javascript" src="/select2/select2.js"></script>
<script type="text/javascript" src="/common/js/bootstrap.min.js"></script>
<script type="text/javascript" src="/common/js/modernizr-2.6.2.min.js"></script>
<script type="text/javascript" src="/common/js/bootstrap-datepicker.js"></script>
<script type="text/javascript" src="/common/js/d3.js"></script>
<script type="text/javascript" src="/common/js/d3.chart.js"></script>
<script type="text/javascript" src="/common/js/d3.chart.scatter.js"></script>
<script type="text/javascript" src="/common/js/hippo/chart-scatter2.js"></script>
<script type="text/javascript" src="/common/js/hippo/chart-springy.js"></script>
<script type="text/javascript" src="/common/js/hippo/chart-transactions.js"></script>
<script type="text/javascript" src="/common/js/date.format.js"></script>
<script type="text/javascript" src="/common/js/hippo/hippo.js"></script>
<script type="text/javascript" src="/common/js/sorttable.js"></script>
<script type="text/javascript" src="/common/js/hippo/servermap/jquery.tmpl.min.js"></script>
<script type="text/javascript" src="/common/js/hippo/servermap/Point2D.js"></script>
<script type="text/javascript" src="/common/js/hippo/servermap/intersection.js"></script>
<script type="text/javascript" src="/common/js/hippo/servermap/springy.js"></script>
<script type="text/javascript" src="/common/js/hippo/servermap/canvas.roundRect.js"></script>
<script type="text/javascript" src="/common/js/hippo/servermap/hippoServerMap.js"></script>
</head>
<body>
<p id="scatter"></p>
<script type="text/javascript">
$(document).ready(function () {
$('#date').datepicker().on('changeDate', function(ev){
updateCharts();
});
$('#time').bind('focus', function(e){
$(this).data("time", $(this).val());
}).bind('blur', function(e){
if ($(this).data("time") !== $(this).val()) {
updateCharts();
}
});
$('#until').delegate("button", "click", function(){
var $this = $(this);
$this.siblings(".btn-primary").removeClass("btn-primary");
$this.addClass("btn-primary");
if ($this.attr("id") === "now") {
$("#date").attr("disabled", "").val("");
$("#time").attr("disabled", "").val("");
updateCharts();
} else {
setQueryDateToNow();
$("#date").attr("disabled", null);
$("#time").attr("disabled", null);
}
});
$('#period').delegate("button", "click", function(){
var $this = $(this);
$this.siblings(".btn-inverse").removeClass("btn-inverse active");
$this.addClass("btn-inverse active");
updateCharts();
$("#auto_refresh").trigger("change");
});
var timer;
$("#auto_refresh").bind("change", function(){
if (this.checked) {
if (timer != null) return;
console.log("[auto-refresh] started.")
clearInterval(timer);
setQueryDateToNow();
updateCharts();
var from = getQueryEndTime();
timer = setInterval(function() {
setQueryDateToNow();
console.log("[auto-refresh] fetching data from=" + from);
getRealtimeScatterData(from, function(data) {
console.log("[auto-refresh] data fetched. " + data.scatter.length);
console.log(getQueryStartTime());
console.log(getQueryEndTime());
updateScatter(getQueryStartTime(), getQueryEndTime(), data.scatter, "#scatter");
from = data.queryTo + 1;
});
}, 3000);
} else {
clearInterval(timer);
timer = null;
console.log("[auto-refresh] stopped.")
}
});
function cleanup() {
$("#springygraph").empty();
$("#scatter").empty();
$("#businessTransactions TBODY").empty();
$("#businessTransactionsDetail TBODY").empty();
$('#chartTabs a:first').tab('show');
$('#transactionTabs a:first').tab('show');
$("#springygraph").css("display", "none");
}
// TODO 조회 버튼을 두 번 연속 눌렀을 때 앞서 수행되던 결과는 취소시켜야 함.
// 예를 들어 1일치 조회하다가 모두 조회 되기 전에 30분 데이터 조회할 때...
function updateCharts() {
if($("#application").val() == "") {
alert("\n\nSelect an application, please.\n\n");
return true;
}
cleanup();
showIndicator();
var serverMapCallback = function(data) {
if (data.graphdata.nodes.length == 0) {
hideIndicator();
}
drawSpringy(data.graphdata, "#springygraph", 1100, 500);
hideIndicator();
$("#springygraph").css("display", "");
};
var businessTransactionCallback = function(data) {
showTransactionList(data.businessTransactions);
};
var scatterCallback = function(data) {
// 처음 조회된 데이터를 그려준다.
updateScatter(getQueryStartTime(), getQueryEndTime(), data.scatter, "#scatter");
if (data.scatter.length == 0) {
return;
}
// 데이터 조회가 추가로 필요한지 확인한다.
var lastTimeStamp = data.scatter[data.scatter.length - 1].timestamp;
if (lastTimeStamp >= getQueryEndTime()) {
return;
}
var queryNext = true;
var fetch = function() {
console.log("fetch scatter data");
clearInterval(scatterFetchTimer);
if(!queryNext || lastTimeStamp >= getQueryEndTime()) {
scatter.hideProgressbar();
console.log("fetching scatter data finished.");
return;
}
try {
scatter.showProgressbar(lastTimeStamp + 1, getQueryEndTime());
console.log("fetching scatter data.");
getScatterData($("#application").val(), lastTimeStamp + 1, getQueryEndTime(), function(data2) {
console.log("fetched " + data2.scatter.length);
scatter.hideProgressbar();
if (data2.scatter.length == 0) {
queryNext = false;
return;
}
updateScatter(getQueryStartTime(), getQueryEndTime(), data2.scatter, "#scatter");
lastTimeStamp = data2.scatter[data2.scatter.length - 1].timestamp;
scatterFetchTimer = setInterval(fetch, 200);
});
} catch(e) {
console.log(e);
}
}
var scatterFetchTimer = setInterval(fetch, 200);
};
if (isQueryFromNow()) {
getLastServerMapData2($("#application").val(), getQueryPeriod(), serverMapCallback);
// getLastServerMapData($("#application").val(), getQueryPeriod(), serverMapCallback);
//getLastBusinessTransactionsData($("#application").val(), getQueryPeriod(), businessTransactionCallback);
//drawScatter($("#application").val(), getQueryStartTime(), getQueryEndTime(), "#scatter");
//getLastScatterData($("#application").val(), getQueryPeriod(), scatterCallback);
} else {
getServerMapData2($("#application").val(), getQueryStartTime(), getQueryEndTime(), serverMapCallback);
// getServerMapData($("#application").val(), getQueryStartTime(), getQueryEndTime(), serverMapCallback);
//getBusinessTransactionsData($("#application").val(), getQueryStartTime(), getQueryEndTime(), businessTransactionCallback);
//drawScatter($("#application").val(), getQueryStartTime(), getQueryEndTime(), "#scatter");
//getScatterData($("#application").val(), getQueryStartTime(), getQueryEndTime(), scatterCallback);
}
}
$.getJSON("/applications.hippo", function(json) {
var target = $("#application");
target.empty();
$(json).each(function(i, e) {
var html = [];
html.push('<option value="');
html.push(e.applicationName);
html.push("@");
html.push(e.code);
html.push('"');
if (json.length == 1) {
html.push(' selected>');
} else {
html.push('>');
}
html.push(e.applicationName + "@" + e.serviceType);
html.push('</option>');
target.append(html.join(''));
$("#application").attr("disabled", null);
function format(state) {
var chunk = state.text.split("@");
if (chunk.length > 1) {
return "<img class='flag' src='/images/icons/" + chunk[1] + ".png'/> " + chunk[0];
} else {
return state;
}
}
$("#application").select2({
placeholder: "Select an application.",
formatResult: format,
formatSelection: format
});
});
});
$("#selectedBusinessTransactionsDetailPager li").click(function() {
alert("[NOT IMPLEMENTED]\n\nNOTE : 한꺼번에 많은 점을 선택했을 때 다 보여주지 않고.. 페이징?? 스크롤 페이징을 사용해도 될 것 같기도 하고...");
});
$("#application").bind("change", function() {
updateCharts();
});
cleanup();
});
</script>
<script id="EdgeBox" type="text/x-jquery-tmpl">
<div class="EdgeBox" style="position:absolute;border:1px solid #000;background:#fff;padding:5px 10px;-webkit-border-radius: 5px; ">
<br/><a href="#" onclick="alert('Sorry. Not implemented.');">Focus on passing transactions</a>
<br/><a href="#" onclick="alert('Sorry. Not implemented.');">Business transactions</a>
<hr/>
Response statistics
<ul>
{{each(key, value) histogram}}
<li>&lt; ${key}ms : ${value}</li>
{{/each}}
<li>Slow : ${slow}</li>
<li>Failed : ${error}</li>
</ul>
<button style="position:absolute;top:2px;right:2px;" onClick="$(this).parent().remove()">X</button>
</div>
</script>
<script id="ServerBox" type="text/x-jquery-tmpl">
<div class="ServerBox" style="position:absolute;border:1px solid #000;background:#fff;padding:5px 10px;-webkit-border-radius: 5px; ">
<br/><a href="#" onclick="alert('Sorry. Not implemented.');">Focus on passing transactions</a>
<br/><a href="#" onclick="alert('Sorry. Not implemented.');">Response scatter chart</a>
<br/><a href="#" onclick="alert('Sorry. Not implemented.');">Show Requests</a>
<hr/>
Application Type
<ul>
<li>${serviceType}</li>
</ul>
Hosts
<ul>
{{each(key, value) hosts}}
<li>
${value}
{{if serviceType == "TOMCAT"}}
<a href="http://nsight.nhncorp.com/dashboard_server/${value.replace('.nhnsystem.com','')}" target="_blank">(NSight)</a>
{{/if}}
</li>
{{/each}}
</ul>
Server instances
<ul>
<li>Not implemented.</li>
</ul>
<button style="position:absolute;top:2px;right:2px;" onClick="$(this).parent().remove()">X</button>
</div>
</script>
<script id="ClientBox" type="text/x-jquery-tmpl">
<div class="ServerBox" style="position:absolute;border:1px solid #000;background:#fff;padding:5px 10px;-webkit-border-radius: 5px; ">
<br/><a href="#" onclick="alert('Sorry. Not implemented.');">Show Requests</a>
<br/>Calls : Not implemented.
<button style="position:absolute;top:2px;right:2px;" onClick="$(this).parent().remove()">X</button>
</div>
</script>
</body>
</html>
@@ -1,34 +0,0 @@
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ taglib prefix="fmt" uri="http://java.sun.com/jsp/jstl/fmt" %>
{
"graphdata" : {
"nodes" : [
<c:forEach items="${nodes}" var="node" varStatus="status">
{
"name" : "${node}",
"hosts" : [
<c:forEach items="${node.hosts}" var="host" varStatus="status2">
"${host}"
<c:if test="${!status2.last}">,</c:if>
</c:forEach>
],
"serviceType" : "${node.serviceType.desc}",
"terminal" : "${node.serviceType.terminal}"
} <c:if test="${!status.last}">,</c:if>
</c:forEach>
],
"links" : [
<c:forEach items="${links}" var="link" varStatus="status">
{
"source" : ${link.from.sequence},
"target" : ${link.to.sequence},
"value" : ${link.histogram.totalCount},
"error" : ${link.histogram.errorCount},
"slow" : ${link.histogram.slowCount},
"histogram" : ${link.histogram}
} <c:if test="${!status.last}">,</c:if>
</c:forEach>
]
}
}
+1 -2
View File
@@ -59,8 +59,7 @@
<filter-mapping>
<filter-name>NoCacheFilter</filter-name>
<url-pattern>*.js</url-pattern>
<url-pattern>*.css</url-pattern>
<url-pattern>/*</url-pattern>
</filter-mapping>
<servlet-mapping>
@@ -137,140 +137,6 @@ var selectDotCallback = function(traces) {
var popupwindow = window.open("/selectedScatter.html", token);
}
/*
var selectDotCallbackDeprecated = function(traces) {
if (traces.length === 0) {
return;
}
if (traces.length === 1) {
openTrace(traces[0].traceId, traces[0].x);
return;
}
var query = [];
var temp = {};
for (var i = 0; i < traces.length; i++) {
if (i > 0) {
query.push("&");
}
query.push("tr");
query.push(i);
query.push("=");
query.push(traces[i].traceId);
query.push("&ti");
query.push(i);
query.push("=");
query.push(traces[i].x)
query.push("&re");
query.push(i);
query.push("=");
query.push(traces[i].y)
}
$.post("/requestmetadata.hippo", query.join(""), function(d) {
$("#selectedBusinessTransactionsDetail TBODY").empty();
var data = jQuery.parseJSON(d).metadata;
var html = [];
for (var i = 0; i < data.length; i++) {
if(data[i].exception) {
html.push("<tr class='error'>");
} else {
html.push("<tr>");
}
html.push("<td style='padding-right:5px;text-align:right'>");
html.push(i + 1);
html.push("</td>");
html.push("<td sorttable_customkey='");
html.push(data[i].startTime);
html.push("'>");
html.push(new Date(data[i].startTime).format("HH:MM:ss l"));
html.push("</td>");
html.push("<td>");
html.push("<a href='#' onclick='openTrace(\"");
html.push(data[i].traceId);
html.push("\", \"");
html.push(data[i].collectorAcceptTime);
html.push("\");'>");
html.push(data[i].traceId);
html.push("</a>");
html.push("</td>");
html.push("<td style='padding-right:30px;text-align:right'>");
html.push(formatNumber(data[i].elapsed));
html.push("</td>");
html.push("<td>");
if (data[i].exception) {
html.push(data[i].exception);
}
html.push("</td>");
html.push("<td>");
html.push(data[i].application);
html.push("</td>");
html.push("<td>");
html.push(data[i].agentId);
html.push("</td>");
html.push("<td>");
html.push("<a href='#' onclick=\"alert('not implemented. ip정보 조회 페이지로 연결.');\">");
html.push(data[i].remoteAddr);
html.push("</a>");
html.push("</td>");
html.push("</tr>");
}
$("#selectedBusinessTransactionsDetail TBODY").append(html.join(''));
$('#traceIdSelectModal').modal({});
})
.fail(function() {
alert("Failed to fetching the request informations.");
});
}
*/
/*
var timer;
$("#auto_refresh").bind("change", function(){
if (this.checked) {
if (timer != null) return;
console.log("[auto-refresh] started.")
clearInterval(timer);
setQueryDateToNow();
updateCharts();
var from = getQueryEndTime();
timer = setInterval(function() {
setQueryDateToNow();
console.log("[auto-refresh] fetching data from=" + from);
getRealtimeScatterData(from, function(data) {
updateScatter(getQueryStartTime(), getQueryEndTime(), data.scatter, "#scatter");
from = data.queryTo + 1;
});
}, 3000);
} else {
clearInterval(timer);
timer = null;
console.log("[auto-refresh] stopped.")
}
});
*/
function updateScatter(start, end, scatter_data, targetId, limit) {
if (scatter_data.length == 0) {
return;
+1 -1
View File
@@ -3,7 +3,7 @@ function expandToNewWindow() {
}
function openTrace(uuid, timestamp) {
window.open("/selectTransaction.hippo?traceId=" + uuid + "&focusTimestamp=" + timestamp);
window.open("/transactionInfo.hippo?traceId=" + uuid + "&focusTimestamp=" + timestamp);
}
function getQueryPeriod() {
-599
View File
@@ -1,599 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<title>HIPPO</title>
<meta charset="utf-8">
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link href="/common/css/bootstrap/bootstrap.css" rel="stylesheet">
<link href="/common/css/bootstrap/bootstrap-responsive.css" rel="stylesheet"/>
<link href="/common/css/hippo/hippo.css" rel="stylesheet"/>
<link href="/common/css/datepicker.css" rel="stylesheet"/>
<link href="/select2/select2-customized.css" rel="stylesheet"/>
<!-- Le HTML5 shim, for IE6-8 support of HTML5 elements -->
<!--[if lt IE 9]>
<script src="http://html5shim.googlecode.com/svn/trunk/html5.js"></script>
<![endif]-->
<script type="text/javascript" src="/common/js/jquery/jquery-1.7.1.min.js"></script>
<script type="text/javascript" src="/common/js/jquery/jquery-ui-1.8.18.custom.min.js"></script>
<script type="text/javascript" src="/select2/select2.js"></script>
<script type="text/javascript" src="/common/js/bootstrap.min.js"></script>
<script type="text/javascript" src="/common/js/modernizr-2.6.2.min.js"></script>
<script type="text/javascript" src="/common/js/bootstrap-datepicker.js"></script>
<script type="text/javascript" src="/common/js/d3.js"></script>
<script type="text/javascript" src="/common/js/d3.chart.js"></script>
<script type="text/javascript" src="/common/js/d3.chart.scatter.js"></script>
<script type="text/javascript" src="/common/js/hippo/chart-scatter2.js"></script>
<script type="text/javascript" src="/common/js/hippo/chart-springy.js"></script>
<script type="text/javascript" src="/common/js/hippo/chart-transactions.js"></script>
<script type="text/javascript" src="/common/js/date.format.js"></script>
<script type="text/javascript" src="/common/js/hippo/hippo.js"></script>
<script type="text/javascript" src="/common/js/sorttable.js"></script>
<script type="text/javascript" src="/common/js/hippo/servermap/jquery.tmpl.min.js"></script>
<script type="text/javascript" src="/common/js/hippo/servermap/Point2D.js"></script>
<script type="text/javascript" src="/common/js/hippo/servermap/intersection.js"></script>
<script type="text/javascript" src="/common/js/hippo/servermap/springy.js"></script>
<script type="text/javascript" src="/common/js/hippo/servermap/canvas.roundRect.js"></script>
<script type="text/javascript" src="/common/js/hippo/servermap/hippoServerMap.js"></script>
</head>
<body>
<div class="navbar navbar-fixed-top">
<div class="navbar-inner">
<div class="container">
<a class="btn btn-navbar" data-toggle="collapse" data-target=".nav-collapse">
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</a>
<a class="brand" href="/">HIPPO</a>
<div class="nav-collapse">
<ul class="nav">
<li><a href="http://devcafe.nhncorp.com/arcus" target="_blank">About</a></li>
<li><a data-toggle="modal" href="#myModal">Configuration</a></li>
<li><a data-toggle="modal" href="#myHelpModal">Help</a></li>
</ul>
</div>
</div>
</div>
</div>
<!-- MODAL -->
<div class="modal hide fade" id="myModal" style="width:1000px; margin-left:-500px">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal">×</button>
<h3>Configuration</h3>
</div>
<div class="modal-body">
Configuration.
</div>
<div class="modal-footer">
<a href="#" class="btn" data-dismiss="modal">Close</a>
<!--
<a href="#" class="btn btn-primary" onclick="saveNewChart();">Add to dashboard</a>
-->
</div>
</div>
<!-- END OF MODAL -->
<!-- HELP-MODAL -->
<div class="modal hide fade" id="myHelpModal" style="width:1000px; margin-left:-500px">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal">×</button>
<h3>Help</h3>
</div>
<div class="modal-body">
<br/>E-mail to us. <a href="mailto:!hippo@nhn.com">!hippo@nhn.com</a><br/>
<br/>Thank you.<br/><br/>
</div>
<div class="modal-footer">
<a href="#" class="btn" data-dismiss="modal">Close</a>
</div>
</div>
<!-- END OF HELP-MODAL -->
<!-- MODAL -->
<div class="modal hide fade" id="traceIdSelectModal" style="width:1200px; margin-left:-600px">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal">×</button>
<h3>Selected Traces</h3>
</div>
<div class="modal-body">
<table id="selectedBusinessTransactionsDetail" class="table table-bordered table-hover sortable">
<thead>
<tr>
<th class="sorttable_numeric">#</th>
<th class="sorttable_numeric">Time</th>
<th>TraceId</th>
<th class="sorttable_numeric">Res. Time (ms)</th>
<th>Exception</th>
<th>Application</th>
<th>AgentId</th>
<th>ClientIP</th>
</tr>
</thead>
<tbody>
</tbody>
</table>
<ul id="selectedBusinessTransactionsDetailPager" class="pager">
<li><a href="#">Previous</a></li>
<li><a href="#">Next</a></li>
</ul>
</div>
<div class="modal-footer">
<a href="#" class="btn" data-dismiss="modal">Close</a>
</div>
</div>
<!-- END OF MODAL -->
<!-- MENU -->
<div class="container">
THIS UI IS DEPRECATED. <a href="/index2.html">MOVE TO THE NEW UI Ver2.</a>
<form class="well well-small form-inline" id="search" onsubmit="return false;">
<select id="application" disabled>
<option>Loading...</option>
</select>
<div id="until" class="btn-group" data-toggle="buttons-radio">
<button id="now" type="button" class="btn btn-primary active">now</button>
<button id="specific" type="button" class="btn">select</button>
</div>
<input id="date" class="" maxlength="10" size="16" type="text" data-date-format="yyyy/mm/dd" style="text-align:center;width:80px" placeholder="yyyy/mm/dd" disabled>
<input id="time" class="" maxlength="8" size="16" type="text" style="text-align:center;width:75px" placeholder="hh:mm:ss" disabled>
<div id="period" class="btn-group" data-toggle="buttons-radio">
<button type="button" value="5" class="btn btn-mini">5m</button>
<button type="button" value="20" class="btn btn-mini btn-inverse active">20m</button>
<button type="button" value="60" class="btn btn-mini">1h</button>
<button type="button" value="360" class="btn btn-mini">6h</button>
<button type="button" value="1440" class="btn btn-mini">1d</button>
<button type="button" value="2880" class="btn btn-mini">2d</button>
</div>
<div class="pull-right">
<label class="checkbox" style="margin:5px">
<input id="auto_refresh" type="checkbox" style="margin-top:6px"> auto-refresh
</label>
</div>
</form>
<div class="row">
<div class="span12">
<ul class="nav nav-tabs" id="chartTabs">
<li><a href="#Graph" data-toggle="tab">Server Graph</a></li>
</ul>
<div class="tab-content">
<div class="tab-pane active" id="Graph">
박스나 연결선을 더블클릭하여 drill down.
<div class="indicator"><img src="/common/images/ajaxloader.gif"/></div>
<div id="springygraph" style="width:1167px;height:700px;border:1px solid #DDDDDD;overflow:hidden;position:relative;display:none;"></div>
</div>
</div>
</div>
</div>
<div class="row">
<div class="span12">
<br/><br/><br/>
</div>
</div>
<div class="row">
<div class="span12">
<ul class="nav nav-tabs" id="transactionTabs">
<li><a href="#ScatterChart" data-toggle="tab">Scatter Plot</a></li>
<li><a href="#BusinessTransactionList" data-toggle="tab">Business Transactions</a></li>
</ul>
<div class="tab-content" style="min-height:550px;">
<div class="tab-pane active" id="ScatterChart">
<div class="indicator"><img src="/common/images/ajaxloader.gif"/></div>
<p id="scatter"></p>
</div>
<div class="tab-pane" id="BusinessTransactionList">
<table id="businessTransactions" class="table table-bordered sortable">
<thead>
<tr>
<th>Name</th>
<th class="sorttable_numeric">Calls</th>
<th class="sorttable_numeric">Time(ms)</th>
<th class="sorttable_numeric">Min Time(ms)</th>
<th class="sorttable_numeric">Max Time(ms)</th>
</tr>
</thead>
<tbody>
</tbody>
</table>
<table id="businessTransactionsDetail" class="table table-bordered sortable">
<thead>
<tr>
<th>#</th>
<th class="sorttable_numeric">Time</th>
<th>TraceId</th>
<th class="sorttable_numeric">Response Time (ms)</th>
</tr>
</thead>
<tbody>
</tbody>
</table>
</div>
</div>
</div>
</div>
<div class="row">
<div class="span12">
<br/>
<br/>
<br/>
<br/>
</div>
</div>
</div>
<!-- END OF MENU -->
<script type="text/javascript">
function openTrace(uuid, timestamp) {
window.open("/selectTransaction.hippo?traceId=" + uuid + "&focusTimestamp=" + timestamp);
}
$(document).ready(function () {
function getServerMapData2(begin, end, callback) {
var application = $("#application").val().split("@");
d3.json("/getServerMapData2.hippo?application=" + application[0] + "&serviceType=" + application[1] + "&from=" + begin + "&to=" + end, function(d) { callback(d); });
}
function getLastServerMapData2(period, callback) {
var application = $("#application").val().split("@");
d3.json("/getLastServerMapData2.hippo?application=" + application[0] + "&serviceType=" + application[1] + "&period=" + period, function(d) { callback(d); });
}
function getServerMapData(begin, end, callback) {
var application = $("#application").val().split("@")[0];
d3.json("/getServerMapData.hippo?application=" + application + "&from=" + begin + "&to=" + end, function(d) { callback(d); });
}
function getLastServerMapData(period, callback) {
var application = $("#application").val().split("@")[0];
d3.json("/getLastServerMapData.hippo?application=" + application + "&period=" + period, function(d) { callback(d); });
}
function getScatterData(begin, end, callback) {
var application = $("#application").val().split("@")[0];
d3.json("/getScatterData.hippo?application=" + application + "&from=" + begin + "&to=" + end + "&limit=5000", function(d) { callback(d); });
}
function getLastScatterData(period, callback) {
var application = $("#application").val().split("@")[0];
d3.json("/getLastScatterData.hippo?application=" + application + "&period=" + period + "&limit=5000", function(d) { callback(d); });
}
function getRealtimeScatterData(begin, callback) {
var application = $("#application").val().split("@")[0];
d3.json("/getRealtimeScatterData.hippo?application=" + application + "&from=" + begin + "&limit=5000", function(d) { callback(d); });
}
function getBusinessTransactionsData(begin, end, callback) {
var application = $("#application").val().split("@")[0];
d3.json("/getBusinessTransactionsData.hippo?application=" + application + "&from=" + begin + "&to=" + end, function(d) { callback(d); });
}
function getLastBusinessTransactionsData(period, callback) {
var application = $("#application").val().split("@")[0];
d3.json("/getLastBusinessTransactionsData.hippo?application=" + application + "&period=" + period, function(d) { callback(d); });
}
function getQueryPeriod() {
return $("#period").find("button.active").val().split(",") * 1000 * 60;
}
function getQueryStartTime() {
return getQueryEndTime() - getQueryPeriod();
}
function getQueryEndTime() {
var format = d3.time.format("%Y/%m/%d %H:%M:%S"),
now = new Date(),
input = format.parse($('#date').val() + ' ' + $('#time').val()) || now;
if (input.getTime() > now.getTime()) {
input = now;
}
return input.getTime();
}
function setQueryDateToNow() {
var date = new Date();
var format_date = d3.time.format("%Y/%m/%d"),
format_time = d3.time.format("%H:%M:%S");
$('#date').val(format_date(date));
$('#time').val(format_time(date));
}
function showIndicator() {
$(".indicator").show();
}
function hideIndicator() {
$(".indicator").hide();
}
function isQueryFromNow() {
return $(".btn#now.active").length > 0;
}
$('#date').datepicker().on('changeDate', function(ev){
updateCharts();
});
$('#time').bind('focus', function(e){
$(this).data("time", $(this).val());
}).bind('blur', function(e){
if ($(this).data("time") !== $(this).val()) {
updateCharts();
}
});
$('#until').delegate("button", "click", function(){
var $this = $(this);
$this.siblings(".btn-primary").removeClass("btn-primary");
$this.addClass("btn-primary");
if ($this.attr("id") === "now") {
$("#date").attr("disabled", "").val("");
$("#time").attr("disabled", "").val("");
updateCharts();
} else {
setQueryDateToNow();
$("#date").attr("disabled", null);
$("#time").attr("disabled", null);
}
});
$('#period').delegate("button", "click", function(){
var $this = $(this);
$this.siblings(".btn-inverse").removeClass("btn-inverse active");
$this.addClass("btn-inverse active");
updateCharts();
$("#auto_refresh").trigger("change");
});
var timer;
$("#auto_refresh").bind("change", function(){
if (this.checked) {
if (timer != null) return;
console.log("[auto-refresh] started.")
clearInterval(timer);
setQueryDateToNow();
updateCharts();
var from = getQueryEndTime();
timer = setInterval(function() {
setQueryDateToNow();
console.log("[auto-refresh] fetching data from=" + from);
getRealtimeScatterData(from, function(data) {
console.log("[auto-refresh] data fetched. " + data.scatter.length);
console.log(getQueryStartTime());
console.log(getQueryEndTime());
updateScatter(getQueryStartTime(), getQueryEndTime(), data.scatter, "#scatter");
from = data.queryTo + 1;
});
}, 3000);
} else {
clearInterval(timer);
timer = null;
console.log("[auto-refresh] stopped.")
}
});
function cleanup() {
$("#springygraph").empty();
$("#scatter").empty();
$("#businessTransactions TBODY").empty();
$("#businessTransactionsDetail TBODY").empty();
$('#chartTabs a:first').tab('show');
$('#transactionTabs a:first').tab('show');
$("#springygraph").css("display", "none");
}
// TODO 조회 버튼을 두 번 연속 눌렀을 때 앞서 수행되던 결과는 취소시켜야 함.
// 예를 들어 1일치 조회하다가 모두 조회 되기 전에 30분 데이터 조회할 때...
function updateCharts() {
if($("#application").val() == "") {
alert("\n\nSelect an application, please.\n\n");
return true;
}
cleanup();
showIndicator();
var serverMapCallback = function(data) {
if (data.graphdata.nodes.length == 0) {
hideIndicator();
}
drawSpringy(data.graphdata, "#springygraph", 1100, 500);
hideIndicator();
$("#springygraph").css("display", "");
};
var businessTransactionCallback = function(data) {
showTransactionList(data.businessTransactions);
};
var scatterCallback = function(data) {
// 처음 조회된 데이터를 그려준다.
updateScatter(getQueryStartTime(), getQueryEndTime(), data.scatter, "#scatter");
if (data.scatter.length == 0) {
return;
}
// 데이터 조회가 추가로 필요한지 확인한다.
var lastTimeStamp = data.scatter[data.scatter.length - 1].timestamp;
if (lastTimeStamp >= getQueryEndTime()) {
return;
}
var queryNext = true;
var fetch = function() {
console.log("fetch scatter data");
clearInterval(scatterFetchTimer);
if(!queryNext || lastTimeStamp >= getQueryEndTime()) {
scatter.hideProgressbar();
console.log("fetching scatter data finished.");
return;
}
try {
scatter.showProgressbar(lastTimeStamp + 1, getQueryEndTime());
console.log("fetching scatter data.");
getScatterData(lastTimeStamp + 1, getQueryEndTime(), function(data2) {
console.log("fetched " + data2.scatter.length);
scatter.hideProgressbar();
if (data2.scatter.length == 0) {
queryNext = false;
return;
}
updateScatter(getQueryStartTime(), getQueryEndTime(), data2.scatter, "#scatter");
lastTimeStamp = data2.scatter[data2.scatter.length - 1].timestamp;
scatterFetchTimer = setInterval(fetch, 200);
});
} catch(e) {
console.log(e);
}
}
var scatterFetchTimer = setInterval(fetch, 200);
};
if (isQueryFromNow()) {
// getLastServerMapData2(getQueryPeriod(), serverMapCallback);
getLastServerMapData(getQueryPeriod(), serverMapCallback);
getLastBusinessTransactionsData(getQueryPeriod(), businessTransactionCallback);
drawScatter(getQueryStartTime(), getQueryEndTime(), "#scatter");
getLastScatterData(getQueryPeriod(), scatterCallback);
} else {
// getServerMapData2(getQueryStartTime(), getQueryEndTime(), serverMapCallback);
getServerMapData(getQueryStartTime(), getQueryEndTime(), serverMapCallback);
getBusinessTransactionsData(getQueryStartTime(), getQueryEndTime(), businessTransactionCallback);
drawScatter(getQueryStartTime(), getQueryEndTime(), "#scatter");
getScatterData(getQueryStartTime(), getQueryEndTime(), scatterCallback);
}
}
$.getJSON("/applications.hippo", function(json) {
var target = $("#application");
target.empty();
$(json).each(function(i, e) {
var html = [];
html.push('<option value="');
html.push(e.applicationName);
html.push("@");
html.push(e.code);
html.push('"');
if (json.length == 1) {
html.push(' selected>');
} else {
html.push('>');
}
html.push(e.applicationName + " [" + e.serviceType + "]");
html.push('</option>');
target.append(html.join(''));
$("#application").attr("disabled", null);
$("#application").select2({
placeholder: "Select an application.",
});
});
});
$("#selectedBusinessTransactionsDetailPager li").click(function() {
alert("[NOT IMPLEMENTED]\n\nNOTE : 한꺼번에 많은 점을 선택했을 때 다 보여주지 않고.. 페이징?? 스크롤 페이징을 사용해도 될 것 같기도 하고...");
});
$("#application").bind("change", function() {
updateCharts();
});
cleanup();
});
</script>
<script id="EdgeBox" type="text/x-jquery-tmpl">
<div class="EdgeBox" style="position:absolute;border:1px solid #000;background:#fff;padding:5px 10px;-webkit-border-radius: 5px; ">
<ul style="margin:0;padding:0 0 0 10px;">
<br/>
<a href="#" onclick="alert('Sorry. Not implemented.');">Focus on passing transactions</a>
<hr/>
<li>
Failed Transaction : ${error}
</li>
<li>
Slow Transaction : ${slow}
</li>
<li>
Response time histogram
<ul>
{{each(key, value) histogram}}
<li>&lt; ${key}ms : ${value}</li>
{{/each}}
</ul>
</li>
</ul>
<button style="position:absolute;top:2px;right:2px;" onClick="$(this).parent().remove()">X</button>
</div>
</script>
<script id="ServerBox" type="text/x-jquery-tmpl">
<div class="ServerBox" style="position:absolute;border:1px solid #000;background:#fff;padding:5px 10px;-webkit-border-radius: 5px; ">
<ul style="margin:0;padding:0 0 0 10px;">
<br/>
<a href="#" onclick="alert('Sorry. Not implemented.');">Focus on passing transactions</a>
<br/>
Application Type : ${serviceType}
<br/>
Hosts
<ul>
{{each(key, value) hosts}}
<li>
${value}
{{if serviceType == "TOMCAT"}}
<a href="http://nsight.nhncorp.com/dashboard_server/${value.replace('.nhnsystem.com','')}" target="_blank">(NSight)</a>
{{/if}}
</li>
{{/each}}
</ul>
</li>
</ul>
<button style="position:absolute;top:2px;right:2px;" onClick="$(this).parent().remove()">X</button>
</div>
</script>
</body>
</html>
-580
View File
@@ -1,580 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<title>HIPPO</title>
<meta charset="utf-8">
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link href="/common/css/bootstrap/bootstrap.css" rel="stylesheet">
<link href="/common/css/bootstrap/bootstrap-responsive.css" rel="stylesheet"/>
<link href="/common/css/hippo/hippo.css" rel="stylesheet"/>
<link href="/common/css/datepicker.css" rel="stylesheet"/>
<link href="/select2/select2-customized.css" rel="stylesheet"/>
<!-- Le HTML5 shim, for IE6-8 support of HTML5 elements -->
<!--[if lt IE 9]>
<script src="http://html5shim.googlecode.com/svn/trunk/html5.js"></script>
<![endif]-->
<script type="text/javascript" src="/common/js/jquery/jquery-1.7.1.min.js"></script>
<script type="text/javascript" src="/common/js/jquery/jquery-ui-1.8.18.custom.min.js"></script>
<script type="text/javascript" src="/select2/select2.js"></script>
<script type="text/javascript" src="/common/js/bootstrap.min.js"></script>
<script type="text/javascript" src="/common/js/modernizr-2.6.2.min.js"></script>
<script type="text/javascript" src="/common/js/bootstrap-datepicker.js"></script>
<script type="text/javascript" src="/common/js/d3.js"></script>
<script type="text/javascript" src="/common/js/d3.chart.js"></script>
<script type="text/javascript" src="/common/js/d3.chart.scatter.js"></script>
<script type="text/javascript" src="/common/js/hippo/chart-scatter2.js"></script>
<script type="text/javascript" src="/common/js/hippo/chart-springy.js"></script>
<script type="text/javascript" src="/common/js/hippo/chart-transactions.js"></script>
<script type="text/javascript" src="/common/js/date.format.js"></script>
<script type="text/javascript" src="/common/js/hippo/hippo.js"></script>
<script type="text/javascript" src="/common/js/sorttable.js"></script>
<script type="text/javascript" src="/common/js/hippo/servermap/jquery.tmpl.min.js"></script>
<script type="text/javascript" src="/common/js/hippo/servermap/Point2D.js"></script>
<script type="text/javascript" src="/common/js/hippo/servermap/intersection.js"></script>
<script type="text/javascript" src="/common/js/hippo/servermap/springy.js"></script>
<script type="text/javascript" src="/common/js/hippo/servermap/canvas.roundRect.js"></script>
<script type="text/javascript" src="/common/js/hippo/servermap/hippoServerMap.js"></script>
<script type="text/javascript" src="/common/js/hippo/help.js"></script>
</head>
<body>
<div class="navbar navbar-fixed-top">
<div class="navbar-inner">
<div class="container-fluid">
<a class="brand" href="/">HIPPO</a>
<form class="navbar-form pull-left">
<select id="application" disabled>
<option>Loading...</option>
</select>
<div id="until" class="btn-group" data-toggle="buttons-radio">
<button id="now" type="button" class="btn btn-primary active">now</button>
<button id="specific" type="button" class="btn">select</button>
</div>
<input id="date" class="" maxlength="10" size="16" type="text" data-date-format="yyyy/mm/dd" style="text-align:center;width:80px" placeholder="yyyy/mm/dd" disabled>
<input id="time" class="" maxlength="8" size="16" type="text" style="text-align:center;width:75px" placeholder="hh:mm:ss" disabled>
<div id="period" class="btn-group" data-toggle="buttons-radio">
<button type="button" value="5" class="btn btn-mini">5m</button>
<button type="button" value="20" class="btn btn-mini btn-inverse active">20m</button>
<button type="button" value="60" class="btn btn-mini">1h</button>
<button type="button" value="360" class="btn btn-mini">6h</button>
<button type="button" value="1440" class="btn btn-mini">1d</button>
<button type="button" value="2880" class="btn btn-mini">2d</button>
</div>
</form>
<div class="nav-collapse pull-right">
<ul class="nav">
<li><a href="http://devcafe.nhncorp.com/arcus" target="_blank">About</a></li>
<li><a data-toggle="modal" href="#myModal">Configuration</a></li>
<li><a data-toggle="modal" href="#myHelpModal">Help</a></li>
</ul>
</div>
</div>
</div>
</div>
<!-- MODAL -->
<div class="modal hide fade" id="myModal" style="width:1000px; margin-left:-500px">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal">×</button>
<h3>Configuration</h3>
</div>
<div class="modal-body">
Configuration.
</div>
<div class="modal-footer">
<a href="#" class="btn" data-dismiss="modal">Close</a>
<!--
<a href="#" class="btn btn-primary" onclick="saveNewChart();">Add to dashboard</a>
-->
</div>
</div>
<!-- END OF MODAL -->
<!-- HELP-MODAL -->
<div class="modal hide fade" id="myHelpModal" style="width:1000px; margin-left:-500px">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal">×</button>
<h3>Help</h3>
</div>
<div class="modal-body">
<br/>E-mail to us. <a href="mailto:!hippo@nhn.com">!hippo@nhn.com</a><br/>
<br/>Thank you.<br/><br/>
</div>
<div class="modal-footer">
<a href="#" class="btn" data-dismiss="modal">Close</a>
</div>
</div>
<!-- END OF HELP-MODAL -->
<!-- MODAL -->
<div class="modal hide fade" id="traceIdSelectModal" style="width:1200px; margin-left:-600px">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal">×</button>
<h3>Selected Traces</h3>
</div>
<div class="modal-body">
<table id="selectedBusinessTransactionsDetail" class="table table-bordered table-hover sortable">
<thead>
<tr>
<th class="sorttable_numeric">#</th>
<th class="sorttable_numeric">Time</th>
<th>TraceId</th>
<th class="sorttable_numeric">Res. Time (ms)</th>
<th>Exception</th>
<th>Application</th>
<th>AgentId</th>
<th>ClientIP</th>
</tr>
</thead>
<tbody>
</tbody>
</table>
<ul id="selectedBusinessTransactionsDetailPager" class="pager">
<li><a href="#">Previous</a></li>
<li><a href="#">Next</a></li>
</ul>
</div>
<div class="modal-footer">
<a href="#" class="btn" data-dismiss="modal">Close</a>
</div>
</div>
<!-- END OF MODAL -->
<!-- MENU -->
<div class="container-fluid">
<!--
<form class="well well-small form-inline" id="search" onsubmit="return false;">
<select id="application" disabled>
<option>Loading...</option>
</select>
<div id="until" class="btn-group" data-toggle="buttons-radio">
<button id="now" type="button" class="btn btn-primary active">now</button>
<button id="specific" type="button" class="btn">select</button>
</div>
<input id="date" class="" maxlength="10" size="16" type="text" data-date-format="yyyy/mm/dd" style="text-align:center;width:80px" placeholder="yyyy/mm/dd" disabled>
<input id="time" class="" maxlength="8" size="16" type="text" style="text-align:center;width:75px" placeholder="hh:mm:ss" disabled>
<div id="period" class="btn-group" data-toggle="buttons-radio">
<button type="button" value="5" class="btn btn-mini">5m</button>
<button type="button" value="20" class="btn btn-mini btn-inverse active">20m</button>
<button type="button" value="60" class="btn btn-mini">1h</button>
<button type="button" value="360" class="btn btn-mini">6h</button>
<button type="button" value="1440" class="btn btn-mini">1d</button>
<button type="button" value="2880" class="btn btn-mini">2d</button>
</div>
<div class="pull-right">
<label class="checkbox" style="margin:5px">
<input id="auto_refresh" type="checkbox" style="margin-top:6px"> auto-refresh
</label>
</div>
</form>
-->
<div class="row-fluid">
<div class="span8">
THIS UI IS DEPRECATED. <a href="/index.html">MOVE TO THE NEW UI Ver3.</a>
<div class="indicator"><img src="/common/images/ajaxloader.gif"/></div>
<div id="springygraph" style="height:700px;border:1px solid #DDDDDD;overflow:hidden;position:relative;display:none;"></div>
</div>
<div class="span4">
<ul class="nav nav-tabs" id="transactionTabs">
<li class="active">
<a href="#ScatterChart" data-toggle="tab">Response scatter <i class="hippo-help icon-question-sign" onclick="help(2001);"></i></a>
</li>
</ul>
<div class="tab-content" style="min-height:400px;">
<div class="tab-pane active" id="ScatterChart">
<div class="indicator"><img src="/common/images/ajaxloader.gif"/></div>
<p id="scatter"></p>
</div>
</div>
<ul class="nav nav-tabs" id="transactionTabs">
<li class="active">
<a href="#BusinessTransactionList" data-toggle="tab">Requests <i class="hippo-help icon-question-sign" onclick="help(2002);"></i></a>
</li>
</ul>
<div class="tab-content" style="min-height:550px;">
<div class="tab-pane active" id="BusinessTransactionList">
<table id="businessTransactions" class="table table-bordered sortable">
<thead>
<tr>
<th>URL</th>
<th class="sorttable_numeric">Calls</th>
<th class="sorttable_numeric">Time(ms)</th>
<th class="sorttable_numeric">Min Time(ms)</th>
<th class="sorttable_numeric">Max Time(ms)</th>
</tr>
</thead>
<tbody>
</tbody>
</table>
<table id="businessTransactionsDetail" class="table table-bordered sortable">
<thead>
<tr>
<th>#</th>
<th class="sorttable_numeric">Time</th>
<th>TraceId</th>
<th class="sorttable_numeric">Response Time (ms)</th>
</tr>
</thead>
<tbody>
</tbody>
</table>
</div>
</div>
</div>
</div>
</div>
<!-- END OF MENU -->
<script type="text/javascript">
$.extend($.tmpl.tag, {
"var": {
open: "var $1;"
}
});
$.extend($.tmpl.tag, { "eval": { open: "$1;"} });
$(document).ready(function () {
$('#date').datepicker().on('changeDate', function(ev){
updateCharts();
});
$('#time').bind('focus', function(e){
$(this).data("time", $(this).val());
}).bind('blur', function(e){
if ($(this).data("time") !== $(this).val()) {
updateCharts();
}
});
$('#until').delegate("button", "click", function(){
var $this = $(this);
$this.siblings(".btn-primary").removeClass("btn-primary");
$this.addClass("btn-primary");
if ($this.attr("id") === "now") {
$("#date").attr("disabled", "").val("");
$("#time").attr("disabled", "").val("");
updateCharts();
} else {
setQueryDateToNow();
$("#date").attr("disabled", null);
$("#time").attr("disabled", null);
}
});
$('#period').delegate("button", "click", function(){
var $this = $(this);
$this.siblings(".btn-inverse").removeClass("btn-inverse active");
$this.addClass("btn-inverse active");
updateCharts();
$("#auto_refresh").trigger("change");
});
var timer;
$("#auto_refresh").bind("change", function(){
if (this.checked) {
if (timer != null) return;
console.log("[auto-refresh] started.")
clearInterval(timer);
setQueryDateToNow();
updateCharts();
var from = getQueryEndTime();
timer = setInterval(function() {
setQueryDateToNow();
console.log("[auto-refresh] fetching data from=" + from);
getRealtimeScatterData(from, function(data) {
console.log("[auto-refresh] data fetched. " + data.scatter.length);
console.log(getQueryStartTime());
console.log(getQueryEndTime());
updateScatter(getQueryStartTime(), getQueryEndTime(), data.scatter, "#scatter");
from = data.queryTo + 1;
});
}, 3000);
} else {
clearInterval(timer);
timer = null;
console.log("[auto-refresh] stopped.")
}
});
function cleanup() {
$("#springygraph").empty();
$("#scatter").empty();
$("#businessTransactions TBODY").empty();
$("#businessTransactionsDetail TBODY").empty();
// $('#chartTabs a:first').tab('show');
// $('#transactionTabs a:first').tab('show');
// $("#springygraph").css("display", "none");
}
// TODO 조회 버튼을 두 번 연속 눌렀을 때 앞서 수행되던 결과는 취소시켜야 함.
// 예를 들어 1일치 조회하다가 모두 조회 되기 전에 30분 데이터 조회할 때...
function updateCharts() {
if($("#application").val() == "") {
alert("\n\nSelect an application, please.\n\n");
return true;
}
cleanup();
showIndicator();
var serverMapCallback = function(data) {
if (data.graphdata.nodes.length == 0) {
hideIndicator();
}
drawSpringy(data.graphdata, "#springygraph", 1100, 500);
hideIndicator();
$("#springygraph").css("display", "");
};
if (isQueryFromNow()) {
getLastServerMapData2($("#application").val(), getQueryPeriod(), serverMapCallback);
// getLastServerMapData($("#application").val(), getQueryPeriod(), serverMapCallback);
// 개발 편의상 모두 표시.
getLastBusinessTransactionsData($("#application").val(), getQueryPeriod(), businessTransactionCallback);
drawScatter($("#application").val(), getQueryStartTime(), getQueryEndTime(), "#scatter");
getLastScatterData($("#application").val(), getQueryPeriod(), scatterCallback);
} else {
getServerMapData2($("#application").val(), getQueryStartTime(), getQueryEndTime(), serverMapCallback);
// getServerMapData($("#application").val(), getQueryStartTime(), getQueryEndTime(), serverMapCallback);
// 개발 편의상 모두 표시.
getBusinessTransactionsData($("#application").val(), getQueryStartTime(), getQueryEndTime(), businessTransactionCallback);
drawScatter($("#application").val(), getQueryStartTime(), getQueryEndTime(), "#scatter");
getScatterData($("#application").val(), getQueryStartTime(), getQueryEndTime(), scatterCallback);
}
}
$.getJSON("/applications.hippo", function(json) {
var target = $("#application");
target.empty();
$(json).each(function(i, e) {
var html = [];
html.push('<option value="');
html.push(e.applicationName);
html.push("@");
html.push(e.code);
html.push('"');
if (json.length == 1) {
html.push(' selected>');
} else {
html.push('>');
}
html.push(e.applicationName + "@" + e.serviceType);
html.push('</option>');
target.append(html.join(''));
$("#application").attr("disabled", null);
function format(state) {
var chunk = state.text.split("@");
if (chunk.length > 1) {
return "<img class='flag' src='/images/icons/" + chunk[1] + ".png'/> " + chunk[0];
} else {
return state;
}
}
$("#application").select2({
placeholder: "Select an application.",
formatResult: format,
formatSelection: format
});
// 임시.
$(".select2-container").css("top", "4px");
});
});
$("#selectedBusinessTransactionsDetailPager li").click(function() {
alert("[NOT IMPLEMENTED]\n\nNOTE : 한꺼번에 많은 점을 선택했을 때 다 보여주지 않고.. 페이징?? 스크롤 페이징을 사용해도 될 것 같기도 하고...");
});
$("#application").bind("change", function() {
updateCharts();
});
cleanup();
});
// TODO 지금은 함수와 변수가 전부 global로 나와있는데 나중에 object로 만들어야 할 듯.
var scatterCallback = function(data) {
// 처음 조회된 데이터를 그려준다.
updateScatter(getQueryStartTime(), getQueryEndTime(), data.scatter, "#scatter");
if (data.scatter.length == 0) {
return;
}
// 데이터 조회가 추가로 필요한지 확인한다.
var lastTimeStamp = data.scatter[data.scatter.length - 1].timestamp;
if (lastTimeStamp >= getQueryEndTime()) {
return;
}
var queryNext = true;
var fetch = function() {
console.log("fetch scatter data");
clearInterval(scatterFetchTimer);
if(!queryNext || lastTimeStamp >= getQueryEndTime()) {
scatter.hideProgressbar();
console.log("fetching scatter data finished.");
return;
}
try {
scatter.showProgressbar(lastTimeStamp + 1, getQueryEndTime());
console.log("fetching scatter data.");
getScatterData($("#application").val(), lastTimeStamp + 1, getQueryEndTime(), function(data2) {
console.log("fetched " + data2.scatter.length);
scatter.hideProgressbar();
if (data2.scatter.length == 0) {
queryNext = false;
return;
}
updateScatter(getQueryStartTime(), getQueryEndTime(), data2.scatter, "#scatter");
lastTimeStamp = data2.scatter[data2.scatter.length - 1].timestamp;
scatterFetchTimer = setInterval(fetch, 200);
});
} catch(e) {
console.log(e);
}
}
var scatterFetchTimer = setInterval(fetch, 200);
};
var businessTransactionCallback = function(data) {
showTransactionList(data.businessTransactions);
};
function showResponseScatter(applicationName) {
if (isQueryFromNow()) {
drawScatter(applicationName, getQueryStartTime(), getQueryEndTime(), "#scatter");
getLastScatterData(applicationName, getQueryPeriod(), scatterCallback);
} else {
drawScatter(applicationName, getQueryStartTime(), getQueryEndTime(), "#scatter");
getScatterData(applicationName, getQueryStartTime(), getQueryEndTime(), scatterCallback);
}
}
function showRequests(applicationName) {
if (isQueryFromNow()) {
getLastBusinessTransactionsData(applicationName, getQueryPeriod(), businessTransactionCallback);
} else {
getBusinessTransactionsData(applicationName, getQueryStartTime(), getQueryEndTime(), businessTransactionCallback);
}
}
</script>
<script id="EdgeBox" type="text/x-jquery-tmpl">
<div class="EdgeBox" style="position:absolute;border:1px solid #000;background:#fff;padding:5px 10px;-webkit-border-radius: 5px; ">
Response statistics
<i class="hippo-help icon-question-sign" onclick="help(1004);"></i>
<ul>
{{var lastKey=''}}
{{each(key, value) histogram}}
<li>&lt;= ${key}ms : ${value}</li>
{{eval lastKey=key}}
{{/each}}
<li>&gt; ${lastKey}ms : ${slow}</li>
<li>Failed : ${error}</li>
</ul>
<hr/>
<a href="#" onclick="alert('Sorry. Not implemented.');">Focus on passing transactions</a>
<i class="hippo-help icon-question-sign" onclick="help(1001);"></i>
<br/><a href="#" onclick="alert('Sorry. Not implemented.');">Show Requests</a>
<i class="hippo-help icon-question-sign" onclick="help(1003);"></i>
<button style="position:absolute;top:2px;right:2px;" onClick="$(this).parent().remove()">X</button>
</div>
</script>
<script id="ServerBox" type="text/x-jquery-tmpl">
<div class="ServerBox" style="position:absolute;border:1px solid #000;background:#fff;padding:5px 10px;-webkit-border-radius: 5px; ">
Application Name
<ul>
<li>${label}</li>
</ul>
Application Type
<ul>
<li>${serviceType}</li>
</ul>
Hosts
<ul>
{{each(key, value) hosts}}
<li>
<span class="label label-success">OK</span>
${value}
<a href="http://nsight.nhncorp.com/dashboard_server/${value.split(':')[0].replace('.nhnsystem.com','')}" target="_blank">(NSight)</a>
</li>
{{/each}}
</ul>
Server instances
<ul>
{{each(key, value) agents}}
<li>
<span class="label label-success">OK</span>
${value.agentId}
<a href="#" onclick="alert('Sorry. Not implemented.');">(Kuvasz)</a>
<a href="#" onclick="alert('Sorry. Not implemented.');">(Tools)</a>
</li>
{{/each}}
</ul>
<hr/>
<a href="#" onclick="alert('Sorry. Not implemented.');">Focus on passing transactions</a>
<i class="hippo-help icon-question-sign" onclick="help(1001);"></i>
<br/><a href="#" onclick="showResponseScatter('${label}');">Response scatter chart</a>
<i class="hippo-help icon-question-sign" onclick="help(1003);"></i>
<br/><a href="#" onclick="showRequests('${label}');">Show Requests</a>
<i class="hippo-help icon-question-sign" onclick="help(1002);"></i>
<button style="position:absolute;top:2px;right:2px;" onClick="$(this).parent().remove()">X</button>
</div>
</script>
<script id="ClientBox" type="text/x-jquery-tmpl">
<div class="ServerBox" style="position:absolute;border:1px solid #000;background:#fff;padding:5px 10px;-webkit-border-radius: 5px; ">
Clients
Calls
<ul>
<li>Sorry. Not implemented.</li>
</ul>
<hr/>
<a href="#" onclick="alert('Sorry. Not implemented.');">Show Requests</a>
<button style="position:absolute;top:2px;right:2px;" onClick="$(this).parent().remove()">X</button>
</div>
</script>
</body>
</html>
-370
View File
@@ -1,370 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<title>PROBE</title>
<meta charset="utf-8">
<meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link href="/common/css/bootstrap/bootstrap.css" rel="stylesheet">
<link href="/common/css/bootstrap/bootstrap-responsive.css" rel="stylesheet"/>
<link href="/common/css/hippo/hippo.css" rel="stylesheet"/>
<link href="/common/css/hippo/sorttable.css" rel="stylesheet"/>
<link href="/common/css/hippo/scatter.css" rel="stylesheet"/>
<link href="/common/css/datepicker.css" rel="stylesheet"/>
<link href="/select2/select2-customized.css" rel="stylesheet"/>
<!-- Le HTML5 shim, for IE6-8 support of HTML5 elements -->
<!--[if lt IE 9]>
<script src="http://html5shim.googlecode.com/svn/trunk/html5.js"></script>
<![endif]-->
<!-- commons -->
<script type="text/javascript" src="/common/js/jquery/jquery-1.9.1.min.js"></script>
<script type="text/javascript" src="/common/js/jquery/jquery-ui-1.10.2.js"></script>
<script type="text/javascript" src="/select2/select2.js"></script>
<script type="text/javascript" src="/common/js/bootstrap.min.js"></script>
<script type="text/javascript" src="/common/js/modernizr-2.6.2.min.js"></script>
<script type="text/javascript" src="/common/js/bootstrap-datepicker.js"></script>
<script type="text/javascript" src="/common/js/sorttable.js"></script>
<script type="text/javascript" src="/common/js/date.format.js"></script>
<script type="text/javascript" src="/common/js/hippo/hippo.js"></script>
<script type="text/javascript" src="/common/js/d3.js"></script>
<!-- scatter chart -->
<script type="text/javascript" src="/common/js/hippo/chart-scatter3.js"></script>
<script type="text/javascript" src="/common/js/hippo/scatter/underscore-min.js"></script>
<script type="text/javascript" src="/common/js/hippo/scatter/date.js"></script>
<script type="text/javascript" src="/common/js/hippo/scatter/jquery.Class.js"></script>
<script type="text/javascript" src="/common/js/hippo/scatter/jquery.dragToSelect.js"></script>
<script type="text/javascript" src="/common/js/hippo/scatter/jquery.BigScatterChart.js"></script>
<!-- server map -->
<script type="text/javascript" src="/common/js/hippo/chart-springy.js"></script>
<script type="text/javascript" src="/common/js/hippo/servermap/jquery.tmpl.min.js"></script>
<script type="text/javascript" src="/common/js/hippo/servermap/Point2D.js"></script>
<script type="text/javascript" src="/common/js/hippo/servermap/intersection.js"></script>
<script type="text/javascript" src="/common/js/hippo/servermap/springy.js"></script>
<script type="text/javascript" src="/common/js/hippo/servermap/canvas.roundRect.js"></script>
<script type="text/javascript" src="/common/js/hippo/servermap/hippoServerMap.js"></script>
<!-- requests list -->
<script type="text/javascript" src="/common/js/hippo/chart-transactions.js"></script>
<!-- help -->
<script type="text/javascript" src="/common/js/hippo/help.js"></script>
<script type="text/javascript" src="/common/js/hippo/message.js"></script>
</head>
<body>
<div class="navbar navbar-fixed-top">
<div class="navbar-inner">
<div class="container">
<a class="brand" href="/">PROBE</a>
<form class="navbar-form pull-left">
<select id="application" disabled>
<option>Loading...</option>
</select>
<div id="until" class="btn-group" data-toggle="buttons-radio">
<button id="now" type="button" class="btn btn-primary active">now</button>
<button id="specific" type="button" class="btn">select</button>
</div>
<input id="date" class="" maxlength="10" size="16" type="text" data-date-format="yyyy/mm/dd" style="text-align:center;width:80px" placeholder="yyyy/mm/dd" disabled>
<input id="time" class="" maxlength="8" size="16" type="text" style="text-align:center;width:75px" placeholder="hh:mm:ss" disabled>
<div id="period" class="btn-group" data-toggle="buttons-radio">
<button type="button" value="5" class="btn btn-mini">5m</button>
<button type="button" value="20" class="btn btn-mini btn-inverse active">20m</button>
<button type="button" value="60" class="btn btn-mini">1h</button>
<button type="button" value="360" class="btn btn-mini">6h</button>
<button type="button" value="1440" class="btn btn-mini">1d</button>
<button type="button" value="2880" class="btn btn-mini">2d</button>
</div>
</form>
<!--
<div class="nav-collapse pull-right">
<ul class="nav">
<li><a href="http://devcafe.nhncorp.com/arcus" target="_blank">About</a></li>
<li><a data-toggle="modal" href="#myModal">Configuration</a></li>
<li><a data-toggle="modal" href="#myHelpModal">Help</a></li>
</ul>
</div>
-->
</div>
</div>
</div>
<!-- MODAL -->
<div class="modal hide fade" id="myModal" style="width:1000px; margin-left:-500px">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal">×</button>
<h3>Configuration</h3>
</div>
<div class="modal-body">
Configuration.
</div>
<div class="modal-footer">
<a href="#" class="btn" data-dismiss="modal">Close</a>
<!--
<a href="#" class="btn btn-primary" onclick="saveNewChart();">Add to dashboard</a>
-->
</div>
</div>
<!-- END OF MODAL -->
<!-- HELP-MODAL -->
<div class="modal hide fade" id="myHelpModal" style="width:1000px; margin-left:-500px">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal">×</button>
<h3>Help</h3>
</div>
<div class="modal-body">
<br/>E-mail to us. <a href="mailto:!hippo@nhn.com">!hippo@nhn.com</a><br/>
<br/>Thank you.<br/><br/>
</div>
<div class="modal-footer">
<a href="#" class="btn" data-dismiss="modal">Close</a>
</div>
</div>
<!-- END OF HELP-MODAL -->
<!-- MODAL -->
<div class="modal hide fade" id="traceIdSelectModal" style="width:1200px; margin-left:-600px">
<div class="modal-header">
<button type="button" class="close" data-dismiss="modal">×</button>
<h3>Selected Traces</h3>
</div>
<div class="modal-body">
<table id="selectedBusinessTransactionsDetail" class="table table-bordered table-hover sortable">
<thead>
<tr>
<th class="sorttable_numeric">#</th>
<th class="sorttable_numeric">Time</th>
<th>TraceId</th>
<th class="sorttable_numeric">Res. Time (ms)</th>
<th>Exception</th>
<th>Application</th>
<th>AgentId</th>
<th>ClientIP</th>
</tr>
</thead>
<tbody>
</tbody>
</table>
<ul id="selectedBusinessTransactionsDetailPager" class="pager">
<li><a href="#">Previous</a></li>
<li><a href="#">Next</a></li>
</ul>
</div>
<div class="modal-footer">
<a href="#" class="btn" data-dismiss="modal">Close</a>
</div>
</div>
<!-- END OF MODAL -->
<div class="container" id="warningMessage"></div>
<!-- MENU -->
<div class="chart-container">
<div class="chart-left">
<div id="springygraph" class="servermap"></div>
</div>
<div class="chart-right">
<h5 style="display:none;" id="scattercharttitle">Response scatter</h5>
<div id="scatterchart"></div>
</div>
</div>
<!-- END OF MENU -->
<script type="text/javascript">
$.extend($.tmpl.tag, {
"var": {
open: "var $1;"
}
});
$.extend($.tmpl.tag, { "eval": { open: "$1;"} });
$(document).ready(function () {
$('#date').datepicker().on('changeDate', function(ev){
updateCharts();
});
$('#time').bind('focus', function(e){
$(this).data("time", $(this).val());
}).bind('blur', function(e){
if ($(this).data("time") !== $(this).val()) {
updateCharts();
}
});
$('#until').delegate("button", "click", function(){
var $this = $(this);
$this.siblings(".btn-primary").removeClass("btn-primary");
$this.addClass("btn-primary");
if ($this.attr("id") === "now") {
$("#date").attr("disabled", "").val("");
$("#time").attr("disabled", "").val("");
updateCharts();
} else {
setQueryDateToNow();
$("#date").attr("disabled", null);
$("#time").attr("disabled", null);
}
});
$('#period').delegate("button", "click", function(){
var $this = $(this);
$this.siblings(".btn-inverse").removeClass("btn-inverse active");
$this.addClass("btn-inverse active");
updateCharts();
$("#auto_refresh").trigger("change");
});
// TODO 조회 버튼을 두 번 연속 눌렀을 때 앞서 수행되던 결과는 취소시켜야 함.
// 예를 들어 1일치 조회하다가 모두 조회 되기 전에 30분 데이터 조회할 때...
function updateCharts() {
var applicationName = $("#application").val().split("@")[0];
if(applicationName == "") {
alert("\n\nSelect an application, please.\n\n");
return true;
}
showServerMap(applicationName);
showResponseScatter(applicationName);
}
$.getJSON("/applications.hippo", function(json) {
var target = $("#application");
target.empty();
$(json).each(function(i, e) {
var html = [];
html.push('<option value="');
html.push(e.applicationName);
html.push("@");
html.push(e.code);
html.push('"');
if (json.length == 1) {
html.push(' selected>');
} else {
html.push('>');
}
html.push(e.applicationName + "@" + e.serviceType);
html.push('</option>');
target.append(html.join(''));
$("#application").attr("disabled", null);
function format(state) {
var chunk = state.text.split("@");
if (chunk.length > 1) {
return "<img class='flag' src='/images/icons/" + chunk[1] + ".png'/> " + chunk[0];
} else {
return state;
}
}
$("#application").select2({
placeholder: "Select an application.",
formatResult: format,
formatSelection: format
});
// 임시.
$(".select2-container").css("top", "4px");
});
});
$("#selectedBusinessTransactionsDetailPager li").click(function() {
alert("[NOT IMPLEMENTED]\n\nNOTE : 한꺼번에 많은 점을 선택했을 때 다 보여주지 않고.. 페이징?? 스크롤 페이징을 사용해도 될 것 같기도 하고...");
});
$("#application").bind("change", function() {
updateCharts();
});
});
</script>
<script id="EdgeBox" type="text/x-jquery-tmpl">
<div class="EdgeBox" style="position:absolute;border:1px solid #000;background:#fff;padding:5px 10px;-webkit-border-radius: 5px; ">
Response statistics
<i class="hippo-action-icon icon-question-sign" onclick="help(1004, this);"></i>
<ul>
{{var lastKey=''}}
{{each(key, value) histogram}}
<li>&lt;= ${key}ms : ${value}</li>
{{eval lastKey=key}}
{{/each}}
<li>&gt; ${lastKey}ms : ${slow}</li>
<li>Failed : ${error}</li>
</ul>
<hr/>
<a href="#" onclick="alert('Sorry. Not implemented.');">Focus on passing transactions</a>
<i class="hippo-action-icon icon-question-sign" onclick="help(1001, this);"></i>
<br/><a href="#" onclick="alert('Sorry. Not implemented.\n query param = ${rawdata.sourceinfo.applicationName} -> ${rawdata.targetinfo.applicationName}');">Show Requests</a>
<i class="hippo-action-icon icon-question-sign" onclick="help(1003, this);"></i>
<button style="position:absolute;top:2px;right:2px;" onClick="$(this).parent().remove()">X</button>
</div>
</script>
<script id="ServerBox" type="text/x-jquery-tmpl">
<div class="ServerBox" style="position:absolute;border:1px solid #000;background:#fff;padding:5px 10px;-webkit-border-radius: 5px; ">
Application Name
<ul>
<li>${label}</li>
</ul>
Application Type
<ul>
<li>${serviceType}</li>
</ul>
{{if hosts.length > 0}}
Hosts
<ul>
{{each(key, value) hosts}}
<li>
<span class="label label-success">OK</span>
${value}
<a href="http://nsight.nhncorp.com/dashboard_server/${value.split(':')[0].replace('.nhnsystem.com','')}" target="_blank">(NSight)</a>
</li>
{{/each}}
</ul>
{{/if}}
{{if agents.length > 0}}
Server instances
<ul>
{{each(key, value) agents}}
<li>
<span class="label label-success">OK</span>
${value.agentId}
<a href="#" onclick="alert('Sorry. Not implemented.');">(Kuvasz)</a>
<a href="#" onclick="alert('Sorry. Not implemented.');">(Tools)</a>
</li>
{{/each}}
</ul>
{{/if}}
<hr/>
<a href="#" onclick="alert('Sorry. Not implemented.');">Focus on passing transactions</a>
<i class="hippo-action-icon icon-question-sign" onclick="help(1001, this);"></i>
<br/><a href="#" onclick="showResponseScatter('${label}');">Response scatter chart</a>
<i class="hippo-action-icon icon-question-sign" onclick="help(1003, this);"></i>
<br/><a href="#" onclick="showRequests('${label}');">Show Requests</a>
<i class="hippo-action-icon icon-question-sign" onclick="help(1002, this);"></i>
<button style="position:absolute;top:2px;right:2px;" onClick="$(this).parent().remove()">X</button>
</div>
</script>
<script id="ClientBox" type="text/x-jquery-tmpl">
<div class="ServerBox" style="position:absolute;border:1px solid #000;background:#fff;padding:5px 10px;-webkit-border-radius: 5px; ">
Clients
Calls
<ul>
<li>Sorry. Not implemented.</li>
</ul>
<hr/>
<a href="#" onclick="alert('Sorry. Not implemented.');">Show Requests</a>
<button style="position:absolute;top:2px;right:2px;" onClick="$(this).parent().remove()">X</button>
</div>
</script>
</body>
</html>
+2 -2
View File
@@ -131,7 +131,7 @@ $(document).ready(function () {
query.push(traces[i].y)
}
$.post("/requestmetadata.hippo", query.join(""), function(d) {
$.post("/transactionmetadata.hippo", query.join(""), function(d) {
writeContents(d);
$("#loader").hide();
})
@@ -165,7 +165,7 @@ var writeContents = function(d) {
html.push("<td>");
html.push("<a href='");
html.push("/selectTransaction.hippo?traceId=" + data[i].traceId + "&focusTimestamp=" + data[i].collectorAcceptTime);
html.push("/transactionInfo.hippo?traceId=" + data[i].traceId + "&focusTimestamp=" + data[i].collectorAcceptTime);
html.push("' target='transactionView'>");
html.push(data[i].traceId);
html.push("</a>");