From dc2586ba95f1a05f84de6c1565d934b04f32c4a6 Mon Sep 17 00:00:00 2001
From: stevensJourney
Date: Mon, 7 Apr 2025 18:23:44 +0200
Subject: [PATCH 01/12] add logging
---
CHANGELOG.md | 21 +++
Sources/PowerSync/Kotlin/DatabaseLogger.swift | 135 ++++++++++++++++++
.../Kotlin/KotlinPowerSyncDatabaseImpl.swift | 10 +-
Sources/PowerSync/Logger.swift | 52 +++++++
Sources/PowerSync/LoggerProtocol.swift | 82 +++++++++++
Sources/PowerSync/PowerSyncDatabase.swift | 7 +-
.../PowerSync/PowerSyncDatabaseProtocol.swift | 6 +
.../KotlinPowerSyncDatabaseImplTests.swift | 45 +++++-
Tests/PowerSyncTests/Kotlin/TestLogger.swift | 23 +++
9 files changed, 376 insertions(+), 5 deletions(-)
create mode 100644 Sources/PowerSync/Kotlin/DatabaseLogger.swift
create mode 100644 Sources/PowerSync/Logger.swift
create mode 100644 Sources/PowerSync/LoggerProtocol.swift
create mode 100644 Tests/PowerSyncTests/Kotlin/TestLogger.swift
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 4523c23..b075199 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,26 @@
# Changelog
+# 1.0.0-Beta.10 (unreleased)
+
+* Added the ability to specify a custom logging implementation
+```swift
+ let db = PowerSyncDatabase(
+ schema: Schema(
+ tables: [
+ Table(
+ name: "users",
+ columns: [
+ .text("name"),
+ .text("email")
+ ]
+ )
+ ]
+ ),
+ logger: DefaultLogger(minSeverity: .debug)
+)
+```
+* added `.close()` method on `PowerSyncDatabaseProtocol`
+
## 1.0.0-Beta.9
* Update PowerSync SQLite core extension to 0.3.12.
diff --git a/Sources/PowerSync/Kotlin/DatabaseLogger.swift b/Sources/PowerSync/Kotlin/DatabaseLogger.swift
new file mode 100644
index 0000000..fe4e3ab
--- /dev/null
+++ b/Sources/PowerSync/Kotlin/DatabaseLogger.swift
@@ -0,0 +1,135 @@
+import PowerSyncKotlin
+
+/// Maps a Kermit `Severity` level to a local `LogSeverity`.
+///
+/// - Parameter severity: The Kermit log severity value from Kotlin.
+/// - Returns: The corresponding `LogSeverity` used in Swift.
+private func mapKermitSeverity(_ severity: PowerSyncKotlin.Kermit_coreSeverity) -> LogSeverity {
+ switch severity {
+ case PowerSyncKotlin.Kermit_coreSeverity.verbose:
+ return LogSeverity.debug
+ case PowerSyncKotlin.Kermit_coreSeverity.debug:
+ return LogSeverity.debug
+ case PowerSyncKotlin.Kermit_coreSeverity.info:
+ return LogSeverity.info
+ case PowerSyncKotlin.Kermit_coreSeverity.warn:
+ return LogSeverity.warning
+ case PowerSyncKotlin.Kermit_coreSeverity.error:
+ return LogSeverity.error
+ case PowerSyncKotlin.Kermit_coreSeverity.assert:
+ return LogSeverity.fault
+ }
+}
+
+/// Maps a local `LogSeverity` to a Kermit-compatible `Kermit_coreSeverity`.
+///
+/// - Parameter severity: The Swift-side `LogSeverity`.
+/// - Returns: The equivalent Kermit log severity.
+private func mapSeverity(_ severity: LogSeverity) -> PowerSyncKotlin.Kermit_coreSeverity {
+ switch severity {
+ case .debug:
+ return PowerSyncKotlin.Kermit_coreSeverity.debug
+ case .info:
+ return PowerSyncKotlin.Kermit_coreSeverity.info
+ case .warning:
+ return PowerSyncKotlin.Kermit_coreSeverity.warn
+ case .error:
+ return PowerSyncKotlin.Kermit_coreSeverity.error
+ case .fault:
+ return PowerSyncKotlin.Kermit_coreSeverity.assert
+ }
+}
+
+/// Adapts a Swift `LogWritterProtocol` to Kermit's `LogWriter` interface.
+///
+/// This allows Kotlin logging (via Kermit) to call into the Swift logging implementation.
+private class KermitLogWriterAdapter: Kermit_coreLogWriter {
+ /// The underlying Swift log writer to forward log messages to.
+ var adapter: LogWriterProtocol
+
+ /// Initializes a new adapter.
+ ///
+ /// - Parameter adapter: A Swift log writer that will handle log output.
+ init(adapter: LogWriterProtocol) {
+ self.adapter = adapter
+ super.init()
+ }
+
+ /// Called by Kermit to log a message.
+ ///
+ /// - Parameters:
+ /// - severity: The severity level of the log.
+ /// - message: The content of the log message.
+ /// - tag: An optional string categorizing the log.
+ /// - throwable: An optional Kotlin exception (ignored here).
+ override func log(severity: Kermit_coreSeverity, message: String, tag: String, throwable: KotlinThrowable?) {
+ adapter.log(
+ severity: mapKermitSeverity(severity),
+ message: message,
+ tag: tag
+ )
+ }
+}
+
+/// A logger implementation that integrates with PowerSync's Kotlin backend using Kermit.
+///
+/// This class bridges Swift log writers with the Kotlin logging system and supports
+/// runtime configuration of severity levels and writer lists.
+public class DatabaseLogger: LoggerProtocol {
+ /// The underlying Kermit logger instance provided by the PowerSyncKotlin SDK.
+ internal let kLogger = PowerSyncKotlin.generateLogger(logger: nil)
+
+ /// Initializes a new logger with an optional list of writers.
+ ///
+ /// - Parameter writers: An array of Swift log writers. Defaults to an empty array.
+ init(writers: [any LogWriterProtocol] = []) {
+ setWriters(writers)
+ }
+
+ /// Sets the minimum severity level that will be logged.
+ ///
+ /// Messages below this level will be ignored.
+ ///
+ /// - Parameter severity: The minimum `LogSeverity` to allow through.
+ public func setMinSeverity(_ severity: LogSeverity) {
+ kLogger.mutableConfig.setMinSeverity(
+ mapSeverity(severity)
+ )
+ }
+
+ /// Sets the list of log writers that will receive log messages.
+ ///
+ /// This updates both the internal writer list and the Kermit logger's configuration.
+ ///
+ /// - Parameter writers: An array of Swift `LogWritterProtocol` implementations.
+ public func setWriters(_ writers: [any LogWriterProtocol]) {
+ kLogger.mutableConfig.setLogWriterList(
+ writers.map { item in KermitLogWriterAdapter(adapter: item) }
+ )
+ }
+
+ /// Logs a debug-level message.
+ public func debug(_ message: String, tag: String) {
+ kLogger.d(messageString: message, throwable: nil, tag: tag)
+ }
+
+ /// Logs an info-level message.
+ public func info(_ message: String, tag: String) {
+ kLogger.i(messageString: message, throwable: nil, tag: tag)
+ }
+
+ /// Logs a warning-level message.
+ public func warning(_ message: String, tag: String) {
+ kLogger.w(messageString: message, throwable: nil, tag: tag)
+ }
+
+ /// Logs an error-level message.
+ public func error(_ message: String, tag: String) {
+ kLogger.e(messageString: message, throwable: nil, tag: tag)
+ }
+
+ /// Logs a fault (assert-level) message, typically used for critical issues.
+ public func fault(_ message: String, tag: String) {
+ kLogger.a(messageString: message, throwable: nil, tag: tag)
+ }
+}
diff --git a/Sources/PowerSync/Kotlin/KotlinPowerSyncDatabaseImpl.swift b/Sources/PowerSync/Kotlin/KotlinPowerSyncDatabaseImpl.swift
index efd3b5e..ea78b55 100644
--- a/Sources/PowerSync/Kotlin/KotlinPowerSyncDatabaseImpl.swift
+++ b/Sources/PowerSync/Kotlin/KotlinPowerSyncDatabaseImpl.swift
@@ -8,13 +8,15 @@ final class KotlinPowerSyncDatabaseImpl: PowerSyncDatabaseProtocol {
init(
schema: Schema,
- dbFilename: String
+ dbFilename: String,
+ logger: DatabaseLogger? = nil
) {
let factory = PowerSyncKotlin.DatabaseDriverFactory()
kotlinDatabase = PowerSyncDatabase(
factory: factory,
schema: KotlinAdapter.Schema.toKotlin(schema),
- dbFilename: dbFilename
+ dbFilename: dbFilename,
+ logger: logger?.kLogger
)
}
@@ -232,4 +234,8 @@ final class KotlinPowerSyncDatabaseImpl: PowerSyncDatabaseProtocol {
func readTransaction(callback: @escaping (any PowerSyncTransaction) throws -> R) async throws -> R {
return try safeCast(await kotlinDatabase.readTransaction(callback: TransactionCallback(callback: callback)), to: R.self)
}
+
+ func close() async throws{
+ try await kotlinDatabase.close()
+ }
}
diff --git a/Sources/PowerSync/Logger.swift b/Sources/PowerSync/Logger.swift
new file mode 100644
index 0000000..8c6351e
--- /dev/null
+++ b/Sources/PowerSync/Logger.swift
@@ -0,0 +1,52 @@
+import OSLog
+
+/// A log writer that bridges custom `LogSeverity` levels to Apple's unified `Logger` framework.
+///
+/// This writer uses `os.Logger` on iOS 14+ and falls back to `print` for earlier versions.
+/// Tags are optionally prefixed to messages in square brackets.
+public class SwiftLogWriter: LogWriterProtocol {
+
+ /// Logs a message with a given severity and optional tag.
+ ///
+ /// - Parameters:
+ /// - severity: The severity level of the message.
+ /// - message: The content of the log message.
+ /// - tag: An optional tag used to categorize the message. If empty, no brackets are shown.
+ public func log(severity: LogSeverity, message: String, tag: String) {
+ let tagPrefix = tag.isEmpty ? "" : "[\(tag)] "
+ let message = "\(tagPrefix) \(message)"
+ if #available(iOS 14.0, *) {
+ let l = Logger()
+
+ switch severity {
+ case .info:
+ l.info("\(message)")
+ case .error:
+ l.error("\(message)")
+ case .debug:
+ l.debug("\(message)")
+ case .warning:
+ l.warning("\(message)")
+ case .fault:
+ l.fault("\(message)")
+ }
+ } else {
+ print("\(severity.rawValue): \(message)")
+ }
+ }
+}
+
+/// A default logger configuration that uses `SwiftLogWritter` and filters messages by minimum severity.
+///
+/// This logger integrates with your custom logging system and uses `os.Logger` under the hood.
+public class DefaultLogger: DatabaseLogger {
+
+ /// Initializes the default logger with an optional minimum severity level.
+ ///
+ /// - Parameter minSeverity: The minimum severity level to log. Defaults to `.debug`.
+ public init(minSeverity: LogSeverity = .debug) {
+ super.init()
+ setMinSeverity(minSeverity)
+ setWriters([SwiftLogWriter()])
+ }
+}
diff --git a/Sources/PowerSync/LoggerProtocol.swift b/Sources/PowerSync/LoggerProtocol.swift
new file mode 100644
index 0000000..620b3c1
--- /dev/null
+++ b/Sources/PowerSync/LoggerProtocol.swift
@@ -0,0 +1,82 @@
+/// Represents the severity level of a log message.
+public enum LogSeverity: String, CaseIterable {
+ /// Informational messages that highlight the progress of the application.
+ case info = "INFO"
+
+ /// Error events that might still allow the application to continue running.
+ case error = "ERROR"
+
+ /// Detailed information typically used for debugging.
+ case debug = "DEBUG"
+
+ /// Potentially harmful situations that are not necessarily errors.
+ case warning = "WARNING"
+
+ /// Serious errors indicating critical failures, often unrecoverable.
+ case fault = "FAULT"
+}
+
+/// A protocol for writing log messages to a specific backend or output.
+///
+/// Conformers handle the actual writing or forwarding of log messages.
+public protocol LogWriterProtocol {
+ /// Logs a message with the given severity and optional tag.
+ ///
+ /// - Parameters:
+ /// - severity: The severity level of the log message.
+ /// - message: The content of the log message.
+ /// - tag: An optional tag to categorize or group the log message.
+ func log(severity: LogSeverity, message: String, tag: String)
+}
+
+/// A protocol defining the interface for a logger that supports severity filtering and multiple writers.
+///
+/// Conformers provide logging APIs and manage attached log writers.
+public protocol LoggerProtocol {
+ /// Sets the minimum severity level to be logged.
+ ///
+ /// Log messages below this severity will be ignored.
+ ///
+ /// - Parameter severity: The minimum severity level to log.
+ func setMinSeverity(_ severity: LogSeverity)
+
+ /// Sets the list of log writers that will handle log output.
+ ///
+ /// - Parameter writters: An array of `LogWritterProtocol` conformers.
+ func setWriters(_ writters: [LogWriterProtocol])
+
+ /// Logs an informational message.
+ ///
+ /// - Parameters:
+ /// - message: The content of the log message.
+ /// - tag: An optional tag to categorize the message.
+ func info(_ message: String, tag: String)
+
+ /// Logs an error message.
+ ///
+ /// - Parameters:
+ /// - message: The content of the log message.
+ /// - tag: An optional tag to categorize the message.
+ func error(_ message: String, tag: String)
+
+ /// Logs a debug message.
+ ///
+ /// - Parameters:
+ /// - message: The content of the log message.
+ /// - tag: An optional tag to categorize the message.
+ func debug(_ message: String, tag: String)
+
+ /// Logs a warning message.
+ ///
+ /// - Parameters:
+ /// - message: The content of the log message.
+ /// - tag: An optional tag to categorize the message.
+ func warning(_ message: String, tag: String)
+
+ /// Logs a fault message, typically used for critical system-level failures.
+ ///
+ /// - Parameters:
+ /// - message: The content of the log message.
+ /// - tag: An optional tag to categorize the message.
+ func fault(_ message: String, tag: String)
+}
diff --git a/Sources/PowerSync/PowerSyncDatabase.swift b/Sources/PowerSync/PowerSyncDatabase.swift
index 454e7e3..e50ce87 100644
--- a/Sources/PowerSync/PowerSyncDatabase.swift
+++ b/Sources/PowerSync/PowerSyncDatabase.swift
@@ -7,14 +7,17 @@ public let DEFAULT_DB_FILENAME = "powersync.db"
/// - Parameters:
/// - schema: The database schema
/// - dbFilename: The database filename. Defaults to "powersync.db"
+/// - logger: Optional logging interface
/// - Returns: A configured PowerSyncDatabase instance
public func PowerSyncDatabase(
schema: Schema,
- dbFilename: String = DEFAULT_DB_FILENAME
+ dbFilename: String = DEFAULT_DB_FILENAME,
+ logger: DatabaseLogger = DefaultLogger()
) -> PowerSyncDatabaseProtocol {
return KotlinPowerSyncDatabaseImpl(
schema: schema,
- dbFilename: dbFilename
+ dbFilename: dbFilename,
+ logger: logger
)
}
diff --git a/Sources/PowerSync/PowerSyncDatabaseProtocol.swift b/Sources/PowerSync/PowerSyncDatabaseProtocol.swift
index ea5418c..9de5f00 100644
--- a/Sources/PowerSync/PowerSyncDatabaseProtocol.swift
+++ b/Sources/PowerSync/PowerSyncDatabaseProtocol.swift
@@ -100,6 +100,12 @@ public protocol PowerSyncDatabaseProtocol: Queries {
///
/// - Parameter clearLocal: Set to false to preserve data in local-only tables.
func disconnectAndClear(clearLocal: Bool) async throws
+
+ /// Close the database, releasing resources.
+ /// Also disconnects any active connection.
+ ///
+ /// Once close is called, this database cannot be used again - a new one must be constructed.
+ func close() async throws
}
public extension PowerSyncDatabaseProtocol {
diff --git a/Tests/PowerSyncTests/Kotlin/KotlinPowerSyncDatabaseImplTests.swift b/Tests/PowerSyncTests/Kotlin/KotlinPowerSyncDatabaseImplTests.swift
index 3c645c5..1bc0767 100644
--- a/Tests/PowerSyncTests/Kotlin/KotlinPowerSyncDatabaseImplTests.swift
+++ b/Tests/PowerSyncTests/Kotlin/KotlinPowerSyncDatabaseImplTests.swift
@@ -16,13 +16,15 @@ final class KotlinPowerSyncDatabaseImplTests: XCTestCase {
database = KotlinPowerSyncDatabaseImpl(
schema: schema,
- dbFilename: ":memory:"
+ dbFilename: ":memory:",
+ logger: DefaultLogger()
)
try await database.disconnectAndClear()
}
override func tearDown() async throws {
try await database.disconnectAndClear()
+ try await database.close()
database = nil
try await super.tearDown()
}
@@ -468,4 +470,45 @@ final class KotlinPowerSyncDatabaseImplTests: XCTestCase {
XCTAssertEqual(peopleCount, 1)
}
+
+ func testCustomLogger() async throws {
+ let logger = TestLogger()
+
+ let db2 = KotlinPowerSyncDatabaseImpl(
+ schema: schema,
+ dbFilename: ":memory:",
+ logger: logger
+ )
+
+ try await db2.close()
+
+ let warningIndex = logger.logs.firstIndex(
+ where: { value in
+ value.contains("warning: Multiple PowerSync instances for the same database have been detected")
+ }
+ )
+
+ XCTAssert(warningIndex! >= 0)
+ }
+
+ func testMinimumSeverity() async throws {
+ let logger = TestLogger(minSeverity: .error)
+
+ let db2 = KotlinPowerSyncDatabaseImpl(
+ schema: schema,
+ dbFilename: ":memory:",
+ logger: logger
+ )
+
+ try await db2.close()
+
+ let warningIndex = logger.logs.firstIndex(
+ where: { value in
+ value.contains("warning: Multiple PowerSync instances for the same database have been detected")
+ }
+ )
+
+ // The warning should not be present due to the min severity
+ XCTAssert(warningIndex == nil)
+ }
}
diff --git a/Tests/PowerSyncTests/Kotlin/TestLogger.swift b/Tests/PowerSyncTests/Kotlin/TestLogger.swift
new file mode 100644
index 0000000..ecc8305
--- /dev/null
+++ b/Tests/PowerSyncTests/Kotlin/TestLogger.swift
@@ -0,0 +1,23 @@
+@testable import PowerSync
+
+
+class TestLogWriterAdapter: LogWriterProtocol {
+ var logs = [String]()
+
+ func log(severity: LogSeverity, message: String, tag: String) {
+ logs.append("\(severity): \(message) (\(tag))")
+ }
+}
+
+class TestLogger: DefaultLogger {
+ let writer = TestLogWriterAdapter()
+
+ public var logs: [String] {
+ return writer.logs
+ }
+
+ override init(minSeverity: LogSeverity = .debug) {
+ super.init(minSeverity: minSeverity)
+ setWriters([writer])
+ }
+}
From 15807622a7c5bee4fc5987a47449268df91e7863 Mon Sep 17 00:00:00 2001
From: stevensJourney
Date: Tue, 8 Apr 2025 09:24:12 +0200
Subject: [PATCH 02/12] Improve loggers
---
Sources/PowerSync/Kotlin/DatabaseLogger.swift | 99 +++++++------------
Sources/PowerSync/Logger.swift | 55 +++++++++--
Sources/PowerSync/LoggerProtocol.swift | 41 +++++---
Sources/PowerSync/PowerSyncDatabase.swift | 4 +-
.../KotlinPowerSyncDatabaseImplTests.swift | 16 +--
Tests/PowerSyncTests/Kotlin/TestLogger.swift | 12 ---
6 files changed, 121 insertions(+), 106 deletions(-)
diff --git a/Sources/PowerSync/Kotlin/DatabaseLogger.swift b/Sources/PowerSync/Kotlin/DatabaseLogger.swift
index fe4e3ab..435ec97 100644
--- a/Sources/PowerSync/Kotlin/DatabaseLogger.swift
+++ b/Sources/PowerSync/Kotlin/DatabaseLogger.swift
@@ -1,57 +1,17 @@
import PowerSyncKotlin
-/// Maps a Kermit `Severity` level to a local `LogSeverity`.
-///
-/// - Parameter severity: The Kermit log severity value from Kotlin.
-/// - Returns: The corresponding `LogSeverity` used in Swift.
-private func mapKermitSeverity(_ severity: PowerSyncKotlin.Kermit_coreSeverity) -> LogSeverity {
- switch severity {
- case PowerSyncKotlin.Kermit_coreSeverity.verbose:
- return LogSeverity.debug
- case PowerSyncKotlin.Kermit_coreSeverity.debug:
- return LogSeverity.debug
- case PowerSyncKotlin.Kermit_coreSeverity.info:
- return LogSeverity.info
- case PowerSyncKotlin.Kermit_coreSeverity.warn:
- return LogSeverity.warning
- case PowerSyncKotlin.Kermit_coreSeverity.error:
- return LogSeverity.error
- case PowerSyncKotlin.Kermit_coreSeverity.assert:
- return LogSeverity.fault
- }
-}
-
-/// Maps a local `LogSeverity` to a Kermit-compatible `Kermit_coreSeverity`.
-///
-/// - Parameter severity: The Swift-side `LogSeverity`.
-/// - Returns: The equivalent Kermit log severity.
-private func mapSeverity(_ severity: LogSeverity) -> PowerSyncKotlin.Kermit_coreSeverity {
- switch severity {
- case .debug:
- return PowerSyncKotlin.Kermit_coreSeverity.debug
- case .info:
- return PowerSyncKotlin.Kermit_coreSeverity.info
- case .warning:
- return PowerSyncKotlin.Kermit_coreSeverity.warn
- case .error:
- return PowerSyncKotlin.Kermit_coreSeverity.error
- case .fault:
- return PowerSyncKotlin.Kermit_coreSeverity.assert
- }
-}
-
-/// Adapts a Swift `LogWritterProtocol` to Kermit's `LogWriter` interface.
+/// Adapts a Swift `LoggerProtocol` to Kermit's `LogWriter` interface.
///
/// This allows Kotlin logging (via Kermit) to call into the Swift logging implementation.
private class KermitLogWriterAdapter: Kermit_coreLogWriter {
/// The underlying Swift log writer to forward log messages to.
- var adapter: LogWriterProtocol
+ let logger: any LoggerProtocol
/// Initializes a new adapter.
///
/// - Parameter adapter: A Swift log writer that will handle log output.
- init(adapter: LogWriterProtocol) {
- self.adapter = adapter
+ init(logger: any LoggerProtocol) {
+ self.logger = logger
super.init()
}
@@ -63,11 +23,20 @@ private class KermitLogWriterAdapter: Kermit_coreLogWriter {
/// - tag: An optional string categorizing the log.
/// - throwable: An optional Kotlin exception (ignored here).
override func log(severity: Kermit_coreSeverity, message: String, tag: String, throwable: KotlinThrowable?) {
- adapter.log(
- severity: mapKermitSeverity(severity),
- message: message,
- tag: tag
- )
+ switch severity {
+ case PowerSyncKotlin.Kermit_coreSeverity.verbose:
+ return logger.debug(message, tag: tag)
+ case PowerSyncKotlin.Kermit_coreSeverity.debug:
+ return logger.debug(message, tag: tag)
+ case PowerSyncKotlin.Kermit_coreSeverity.info:
+ return logger.info(message, tag: tag)
+ case PowerSyncKotlin.Kermit_coreSeverity.warn:
+ return logger.warning(message, tag: tag)
+ case PowerSyncKotlin.Kermit_coreSeverity.error:
+ return logger.error(message, tag: tag)
+ case PowerSyncKotlin.Kermit_coreSeverity.assert:
+ return logger.fault(message, tag: tag)
+ }
}
}
@@ -75,15 +44,21 @@ private class KermitLogWriterAdapter: Kermit_coreLogWriter {
///
/// This class bridges Swift log writers with the Kotlin logging system and supports
/// runtime configuration of severity levels and writer lists.
-public class DatabaseLogger: LoggerProtocol {
+internal class DatabaseLogger: LoggerProtocol {
/// The underlying Kermit logger instance provided by the PowerSyncKotlin SDK.
- internal let kLogger = PowerSyncKotlin.generateLogger(logger: nil)
+ public let kLogger = PowerSyncKotlin.generateLogger(logger: nil)
+ public let logger: any LoggerProtocol
/// Initializes a new logger with an optional list of writers.
///
/// - Parameter writers: An array of Swift log writers. Defaults to an empty array.
- init(writers: [any LogWriterProtocol] = []) {
- setWriters(writers)
+ init(_ logger: any LoggerProtocol) {
+ self.logger = logger
+ // Set to the lowest severity. The adapter downstream logger should filter by severity
+ kLogger.mutableConfig.setMinSeverity(Kermit_coreSeverity.verbose)
+ kLogger.mutableConfig.setLogWriterList(
+ [KermitLogWriterAdapter(logger: logger)]
+ )
}
/// Sets the minimum severity level that will be logged.
@@ -92,9 +67,7 @@ public class DatabaseLogger: LoggerProtocol {
///
/// - Parameter severity: The minimum `LogSeverity` to allow through.
public func setMinSeverity(_ severity: LogSeverity) {
- kLogger.mutableConfig.setMinSeverity(
- mapSeverity(severity)
- )
+ logger.setMinSeverity(severity)
}
/// Sets the list of log writers that will receive log messages.
@@ -103,33 +76,31 @@ public class DatabaseLogger: LoggerProtocol {
///
/// - Parameter writers: An array of Swift `LogWritterProtocol` implementations.
public func setWriters(_ writers: [any LogWriterProtocol]) {
- kLogger.mutableConfig.setLogWriterList(
- writers.map { item in KermitLogWriterAdapter(adapter: item) }
- )
+ logger.setWriters(writers)
}
/// Logs a debug-level message.
public func debug(_ message: String, tag: String) {
- kLogger.d(messageString: message, throwable: nil, tag: tag)
+ logger.debug(message, tag: tag)
}
/// Logs an info-level message.
public func info(_ message: String, tag: String) {
- kLogger.i(messageString: message, throwable: nil, tag: tag)
+ logger.info(message, tag: tag)
}
/// Logs a warning-level message.
public func warning(_ message: String, tag: String) {
- kLogger.w(messageString: message, throwable: nil, tag: tag)
+ logger.warning(message, tag: tag)
}
/// Logs an error-level message.
public func error(_ message: String, tag: String) {
- kLogger.e(messageString: message, throwable: nil, tag: tag)
+ logger.error(message, tag: tag)
}
/// Logs a fault (assert-level) message, typically used for critical issues.
public func fault(_ message: String, tag: String) {
- kLogger.a(messageString: message, throwable: nil, tag: tag)
+ logger.fault(message, tag: tag)
}
}
diff --git a/Sources/PowerSync/Logger.swift b/Sources/PowerSync/Logger.swift
index 8c6351e..5e44710 100644
--- a/Sources/PowerSync/Logger.swift
+++ b/Sources/PowerSync/Logger.swift
@@ -1,10 +1,9 @@
import OSLog
-/// A log writer that bridges custom `LogSeverity` levels to Apple's unified `Logger` framework.
+/// A log writer which prints to the standard output
///
/// This writer uses `os.Logger` on iOS 14+ and falls back to `print` for earlier versions.
-/// Tags are optionally prefixed to messages in square brackets.
-public class SwiftLogWriter: LogWriterProtocol {
+public class PrintLogWriter: LogWriterProtocol {
/// Logs a message with a given severity and optional tag.
///
@@ -39,14 +38,54 @@ public class SwiftLogWriter: LogWriterProtocol {
/// A default logger configuration that uses `SwiftLogWritter` and filters messages by minimum severity.
///
/// This logger integrates with your custom logging system and uses `os.Logger` under the hood.
-public class DefaultLogger: DatabaseLogger {
+public class DefaultLogger: LoggerProtocol {
+ public var minSeverirty: LogSeverity
+ public var writers: [any LogWriterProtocol]
/// Initializes the default logger with an optional minimum severity level.
///
/// - Parameter minSeverity: The minimum severity level to log. Defaults to `.debug`.
- public init(minSeverity: LogSeverity = .debug) {
- super.init()
- setMinSeverity(minSeverity)
- setWriters([SwiftLogWriter()])
+ public init(minSeverity: LogSeverity = .debug, writers: [any LogWriterProtocol]? = nil ) {
+ self.writers = writers ?? [ PrintLogWriter() ]
+ self.minSeverirty = minSeverity
+ }
+
+ public func setWriters(_ writters: [any LogWriterProtocol]) {
+ self.writers = writters
+ }
+
+ public func setMinSeverity(_ severity: LogSeverity) {
+ self.minSeverirty = severity
+ }
+
+
+ public func debug(_ message: String, tag: String) {
+ self.writeLog(message, tag: tag, severity: LogSeverity.debug)
+ }
+
+ public func error(_ message: String, tag: String) {
+ self.writeLog(message, tag: tag, severity: LogSeverity.error)
+ }
+
+ public func info(_ message: String, tag: String) {
+ self.writeLog(message, tag: tag, severity: LogSeverity.info)
+ }
+
+ public func warning(_ message: String, tag: String) {
+ self.writeLog(message, tag: tag, severity: LogSeverity.warning)
+ }
+
+ public func fault(_ message: String, tag: String) {
+ self.writeLog(message, tag: tag, severity: LogSeverity.fault)
+ }
+
+ private func writeLog(_ message: String, tag: String, severity: LogSeverity) {
+ if (severity.rawValue < self.minSeverirty.rawValue) {
+ return
+ }
+
+ for writer in self.writers {
+ writer.log(severity: severity, message: message, tag: tag)
+ }
}
}
diff --git a/Sources/PowerSync/LoggerProtocol.swift b/Sources/PowerSync/LoggerProtocol.swift
index 620b3c1..a99f4ce 100644
--- a/Sources/PowerSync/LoggerProtocol.swift
+++ b/Sources/PowerSync/LoggerProtocol.swift
@@ -1,19 +1,34 @@
-/// Represents the severity level of a log message.
-public enum LogSeverity: String, CaseIterable {
- /// Informational messages that highlight the progress of the application.
- case info = "INFO"
-
- /// Error events that might still allow the application to continue running.
- case error = "ERROR"
-
+public enum LogSeverity: Int, CaseIterable {
/// Detailed information typically used for debugging.
- case debug = "DEBUG"
-
+ case debug = 0
+
+ /// Informational messages that highlight the progress of the application.
+ case info = 1
+
/// Potentially harmful situations that are not necessarily errors.
- case warning = "WARNING"
-
+ case warning = 2
+
+ /// Error events that might still allow the application to continue running.
+ case error = 3
+
/// Serious errors indicating critical failures, often unrecoverable.
- case fault = "FAULT"
+ case fault = 4
+
+ /// Map severity to its string representation
+ public var stringValue: String {
+ switch self {
+ case .debug: return "DEBUG"
+ case .info: return "INFO"
+ case .warning: return "WARNING"
+ case .error: return "ERROR"
+ case .fault: return "FAULT"
+ }
+ }
+
+ /// Convert Int to String representation
+ public static func string(from intValue: Int) -> String? {
+ return LogSeverity(rawValue: intValue)?.stringValue
+ }
}
/// A protocol for writing log messages to a specific backend or output.
diff --git a/Sources/PowerSync/PowerSyncDatabase.swift b/Sources/PowerSync/PowerSyncDatabase.swift
index e50ce87..41c7d71 100644
--- a/Sources/PowerSync/PowerSyncDatabase.swift
+++ b/Sources/PowerSync/PowerSyncDatabase.swift
@@ -12,12 +12,12 @@ public let DEFAULT_DB_FILENAME = "powersync.db"
public func PowerSyncDatabase(
schema: Schema,
dbFilename: String = DEFAULT_DB_FILENAME,
- logger: DatabaseLogger = DefaultLogger()
+ logger: (any LoggerProtocol)? = nil
) -> PowerSyncDatabaseProtocol {
return KotlinPowerSyncDatabaseImpl(
schema: schema,
dbFilename: dbFilename,
- logger: logger
+ logger: logger != nil ? DatabaseLogger(logger!) : nil
)
}
diff --git a/Tests/PowerSyncTests/Kotlin/KotlinPowerSyncDatabaseImplTests.swift b/Tests/PowerSyncTests/Kotlin/KotlinPowerSyncDatabaseImplTests.swift
index 1bc0767..191d0fe 100644
--- a/Tests/PowerSyncTests/Kotlin/KotlinPowerSyncDatabaseImplTests.swift
+++ b/Tests/PowerSyncTests/Kotlin/KotlinPowerSyncDatabaseImplTests.swift
@@ -17,7 +17,7 @@ final class KotlinPowerSyncDatabaseImplTests: XCTestCase {
database = KotlinPowerSyncDatabaseImpl(
schema: schema,
dbFilename: ":memory:",
- logger: DefaultLogger()
+ logger: DatabaseLogger(DefaultLogger())
)
try await database.disconnectAndClear()
}
@@ -472,17 +472,18 @@ final class KotlinPowerSyncDatabaseImplTests: XCTestCase {
}
func testCustomLogger() async throws {
- let logger = TestLogger()
+ let testWriter = TestLogWriterAdapter()
+ let logger = DefaultLogger(minSeverity: LogSeverity.debug, writers: [testWriter])
let db2 = KotlinPowerSyncDatabaseImpl(
schema: schema,
dbFilename: ":memory:",
- logger: logger
+ logger: DatabaseLogger(logger)
)
try await db2.close()
- let warningIndex = logger.logs.firstIndex(
+ let warningIndex = testWriter.logs.firstIndex(
where: { value in
value.contains("warning: Multiple PowerSync instances for the same database have been detected")
}
@@ -492,17 +493,18 @@ final class KotlinPowerSyncDatabaseImplTests: XCTestCase {
}
func testMinimumSeverity() async throws {
- let logger = TestLogger(minSeverity: .error)
+ let testWriter = TestLogWriterAdapter()
+ let logger = DefaultLogger(minSeverity: LogSeverity.error, writers: [testWriter])
let db2 = KotlinPowerSyncDatabaseImpl(
schema: schema,
dbFilename: ":memory:",
- logger: logger
+ logger: DatabaseLogger(logger)
)
try await db2.close()
- let warningIndex = logger.logs.firstIndex(
+ let warningIndex = testWriter.logs.firstIndex(
where: { value in
value.contains("warning: Multiple PowerSync instances for the same database have been detected")
}
diff --git a/Tests/PowerSyncTests/Kotlin/TestLogger.swift b/Tests/PowerSyncTests/Kotlin/TestLogger.swift
index ecc8305..fa26789 100644
--- a/Tests/PowerSyncTests/Kotlin/TestLogger.swift
+++ b/Tests/PowerSyncTests/Kotlin/TestLogger.swift
@@ -9,15 +9,3 @@ class TestLogWriterAdapter: LogWriterProtocol {
}
}
-class TestLogger: DefaultLogger {
- let writer = TestLogWriterAdapter()
-
- public var logs: [String] {
- return writer.logs
- }
-
- override init(minSeverity: LogSeverity = .debug) {
- super.init(minSeverity: minSeverity)
- setWriters([writer])
- }
-}
From 7e9325ba4d2c5b4711183f68b9a868cb66aecdf2 Mon Sep 17 00:00:00 2001
From: stevensJourney
Date: Tue, 8 Apr 2025 09:44:14 +0200
Subject: [PATCH 03/12] optional tag usage
---
Sources/PowerSync/Kotlin/DatabaseLogger.swift | 20 ++++-----
Sources/PowerSync/Logger.swift | 42 +++++++++++--------
Sources/PowerSync/LoggerProtocol.swift | 12 +++---
Tests/PowerSyncTests/Kotlin/TestLogger.swift | 4 +-
4 files changed, 42 insertions(+), 36 deletions(-)
diff --git a/Sources/PowerSync/Kotlin/DatabaseLogger.swift b/Sources/PowerSync/Kotlin/DatabaseLogger.swift
index 435ec97..eb0617e 100644
--- a/Sources/PowerSync/Kotlin/DatabaseLogger.swift
+++ b/Sources/PowerSync/Kotlin/DatabaseLogger.swift
@@ -9,7 +9,7 @@ private class KermitLogWriterAdapter: Kermit_coreLogWriter {
/// Initializes a new adapter.
///
- /// - Parameter adapter: A Swift log writer that will handle log output.
+ /// - Parameter logger: A Swift log writer that will handle log output.
init(logger: any LoggerProtocol) {
self.logger = logger
super.init()
@@ -20,7 +20,7 @@ private class KermitLogWriterAdapter: Kermit_coreLogWriter {
/// - Parameters:
/// - severity: The severity level of the log.
/// - message: The content of the log message.
- /// - tag: An optional string categorizing the log.
+ /// - tag: A string categorizing the log.
/// - throwable: An optional Kotlin exception (ignored here).
override func log(severity: Kermit_coreSeverity, message: String, tag: String, throwable: KotlinThrowable?) {
switch severity {
@@ -40,7 +40,7 @@ private class KermitLogWriterAdapter: Kermit_coreLogWriter {
}
}
-/// A logger implementation that integrates with PowerSync's Kotlin backend using Kermit.
+/// A logger implementation that integrates with PowerSync's Kotlin core using Kermit.
///
/// This class bridges Swift log writers with the Kotlin logging system and supports
/// runtime configuration of severity levels and writer lists.
@@ -51,10 +51,10 @@ internal class DatabaseLogger: LoggerProtocol {
/// Initializes a new logger with an optional list of writers.
///
- /// - Parameter writers: An array of Swift log writers. Defaults to an empty array.
+ /// - Parameter logger: A logger which will be called for each internal log operation
init(_ logger: any LoggerProtocol) {
self.logger = logger
- // Set to the lowest severity. The adapter downstream logger should filter by severity
+ // Set to the lowest severity. The provided logger should filter by severity
kLogger.mutableConfig.setMinSeverity(Kermit_coreSeverity.verbose)
kLogger.mutableConfig.setLogWriterList(
[KermitLogWriterAdapter(logger: logger)]
@@ -80,27 +80,27 @@ internal class DatabaseLogger: LoggerProtocol {
}
/// Logs a debug-level message.
- public func debug(_ message: String, tag: String) {
+ public func debug(_ message: String, tag: String?) {
logger.debug(message, tag: tag)
}
/// Logs an info-level message.
- public func info(_ message: String, tag: String) {
+ public func info(_ message: String, tag: String?) {
logger.info(message, tag: tag)
}
/// Logs a warning-level message.
- public func warning(_ message: String, tag: String) {
+ public func warning(_ message: String, tag: String?) {
logger.warning(message, tag: tag)
}
/// Logs an error-level message.
- public func error(_ message: String, tag: String) {
+ public func error(_ message: String, tag: String?) {
logger.error(message, tag: tag)
}
/// Logs a fault (assert-level) message, typically used for critical issues.
- public func fault(_ message: String, tag: String) {
+ public func fault(_ message: String, tag: String?) {
logger.fault(message, tag: tag)
}
}
diff --git a/Sources/PowerSync/Logger.swift b/Sources/PowerSync/Logger.swift
index 5e44710..4ec845f 100644
--- a/Sources/PowerSync/Logger.swift
+++ b/Sources/PowerSync/Logger.swift
@@ -11,8 +11,14 @@ public class PrintLogWriter: LogWriterProtocol {
/// - severity: The severity level of the message.
/// - message: The content of the log message.
/// - tag: An optional tag used to categorize the message. If empty, no brackets are shown.
- public func log(severity: LogSeverity, message: String, tag: String) {
- let tagPrefix = tag.isEmpty ? "" : "[\(tag)] "
+ public func log(severity: LogSeverity, message: String, tag: String?) {
+ let tagPrefix: String
+ if let tag, !tag.isEmpty {
+ tagPrefix = "[\(tag)] "
+ } else {
+ tagPrefix = ""
+ }
+
let message = "\(tagPrefix) \(message)"
if #available(iOS 14.0, *) {
let l = Logger()
@@ -30,21 +36,21 @@ public class PrintLogWriter: LogWriterProtocol {
l.fault("\(message)")
}
} else {
- print("\(severity.rawValue): \(message)")
+ print("\(severity.stringValue): \(message)")
}
}
}
-/// A default logger configuration that uses `SwiftLogWritter` and filters messages by minimum severity.
-///
-/// This logger integrates with your custom logging system and uses `os.Logger` under the hood.
+/// A default logger configuration that uses `PrintLogWritter` and filters messages by minimum severity.
public class DefaultLogger: LoggerProtocol {
public var minSeverirty: LogSeverity
public var writers: [any LogWriterProtocol]
/// Initializes the default logger with an optional minimum severity level.
///
- /// - Parameter minSeverity: The minimum severity level to log. Defaults to `.debug`.
+ /// - Parameters
+ /// - minSeverity: The minimum severity level to log. Defaults to `.debug`.
+ /// - writers: Optional writers which logs should be written to. Defaults to a `PrintLogWriter`.
public init(minSeverity: LogSeverity = .debug, writers: [any LogWriterProtocol]? = nil ) {
self.writers = writers ?? [ PrintLogWriter() ]
self.minSeverirty = minSeverity
@@ -59,27 +65,27 @@ public class DefaultLogger: LoggerProtocol {
}
- public func debug(_ message: String, tag: String) {
- self.writeLog(message, tag: tag, severity: LogSeverity.debug)
+ public func debug(_ message: String, tag: String? = nil) {
+ self.writeLog(message, severity: LogSeverity.debug, tag: tag)
}
- public func error(_ message: String, tag: String) {
- self.writeLog(message, tag: tag, severity: LogSeverity.error)
+ public func error(_ message: String, tag: String? = nil) {
+ self.writeLog(message, severity: LogSeverity.error, tag: tag)
}
- public func info(_ message: String, tag: String) {
- self.writeLog(message, tag: tag, severity: LogSeverity.info)
+ public func info(_ message: String, tag: String? = nil) {
+ self.writeLog(message, severity: LogSeverity.info, tag: tag)
}
- public func warning(_ message: String, tag: String) {
- self.writeLog(message, tag: tag, severity: LogSeverity.warning)
+ public func warning(_ message: String, tag: String? = nil) {
+ self.writeLog(message, severity: LogSeverity.warning, tag: tag)
}
- public func fault(_ message: String, tag: String) {
- self.writeLog(message, tag: tag, severity: LogSeverity.fault)
+ public func fault(_ message: String, tag: String? = nil) {
+ self.writeLog(message, severity: LogSeverity.fault, tag: tag)
}
- private func writeLog(_ message: String, tag: String, severity: LogSeverity) {
+ private func writeLog(_ message: String, severity: LogSeverity, tag: String?) {
if (severity.rawValue < self.minSeverirty.rawValue) {
return
}
diff --git a/Sources/PowerSync/LoggerProtocol.swift b/Sources/PowerSync/LoggerProtocol.swift
index a99f4ce..4177d32 100644
--- a/Sources/PowerSync/LoggerProtocol.swift
+++ b/Sources/PowerSync/LoggerProtocol.swift
@@ -41,7 +41,7 @@ public protocol LogWriterProtocol {
/// - severity: The severity level of the log message.
/// - message: The content of the log message.
/// - tag: An optional tag to categorize or group the log message.
- func log(severity: LogSeverity, message: String, tag: String)
+ func log(severity: LogSeverity, message: String, tag: String?)
}
/// A protocol defining the interface for a logger that supports severity filtering and multiple writers.
@@ -65,33 +65,33 @@ public protocol LoggerProtocol {
/// - Parameters:
/// - message: The content of the log message.
/// - tag: An optional tag to categorize the message.
- func info(_ message: String, tag: String)
+ func info(_ message: String, tag: String?)
/// Logs an error message.
///
/// - Parameters:
/// - message: The content of the log message.
/// - tag: An optional tag to categorize the message.
- func error(_ message: String, tag: String)
+ func error(_ message: String, tag: String?)
/// Logs a debug message.
///
/// - Parameters:
/// - message: The content of the log message.
/// - tag: An optional tag to categorize the message.
- func debug(_ message: String, tag: String)
+ func debug(_ message: String, tag: String?)
/// Logs a warning message.
///
/// - Parameters:
/// - message: The content of the log message.
/// - tag: An optional tag to categorize the message.
- func warning(_ message: String, tag: String)
+ func warning(_ message: String, tag: String?)
/// Logs a fault message, typically used for critical system-level failures.
///
/// - Parameters:
/// - message: The content of the log message.
/// - tag: An optional tag to categorize the message.
- func fault(_ message: String, tag: String)
+ func fault(_ message: String, tag: String?)
}
diff --git a/Tests/PowerSyncTests/Kotlin/TestLogger.swift b/Tests/PowerSyncTests/Kotlin/TestLogger.swift
index fa26789..bff9ad1 100644
--- a/Tests/PowerSyncTests/Kotlin/TestLogger.swift
+++ b/Tests/PowerSyncTests/Kotlin/TestLogger.swift
@@ -4,8 +4,8 @@
class TestLogWriterAdapter: LogWriterProtocol {
var logs = [String]()
- func log(severity: LogSeverity, message: String, tag: String) {
- logs.append("\(severity): \(message) (\(tag))")
+ func log(severity: LogSeverity, message: String, tag: String?) {
+ logs.append("\(severity): \(message) \(tag != nil ? "\(tag!)" : "")")
}
}
From f3e921096a1c347a861ff518c452096a3278c0af Mon Sep 17 00:00:00 2001
From: stevensJourney
Date: Tue, 8 Apr 2025 09:51:51 +0200
Subject: [PATCH 04/12] Added logger to readme
---
README.md | 30 +++++++++++++++++++++++++++---
1 file changed, 27 insertions(+), 3 deletions(-)
diff --git a/README.md b/README.md
index 40a4849..682258c 100644
--- a/README.md
+++ b/README.md
@@ -2,7 +2,7 @@
-*[PowerSync](https://www.powersync.com) is a sync engine for building local-first apps with instantly-responsive UI/UX and simplified state transfer. Syncs between SQLite on the client-side and Postgres, MongoDB or MySQL on the server-side.*
+_[PowerSync](https://www.powersync.com) is a sync engine for building local-first apps with instantly-responsive UI/UX and simplified state transfer. Syncs between SQLite on the client-side and Postgres, MongoDB or MySQL on the server-side._
# PowerSync Swift
@@ -16,7 +16,7 @@ This SDK is currently in a beta release it is suitable for production use, given
- [Sources](./Sources/)
- - This is the Swift SDK implementation.
+ - This is the Swift SDK implementation.
## Demo Apps / Example Projects
@@ -51,11 +51,35 @@ to your `Package.swift` file and pin the dependency to a specific version. The v
to your `Package.swift` file and pin the dependency to a specific version. This is required because the package is in beta.
+## Usage
+
+Create a PowerSync client
+
+```swift
+import PowerSync
+
+let powersync = PowerSyncDatabase(
+ schema: Schema(
+ tables: [
+ Table(
+ name: "users",
+ columns: [
+ .text("count"),
+ .integer("is_active"),
+ .real("weight"),
+ .text("description")
+ ]
+ )
+ ]
+ ),
+ logger: DefaultLogger(minSeverity: .debug)
+)
+```
+
## Underlying Kotlin Dependency
The PowerSync Swift SDK currently makes use of the [PowerSync Kotlin Multiplatform SDK](https://github.com/powersync-ja/powersync-kotlin) with the API tool [SKIE](https://skie.touchlab.co/) and KMMBridge under the hood to help generate and publish a native Swift package. We will move to an entirely Swift native API in v1 and do not expect there to be any breaking changes. For more details, see the [Swift SDK reference](https://docs.powersync.com/client-sdk-references/swift).
-
## Migration from Alpha to Beta
See these [developer notes](https://docs.powersync.com/client-sdk-references/swift#migrating-from-the-alpha-to-the-beta-sdk) if you are migrating from the alpha to the beta version of the Swift SDK.
From 28cff0409a471b99086210ae9bbc872820c43fb9 Mon Sep 17 00:00:00 2001
From: stevensJourney
Date: Tue, 8 Apr 2025 10:08:08 +0200
Subject: [PATCH 05/12] update test
---
.../Kotlin/KotlinPowerSyncDatabaseImplTests.swift | 7 ++++++-
1 file changed, 6 insertions(+), 1 deletion(-)
diff --git a/Tests/PowerSyncTests/Kotlin/KotlinPowerSyncDatabaseImplTests.swift b/Tests/PowerSyncTests/Kotlin/KotlinPowerSyncDatabaseImplTests.swift
index 191d0fe..fb747d0 100644
--- a/Tests/PowerSyncTests/Kotlin/KotlinPowerSyncDatabaseImplTests.swift
+++ b/Tests/PowerSyncTests/Kotlin/KotlinPowerSyncDatabaseImplTests.swift
@@ -24,7 +24,12 @@ final class KotlinPowerSyncDatabaseImplTests: XCTestCase {
override func tearDown() async throws {
try await database.disconnectAndClear()
- try await database.close()
+ // Tests currently fail if this is called.
+ // The watched query tests try and read from the DB while it's closing.
+ // This causes a PowerSyncException to be thrown in the Kotlin flow.
+ // Custom exceptions are not supported by SKIEE. This causes a crash.
+ // FIXME: Reapply once watched query errors are handled better.
+ // try await database.close()
database = nil
try await super.tearDown()
}
From 075ff0cca7eed5abcb0672979fa7032f4c2162b4 Mon Sep 17 00:00:00 2001
From: stevensJourney
Date: Tue, 8 Apr 2025 10:16:01 +0200
Subject: [PATCH 06/12] cleanup
---
Sources/PowerSync/PowerSyncDatabase.swift | 4 ++--
.../Kotlin/KotlinPowerSyncDatabaseImplTests.swift | 2 +-
2 files changed, 3 insertions(+), 3 deletions(-)
diff --git a/Sources/PowerSync/PowerSyncDatabase.swift b/Sources/PowerSync/PowerSyncDatabase.swift
index 41c7d71..dbabf92 100644
--- a/Sources/PowerSync/PowerSyncDatabase.swift
+++ b/Sources/PowerSync/PowerSyncDatabase.swift
@@ -12,12 +12,12 @@ public let DEFAULT_DB_FILENAME = "powersync.db"
public func PowerSyncDatabase(
schema: Schema,
dbFilename: String = DEFAULT_DB_FILENAME,
- logger: (any LoggerProtocol)? = nil
+ logger: (any LoggerProtocol) = DefaultLogger()
) -> PowerSyncDatabaseProtocol {
return KotlinPowerSyncDatabaseImpl(
schema: schema,
dbFilename: dbFilename,
- logger: logger != nil ? DatabaseLogger(logger!) : nil
+ logger: DatabaseLogger(logger)
)
}
diff --git a/Tests/PowerSyncTests/Kotlin/KotlinPowerSyncDatabaseImplTests.swift b/Tests/PowerSyncTests/Kotlin/KotlinPowerSyncDatabaseImplTests.swift
index fb747d0..a97ff9f 100644
--- a/Tests/PowerSyncTests/Kotlin/KotlinPowerSyncDatabaseImplTests.swift
+++ b/Tests/PowerSyncTests/Kotlin/KotlinPowerSyncDatabaseImplTests.swift
@@ -27,7 +27,7 @@ final class KotlinPowerSyncDatabaseImplTests: XCTestCase {
// Tests currently fail if this is called.
// The watched query tests try and read from the DB while it's closing.
// This causes a PowerSyncException to be thrown in the Kotlin flow.
- // Custom exceptions are not supported by SKIEE. This causes a crash.
+ // Custom exceptions in flows are not supported by SKIEE. This causes a crash.
// FIXME: Reapply once watched query errors are handled better.
// try await database.close()
database = nil
From 8f0911f32e74c9fe10d2119debab51cf946e5156 Mon Sep 17 00:00:00 2001
From: stevensJourney <51082125+stevensJourney@users.noreply.github.com>
Date: Tue, 8 Apr 2025 10:24:16 +0200
Subject: [PATCH 07/12] Update Sources/PowerSync/Kotlin/DatabaseLogger.swift
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
---
Sources/PowerSync/Kotlin/DatabaseLogger.swift | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/Sources/PowerSync/Kotlin/DatabaseLogger.swift b/Sources/PowerSync/Kotlin/DatabaseLogger.swift
index eb0617e..9b0289e 100644
--- a/Sources/PowerSync/Kotlin/DatabaseLogger.swift
+++ b/Sources/PowerSync/Kotlin/DatabaseLogger.swift
@@ -74,7 +74,7 @@ internal class DatabaseLogger: LoggerProtocol {
///
/// This updates both the internal writer list and the Kermit logger's configuration.
///
- /// - Parameter writers: An array of Swift `LogWritterProtocol` implementations.
+ /// - Parameter writers: An array of Swift `LogWriterProtocol` implementations.
public func setWriters(_ writers: [any LogWriterProtocol]) {
logger.setWriters(writers)
}
From b2efe62fb5deee24f2f4047c35bf5f5c9213c702 Mon Sep 17 00:00:00 2001
From: stevensJourney <51082125+stevensJourney@users.noreply.github.com>
Date: Tue, 8 Apr 2025 10:24:32 +0200
Subject: [PATCH 08/12] Update Sources/PowerSync/LoggerProtocol.swift
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
---
Sources/PowerSync/LoggerProtocol.swift | 5 ++---
1 file changed, 2 insertions(+), 3 deletions(-)
diff --git a/Sources/PowerSync/LoggerProtocol.swift b/Sources/PowerSync/LoggerProtocol.swift
index 4177d32..105f0a1 100644
--- a/Sources/PowerSync/LoggerProtocol.swift
+++ b/Sources/PowerSync/LoggerProtocol.swift
@@ -57,9 +57,8 @@ public protocol LoggerProtocol {
/// Sets the list of log writers that will handle log output.
///
- /// - Parameter writters: An array of `LogWritterProtocol` conformers.
- func setWriters(_ writters: [LogWriterProtocol])
-
+ /// - Parameter writers: An array of `LogWriterProtocol` conformers.
+ func setWriters(_ writers: [LogWriterProtocol])
/// Logs an informational message.
///
/// - Parameters:
From 12250ffd23140a778c270eee2e1e5cb59b2c2cc2 Mon Sep 17 00:00:00 2001
From: stevensJourney <51082125+stevensJourney@users.noreply.github.com>
Date: Tue, 8 Apr 2025 10:24:41 +0200
Subject: [PATCH 09/12] Update Sources/PowerSync/Logger.swift
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
---
Sources/PowerSync/Logger.swift | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/Sources/PowerSync/Logger.swift b/Sources/PowerSync/Logger.swift
index 4ec845f..7113a64 100644
--- a/Sources/PowerSync/Logger.swift
+++ b/Sources/PowerSync/Logger.swift
@@ -43,7 +43,7 @@ public class PrintLogWriter: LogWriterProtocol {
/// A default logger configuration that uses `PrintLogWritter` and filters messages by minimum severity.
public class DefaultLogger: LoggerProtocol {
- public var minSeverirty: LogSeverity
+ public var minSeverity: LogSeverity
public var writers: [any LogWriterProtocol]
/// Initializes the default logger with an optional minimum severity level.
From 608cb4614c5ee7ffed2b44e6e7e01b39293e23aa Mon Sep 17 00:00:00 2001
From: stevensJourney
Date: Tue, 8 Apr 2025 10:25:30 +0200
Subject: [PATCH 10/12] fix typo
---
Sources/PowerSync/Logger.swift | 6 +++---
1 file changed, 3 insertions(+), 3 deletions(-)
diff --git a/Sources/PowerSync/Logger.swift b/Sources/PowerSync/Logger.swift
index 7113a64..648cd6e 100644
--- a/Sources/PowerSync/Logger.swift
+++ b/Sources/PowerSync/Logger.swift
@@ -53,7 +53,7 @@ public class DefaultLogger: LoggerProtocol {
/// - writers: Optional writers which logs should be written to. Defaults to a `PrintLogWriter`.
public init(minSeverity: LogSeverity = .debug, writers: [any LogWriterProtocol]? = nil ) {
self.writers = writers ?? [ PrintLogWriter() ]
- self.minSeverirty = minSeverity
+ self.minSeverity = minSeverity
}
public func setWriters(_ writters: [any LogWriterProtocol]) {
@@ -61,7 +61,7 @@ public class DefaultLogger: LoggerProtocol {
}
public func setMinSeverity(_ severity: LogSeverity) {
- self.minSeverirty = severity
+ self.minSeverity = severity
}
@@ -86,7 +86,7 @@ public class DefaultLogger: LoggerProtocol {
}
private func writeLog(_ message: String, severity: LogSeverity, tag: String?) {
- if (severity.rawValue < self.minSeverirty.rawValue) {
+ if (severity.rawValue < self.minSeverity.rawValue) {
return
}
From 23beb9adaa05f44beb99f9418a66008655c3ce39 Mon Sep 17 00:00:00 2001
From: stevensJourney
Date: Tue, 8 Apr 2025 10:31:49 +0200
Subject: [PATCH 11/12] cleanup print logger
---
Sources/PowerSync/Logger.swift | 67 +++++++++++++++++++++-------------
1 file changed, 41 insertions(+), 26 deletions(-)
diff --git a/Sources/PowerSync/Logger.swift b/Sources/PowerSync/Logger.swift
index 648cd6e..988d013 100644
--- a/Sources/PowerSync/Logger.swift
+++ b/Sources/PowerSync/Logger.swift
@@ -2,45 +2,60 @@ import OSLog
/// A log writer which prints to the standard output
///
-/// This writer uses `os.Logger` on iOS 14+ and falls back to `print` for earlier versions.
+/// This writer uses `os.Logger` on iOS/macOS/tvOS/watchOS 14+ and falls back to `print` for earlier versions.
public class PrintLogWriter: LogWriterProtocol {
+ private let subsystem: String
+ private let category: String
+ private lazy var logger: Any? = {
+ if #available(iOS 14.0, macOS 11.0, tvOS 14.0, watchOS 7.0, *) {
+ return Logger(subsystem: subsystem, category: category)
+ }
+ return nil
+ }()
+
+ /// Creates a new PrintLogWriter
+ /// - Parameters:
+ /// - subsystem: The subsystem identifier (typically reverse DNS notation of your app)
+ /// - category: The category within your subsystem
+ public init(subsystem: String = Bundle.main.bundleIdentifier ?? "com.powersync.logger",
+ category: String = "default") {
+ self.subsystem = subsystem
+ self.category = category
+ }
+
/// Logs a message with a given severity and optional tag.
- ///
- /// - Parameters:
- /// - severity: The severity level of the message.
- /// - message: The content of the log message.
- /// - tag: An optional tag used to categorize the message. If empty, no brackets are shown.
+ /// - Parameters:
+ /// - severity: The severity level of the message.
+ /// - message: The content of the log message.
+ /// - tag: An optional tag used to categorize the message. If empty, no brackets are shown.
public func log(severity: LogSeverity, message: String, tag: String?) {
- let tagPrefix: String
- if let tag, !tag.isEmpty {
- tagPrefix = "[\(tag)] "
- } else {
- tagPrefix = ""
- }
+ let tagPrefix = tag.map { !$0.isEmpty ? "[\($0)] " : "" } ?? ""
+ let formattedMessage = "\(tagPrefix)\(message)"
- let message = "\(tagPrefix) \(message)"
- if #available(iOS 14.0, *) {
- let l = Logger()
+ if #available(iOS 14.0, macOS 11.0, tvOS 14.0, watchOS 7.0, *) {
+ guard let logger = logger as? Logger else { return }
switch severity {
- case .info:
- l.info("\(message)")
- case .error:
- l.error("\(message)")
- case .debug:
- l.debug("\(message)")
- case .warning:
- l.warning("\(message)")
- case .fault:
- l.fault("\(message)")
+ case .info:
+ logger.info("\(formattedMessage, privacy: .public)")
+ case .error:
+ logger.error("\(formattedMessage, privacy: .public)")
+ case .debug:
+ logger.debug("\(formattedMessage, privacy: .public)")
+ case .warning:
+ logger.warning("\(formattedMessage, privacy: .public)")
+ case .fault:
+ logger.fault("\(formattedMessage, privacy: .public)")
}
} else {
- print("\(severity.stringValue): \(message)")
+ print("\(severity.stringValue): \(formattedMessage)")
}
}
}
+
+
/// A default logger configuration that uses `PrintLogWritter` and filters messages by minimum severity.
public class DefaultLogger: LoggerProtocol {
public var minSeverity: LogSeverity
From 0285979a15e73ac3797740b3cef16aa09cc727c3 Mon Sep 17 00:00:00 2001
From: stevensJourney
Date: Mon, 14 Apr 2025 09:37:37 +0200
Subject: [PATCH 12/12] Cleanup protocol
---
Sources/PowerSync/Kotlin/DatabaseLogger.swift | 18 ------------------
Sources/PowerSync/LoggerProtocol.swift | 11 -----------
2 files changed, 29 deletions(-)
diff --git a/Sources/PowerSync/Kotlin/DatabaseLogger.swift b/Sources/PowerSync/Kotlin/DatabaseLogger.swift
index 9b0289e..21229d1 100644
--- a/Sources/PowerSync/Kotlin/DatabaseLogger.swift
+++ b/Sources/PowerSync/Kotlin/DatabaseLogger.swift
@@ -61,24 +61,6 @@ internal class DatabaseLogger: LoggerProtocol {
)
}
- /// Sets the minimum severity level that will be logged.
- ///
- /// Messages below this level will be ignored.
- ///
- /// - Parameter severity: The minimum `LogSeverity` to allow through.
- public func setMinSeverity(_ severity: LogSeverity) {
- logger.setMinSeverity(severity)
- }
-
- /// Sets the list of log writers that will receive log messages.
- ///
- /// This updates both the internal writer list and the Kermit logger's configuration.
- ///
- /// - Parameter writers: An array of Swift `LogWriterProtocol` implementations.
- public func setWriters(_ writers: [any LogWriterProtocol]) {
- logger.setWriters(writers)
- }
-
/// Logs a debug-level message.
public func debug(_ message: String, tag: String?) {
logger.debug(message, tag: tag)
diff --git a/Sources/PowerSync/LoggerProtocol.swift b/Sources/PowerSync/LoggerProtocol.swift
index 105f0a1..f2c3396 100644
--- a/Sources/PowerSync/LoggerProtocol.swift
+++ b/Sources/PowerSync/LoggerProtocol.swift
@@ -48,17 +48,6 @@ public protocol LogWriterProtocol {
///
/// Conformers provide logging APIs and manage attached log writers.
public protocol LoggerProtocol {
- /// Sets the minimum severity level to be logged.
- ///
- /// Log messages below this severity will be ignored.
- ///
- /// - Parameter severity: The minimum severity level to log.
- func setMinSeverity(_ severity: LogSeverity)
-
- /// Sets the list of log writers that will handle log output.
- ///
- /// - Parameter writers: An array of `LogWriterProtocol` conformers.
- func setWriters(_ writers: [LogWriterProtocol])
/// Logs an informational message.
///
/// - Parameters: