Commit 9cb914df authored by Todd Volkert's avatar Todd Volkert Committed by GitHub

Allow for application-specific log readers. (#6898)

* Allow for application-specific log readers.

When running with prebuilt application binaries, those applications
aren't guaranteed to be named "Runner" (as it is when we build
the app locally in Flutter tools)
parent dbacf775
...@@ -294,6 +294,7 @@ class AndroidDevice extends Device { ...@@ -294,6 +294,7 @@ class AndroidDevice extends Device {
ProtocolDiscovery observatoryDiscovery; ProtocolDiscovery observatoryDiscovery;
ProtocolDiscovery diagnosticDiscovery; ProtocolDiscovery diagnosticDiscovery;
DeviceLogReader logReader = getLogReader();
if (options.debuggingEnabled) { if (options.debuggingEnabled) {
observatoryDiscovery = new ProtocolDiscovery(logReader, ProtocolDiscovery.kObservatoryService); observatoryDiscovery = new ProtocolDiscovery(logReader, ProtocolDiscovery.kObservatoryService);
diagnosticDiscovery = new ProtocolDiscovery(logReader, ProtocolDiscovery.kDiagnosticService); diagnosticDiscovery = new ProtocolDiscovery(logReader, ProtocolDiscovery.kDiagnosticService);
...@@ -439,9 +440,9 @@ class AndroidDevice extends Device { ...@@ -439,9 +440,9 @@ class AndroidDevice extends Device {
} }
@override @override
DeviceLogReader get logReader { DeviceLogReader getLogReader({ApplicationPackage app}) {
if (_logReader == null) // The Android log reader isn't app-specific.
_logReader = new _AdbLogReader(this); _logReader ??= new _AdbLogReader(this);
return _logReader; return _logReader;
} }
...@@ -487,7 +488,7 @@ class AndroidDevice extends Device { ...@@ -487,7 +488,7 @@ class AndroidDevice extends Device {
Future<List<DiscoveredApp>> discoverApps() { Future<List<DiscoveredApp>> discoverApps() {
RegExp discoverExp = new RegExp(r'DISCOVER: (.*)'); RegExp discoverExp = new RegExp(r'DISCOVER: (.*)');
List<DiscoveredApp> result = <DiscoveredApp>[]; List<DiscoveredApp> result = <DiscoveredApp>[];
StreamSubscription<String> logs = logReader.logLines.listen((String line) { StreamSubscription<String> logs = getLogReader().logLines.listen((String line) {
Match match = discoverExp.firstMatch(line); Match match = discoverExp.firstMatch(line);
if (match != null) { if (match != null) {
Map<String, dynamic> app = JSON.decode(match.group(1)); Map<String, dynamic> app = JSON.decode(match.group(1));
......
...@@ -301,7 +301,11 @@ Future<int> startApp(DriveCommand command) async { ...@@ -301,7 +301,11 @@ Future<int> startApp(DriveCommand command) async {
printTrace('Starting application.'); printTrace('Starting application.');
// Forward device log messages to the terminal window running the "drive" command. // Forward device log messages to the terminal window running the "drive" command.
command._deviceLogSubscription = command.device.logReader.logLines.listen(printStatus); command._deviceLogSubscription = command
.device
.getLogReader(app: package)
.logLines
.listen(printStatus);
LaunchResult result = await command.device.startApp( LaunchResult result = await command.device.startApp(
package, package,
......
...@@ -41,7 +41,7 @@ class LogsCommand extends FlutterCommand { ...@@ -41,7 +41,7 @@ class LogsCommand extends FlutterCommand {
if (argResults['clear']) if (argResults['clear'])
device.clearLogs(); device.clearLogs();
DeviceLogReader logReader = device.logReader; DeviceLogReader logReader = device.getLogReader();
Cache.releaseLockEarly(); Cache.releaseLockEarly();
......
...@@ -164,8 +164,10 @@ abstract class Device { ...@@ -164,8 +164,10 @@ abstract class Device {
String get sdkNameAndVersion; String get sdkNameAndVersion;
/// Get the log reader for this device. /// Get a log reader for this device.
DeviceLogReader get logReader; /// If [app] is specified, this will return a log reader specific to that
/// application. Otherwise, a global log reader will be returned.
DeviceLogReader getLogReader({ApplicationPackage app});
/// Get the port forwarder for this device. /// Get the port forwarder for this device.
DevicePortForwarder get portForwarder; DevicePortForwarder get portForwarder;
......
...@@ -226,7 +226,7 @@ class HotRunner extends ResidentRunner { ...@@ -226,7 +226,7 @@ class HotRunner extends ResidentRunner {
Map<String, dynamic> platformArgs = new Map<String, dynamic>(); Map<String, dynamic> platformArgs = new Map<String, dynamic>();
await startEchoingDeviceLog(); await startEchoingDeviceLog(_package);
printTrace('Launching loader on ${device.name}...'); printTrace('Launching loader on ${device.name}...');
......
...@@ -19,6 +19,8 @@ const String _ideviceinstallerInstructions = ...@@ -19,6 +19,8 @@ const String _ideviceinstallerInstructions =
'To work with iOS devices, please install ideviceinstaller.\n' 'To work with iOS devices, please install ideviceinstaller.\n'
'If you use homebrew, you can install it with "\$ brew install ideviceinstaller".'; 'If you use homebrew, you can install it with "\$ brew install ideviceinstaller".';
const Duration kPortForwardTimeout = const Duration(seconds: 10);
class IOSDevices extends PollingDeviceDiscovery { class IOSDevices extends PollingDeviceDiscovery {
IOSDevices() : super('IOSDevices'); IOSDevices() : super('IOSDevices');
...@@ -72,7 +74,7 @@ class IOSDevice extends Device { ...@@ -72,7 +74,7 @@ class IOSDevice extends Device {
@override @override
final String name; final String name;
_IOSDeviceLogReader _logReader; Map<ApplicationPackage, _IOSDeviceLogReader> _logReaders;
_IOSDevicePortForwarder _portForwarder; _IOSDevicePortForwarder _portForwarder;
...@@ -251,11 +253,13 @@ class IOSDevice extends Device { ...@@ -251,11 +253,13 @@ class IOSDevice extends Device {
// ports post launch. // ports post launch.
printTrace("Debugging is enabled, connecting to observatory and the diagnostic server"); printTrace("Debugging is enabled, connecting to observatory and the diagnostic server");
Future<int> forwardObsPort = _acquireAndForwardPort(ProtocolDiscovery.kObservatoryService, Future<int> forwardObsPort = _acquireAndForwardPort(app,
ProtocolDiscovery.kObservatoryService,
debuggingOptions.observatoryPort); debuggingOptions.observatoryPort);
Future<int> forwardDiagPort; Future<int> forwardDiagPort;
if (debuggingOptions.buildMode == BuildMode.debug) { if (debuggingOptions.buildMode == BuildMode.debug) {
forwardDiagPort = _acquireAndForwardPort(ProtocolDiscovery.kDiagnosticService, forwardDiagPort = _acquireAndForwardPort(app,
ProtocolDiscovery.kDiagnosticService,
debuggingOptions.diagnosticPort); debuggingOptions.diagnosticPort);
} else { } else {
forwardDiagPort = new Future<int>.value(null); forwardDiagPort = new Future<int>.value(null);
...@@ -272,7 +276,13 @@ class IOSDevice extends Device { ...@@ -272,7 +276,13 @@ class IOSDevice extends Device {
} }
printTrace("Application launched on the device. Attempting to forward ports."); printTrace("Application launched on the device. Attempting to forward ports.");
return Future.wait(<Future<int>>[forwardObsPort, forwardDiagPort]); return await Future.wait(<Future<int>>[forwardObsPort, forwardDiagPort])
.timeout(
kPortForwardTimeout,
onTimeout: () {
throw new TimeoutException('Timeout while waiting to acquire and forward ports.');
},
);
}); });
printTrace("Local Observatory Port: ${ports[0]}"); printTrace("Local Observatory Port: ${ports[0]}");
...@@ -293,10 +303,13 @@ class IOSDevice extends Device { ...@@ -293,10 +303,13 @@ class IOSDevice extends Device {
return new LaunchResult.succeeded(observatoryPort: localObsPort, diagnosticPort: localDiagPort); return new LaunchResult.succeeded(observatoryPort: localObsPort, diagnosticPort: localDiagPort);
} }
Future<int> _acquireAndForwardPort(String serviceName, int localPort) async { Future<int> _acquireAndForwardPort(
ApplicationPackage app,
String serviceName,
int localPort) async {
Duration stepTimeout = const Duration(seconds: 60); Duration stepTimeout = const Duration(seconds: 60);
Future<int> remote = new ProtocolDiscovery(logReader, serviceName).nextPort(); Future<int> remote = new ProtocolDiscovery(getLogReader(app: app), serviceName).nextPort();
int remotePort = await remote.timeout(stepTimeout, int remotePort = await remote.timeout(stepTimeout,
onTimeout: () { onTimeout: () {
...@@ -365,11 +378,9 @@ class IOSDevice extends Device { ...@@ -365,11 +378,9 @@ class IOSDevice extends Device {
String get _buildVersion => _getDeviceInfo(id, 'BuildVersion'); String get _buildVersion => _getDeviceInfo(id, 'BuildVersion');
@override @override
DeviceLogReader get logReader { DeviceLogReader getLogReader({ApplicationPackage app}) {
if (_logReader == null) _logReaders ??= <ApplicationPackage, _IOSDeviceLogReader>{};
_logReader = new _IOSDeviceLogReader(this); return _logReaders.putIfAbsent(app, () => new _IOSDeviceLogReader(this, app));
return _logReader;
} }
@override @override
...@@ -398,11 +409,20 @@ class IOSDevice extends Device { ...@@ -398,11 +409,20 @@ class IOSDevice extends Device {
} }
class _IOSDeviceLogReader extends DeviceLogReader { class _IOSDeviceLogReader extends DeviceLogReader {
_IOSDeviceLogReader(this.device) { RegExp _lineRegex;
_IOSDeviceLogReader(this.device, ApplicationPackage app) {
_linesController = new StreamController<String>.broadcast( _linesController = new StreamController<String>.broadcast(
onListen: _start, onListen: _start,
onCancel: _stop onCancel: _stop
); );
// Match for lines for the runner in syslog.
//
// iOS 9 format: Runner[297] <Notice>:
// iOS 10 format: Runner(libsystem_asl.dylib)[297] <Notice>:
String appName = app == null ? '' : app.name.replaceAll('.app', '');
_lineRegex = new RegExp(appName + r'(\(.*\))?\[[\d]+\] <[A-Za-z]+>: ');
} }
final IOSDevice device; final IOSDevice device;
...@@ -429,14 +449,8 @@ class _IOSDeviceLogReader extends DeviceLogReader { ...@@ -429,14 +449,8 @@ class _IOSDeviceLogReader extends DeviceLogReader {
}); });
} }
// Match for lines for the runner in syslog.
//
// iOS 9 format: Runner[297] <Notice>:
// iOS 10 format: Runner(libsystem_asl.dylib)[297] <Notice>:
static final RegExp _runnerRegex = new RegExp(r'Runner(\(.*\))?\[[\d]+\] <[A-Za-z]+>: ');
void _onLine(String line) { void _onLine(String line) {
Match match = _runnerRegex.firstMatch(line); Match match = _lineRegex.firstMatch(line);
if (match != null) { if (match != null) {
// Only display the log line after the initial device and executable information. // Only display the log line after the initial device and executable information.
......
...@@ -315,7 +315,7 @@ class IOSSimulator extends Device { ...@@ -315,7 +315,7 @@ class IOSSimulator extends Device {
@override @override
bool get supportsHotMode => true; bool get supportsHotMode => true;
_IOSSimulatorLogReader _logReader; Map<ApplicationPackage, _IOSSimulatorLogReader> _logReaders;
_IOSSimulatorDevicePortForwarder _portForwarder; _IOSSimulatorDevicePortForwarder _portForwarder;
String get xcrunPath => path.join('/usr', 'bin', 'xcrun'); String get xcrunPath => path.join('/usr', 'bin', 'xcrun');
...@@ -431,7 +431,8 @@ class IOSSimulator extends Device { ...@@ -431,7 +431,8 @@ class IOSSimulator extends Device {
ProtocolDiscovery observatoryDiscovery; ProtocolDiscovery observatoryDiscovery;
if (debuggingOptions.debuggingEnabled) if (debuggingOptions.debuggingEnabled)
observatoryDiscovery = new ProtocolDiscovery(logReader, ProtocolDiscovery.kObservatoryService); observatoryDiscovery = new ProtocolDiscovery(getLogReader(app: app),
ProtocolDiscovery.kObservatoryService);
// Prepare launch arguments. // Prepare launch arguments.
List<String> args = <String>[]; List<String> args = <String>[];
...@@ -554,11 +555,9 @@ class IOSSimulator extends Device { ...@@ -554,11 +555,9 @@ class IOSSimulator extends Device {
String get sdkNameAndVersion => category; String get sdkNameAndVersion => category;
@override @override
DeviceLogReader get logReader { DeviceLogReader getLogReader({ApplicationPackage app}) {
if (_logReader == null) _logReaders ??= <ApplicationPackage, _IOSSimulatorLogReader>{};
_logReader = new _IOSSimulatorLogReader(this); return _logReaders.putIfAbsent(app, () => new _IOSSimulatorLogReader(this, app));
return _logReader;
} }
@override @override
...@@ -629,13 +628,16 @@ class IOSSimulator extends Device { ...@@ -629,13 +628,16 @@ class IOSSimulator extends Device {
} }
class _IOSSimulatorLogReader extends DeviceLogReader { class _IOSSimulatorLogReader extends DeviceLogReader {
_IOSSimulatorLogReader(this.device) { String _appName;
_IOSSimulatorLogReader(this.device, ApplicationPackage app) {
_linesController = new StreamController<String>.broadcast( _linesController = new StreamController<String>.broadcast(
onListen: () { onListen: () {
_start(); _start();
}, },
onCancel: _stop onCancel: _stop
); );
_appName = app == null ? null : app.name.replaceAll('.app', '');
} }
final IOSSimulator device; final IOSSimulator device;
...@@ -712,8 +714,9 @@ class _IOSSimulatorLogReader extends DeviceLogReader { ...@@ -712,8 +714,9 @@ class _IOSSimulatorLogReader extends DeviceLogReader {
&& content.endsWith(']: 0x1')) && content.endsWith(']: 0x1'))
return null; return null;
if (category == 'Runner') if (_appName != null && category == _appName) {
return content; return content;
}
return '$category: $content'; return '$category: $content';
} }
match = _lastMessageSingleRegex.matchAsPrefix(string); match = _lastMessageSingleRegex.matchAsPrefix(string);
......
...@@ -8,6 +8,7 @@ import 'dart:io'; ...@@ -8,6 +8,7 @@ import 'dart:io';
import 'package:meta/meta.dart'; import 'package:meta/meta.dart';
import 'package:path/path.dart' as path; import 'package:path/path.dart' as path;
import 'application_package.dart';
import 'base/logger.dart'; import 'base/logger.dart';
import 'build_info.dart'; import 'build_info.dart';
import 'device.dart'; import 'device.dart';
...@@ -96,11 +97,11 @@ abstract class ResidentRunner { ...@@ -96,11 +97,11 @@ abstract class ResidentRunner {
}); });
} }
Future<Null> startEchoingDeviceLog() async { Future<Null> startEchoingDeviceLog(ApplicationPackage app) async {
if (_loggingSubscription != null) { if (_loggingSubscription != null) {
return; return;
} }
_loggingSubscription = device.logReader.logLines.listen((String line) { _loggingSubscription = device.getLogReader(app: app).logLines.listen((String line) {
if (!line.contains('Observatory listening on http') && if (!line.contains('Observatory listening on http') &&
!line.contains('Diagnostic server listening on http')) !line.contains('Diagnostic server listening on http'))
printStatus(line); printStatus(line);
......
...@@ -119,7 +119,7 @@ class RunAndStayResident extends ResidentRunner { ...@@ -119,7 +119,7 @@ class RunAndStayResident extends ResidentRunner {
if (traceStartup != null) if (traceStartup != null)
platformArgs = <String, dynamic>{ 'trace-startup': traceStartup }; platformArgs = <String, dynamic>{ 'trace-startup': traceStartup };
await startEchoingDeviceLog(); await startEchoingDeviceLog(_package);
if (_mainPath == null) { if (_mainPath == null) {
assert(prebuiltMode); assert(prebuiltMode);
printStatus('Running ${_package.displayName} on ${device.name}'); printStatus('Running ${_package.displayName} on ${device.name}');
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment