Apply basic log filtering and formatting to fuchsia logs. (#24143)

diff --git a/packages/flutter_tools/lib/src/base/time.dart b/packages/flutter_tools/lib/src/base/time.dart
new file mode 100644
index 0000000..5ba7e02
--- /dev/null
+++ b/packages/flutter_tools/lib/src/base/time.dart
@@ -0,0 +1,34 @@
+// Copyright 2018 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+import 'context.dart';
+
+/// The current system clock instance.
+SystemClock get systemClock => context[SystemClock];
+
+/// A class for making time based operations testable.
+class SystemClock {
+  /// A const constructor to allow subclasses to be const.
+  const SystemClock();
+
+  /// Create a clock with a fixed current time.
+  const factory SystemClock.fixed(DateTime time) = _FixedTimeClock;
+
+  /// Retrieve the current time.
+  DateTime now() => DateTime.now();
+
+  /// Compute the time a given duration ago.
+  DateTime ago(Duration duration) {
+    return now().subtract(duration);
+  }
+}
+
+class _FixedTimeClock extends SystemClock {
+  const _FixedTimeClock(this._fixedTime);
+
+  final DateTime _fixedTime;
+
+  @override
+  DateTime now() => _fixedTime;
+}
diff --git a/packages/flutter_tools/lib/src/base/utils.dart b/packages/flutter_tools/lib/src/base/utils.dart
index b0e5049..4bdb587 100644
--- a/packages/flutter_tools/lib/src/base/utils.dart
+++ b/packages/flutter_tools/lib/src/base/utils.dart
@@ -8,7 +8,6 @@
 
 import 'package:crypto/crypto.dart';
 import 'package:intl/intl.dart';
-import 'package:quiver/time.dart';
 
 import '../globals.dart';
 import 'context.dart';
@@ -251,8 +250,6 @@
   return map.cast<String, dynamic>();
 }
 
-Clock get clock => context[Clock];
-
 typedef AsyncCallback = Future<void> Function();
 
 /// A [Timer] inspired class that:
diff --git a/packages/flutter_tools/lib/src/commands/run.dart b/packages/flutter_tools/lib/src/commands/run.dart
index cb0e66e..145e668 100644
--- a/packages/flutter_tools/lib/src/commands/run.dart
+++ b/packages/flutter_tools/lib/src/commands/run.dart
@@ -6,6 +6,7 @@
 
 import '../base/common.dart';
 import '../base/file_system.dart';
+import '../base/time.dart';
 import '../base/utils.dart';
 import '../build_info.dart';
 import '../cache.dart';
@@ -300,7 +301,7 @@
       } catch (error) {
         throwToolExit(error.toString());
       }
-      final DateTime appStartedTime = clock.now();
+      final DateTime appStartedTime = systemClock.now();
       final int result = await app.runner.waitForAppToFinish();
       if (result != 0)
         throwToolExit(null, exitCode: result);
@@ -391,7 +392,7 @@
     final Completer<void> appStartedTimeRecorder = Completer<void>.sync();
     // This callback can't throw.
     appStartedTimeRecorder.future.then<void>( // ignore: unawaited_futures
-      (_) { appStartedTime = clock.now(); }
+      (_) { appStartedTime = systemClock.now(); }
     );
 
     final int result = await runner.run(
diff --git a/packages/flutter_tools/lib/src/context_runner.dart b/packages/flutter_tools/lib/src/context_runner.dart
index f973516..d9dff8a 100644
--- a/packages/flutter_tools/lib/src/context_runner.dart
+++ b/packages/flutter_tools/lib/src/context_runner.dart
@@ -4,8 +4,6 @@
 
 import 'dart:async';
 
-import 'package:quiver/time.dart';
-
 import 'android/android_sdk.dart';
 import 'android/android_studio.dart';
 import 'android/android_workflow.dart';
@@ -19,6 +17,7 @@
 import 'base/logger.dart';
 import 'base/os.dart';
 import 'base/platform.dart';
+import 'base/time.dart';
 import 'base/utils.dart';
 import 'cache.dart';
 import 'compile.dart';
@@ -55,7 +54,6 @@
       AssetBundleFactory: () => AssetBundleFactory.defaultInstance,
       BotDetector: () => const BotDetector(),
       Cache: () => Cache(),
-      Clock: () => const Clock(),
       CocoaPods: () => CocoaPods(),
       CocoaPodsValidator: () => const CocoaPodsValidator(),
       Config: () => Config(),
@@ -68,7 +66,7 @@
       FuchsiaArtifacts: () => FuchsiaArtifacts(),
       FuchsiaWorkflow: () => FuchsiaWorkflow(),
       Flags: () => const EmptyFlags(),
-      FlutterVersion: () => FlutterVersion(const Clock()),
+      FlutterVersion: () => FlutterVersion(const SystemClock()),
       GenSnapshot: () => const GenSnapshot(),
       HotRunnerConfig: () => HotRunnerConfig(),
       IMobileDevice: () => const IMobileDevice(),
@@ -80,6 +78,7 @@
       OperatingSystemUtils: () => OperatingSystemUtils(),
       PlistBuddy: () => const PlistBuddy(),
       SimControl: () => SimControl(),
+      SystemClock: () => const SystemClock(),
       Stdio: () => const Stdio(),
       Usage: () => Usage(),
       Xcode: () => Xcode(),
diff --git a/packages/flutter_tools/lib/src/fuchsia/fuchsia_device.dart b/packages/flutter_tools/lib/src/fuchsia/fuchsia_device.dart
index dba2a3a..c1ada88 100644
--- a/packages/flutter_tools/lib/src/fuchsia/fuchsia_device.dart
+++ b/packages/flutter_tools/lib/src/fuchsia/fuchsia_device.dart
@@ -12,6 +12,7 @@
 import '../base/platform.dart';
 import '../base/process.dart';
 import '../base/process_manager.dart';
+import '../base/time.dart';
 import '../build_info.dart';
 import '../device.dart';
 import '../globals.dart';
@@ -25,23 +26,48 @@
 
 /// Read the log for a particular device.
 class _FuchsiaLogReader extends DeviceLogReader {
-  _FuchsiaLogReader(this._device);
+  _FuchsiaLogReader(this._device, [this._app]);
 
-  // TODO(jonahwilliams): handle filtering log output from different modules.
-  static final Pattern flutterLogOutput = RegExp(r'\[\d+\.\d+\]\[\d+\]\[\d+\]\[klog\] INFO: \w+\(flutter\): ');
+  static final RegExp _flutterLogOutput = RegExp(r'INFO: \w+\(flutter\): ');
+  static final RegExp _utcDateOutput = RegExp(r'\d+\-\d+\-\d+ \d+:\d+:\d+');
 
   FuchsiaDevice _device;
+  ApplicationPackage _app;
 
   @override String get name => _device.name;
 
   Stream<String> _logLines;
   @override
   Stream<String> get logLines {
-    _logLines ??= fuchsiaSdk.syslogs()
-      .where((String line) => flutterLogOutput.matchAsPrefix(line) != null);
+    _logLines ??= _processLogs(fuchsiaSdk.syslogs());
     return _logLines;
   }
 
+  Stream<String> _processLogs(Stream<String> lines) async* {
+    // Get the starting time of the log processor to filter logs from before
+    // the process attached.
+    final DateTime startTime = systemClock.now();
+    // Determine if line comes from flutter, and optionally whether it matches
+    // the correct fuchsia module.
+    final RegExp matchRegExp = _app == null
+      ? _flutterLogOutput
+      : RegExp('INFO: ${_app.name}\\(flutter\\): ');
+    await for (String line in lines.where(matchRegExp.hasMatch)) {
+      // Read out the date string from the log and compare it to the current time:
+      // Example: 2018-11-09 01:27:45
+      final String rawDate = _utcDateOutput.firstMatch(line)?.group(0);
+      if (rawDate == null) {
+        continue;
+      }
+      final DateTime logTime = DateTime.parse(rawDate);
+      if (logTime.millisecondsSinceEpoch < startTime.millisecondsSinceEpoch) {
+        continue;
+      }
+      // Format log into a useful string:
+      yield '[${logTime.toLocal()}] Flutter: ${line.split(matchRegExp).last}';
+    }
+  }
+
   @override
   String toString() => name;
 }
@@ -152,7 +178,7 @@
   Future<String> get sdkNameAndVersion async => 'Fuchsia';
 
   @override
-  DeviceLogReader getLogReader({ApplicationPackage app}) => _logReader ??= _FuchsiaLogReader(this);
+  DeviceLogReader getLogReader({ApplicationPackage app}) => _logReader ??= _FuchsiaLogReader(this, app);
   _FuchsiaLogReader _logReader;
 
   @override
@@ -279,6 +305,13 @@
   }
 }
 
+class FuchsiaModulePackage extends ApplicationPackage {
+  FuchsiaModulePackage({@required this.name}) : super(id: name);
+
+  @override
+  final String name;
+}
+
 /// Parses output from `dart.services` output on a fuchsia device.
 ///
 /// Example output:
diff --git a/packages/flutter_tools/lib/src/fuchsia/fuchsia_sdk.dart b/packages/flutter_tools/lib/src/fuchsia/fuchsia_sdk.dart
index a67ea94..201fefe 100644
--- a/packages/flutter_tools/lib/src/fuchsia/fuchsia_sdk.dart
+++ b/packages/flutter_tools/lib/src/fuchsia/fuchsia_sdk.dart
@@ -12,7 +12,6 @@
 import '../base/platform.dart';
 import '../base/process.dart';
 import '../base/process_manager.dart';
-import '../globals.dart';
 
 /// The [FuchsiaSdk] instance.
 FuchsiaSdk get fuchsiaSdk => context[FuchsiaSdk];
@@ -27,7 +26,7 @@
 class FuchsiaSdk {
   static const List<String> _netaddrCommand = <String>['fx', 'netaddr', '--fuchsia', '--nowait'];
   static const List<String> _netlsCommand = <String>['fx', 'netls', '--nowait'];
-  static const List<String> _syslogCommand = <String>['fx', 'syslog'];
+  static const List<String> _syslogCommand = <String>['fx', 'syslog', '--clock', 'Local'];
 
   /// Invokes the `netaddr` command.
   ///
@@ -57,12 +56,11 @@
         process.kill();
       });
       processManager.start(_syslogCommand).then((Process newProcess) {
-        printTrace('Running logs');
         if (controller.isClosed) {
           return;
         }
         process = newProcess;
-        process.exitCode.then((_) => controller.close);
+        process.exitCode.whenComplete(controller.close);
         controller.addStream(process.stdout.transform(utf8.decoder).transform(const LineSplitter()));
       });
       return controller.stream;
diff --git a/packages/flutter_tools/lib/src/runner/flutter_command.dart b/packages/flutter_tools/lib/src/runner/flutter_command.dart
index 95ae66f..83ea0b4 100644
--- a/packages/flutter_tools/lib/src/runner/flutter_command.dart
+++ b/packages/flutter_tools/lib/src/runner/flutter_command.dart
@@ -13,6 +13,7 @@
 import '../base/common.dart';
 import '../base/context.dart';
 import '../base/file_system.dart';
+import '../base/time.dart';
 import '../base/utils.dart';
 import '../build_info.dart';
 import '../bundle.dart' as bundle;
@@ -320,7 +321,7 @@
   /// so that this method can record and report the overall time to analytics.
   @override
   Future<void> run() {
-    final DateTime startTime = clock.now();
+    final DateTime startTime = systemClock.now();
 
     return context.run<void>(
       name: 'command',
@@ -336,7 +337,7 @@
           commandResult = const FlutterCommandResult(ExitStatus.fail);
           rethrow;
         } finally {
-          final DateTime endTime = clock.now();
+          final DateTime endTime = systemClock.now();
           printTrace('"flutter $name" took ${getElapsedAsMilliseconds(endTime.difference(startTime))}.');
           // This is checking the result of the call to 'usagePath'
           // (a Future<String>), and not the result of evaluating the Future.
diff --git a/packages/flutter_tools/lib/src/version.dart b/packages/flutter_tools/lib/src/version.dart
index 444acc2..5f09e68 100644
--- a/packages/flutter_tools/lib/src/version.dart
+++ b/packages/flutter_tools/lib/src/version.dart
@@ -6,7 +6,6 @@
 import 'dart:convert';
 
 import 'package:meta/meta.dart';
-import 'package:quiver/time.dart';
 
 import 'base/common.dart';
 import 'base/context.dart';
@@ -14,12 +13,13 @@
 import 'base/io.dart';
 import 'base/process.dart';
 import 'base/process_manager.dart';
+import 'base/time.dart';
 import 'cache.dart';
 import 'globals.dart';
 
 class FlutterVersion {
   @visibleForTesting
-  FlutterVersion([this._clock = const Clock()]) {
+  FlutterVersion([this._clock = const SystemClock()]) {
     _channel = _runGit('git rev-parse --abbrev-ref --symbolic @{u}');
     final String branch = _runGit('git rev-parse --abbrev-ref HEAD');
     _branch = branch == 'HEAD' ? _channel : branch;
@@ -38,7 +38,7 @@
     _frameworkVersion = GitTagVersion.determine().frameworkVersionFor(_frameworkRevision);
   }
 
-  final Clock _clock;
+  final SystemClock _clock;
 
   String _repositoryUrl;
   String get repositoryUrl => _repositoryUrl;
@@ -273,7 +273,7 @@
 
     // Do not load the stamp before the above server check as it may modify the stamp file.
     final VersionCheckStamp stamp = await VersionCheckStamp.load();
-    final DateTime lastTimeWarningWasPrinted = stamp.lastTimeWarningWasPrinted ?? _clock.agoBy(kMaxTimeSinceLastWarning * 2);
+    final DateTime lastTimeWarningWasPrinted = stamp.lastTimeWarningWasPrinted ?? _clock.ago(kMaxTimeSinceLastWarning * 2);
     final bool beenAWhileSinceWarningWasPrinted = _clock.now().difference(lastTimeWarningWasPrinted) > kMaxTimeSinceLastWarning;
 
     // We show a warning if either we know there is a new remote version, or we couldn't tell but the local