| 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>();
|
| }
|
|
|