Route iOS log output to BunnyLogs in real time using a custom CocoaLumberjack DDAbstractLogger — fire-and-forget URLSession POSTs, no blocking.
Add CocoaLumberjack via Swift Package Manager in Xcode (File → Add Package Dependencies):
https://github.com/CocoaLumberjack/CocoaLumberjack.git
Or via CocoaPods:
pod 'CocoaLumberjack/Swift'
Create a DDAbstractLogger subclass that POSTs each log record to your stream:
import CocoaLumberjack
import Foundation
final class BunnyLogsLogger: DDAbstractLogger {
private let uuid: String
private let program: String
private let session: URLSession
init(uuid: String, program: String = "ios") {
self.uuid = uuid
self.program = program
self.session = URLSession(configuration: .default)
}
override func log(message logMessage: DDLogMessage) {
guard let url = URL(string: "https://bunnylogs.com/live/\(uuid)") else { return }
let body: [String: String] = [
"message": logMessage.message,
"level": levelName(for: logMessage.level),
"program": program,
]
guard let data = try? JSONSerialization.data(withJSONObject: body) else { return }
var request = URLRequest(url: url)
request.httpMethod = "POST"
request.setValue("application/json", forHTTPHeaderField: "Content-Type")
request.httpBody = data
session.dataTask(with: request).resume()
}
private func levelName(for level: DDLogLevel) -> String {
switch level {
case .verbose: return "VERBOSE"
case .debug: return "DEBUG"
case .info: return "INFO"
case .warning: return "WARN"
case .error: return "ERROR"
default: return "INFO"
}
}
}
Register the logger in your app entry point. For SwiftUI apps, use the @main struct's init(); for UIKit apps, use application(_:didFinishLaunchingWithOptions:):
SwiftUI
import CocoaLumberjack
import SwiftUI
@main
struct MyApp: App {
init() {
DDLog.add(DDOSLogger.sharedInstance) // keep Xcode console
#if !DEBUG
DDLog.add(BunnyLogsLogger(uuid: "<your-uuid>"))
#endif
}
var body: some Scene {
WindowGroup { ContentView() }
}
}
UIKit
import CocoaLumberjack
@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
func application(_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
DDLog.add(DDOSLogger.sharedInstance)
#if !DEBUG
DDLog.add(BunnyLogsLogger(uuid: "<your-uuid>"))
#endif
return true
}
}
Replace <your-uuid> with the UUID from your stream URL (https://bunnylogs.com/live/<uuid>).
Log anywhere in your app using CocoaLumberjack's free functions:
DDLogInfo("User signed in: \(userId)")
DDLogWarning("Cache miss — fetching from network")
DDLogError("Payment failed: \(error.localizedDescription)")
Pass a DDLogLevel when adding the logger to only forward warnings and errors to BunnyLogs in production:
DDLog.add(BunnyLogsLogger(uuid: "<your-uuid>"), with: .warning)
| BunnyLogs field | Source |
|---|---|
message | DDLogMessage.message |
level | Mapped from DDLogLevel (VERBOSE…ERROR) |
program | program constructor parameter (default: ios) |
timestamp | Server receipt time (no client timestamp sent) |
session.dataTask(with:).resume() dispatches immediately on URLSession's background thread — log(message:) returns without waiting for the network call.Info.plist, a secrets file, or an environment variable rather than hardcoding it in source.log(message:).level=ERROR and program=ios to get notified on errors via Slack, Telegram, or Discord.