| OLD | NEW |
| 1 // Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file |
| 2 // for details. All rights reserved. Use of this source code is governed by a | 2 // for details. All rights reserved. Use of this source code is governed by a |
| 3 // BSD-style license that can be found in the LICENSE file. | 3 // BSD-style license that can be found in the LICENSE file. |
| 4 | 4 |
| 5 library metrics; | 5 library metrics; |
| 6 | 6 |
| 7 import 'dart:async'; | 7 import 'dart:async'; |
| 8 import 'dart:html'; | 8 import 'dart:html'; |
| 9 import 'observatory_element.dart'; | 9 import 'package:observatory/models.dart' as M; |
| 10 import 'package:charted/charted.dart'; | 10 import 'package:observatory/src/elements/helpers/rendering_scheduler.dart'; |
| 11 import 'package:observatory/app.dart'; | 11 import 'package:observatory/src/elements/helpers/tag.dart'; |
| 12 import 'package:observatory/service.dart'; | 12 import 'package:observatory/src/elements/metric/details.dart'; |
| 13 import 'package:polymer/polymer.dart'; | 13 import 'package:observatory/src/elements/metric/graph.dart'; |
| 14 import 'package:observatory/src/elements/nav/bar.dart'; |
| 15 import 'package:observatory/src/elements/nav/isolate_menu.dart'; |
| 16 import 'package:observatory/src/elements/nav/menu.dart'; |
| 17 import 'package:observatory/src/elements/nav/notify.dart'; |
| 18 import 'package:observatory/src/elements/nav/refresh.dart'; |
| 19 import 'package:observatory/src/elements/nav/top_menu.dart'; |
| 20 import 'package:observatory/src/elements/nav/vm_menu.dart'; |
| 14 | 21 |
| 15 @CustomTag('metrics-page') | 22 class MetricsPageElement extends HtmlElement implements Renderable { |
| 16 class MetricsPageElement extends ObservatoryElement { | 23 static const tag = const Tag<MetricsPageElement>('metrics-page', |
| 24 dependencies: const [ |
| 25 MetricDetailsElement.tag, |
| 26 MetricGraphElement.tag, |
| 27 NavBarElement.tag, |
| 28 NavTopMenuElement.tag, |
| 29 NavVMMenuElement.tag, |
| 30 NavIsolateMenuElement.tag, |
| 31 NavMenuElement.tag, |
| 32 NavRefreshElement.tag, |
| 33 NavNotifyElement.tag, |
| 34 ]); |
| 35 |
| 36 RenderingScheduler<MetricsPageElement> _r; |
| 37 |
| 38 Stream<RenderedEvent<MetricsPageElement>> get onRendered => _r.onRendered; |
| 39 |
| 40 M.VM _vm; |
| 41 M.IsolateRef _isolate; |
| 42 M.EventRepository _events; |
| 43 M.NotificationRepository _notifications; |
| 44 M.MetricRepository _metrics; |
| 45 List<M.Metric> _available; |
| 46 M.Metric _selected; |
| 47 |
| 48 |
| 49 M.VMRef get vm => _vm; |
| 50 M.IsolateRef get isolate => _isolate; |
| 51 M.NotificationRepository get notifications => _notifications; |
| 52 |
| 53 factory MetricsPageElement(M.VM vm, M.IsolateRef isolate, |
| 54 M.EventRepository events, |
| 55 M.NotificationRepository notifications, |
| 56 M.MetricRepository metrics, |
| 57 {RenderingQueue queue}) { |
| 58 assert(vm != null); |
| 59 assert(isolate != null); |
| 60 assert(events != null); |
| 61 assert(notifications != null); |
| 62 MetricsPageElement e = document.createElement(tag.name); |
| 63 e._r = new RenderingScheduler(e, queue: queue); |
| 64 e._vm = vm; |
| 65 e._isolate = isolate; |
| 66 e._events = events; |
| 67 e._notifications = notifications; |
| 68 e._metrics = metrics; |
| 69 return e; |
| 70 } |
| 71 |
| 17 MetricsPageElement.created() : super.created(); | 72 MetricsPageElement.created() : super.created(); |
| 18 | 73 |
| 19 @observable MetricsPage page; | 74 @override |
| 20 @observable Isolate isolate; | 75 attached() { |
| 21 @observable ServiceMetric selectedMetric; | 76 super.attached(); |
| 22 | 77 _r.enable(); |
| 23 void _autoPickSelectedMetric() { | 78 _refresh(); |
| 24 if (selectedMetric != null) { | |
| 25 return; | |
| 26 } | |
| 27 // Attempt to pick the last selected metric. | |
| 28 if ((isolate != null) && (page != null) && | |
| 29 (page.selectedMetricId != null)) { | |
| 30 selectedMetric = isolate.dartMetrics[page.selectedMetricId]; | |
| 31 if (selectedMetric != null) { | |
| 32 return; | |
| 33 } | |
| 34 selectedMetric = isolate.nativeMetrics[page.selectedMetricId]; | |
| 35 } | |
| 36 if ((selectedMetric == null) && (isolate != null)) { | |
| 37 var values = isolate.dartMetrics.values.toList(); | |
| 38 if ((values != null) && (values.length > 0)) { | |
| 39 // Fall back and pick the first isolate metric. | |
| 40 selectedMetric = values.first; | |
| 41 } | |
| 42 if (selectedMetric != null) { | |
| 43 return; | |
| 44 } | |
| 45 values = isolate.nativeMetrics.values.toList(); | |
| 46 if ((values != null) && (values.length > 0)) { | |
| 47 // Fall back and pick the first isolate metric. | |
| 48 selectedMetric = values.first; | |
| 49 } | |
| 50 } | |
| 51 } | 79 } |
| 52 | 80 |
| 53 void attached() { | 81 @override |
| 54 _autoPickSelectedMetric(); | 82 detached() { |
| 83 super.detached(); |
| 84 _r.disable(notify: true); |
| 85 children = []; |
| 55 } | 86 } |
| 56 | 87 |
| 57 void isolateChanged(oldValue) { | 88 void render() { |
| 58 if (isolate != null) { | 89 children = [ |
| 59 isolate.refreshMetrics().then((_) { | 90 new NavBarElement(queue: _r.queue) |
| 60 _autoPickSelectedMetric(); | 91 ..children = [ |
| 61 }); | 92 new NavTopMenuElement(queue: _r.queue), |
| 62 } | 93 new NavVMMenuElement(_vm, _events, queue: _r.queue), |
| 94 new NavIsolateMenuElement(_isolate, _events, queue: _r.queue), |
| 95 new NavMenuElement('metrics', last: true, queue: _r.queue), |
| 96 new NavRefreshElement(queue: _r.queue) |
| 97 ..onRefresh.listen((e) { |
| 98 e.element.disabled = true; |
| 99 _refresh(); |
| 100 }), |
| 101 new NavNotifyElement(_notifications, queue: _r.queue) |
| 102 ], |
| 103 new DivElement()..classes = ['content-centered-big'] |
| 104 ..children = [ |
| 105 new HeadingElement.h2()..text = 'Metrics', |
| 106 new HRElement(), |
| 107 new DivElement()..classes = ['memberList'] |
| 108 ..children = [ |
| 109 new DivElement()..classes = ['memberItem'] |
| 110 ..children = [ |
| 111 new DivElement()..classes = ['memberName'] |
| 112 ..text = 'Metric', |
| 113 new DivElement()..classes = ['memberValue'] |
| 114 ..children = _available == null |
| 115 ? [new SpanElement()..text = 'Loading..'] |
| 116 : _createMetricSelect() |
| 117 ] |
| 118 ], |
| 119 new HRElement(), |
| 120 new DivElement() |
| 121 ..children = _selected == null ? const [] |
| 122 : [ |
| 123 new MetricDetailsElement(_isolate, _selected, _metrics, |
| 124 queue: _r.queue) |
| 125 ], |
| 126 new HRElement(), |
| 127 new DivElement()..classes = ['graph'] |
| 128 ..children = _selected == null ? const [] |
| 129 : [ |
| 130 new MetricGraphElement(_isolate, _selected, _metrics, |
| 131 queue: _r.queue) |
| 132 ] |
| 133 ], |
| 134 ]; |
| 63 } | 135 } |
| 64 | 136 |
| 65 Future refresh() { | 137 Future _refresh() async { |
| 66 return isolate.refreshMetrics(); | 138 _available = (await _metrics.list(_isolate)).toList(); |
| 139 if (!_available.contains(_selected)) { |
| 140 _selected = _available.first; |
| 141 } |
| 142 _r.dirty(); |
| 67 } | 143 } |
| 68 | 144 |
| 69 void selectMetric(Event e, var detail, Element target) { | 145 List<Element> _createMetricSelect() { |
| 70 String id = target.attributes['data-id']; | 146 var s; |
| 71 selectedMetric = isolate.dartMetrics[id]; | 147 return [ |
| 72 if (selectedMetric == null) { | 148 s = new SelectElement() |
| 73 // Check VM metrics. | 149 ..value = _selected.name |
| 74 selectedMetric = isolate.nativeMetrics[id]; | 150 ..children = _available.map((metric) { |
| 75 } | 151 return new OptionElement(value: metric.name, |
| 76 if (selectedMetric != null) { | 152 selected: _selected == metric) |
| 77 page.selectedMetricId = id; | 153 ..text = metric.name; |
| 78 } else { | 154 }).toList(growable: false) |
| 79 page.selectedMetricId = null; | 155 ..onChange.listen((_) { |
| 80 } | 156 _selected = _available[s.selectedIndex]; |
| 157 _r.dirty(); |
| 158 }) |
| 159 ]; |
| 81 } | 160 } |
| 82 } | 161 } |
| 83 | |
| 84 @CustomTag('metric-details') | |
| 85 class MetricDetailsElement extends ObservatoryElement { | |
| 86 MetricDetailsElement.created() : super.created(); | |
| 87 @published MetricsPage page; | |
| 88 @published ServiceMetric metric; | |
| 89 | |
| 90 int _findIndex(SelectElement element, int value) { | |
| 91 if (element == null) { | |
| 92 return null; | |
| 93 } | |
| 94 for (var i = 0; i < element.options.length; i++) { | |
| 95 var optionElement = element.options[i]; | |
| 96 int optionValue = int.parse(optionElement.value); | |
| 97 if (optionValue == value) { | |
| 98 return i; | |
| 99 } | |
| 100 } | |
| 101 return null; | |
| 102 } | |
| 103 | |
| 104 void attached() { | |
| 105 super.attached(); | |
| 106 _updateSelectedIndexes(); | |
| 107 } | |
| 108 | |
| 109 void _updateSelectedIndexes() { | |
| 110 if (metric == null) { | |
| 111 return; | |
| 112 } | |
| 113 SelectElement refreshRateElement = shadowRoot.querySelector('#refreshrate'); | |
| 114 if (refreshRateElement == null) { | |
| 115 // Race between shadowRoot setup and events. | |
| 116 return; | |
| 117 } | |
| 118 int period = 0; | |
| 119 if (metric.poller != null) { | |
| 120 period = metric.poller.pollPeriod.inMilliseconds; | |
| 121 } | |
| 122 var index = _findIndex(refreshRateElement, period); | |
| 123 assert(index != null); | |
| 124 refreshRateElement.selectedIndex = index; | |
| 125 SelectElement bufferSizeElement = shadowRoot.querySelector('#buffersize'); | |
| 126 index = _findIndex(bufferSizeElement, metric.sampleBufferSize); | |
| 127 assert(index != null); | |
| 128 bufferSizeElement.selectedIndex = index; | |
| 129 } | |
| 130 | |
| 131 metricChanged(oldValue) { | |
| 132 _updateSelectedIndexes(); | |
| 133 } | |
| 134 | |
| 135 void refreshRateChange(Event e, var detail, Element target) { | |
| 136 var value = int.parse((target as SelectElement).value); | |
| 137 if (metric == null) { | |
| 138 return; | |
| 139 } | |
| 140 page.setRefreshPeriod(value, metric); | |
| 141 } | |
| 142 | |
| 143 void sampleBufferSizeChange(Event e, var detail, Element target) { | |
| 144 var value = int.parse((target as SelectElement).value); | |
| 145 if (metric == null) { | |
| 146 return; | |
| 147 } | |
| 148 metric.sampleBufferSize = value; | |
| 149 } | |
| 150 } | |
| 151 | |
| 152 @CustomTag('metrics-graph') | |
| 153 class MetricsGraphElement extends ObservatoryElement { | |
| 154 MetricsGraphElement.created() : super.created(); | |
| 155 | |
| 156 HtmlElement _wrapper; | |
| 157 HtmlElement _areaHost; | |
| 158 CartesianArea _area; | |
| 159 ChartData _data; | |
| 160 final _columns = [ | |
| 161 new ChartColumnSpec(label: 'Time', type: ChartColumnSpec.TYPE_TIMESTAMP), | |
| 162 new ChartColumnSpec(label: 'Value', formatter: (v) => v.toString()) | |
| 163 ]; | |
| 164 final _rows = [[0, 1000000.0]]; | |
| 165 | |
| 166 @published ServiceMetric metric; | |
| 167 @observable Isolate isolate; | |
| 168 | |
| 169 void attached() { | |
| 170 super.attached(); | |
| 171 // Redraw once a second. | |
| 172 pollPeriod = new Duration(seconds: 1); | |
| 173 _reset(); | |
| 174 } | |
| 175 | |
| 176 void onPoll() { | |
| 177 if (metric == null) { | |
| 178 return; | |
| 179 } | |
| 180 _update(); | |
| 181 _draw(); | |
| 182 } | |
| 183 | |
| 184 void _reset() { | |
| 185 _rows.clear(); | |
| 186 _wrapper = shadowRoot.querySelector('#metric-chart'); | |
| 187 assert(_wrapper != null); | |
| 188 _areaHost = _wrapper.querySelector('.chart-host'); | |
| 189 assert(_areaHost != null); | |
| 190 _areaHost.children.clear(); | |
| 191 var series = new ChartSeries("one", [1], new LineChartRenderer()); | |
| 192 var config = new ChartConfig([series], [0]); | |
| 193 config.minimumSize = new Rect(800, 600); | |
| 194 _data = new ChartData(_columns, _rows); | |
| 195 _area = new CartesianArea(_areaHost, | |
| 196 _data, | |
| 197 config, | |
| 198 state: new ChartState()); | |
| 199 } | |
| 200 | |
| 201 void _update() { | |
| 202 _rows.clear(); | |
| 203 for (var i = 0; i < metric.samples.length; i++) { | |
| 204 var sample = metric.samples[i]; | |
| 205 _rows.add([sample.time.millisecondsSinceEpoch, sample.value]); | |
| 206 } | |
| 207 } | |
| 208 | |
| 209 void _draw() { | |
| 210 if (_rows.length < 2) { | |
| 211 return; | |
| 212 } | |
| 213 _area.data = new ChartData(_columns, _rows); | |
| 214 _area.draw(); | |
| 215 } | |
| 216 | |
| 217 metricChanged(oldValue) { | |
| 218 if (oldValue != metric) { | |
| 219 _reset(); | |
| 220 } | |
| 221 } | |
| 222 } | |
| OLD | NEW |