Chromium Code Reviews| 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 |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..bba7815b74183b9fb4259a95820ca543cf157d86 |
| --- /dev/null |
| +++ b/runtime/observatory/lib/src/elements/logging.dart |
| @@ -0,0 +1,180 @@ |
| +// Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file |
| +// for details. All rights reserved. Use of this source code is governed by a |
| +// BSD-style license that can be found in the LICENSE file. |
| + |
| +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'; |
| + |
| + LoggingPageElement.created() : super.created(); |
| + |
| + domReady() { |
| + super.domReady(); |
| + _insertLevels(); |
| + } |
| + |
| + attached() { |
| + super.attached(); |
| + _sub(); |
| + _resizeSubscription = window.onResize.listen((_) => _updatePageHeight()); |
| + _updatePageHeight(); |
| + // Turn on the periodic poll timer for this page. |
| + pollPeriod = const Duration(milliseconds:100); |
| + } |
| + |
| + 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; |
| + } |
| + |
| + _renderFull() { |
| + DivElement logContainer = shadowRoot.querySelector(_kLogSelector); |
| + logContainer.children.clear(); |
| + pendingLogRecords.clear(); |
| + var lastElement; |
| + for (var logRecord in logRecords) { |
| + if (_shouldDisplay(logRecord)) { |
| + lastElement = _renderAppend(logRecord); |
| + } |
| + } |
| + if (lastElement != null) { |
| + lastElement.scrollIntoView(); |
| + } |
| + } |
| + |
| + _flushPendingLogs() { |
| + var lastElement; |
| + for (var logRecord in pendingLogRecords) { |
| + lastElement = _renderAppend(logRecord); |
| + } |
| + if (lastElement != null) { |
|
rmacnak
2015/07/22 00:29:26
Should only scroll if already positioned at the en
Cutch
2015/07/22 21:33:32
Done.
|
| + lastElement.scrollIntoView(); |
| + } |
| + pendingLogRecords.clear(); |
| + } |
| + |
| + _onEvent(ServiceEvent event) { |
| + assert(event.kind == VM.kLoggingStream); |
|
rmacnak
2015/07/22 00:29:26
Isolate.kLoggingStream
Cutch
2015/07/22 21:33:32
Done.
|
| + _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>(); |
| +} |