mirror of
https://github.com/apple/sourcekit-lsp.git
synced 2026-03-02 18:23:24 +01:00
OSLog is the suggesting logging solution on Apple platforms and we should be using it there, taking advantage of the different log levels and privacy masking. Switch sourcekit-lsp to use OSLog on Apple platforms and implement a logger that is API-compatible with OSLog for all uses in sourcekit-lsp and which can be used on non-Darwin platforms. The goal of this commit is to introduce the new logging API. There are still improvements about what we log and we can display more privacy-insensitive information after masking. Those changes will be in follow-up commits.
138 lines
4.0 KiB
Swift
138 lines
4.0 KiB
Swift
//===----------------------------------------------------------------------===//
|
|
//
|
|
// This source file is part of the Swift.org open source project
|
|
//
|
|
// Copyright (c) 2014 - 2020 Apple Inc. and the Swift project authors
|
|
// Licensed under Apache License v2.0 with Runtime Library Exception
|
|
//
|
|
// See https://swift.org/LICENSE.txt for license information
|
|
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
@_exported import Csourcekitd
|
|
import Dispatch
|
|
import Foundation
|
|
import LSPLogging
|
|
import SKSupport
|
|
|
|
/// Access to sourcekitd API, taking care of initialization, shutdown, and notification handler
|
|
/// multiplexing.
|
|
///
|
|
/// *Users* of this protocol should not call the api functions `initialize`, `shutdown`, or
|
|
/// `set_notification_handler`, which are global state managed internally by this class.
|
|
///
|
|
/// *Implementors* are expected to handle initialization and shutdown, e.g. during `init` and
|
|
/// `deinit` or by wrapping an existing sourcekitd session that outlives this object.
|
|
public protocol SourceKitD: AnyObject {
|
|
/// The sourcekitd API functions.
|
|
var api: sourcekitd_functions_t { get }
|
|
|
|
/// Convenience for accessing known keys.
|
|
var keys: sourcekitd_keys { get }
|
|
|
|
/// Convenience for accessing known keys.
|
|
var requests: sourcekitd_requests { get }
|
|
|
|
/// Convenience for accessing known keys.
|
|
var values: sourcekitd_values { get }
|
|
|
|
/// Adds a new notification handler, which will be weakly referenced.
|
|
func addNotificationHandler(_ handler: SKDNotificationHandler)
|
|
|
|
/// Removes a previously registered notification handler.
|
|
func removeNotificationHandler(_ handler: SKDNotificationHandler)
|
|
}
|
|
|
|
public enum SKDError: Error, Equatable {
|
|
/// The service has crashed.
|
|
case connectionInterrupted
|
|
|
|
/// The request was unknown or had an invalid or missing parameter.
|
|
case requestInvalid(String)
|
|
|
|
/// The request failed.
|
|
case requestFailed(String)
|
|
|
|
/// The request was cancelled.
|
|
case requestCancelled
|
|
|
|
/// Loading a required symbol from the sourcekitd library failed.
|
|
case missingRequiredSymbol(String)
|
|
}
|
|
|
|
extension SourceKitD {
|
|
|
|
// MARK: - Convenience API for requests.
|
|
|
|
/// Send the given request and synchronously receive a reply dictionary (or error).
|
|
public func sendSync(_ req: SKDRequestDictionary) throws -> SKDResponseDictionary {
|
|
logRequest(req)
|
|
|
|
let resp = SKDResponse(api.send_request_sync(req.dict), sourcekitd: self)
|
|
|
|
logResponse(resp)
|
|
|
|
guard let dict = resp.value else {
|
|
throw resp.error!
|
|
}
|
|
|
|
return dict
|
|
}
|
|
|
|
public func send(_ req: SKDRequestDictionary) async throws -> SKDResponseDictionary {
|
|
logRequest(req)
|
|
|
|
return try await withCheckedThrowingContinuation { continuation in
|
|
var handle: sourcekitd_request_handle_t? = nil
|
|
|
|
api.send_request(req.dict, &handle) { [weak self] _resp in
|
|
guard let self = self else { return }
|
|
|
|
let resp = SKDResponse(_resp, sourcekitd: self)
|
|
|
|
logResponse(resp)
|
|
|
|
guard let dict = resp.value else {
|
|
continuation.resume(throwing: resp.error!)
|
|
return
|
|
}
|
|
|
|
continuation.resume(returning: dict)
|
|
}
|
|
|
|
// FIXME: (async) Cancellation
|
|
}
|
|
}
|
|
|
|
public func cancel(_ handle: sourcekitd_request_handle_t) {
|
|
api.cancel_request(handle)
|
|
}
|
|
}
|
|
|
|
private func logRequest(_ request: SKDRequestDictionary) {
|
|
// FIXME: Ideally we could log the request key here at the info level but the dictionary is
|
|
// readonly.
|
|
logger.log(
|
|
"""
|
|
Sending sourcekitd request:
|
|
\(request.forLogging)
|
|
"""
|
|
)
|
|
}
|
|
|
|
private func logResponse(_ response: SKDResponse) {
|
|
logger.log(
|
|
level: (response.error == nil || response.error == .requestCancelled) ? .debug : .error,
|
|
"""
|
|
Received sourcekitd response:
|
|
\(response.forLogging)
|
|
"""
|
|
)
|
|
}
|
|
|
|
/// A sourcekitd notification handler in a class to allow it to be uniquely referenced.
|
|
public protocol SKDNotificationHandler: AnyObject {
|
|
func notification(_: SKDResponse) -> Void
|
|
}
|