Skip to content

Commit 655a93f

Browse files
committed
Add structure to the index logs
When the client supports it, communicate the structure of tasks that were stared during background indexing or by the build server to the client. If there are multiple operations happening in parallel, this allows the client to display them in separate log tracks instead of interspersing them with the emoji prefixes like we do today.
1 parent bafd8ba commit 655a93f

22 files changed

+441
-69
lines changed

Contributor Documentation/LSP Extensions.md

Lines changed: 55 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -481,7 +481,7 @@ export interface DidChangeActiveDocumentParams {
481481
482482
## `window/logMessage`
483483
484-
Added field:
484+
Added fields:
485485
486486
```ts
487487
/**
@@ -490,6 +490,60 @@ Added field:
490490
* Clients may ignore this parameter and add the message to the global log
491491
*/
492492
logName?: string;
493+
494+
495+
/**
496+
* If specified, allows grouping log messages that belong to the same originating task together instead of logging
497+
* them in chronological order in which they were produced.
498+
*
499+
* LSP Extension guarded by the experimental `structured-logs` feature.
500+
*/
501+
structure?: StructuredLogBegin | StructuredLogReport | StructuredLogEnd;
502+
```
503+
504+
With
505+
506+
```ts
507+
/**
508+
* Indicates the beginning of a new task that may receive updates with `StructuredLogReport` or `StructuredLogEnd`
509+
* payloads.
510+
*/
511+
export interface StructuredLogBegin {
512+
kind: 'begin'
513+
514+
/**
515+
* A succinct title that can be used to describe the task that started this structured.
516+
*/
517+
title: string;
518+
519+
/**
520+
* A unique identifier, identifying the task this structured log message belongs to.
521+
*/
522+
taskID: string;
523+
}
524+
525+
526+
/**
527+
* Adds a new log message to a structured log without ending it.
528+
*/
529+
export interface StructuredLogReport {
530+
/*
531+
* A unique identifier, identifying the task this structured log message belongs to.
532+
*/
533+
taskID: string;
534+
}
535+
536+
/**
537+
* Ends a structured log. No more `StructuredLogReport` updates should be sent for this task ID.
538+
*
539+
* The task ID may be re-used for new structured logs by beginning a new structured log for that task.
540+
*/
541+
export interface StructuredLogEnd {
542+
/*
543+
* A unique identifier, identifying the task this structured log message belongs to.
544+
*/
545+
taskID: string;
546+
}
493547
```
494548
495549
## `workspace/_pollIndex`

Documentation/Configuration File.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ The structure of the file is currently not guaranteed to be stable. Options may
5353
- `noLazy`: Prepare a target without generating object files but do not do lazy type checking and function body skipping. This uses SwiftPM's `--experimental-prepare-for-indexing-no-lazy` flag.
5454
- `enabled`: Prepare a target without generating object files.
5555
- `cancelTextDocumentRequestsOnEditAndClose: boolean`: Whether sending a `textDocument/didChange` or `textDocument/didClose` notification for a document should cancel all pending requests for that document.
56-
- `experimentalFeatures: ("on-type-formatting"|"set-options-request"|"sourcekit-options-request"|"is-indexing-request")[]`: Experimental features that are enabled.
56+
- `experimentalFeatures: ("on-type-formatting"|"set-options-request"|"sourcekit-options-request"|"is-indexing-request"|"structured-logs")[]`: Experimental features that are enabled.
5757
- `swiftPublishDiagnosticsDebounceDuration: number`: The time that `SwiftLanguageService` should wait after an edit before starting to compute diagnostics and sending a `PublishDiagnosticsNotification`.
5858
- `workDoneProgressDebounceDuration: number`: When a task is started that should be displayed to the client as a work done progress, how many milliseconds to wait before actually starting the work done progress. This prevents flickering of the work done progress in the client for short-lived index tasks which end within this duration.
5959
- `sourcekitdRequestTimeout: number`: The maximum duration that a sourcekitd request should be allowed to execute before being declared as timed out. In general, editors should cancel requests that they are no longer interested in, but in case editors don't cancel requests, this ensures that a long-running non-cancelled request is not blocking sourcekitd and thus most semantic functionality. In particular, VS Code does not cancel the semantic tokens request, which can cause a long-running AST build that blocks sourcekitd.

Sources/BuildServerProtocol/Messages/OnBuildLogMessageNotification.swift

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -18,22 +18,23 @@ import LanguageServerProtocol
1818

1919
/// The log message notification is sent from a server to a client to ask the client to log a particular message in its console.
2020
///
21-
/// A `build/logMessage`` notification is similar to LSP's `window/logMessage``, except for a few additions like id and originId.
21+
/// A `build/logMessage`` notification is similar to LSP's `window/logMessage``.
2222
public struct OnBuildLogMessageNotification: NotificationType {
2323
public static let method: String = "build/logMessage"
2424

2525
/// The message type.
2626
public var type: MessageType
2727

28-
/// The task id if any.
29-
public var task: TaskId?
30-
3128
/// The actual message.
3229
public var message: String
3330

34-
public init(type: MessageType, task: TaskId? = nil, message: String) {
31+
/// If specified, allows grouping log messages that belong to the same originating task together instead of logging
32+
/// them in chronological order in which they were produced.
33+
public var structure: StructuredLogKind?
34+
35+
public init(type: MessageType, message: String, structure: StructuredLogKind?) {
3536
self.type = type
36-
self.task = task
3737
self.message = message
38+
self.structure = structure
3839
}
3940
}

Sources/BuildSystemIntegration/BuildSystemManager.swift

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -631,15 +631,18 @@ package actor BuildSystemManager: QueueBasedMessageHandler {
631631
}
632632

633633
private func logMessage(notification: BuildServerProtocol.OnBuildLogMessageNotification) async {
634-
let message =
635-
if let taskID = notification.task?.id {
636-
prefixMessageWithTaskEmoji(taskID: taskID, message: notification.message)
637-
} else {
638-
notification.message
639-
}
640634
await connectionToClient.waitUntilInitialized()
641-
connectionToClient.send(
642-
LanguageServerProtocol.LogMessageNotification(type: .info, message: message, logName: "SourceKit-LSP: Indexing")
635+
let type: WindowMessageType =
636+
switch notification.type {
637+
case .error: .error
638+
case .warning: .warning
639+
case .info: .info
640+
case .log: .log
641+
}
642+
connectionToClient.logMessageToIndexLog(
643+
message: notification.message,
644+
type: type,
645+
structure: notification.structure
643646
)
644647
}
645648

Sources/BuildSystemIntegration/BuildSystemManagerDelegate.swift

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,4 +53,7 @@ package protocol BuildSystemManagerConnectionToClient: Sendable, Connection {
5353

5454
/// Start watching for file changes with the given glob patterns.
5555
func watchFiles(_ fileWatchers: [FileSystemWatcher]) async
56+
57+
/// Log a message in the client's index log.
58+
func logMessageToIndexLog(message: String, type: WindowMessageType, structure: StructuredLogKind?)
5659
}

Sources/BuildSystemIntegration/CMakeLists.txt

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,6 @@ add_library(BuildSystemIntegration STATIC
1818
JSONCompilationDatabaseBuildSystem.swift
1919
LegacyBuildServerBuildSystem.swift
2020
MainFilesProvider.swift
21-
PrefixMessageWithTaskEmoji.swift
2221
SplitShellCommand.swift
2322
SwiftPMBuildSystem.swift
2423
TestBuildSystem.swift)

Sources/BuildSystemIntegration/SwiftPMBuildSystem.swift

Lines changed: 51 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -188,9 +188,23 @@ package actor SwiftPMBuildSystem: BuiltInBuildSystem {
188188
self.testHooks = testHooks
189189
self.connectionToSourceKitLSP = connectionToSourceKitLSP
190190

191+
// Start an open-ended log for messages that we receive during package loading. We never end this log.
192+
let logTaskID = "swiftpm-log-\(UUID())"
193+
connectionToSourceKitLSP.send(
194+
OnBuildLogMessageNotification(
195+
type: .info,
196+
message: "",
197+
structure: .begin(StructuredLogBegin(title: "SwiftPM log for \(projectRoot.path)", taskID: logTaskID))
198+
)
199+
)
200+
191201
self.observabilitySystem = ObservabilitySystem({ scope, diagnostic in
192202
connectionToSourceKitLSP.send(
193-
OnBuildLogMessageNotification(type: .info, task: TaskId(id: "swiftpm-log"), message: diagnostic.description)
203+
OnBuildLogMessageNotification(
204+
type: .info,
205+
message: diagnostic.description,
206+
structure: .report(StructuredLogReport(taskID: logTaskID))
207+
)
194208
)
195209
logger.log(level: diagnostic.severity.asLogLevel, "SwiftPM log: \(diagnostic.description)")
196210
})
@@ -638,12 +652,6 @@ package actor SwiftPMBuildSystem: BuiltInBuildSystem {
638652
return VoidResponse()
639653
}
640654

641-
private nonisolated func logMessageToIndexLog(_ taskID: TaskId, _ message: String) {
642-
connectionToSourceKitLSP.send(
643-
BuildServerProtocol.OnBuildLogMessageNotification(type: .info, task: taskID, message: message)
644-
)
645-
}
646-
647655
private func prepare(singleTarget target: BuildTargetIdentifier) async throws {
648656
if target == .forPackageManifest {
649657
// Nothing to prepare for package manifests.
@@ -698,15 +706,36 @@ package actor SwiftPMBuildSystem: BuiltInBuildSystem {
698706
let start = ContinuousClock.now
699707

700708
let taskID: TaskId = TaskId(id: "preparation-\(preparationTaskID.fetchAndIncrement())")
701-
logMessageToIndexLog(
702-
taskID,
703-
"""
704-
Preparing \(self.swiftPMTargets[target]?.name ?? target.uri.stringValue)
705-
\(arguments.joined(separator: " "))
706-
"""
709+
connectionToSourceKitLSP.send(
710+
BuildServerProtocol.OnBuildLogMessageNotification(
711+
type: .info,
712+
message: "\(arguments.joined(separator: " "))",
713+
structure: .begin(
714+
StructuredLogBegin(
715+
title: "Preparing \(self.swiftPMTargets[target]?.name ?? target.uri.stringValue)",
716+
taskID: taskID.id
717+
)
718+
)
719+
)
707720
)
708-
let stdoutHandler = PipeAsStringHandler { self.logMessageToIndexLog(taskID, $0) }
709-
let stderrHandler = PipeAsStringHandler { self.logMessageToIndexLog(taskID, $0) }
721+
let stdoutHandler = PipeAsStringHandler { message in
722+
self.connectionToSourceKitLSP.send(
723+
BuildServerProtocol.OnBuildLogMessageNotification(
724+
type: .info,
725+
message: message,
726+
structure: .report(StructuredLogReport(taskID: taskID.id))
727+
)
728+
)
729+
}
730+
let stderrHandler = PipeAsStringHandler { message in
731+
self.connectionToSourceKitLSP.send(
732+
BuildServerProtocol.OnBuildLogMessageNotification(
733+
type: .info,
734+
message: message,
735+
structure: .report(StructuredLogReport(taskID: taskID.id))
736+
)
737+
)
738+
}
710739

711740
let result = try await Process.run(
712741
arguments: arguments,
@@ -717,7 +746,13 @@ package actor SwiftPMBuildSystem: BuiltInBuildSystem {
717746
)
718747
)
719748
let exitStatus = result.exitStatus.exhaustivelySwitchable
720-
logMessageToIndexLog(taskID, "Finished with \(exitStatus.description) in \(start.duration(to: .now))")
749+
self.connectionToSourceKitLSP.send(
750+
BuildServerProtocol.OnBuildLogMessageNotification(
751+
type: exitStatus.isSuccess ? .info : .error,
752+
message: "Finished with \(exitStatus.description) in \(start.duration(to: .now))",
753+
structure: .end(StructuredLogEnd(taskID: taskID.id))
754+
)
755+
)
721756
switch exitStatus {
722757
case .terminated(code: 0):
723758
break

0 commit comments

Comments
 (0)