Skip to content

Commit b997c71

Browse files
committed
Change to runtime framework linking
1 parent 83245c5 commit b997c71

7 files changed

Lines changed: 68 additions & 89 deletions

File tree

Package.swift

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -25,10 +25,12 @@ let package = Package(
2525

2626
.target(
2727
name: "ExternalAppLoggerHeaders",
28-
path: "Sources/Headers",
29-
linkerSettings: [
30-
.linkedFramework("LoggingSupport")
31-
]
28+
path: "Sources/Headers"
29+
),
30+
31+
.testTarget(
32+
name: "LogStreamTests",
33+
dependencies: ["LogStream"]
3234
)
3335
]
3436
)

README.md

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,9 @@ This package provides some utilities to capture logs of other processes. This ca
44

55
The logs captured are the same ones visible in Console.app.
66

7+
> [!IMPORTANT]
8+
> This package makes use of the private framework LoggingSupport. While unlikely, OS updates might change the internals of the framework and break this package.
9+
710
## Usage
811

912
Capturing logs for one process:
@@ -29,5 +32,3 @@ To capture logs of all processes, use the following initializer:
2932
```swift
3033
public static func logs(flags: ActivityStreamOptions) -> AsyncStream<LogMessage>
3134
```
32-
33-
> Note: To link against the private framework LoggingSupport, which is used by this package, you may be required to set an extra search path. You can do this by adding the following to `System Framework Search Paths` in your projects build settings: `$(DEVELOPER_SDK_DIR)/MacOSX.sdk/System/Library/PrivateFrameworks`

Sources/Headers/include/OSActivityEvent.h

Lines changed: 0 additions & 56 deletions
This file was deleted.

Sources/LogStream/ActivityStreamOptions.swift

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,4 +26,6 @@ public extension ActivityStreamOptions {
2626
static let info = ActivityStreamOptions(rawValue: 1 << 8)
2727
static let promiscuous = ActivityStreamOptions(rawValue: 1 << 9)
2828
static let preciseTimestamps = ActivityStreamOptions(rawValue: 1 << 9)
29+
30+
static let `default`: ActivityStreamOptions = [.historical, .processOnly]
2931
}

Sources/LogStream/LogMessage.swift

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ extension LogMessage: Hashable {
4545
}
4646

4747
extension LogMessage {
48-
init(_ event: OSActivityLogMessageEvent) {
48+
init(_ event: _OSActivityLogMessageEvent) {
4949
self.init(
5050
message: event.eventMessage,
5151
date: event.timestamp,
@@ -57,3 +57,16 @@ extension LogMessage {
5757
)
5858
}
5959
}
60+
61+
@objc
62+
protocol _OSActivityLogMessageEvent {
63+
var eventMessage: String { get set }
64+
var timestamp: Date { get set }
65+
var subsystem: String? { get set }
66+
var category: String? { get set }
67+
var messageType: UInt8 { get set }
68+
var process: String { get set }
69+
var processID: pid_t { get set }
70+
71+
init(entry: UnsafeMutablePointer<os_activity_stream_entry_s>)
72+
}

Sources/LogStream/LogStream.swift

Lines changed: 14 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
//
77

88
import ExternalAppLoggerHeaders
9+
import Foundation
910

1011
public enum LogStream {
1112

@@ -28,17 +29,17 @@ public enum LogStream {
2829
/// }
2930
/// }
3031
/// ```
31-
public static func logs(for processID: pid_t, flags: ActivityStreamOptions = [.historical, .processOnly]) -> AsyncStream<LogMessage> {
32+
public static func logs(for processID: pid_t, flags: ActivityStreamOptions = .default) -> AsyncStream<LogMessage> {
3233

3334
let (stream, continuation) = AsyncStream.makeStream(of: LogMessage.self)
3435

3536
let logstream = createStream(pid: processID, flags: flags, continuation: continuation)
3637

3738
continuation.onTermination = { _ in
38-
cancelLog(stream: logstream)
39+
LoggingSupport.cancelLog(logstream)
3940
}
4041

41-
resumeLog(stream: logstream)
42+
LoggingSupport.resumeLog(logstream)
4243

4344
return stream
4445
}
@@ -60,18 +61,18 @@ public enum LogStream {
6061
/// }
6162
/// }
6263
/// ```
63-
public static func logs(for processIDs: [pid_t], flags: ActivityStreamOptions = [.historical, .processOnly]) -> AsyncStream<LogMessage> {
64+
public static func logs(for processIDs: [pid_t], flags: ActivityStreamOptions = .default) -> AsyncStream<LogMessage> {
6465
let (stream, continuation) = AsyncStream.makeStream(of: LogMessage.self)
6566

6667
let logstreams = processIDs.map {
6768
createStream(pid: $0, flags: flags, continuation: continuation)
6869
}
6970

7071
continuation.onTermination = { _ in
71-
logstreams.forEach(cancelLog)
72+
logstreams.forEach(LoggingSupport.cancelLog)
7273
}
7374

74-
logstreams.forEach(resumeLog)
75+
logstreams.forEach(LoggingSupport.resumeLog)
7576

7677
return stream
7778
}
@@ -91,43 +92,30 @@ public enum LogStream {
9192
/// }
9293
/// }
9394
/// ```
94-
public static func logs(flags: ActivityStreamOptions = [.historical, .processOnly]) -> AsyncStream<LogMessage> {
95+
public static func logs(flags: ActivityStreamOptions = .default) -> AsyncStream<LogMessage> {
9596
LogStream.logs(for: -1, flags: flags)
9697
}
9798

99+
static let messageClass = unsafeBitCast(NSClassFromString("OSActivityLogMessageEvent"), to: _OSActivityLogMessageEvent.Type.self)
100+
98101
static func createStream(
99102
pid: pid_t,
100103
flags: ActivityStreamOptions,
101104
continuation: AsyncStream<LogMessage>.Continuation
102-
) -> ActivityStream? {
103-
streamLog(pid: pid, flags: flags.rawValue) { entryPointer, error in
105+
) -> LoggingSupport.ActivityStream? {
106+
LoggingSupport.streamLog(pid, flags.rawValue) { entryPointer, error in
104107
guard error == 0, let entryPointer else { return false }
105108

106109
let entry = entryPointer.pointee
107110

108-
guard
111+
guard
109112
entry.type == OS_ACTIVITY_STREAM_TYPE_LOG_MESSAGE || entry.type == OS_ACTIVITY_STREAM_TYPE_LEGACY_LOG_MESSAGE
110113
else { return true }
111114

112-
let event = OSActivityLogMessageEvent(entry: entryPointer)
115+
let event = messageClass.init(entry: entryPointer)
113116

114117
continuation.yield(LogMessage(event))
115118
return true
116119
}
117120
}
118121
}
119-
120-
typealias ActivityStream = os_activity_stream_t
121-
122-
@_silgen_name("os_activity_stream_for_pid")
123-
fileprivate func streamLog(
124-
pid: pid_t,
125-
flags: ActivityStreamOptions.RawValue,
126-
stream_block: (@convention(block) (os_activity_stream_entry_t?, Int32) -> Bool)?
127-
) -> ActivityStream?
128-
129-
@_silgen_name("os_activity_stream_resume")
130-
fileprivate func resumeLog(stream: ActivityStream?)
131-
132-
@_silgen_name("os_activity_stream_cancel")
133-
fileprivate func cancelLog(stream: ActivityStream?)
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
//
2+
// LoggingSupport.swift
3+
//
4+
//
5+
// Created by Wouter on 26/4/24.
6+
//
7+
8+
import Foundation
9+
import ExternalAppLoggerHeaders
10+
11+
enum LoggingSupport {
12+
nonisolated(unsafe) static let handle = dlopen("/System/Library/PrivateFrameworks/LoggingSupport.framework/LoggingSupport", RTLD_LAZY),
13+
streamLog = unsafeBitCast(dlsym(handle, "os_activity_stream_for_pid"), to: StreamLog.self),
14+
resumeLog = unsafeBitCast(dlsym(handle, "os_activity_stream_resume"), to: ResumeLog.self),
15+
cancelLog = unsafeBitCast(dlsym(handle, "os_activity_stream_cancel"), to: CancelLog.self)
16+
17+
typealias StreamLog = @convention(c) (
18+
pid_t,
19+
ActivityStreamOptions.RawValue,
20+
(@convention(block) (os_activity_stream_entry_t?, Int32) -> Bool)?
21+
) -> ActivityStream?
22+
23+
typealias ResumeLog = @convention(c) (ActivityStream?) -> Void
24+
25+
typealias CancelLog = ResumeLog
26+
27+
typealias ActivityStream = OpaquePointer
28+
29+
}

0 commit comments

Comments
 (0)