Merge remote-tracking branch 'origin/master' into better-logging
diff --git a/packages/devtools_app/lib/src/app.dart b/packages/devtools_app/lib/src/app.dart index af521bb..67667e6 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/offline_screen.dart'; import 'shared/primitives/auto_dispose.dart'; import 'shared/primitives/utils.dart'; @@ -515,6 +516,47 @@ toggle: preferences.toggleVmDeveloperMode, gaItem: gac.vmDeveloperMode, ), + Column( + children: [ + 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: 'Clear Logs', + minScreenWidthForTextBeforeScaling: + double.infinity, // Forces Icon only mode for button + tooltip: 'Clear Logs', + onPressed: () => LogStorage.root.clear(), + ), + ], + ), + const Row( + children: [ + SizedBox( + width: defaultSpacing, + ), + Icon(Icons.warning), + SizedBox( + width: defaultSpacing, + ), + Text( + 'Logs may contain sensitive information. Always check their contents before sharing.', + ) + ], + ), + ], + ), ], ), actions: const [
diff --git a/packages/devtools_app/lib/src/framework/framework_core.dart b/packages/devtools_app/lib/src/framework/framework_core.dart index 897235d..afd4501 100644 --- a/packages/devtools_app/lib/src/framework/framework_core.dart +++ b/packages/devtools_app/lib/src/framework/framework_core.dart
@@ -39,7 +39,7 @@ static void init() { // Print the version number at startup. - log('DevTools version ${devtools.version}.'); + log('zzzDevTools version ${devtools.version}.'); } /// Returns true if we're able to connect to a device and false otherwise.
diff --git a/packages/devtools_app/lib/src/screens/network/network_request_inspector_views.dart b/packages/devtools_app/lib/src/screens/network/network_request_inspector_views.dart index cdf6286..7b1f357 100644 --- a/packages/devtools_app/lib/src/screens/network/network_request_inspector_views.dart +++ b/packages/devtools_app/lib/src/screens/network/network_request_inspector_views.dart
@@ -4,6 +4,7 @@ import 'package:flutter/material.dart'; import 'package:image/image.dart' as image; +import 'package:logging/logging.dart'; import '../../shared/common_widgets.dart'; import '../../shared/http/http.dart'; @@ -14,6 +15,8 @@ import '../../shared/ui/colors.dart'; import 'network_model.dart'; +final _log = Logger('network_request_inspector_views'); + // Approximately double the indent of the expandable tile's title. const double _rowIndentPadding = 30; @@ -172,6 +175,7 @@ @override Widget build(BuildContext context) { + _log.info('HttpResponseView: building'); return ValueListenableBuilder( valueListenable: data.requestUpdatedNotifier, builder: (context, __, ___) { @@ -184,17 +188,23 @@ final responseBody = data.responseBody!; final isLoading = data.isFetchingFullData; if (isLoading) { + _log.info('HttpResponseView: loading'); return CenteredCircularProgressIndicator( size: mediumProgressSize, ); } + _log.info('HttpResponseView: DONE loading'); + if (contentType != null && contentType.contains('image')) { + _log.info('HttpResponseView: showing an image response'); child = ImageResponseView(data); } else if (contentType != null && contentType.contains('json') && responseBody.isNotEmpty) { + _log.info('HttpResponseView: showing JSONVIewer'); child = JsonViewer(encodedJson: responseBody); } else { + _log.info('HttpResponseView: showing text'); child = Text( responseBody, style: theme.fixedFontStyle,
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 53d4ac9..675664b 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 395f012..5b07445 100644 --- a/packages/devtools_app/lib/src/shared/common_widgets.dart +++ b/packages/devtools_app/lib/src/shared/common_widgets.dart
@@ -332,11 +332,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 ead7ca7..4ef42b3 100644 --- a/packages/devtools_app/lib/src/shared/preferences.dart +++ b/packages/devtools_app/lib/src/shared/preferences.dart
@@ -6,15 +6,19 @@ 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; import 'analytics/constants.dart' as gac; import 'diagnostics/inspector_service.dart'; import 'globals.dart'; +import 'log_storage.dart'; 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 +29,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); @@ -63,6 +70,29 @@ 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'); + } + }); + + Logger.root.onRecord.listen((record) { + LogStorage.root.addLog( + '[${record.loggerName}-${record.level.name}]: ${record.time.toUtc()}: ${record.message}', + ); + }); + await inspector.init(); await memory.init(); await performance.init(); @@ -91,6 +121,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; @@ -111,6 +145,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 6bbe129..4cd0f57 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: