Skip to content

Commit cac181b

Browse files
leogdionclaude
andcommitted
refactor: migrate from swift-log to BushelKit FelinePine logging
Replace custom BushelLogger (swift-log wrapper) with BushelKit's FelinePine logging system. This consolidates logging infrastructure across the project. Changes: - Remove swift-log dependency, add BushelLogging from BushelKit - Create ConsoleOutput utility for user-facing CLI messages - Migrate all types to use Loggable protocol with Self.logger - Map subsystems to categories: cloudKit→.data, dataSource→.hub, sync→.application - Remove custom logging features (emoji, explain() method) - Delete Logger.swift All services, fetchers, and CLI commands now use standard FelinePine logging. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
1 parent b99b868 commit cac181b

File tree

13 files changed

+231
-214
lines changed

13 files changed

+231
-214
lines changed

Package.swift

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -95,8 +95,7 @@ let package = Package(
9595
.package(url: "https://github.com/brightdigit/IPSWDownloads.git", from: "1.0.0"),
9696
.package(url: "https://github.com/brightdigit/BushelKit.git", from: "3.0.0-alpha.1"),
9797
.package(url: "https://github.com/scinfu/SwiftSoup.git", from: "2.6.0"),
98-
.package(url: "https://github.com/apple/swift-argument-parser.git", from: "1.5.0"),
99-
.package(url: "https://github.com/apple/swift-log.git", from: "1.0.0")
98+
.package(url: "https://github.com/apple/swift-argument-parser.git", from: "1.5.0")
10099
],
101100
targets: [
102101
.target(
@@ -105,7 +104,7 @@ let package = Package(
105104
.product(name: "MistKit", package: "MistKit"),
106105
.product(name: "IPSWDownloads", package: "IPSWDownloads"),
107106
.product(name: "SwiftSoup", package: "SwiftSoup"),
108-
.product(name: "Logging", package: "swift-log")
107+
.product(name: "BushelLogging", package: "BushelKit")
109108
],
110109
swiftSettings: swiftSettings
111110
),

Sources/BushelCloudCLI/Commands/ClearCommand.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -68,8 +68,8 @@ struct ClearCommand: AsyncParsableCommand {
6868
// MARK: - Execution
6969

7070
mutating func run() async throws {
71-
// Enable verbose logging if requested
72-
BushelLogger.isVerbose = verbose
71+
// Enable verbose console output if requested
72+
ConsoleOutput.isVerbose = verbose
7373

7474
// Get Server-to-Server credentials from environment if not provided
7575
let resolvedKeyID =

Sources/BushelCloudCLI/Commands/ExportCommand.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -76,8 +76,8 @@ struct ExportCommand: AsyncParsableCommand {
7676
// MARK: - Execution
7777

7878
mutating func run() async throws {
79-
// Enable verbose logging if requested
80-
BushelLogger.isVerbose = verbose
79+
// Enable verbose console output if requested
80+
ConsoleOutput.isVerbose = verbose
8181

8282
// Get Server-to-Server credentials from environment if not provided
8383
let resolvedKeyID =

Sources/BushelCloudCLI/Commands/SyncCommand.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -99,8 +99,8 @@ struct SyncCommand: AsyncParsableCommand {
9999
// MARK: - Execution
100100

101101
mutating func run() async throws {
102-
// Enable verbose logging if requested
103-
BushelLogger.isVerbose = verbose
102+
// Enable verbose console output if requested
103+
ConsoleOutput.isVerbose = verbose
104104

105105
// Get Server-to-Server credentials from environment if not provided
106106
let resolvedKeyID =

Sources/BushelCloudKit/CloudKit/BushelCloudKitService.swift

Lines changed: 20 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929

3030
public import Foundation
3131
public import MistKit
32+
public import BushelLogging
3233

3334
/// CloudKit service wrapper for Bushel demo operations
3435
///
@@ -112,26 +113,24 @@ public struct BushelCloudKitService: Sendable, RecordManaging, CloudKitRecordCol
112113
let batches = operations.chunked(into: batchSize)
113114

114115
print("Syncing \(operations.count) \(recordType) record(s) in \(batches.count) batch(es)...")
115-
BushelLogger.verbose(
116-
"CloudKit batch limit: 200 operations/request. Using \(batches.count) batch(es) for \(operations.count) records.",
117-
subsystem: BushelLogger.cloudKit
116+
Self.logger.debug(
117+
"CloudKit batch limit: 200 operations/request. Using \(batches.count) batch(es) for \(operations.count) records."
118118
)
119119

120120
var totalSucceeded = 0
121121
var totalFailed = 0
122122

123123
for (index, batch) in batches.enumerated() {
124124
print(" Batch \(index + 1)/\(batches.count): \(batch.count) records...")
125-
BushelLogger.verbose(
126-
"Calling MistKit service.modifyRecords() with \(batch.count) RecordOperation objects",
127-
subsystem: BushelLogger.cloudKit
125+
Self.logger.debug(
126+
"Calling MistKit service.modifyRecords() with \(batch.count) RecordOperation objects"
128127
)
129128

130129
let results = try await service.modifyRecords(batch)
131130

132-
BushelLogger.verbose(
133-
"Received \(results.count) RecordInfo responses from CloudKit",
134-
subsystem: BushelLogger.cloudKit)
131+
Self.logger.debug(
132+
"Received \(results.count) RecordInfo responses from CloudKit"
133+
)
135134

136135
// Filter out error responses using isError property
137136
let successfulRecords = results.filter { !$0.isError }
@@ -147,14 +146,14 @@ public struct BushelCloudKitService: Sendable, RecordManaging, CloudKitRecordCol
147146
// Log error details in verbose mode
148147
let errorRecords = results.filter { $0.isError }
149148
for errorRecord in errorRecords {
150-
BushelLogger.verbose(
151-
"Error: recordName=\(errorRecord.recordName), reason=\(errorRecord.recordType)",
152-
subsystem: BushelLogger.cloudKit
149+
Self.logger.debug(
150+
"Error: recordName=\(errorRecord.recordName), reason=\(errorRecord.recordType)"
153151
)
154152
}
155153
} else {
156-
BushelLogger.success(
157-
"CloudKit confirmed \(successfulRecords.count) records", subsystem: BushelLogger.cloudKit)
154+
Self.logger.info(
155+
"CloudKit confirmed \(successfulRecords.count) records"
156+
)
158157
}
159158
}
160159

@@ -164,10 +163,14 @@ public struct BushelCloudKitService: Sendable, RecordManaging, CloudKitRecordCol
164163

165164
if totalFailed > 0 {
166165
print(" ❌ Failed: \(totalFailed) operations")
167-
BushelLogger.explain(
168-
"Use --verbose flag to see CloudKit error details (serverErrorCode, reason, etc.)",
169-
subsystem: BushelLogger.cloudKit
166+
Self.logger.debug(
167+
"Use --verbose flag to see CloudKit error details (serverErrorCode, reason, etc.)"
170168
)
171169
}
172170
}
173171
}
172+
173+
// MARK: - Loggable Conformance
174+
extension BushelCloudKitService: Loggable {
175+
public static let loggingCategory: BushelLogging.Category = .data
176+
}

Sources/BushelCloudKit/CloudKit/SyncEngine.swift

Lines changed: 61 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929

3030
public import Foundation
3131
public import MistKit
32+
public import BushelLogging
3233

3334
/// Orchestrates the complete sync process from data sources to CloudKit
3435
///
@@ -79,33 +80,32 @@ public struct SyncEngine: Sendable {
7980
/// Execute full sync from all data sources to CloudKit
8081
public func sync(options: SyncOptions = SyncOptions()) async throws -> SyncResult {
8182
print("\n" + String(repeating: "=", count: 60))
82-
BushelLogger.info("🔄 Starting Bushel CloudKit Sync", subsystem: BushelLogger.sync)
83+
ConsoleOutput.info("Starting Bushel CloudKit Sync")
8384
print(String(repeating: "=", count: 60))
85+
Self.logger.info("Sync started")
8486

8587
if options.dryRun {
86-
BushelLogger.info(
87-
"🧪 DRY RUN MODE - No changes will be made to CloudKit", subsystem: BushelLogger.sync)
88+
ConsoleOutput.info("DRY RUN MODE - No changes will be made to CloudKit")
89+
Self.logger.info("Sync running in dry-run mode")
8890
}
8991

90-
BushelLogger.explain(
91-
"This sync demonstrates MistKit's Server-to-Server authentication and bulk record operations",
92-
subsystem: BushelLogger.sync
92+
Self.logger.debug(
93+
"Using MistKit Server-to-Server authentication for bulk record operations"
9394
)
9495

9596
// Step 1: Fetch from all data sources
9697
print("\n📥 Step 1: Fetching data from external sources...")
97-
BushelLogger.verbose(
98-
"Initializing data source pipeline to fetch from ipsw.me, TheAppleWiki, MESU, and other sources",
99-
subsystem: BushelLogger.dataSource)
98+
Self.logger.debug(
99+
"Initializing data source pipeline to fetch from ipsw.me, TheAppleWiki, MESU, and other sources"
100+
)
100101

101102
let fetchResult = try await pipeline.fetch(options: options.pipelineOptions)
102103

103-
BushelLogger.verbose(
104-
"Data fetch complete. Beginning deduplication and merge phase.",
105-
subsystem: BushelLogger.dataSource)
106-
BushelLogger.explain(
107-
"Multiple data sources may have overlapping data. The pipeline deduplicates by version+build number.",
108-
subsystem: BushelLogger.dataSource
104+
Self.logger.debug(
105+
"Data fetch complete. Beginning deduplication and merge phase."
106+
)
107+
Self.logger.debug(
108+
"Multiple data sources may have overlapping data. The pipeline deduplicates by version+build number."
109109
)
110110

111111
let stats = SyncResult(
@@ -124,18 +124,18 @@ public struct SyncEngine: Sendable {
124124
print(" ─────────────────────")
125125
print(" Total: \(totalRecords) records")
126126

127-
BushelLogger.verbose(
128-
"Records ready for CloudKit upload: \(totalRecords) total", subsystem: BushelLogger.sync)
127+
Self.logger.debug(
128+
"Records ready for CloudKit upload: \(totalRecords) total"
129+
)
129130

130131
// Step 2: Sync to CloudKit (unless dry run)
131132
if !options.dryRun {
132133
print("\n☁️ Step 2: Syncing to CloudKit...")
133-
BushelLogger.verbose(
134-
"Using MistKit to batch upload records to CloudKit public database",
135-
subsystem: BushelLogger.cloudKit)
136-
BushelLogger.explain(
137-
"MistKit handles authentication, batching (200 records/request), and error handling automatically",
138-
subsystem: BushelLogger.cloudKit
134+
Self.logger.debug(
135+
"Using MistKit to batch upload records to CloudKit public database"
136+
)
137+
Self.logger.debug(
138+
"MistKit handles authentication, batching (200 records/request), and error handling automatically"
139139
)
140140

141141
// Sync in dependency order: SwiftVersion → RestoreImage → XcodeVersion
@@ -151,73 +151,79 @@ public struct SyncEngine: Sendable {
151151
print("\(stats.restoreImagesCount) restore images")
152152
print("\(stats.xcodeVersionsCount) Xcode versions")
153153
print("\(stats.swiftVersionsCount) Swift versions")
154-
BushelLogger.verbose(
155-
"Dry run mode: No CloudKit operations performed", subsystem: BushelLogger.sync)
154+
Self.logger.debug(
155+
"Dry run mode: No CloudKit operations performed"
156+
)
156157
}
157158

158159
print("\n" + String(repeating: "=", count: 60))
159-
BushelLogger.success("Sync completed successfully!", subsystem: BushelLogger.sync)
160+
ConsoleOutput.success("Sync completed successfully!")
160161
print(String(repeating: "=", count: 60))
162+
Self.logger.info("Sync completed successfully")
161163

162164
return stats
163165
}
164166

165167
/// Delete all records from CloudKit
166168
public func clear() async throws {
167169
print("\n" + String(repeating: "=", count: 60))
168-
BushelLogger.info("🗑️ Clearing all CloudKit data", subsystem: BushelLogger.cloudKit)
170+
ConsoleOutput.info("Clearing all CloudKit data")
169171
print(String(repeating: "=", count: 60))
172+
Self.logger.info("Clearing all CloudKit records")
170173

171174
try await cloudKitService.deleteAllRecords()
172175

173176
print("\n" + String(repeating: "=", count: 60))
174-
BushelLogger.success("Clear completed successfully!", subsystem: BushelLogger.sync)
177+
ConsoleOutput.success("Clear completed successfully!")
175178
print(String(repeating: "=", count: 60))
179+
Self.logger.info("Clear completed successfully")
176180
}
177181

178182
/// Export all records from CloudKit to a structured format
179183
public func export() async throws -> ExportResult {
180184
print("\n" + String(repeating: "=", count: 60))
181-
BushelLogger.info("📤 Exporting data from CloudKit", subsystem: BushelLogger.cloudKit)
185+
ConsoleOutput.info("Exporting data from CloudKit")
182186
print(String(repeating: "=", count: 60))
187+
Self.logger.info("Exporting CloudKit data")
183188

184-
BushelLogger.explain(
185-
"Using MistKit's queryRecords() to fetch all records of each type from the public database",
186-
subsystem: BushelLogger.cloudKit
189+
Self.logger.debug(
190+
"Using MistKit queryRecords() to fetch all records of each type from the public database"
187191
)
188192

189193
print("\n📥 Fetching RestoreImage records...")
190-
BushelLogger.verbose(
191-
"Querying CloudKit for recordType: 'RestoreImage' with limit: 1000",
192-
subsystem: BushelLogger.cloudKit)
194+
Self.logger.debug(
195+
"Querying CloudKit for recordType: 'RestoreImage' with limit: 1000"
196+
)
193197
let restoreImages = try await cloudKitService.queryRecords(recordType: "RestoreImage")
194-
BushelLogger.verbose(
195-
"Retrieved \(restoreImages.count) RestoreImage records", subsystem: BushelLogger.cloudKit)
198+
Self.logger.debug(
199+
"Retrieved \(restoreImages.count) RestoreImage records"
200+
)
196201

197202
print("📥 Fetching XcodeVersion records...")
198-
BushelLogger.verbose(
199-
"Querying CloudKit for recordType: 'XcodeVersion' with limit: 1000",
200-
subsystem: BushelLogger.cloudKit)
203+
Self.logger.debug(
204+
"Querying CloudKit for recordType: 'XcodeVersion' with limit: 1000"
205+
)
201206
let xcodeVersions = try await cloudKitService.queryRecords(recordType: "XcodeVersion")
202-
BushelLogger.verbose(
203-
"Retrieved \(xcodeVersions.count) XcodeVersion records", subsystem: BushelLogger.cloudKit)
207+
Self.logger.debug(
208+
"Retrieved \(xcodeVersions.count) XcodeVersion records"
209+
)
204210

205211
print("📥 Fetching SwiftVersion records...")
206-
BushelLogger.verbose(
207-
"Querying CloudKit for recordType: 'SwiftVersion' with limit: 1000",
208-
subsystem: BushelLogger.cloudKit)
212+
Self.logger.debug(
213+
"Querying CloudKit for recordType: 'SwiftVersion' with limit: 1000"
214+
)
209215
let swiftVersions = try await cloudKitService.queryRecords(recordType: "SwiftVersion")
210-
BushelLogger.verbose(
211-
"Retrieved \(swiftVersions.count) SwiftVersion records", subsystem: BushelLogger.cloudKit)
216+
Self.logger.debug(
217+
"Retrieved \(swiftVersions.count) SwiftVersion records"
218+
)
212219

213220
print("\n✅ Exported:")
214221
print("\(restoreImages.count) restore images")
215222
print("\(xcodeVersions.count) Xcode versions")
216223
print("\(swiftVersions.count) Swift versions")
217224

218-
BushelLogger.explain(
219-
"MistKit returns RecordInfo structs with record metadata. Use .fields to access CloudKit field values.",
220-
subsystem: BushelLogger.cloudKit
225+
Self.logger.debug(
226+
"MistKit returns RecordInfo structs with record metadata. Use .fields to access CloudKit field values."
221227
)
222228

223229
return ExportResult(
@@ -255,3 +261,8 @@ public struct SyncEngine: Sendable {
255261
}
256262
}
257263
}
264+
265+
// MARK: - Loggable Conformance
266+
extension SyncEngine: Loggable {
267+
public static let loggingCategory: BushelLogging.Category = .application
268+
}

0 commit comments

Comments
 (0)