Add logging basics
diff --git a/packages/devtools_app/lib/main.dart b/packages/devtools_app/lib/main.dart index b0e2e61..8ab83fe 100644 --- a/packages/devtools_app/lib/main.dart +++ b/packages/devtools_app/lib/main.dart
@@ -4,6 +4,7 @@ import 'package:flutter/material.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart'; +import 'package:logging/logging.dart'; import 'src/app.dart'; import 'src/extension_points/extensions_base.dart'; @@ -18,6 +19,7 @@ import 'src/shared/config_specific/url_strategy/url_strategy.dart'; import 'src/shared/feature_flags.dart'; import 'src/shared/globals.dart'; +import 'src/shared/log_storage.dart'; import 'src/shared/preferences.dart'; import 'src/shared/primitives/url_utils.dart'; import 'src/shared/primitives/utils.dart'; @@ -60,6 +62,12 @@ // Load the Dart syntax highlighting grammar. await SyntaxHighlighter.initialize(); + Logger.root.onRecord.listen((record) { + LogStorage.root.addLog( + '[${record.loggerName}-${record.level.name}]: ${record.time.toUtc()}: ${record.message}', + ); + }); + setupErrorHandling(() async { // Run the app. runApp(
diff --git a/packages/devtools_app/lib/src/app.dart b/packages/devtools_app/lib/src/app.dart index e5ad6f8..7a2fd8f 100644 --- a/packages/devtools_app/lib/src/app.dart +++ b/packages/devtools_app/lib/src/app.dart
@@ -46,6 +46,7 @@ import 'shared/console/primitives/simple_items.dart'; import 'shared/dialogs.dart'; import 'shared/globals.dart'; +import 'shared/log_storage.dart'; import 'shared/primitives/auto_dispose.dart'; import 'shared/primitives/utils.dart'; import 'shared/routing.dart'; @@ -515,6 +516,27 @@ toggle: preferences.toggleVmDeveloperMode, gaItem: gac.vmDeveloperMode, ), + Row( + children: [ + CheckboxSetting( + label: const Text( + 'Enable verbose logging', + ), + listenable: preferences.verboseLoggingEnabled, + toggle: (enable) => preferences.setVerboseLogging(enable), + gaItem: gac.verboseLogging, + ), + CopyToClipboardControl( + dataProvider: () => LogStorage.root.toString(), + tooltip: 'Copy Logs', + ), + ClearButton( + label: '', // TODO this needs to be better centered + tooltip: 'Clear Logs', + onPressed: () => LogStorage.root.clear(), + ), + ], + ), ], ), actions: const [
diff --git a/packages/devtools_app/lib/src/service/vm_service_wrapper.dart b/packages/devtools_app/lib/src/service/vm_service_wrapper.dart index fd23717..b126780 100644 --- a/packages/devtools_app/lib/src/service/vm_service_wrapper.dart +++ b/packages/devtools_app/lib/src/service/vm_service_wrapper.dart
@@ -7,10 +7,12 @@ library vm_service_wrapper; import 'dart:async'; +import 'dart:math'; import 'package:collection/collection.dart' show IterableExtension; import 'package:dds_service_extensions/dds_service_extensions.dart'; import 'package:flutter/foundation.dart'; +import 'package:logging/logging.dart'; import 'package:vm_service/vm_service.dart'; import '../screens/vm_developer/vm_service_private_extensions.dart'; @@ -18,6 +20,8 @@ import '../shared/primitives/utils.dart'; import 'json_to_service_cache.dart'; +final _log = Logger('vm_service_wrapper'); + class VmServiceWrapper implements VmService { VmServiceWrapper( this._vmService, @@ -955,13 +959,34 @@ @visibleForTesting Future<T> trackFuture<T>(String name, Future<T> future) { + Future<T> loggedFuture = future; + + if (_log.isLoggable(Level.INFO)) { + final futureLogId = Random().nextInt(1 << 30); + _log.info('[$futureLogId]-trackFuture($name,...): Started'); + loggedFuture = loggedFuture.then( + (value) { + _log.info( + '[$futureLogId]-trackFuture($name,...): Succeeded', + ); + return value; + }, + onError: (error) { + _log.info( + '[$futureLogId]-trackFuture($name,...): Failed with: $error', + ); + throw error; + }, + ); + } + if (!trackFutures) { - return future; + return loggedFuture; } vmServiceCallCount++; vmServiceCalls.add(name); - final trackedFuture = TrackedFuture(name, future as Future<Object>); + final trackedFuture = TrackedFuture(name, loggedFuture as Future<Object>); if (_allFuturesCompleter.isCompleted) { _allFuturesCompleter = Completer<bool>(); } @@ -974,11 +999,11 @@ } } - future.then( + loggedFuture.then( (value) => futureComplete(), onError: (error) => futureComplete(), ); - return future; + return loggedFuture; } /// Adds support for private VM RPCs that can only be used when VM developer
diff --git a/packages/devtools_app/lib/src/shared/analytics/constants.dart b/packages/devtools_app/lib/src/shared/analytics/constants.dart index 178e332..b95122b 100644 --- a/packages/devtools_app/lib/src/shared/analytics/constants.dart +++ b/packages/devtools_app/lib/src/shared/analytics/constants.dart
@@ -111,6 +111,7 @@ const denseMode = 'denseMode'; const analytics = 'analytics'; const vmDeveloperMode = 'vmDeveloperMode'; +const verboseLogging = 'verboseLogging'; const inspectorHoverEvalMode = 'inspectorHoverEvalMode'; // Object explorer:
diff --git a/packages/devtools_app/lib/src/shared/common_widgets.dart b/packages/devtools_app/lib/src/shared/common_widgets.dart index 92d4f14..c534f03 100644 --- a/packages/devtools_app/lib/src/shared/common_widgets.dart +++ b/packages/devtools_app/lib/src/shared/common_widgets.dart
@@ -331,11 +331,12 @@ double? minScreenWidthForTextBeforeScaling, String tooltip = 'Clear', bool outlined = true, + String label = 'Clear', required VoidCallback? onPressed, }) : super( key: key, icon: Icons.block, - label: 'Clear', + label: label, tooltip: tooltip, outlined: outlined, minScreenWidthForTextBeforeScaling:
diff --git a/packages/devtools_app/lib/src/shared/config_specific/logger/logger_default.dart b/packages/devtools_app/lib/src/shared/config_specific/logger/logger_default.dart index dbe7b24..3c27f10 100644 --- a/packages/devtools_app/lib/src/shared/config_specific/logger/logger_default.dart +++ b/packages/devtools_app/lib/src/shared/config_specific/logger/logger_default.dart
@@ -2,17 +2,24 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +import 'package:logging/logging.dart'; + import 'logger.dart'; +final _log = Logger('main_log'); + void log(Object message, [LogLevel level = LogLevel.debug]) { switch (level) { case LogLevel.debug: print(message); + _log.info(message); break; case LogLevel.warning: print('[WARNING]: $message'); + _log.warning(message); break; case LogLevel.error: print('[ERROR]: $message'); + _log.shout(message); } }
diff --git a/packages/devtools_app/lib/src/shared/config_specific/logger/logger_html.dart b/packages/devtools_app/lib/src/shared/config_specific/logger/logger_html.dart index 7692a06..3485952 100644 --- a/packages/devtools_app/lib/src/shared/config_specific/logger/logger_html.dart +++ b/packages/devtools_app/lib/src/shared/config_specific/logger/logger_html.dart
@@ -4,17 +4,24 @@ import 'dart:html'; +import 'package:logging/logging.dart'; + import 'logger.dart'; +final _log = Logger('main_log'); + void log(Object message, [LogLevel level = LogLevel.debug]) { switch (level) { case LogLevel.debug: window.console.log(message); + _log.info(message); break; case LogLevel.warning: window.console.warn(message); + _log.warning(message); break; case LogLevel.error: window.console.error(message); + _log.shout(message); } }
diff --git a/packages/devtools_app/lib/src/shared/config_specific/logger/logger_io.dart b/packages/devtools_app/lib/src/shared/config_specific/logger/logger_io.dart index 83e3af0..461ce40 100644 --- a/packages/devtools_app/lib/src/shared/config_specific/logger/logger_io.dart +++ b/packages/devtools_app/lib/src/shared/config_specific/logger/logger_io.dart
@@ -2,17 +2,24 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +import 'package:logging/logging.dart'; + import 'logger.dart'; +final _log = Logger('main_log'); + void log(Object message, [LogLevel level = LogLevel.debug]) { switch (level) { case LogLevel.debug: print(message); + _log.info(message); break; case LogLevel.warning: print('[WARNING]: $message'); + _log.warning(message); break; case LogLevel.error: print('[ERROR]: $message'); + _log.shout(message); } }
diff --git a/packages/devtools_app/lib/src/shared/log_storage.dart b/packages/devtools_app/lib/src/shared/log_storage.dart new file mode 100644 index 0000000..ad7f6ec --- /dev/null +++ b/packages/devtools_app/lib/src/shared/log_storage.dart
@@ -0,0 +1,27 @@ +import 'dart:collection'; + +/// TODO Dart Doc +class LogStorage { + static const int maxLogEntries = 3000; + + final Queue<String> _logs = Queue<String>(); + + void addLog(String message) { + _logs.add(message); + if (_logs.length > maxLogEntries) { + _logs.removeFirst(); + } + } + + void clear() { + _logs.clear(); + } + + @override + String toString() { + return _logs.join('\n'); + } + + // Static instance for storing the app's logs. + static final LogStorage root = LogStorage(); +}
diff --git a/packages/devtools_app/lib/src/shared/preferences.dart b/packages/devtools_app/lib/src/shared/preferences.dart index 6a28366..f6b19d8 100644 --- a/packages/devtools_app/lib/src/shared/preferences.dart +++ b/packages/devtools_app/lib/src/shared/preferences.dart
@@ -6,6 +6,7 @@ import 'dart:convert'; import 'package:flutter/foundation.dart'; +import 'package:logging/logging.dart'; import '../service/vm_service_wrapper.dart'; import 'analytics/analytics.dart' as ga; @@ -15,6 +16,8 @@ import 'primitives/auto_dispose.dart'; import 'primitives/utils.dart'; +final _log = Logger('Preferences'); + /// A controller for global application preferences. class PreferencesController extends DisposableController with AutoDisposeControllerMixin { @@ -25,6 +28,9 @@ ValueListenable<bool> get vmDeveloperModeEnabled => _vmDeveloperMode; final _vmDeveloperMode = ValueNotifier<bool>(false); + ValueListenable<bool> get verboseLoggingEnabled => _verboseLogging; + final _verboseLogging = ValueNotifier<bool>(Logger.root.level == Level.INFO); + ValueListenable<bool> get denseModeEnabled => _denseMode; final _denseMode = ValueNotifier<bool>(false); @@ -60,6 +66,23 @@ storage.setValue('ui.denseMode', '${_denseMode.value}'); }); + final String? verboseLoggingEnabledValue = + await storage.getValue('verboseLogging'); + + setVerboseLogging(verboseLoggingEnabledValue == 'true'); + + addAutoDisposeListener(_verboseLogging, () { + storage.setValue('verboseLogging', _verboseLogging.value.toString()); + + if (_verboseLogging.value) { + Logger.root.level = Level.INFO; + _log.warning('verboseLogging enabled'); + } else { + Logger.root.level = Level.WARNING; + _log.warning('verboseLogging disabled'); + } + }); + await inspector.init(); await memory.init(); await cpuProfiler.init(); @@ -85,6 +108,10 @@ VmServiceWrapper.enablePrivateRpcs = enableVmDeveloperMode; } + void setVerboseLogging(bool enableVerboseLogging) { + _verboseLogging.value = enableVerboseLogging; + } + /// Change the value for the dense mode setting. void toggleDenseMode(bool enableDenseMode) { _denseMode.value = enableDenseMode; @@ -105,6 +132,7 @@ final _customPubRootDirectories = ListValueNotifier<String>([]); final _customPubRootDirectoriesAreBusy = ValueNotifier<bool>(false); final _busyCounter = ValueNotifier<int>(0); + static const _verboseLoggingStorageId = 'verboseLogging'; static const _hoverEvalModeStorageId = 'inspector.hoverEvalMode'; static const _customPubRootDirectoriesStoragePrefix = 'inspector.customPubRootDirectories';
diff --git a/packages/devtools_app/pubspec.yaml b/packages/devtools_app/pubspec.yaml index 06b372e..c8a7eb1 100644 --- a/packages/devtools_app/pubspec.yaml +++ b/packages/devtools_app/pubspec.yaml
@@ -44,6 +44,7 @@ intl: '>=0.16.1 <0.18.0' js: ^0.6.1+1 leak_tracker: 2.0.1 + logging: ^1.1.1 mime: ^1.0.0 path: ^1.8.0 perfetto_compiled: