Skip to content

Commit d762e3f

Browse files
committed
Improve OSLog support with backward compatibility
1 parent 853c8eb commit d762e3f

11 files changed

Lines changed: 1371 additions & 178 deletions

MIGRATION_GUIDE.md

Lines changed: 248 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,248 @@
1+
# PrettyLogger Migration Guide
2+
3+
This guide shows how easy it is to migrate from the old print-based API to the new OSLog-based API while keeping the same function names.
4+
5+
## Overview
6+
7+
PrettyLogger now uses Apple's unified logging system (OSLog) instead of `print` statements, providing better performance, privacy controls, and integration with system tools. The best part? **You keep using the same function names!**
8+
9+
## Quick Migration
10+
11+
### What Changes
12+
- **Function signatures**: New parameters for `category` and `privacy`
13+
- **Parameter format**: String interpolation instead of variadic parameters
14+
- **Logging backend**: OSLog instead of print statements
15+
16+
### What Stays the Same
17+
- **Function names**: `logInfo`, `logError`, `logWarning`, etc.
18+
- **Import statement**: `import PrettyLogger`
19+
- **Basic usage patterns**
20+
21+
## Step-by-Step Migration
22+
23+
### 1. Simple Messages (No Changes Needed!)
24+
25+
```swift
26+
// Before and After - EXACTLY THE SAME!
27+
logInfo("User logged in successfully")
28+
logError("Network connection failed")
29+
logWarning("Low memory warning")
30+
logDebug("Processing user data")
31+
```
32+
33+
### 2. Multiple Parameters → String Interpolation
34+
35+
```swift
36+
// Before (deprecated)
37+
logInfo("User:", username, "logged in at:", timestamp)
38+
logError("Error code:", errorCode, "message:", error.localizedDescription)
39+
40+
// After (recommended)
41+
logInfo("User: \(username) logged in at: \(timestamp)")
42+
logError("Error code: \(errorCode) message: \(error.localizedDescription)")
43+
```
44+
45+
### 3. Add Categories for Better Organization (Optional)
46+
47+
```swift
48+
// Before
49+
logInfo("API request completed")
50+
logError("Database connection failed")
51+
logDebug("Cache hit for key: user_123")
52+
53+
// After (enhanced with categories)
54+
logInfo("API request completed", category: "Network")
55+
logError("Database connection failed", category: "Database")
56+
logDebug("Cache hit for key: user_123", category: "Cache")
57+
```
58+
59+
### 4. Add Privacy Controls for Sensitive Data (Optional)
60+
61+
```swift
62+
// Before
63+
logDebug("Auth token: abc123xyz")
64+
logInfo("User email: user@example.com")
65+
66+
// After (with privacy)
67+
logDebug("Auth token: \(token)", category: "Auth", privacy: .private)
68+
logInfo("User email: \(email)", category: "Auth", privacy: .private)
69+
```
70+
71+
## Real-World Example
72+
73+
### Before (Old API)
74+
```swift
75+
class AuthManager {
76+
func login(email: String, password: String) -> Bool {
77+
logInfo("Starting login process")
78+
logDebug("Email:", email, "Password length:", password.count)
79+
80+
if email.isEmpty {
81+
logError("Login failed:", "Email is empty")
82+
return false
83+
}
84+
85+
// Simulate API call
86+
logTrace("Making API call to:", "/auth/login")
87+
88+
// Success
89+
logInfo("Login successful for user:", email)
90+
return true
91+
}
92+
}
93+
```
94+
95+
### After (New API)
96+
```swift
97+
class AuthManager {
98+
func login(email: String, password: String) -> Bool {
99+
logInfo("Starting login process", category: "Auth")
100+
logDebug("Email: \(email) Password length: \(password.count)",
101+
category: "Auth", privacy: .private)
102+
103+
if email.isEmpty {
104+
logError("Login failed: Email is empty", category: "Auth")
105+
return false
106+
}
107+
108+
// Simulate API call
109+
logTrace("Making API call to: /auth/login", category: "Network")
110+
111+
// Success
112+
logInfo("Login successful for user: \(email)",
113+
category: "Auth", privacy: .private)
114+
return true
115+
}
116+
}
117+
```
118+
119+
## Migration Strategies
120+
121+
### Strategy 1: Minimal Changes (Easiest)
122+
1. Replace variadic parameters with string interpolation
123+
2. Remove any `separator` and `terminator` parameters
124+
3. Done! Your logs now use OSLog
125+
126+
### Strategy 2: Gradual Enhancement (Recommended)
127+
1. Start with minimal changes (Strategy 1)
128+
2. Gradually add categories to group related logs
129+
3. Add privacy controls for sensitive data
130+
4. Test with Console.app and Instruments
131+
132+
### Strategy 3: Complete Modernization
133+
1. Apply minimal changes
134+
2. Define category constants
135+
3. Add comprehensive privacy controls
136+
4. Update log messages for better structure
137+
5. Add performance monitoring
138+
139+
## Category Best Practices
140+
141+
### Define Constants
142+
```swift
143+
extension String {
144+
static let auth = "Authentication"
145+
static let network = "Network"
146+
static let database = "Database"
147+
static let ui = "UserInterface"
148+
static let cache = "Cache"
149+
}
150+
151+
// Usage
152+
logError("Connection timeout", category: .network)
153+
logInfo("User authenticated", category: .auth)
154+
```
155+
156+
### Hierarchical Categories
157+
```swift
158+
// Use dot notation for subcategories
159+
logDebug("Cache miss", category: "Cache.User")
160+
logInfo("Database query", category: "Database.Read")
161+
logError("Network timeout", category: "Network.API")
162+
```
163+
164+
## Privacy Guidelines
165+
166+
| Data Type | Recommended Privacy | Example |
167+
|-----------|-------------------|---------|
168+
| User IDs | `.auto` | `logInfo("User \(userID) logged in", privacy: .auto)` |
169+
| Email addresses | `.private` | `logDebug("Email: \(email)", privacy: .private)` |
170+
| Passwords/Tokens | `.private` | `logTrace("Token refreshed", privacy: .private)` |
171+
| App version | `.public` | `logInfo("App version: \(version)", privacy: .public)` |
172+
| Error messages | `.auto` | `logError("Network error: \(error)", privacy: .auto)` |
173+
174+
## Testing Your Migration
175+
176+
### 1. Compile and Run
177+
```bash
178+
# Make sure everything compiles
179+
swift build
180+
181+
# Run your tests
182+
swift test
183+
```
184+
185+
### 2. Check Console.app
186+
1. Open Console.app on macOS
187+
2. Filter by your app's bundle identifier
188+
3. Verify logs appear with proper categories
189+
4. Test privacy settings
190+
191+
### 3. Use Command Line Tools
192+
```bash
193+
# Show logs from your app
194+
log show --predicate 'subsystem == "com.yourapp.bundleid"' --last 1h
195+
196+
# Filter by category
197+
log show --predicate 'category == "Network"' --last 30m
198+
```
199+
200+
## Common Issues and Solutions
201+
202+
### Issue: Too Many Parameters
203+
```swift
204+
// Problem: Old habit of many parameters
205+
logInfo("User:", user.id, "action:", action, "result:", result, "time:", time)
206+
207+
// Solution: Use string interpolation
208+
logInfo("User: \(user.id) action: \(action) result: \(result) time: \(time)")
209+
```
210+
211+
### Issue: Missing Privacy Controls
212+
```swift
213+
// Problem: Sensitive data exposed
214+
logDebug("Processing payment for card: \(cardNumber)")
215+
216+
// Solution: Mark as private
217+
logDebug("Processing payment for card: \(cardNumber)",
218+
category: "Payment", privacy: .private)
219+
```
220+
221+
### Issue: No Categories
222+
```swift
223+
// Problem: All logs mixed together
224+
logError("Database connection failed")
225+
logError("Network request timeout")
226+
227+
// Solution: Use categories
228+
logError("Database connection failed", category: "Database")
229+
logError("Network request timeout", category: "Network")
230+
```
231+
232+
## Benefits After Migration
233+
234+
**Better Performance**: OSLog is faster and more efficient than print
235+
**Privacy Controls**: Automatic handling of sensitive data
236+
**System Integration**: Works with Console.app, Instruments, and log command
237+
**Structured Logging**: Categories help organize and filter logs
238+
**Production Ready**: Proper log levels for release builds
239+
**Same Function Names**: No need to learn new API
240+
241+
## Need Help?
242+
243+
- Check `USAGE_EXAMPLES.md` for comprehensive examples
244+
- Use Console.app to verify your logs are working
245+
- Test privacy settings in release builds
246+
- Consider gradual migration for large codebases
247+
248+
The migration is designed to be as smooth as possible while providing modern logging capabilities!

Sources/Global.swift

Lines changed: 63 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -1,69 +1,95 @@
11
import Foundation
22

3-
@available(macOS 11.0, iOS 14.0, watchOS 7.0, tvOS 14.0, *)
4-
public func fatal(_ message: String, category: String? = nil, privacy: PrettyLoggerPrivacy = .auto) {
5-
PrettyLogger.shared.fatal(message, category: category, privacy: privacy)
3+
// MARK: - Primary OSLog-based API
4+
5+
public func logFatal(_ message: String, category: String? = nil, privacy: PrettyLoggerPrivacy = .auto) {
6+
PrettyLogger.shared.logFatal(message, category: category, privacy: privacy)
67
}
78

8-
@available(macOS 11.0, iOS 14.0, watchOS 7.0, tvOS 14.0, *)
9-
public func error(_ message: String, category: String? = nil, privacy: PrettyLoggerPrivacy = .auto) {
10-
PrettyLogger.shared.error(message, category: category, privacy: privacy)
9+
public func logError(_ message: String, category: String? = nil, privacy: PrettyLoggerPrivacy = .auto) {
10+
PrettyLogger.shared.logError(message, category: category, privacy: privacy)
1111
}
1212

13-
@available(macOS 11.0, iOS 14.0, watchOS 7.0, tvOS 14.0, *)
14-
public func warning(_ message: String, category: String? = nil, privacy: PrettyLoggerPrivacy = .auto) {
15-
PrettyLogger.shared.warning(message, category: category, privacy: privacy)
13+
public func logWarning(_ message: String, category: String? = nil, privacy: PrettyLoggerPrivacy = .auto) {
14+
PrettyLogger.shared.logWarning(message, category: category, privacy: privacy)
1615
}
1716

18-
@available(macOS 11.0, iOS 14.0, watchOS 7.0, tvOS 14.0, *)
19-
public func info(_ message: String, category: String? = nil, privacy: PrettyLoggerPrivacy = .auto) {
20-
PrettyLogger.shared.info(message, category: category, privacy: privacy)
17+
public func logInfo(_ message: String, category: String? = nil, privacy: PrettyLoggerPrivacy = .auto) {
18+
PrettyLogger.shared.logInfo(message, category: category, privacy: privacy)
2119
}
2220

23-
@available(macOS 11.0, iOS 14.0, watchOS 7.0, tvOS 14.0, *)
24-
public func debug(_ message: String, category: String? = nil, privacy: PrettyLoggerPrivacy = .auto) {
25-
PrettyLogger.shared.debug(message, category: category, privacy: privacy)
21+
public func logDebug(_ message: String, category: String? = nil, privacy: PrettyLoggerPrivacy = .auto) {
22+
PrettyLogger.shared.logDebug(message, category: category, privacy: privacy)
2623
}
2724

28-
@available(macOS 11.0, iOS 14.0, watchOS 7.0, tvOS 14.0, *)
29-
public func trace(_ message: String, category: String? = nil, privacy: PrettyLoggerPrivacy = .auto) {
30-
PrettyLogger.shared.trace(message, category: category, privacy: privacy)
25+
public func logTrace(_ message: String, category: String? = nil, privacy: PrettyLoggerPrivacy = .auto) {
26+
PrettyLogger.shared.logTrace(message, category: category, privacy: privacy)
3127
}
3228

33-
// MARK: - Deprecations
29+
// MARK: - Legacy print-based API (deprecated)
3430

35-
@available(*, deprecated, renamed: "fatal", message: "Use `fatal` to use the unified OS logger")
31+
@available(*, deprecated, message: "Use logFatal(_ message: String, category: String?, privacy: PrettyLoggerPrivacy) instead")
3632
@discardableResult
37-
public func logFatal(_ items: Any..., separator: String? = nil, terminator: String? = nil, file: String = #file, line: Int = #line, column: Int = #column, function: String = #function) -> String? {
38-
return PrettyLogger.shared.logFatal(items, separator: separator, terminator: terminator, file: file, line: line, column: column, function: function)
33+
public func logFatal(
34+
_ items: Any..., separator: String? = nil, terminator: String? = nil, file: String = #file,
35+
line: Int = #line, column: Int = #column, function: String = #function
36+
) -> String? {
37+
return PrettyLogger.shared.logFatalLegacy(
38+
items, separator: separator, terminator: terminator, file: file, line: line, column: column,
39+
function: function)
3940
}
4041

41-
@available(*, deprecated, renamed: "error", message: "Use `error` to use the unified OS logger")
42+
@available(*, deprecated, message: "Use logError(_ message: String, category: String?, privacy: PrettyLoggerPrivacy) instead")
4243
@discardableResult
43-
public func logError(_ items: Any..., separator: String? = nil, terminator: String? = nil, file: String = #file, line: Int = #line, column: Int = #column, function: String = #function) -> String? {
44-
return PrettyLogger.shared.logError(items, separator: separator, terminator: terminator, file: file, line: line, column: column, function: function)
44+
public func logError(
45+
_ items: Any..., separator: String? = nil, terminator: String? = nil, file: String = #file,
46+
line: Int = #line, column: Int = #column, function: String = #function
47+
) -> String? {
48+
return PrettyLogger.shared.logErrorLegacy(
49+
items, separator: separator, terminator: terminator, file: file, line: line, column: column,
50+
function: function)
4551
}
4652

47-
@available(*, deprecated, renamed: "warning", message: "Use `warning` to use the unified OS logger")
53+
@available(*, deprecated, message: "Use logWarning(_ message: String, category: String?, privacy: PrettyLoggerPrivacy) instead")
4854
@discardableResult
49-
public func logWarning(_ items: Any..., separator: String? = nil, terminator: String? = nil, file: String = #file, line: Int = #line, column: Int = #column, function: String = #function) -> String? {
50-
return PrettyLogger.shared.logWarning(items, separator: separator, terminator: terminator, file: file, line: line, column: column, function: function)
55+
public func logWarning(
56+
_ items: Any..., separator: String? = nil, terminator: String? = nil, file: String = #file,
57+
line: Int = #line, column: Int = #column, function: String = #function
58+
) -> String? {
59+
return PrettyLogger.shared.logWarningLegacy(
60+
items, separator: separator, terminator: terminator, file: file, line: line, column: column,
61+
function: function)
5162
}
5263

53-
@available(*, deprecated, renamed: "info", message: "Use `info` to use the unified OS logger")
64+
@available(*, deprecated, message: "Use logInfo(_ message: String, category: String?, privacy: PrettyLoggerPrivacy) instead")
5465
@discardableResult
55-
public func logInfo(_ items: Any..., separator: String? = nil, terminator: String? = nil, file: String = #file, line: Int = #line, column: Int = #column, function: String = #function) -> String? {
56-
return PrettyLogger.shared.logInfo(items, separator: separator, terminator: terminator, file: file, line: line, column: column, function: function)
66+
public func logInfo(
67+
_ items: Any..., separator: String? = nil, terminator: String? = nil, file: String = #file,
68+
line: Int = #line, column: Int = #column, function: String = #function
69+
) -> String? {
70+
return PrettyLogger.shared.logInfoLegacy(
71+
items, separator: separator, terminator: terminator, file: file, line: line, column: column,
72+
function: function)
5773
}
5874

59-
@available(*, deprecated, renamed: "debug", message: "Use `debug` to use the unified OS logger")
75+
@available(*, deprecated, message: "Use logDebug(_ message: String, category: String?, privacy: PrettyLoggerPrivacy) instead")
6076
@discardableResult
61-
public func logDebug(_ items: Any..., separator: String? = nil, terminator: String? = nil, file: String = #file, line: Int = #line, column: Int = #column, function: String = #function) -> String? {
62-
return PrettyLogger.shared.logDebug(items, separator: separator, terminator: terminator, file: file, line: line, column: column, function: function)
77+
public func logDebug(
78+
_ items: Any..., separator: String? = nil, terminator: String? = nil, file: String = #file,
79+
line: Int = #line, column: Int = #column, function: String = #function
80+
) -> String? {
81+
return PrettyLogger.shared.logDebugLegacy(
82+
items, separator: separator, terminator: terminator, file: file, line: line, column: column,
83+
function: function)
6384
}
6485

65-
@available(*, deprecated, renamed: "trace", message: "Use `trace` to use the unified OS logger")
86+
@available(*, deprecated, message: "Use logTrace(_ message: String, category: String?, privacy: PrettyLoggerPrivacy) instead")
6687
@discardableResult
67-
public func logTrace(_ items: Any..., separator: String? = nil, terminator: String? = nil, file: String = #file, line: Int = #line, column: Int = #column, function: String = #function) -> String? {
68-
return PrettyLogger.shared.logTrace(items, separator: separator, terminator: terminator, file: file, line: line, column: column, function: function)
88+
public func logTrace(
89+
_ items: Any..., separator: String? = nil, terminator: String? = nil, file: String = #file,
90+
line: Int = #line, column: Int = #column, function: String = #function
91+
) -> String? {
92+
return PrettyLogger.shared.logTraceLegacy(
93+
items, separator: separator, terminator: terminator, file: file, line: line, column: column,
94+
function: function)
6995
}

0 commit comments

Comments
 (0)