Skip to content

Commit

Permalink
enables configurable log filtering on Automerge-swift (#47)
Browse files Browse the repository at this point in the history
  • Loading branch information
heckj committed Jul 14, 2023
1 parent 1e53b3a commit 807ab7c
Show file tree
Hide file tree
Showing 10 changed files with 151 additions and 59 deletions.
24 changes: 13 additions & 11 deletions Sources/Automerge/Codable/Document+retrieveObjectId.swift
Original file line number Diff line number Diff line change
@@ -1,16 +1,5 @@
import os // for structured logging

@usableFromInline
func tracePrint(indent: Int = 0, _ stringval: String) {
#if DEBUG
if #available(macOS 11, iOS 14, *) {
let logger = Logger(subsystem: "Automerge", category: "AutomergeEncoder")
let prefix = String(repeating: " ", count: indent)
logger.debug("\(prefix, privacy: .public)\(stringval, privacy: .public)")
}
#endif
}

// // MARK: Cache for Object Id Lookups
//
// typealias CacheKey = [AnyCodingKey]
Expand All @@ -23,6 +12,19 @@ func tracePrint(indent: Int = 0, _ stringval: String) {
// }

extension Document {
@usableFromInline
func tracePrint(indent: Int = 0, _ stringval: String) {
#if DEBUG
if reportingLogLevel >= .tracing {
if #available(macOS 11, iOS 14, *) {
let logger = Logger(subsystem: "Automerge", category: "AutomergeEncoder")
let prefix = String(repeating: " ", count: indent)
logger.debug("\(prefix, privacy: .public)\(stringval, privacy: .public)")
}
}
#endif
}

/// Returns an Automerge objectId for the location within the document.
///
/// The function looks up an Automerge Object Id for a specific schema location, optionally creating schema if
Expand Down
15 changes: 12 additions & 3 deletions Sources/Automerge/Codable/Encoding/AutomergeEncoder.swift
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,18 @@ public struct AutomergeEncoder {
var doc: Document
var schemaStrategy: SchemaStrategy
var cautiousWrite: Bool
let logLevel: LogVerbosity

public init(doc: Document, strategy: SchemaStrategy = .createWhenNeeded, cautiousWrite: Bool = false) {
public init(
doc: Document,
strategy: SchemaStrategy = .createWhenNeeded,
cautiousWrite: Bool = false,
reportingLoglevel: LogVerbosity = .errorOnly
) {
self.doc = doc
schemaStrategy = strategy
self.cautiousWrite = cautiousWrite
logLevel = reportingLoglevel
}

public func encode<T: Encodable>(_ value: T?) throws {
Expand All @@ -25,7 +32,8 @@ public struct AutomergeEncoder {
codingPath: [],
doc: doc,
strategy: schemaStrategy,
cautiousWrite: cautiousWrite
cautiousWrite: cautiousWrite,
logLevel: logLevel
)
try value.encode(to: encoder)
}
Expand All @@ -36,7 +44,8 @@ public struct AutomergeEncoder {
codingPath: path,
doc: doc,
strategy: schemaStrategy,
cautiousWrite: cautiousWrite
cautiousWrite: cautiousWrite,
logLevel: logLevel
)
try value.encode(to: encoder)
}
Expand Down
8 changes: 6 additions & 2 deletions Sources/Automerge/Codable/Encoding/AutomergeEncoderImpl.swift
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import os

/// The internal implementation of AutomergeEncoder.
///
/// Instances of the class capture one of the various kinds of schema value types - single value, array, or object.
Expand All @@ -8,7 +10,7 @@ final class AutomergeEncoderImpl {
let document: Document
let schemaStrategy: SchemaStrategy
let cautiousWrite: Bool

let reportingLogLevel: LogVerbosity
// indicator that the singleValue has written a value
var singleValueWritten: Bool = false

Expand All @@ -17,13 +19,15 @@ final class AutomergeEncoderImpl {
codingPath: [CodingKey],
doc: Document,
strategy: SchemaStrategy,
cautiousWrite: Bool
cautiousWrite: Bool,
logLevel: LogVerbosity
) {
self.userInfo = userInfo
self.codingPath = codingPath
document = doc
schemaStrategy = strategy
self.cautiousWrite = cautiousWrite
reportingLogLevel = logLevel
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,9 @@ struct AutomergeKeyedEncodingContainer<K: CodingKey>: KeyedEncodingContainerProt
}
if #available(macOS 11, iOS 14, *) {
let logger = Logger(subsystem: "Automerge", category: "AutomergeEncoder")
logger.debug("Establishing Keyed Encoding Container for path \(codingPath.map { AnyCodingKey($0) })")
if impl.reportingLogLevel >= LogVerbosity.debug {
logger.debug("Establishing Keyed Encoding Container for path \(codingPath.map { AnyCodingKey($0) })")
}
}
}

Expand Down Expand Up @@ -279,7 +281,8 @@ struct AutomergeKeyedEncodingContainer<K: CodingKey>: KeyedEncodingContainerProt
codingPath: newPath,
doc: document,
strategy: impl.schemaStrategy,
cautiousWrite: impl.cautiousWrite
cautiousWrite: impl.cautiousWrite,
logLevel: impl.reportingLogLevel
)
switch T.self {
case is Date.Type:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,12 @@ struct AutomergeSingleValueEncodingContainer: SingleValueEncodingContainer {
}
if #available(macOS 11, iOS 14, *) {
let logger = Logger(subsystem: "Automerge", category: "AutomergeEncoder")
logger.debug("Establishing Single Value Encoding Container for path \(codingPath.map { AnyCodingKey($0) })")
if impl.reportingLogLevel >= LogVerbosity.debug {
logger
.debug(
"Establishing Single Value Encoding Container for path \(codingPath.map { AnyCodingKey($0) })"
)
}
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,9 @@ struct AutomergeUnkeyedEncodingContainer: UnkeyedEncodingContainer {
}
if #available(macOS 11, iOS 14, *) {
let logger = Logger(subsystem: "Automerge", category: "AutomergeEncoder")
logger.debug("Establishing Unkeyed Encoding Container for path \(codingPath.map { AnyCodingKey($0) })")
if impl.reportingLogLevel >= LogVerbosity.debug {
logger.debug("Establishing Unkeyed Encoding Container for path \(codingPath.map { AnyCodingKey($0) })")
}
}
}

Expand All @@ -62,7 +64,8 @@ struct AutomergeUnkeyedEncodingContainer: UnkeyedEncodingContainer {
codingPath: newPath,
doc: document,
strategy: impl.schemaStrategy,
cautiousWrite: impl.cautiousWrite
cautiousWrite: impl.cautiousWrite,
logLevel: impl.reportingLogLevel
)
guard let objectId = objectId else {
throw reportBestError()
Expand Down
10 changes: 7 additions & 3 deletions Sources/Automerge/Document.swift
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import Foundation
public class Document: @unchecked Sendable {
private var doc: WrappedDoc
fileprivate let queue = DispatchQueue(label: "automerge-sync-queue", qos: .userInteractive)
internal var reportingLogLevel: LogVerbosity

/// The actor ID of this document
public var actor: ActorId {
Expand All @@ -34,8 +35,9 @@ public class Document: @unchecked Sendable {
}

/// Create an new empty document with a random actor ID
public init() {
public init(logLevel: LogVerbosity = .errorOnly) {
doc = WrappedDoc(Doc())
self.reportingLogLevel = logLevel
}

/// Load the document in `bytes`
Expand All @@ -44,12 +46,14 @@ public class Document: @unchecked Sendable {
/// concatenation of many calls to ``encodeChangesSince(heads:)``, or
/// ``encodeNewChanges()`` or the concatenation of any of those, or really
/// any sequence of bytes containing valid encodings of automerge changes.
public init(_ bytes: Data) throws {
public init(_ bytes: Data, logLevel: LogVerbosity = .errorOnly) throws {
doc = try WrappedDoc { try Doc.load(bytes: Array(bytes)) }
self.reportingLogLevel = logLevel
}

private init(doc: Doc) {
private init(doc: Doc, logLevel: LogVerbosity = .errorOnly) {
self.doc = WrappedDoc(doc)
self.reportingLogLevel = logLevel
}

/// Set or update the value at `key` in the map `obj` to `value`
Expand Down
27 changes: 27 additions & 0 deletions Sources/Automerge/LogVerbosity.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
/// A type that indicates the amount of logging to be exposed from the Automerge library.
public enum LogVerbosity: Int, Comparable, Equatable {
// DEVNOTE(heckj): Using an internal/custom enumeration to indicate
// these values because this library supports back to macOS 11.15
// when Unified Logging wasn't available on related platforms.
//
// In addition, I'm using this as a comparator before logging anything
// because the default os.Logger implementation made available to
// both AppKit and UIKit doesn't include any filtering capability by level.

/// Determines whether the first verbosity level is less verbose than the second.
/// - Parameters:
/// - lhs: The first verbosity level to compare.
/// - rhs: The second verbosity level to compare.
/// - Returns: Returns true if the first verbosity level is less than the second.
public static func < (lhs: LogVerbosity, rhs: LogVerbosity) -> Bool {
lhs.rawValue < rhs.rawValue
}

// loosely matching the levels from https://datatracker.ietf.org/doc/html/rfc5424
/// Log errors only.
case errorOnly = 3
/// Logs include debugging and informational detail.
case debug = 6
/// Logs include all debugging and additional tracing details.
case tracing = 8
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,8 @@ final class AutomergeKeyEncoderImplTests: XCTestCase {
codingPath: [],
doc: doc,
strategy: .createWhenNeeded,
cautiousWrite: false
cautiousWrite: false,
logLevel: .errorOnly
)
rootKeyedContainer = impl.container(keyedBy: SampleCodingKeys.self)

Expand All @@ -26,7 +27,8 @@ final class AutomergeKeyEncoderImplTests: XCTestCase {
codingPath: [],
doc: doc,
strategy: .createWhenNeeded,
cautiousWrite: true
cautiousWrite: true,
logLevel: .errorOnly
)
cautiousKeyedContainer = cautious.container(keyedBy: SampleCodingKeys.self)
}
Expand Down Expand Up @@ -64,7 +66,8 @@ final class AutomergeKeyEncoderImplTests: XCTestCase {
codingPath: [AnyCodingKey("nothere")],
doc: doc,
strategy: .readonly,
cautiousWrite: false
cautiousWrite: false,
logLevel: .errorOnly
)
rootKeyedContainer = impl.container(keyedBy: SampleCodingKeys.self)
XCTAssertThrowsError(try rootKeyedContainer.encode(Float(3.4), forKey: .value))
Expand Down Expand Up @@ -259,7 +262,8 @@ final class AutomergeKeyEncoderImplTests: XCTestCase {
codingPath: [AnyCodingKey("nothere")],
doc: doc,
strategy: .readonly,
cautiousWrite: false
cautiousWrite: false,
logLevel: .errorOnly
)
rootKeyedContainer = impl.container(keyedBy: SampleCodingKeys.self)
XCTAssertThrowsError(try rootKeyedContainer.encode(true, forKey: .value))
Expand All @@ -271,7 +275,8 @@ final class AutomergeKeyEncoderImplTests: XCTestCase {
codingPath: [AnyCodingKey("nothere")],
doc: doc,
strategy: .readonly,
cautiousWrite: false
cautiousWrite: false,
logLevel: .errorOnly
)
rootKeyedContainer = impl.container(keyedBy: SampleCodingKeys.self)
XCTAssertThrowsError(try rootKeyedContainer.encode(Double(8.16), forKey: .value))
Expand All @@ -283,7 +288,8 @@ final class AutomergeKeyEncoderImplTests: XCTestCase {
codingPath: [AnyCodingKey("nothere")],
doc: doc,
strategy: .readonly,
cautiousWrite: false
cautiousWrite: false,
logLevel: .errorOnly
)
rootKeyedContainer = impl.container(keyedBy: SampleCodingKeys.self)
XCTAssertThrowsError(try rootKeyedContainer.encode(Int(8), forKey: .value))
Expand All @@ -295,7 +301,8 @@ final class AutomergeKeyEncoderImplTests: XCTestCase {
codingPath: [AnyCodingKey("nothere")],
doc: doc,
strategy: .readonly,
cautiousWrite: false
cautiousWrite: false,
logLevel: .errorOnly
)
rootKeyedContainer = impl.container(keyedBy: SampleCodingKeys.self)
XCTAssertThrowsError(try rootKeyedContainer.encode(Int8(8), forKey: .value))
Expand All @@ -307,7 +314,8 @@ final class AutomergeKeyEncoderImplTests: XCTestCase {
codingPath: [AnyCodingKey("nothere")],
doc: doc,
strategy: .readonly,
cautiousWrite: false
cautiousWrite: false,
logLevel: .errorOnly
)
rootKeyedContainer = impl.container(keyedBy: SampleCodingKeys.self)
XCTAssertThrowsError(try rootKeyedContainer.encode(Int16(8), forKey: .value))
Expand All @@ -319,7 +327,8 @@ final class AutomergeKeyEncoderImplTests: XCTestCase {
codingPath: [AnyCodingKey("nothere")],
doc: doc,
strategy: .readonly,
cautiousWrite: false
cautiousWrite: false,
logLevel: .errorOnly
)
rootKeyedContainer = impl.container(keyedBy: SampleCodingKeys.self)
XCTAssertThrowsError(try rootKeyedContainer.encode(Int32(8), forKey: .value))
Expand All @@ -331,7 +340,8 @@ final class AutomergeKeyEncoderImplTests: XCTestCase {
codingPath: [AnyCodingKey("nothere")],
doc: doc,
strategy: .readonly,
cautiousWrite: false
cautiousWrite: false,
logLevel: .errorOnly
)
rootKeyedContainer = impl.container(keyedBy: SampleCodingKeys.self)
XCTAssertThrowsError(try rootKeyedContainer.encode(Int64(8), forKey: .value))
Expand All @@ -343,7 +353,8 @@ final class AutomergeKeyEncoderImplTests: XCTestCase {
codingPath: [AnyCodingKey("nothere")],
doc: doc,
strategy: .readonly,
cautiousWrite: false
cautiousWrite: false,
logLevel: .errorOnly
)
rootKeyedContainer = impl.container(keyedBy: SampleCodingKeys.self)
XCTAssertThrowsError(try rootKeyedContainer.encode(UInt(8), forKey: .value))
Expand All @@ -355,7 +366,8 @@ final class AutomergeKeyEncoderImplTests: XCTestCase {
codingPath: [AnyCodingKey("nothere")],
doc: doc,
strategy: .readonly,
cautiousWrite: false
cautiousWrite: false,
logLevel: .errorOnly
)
rootKeyedContainer = impl.container(keyedBy: SampleCodingKeys.self)
XCTAssertThrowsError(try rootKeyedContainer.encode(UInt8(8), forKey: .value))
Expand All @@ -367,7 +379,8 @@ final class AutomergeKeyEncoderImplTests: XCTestCase {
codingPath: [AnyCodingKey("nothere")],
doc: doc,
strategy: .readonly,
cautiousWrite: false
cautiousWrite: false,
logLevel: .errorOnly
)
rootKeyedContainer = impl.container(keyedBy: SampleCodingKeys.self)
XCTAssertThrowsError(try rootKeyedContainer.encode(UInt16(8), forKey: .value))
Expand All @@ -379,7 +392,8 @@ final class AutomergeKeyEncoderImplTests: XCTestCase {
codingPath: [AnyCodingKey("nothere")],
doc: doc,
strategy: .readonly,
cautiousWrite: false
cautiousWrite: false,
logLevel: .errorOnly
)
rootKeyedContainer = impl.container(keyedBy: SampleCodingKeys.self)
XCTAssertThrowsError(try rootKeyedContainer.encode(UInt32(8), forKey: .value))
Expand All @@ -391,7 +405,8 @@ final class AutomergeKeyEncoderImplTests: XCTestCase {
codingPath: [AnyCodingKey("nothere")],
doc: doc,
strategy: .readonly,
cautiousWrite: false
cautiousWrite: false,
logLevel: .errorOnly
)
rootKeyedContainer = impl.container(keyedBy: SampleCodingKeys.self)
XCTAssertThrowsError(try rootKeyedContainer.encode(UInt64(8), forKey: .value))
Expand All @@ -407,7 +422,8 @@ final class AutomergeKeyEncoderImplTests: XCTestCase {
codingPath: [AnyCodingKey("nothere")],
doc: doc,
strategy: .readonly,
cautiousWrite: false
cautiousWrite: false,
logLevel: .errorOnly
)
rootKeyedContainer = impl.container(keyedBy: SampleCodingKeys.self)
XCTAssertThrowsError(try rootKeyedContainer.encode(SimpleStruct(a: "foo"), forKey: .value))
Expand Down
Loading

0 comments on commit 807ab7c

Please sign in to comment.