Index: runtime/observatory/lib/src/elements/logging.dart |
diff --git a/runtime/observatory/lib/src/elements/logging.dart b/runtime/observatory/lib/src/elements/logging.dart |
index 8f263050e1657d803f9ba75e41e129b4c26b3959..e519a0a9b94f0d10bd4c3bf869ef49b9d75401a9 100644 |
--- a/runtime/observatory/lib/src/elements/logging.dart |
+++ b/runtime/observatory/lib/src/elements/logging.dart |
@@ -6,196 +6,127 @@ library logging_page; |
import 'dart:async'; |
import 'dart:html'; |
-import 'observatory_element.dart'; |
import 'package:logging/logging.dart'; |
-import 'package:observatory/service.dart'; |
-import 'package:observatory/app.dart'; |
-import 'package:observatory/elements.dart'; |
-import 'package:observatory/utils.dart'; |
-import 'package:polymer/polymer.dart'; |
- |
-@CustomTag('logging-page') |
-class LoggingPageElement extends ObservatoryElement { |
- static const _kPageSelector = '#page'; |
- static const _kLogSelector = '#log'; |
- static const _kSeverityLevelSelector = '#severityLevelSelector'; |
+import 'package:observatory/models.dart' as M; |
+import 'package:observatory/src/elements/helpers/rendering_scheduler.dart'; |
+import 'package:observatory/src/elements/helpers/tag.dart'; |
+import 'package:observatory/src/elements/logging_list.dart'; |
+import 'package:observatory/src/elements/nav/bar.dart'; |
+import 'package:observatory/src/elements/nav/class_menu.dart'; |
+import 'package:observatory/src/elements/nav/isolate_menu.dart'; |
+import 'package:observatory/src/elements/nav/menu.dart'; |
+import 'package:observatory/src/elements/nav/notify.dart'; |
+import 'package:observatory/src/elements/nav/refresh.dart'; |
+import 'package:observatory/src/elements/nav/top_menu.dart'; |
+import 'package:observatory/src/elements/nav/vm_menu.dart'; |
+import 'package:observatory/src/elements/view_footer.dart'; |
+ |
+class LoggingPageElement extends HtmlElement implements Renderable { |
+ static const tag = const Tag<LoggingPageElement>('logging-page', |
+ dependencies: const [ |
+ LoggingListElement.tag, |
+ NavBarElement.tag, |
+ NavClassMenuElement.tag, |
+ NavTopMenuElement.tag, |
+ NavVMMenuElement.tag, |
+ NavIsolateMenuElement.tag, |
+ NavMenuElement.tag, |
+ NavRefreshElement.tag, |
+ NavNotifyElement.tag, |
+ ViewFooterElement.tag |
+ ]); |
+ |
+ RenderingScheduler<LoggingPageElement> _r; |
+ |
+ Stream<RenderedEvent<LoggingPageElement>> get onRendered => _r.onRendered; |
+ |
+ M.VM _vm; |
+ M.IsolateRef _isolate; |
+ M.EventRepository _events; |
+ M.NotificationRepository _notifications; |
+ Level _level = Level.ALL; |
+ |
+ |
+ M.VMRef get vm => _vm; |
+ M.IsolateRef get isolate => _isolate; |
+ M.NotificationRepository get notifications => _notifications; |
+ |
+ factory LoggingPageElement(M.VM vm, M.IsolateRef isolate, |
+ M.EventRepository events, |
+ M.NotificationRepository notifications, |
+ {RenderingQueue queue}) { |
+ assert(vm != null); |
+ assert(isolate != null); |
+ assert(events != null); |
+ assert(notifications != null); |
+ LoggingPageElement e = document.createElement(tag.name); |
+ e._r = new RenderingScheduler(e, queue: queue); |
+ e._vm = vm; |
+ e._isolate = isolate; |
+ e._events = events; |
+ e._notifications = notifications; |
+ return e; |
+ } |
LoggingPageElement.created() : super.created(); |
- domReady() { |
- super.domReady(); |
- _insertLevels(); |
- } |
- |
+ @override |
attached() { |
super.attached(); |
- _sub(); |
- _resizeSubscription = window.onResize.listen((_) => _updatePageHeight()); |
- _updatePageHeight(); |
- // Turn on the periodic poll timer for this page. |
- pollPeriod = const Duration(milliseconds:100); |
+ _r.enable(); |
} |
+ @override |
detached() { |
super.detached(); |
- if (_resizeSubscription != null) { |
- _resizeSubscription.cancel(); |
- _resizeSubscription = null; |
- } |
- _unsub(); |
- } |
- |
- void onPoll() { |
- _flushPendingLogs(); |
- } |
- |
- _updatePageHeight() { |
- HtmlElement e = shadowRoot.querySelector(_kPageSelector); |
- final totalHeight = window.innerHeight; |
- final top = e.offset.top; |
- final bottomMargin = 32; |
- final mainHeight = totalHeight - top - bottomMargin; |
- e.style.setProperty('height', '${mainHeight}px'); |
- } |
- |
- _insertLevels() { |
- SelectElement severityLevelSelector = |
- shadowRoot.querySelector(_kSeverityLevelSelector); |
- severityLevelSelector.children.clear(); |
- _maxLevelLabelLength = 0; |
- for (var level in Level.LEVELS) { |
- var option = new OptionElement(); |
- option.value = level.value.toString(); |
- option.label = level.name; |
- if (level.name.length > _maxLevelLabelLength) { |
- _maxLevelLabelLength = level.name.length; |
- } |
- severityLevelSelector.children.add(option); |
- } |
- severityLevelSelector.selectedIndex = 0; |
- severityLevel = Level.ALL.value.toString(); |
- } |
- |
- _reset() { |
- logRecords.clear(); |
- _unsub(); |
- _sub(); |
- _renderFull(); |
- } |
- |
- _unsub() { |
- cancelFutureSubscription(_loggingSubscriptionFuture); |
- _loggingSubscriptionFuture = null; |
- } |
- |
- _sub() { |
- if (_loggingSubscriptionFuture != null) { |
- // Already subscribed. |
- return; |
- } |
- _loggingSubscriptionFuture = |
- app.vm.listenEventStream(Isolate.kLoggingStream, _onEvent); |
- } |
- |
- _append(Map logRecord) { |
- logRecords.add(logRecord); |
- if (_shouldDisplay(logRecord)) { |
- // Queue for display. |
- pendingLogRecords.add(logRecord); |
- } |
- } |
- |
- Element _renderAppend(Map logRecord) { |
- DivElement logContainer = shadowRoot.querySelector(_kLogSelector); |
- var element = new DivElement(); |
- element.classes.add('logItem'); |
- element.classes.add(logRecord['level'].name); |
- element.appendText( |
- '${logRecord["level"].name.padLeft(_maxLevelLabelLength)} ' |
- '${Utils.formatDateTime(logRecord["time"])} ' |
- '${logRecord["message"].valueAsString}\n'); |
- logContainer.children.add(element); |
- return element; |
+ _r.disable(notify: true); |
+ children = []; |
+ } |
+ |
+ LoggingListElement _logs; |
+ |
+ void render() { |
+ _logs = _logs ?? new LoggingListElement(_isolate, _events); |
+ _logs.level = _level; |
+ children = [ |
+ new NavBarElement(queue: _r.queue) |
+ ..children = [ |
+ new NavTopMenuElement(queue: _r.queue), |
+ new NavVMMenuElement(_vm, _events, queue: _r.queue), |
+ new NavIsolateMenuElement(_isolate, _events, queue: _r.queue), |
+ new NavMenuElement('logging', last: true, queue: _r.queue), |
+ new NavRefreshElement(label: 'clear', queue: _r.queue) |
+ ..onRefresh.listen((e) async { |
+ e.element.disabled = true; |
+ _logs = null; |
+ _r.dirty(); |
+ }), |
+ new NavNotifyElement(_notifications, queue: _r.queue) |
+ ], |
+ new DivElement()..classes = ['content-centered-big'] |
+ ..children = [ |
+ new HeadingElement.h2()..text = 'Logging', |
+ new SpanElement() |
+ ..text = 'Show messages with severity ', |
+ _createLevelSelector(), |
+ new HRElement(), |
+ _logs |
+ ] |
+ ]; |
+ } |
+ |
+ Element _createLevelSelector() { |
+ var s = new SelectElement() |
+ ..value = _level.name |
+ ..children = Level.LEVELS.map((level) { |
+ return new OptionElement(value : level.name, |
+ selected: _level == level) |
+ ..text = level.name; |
+ }).toList(growable: false); |
+ s.onChange.listen((_) { |
+ _level = Level.LEVELS[s.selectedIndex]; |
+ _r.dirty(); |
+ }); |
+ return s; |
} |
- |
- _renderFull() { |
- DivElement logContainer = shadowRoot.querySelector(_kLogSelector); |
- logContainer.children.clear(); |
- pendingLogRecords.clear(); |
- for (var logRecord in logRecords) { |
- if (_shouldDisplay(logRecord)) { |
- _renderAppend(logRecord); |
- } |
- } |
- _scrollToBottom(logContainer); |
- } |
- |
- /// Is [container] scrolled to the within [threshold] pixels of the bottom? |
- static bool _isScrolledToBottom(DivElement container, [int threshold = 2]) { |
- if (container == null) { |
- return false; |
- } |
- // scrollHeight -> complete height of element including scrollable area. |
- // clientHeight -> height of element on page. |
- // scrollTop -> how far is an element scrolled (from 0 to scrollHeight). |
- final distanceFromBottom = |
- container.scrollHeight - container.clientHeight - container.scrollTop; |
- const threshold = 2; // 2 pixel slop. |
- return distanceFromBottom <= threshold; |
- } |
- |
- /// Scroll [container] so the bottom content is visible. |
- static _scrollToBottom(DivElement container) { |
- if (container == null) { |
- return; |
- } |
- // Adjust scroll so that the bottom of the content is visible. |
- container.scrollTop = container.scrollHeight - container.clientHeight; |
- } |
- |
- _flushPendingLogs() { |
- DivElement logContainer = shadowRoot.querySelector(_kLogSelector); |
- bool autoScroll = _isScrolledToBottom(logContainer); |
- for (var logRecord in pendingLogRecords) { |
- _renderAppend(logRecord); |
- } |
- if (autoScroll) { |
- _scrollToBottom(logContainer); |
- } |
- pendingLogRecords.clear(); |
- } |
- |
- _onEvent(ServiceEvent event) { |
- assert(event.kind == Isolate.kLoggingStream); |
- _append(event.logRecord); |
- } |
- |
- void isolateChanged(oldValue) { |
- _reset(); |
- } |
- |
- void severityLevelChanged(oldValue) { |
- _severityLevelValue = int.parse(severityLevel); |
- _renderFull(); |
- } |
- |
- Future clear() { |
- logRecords.clear(); |
- pendingLogRecords.clear(); |
- _renderFull(); |
- return new Future.value(null); |
- } |
- |
- bool _shouldDisplay(Map logRecord) { |
- return logRecord['level'].value >= _severityLevelValue; |
- } |
- |
- @observable Isolate isolate; |
- @observable String severityLevel; |
- int _severityLevelValue = 0; |
- int _maxLevelLabelLength = 0; |
- Future<StreamSubscription> _loggingSubscriptionFuture; |
- StreamSubscription _resizeSubscription; |
- final List<Map> logRecords = new List<Map>(); |
- final List<Map> pendingLogRecords = new List<Map>(); |
} |