Skip to content

Commit db61954

Browse files
committed
fix duplicate logs
1 parent 7f1dae0 commit db61954

File tree

1 file changed

+49
-21
lines changed

1 file changed

+49
-21
lines changed

webdev/lib/src/daemon/app_domain.dart

Lines changed: 49 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,9 @@ class AppDomain extends Domain {
2525

2626
final _appStates = <String, _AppState>{};
2727

28+
// Prevents duplicate stdout listeners for the same appId
29+
final _activeListeners = <String>{};
30+
2831
// Mapping from service name to service method.
2932
final Map<String, String> _registeredMethodsForService = <String, String>{};
3033

@@ -85,24 +88,11 @@ class AppDomain extends Domain {
8588
'deviceId': 'chrome',
8689
'launchMode': 'run'
8790
});
88-
// TODO(grouma) - limit the catch to the appropriate error.
89-
try {
90-
await vmService.streamCancel('Stdout');
91-
} catch (_) {}
92-
try {
93-
await vmService.streamListen('Stdout');
94-
} catch (_) {}
95-
try {
96-
vmService.onServiceEvent.listen(_onServiceEvent);
97-
await vmService.streamListen('Service');
98-
} catch (_) {}
91+
92+
// Set up VM service listeners (only once per appId to prevent duplicates)
9993
// ignore: cancel_subscriptions
100-
final stdOutSub = vmService.onStdoutEvent.listen((log) {
101-
sendEvent('app.log', {
102-
'appId': appId,
103-
'log': utf8.decode(base64.decode(log.bytes!)),
104-
});
105-
});
94+
final stdOutSub = await _setupVmServiceListeners(appId, vmService);
95+
10696
sendEvent('app.debugPort', {
10797
'appId': appId,
10898
'port': debugConnection.port,
@@ -121,8 +111,7 @@ class AppDomain extends Domain {
121111
appConnection.runMain();
122112

123113
unawaited(debugConnection.onDone.whenComplete(() {
124-
appState.dispose();
125-
_appStates.remove(appId);
114+
_cleanupAppConnection(appId, appState);
126115
}));
127116
}
128117

@@ -223,20 +212,59 @@ class AppDomain extends Domain {
223212
return true;
224213
}
225214

215+
/// Sets up VM service listeners for the given appId if not already active.
216+
/// Returns the stdout subscription if created, null otherwise.
217+
Future<StreamSubscription<Event>?> _setupVmServiceListeners(
218+
String appId, VmService vmService) async {
219+
if (_activeListeners.contains(appId)) {
220+
return null; // Already listening for this appId
221+
}
222+
223+
_activeListeners.add(appId);
224+
225+
// TODO(grouma) - limit the catch to the appropriate error.
226+
try {
227+
await vmService.streamCancel('Stdout');
228+
} catch (_) {}
229+
try {
230+
await vmService.streamListen('Stdout');
231+
} catch (_) {}
232+
try {
233+
vmService.onServiceEvent.listen(_onServiceEvent);
234+
await vmService.streamListen('Service');
235+
} catch (_) {}
236+
237+
// ignore: cancel_subscriptions
238+
return vmService.onStdoutEvent.listen((log) {
239+
sendEvent('app.log', {
240+
'appId': appId,
241+
'log': utf8.decode(base64.decode(log.bytes!)),
242+
});
243+
});
244+
}
245+
246+
/// Cleans up an app connection and its associated listeners.
247+
void _cleanupAppConnection(String appId, _AppState appState) {
248+
appState.dispose();
249+
_appStates.remove(appId);
250+
_activeListeners.remove(appId);
251+
}
252+
226253
@override
227254
void dispose() {
228255
_isShutdown = true;
229256
for (final state in _appStates.values) {
230257
state.dispose();
231258
}
232259
_appStates.clear();
260+
_activeListeners.clear();
233261
}
234262
}
235263

236264
class _AppState {
237265
final DebugConnection _debugConnection;
238266
final StreamSubscription<BuildResult> _resultSub;
239-
final StreamSubscription<Event> _stdOutSub;
267+
final StreamSubscription<Event>? _stdOutSub;
240268

241269
bool _isDisposed = false;
242270

@@ -247,7 +275,7 @@ class _AppState {
247275
void dispose() {
248276
if (_isDisposed) return;
249277
_isDisposed = true;
250-
_stdOutSub.cancel();
278+
_stdOutSub?.cancel();
251279
_resultSub.cancel();
252280
_debugConnection.close();
253281
}

0 commit comments

Comments
 (0)