iOS (CocoaLumberjack)

Route iOS log output to BunnyLogs in real time using a custom CocoaLumberjack DDAbstractLogger — fire-and-forget URLSession POSTs, no blocking.

Installation

Add CocoaLumberjack via Swift Package Manager in Xcode (File → Add Package Dependencies):

https://github.com/CocoaLumberjack/CocoaLumberjack.git

Or via CocoaPods:

pod 'CocoaLumberjack/Swift'
BunnyLogsLogger

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"
        }
    }
}
Setup

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>).

Usage

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)")
Filtering by level

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)
Field mapping
BunnyLogs fieldSource
messageDDLogMessage.message
levelMapped from DDLogLevel (VERBOSEERROR)
programprogram constructor parameter (default: ios)
timestampServer receipt time (no client timestamp sent)
Notes
  • session.dataTask(with:).resume() dispatches immediately on URLSession's background thread — log(message:) returns without waiting for the network call.
  • Store the UUID in your Info.plist, a secrets file, or an environment variable rather than hardcoding it in source.
  • CocoaLumberjack calls loggers on a dedicated serial queue, so no additional synchronisation is needed inside log(message:).
  • High-frequency log sites (e.g. per-frame render logs) should guard with a minimum level or batch records before POSTing.
  • Set up an Alert in BunnyLogs matching level=ERROR and program=ios to get notified on errors via Slack, Telegram, or Discord.