Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@ let package = Package(
.package(url: "https://github.com/vapor/jwt-kit.git", from: "4.13.5"),
// Only used for DocC generation
.package(url: "https://github.com/apple/swift-docc-plugin.git", from: "1.3.0"),
// Local uniffi bindings
.package(path: "../rust-sdks/livekit-uniffi"),
],
targets: [
.target(
Expand All @@ -39,6 +41,7 @@ let package = Package(
.product(name: "DequeModule", package: "swift-collections"),
.product(name: "OrderedCollections", package: "swift-collections"),
.product(name: "JWTKit", package: "jwt-kit"),
.product(name: "LiveKitFFI", package: "livekit-uniffi"),
"LKObjCHelpers",
],
exclude: [
Expand Down
3 changes: 3 additions & 0 deletions [email protected]
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,8 @@ let package = Package(
.package(url: "https://github.com/vapor/jwt-kit.git", from: "4.13.5"),
// Only used for DocC generation
.package(url: "https://github.com/apple/swift-docc-plugin.git", from: "1.3.0"),
// Local uniffi bindings
.package(path: "../rust-sdks/livekit-uniffi"),
],
targets: [
.target(
Expand All @@ -40,6 +42,7 @@ let package = Package(
.product(name: "DequeModule", package: "swift-collections"),
.product(name: "OrderedCollections", package: "swift-collections"),
.product(name: "JWTKit", package: "jwt-kit"),
.product(name: "LiveKitFFI", package: "livekit-uniffi"),
"LKObjCHelpers",
],
exclude: [
Expand Down
73 changes: 65 additions & 8 deletions Sources/LiveKit/Support/Logger.swift
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@

import OSLog
internal import LiveKitWebRTC
internal import LiveKitFFI

// MARK: - Logger

Expand Down Expand Up @@ -69,30 +70,36 @@ public struct DisabledLogger: Logger {
/// A logger that logs to OSLog
/// - Parameter minLevel: The minimum level to log
/// - Parameter rtc: Whether to log WebRTC output
/// - Parameter ffi: Whether to log Rust FFI output
open class OSLogger: Logger, @unchecked Sendable {
private static let subsystem = "io.livekit.sdk"

private let queue = DispatchQueue(label: "io.livekit.oslogger", qos: .utility)
private static let queue = DispatchQueue(label: "io.livekit.oslogger", qos: .utility)

private nonisolated(unsafe) static var ffiBootstrapped = false

private var logs: [String: OSLog] = [:]

private lazy var rtcLogger = LKRTCCallbackLogger()
private var ffiLogForwardTask: Task<Void, Never>?

private let minLevel: LogLevel

public init(minLevel: LogLevel = .info, rtc: Bool = false) {
public init(minLevel: LogLevel = .info, rtc: Bool = false, ffi: Bool = true) {
self.minLevel = minLevel

guard rtc else { return }
if rtc {
startRTCLogForwarding(minLevel: minLevel)
}

let rtcLog = OSLog(subsystem: Self.subsystem, category: "WebRTC")
rtcLogger.severity = minLevel.rtcSeverity
rtcLogger.start { message, severity in
os_log("%{public}@", log: rtcLog, type: severity.osLogType, message)
if ffi {
startFFILogForwarding(minLevel: minLevel)
}
}

deinit {
rtcLogger.stop()
ffiLogForwardTask?.cancel()
}

public func log(
Expand All @@ -116,7 +123,7 @@ open class OSLogger: Logger, @unchecked Sendable {

let metadata = buildScopedMetadataString()

queue.async {
Self.queue.async {
func getOSLog(for type: Any.Type) -> OSLog {
let typeName = String(describing: type)

Expand All @@ -132,6 +139,36 @@ open class OSLogger: Logger, @unchecked Sendable {
os_log("%{public}@", log: getOSLog(for: type), type: level.osLogType, "\(type).\(function) \(message)\(metadata)")
}
}

private func startRTCLogForwarding(minLevel: LogLevel) {
let rtcLog = OSLog(subsystem: Self.subsystem, category: "WebRTC")

rtcLogger.severity = minLevel.rtcSeverity
rtcLogger.start { message, severity in
os_log("%{public}@", log: rtcLog, type: severity.osLogType, message)
}
}

private func startFFILogForwarding(minLevel: LogLevel) {
Self.queue.sync {
if !Self.ffiBootstrapped {
logForwardBootstrap(level: minLevel.logForwardFilter)
Self.ffiBootstrapped = true
}
}

let ffiLog = OSLog(subsystem: Self.subsystem, category: "FFI")

ffiLogForwardTask = Task(priority: .utility) {
for await entry in AsyncStream(unfolding: logForwardReceive) {
guard !Task.isCancelled else { break }

let message = "\(entry.target) \(entry.message)"

os_log("%{public}@", log: ffiLog, type: entry.level.osLogType, message)
}
}
}
}

// MARK: - Loggable
Expand Down Expand Up @@ -196,6 +233,15 @@ public enum LogLevel: Int, Sendable, Comparable {
}
}

var logForwardFilter: LogForwardFilter {
switch self {
case .debug: .debug
case .info: .info
case .warning: .warn
case .error: .error
}
}

@inlinable
public static func < (lhs: LogLevel, rhs: LogLevel) -> Bool {
lhs.rawValue < rhs.rawValue
Expand All @@ -214,3 +260,14 @@ extension LKRTCLoggingSeverity {
}
}
}

extension LogForwardLevel {
var osLogType: OSLogType {
switch self {
case .error: .error
case .warn: .default
case .info: .info
case .debug, .trace: .debug
}
}
}
51 changes: 34 additions & 17 deletions Tests/LiveKitTestSupport/TokenGenerator.swift
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,8 @@
* limitations under the License.
*/

import JWTKit
@testable import LiveKit
import LiveKitFFI

public class TokenGenerator {
// 30 mins
Expand All @@ -31,10 +31,6 @@ public class TokenGenerator {
public var metadata: String?
public var videoGrant: LiveKitJWTPayload.VideoGrant?

// MARK: - Private

private let signers = JWTSigners()

public init(apiKey: String,
apiSecret: String,
identity: String,
Expand All @@ -47,19 +43,40 @@ public class TokenGenerator {
}

public func sign() throws -> String {
// Add HMAC with SHA-256 signer.
signers.use(.hs256(key: apiSecret))

let n = Date().timeIntervalSince1970
var ffiVideoGrants: VideoGrants?
if let grant = videoGrant {
ffiVideoGrants = VideoGrants(
roomCreate: grant.roomCreate ?? false,
roomList: grant.roomList ?? false,
roomRecord: grant.roomRecord ?? false,
roomAdmin: grant.roomAdmin ?? false,
roomJoin: grant.roomJoin ?? false,
room: grant.room ?? "",
destinationRoom: "",
canPublish: grant.canPublish ?? false,
canSubscribe: grant.canSubscribe ?? false,
canPublishData: grant.canPublishData ?? false,
canPublishSources: grant.canPublishSources ?? [],
canUpdateOwnMetadata: false,
ingressAdmin: false,
hidden: grant.hidden ?? false,
recorder: grant.recorder ?? false
)
}

let p = LiveKitJWTPayload(exp: .init(value: Date(timeIntervalSince1970: floor(n + ttl))),
iss: .init(stringLiteral: apiKey),
nbf: .init(value: Date(timeIntervalSince1970: floor(n))),
sub: .init(stringLiteral: identity),
name: name,
metadata: metadata,
video: videoGrant)
let credentials = ApiCredentials(key: apiKey, secret: apiSecret)
let options = TokenOptions(
ttl: ttl,
videoGrants: ffiVideoGrants,
sipGrants: nil,
identity: identity,
name: name,
metadata: metadata,
attributes: nil,
sha256: nil,
roomName: videoGrant?.room
)

return try signers.sign(p)
return try generateToken(options: options, credentials: credentials)
}
}
Loading