Skip to content

Commit bc6b01e

Browse files
bwilkersonCommit Queue
authored andcommitted
Add support for capturing a session log for a short time
Adds a new logging sink that supports an in-memory storage of events for a subset of the whole server session. This is support for being able to add 'start' and 'stop' buttons to the insight pages, which will allow us and users to capture a smaller number of log entries. Change-Id: Ia562e4da7b09c11ad9191c24cb85102970e1b116 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/461901 Reviewed-by: Keerti Parthasarathy <keertip@google.com> Commit-Queue: Brian Wilkerson <brianwilkerson@google.com> Reviewed-by: Jake Macdonald <jakemac@google.com>
1 parent e0d7c13 commit bc6b01e

File tree

7 files changed

+85
-20
lines changed

7 files changed

+85
-20
lines changed

pkg/analysis_server/lib/src/server/driver.dart

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -335,10 +335,12 @@ class Driver implements ServerStarter {
335335
// Initialize the session logging service.
336336
var sessionLogFilePath = results.option(sessionLogOption);
337337
_sessionLogger = SessionLogger();
338+
var inMemorySink = SessionLoggerInMemorySink(maxBufferLength: 256);
339+
_sessionLogger.sink = inMemorySink;
338340
if (sessionLogFilePath != null) {
339-
_sessionLogger.sink = SessionLoggerFileSink(sessionLogFilePath);
340-
_sessionLogger.logCommandLine(arguments: arguments);
341+
inMemorySink.nextLogger = SessionLoggerFileSink(sessionLogFilePath);
341342
}
343+
_sessionLogger.logCommandLine(arguments: arguments);
342344

343345
int? diagnosticServerPort;
344346
var portValue =

pkg/analysis_server/tool/log_player/log_entry.dart renamed to pkg/analysis_server/lib/src/session_logger/log_entry.dart

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,6 @@
22
// for details. All rights reserved. Use of this source code is governed by a
33
// BSD-style license that can be found in the LICENSE file.
44

5-
/// @docImport 'log.dart';
6-
library;
7-
85
import 'package:analysis_server/src/session_logger/entry_keys.dart' as key;
96
import 'package:analysis_server/src/session_logger/entry_kind.dart';
107
import 'package:analysis_server/src/session_logger/process_id.dart';
@@ -43,6 +40,13 @@ extension type Message(JsonMap map) {
4340
/// Whether this message is a request for the server to exit.
4441
bool get isExit => method == 'exit';
4542

43+
/// Whether this message is a request for the server to initialize itself.
44+
bool get isInitialize => method == 'initialize';
45+
46+
/// Whether this message is a notification to the server indicating that the
47+
/// client is initialized.
48+
bool get isInitialized => method == 'initialized';
49+
4650
/// Whether this message is a request from the server to log a message.
4751
bool get isLogMessage => method == 'window/logMessage';
4852

pkg/analysis_server/lib/src/session_logger/session_logger.dart

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ class SessionLogger {
1717
void logCommandLine({required List<String> arguments}) {
1818
sink?.writeLogEntry({
1919
key.time: DateTime.now().millisecondsSinceEpoch,
20-
key.kind: EntryKind.message.name,
20+
key.kind: EntryKind.commandLine.name,
2121
key.argList: arguments,
2222
});
2323
}

pkg/analysis_server/lib/src/session_logger/session_logger_sink.dart

Lines changed: 70 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
import 'dart:convert';
66
import 'dart:io';
77

8+
import 'package:analysis_server/src/session_logger/log_entry.dart';
89
import 'package:analysis_server/src/session_logger/process_id.dart';
910

1011
/// A sink for a session logger that will write entries to a file.
@@ -32,27 +33,87 @@ class SessionLoggerFileSink extends SessionLoggerSink {
3233
}
3334

3435
/// A sink for a session logger that will write entries to an in-memory buffer.
35-
class SessionLoggerMemorySink extends SessionLoggerSink {
36-
/// The maximum number of entries stored in the [buffer].
36+
class SessionLoggerInMemorySink extends SessionLoggerSink {
37+
/// The maximum number of entries stored in the [_sessionBuffer].
3738
int maxBufferLength;
3839

39-
/// The buffer in which entries are stored.
40-
List<JsonMap> buffer = [];
40+
/// Whether entries should be captured in the buffer.
41+
bool _capturingEntries = false;
42+
43+
/// A session logger to which requests should be forwarded, or `null` if there
44+
/// is no logger to forward requests to.
45+
SessionLoggerSink? nextLogger;
46+
47+
/// The buffer in which initialization related entries are stored.
48+
final List<LogEntry> _initializationBuffer = [];
49+
50+
/// The buffer in which normal entries are stored.
51+
final List<LogEntry> _sessionBuffer = [];
4152

4253
/// Initialize a newly created sink to store up to [maxBufferLength] entries.
43-
SessionLoggerMemorySink(this.maxBufferLength);
54+
SessionLoggerInMemorySink({required this.maxBufferLength});
4455

4556
@override
4657
Future<void> close() async {
47-
// There's nothing to do in this case.
58+
await nextLogger?.close();
59+
}
60+
61+
/// Stops the capturing of entries.
62+
void startCapture() {
63+
_capturingEntries = true;
64+
}
65+
66+
/// Stops the capturing of entries and returns a list of the entries that were
67+
/// captured.
68+
///
69+
/// The list includes necessary initialization entries that might have
70+
/// occurred before the capture was started.
71+
List<LogEntry> stopCapture() {
72+
_capturingEntries = false;
73+
var capturedEntries = [..._initializationBuffer, ..._sessionBuffer];
74+
_sessionBuffer.clear();
75+
return capturedEntries;
4876
}
4977

5078
@override
5179
void writeLogEntry(JsonMap entry) {
52-
if (buffer.length > maxBufferLength) {
53-
buffer.removeAt(0);
80+
nextLogger?.writeLogEntry(entry);
81+
var logEntry = LogEntry(entry);
82+
if (_isInitializationEntry(logEntry)) {
83+
_initializationBuffer.add(logEntry);
84+
return;
85+
}
86+
if (_capturingEntries) {
87+
if (_sessionBuffer.length > maxBufferLength) {
88+
_sessionBuffer.removeAt(0);
89+
}
90+
_sessionBuffer.add(logEntry);
91+
} else {
92+
// TODO(brianwilkerson): We also need to collect the most recent messages
93+
// related to which directories are open in the workspace and which files
94+
// are priority files. These should be in separate lists so that we can
95+
// flush messages that are no longer required in order to reproduce the
96+
// captured messages.
97+
}
98+
}
99+
100+
/// Returns whether the [entry] is an initialization entry.
101+
///
102+
/// An initialization entry is defined as an entry that would need to be
103+
/// replayed in order to make the captured entries make sense.
104+
///
105+
/// Initialization entries are captured even when [captureEntries] is `false`.
106+
bool _isInitializationEntry(LogEntry entry) {
107+
if (entry.isCommandLine) return true;
108+
if (entry.isMessage) {
109+
// TODO(brianwilkerson): This list is incomplete in two ways.
110+
// 1. It does not support the legacy protocol.
111+
// 2. It does not capture entries that indicate the state of either the
112+
// workspace or the priority files.
113+
var message = entry.message;
114+
return message.isInitialize || message.isInitialized;
54115
}
55-
buffer.add(entry);
116+
return false;
56117
}
57118
}
58119

pkg/analysis_server/tool/log_player/log.dart

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,9 @@
44

55
import 'dart:convert';
66

7+
import 'package:analysis_server/src/session_logger/log_entry.dart';
78
import 'package:analyzer/file_system/file_system.dart';
89

9-
import 'log_entry.dart';
10-
1110
/// The content of a log file.
1211
class Log {
1312
/// The entries in the log.

pkg/analysis_server/tool/log_player/log_player.dart

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,10 @@
22
// for details. All rights reserved. Use of this source code is governed by a
33
// BSD-style license that can be found in the LICENSE file.
44

5+
import 'package:analysis_server/src/session_logger/log_entry.dart';
56
import 'package:analysis_server/src/session_logger/process_id.dart';
67

78
import 'log.dart';
8-
import 'log_entry.dart';
99
import 'server_driver.dart';
1010

1111
/// An object used to play back the messages in a log.

pkg/analysis_server/tool/log_player/server_driver.dart

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,7 @@ import 'dart:io';
77

88
import 'package:analysis_server/lsp_protocol/protocol.dart' show jsonRpcVersion;
99
import 'package:analysis_server/src/server/driver.dart';
10-
11-
import 'log_entry.dart';
10+
import 'package:analysis_server/src/session_logger/log_entry.dart';
1211

1312
/// The driver used to communicate with the analysis server.
1413
class ServerDriver {

0 commit comments

Comments
 (0)