Skip to content

Commit 09bd0f6

Browse files
authored
Support logging 'flutter run' communication to DAP clients (#118674)
* Support logging 'flutter run' communication to DAP clients Fixes Dart-Code/Dart-Code#4266. * Fix test
1 parent 818bb4e commit 09bd0f6

File tree

4 files changed

+62
-6
lines changed

4 files changed

+62
-6
lines changed

packages/flutter_tools/lib/src/debug_adapters/flutter_adapter.dart

+20-3
Original file line numberDiff line numberDiff line change
@@ -346,6 +346,7 @@ class FlutterDebugAdapter extends FlutterBaseDebugAdapter {
346346
final String messageString = jsonEncode(message);
347347
// Flutter requests are always wrapped in brackets as an array.
348348
final String payload = '[$messageString]\n';
349+
_logTraffic('==> [Flutter] $payload');
349350
process.stdin.writeln(payload);
350351
}
351352

@@ -447,7 +448,7 @@ class FlutterDebugAdapter extends FlutterBaseDebugAdapter {
447448
@override
448449
void handleExitCode(int code) {
449450
final String codeSuffix = code == 0 ? '' : ' ($code)';
450-
logger?.call('Process exited ($code)');
451+
_logTraffic('<== [Flutter] Process exited ($code)');
451452
handleSessionTerminate(codeSuffix);
452453
}
453454

@@ -559,7 +560,7 @@ class FlutterDebugAdapter extends FlutterBaseDebugAdapter {
559560

560561
@override
561562
void handleStderr(List<int> data) {
562-
logger?.call('stderr: $data');
563+
_logTraffic('<== [Flutter] [stderr] $data');
563564
sendOutput('stderr', utf8.decode(data));
564565
}
565566

@@ -575,7 +576,7 @@ class FlutterDebugAdapter extends FlutterBaseDebugAdapter {
575576
// - the item has an "event" field that is a String
576577
// - the item has a "params" field that is a Map<String, Object?>?
577578

578-
logger?.call('stdout: $data');
579+
_logTraffic('<== [Flutter] $data');
579580

580581
// Output is sent as console (eg. output from tooling) until the app has
581582
// started, then stdout (users output). This is so info like
@@ -639,6 +640,22 @@ class FlutterDebugAdapter extends FlutterBaseDebugAdapter {
639640
}
640641
}
641642

643+
/// Logs JSON traffic to aid debugging.
644+
///
645+
/// If `sendLogsToClient` was `true` in the launch/attach config, logs will
646+
/// also be sent back to the client in a "dart.log" event to simplify
647+
/// capturing logs from the IDE (such as using the **Dart: Capture Logs**
648+
/// command in VS Code).
649+
void _logTraffic(String message) {
650+
logger?.call(message);
651+
if (sendLogsToClient) {
652+
sendEvent(
653+
RawEventBody(<String, String>{'message': message}),
654+
eventType: 'dart.log',
655+
);
656+
}
657+
}
658+
642659
/// Performs a restart/reload by sending the `app.restart` message to the `flutter run --machine` process.
643660
Future<void> _performRestart(
644661
bool fullRestart, [

packages/flutter_tools/test/general.shard/dap/mocks.dart

+5
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,11 @@ class MockFlutterDebugAdapter extends FlutterDebugAdapter {
5757
late List<String> processArgs;
5858
late Map<String, String>? env;
5959

60+
/// Overrides base implementation of [sendLogsToClient] which requires valid
61+
/// `args` to have been set which may not be the case for mocks.
62+
@override
63+
bool get sendLogsToClient => false;
64+
6065
final StreamController<Map<String, Object?>> _dapToClientMessagesController = StreamController<Map<String, Object?>>.broadcast();
6166

6267
/// A stream of all messages sent from the adapter back to the client.

packages/flutter_tools/test/integration.shard/debug_adapter/flutter_adapter_test.dart

+33
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,39 @@ void main() {
7070
]);
7171
});
7272

73+
testWithoutContext('logs to client when sendLogsToClient=true', () async {
74+
final BasicProject project = BasicProject();
75+
await project.setUpIn(tempDir);
76+
77+
// Launch the app and wait for it to print "topLevelFunction".
78+
await Future.wait(<Future<void>>[
79+
dap.client.stdoutOutput.firstWhere((String output) => output.startsWith('topLevelFunction')),
80+
dap.client.start(
81+
launch: () => dap.client.launch(
82+
cwd: project.dir.path,
83+
noDebug: true,
84+
toolArgs: <String>['-d', 'flutter-tester'],
85+
sendLogsToClient: true,
86+
),
87+
),
88+
], eagerError: true);
89+
90+
// Capture events while terminating.
91+
final Future<List<Event>> logEventsFuture = dap.client.events('dart.log').toList();
92+
await dap.client.terminate();
93+
94+
// Ensure logs contain both the app.stop request and the result.
95+
final List<Event> logEvents = await logEventsFuture;
96+
final List<String> logMessages = logEvents.map((Event l) => (l.body! as Map<String, Object?>)['message']! as String).toList();
97+
expect(
98+
logMessages,
99+
containsAll(<Matcher>[
100+
startsWith('==> [Flutter] [{"id":1,"method":"app.stop"'),
101+
startsWith('<== [Flutter] [{"id":1,"result":true}]'),
102+
]),
103+
);
104+
});
105+
73106
testWithoutContext('can run and terminate a Flutter app in noDebug mode', () async {
74107
final BasicProject project = BasicProject();
75108
await project.setUpIn(tempDir);

packages/flutter_tools/test/integration.shard/debug_adapter/test_client.dart

+4-3
Original file line numberDiff line numberDiff line change
@@ -153,6 +153,7 @@ class DapTestClient {
153153
bool? debugExternalPackageLibraries,
154154
bool? evaluateGettersInDebugViews,
155155
bool? evaluateToStringInDebugViews,
156+
bool sendLogsToClient = false,
156157
}) {
157158
return sendRequest(
158159
FlutterLaunchRequestArguments(
@@ -167,9 +168,9 @@ class DapTestClient {
167168
evaluateGettersInDebugViews: evaluateGettersInDebugViews,
168169
evaluateToStringInDebugViews: evaluateToStringInDebugViews,
169170
// When running out of process, VM Service traffic won't be available
170-
// to the client-side logger, so force logging on which sends VM Service
171-
// traffic in a custom event.
172-
sendLogsToClient: captureVmServiceTraffic,
171+
// to the client-side logger, so force logging regardless of
172+
// `sendLogsToClient` which sends VM Service traffic in a custom event.
173+
sendLogsToClient: sendLogsToClient || captureVmServiceTraffic,
173174
),
174175
// We can't automatically pick the command when using a custom type
175176
// (FlutterLaunchRequestArguments).

0 commit comments

Comments
 (0)