Skip to content

Commit 9cd33a6

Browse files
authored
Merge pull request #924 from ahoppen/ahoppen/capture-clangd-stderr
Capture stderr from clangd and forward it to the logger
2 parents 8d71b31 + 81589bc commit 9cd33a6

File tree

1 file changed

+31
-0
lines changed

1 file changed

+31
-0
lines changed

Sources/SourceKitLSP/Clang/ClangLanguageServer.swift

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,26 @@ extension NSLock {
3232
}
3333
}
3434

35+
/// Gathers data from clangd's stderr pipe. When it has accumulated a full line, writes the the line to the logger.
36+
fileprivate class ClangdStderrLogForwarder {
37+
private var buffer = Data()
38+
39+
func handle(_ newData: Data) {
40+
self.buffer += newData
41+
while let newlineIndex = self.buffer.firstIndex(of: UInt8(ascii: "\n")) {
42+
// Output a separate log message for every line in clangd's stderr.
43+
// The reason why we don't output multiple lines in a single log message is that
44+
// a) os_log truncates log messages at about 1000 bytes. The assumption is that a single line is usually less
45+
// than 1000 bytes long but if we merge multiple lines into one message, we might easily exceed this limit.
46+
// b) It might be confusing why sometimes a single log message contains one line while sometimes it contains
47+
// multiple.
48+
let logger = Logger(subsystem: subsystem, category: "clangd-stderr")
49+
logger.info("\(String(data: self.buffer[...newlineIndex], encoding: .utf8) ?? "<invalid UTF-8>")")
50+
buffer = buffer[buffer.index(after: newlineIndex)...]
51+
}
52+
}
53+
}
54+
3555
/// A thin wrapper over a connection to a clangd server providing build setting handling.
3656
///
3757
/// In addition, it also intercepts notifications and replies from clangd in order to do things
@@ -203,6 +223,17 @@ actor ClangLanguageServerShim: ToolchainLanguageServer, MessageHandler {
203223

204224
process.standardOutput = clangdToUs
205225
process.standardInput = usToClangd
226+
let logForwarder = ClangdStderrLogForwarder()
227+
let stderrHandler = Pipe()
228+
stderrHandler.fileHandleForReading.readabilityHandler = { fileHandle in
229+
let newData = fileHandle.availableData
230+
if newData.count == 0 {
231+
stderrHandler.fileHandleForReading.readabilityHandler = nil
232+
} else {
233+
logForwarder.handle(newData)
234+
}
235+
}
236+
process.standardError = stderrHandler
206237
process.terminationHandler = { [weak self] process in
207238
logger.log(
208239
level: process.terminationReason == .exit ? .default : .error,

0 commit comments

Comments
 (0)