Skip to content

Commit 1fe547f

Browse files
committed
Implement Android logging
1 parent ae3a692 commit 1fe547f

File tree

7 files changed

+179
-10
lines changed

7 files changed

+179
-10
lines changed

.github/workflows/ci.yml

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
name: swift-android-oslog ci
2+
on:
3+
push:
4+
branches: [ main ]
5+
workflow_dispatch:
6+
pull_request:
7+
branches:
8+
- '*'
9+
schedule:
10+
- cron: '45 2,13 * * *'
11+
jobs:
12+
test:
13+
# emulator fails to launch with: HVF error: HV_UNSUPPORTED
14+
#runs-on: macos-15
15+
runs-on: macos-13
16+
steps:
17+
- uses: actions/checkout@v4
18+
- name: "Test Swift Package Locally"
19+
run: swift test
20+
- name: "Test Swift Package on Android"
21+
uses: skiptools/swift-android-action@v1
22+
- name: "Test Swift Package on iOS"
23+
run: xcodebuild test -sdk "iphonesimulator" -destination "platform=iOS Simulator,name=iPhone 15" -scheme "$(xcodebuild -list -json | jq -r '.workspace.schemes[-1]')"
24+

Package.swift

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// swift-tools-version: 6.0
1+
// swift-tools-version: 5.9
22
import PackageDescription
33

44
let package = Package(
@@ -7,8 +7,8 @@ let package = Package(
77
.library(name: "AndroidOSLog", targets: ["AndroidOSLog"])
88
],
99
targets: [
10-
.target(name: "CAndroidLog"),
11-
.target(name: "AndroidOSLog", dependencies: ["CAndroidLog"]),
12-
.testTarget(name: "AndroidOSLogTests", dependencies: ["AndroidOSLog"])
10+
.systemLibrary(name: "CAndroidLog"),
11+
.target(name: "AndroidOSLog", dependencies: [.target(name: "CAndroidLog", condition: .when(platforms: [.android]))]),
12+
.testTarget(name: "AndroidOSLogTests", dependencies: [.target(name: "AndroidOSLog")])
1313
]
1414
)

Sources/AndroidOSLog/OSLog.swift

Lines changed: 115 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,115 @@
1-
// The Swift Programming Language
2-
// https://docs.swift.org/swift-book
1+
#if os(Android)
2+
import Android
3+
import CAndroidLog
4+
5+
public typealias OSLogMessage = String
6+
7+
public struct Logger : @unchecked Sendable {
8+
public let subsystem: String
9+
public let category: String
10+
11+
/// Creates a logger for logging to the default subsystem.
12+
public init() {
13+
self.subsystem = ""
14+
self.category = ""
15+
}
16+
17+
/// Creates a custom logger for logging to a specific subsystem and category.
18+
public init(subsystem: String, category: String) {
19+
self.subsystem = subsystem
20+
self.category = category
21+
}
22+
23+
public func log(_ message: OSLogMessage) {
24+
androidLog(priority: ANDROID_LOG_DEFAULT, message: message)
25+
}
26+
27+
public func log(level: OSLogType, _ message: OSLogMessage) {
28+
androidLog(priority: ANDROID_LOG_DEFAULT, message: message)
29+
}
30+
31+
public func trace(_ message: OSLogMessage) {
32+
androidLog(priority: ANDROID_LOG_VERBOSE, message: message)
33+
}
34+
35+
public func debug(_ message: OSLogMessage) {
36+
androidLog(priority: ANDROID_LOG_DEBUG, message: message)
37+
}
38+
39+
public func info(_ message: OSLogMessage) {
40+
androidLog(priority: ANDROID_LOG_INFO, message: message)
41+
}
42+
43+
public func notice(_ message: OSLogMessage) {
44+
androidLog(priority: ANDROID_LOG_INFO, message: message)
45+
}
46+
47+
public func warning(_ message: OSLogMessage) {
48+
androidLog(priority: ANDROID_LOG_WARN, message: message)
49+
}
50+
51+
public func error(_ message: OSLogMessage) {
52+
androidLog(priority: ANDROID_LOG_ERROR, message: message)
53+
}
54+
55+
public func critical(_ message: OSLogMessage) {
56+
androidLog(priority: ANDROID_LOG_ERROR, message: message)
57+
}
58+
59+
public func fault(_ message: OSLogMessage) {
60+
androidLog(priority: ANDROID_LOG_FATAL, message: message)
61+
}
62+
63+
public func log(type: OSLogType, message: OSLogMessage) {
64+
let priority: android_LogPriority
65+
switch type {
66+
case .info: priority = ANDROID_LOG_INFO
67+
case .debug: priority = ANDROID_LOG_DEBUG
68+
case .error: priority = ANDROID_LOG_ERROR
69+
case .fault: priority = ANDROID_LOG_FATAL
70+
default: priority = ANDROID_LOG_DEFAULT
71+
}
72+
73+
androidLog(priority: priority, message: message)
74+
}
75+
76+
private func androidLog(priority: android_LogPriority, message: OSLogMessage) {
77+
let tag = subsystem.isEmpty && category.isEmpty ? "" : (subsystem + "/" + category)
78+
tag.withCString { tagPtr in
79+
message.withCString { messagePtr in
80+
//swift_android_log(priority, tagPtr, messagePtr)
81+
__android_log_write(Int32(priority.rawValue), tagPtr, messagePtr)
82+
}
83+
}
84+
}
85+
}
86+
87+
//extension OSLog.Category {
88+
// public static let dynamicTracing: OSLog.Category
89+
// public static let dynamicStackTracing: OSLog.Category
90+
//}
91+
92+
public struct OSLogType : Equatable, RawRepresentable {
93+
public let rawValue: UInt8
94+
95+
public init(_ rawValue: UInt8) {
96+
self.rawValue = rawValue
97+
}
98+
99+
public init(rawValue: UInt8) {
100+
self.rawValue = rawValue
101+
}
102+
}
103+
104+
extension OSLogType {
105+
public static let `default`: OSLogType = OSLogType(0x00)
106+
public static let info: OSLogType = OSLogType(0x01)
107+
public static let debug: OSLogType = OSLogType(0x02)
108+
public static let error: OSLogType = OSLogType(0x10)
109+
public static let fault: OSLogType = OSLogType(0x11)
110+
}
111+
112+
#elseif canImport(OSLog)
113+
@_exported import OSLog
114+
#endif
115+

Sources/CAndroidLog/coslog.c

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
#if defined(__ANDROID__)
2+
#include "coslog.h"
3+
4+
// https://android.googlesource.com/platform/system/core/+/jb-dev/include/android/log.h
5+
void swift_android_log(android_LogPriority level, const char *tag, const char *msg) {
6+
__android_log_write(level, tag, msg);
7+
}
8+
#endif

Sources/CAndroidLog/include/coslog.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
#include <android/log.h>
2+
3+
void swift_android_log(android_LogPriority level, const char *tag, const char *msg);

Sources/CAndroidLog/module.modulemap

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
module CAndroidLog [system] {
2+
link "log"
3+
export *
4+
}

Tests/AndroidOSLogTests/AndroidOSLogTests.swift

Lines changed: 21 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,26 @@
11
import XCTest
2-
@testable import AndroidOSLog
2+
import AndroidOSLog // note: on non-android platforms, this will just export the system OSLog
33

4+
@available(iOS 14.0, *)
45
class AndroidOSLogTests : XCTestCase {
5-
public func testOSLog() {
6-
XCTAssertEqual(2, 1 + 1)
6+
public func testOSLogAPI() {
7+
let emptyLogger = Logger()
8+
emptyLogger.info("Android logger test: empty message")
9+
10+
let logger = Logger(subsystem: "AndroidOSLog", category: "test")
11+
12+
logger.log("Android logger test: LOG message")
13+
14+
logger.info("Android logger test: INFO message")
15+
logger.error("Android logger test: ERROR message")
16+
logger.debug("Android logger test: DEBUG message")
17+
logger.trace("Android logger test: TRACE message")
18+
logger.critical("Android logger test: CRITICAL message")
19+
20+
logger.log(level: OSLogType.default, "Android logger test: DEFAULT message")
21+
logger.log(level: OSLogType.info, "Android logger test: INFO message")
22+
logger.log(level: OSLogType.debug, "Android logger test: DEBUG message")
23+
logger.log(level: OSLogType.error, "Android logger test: ERROR message")
24+
logger.log(level: OSLogType.fault, "Android logger test: FAULT message")
725
}
826
}
9-

0 commit comments

Comments
 (0)