flutter/packages/flutter_tools/lib/src/commands/logs.dart
Jonah Williams 377879825e
[flutter_tools] prevent StateError when log reader is disposed (#52173)
Previously the AdbLogReader did async setup in the StreamController.onListen callback, specifically it would query the api version and start the adb process. If the log subscription was cancelled before this setup completed, then the log output could (haven't confirmed) get added to a closed controller, causing the above state error.
2020-03-10 11:34:40 -07:00

89 lines
2.3 KiB
Dart

// Copyright 2014 The Flutter 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 'dart:async';
import '../base/common.dart';
import '../base/io.dart';
import '../cache.dart';
import '../device.dart';
import '../globals.dart' as globals;
import '../runner/flutter_command.dart';
class LogsCommand extends FlutterCommand {
LogsCommand() {
argParser.addFlag('clear',
negatable: false,
abbr: 'c',
help: 'Clear log history before reading from logs.',
);
}
@override
final String name = 'logs';
@override
final String description = 'Show log output for running Flutter apps.';
@override
Future<Set<DevelopmentArtifact>> get requiredArtifacts async => const <DevelopmentArtifact>{};
Device device;
@override
Future<FlutterCommandResult> verifyThenRunCommand(String commandPath) async {
device = await findTargetDevice();
if (device == null) {
throwToolExit(null);
}
return super.verifyThenRunCommand(commandPath);
}
@override
Future<FlutterCommandResult> runCommand() async {
if (boolArg('clear')) {
device.clearLogs();
}
final DeviceLogReader logReader = await device.getLogReader();
Cache.releaseLockEarly();
globals.printStatus('Showing $logReader logs:');
final Completer<int> exitCompleter = Completer<int>();
// Start reading.
final StreamSubscription<String> subscription = logReader.logLines.listen(
(String message) => globals.printStatus(message, wrap: false),
onDone: () {
exitCompleter.complete(0);
},
onError: (dynamic error) {
exitCompleter.complete(error is int ? error : 1);
},
);
// When terminating, close down the log reader.
ProcessSignal.SIGINT.watch().listen((ProcessSignal signal) {
subscription.cancel();
globals.printStatus('');
exitCompleter.complete(0);
});
ProcessSignal.SIGTERM.watch().listen((ProcessSignal signal) {
subscription.cancel();
exitCompleter.complete(0);
});
// Wait for the log reader to be finished.
final int result = await exitCompleter.future;
await subscription.cancel();
if (result != 0) {
throwToolExit('Error listening to $logReader logs.');
}
return FlutterCommandResult.success();
}
}