Skip to content

Commit e1ad802

Browse files
authored
fix: Await all logging has completed before finalizing the session. (serverpod#2554)
1 parent 2142c8b commit e1ad802

File tree

3 files changed

+109
-3
lines changed

3 files changed

+109
-3
lines changed

packages/serverpod/lib/src/server/log_manager/log_manager.dart

Lines changed: 53 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import 'dart:async';
12
import 'dart:io';
23
import 'package:meta/meta.dart';
34
import 'package:serverpod/database.dart';
@@ -27,6 +28,8 @@ class SessionLogManager {
2728

2829
int get _nextLogOrderId => ++_logOrderId;
2930

31+
final _FutureTaskManager _logTasks;
32+
3033
/// Creates a new [LogManager] from [RuntimeSettings].
3134
@internal
3235
SessionLogManager(
@@ -37,7 +40,8 @@ class SessionLogManager {
3740
_numberOfQueries = 0,
3841
_logWriter = logWriter,
3942
_settingsForSession = settingsForSession,
40-
_serverId = serverId;
43+
_serverId = serverId,
44+
_logTasks = _FutureTaskManager();
4145

4246
bool _shouldLogQuery({
4347
required Session session,
@@ -242,10 +246,11 @@ class SessionLogManager {
242246
Function(int, T) setSessionLogId,
243247
) async {
244248
await _attemptOpenStreamingLog(session: session);
249+
245250
if (_continuouslyLogging(session) && session is StreamingSession) {
246251
try {
247252
setSessionLogId(session.sessionLogId!, entry);
248-
await writeLog(session, entry);
253+
_logTasks.addTask(() => writeLog(session, entry));
249254
} catch (exception, stackTrace) {
250255
stderr
251256
.writeln('${DateTime.now().toUtc()} FAILED TO LOG STREAMING $type');
@@ -311,6 +316,9 @@ class SessionLogManager {
311316
String? exception,
312317
StackTrace? stackTrace,
313318
}) async {
319+
await _openStreamLogLock.synchronized(() {});
320+
await _logTasks.awaitAllTasks();
321+
314322
var duration = session.duration;
315323
var cachedEntry = session.sessionLogs;
316324
LogSettings logSettings = _settingsForSession(session);
@@ -424,3 +432,46 @@ class LogManager {
424432
return logEntry;
425433
}
426434
}
435+
436+
typedef _TaskCallback = Future<void> Function();
437+
438+
class _FutureTaskManager {
439+
final Set<_TaskCallback> _pendingTasks = {};
440+
441+
Completer<void>? _tasksCompleter;
442+
443+
/// Synchronously adds a task to the task manager.
444+
void addTask(_TaskCallback task) {
445+
_tasksCompleter ??= Completer<void>();
446+
_pendingTasks.add(task);
447+
448+
task().then((value) {
449+
_completeTask(task);
450+
}).onError((error, stackTrace) {
451+
_completeTask(task);
452+
var e = error;
453+
if (e is Exception) throw e;
454+
if (e is Error) throw e;
455+
});
456+
}
457+
458+
void _completeTask(_TaskCallback task) {
459+
_pendingTasks.remove(task);
460+
461+
var tasksCompleter = _tasksCompleter;
462+
if (_pendingTasks.isEmpty && tasksCompleter != null) {
463+
tasksCompleter.complete();
464+
_tasksCompleter = null;
465+
}
466+
}
467+
468+
Future<void> awaitAllTasks() {
469+
var completer = _tasksCompleter;
470+
471+
if (completer == null) {
472+
return Future.value();
473+
} else {
474+
return completer.future;
475+
}
476+
}
477+
}

tests/serverpod_test_server/test_integration/logging/endpoint_call_logging_test.dart

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -232,13 +232,15 @@ void main() async {
232232
await client.logging.failedQueryMethod();
233233
} catch (_) {}
234234

235+
await Future.delayed(Duration(milliseconds: 100));
236+
235237
var logs = await LoggingUtil.findAllLogs(session);
236238

237239
expect(logs, hasLength(1));
238240

239241
expect(logs.first.sessionLogEntry.endpoint, 'logging');
240242
expect(logs.first.sessionLogEntry.method, 'failedQueryMethod');
241-
}, skip: 'Fail because of the synchronized lock method, not sure why.');
243+
});
242244

243245
test(
244246
'Given a log setting with everything turned on when calling a method logging a message then the log including the message log is written.',

tests/serverpod_test_server/test_integration/logging/endpoint_stream_logging_test.dart

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,59 @@ void main() async {
5151
expect(logs.first.messages, hasLength(1));
5252
});
5353

54+
test(
55+
'Given that continuous logging is turned on when sending a stream message and closing the connection then the log is created.',
56+
() async {
57+
var settings = RuntimeSettingsBuilder()
58+
.withLogSettings(LogSettingsBuilder()
59+
.withLogStreamingSessionsContinuously(true)
60+
.build())
61+
.build();
62+
await server.updateRuntimeSettings(settings);
63+
await client.openStreamingConnection(
64+
disconnectOnLostInternetConnection: false,
65+
);
66+
67+
await client.logging.sendStreamMessage(Types());
68+
69+
await client.closeStreamingConnection();
70+
71+
// Wait for the log to be written
72+
await Future.delayed(Duration(milliseconds: 100));
73+
74+
var logs = await LoggingUtil.findAllLogs(session);
75+
76+
expect(logs, hasLength(1));
77+
expect(logs.first.messages, hasLength(1));
78+
});
79+
80+
test(
81+
'Given that continuous logging is turned on when sending a stream message and closing the connection then the log is created.',
82+
() async {
83+
var settings = RuntimeSettingsBuilder()
84+
.withLogSettings(LogSettingsBuilder()
85+
.withLogStreamingSessionsContinuously(true)
86+
.build())
87+
.build();
88+
await server.updateRuntimeSettings(settings);
89+
await client.openStreamingConnection(
90+
disconnectOnLostInternetConnection: false,
91+
);
92+
93+
await client.logging.sendStreamMessage(Types());
94+
await client.logging.sendStreamMessage(Types());
95+
96+
await client.closeStreamingConnection();
97+
98+
// Wait for the log to be written
99+
await Future.delayed(Duration(milliseconds: 100));
100+
101+
var logs = await LoggingUtil.findAllLogs(session);
102+
103+
expect(logs, hasLength(1));
104+
expect(logs.first.messages, hasLength(2));
105+
});
106+
54107
test(
55108
'Given that continuous logging is turned on when sending several stream messages without closing the connection then the logs are created with different message ids.',
56109
() async {

0 commit comments

Comments
 (0)