diff --git a/src/main/java/com/nhn/pinpoint/web/controller/AgentStatController.java b/src/main/java/com/nhn/pinpoint/web/controller/AgentStatController.java index a8ef751b3..2ea055611 100644 --- a/src/main/java/com/nhn/pinpoint/web/controller/AgentStatController.java +++ b/src/main/java/com/nhn/pinpoint/web/controller/AgentStatController.java @@ -22,7 +22,7 @@ import com.nhn.pinpoint.common.bo.AgentInfoBo; import com.nhn.pinpoint.thrift.dto.TAgentStat; import com.nhn.pinpoint.web.service.AgentInfoService; import com.nhn.pinpoint.web.service.AgentStatService; -import com.nhn.pinpoint.web.vo.linechart.AgentStatLineChart; +import com.nhn.pinpoint.web.vo.linechart.agentstat.AgentStatChartGroup; @Controller public class AgentStatController { @@ -45,20 +45,25 @@ public class AgentStatController { @RequestParam("agentId") String agentId, @RequestParam("from") long from, @RequestParam("to") long to, + @RequestParam(value = "sampleRate", required = false) Integer sampleRate, @RequestParam(value = "_callback", required = false) String jsonpCallback) throws Exception { StopWatch watch = new StopWatch(); - watch.start("getAgentStat"); - + watch.start("agentStatService.selectAgentStatList"); List agentStatList = agentStatService.selectAgentStatList(agentId, from, to); - watch.stop(); + if (logger.isInfoEnabled()) { logger.info("getAgentStat(agentId={}, from={}, to={}) : {}ms", agentId, from, to, watch.getLastTaskTimeMillis()); } - AgentStatLineChart chart = new AgentStatLineChart(); + int nPoints = (int) (to - from) / 5000; + if (sampleRate == null) { + sampleRate = nPoints < 300 ? 1 : nPoints / 300; + } + + AgentStatChartGroup chart = new AgentStatChartGroup(); for (TAgentStat each : agentStatList) { - chart.addData(each); + chart.addData(each, sampleRate); } // JSON or JSONP response diff --git a/src/main/java/com/nhn/pinpoint/web/vo/linechart/AgentStatLineChart.java b/src/main/java/com/nhn/pinpoint/web/vo/linechart/AgentStatLineChart.java deleted file mode 100644 index d1816a418..000000000 --- a/src/main/java/com/nhn/pinpoint/web/vo/linechart/AgentStatLineChart.java +++ /dev/null @@ -1,72 +0,0 @@ -package com.nhn.pinpoint.web.vo.linechart; - -import java.util.HashMap; -import java.util.Map; - -import com.nhn.pinpoint.thrift.dto.TAgentStat; -import com.nhn.pinpoint.thrift.dto.TStatWithCmsCollector; -import com.nhn.pinpoint.thrift.dto.TAgentStat._Fields; - -/** - * FIXME 일반화 해야 할까? - * @author harebox - */ -public class AgentStatLineChart { - - private String type; - private Map charts = new HashMap(); - - public void addData(TAgentStat data) { - if (data == null) { - return; - } - - // 먼저 메시지에 포함된 데이터 타입을 알아낸다. - _Fields type = data.getSetField(); - Object typeObject = data.getFieldValue(type); - - switch (type) { - case CMS: - TStatWithCmsCollector stat = (TStatWithCmsCollector) typeObject; - for (TStatWithCmsCollector._Fields each : TStatWithCmsCollector.metaDataMap.keySet()) { - Object fieldValue = stat.getFieldValue(each); - if (! (fieldValue instanceof Long)) { - continue; - } - - if (! charts.containsKey(each.getFieldName())) { - charts.put(each.getFieldName(), new TimestampToValue()); - } - - TimestampToValue chart = charts.get(each.getFieldName()); - chart.addData(stat.getTimestamp(), (Long) fieldValue); - } - break; - case G1: - // FIXME 아직 구현되지 않음. - break; - case PARALLEL: - // FIXME 아직 구현되지 않음. - break; - } - - this.type = type.getFieldName(); - } - - public String getType() { - return type; - } - - public void setType(String type) { - this.type = type; - } - - public Map getCharts() { - return charts; - } - - public void setCharts(Map charts) { - this.charts = charts; - } - -} \ No newline at end of file diff --git a/src/main/java/com/nhn/pinpoint/web/vo/linechart/Chart.java b/src/main/java/com/nhn/pinpoint/web/vo/linechart/Chart.java new file mode 100644 index 000000000..9236ab271 --- /dev/null +++ b/src/main/java/com/nhn/pinpoint/web/vo/linechart/Chart.java @@ -0,0 +1,57 @@ +package com.nhn.pinpoint.web.vo.linechart; + +import java.util.LinkedList; +import java.util.List; + +public abstract class Chart { + + private String title; + private String xAxisName; + private String yAxisName; + + public String getTitle() { + return title; + } + + public void setTitle(String title) { + this.title = title; + } + + public void setXAxisName(String name) { + this.xAxisName = name; + } + + public void setYAxisName(String name) { + this.yAxisName = name; + } + + public String getxAxisName() { + return xAxisName; + } + + public void setxAxisName(String xAxisName) { + this.xAxisName = xAxisName; + } + + public String getyAxisName() { + return yAxisName; + } + + public void setyAxisName(String yAxisName) { + this.yAxisName = yAxisName; + } + + public static final class Points { + + private List points = new LinkedList(); + + public Points() { + } + + public List getPoints() { + return points; + } + + } + +} diff --git a/src/main/java/com/nhn/pinpoint/web/vo/linechart/DownSampler.java b/src/main/java/com/nhn/pinpoint/web/vo/linechart/DownSampler.java new file mode 100644 index 000000000..8f37bd07d --- /dev/null +++ b/src/main/java/com/nhn/pinpoint/web/vo/linechart/DownSampler.java @@ -0,0 +1,13 @@ +package com.nhn.pinpoint.web.vo.linechart; + + +/** + * @author harebox + */ +public interface DownSampler { + + long sampleLong(Long[] longs); + + double sampleDouble(Double[] doubles); + +} diff --git a/src/main/java/com/nhn/pinpoint/web/vo/linechart/DownSamplers.java b/src/main/java/com/nhn/pinpoint/web/vo/linechart/DownSamplers.java new file mode 100644 index 000000000..255aa7628 --- /dev/null +++ b/src/main/java/com/nhn/pinpoint/web/vo/linechart/DownSamplers.java @@ -0,0 +1,104 @@ +package com.nhn.pinpoint.web.vo.linechart; + + +/** + * Time-series 처럼 연속된 데이터를 다운 샘플링한다. + * + * @author harebox + */ +public class DownSamplers { + + public static final DownSampler MIN = new Min(); + public static final DownSampler MAX = new Max(); + public static final DownSampler AVG = new Avg(); + + private DownSamplers() { + } + + static class Min implements DownSampler { + + public long sampleLong(Long[] longs) { + if (longs == null || longs.length == 0) { + return 0; + } + Long min = Long.MAX_VALUE; + for (Long each : longs) { + if (min > each) { + min = each; + } + } + return min; + } + + public double sampleDouble(Double[] doubles) { + if (doubles == null || doubles.length == 0) { + return 0.0; + } + Double min = Double.MAX_VALUE; + for (Double each : doubles) { + if (min > each) { + min = each; + } + } + return min; + } + + } + + static class Max implements DownSampler { + + public long sampleLong(Long[] longs) { + if (longs == null || longs.length == 0) { + return 0; + } + Long max = Long.MIN_VALUE; + for (Long each : longs) { + if (max < each) { + max = each; + } + } + return max; + } + + public double sampleDouble(Double[] doubles) { + if (doubles == null || doubles.length == 0) { + return 0.0; + } + Double max = Double.MIN_VALUE; + for (Double each : doubles) { + if (max < each) { + max = each; + } + } + return max; + } + + } + + static class Avg implements DownSampler { + + public long sampleLong(Long[] longs) { + if (longs == null || longs.length == 0) { + return 0; + } + long total = 0; + for (Long each : longs) { + total += each; + } + return total / longs.length; + } + + public double sampleDouble(Double[] doubles) { + if (doubles == null || doubles.length == 0) { + return 0.0; + } + double total = 0.0; + for (Double each : doubles) { + total += each; + } + return total / doubles.length; + } + + } + +} diff --git a/src/main/java/com/nhn/pinpoint/web/vo/linechart/LineChart.java b/src/main/java/com/nhn/pinpoint/web/vo/linechart/LineChart.java new file mode 100644 index 000000000..b8d4df21b --- /dev/null +++ b/src/main/java/com/nhn/pinpoint/web/vo/linechart/LineChart.java @@ -0,0 +1,25 @@ +package com.nhn.pinpoint.web.vo.linechart; + +import java.util.List; + +public class LineChart extends Chart { + + protected Chart.Points points; + + public LineChart() { + this.points = new Points(); + } + + public void addPoint(Long[] point) { + points.getPoints().add(point); + } + + public void setPoints(Points points) { + this.points = points; + } + + public List getPoints() { + return this.points.getPoints(); + } + +} diff --git a/src/main/java/com/nhn/pinpoint/web/vo/linechart/SampledLineChart.java b/src/main/java/com/nhn/pinpoint/web/vo/linechart/SampledLineChart.java new file mode 100644 index 000000000..805b0486d --- /dev/null +++ b/src/main/java/com/nhn/pinpoint/web/vo/linechart/SampledLineChart.java @@ -0,0 +1,40 @@ +package com.nhn.pinpoint.web.vo.linechart; + +import java.security.InvalidParameterException; + + +public final class SampledLineChart extends LineChart { + + int sampleRate; + int sampleIndex; + Long[] sampleBuffer; + + public SampledLineChart(int sampleRate) { + this.sampleRate = sampleRate; + this.sampleBuffer = new Long[sampleRate]; + this.sampleIndex = 0; + } + + @Override + public void addPoint(Long[] point) { + if (point == null || point.length != 2) { + throw new InvalidParameterException("point array should be Number[2]"); + } + + sampleBuffer[sampleIndex++] = point[1]; + + // FIXME 선택 가능하게. 모두 다 하는 경우에는 그냥 메소드 한번으로 끝낼 수도 있지만 일단... + if (sampleIndex == sampleRate) { + // point[x, minY, maxY, avgY] + Long[] samplePoint = new Long[4]; + samplePoint[0] = point[0]; + samplePoint[1] = DownSamplers.MIN.sampleLong(sampleBuffer); + samplePoint[2] = DownSamplers.MAX.sampleLong(sampleBuffer); + samplePoint[3] = DownSamplers.AVG.sampleLong(sampleBuffer); + + getPoints().add(samplePoint); + sampleIndex = 0; + } + } + +} diff --git a/src/main/java/com/nhn/pinpoint/web/vo/linechart/TimestampToValue.java b/src/main/java/com/nhn/pinpoint/web/vo/linechart/TimestampToValue.java deleted file mode 100644 index a8b809cec..000000000 --- a/src/main/java/com/nhn/pinpoint/web/vo/linechart/TimestampToValue.java +++ /dev/null @@ -1,44 +0,0 @@ -package com.nhn.pinpoint.web.vo.linechart; - -import java.security.InvalidParameterException; -import java.util.ArrayList; -import java.util.List; - -/** - * FIXME 일반화 해야 할까? - * @author harebox - */ -public class TimestampToValue { - private List x; // timestamp - private List y; // value - - public TimestampToValue() { - this.x = new ArrayList(); - this.y = new ArrayList(); - } - - public void addData(long xv, long yv) { - if (x.size() != y.size()) { - throw new InvalidParameterException("invalid status : x-axis.size=" + x.size() + ", y-axis.size=" + y.size()); - } - this.x.add(xv); - this.y.add(yv); - } - - public List getX() { - return x; - } - - public void setX(List x) { - this.x = x; - } - - public List getY() { - return y; - } - - public void setY(List y) { - this.y = y; - } - -} \ No newline at end of file diff --git a/src/main/java/com/nhn/pinpoint/web/vo/linechart/agentstat/AgentStatChartGroup.java b/src/main/java/com/nhn/pinpoint/web/vo/linechart/agentstat/AgentStatChartGroup.java new file mode 100644 index 000000000..f1195222c --- /dev/null +++ b/src/main/java/com/nhn/pinpoint/web/vo/linechart/agentstat/AgentStatChartGroup.java @@ -0,0 +1,119 @@ +package com.nhn.pinpoint.web.vo.linechart.agentstat; + +import java.util.HashMap; +import java.util.Map; + +import com.nhn.pinpoint.thrift.dto.TAgentStat; +import com.nhn.pinpoint.thrift.dto.TAgentStat._Fields; +import com.nhn.pinpoint.thrift.dto.TStatWithCmsCollector; +import com.nhn.pinpoint.thrift.dto.TStatWithG1Collector; +import com.nhn.pinpoint.thrift.dto.TStatWithParallelCollector; +import com.nhn.pinpoint.web.vo.linechart.LineChart; +import com.nhn.pinpoint.web.vo.linechart.SampledLineChart; + +/** + * @author harebox + */ +public class AgentStatChartGroup { + + private String type; + private Map charts = new HashMap(); + + public void addData(TAgentStat data, int sampleRate) { + if (data == null) { + return; + } + + // 먼저 메시지에 포함된 데이터 타입을 알아낸다. + _Fields type = data.getSetField(); + Object typeObject = data.getFieldValue(type); + + // TODO profiler에서 한 것 처럼 리팩토링 해야 한다. + switch (type) { + case CMS: + TStatWithCmsCollector cms = (TStatWithCmsCollector) typeObject; + for (TStatWithCmsCollector._Fields each : TStatWithCmsCollector.metaDataMap.keySet()) { + Object fieldValue = cms.getFieldValue(each); + if (! (fieldValue instanceof Long)) { + continue; + } + + if (! charts.containsKey(each.getFieldName())) { + charts.put(each.getFieldName(), new SampledLineChart(sampleRate)); + } + + LineChart chart = charts.get(each.getFieldName()); + chart.addPoint(new Long[]{ cms.getTimestamp(), (Long) fieldValue }); + } + break; + case G1: + TStatWithG1Collector g1 = (TStatWithG1Collector) typeObject; + for (TStatWithG1Collector._Fields each : TStatWithG1Collector.metaDataMap.keySet()) { + Object fieldValue = g1.getFieldValue(each); + if (! (fieldValue instanceof Long)) { + continue; + } + + if (! charts.containsKey(each.getFieldName())) { + charts.put(each.getFieldName(), new SampledLineChart(sampleRate)); + } + + LineChart chart = charts.get(each.getFieldName()); + chart.addPoint(new Long[]{ g1.getTimestamp(), (Long) fieldValue }); + } + break; + case PARALLEL: + TStatWithParallelCollector parallel = (TStatWithParallelCollector) typeObject; + for (TStatWithParallelCollector._Fields each : TStatWithParallelCollector.metaDataMap.keySet()) { + Object fieldValue = parallel.getFieldValue(each); + if (! (fieldValue instanceof Long)) { + continue; + } + + if (! charts.containsKey(each.getFieldName())) { + charts.put(each.getFieldName(), new SampledLineChart(sampleRate)); + } + + LineChart chart = charts.get(each.getFieldName()); + Long[] point = new Long[]{ parallel.getTimestamp(), (Long) fieldValue }; + chart.addPoint(point); + } + break; + case SERIAL: + TStatWithG1Collector serial = (TStatWithG1Collector) typeObject; + for (TStatWithG1Collector._Fields each : TStatWithG1Collector.metaDataMap.keySet()) { + Object fieldValue = serial.getFieldValue(each); + if (! (fieldValue instanceof Long)) { + continue; + } + + if (! charts.containsKey(each.getFieldName())) { + charts.put(each.getFieldName(), new SampledLineChart(sampleRate)); + } + + LineChart chart = charts.get(each.getFieldName()); + chart.addPoint(new Long[]{ serial.getTimestamp(), (Long) fieldValue }); + } + break; + } + + this.type = type.getFieldName(); + } + + public String getType() { + return type; + } + + public void setType(String type) { + this.type = type; + } + + public Map getCharts() { + return charts; + } + + public void setCharts(Map charts) { + this.charts = charts; + } + +} \ No newline at end of file diff --git a/src/main/webapp/scripts/directives/agentInfo.js b/src/main/webapp/scripts/directives/agentInfo.js index 9fb320fe4..88823fe59 100644 --- a/src/main/webapp/scripts/directives/agentInfo.js +++ b/src/main/webapp/scripts/directives/agentInfo.js @@ -1,7 +1,7 @@ 'use strict'; pinpointApp.constant('agentInfoConfig', { - agentUrl: 'http://10.25.149.249:9996/agents?callback=JSON_CALLBACK&agentId=' + agentStatUrl: '/getAgentStat.pinpoint' }); pinpointApp.directive('agentInfo', [ 'agentInfoConfig', '$routeParams', '$http', '$timeout', function (cfg, $routeParams, $http, $timeout) { @@ -10,153 +10,221 @@ pinpointApp.directive('agentInfo', [ 'agentInfoConfig', '$routeParams', '$http', replace: true, templateUrl: 'views/agentInfo.html', link: function postLink(scope, element, attrs) { + + // define private variables + var oNavbarDao; // define private variables of methods - var getAgentStats, refresh; + var getSampleRate, getAgentStat, showAgentStat, d3MakeGcCharts; // initialize scope.agentInfoTemplate = 'views/agentInfoReady.html'; + // TODO this is dummy + scope.info = [ + { key: 'Application Type', val: 'Tomcat' }, + { key: 'JVM Version', val: '1.6.0_32' }, + { key: 'JVM Options', val: '' } + ]; + /** * scope event of agentList.angetChanged */ - scope.$on('agentList.agentChanged', function (event, agent) { + scope.$on('agentList.agentChanged', function (event, oNavbarDao, agent) { scope.agentInfoTemplate = 'views/agentInfoMain.html'; scope.agent = agent; console.log('got agentList.agentChanged', agent); + showAgentStat(agent.agentId, oNavbarDao.getQueryStartTime(), oNavbarDao.getQueryEndTime(), oNavbarDao.getPeriod()); + }); + + /** + * scope event of navbar.changed + */ + scope.$on('navbar.changed', function (event, oNavbarDao) { + console.log('got navbar.changed', oNavbarDao); }); - return; - /** - * get agent stats - * @param agent_id - * @param callback + * calculate a sampling rate based on the given period + * @param period in minutes */ - getAgentStats = function (agent_id, callback) { - // FIXME collector Stat URL을 제공할 수 있는 API 필요. - // zookeeper에 공통 정보를 기록해두면 될 것 같음. -// var url = 'http://10.25.149.249:9996/agents?callback=JSON_CALLBACK&agentId=' + agent_id; - var url = cfg.agentUrl + agent_id; - var config = { cache: true }; - $http.jsonp(url, config).success(function (data, status) { - callback(data); - }).error(function (data, status) { - console.log("error", data, status); - }); + getSampleRate = function (period) { + var MAX_POINTS = 100; + var points = (period * 60) / 5; + var rate = Math.floor(points / MAX_POINTS); + return points <= MAX_POINTS ? 1 : rate; }; /** - * refresh - * @param interval - */ - refresh = function (interval) { - var timestamp = new Date().getTime(); - var LIMIT_LINE_COUNT = true; - var MAX_LINE_COUNT = 100; - scope.metrics = [ - { id: 'total', title: 'Memory.Total', span: 'span12', line: [ - { id: 'jvm.memory.total.init', key: 'init', values: [] }, - { id: 'jvm.memory.total.used', key: 'used', values: [] }, - { id: 'jvm.memory.total.committed', key: 'committed', values: [] }, - { id: 'jvm.memory.total.max', key: 'max', values: [] }, - { id: 'jvm.gc.ConcurrentMarkSweep', key: 'CMS time', values: [], bar: true } - ] }, - { id: 'heap', title: 'Memory.Heap', span: 'span6', line: [ - { id: 'jvm.memory.heap.init', key: 'init', values: [] }, - { id: 'jvm.memory.heap.used', key: 'used', values: [] }, - { id: 'jvm.memory.heap.committed', key: 'committed', values: [] }, - { id: 'jvm.memory.heap.max', key: 'max', values: [] } - ] }, - { id: 'non_heap', title: 'Memory.Non-Heap', span: 'span6', line: [ - { id: 'jvm.memory.non-heap.init', key: 'init', values: [] }, - { id: 'jvm.memory.non-heap.used', key: 'used', values: [] }, - { id: 'jvm.memory.non-heap.committed', key: 'committed', values: [] }, - { id: 'jvm.memory.non-heap.max', key: 'max', values: [] } - ] } - ]; - scope.metrics.forEach(function (each) { - for (var i = 0; i < each.line.length; ++i) { - for (var j = timestamp - (MAX_LINE_COUNT * 5000); j < timestamp; j += 5000) { - each.line[i].values.push({x: j, y: 0}); - } - } - }); + jvmGcPSMarkSweepCount + jvmGcPSMarkSweepTime + jvmGcPSScavengeCount + jvmGcPSScavengeTime + jvmMemoryPoolsCodeCacheUsage + jvmMemoryPoolsPSEdenSpaceUsage + jvmMemoryPoolsPSOldGenUsage + jvmMemoryPoolsPSPermGenUsage + jvmMemoryPoolsPSSurvivorSpaceUsage + */ + d3MakeGcCharts = function (agentStat, cb) { + var total = { id: 'total', title: 'Total (Heap + Non-Heap)', span: 'span12', line: [ + { id: 'jvmMemoryTotalUsed', key: 'used', values: [] }, + { id: 'jvmMemoryTotalMax', key: 'max', values: [] }, + { id: 'gc', key: 'gc', values: [], bar: true } + ]}; + + var heap = { id: 'heap', title: 'Heap', span: 'span5', line: [ + { id: 'jvmMemoryHeapUsed', key: 'used', values: [] }, + { id: 'jvmMemoryHeapMax', key: 'max', values: [] }, + { id: 'gc', key: 'gc', values: [], bar: true } + ]}; + + var nonheap = { id: 'nonheap', title: 'Non-Heap', span: 'span5', line: [ + { id: 'jvmMemoryNonHeapUsed', key: 'used', values: [] }, + { id: 'jvmMemoryNonHeapMax', key: 'max', values: [] }, + { id: 'gc', key: 'gc', values: [], bar: true } + ]}; + + var result = [ total, heap, nonheap ]; + scope.memoryGroup = result; - var refresher = $timeout(function doRefresh() { - getAgentStats(scope.agent_id, function (data) { - timestamp = new Date().getTime(); - scope.agent_stats = data; - scope.timestamp = d3.time.format('%x %X')(new Date(timestamp)); + var POINTS_TIMESTAMP = 0; + var POINTS_MIN = 1; + var POINTS_MAX = 2; + var POINTS_AVG = 3; - scope.metrics.forEach(function (each) { - // update the data - for (var i = 0; i < each.line.length; ++i) { - // cut off the first of line graph - if (LIMIT_LINE_COUNT && each.line[i].values.length > MAX_LINE_COUNT) { - each.line[i].values.shift(); + result.forEach(function (each) { + each.line.forEach(function (line) { + if (line.bar) { + // bar chart + var key; + if ('serial' == agentStat.type) { + key = ''; + } else if ('parallel' == agentStat.type) { + key = 'jvmGcPSMarkSweep'; + } else if ('cms' == agentStat.type) { + key = ''; + } else if ('g1' == agentStat.type) { + key = ''; + } + if (key) { + var pointsTime = agentStat.charts[key+'Time'].points; + var pointsCount = agentStat.charts[key+'Count'].points; + + if (pointsTime.length != pointsCount.length) { + console.log('assertion error', 'time.length != count.length'); + return; } - if (each.line[i].bar) { - // bar chart - var time = data[each.line[i].id + ".time"].value; - var prev_time = each.line[i].prev_time; - if (prev_time && time - prev_time > 0) { - each.line[i].values.push({x: timestamp, y: time - prev_time}); + + // 1st point + line.values.push({x: pointsTime[0][POINTS_TIMESTAMP], y: 0}); + line.prevTime = 0; + line.prevCount = 0; + + for (var i = pointsCount.length-1; i >= 0; --i) { + var timestamp = pointsTime[i][POINTS_TIMESTAMP]; + var currTime = pointsTime[i][POINTS_MAX]; + var currCount = pointsCount[i][POINTS_MAX]; + var prevTime = line.prevTime; + var prevCount = line.prevCount; + + if ((currCount - prevCount > 0) && (currTime - prevTime > 0)) { + line.values.push({x: timestamp, y: currTime - prevTime}); + line.prevTime = currTime; + line.prevCount = currCount; } else { - each.line[i].values.push({x: timestamp, y: 0}); + line.values.push({x: timestamp, y: 0}); } - each.line[i].prev_time = time; - } else { - // line chart - each.line[i].values.push({x: timestamp, y: data[each.line[i].id].value}); } } - // FIXME update the graph... should be a directive. - nv.addGraph(function () { - var chart = nv.models.linePlusBarChart(); - chart.x(function (d, i) { - return i; - }); - chart.xAxis.tickFormat(function (d) { - var dx = each.line[0].values[d] && each.line[0].values[d].x || 0; - return d3.time.format('%X')(new Date(dx)); - }); - chart.y1Axis.axisLabel('CMS elapsed (ms)').tickFormat(function (d) { - return d; - }); - chart.y2Axis.tickFormat(function (d) { - var sizes = [' B', 'KB', 'MB', 'GB', 'TB']; - var posttxt = 0; - var precision = 2; - if (d == 0) return '0'; - while (d >= 1024) { - posttxt++; - d = d / 1024; - } - return parseInt(d).toFixed(precision) + " " + sizes[posttxt]; - }); - chart.bars.forceY([0]); - chart.lines.forceY([0]); - chart.margin({top: 30, right: 100, bottom: 50, left: 100}) - d3.select('#line_' + each.id).datum(each.line).transition().duration(100).call(chart); - d3.select("#circle").attr("stroke-width", "1px"); - nv.utils.windowResize(chart.update); - return chart; - }); + } else { + // line chart + var points = agentStat.charts[line.id].points; + for (var i = points.length-1; i >= 0; --i) { + line.values.push({x: points[i][POINTS_TIMESTAMP], y: points[i][POINTS_MAX]}); + }; + } + }); + console.log(each.line); + + // draw a chart + nv.addGraph(function () { + var chart = nv.models.linePlusBarChart(); + chart.x(function (d, i) { + return i; }); + chart.xAxis.tickFormat(function (d) { + var dx = each.line[0].values[d] && each.line[0].values[d].x || 0; + return d3.time.format('%X')(new Date(dx)); + }); + chart.y1Axis.axisLabel('GC elapsed time (ms)').tickFormat(function (d) { + return d; + }); + chart.y2Axis.tickFormat(function (d) { + var sizes = [' B', 'KB', 'MB', 'GB', 'TB']; + var posttxt = 0; + var precision = 2; + if (d == 0) return '0'; + while (d >= 1024) { + posttxt++; + d = d / 1024; + } + return parseInt(d).toFixed(precision) + " " + sizes[posttxt]; + }); + chart.bars.forceY([0]); + chart.lines.forceY([0]); + chart.margin({top: 30, right: 100, bottom: 50, left: 100}) + d3.select('#line_' + each.id).datum(each.line).transition().duration(100).call(chart); + d3.select("#circle").attr("stroke-width", "1px"); + nv.utils.windowResize(chart.update); + return chart; }); - refresher = $timeout(doRefresh, interval); }); - - // destroy the timer on close - //$scope.$on('$destroy', function(e) { - // $timeout.cancel(refresher); - //}); }; - scope.agent_id = $routeParams.agentId; - scope.agent_stats = {}; - refresh(5000); + /** + * get agent stat + * @param query + * @param cb + */ + getAgentStat = function (query, cb) { + jQuery.ajax({ + type: 'GET', + url: cfg.agentStatUrl, + cache: false, + dataType: 'json', + data: query, + success: function (result) { + console.log('result', result); + cb(result); + }, + error: function (xhr, status, error) { + console.log("ERROR", status, error); + } + }); + }; + + /** + * show agent stat + * @param agentId + * @param from + * @param to + */ + showAgentStat = function (agentId, from, to, period) { + var query = { + agentId: agentId, + from: from, + to: to, + sampleRate: getSampleRate(period) + }; + + getAgentStat(query, function (result) { + scope.agentStat = result; + scope.info.push({key:'JVM GC Type', val:result.type}); + d3MakeGcCharts(result, function() { }); + scope.$digest(); + }); + }; } }; }]); diff --git a/src/main/webapp/scripts/directives/agentList.js b/src/main/webapp/scripts/directives/agentList.js index 2e3318827..06c4f941c 100644 --- a/src/main/webapp/scripts/directives/agentList.js +++ b/src/main/webapp/scripts/directives/agentList.js @@ -68,7 +68,7 @@ pinpointApp.directive('agentList', [ 'agentListConfig', '$rootScope', function ( */ scope.select = function (agent) { scope.currentAgent = agent; - $rootScope.$broadcast('agentList.agentChanged', agent); + $rootScope.$broadcast('agentList.agentChanged', oNavbarDao, agent); }; /** diff --git a/src/main/webapp/views/agentInfoMain.html b/src/main/webapp/views/agentInfoMain.html index 9d6693ce1..79b2d89a7 100644 --- a/src/main/webapp/views/agentInfoMain.html +++ b/src/main/webapp/views/agentInfoMain.html @@ -5,12 +5,36 @@
-
-
{{each.title}}
- +
+

Informations

+
+ + + + + + + + + + + + + +
KeyValue
{{i.key}}{{i.val}}
+
+

Memory & Garbage Collection

+
+
+
{{each.title}}
+ +
+
+
+
diff --git a/src/test/java/com/nhn/pinpoint/web/dao/hbase/HbaseAgentStatDaoTest.java b/src/test/java/com/nhn/pinpoint/web/dao/hbase/HbaseAgentStatDaoTest.java index a6f4af462..619887cca 100644 --- a/src/test/java/com/nhn/pinpoint/web/dao/hbase/HbaseAgentStatDaoTest.java +++ b/src/test/java/com/nhn/pinpoint/web/dao/hbase/HbaseAgentStatDaoTest.java @@ -13,7 +13,7 @@ import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; * @author harebox */ @RunWith(SpringJUnit4ClassRunner.class) -@ContextConfiguration("classpath:applicationContext-test.xml") +@ContextConfiguration("classpath:applicationContext.xml") public class HbaseAgentStatDaoTest { @Autowired @@ -22,7 +22,7 @@ public class HbaseAgentStatDaoTest { @Test public void selectAgentStat() { long timestamp = System.currentTimeMillis(); - List result = dao.scanAgentStatList("FRONT-WEB", timestamp - 100000, timestamp); + List result = dao.scanAgentStatList("FRONT-WEB1", timestamp - 100000, timestamp); System.out.println(result); } diff --git a/src/test/java/com/nhn/pinpoint/web/vo/linechart/SampledLineChartTest.java b/src/test/java/com/nhn/pinpoint/web/vo/linechart/SampledLineChartTest.java new file mode 100644 index 000000000..69dca9a67 --- /dev/null +++ b/src/test/java/com/nhn/pinpoint/web/vo/linechart/SampledLineChartTest.java @@ -0,0 +1,28 @@ +package com.nhn.pinpoint.web.vo.linechart; + +import static org.junit.Assert.assertTrue; + +import org.codehaus.jackson.map.ObjectMapper; +import org.junit.Test; + +public class SampledLineChartTest { + + @Test + public void tdd() throws Exception { + int sampleRate = 60; + int totalPoints = 10000; + + LineChart lineChart = new SampledLineChart(sampleRate); + + for (long i = 0; i < totalPoints; i++) { + lineChart.addPoint(new Long[]{i, i}); + } + + assertTrue(lineChart.getPoints().size() == totalPoints / sampleRate); + + ObjectMapper mapper = new ObjectMapper(); + String result = mapper.writeValueAsString(lineChart); + System.out.println(result); + } + +}