[flutter_tools] abstract logger construction (why can't I hold all these loggers) (#61201)
We have too many loggers, and the logger construction rules are too complicated to be untested. Capture these in a LoggerFactory and test that construction is correct.
diff --git a/packages/flutter_tools/lib/executable.dart b/packages/flutter_tools/lib/executable.dart
index b0eb4ad..e69ba78 100644
--- a/packages/flutter_tools/lib/executable.dart
+++ b/packages/flutter_tools/lib/executable.dart
@@ -4,13 +4,17 @@
import 'dart:async';
+import 'package:meta/meta.dart';
+
import 'runner.dart' as runner;
import 'src/base/context.dart';
+import 'src/base/io.dart';
import 'src/base/logger.dart';
import 'src/base/template.dart';
// The build_runner code generation is provided here to make it easier to
// avoid introducing the dependency into google3. Not all build* packages
// are synced internally.
+import 'src/base/terminal.dart';
import 'src/build_runner/build_runner.dart';
import 'src/build_runner/mustache_template.dart';
import 'src/build_runner/resident_web_runner.dart';
@@ -135,36 +139,80 @@
WebRunnerFactory: () => DwdsWebRunnerFactory(),
// The mustache dependency is different in google3
TemplateRenderer: () => const MustacheTemplateRenderer(),
- if (daemon)
- Logger: () => NotifyingLogger(
- verbose: verbose,
- parent: VerboseLogger(StdoutLogger(
- timeoutConfiguration: timeoutConfiguration,
- stdio: globals.stdio,
- terminal: globals.terminal,
- outputPreferences: globals.outputPreferences,
- ),
- ))
- else if (runMachine && !verbose)
- Logger: () => AppRunLogger(parent: StdoutLogger(
- timeoutConfiguration: timeoutConfiguration,
- stdio: globals.stdio,
- terminal: globals.terminal,
+ Logger: () {
+ final LoggerFactory loggerFactory = LoggerFactory(
outputPreferences: globals.outputPreferences,
- ))
- else if (runMachine && verbose)
- Logger: () => AppRunLogger(parent: VerboseLogger(StdoutLogger(
- timeoutConfiguration: timeoutConfiguration,
- stdio: globals.stdio,
terminal: globals.terminal,
- outputPreferences: globals.outputPreferences,
- )))
- else if (verbose && !muteCommandLogging)
- Logger: () => VerboseLogger(StdoutLogger(
- timeoutConfiguration: timeoutConfiguration,
stdio: globals.stdio,
- terminal: globals.terminal,
- outputPreferences: globals.outputPreferences,
- ))
+ timeoutConfiguration: timeoutConfiguration,
+ );
+ return loggerFactory.createLogger(
+ daemon: daemon,
+ machine: runMachine,
+ verbose: verbose && !muteCommandLogging,
+ windows: globals.platform.isWindows,
+ );
+ }
});
}
+
+
+/// An abstraction for instantiation of the correct logger type.
+///
+/// Our logger class hierarchy and runtime requirements are overly complicated.
+class LoggerFactory {
+ LoggerFactory({
+ @required Terminal terminal,
+ @required Stdio stdio,
+ @required OutputPreferences outputPreferences,
+ @required TimeoutConfiguration timeoutConfiguration,
+ StopwatchFactory stopwatchFactory = const StopwatchFactory(),
+ }) : _terminal = terminal,
+ _stdio = stdio,
+ _timeoutConfiguration = timeoutConfiguration,
+ _stopwatchFactory = stopwatchFactory,
+ _outputPreferences = outputPreferences;
+
+ final Terminal _terminal;
+ final Stdio _stdio;
+ final TimeoutConfiguration _timeoutConfiguration;
+ final StopwatchFactory _stopwatchFactory;
+ final OutputPreferences _outputPreferences;
+
+ /// Create the appropriate logger for the current platform and configuration.
+ Logger createLogger({
+ @required bool verbose,
+ @required bool machine,
+ @required bool daemon,
+ @required bool windows,
+ }) {
+ Logger logger;
+ if (windows) {
+ logger = WindowsStdoutLogger(
+ terminal: _terminal,
+ stdio: _stdio,
+ outputPreferences: _outputPreferences,
+ timeoutConfiguration: _timeoutConfiguration,
+ stopwatchFactory: _stopwatchFactory,
+ );
+ } else {
+ logger = StdoutLogger(
+ terminal: _terminal,
+ stdio: _stdio,
+ outputPreferences: _outputPreferences,
+ timeoutConfiguration: _timeoutConfiguration,
+ stopwatchFactory: _stopwatchFactory
+ );
+ }
+ if (verbose) {
+ logger = VerboseLogger(logger, stopwatchFactory: _stopwatchFactory);
+ }
+ if (daemon) {
+ return NotifyingLogger(verbose: verbose, parent: logger);
+ }
+ if (machine) {
+ return AppRunLogger(parent: logger);
+ }
+ return logger;
+ }
+}
diff --git a/packages/flutter_tools/lib/src/base/logger.dart b/packages/flutter_tools/lib/src/base/logger.dart
index 075c234..20ab0e1 100644
--- a/packages/flutter_tools/lib/src/base/logger.dart
+++ b/packages/flutter_tools/lib/src/base/logger.dart
@@ -175,7 +175,7 @@
class StdoutLogger extends Logger {
StdoutLogger({
- @required AnsiTerminal terminal,
+ @required Terminal terminal,
@required Stdio stdio,
@required OutputPreferences outputPreferences,
@required TimeoutConfiguration timeoutConfiguration,
@@ -188,7 +188,7 @@
_stopwatchFactory = stopwatchFactory;
@override
- final AnsiTerminal _terminal;
+ final Terminal _terminal;
@override
final OutputPreferences _outputPreferences;
@override
@@ -345,7 +345,7 @@
/// they will show up as the unrepresentable character symbol '�'.
class WindowsStdoutLogger extends StdoutLogger {
WindowsStdoutLogger({
- @required AnsiTerminal terminal,
+ @required Terminal terminal,
@required Stdio stdio,
@required OutputPreferences outputPreferences,
@required TimeoutConfiguration timeoutConfiguration,
@@ -1006,7 +1006,7 @@
this.padding = kDefaultStatusPadding,
@required Duration timeout,
@required Stopwatch stopwatch,
- @required AnsiTerminal terminal,
+ @required Terminal terminal,
VoidCallback onFinish,
Stdio stdio,
TimeoutConfiguration timeoutConfiguration,
diff --git a/packages/flutter_tools/test/general.shard/base/logger_test.dart b/packages/flutter_tools/test/general.shard/base/logger_test.dart
index 6ca3251..2130457 100644
--- a/packages/flutter_tools/test/general.shard/base/logger_test.dart
+++ b/packages/flutter_tools/test/general.shard/base/logger_test.dart
@@ -5,10 +5,12 @@
import 'dart:async';
import 'dart:convert' show jsonEncode;
+import 'package:flutter_tools/executable.dart';
import 'package:flutter_tools/src/base/io.dart';
import 'package:flutter_tools/src/base/logger.dart';
import 'package:flutter_tools/src/base/platform.dart';
import 'package:flutter_tools/src/base/terminal.dart';
+import 'package:flutter_tools/src/commands/daemon.dart';
import 'package:mockito/mockito.dart';
import 'package:quiver/testing/async.dart';
@@ -24,6 +26,52 @@
class MockStdout extends Mock implements Stdout {}
void main() {
+ testWithoutContext('correct logger instance is created', () {
+ final LoggerFactory loggerFactory = LoggerFactory(
+ terminal: Terminal.test(),
+ stdio: mocks.MockStdio(),
+ outputPreferences: OutputPreferences.test(),
+ timeoutConfiguration: const TimeoutConfiguration(),
+ );
+
+ expect(loggerFactory.createLogger(
+ verbose: false,
+ machine: false,
+ daemon: false,
+ windows: false,
+ ), isA<StdoutLogger>());
+ expect(loggerFactory.createLogger(
+ verbose: false,
+ machine: false,
+ daemon: false,
+ windows: true,
+ ), isA<WindowsStdoutLogger>());
+ expect(loggerFactory.createLogger(
+ verbose: true,
+ machine: false,
+ daemon: false,
+ windows: true,
+ ), isA<VerboseLogger>());
+ expect(loggerFactory.createLogger(
+ verbose: true,
+ machine: false,
+ daemon: false,
+ windows: false,
+ ), isA<VerboseLogger>());
+ expect(loggerFactory.createLogger(
+ verbose: false,
+ machine: false,
+ daemon: true,
+ windows: false,
+ ), isA<NotifyingLogger>());
+ expect(loggerFactory.createLogger(
+ verbose: false,
+ machine: true,
+ daemon: false,
+ windows: false,
+ ), isA<AppRunLogger>());
+ });
+
group('AppContext', () {
FakeStopwatch fakeStopWatch;