Skip to content

Commit d913222

Browse files
authored
Merge pull request #1799 from ahoppen/input-mirror
Add a logging option to record all notifications received by SoruceKit-LSP
2 parents bdf5ab4 + 98c44f8 commit d913222

File tree

5 files changed

+43
-3
lines changed

5 files changed

+43
-3
lines changed

Documentation/Configuration File.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ The structure of the file is currently not guaranteed to be stable. Options may
4242
- `logging`: Dictionary with the following keys, changing SourceKit-LSP’s logging behavior on non-Apple platforms. On Apple platforms, logging is done through the [system log](Diagnose%20Bundle.md#Enable%20Extended%20Logging). These options can only be set globally and not per workspace.
4343
- `logLevel: "debug"|"info"|"default"|"error"|"fault"`: The level from which one onwards log messages should be written.
4444
- `privacyLevel: "public"|"private"|"sensitive"`: Whether potentially sensitive information should be redacted. Default is `public`, which redacts potentially sensitive information.
45+
- `inputMirrorDirectory: string`: Write all input received by SourceKit-LSP on stdin to a file in this directory. Useful to record and replay an entire SourceKit-LSP session.
4546
- `defaultWorkspaceType: "buildserver"|"compdb"|"swiftpm"`: Overrides workspace type selection logic.
4647
- `generatedFilesPath: string`: Directory in which generated interfaces and macro expansions should be stored.
4748
- `backgroundIndexing: bool`: Explicitly enable or disable background indexing.

Package.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -281,6 +281,7 @@ var targets: [Target] = [
281281
.testTarget(
282282
name: "SKSupportTests",
283283
dependencies: [
284+
"SKLogging",
284285
"SKSupport",
285286
"SKTestSupport",
286287
"SwiftExtensions",

Sources/LanguageServerProtocolJSONRPC/JSONRPCConnection.swift

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -265,9 +265,15 @@ public final class JSONRPCConnection: Connection {
265265
/// Start processing `inFD` and send messages to `receiveHandler`.
266266
///
267267
/// - parameter receiveHandler: The message handler to invoke for requests received on the `inFD`.
268+
/// - parameter mirrorFile: If non-nil, all input received by this `JSONRPCConnection` will be written to the file
269+
/// handle
268270
///
269271
/// - Important: `start` must be called before sending any data over the `JSONRPCConnection`.
270-
public func start(receiveHandler: MessageHandler, closeHandler: @escaping @Sendable () async -> Void = {}) {
272+
public func start(
273+
receiveHandler: MessageHandler,
274+
mirrorFile: FileHandle? = nil,
275+
closeHandler: @escaping @Sendable () async -> Void = {}
276+
) {
271277
queue.sync {
272278
precondition(state == .created)
273279
state = .running
@@ -294,6 +300,10 @@ public final class JSONRPCConnection: Connection {
294300
return
295301
}
296302

303+
orLog("Writing data mirror file") {
304+
try mirrorFile?.write(contentsOf: data)
305+
}
306+
297307
// Parse and handle any messages in `buffer + data`, leaving any remaining unparsed bytes in `buffer`.
298308
if self.requestBuffer.isEmpty {
299309
data.withUnsafeBytes { (pointer: UnsafePointer<UInt8>) in

Sources/SKOptions/SourceKitLSPOptions.swift

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -200,21 +200,30 @@ public struct SourceKitLSPOptions: Sendable, Codable, Equatable {
200200
public struct LoggingOptions: Sendable, Codable, Equatable {
201201
/// The level from which one onwards log messages should be written.
202202
public var level: String?
203+
203204
/// Whether potentially sensitive information should be redacted.
204205
public var privacyLevel: String?
205206

207+
/// Write all input received by SourceKit-LSP on stdin to a file in this directory.
208+
///
209+
/// Useful to record and replay an entire SourceKit-LSP session.
210+
public var inputMirrorDirectory: String?
211+
206212
public init(
207213
level: String? = nil,
208-
privacyLevel: String? = nil
214+
privacyLevel: String? = nil,
215+
inputMirrorDirectory: String? = nil
209216
) {
210217
self.level = level
211218
self.privacyLevel = privacyLevel
219+
self.inputMirrorDirectory = inputMirrorDirectory
212220
}
213221

214222
static func merging(base: LoggingOptions, override: LoggingOptions?) -> LoggingOptions {
215223
return LoggingOptions(
216224
level: override?.level ?? base.level,
217-
privacyLevel: override?.privacyLevel ?? base.privacyLevel
225+
privacyLevel: override?.privacyLevel ?? base.privacyLevel,
226+
inputMirrorDirectory: override?.inputMirrorDirectory ?? base.inputMirrorDirectory
218227
)
219228
}
220229
}

Sources/sourcekit-lsp/SourceKitLSP.swift

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -331,6 +331,24 @@ struct SourceKitLSP: AsyncParsableCommand {
331331

332332
let installPath = try AbsolutePath(validating: Bundle.main.bundlePath)
333333

334+
var inputMirror: FileHandle? = nil
335+
if let inputMirrorDirectory = globalConfigurationOptions.loggingOrDefault.inputMirrorDirectory {
336+
orLog("Setting up input mirror") {
337+
let dateFormatter = ISO8601DateFormatter()
338+
dateFormatter.timeZone = NSTimeZone.local
339+
let date = dateFormatter.string(from: Date()).replacingOccurrences(of: ":", with: "-")
340+
341+
let inputMirrorDirectory = URL(fileURLWithPath: inputMirrorDirectory)
342+
let inputMirrorURL = inputMirrorDirectory.appendingPathComponent("\(date).log")
343+
344+
logger.log("Mirroring input to \(inputMirrorURL)")
345+
try FileManager.default.createDirectory(at: inputMirrorDirectory, withIntermediateDirectories: true)
346+
FileManager.default.createFile(atPath: try inputMirrorURL.filePath, contents: nil)
347+
348+
inputMirror = try FileHandle(forWritingTo: inputMirrorURL)
349+
}
350+
}
351+
334352
let server = SourceKitLSPServer(
335353
client: clientConnection,
336354
toolchainRegistry: ToolchainRegistry(installPath: installPath, localFileSystem),
@@ -342,6 +360,7 @@ struct SourceKitLSP: AsyncParsableCommand {
342360
)
343361
clientConnection.start(
344362
receiveHandler: server,
363+
mirrorFile: inputMirror,
345364
closeHandler: {
346365
await server.prepareForExit()
347366
// Use _Exit to avoid running static destructors due to https://github.com/swiftlang/swift/issues/55112.

0 commit comments

Comments
 (0)