CocoaLumberjack

Fast, simple, yet powerful and flexible logging framework for macOS, iOS, tvOS, and watchOS. Orders of magnitude faster than NSLog, available with single-line configuration at application launch. Provides rich customization options and extensibility.

Logging LibrarySwiftiOSmacOStvOSwatchOSPerformance

Library

CocoaLumberjack

Overview

CocoaLumberjack is a fast, simple, yet powerful and flexible logging framework for macOS, iOS, tvOS, and watchOS. It delivers orders of magnitude better performance than NSLog and provides an excellent development experience with single-line configuration at application launch. With rich customization options and extensibility, it handles advanced logging requirements. Through over 15 years of development experience and mature APIs, it realizes a reliable logging solution for iOS and macOS application development, supporting both Swift and Objective-C languages.

Details

CocoaLumberjack 2025 edition maintains its solid position as a veteran library popular in the Swift community. While continued use in existing projects and new adoption by developers prioritizing flexibility are observed, new project adoption is declining due to the proliferation of Apple's official OSLog. However, it maintains advantages in projects with complex logging requirements through high customizability, rich output destination options, file rotation capabilities, and Swift Package Manager support. With multi-platform support (macOS, iOS, tvOS, watchOS, visionOS) and high performance, it is valued as an important choice in professional iOS application development.

Key Features

  • High-speed performance: Orders of magnitude faster asynchronous log processing than NSLog
  • Flexible configuration: Support for simultaneous writing to multiple loggers and output destinations
  • Swift integration: Complete support for both Swift and Objective-C
  • Rich output destinations: Files, console, system log, custom logs, etc.
  • Apple platform support: Compatible with iOS, macOS, tvOS, watchOS, visionOS
  • High customizability: Implementation support for custom formatters, filters, and log processing

Pros and Cons

Pros

  • Significant performance improvement and efficient resource usage compared to NSLog
  • Simple setup with immediate availability through single-line initialization code
  • Advanced features like file rotation, compression, and automatic cleanup
  • Various installation methods including CocoaPods, Carthage, and Swift Package Manager
  • Stability and reliability through over 15 years of development experience
  • Rich documentation and active community support

Cons

  • Decreased new adoption opportunities due to Apple's official OSLog proliferation
  • May be overkill for small-scale applications
  • Addition of third-party library dependencies
  • Configuration complexity when used as Swift-log backend
  • End of support for iOS11 and earlier, macOS 10.13 and earlier
  • Privacy and data collection responsibility delegated to app developers

Reference Pages

Usage Examples

Installation and Basic Setup

// Swift Package Manager (Recommended)
// File -> Add Package Dependencies...
// https://github.com/CocoaLumberjack/CocoaLumberjack.git

// For CocoaPods
// pod 'CocoaLumberjack/Swift'

// For Carthage
// github "CocoaLumberjack/CocoaLumberjack"

import CocoaLumberjack

class AppDelegate: UIApplicationDelegate {
    
    func application(_ application: UIApplication, 
                    didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
        
        // Basic initialization (single-line setup)
        DDLog.add(DDOSLogger.sharedInstance) // OS log (Apple unified logging)
        DDLog.add(DDTTYLogger.sharedInstance!) // Xcode console
        
        // Log level setup
        #if DEBUG
        DDLog.logLevel = .all
        #else
        DDLog.logLevel = .warning
        #endif
        
        // Basic log output
        DDLogVerbose("Application started - verbose")
        DDLogDebug("Application started - debug")
        DDLogInfo("Application started - info")
        DDLogWarn("Application started - warning")
        DDLogError("Application started - error")
        
        return true
    }
}

// SwiftUI setup example
@main
struct MyApp: App {
    
    init() {
        setupLogging()
    }
    
    private func setupLogging() {
        // DDOSLogger: Apple unified logging system
        DDLog.add(DDOSLogger.sharedInstance)
        
        // DDTTYLogger: Development environment console output
        if let ttyLogger = DDTTYLogger.sharedInstance {
            DDLog.add(ttyLogger)
            
            // Color output setup
            ttyLogger.colorsEnabled = true
            ttyLogger.setForegroundColor(.red, backgroundColor: nil, for: .error)
            ttyLogger.setForegroundColor(.orange, backgroundColor: nil, for: .warning)
            ttyLogger.setForegroundColor(.green, backgroundColor: nil, for: .info)
        }
        
        #if DEBUG
        DDLog.logLevel = .all
        #else
        DDLog.logLevel = .info
        #endif
    }
    
    var body: some Scene {
        WindowGroup {
            ContentView()
                .onAppear {
                    DDLogInfo("SwiftUI application display started")
                }
        }
    }
}

Basic Log Levels and Output Destination Setup

import CocoaLumberjack

// Log level definition
extension DDLogLevel {
    static var appLogLevel: DDLogLevel {
        #if DEBUG
        return .all
        #elseif STAGING
        return .info
        #else
        return .warning
        #endif
    }
}

class LoggingManager {
    
    static let shared = LoggingManager()
    
    private init() {
        setupLoggers()
    }
    
    private func setupLoggers() {
        // Global log level setup
        DDLog.logLevel = DDLogLevel.appLogLevel
        
        // 1. OS log (system integration)
        let osLogger = DDOSLogger.sharedInstance
        osLogger.logFormatter = CustomOSLogFormatter()
        DDLog.add(osLogger)
        
        // 2. Console log (development environment)
        if let ttyLogger = DDTTYLogger.sharedInstance {
            ttyLogger.logFormatter = CustomConsoleFormatter()
            ttyLogger.colorsEnabled = true
            setupConsoleColors(ttyLogger)
            DDLog.add(ttyLogger)
        }
        
        // 3. File log (production/debug)
        let fileLogger = DDFileLogger()
        fileLogger.rollingFrequency = 60 * 60 * 24 // 24 hours
        fileLogger.logFileManager.maximumNumberOfLogFiles = 7 // 1 week
        fileLogger.maximumFileSize = 1024 * 1024 * 10 // 10MB
        fileLogger.logFormatter = CustomFileFormatter()
        DDLog.add(fileLogger)
        
        DDLogInfo("Logging system initialization completed")
        DDLogInfo("File log path: \(fileLogger.logFileManager.logsDirectory)")
    }
    
    private func setupConsoleColors(_ logger: DDTTYLogger) {
        logger.setForegroundColor(.white, backgroundColor: .red, for: .error)
        logger.setForegroundColor(.orange, backgroundColor: nil, for: .warning)
        logger.setForegroundColor(.green, backgroundColor: nil, for: .info)
        logger.setForegroundColor(.lightGray, backgroundColor: nil, for: .debug)
        logger.setForegroundColor(.darkGray, backgroundColor: nil, for: .verbose)
    }
    
    // Dynamic log level change
    func setLogLevel(_ level: DDLogLevel) {
        DDLog.logLevel = level
        DDLogInfo("Log level changed: \(level)")
    }
    
    // Log file management
    func getLogFiles() -> [String] {
        guard let fileLogger = DDLog.allLoggers.first(where: { $0 is DDFileLogger }) as? DDFileLogger else {
            return []
        }
        
        return fileLogger.logFileManager.sortedLogFilePaths
    }
    
    func exportLogFiles() -> URL? {
        let logFiles = getLogFiles()
        guard !logFiles.isEmpty else { return nil }
        
        // Export log files in ZIP format
        let documentsPath = FileManager.default.urls(for: .documentDirectory, 
                                                   in: .userDomainMask).first!
        let exportURL = documentsPath.appendingPathComponent("app_logs_\(Date()).zip")
        
        // Actual ZIP creation process omitted
        DDLogInfo("Log file export: \(exportURL)")
        return exportURL
    }
}

// Custom formatter implementation
class CustomConsoleFormatter: NSObject, DDLogFormatter {
    
    let dateFormatter: DateFormatter = {
        let formatter = DateFormatter()
        formatter.dateFormat = "HH:mm:ss.SSS"
        return formatter
    }()
    
    func format(message logMessage: DDLogMessage) -> String? {
        let timestamp = dateFormatter.string(from: logMessage.timestamp)
        let logLevel = logLevelString(logMessage.level)
        let fileName = (logMessage.fileName as NSString).lastPathComponent
        let function = logMessage.function ?? "unknown"
        let line = logMessage.line
        
        return "\(timestamp) [\(logLevel)] \(fileName):\(line) \(function) - \(logMessage.message)"
    }
    
    private func logLevelString(_ level: DDLogLevel) -> String {
        switch level {
        case .error: return "ERROR"
        case .warning: return "WARN "
        case .info: return "INFO "
        case .debug: return "DEBUG"
        case .verbose: return "TRACE"
        default: return "UNKNOWN"
        }
    }
}

class CustomFileFormatter: NSObject, DDLogFormatter {
    
    let dateFormatter: DateFormatter = {
        let formatter = DateFormatter()
        formatter.dateFormat = "yyyy-MM-dd HH:mm:ss.SSS"
        return formatter
    }()
    
    func format(message logMessage: DDLogMessage) -> String? {
        let timestamp = dateFormatter.string(from: logMessage.timestamp)
        let logLevel = logLevelString(logMessage.level)
        let fileName = (logMessage.fileName as NSString).lastPathComponent
        let function = logMessage.function ?? "unknown"
        let line = logMessage.line
        
        return "\(timestamp) [\(logLevel)] [\(fileName):\(line)] \(function) - \(logMessage.message)"
    }
    
    private func logLevelString(_ level: DDLogLevel) -> String {
        switch level {
        case .error: return "ERROR"
        case .warning: return "WARN "
        case .info: return "INFO "
        case .debug: return "DEBUG"
        case .verbose: return "TRACE"
        default: return "UNKNOWN"
        }
    }
}

class CustomOSLogFormatter: NSObject, DDLogFormatter {
    
    func format(message logMessage: DDLogMessage) -> String? {
        let fileName = (logMessage.fileName as NSString).lastPathComponent
        let function = logMessage.function ?? "unknown"
        let line = logMessage.line
        
        return "[\(fileName):\(line)] \(function) - \(logMessage.message)"
    }
}

// Usage example
class ViewController: UIViewController {
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        // Log manager initialization
        _ = LoggingManager.shared
        
        DDLogInfo("ViewController load completed")
        
        // Test various log levels
        testLogging()
    }
    
    private func testLogging() {
        DDLogVerbose("Detailed trace information")
        DDLogDebug("Debug information: viewDidLoad")
        DDLogInfo("User action: screen display")
        DDLogWarn("Warning: deprecated API usage")
        DDLogError("Error: network connection failed")
        
        // Parameterized logging
        let userId = 12345
        let actionName = "button_tap"
        DDLogInfo("User action - UserID: \(userId), Action: \(actionName)")
    }
}

Advanced Customization and Filtering

import CocoaLumberjack

// Custom log context definition
let LogContextUserActions = 1000
let LogContextNetworking = 2000
let LogContextDatabase = 3000
let LogContextAuthentication = 4000

// Context-specific log macros
func DDLogUserAction(_ message: @autoclosure () -> String, 
                    level: DDLogLevel = .info,
                    context: Int = LogContextUserActions,
                    file: StaticString = #file,
                    function: StaticString = #function,
                    line: UInt = #line) {
    _DDLogMessage(message(), level: level, flag: DDLogFlag(level), 
                 context: context, file: file, function: function, line: line)
}

func DDLogNetwork(_ message: @autoclosure () -> String,
                 level: DDLogLevel = .debug,
                 context: Int = LogContextNetworking,
                 file: StaticString = #file,
                 function: StaticString = #function,
                 line: UInt = #line) {
    _DDLogMessage(message(), level: level, flag: DDLogFlag(level),
                 context: context, file: file, function: function, line: line)
}

func DDLogDatabase(_ message: @autoclosure () -> String,
                  level: DDLogLevel = .debug,
                  context: Int = LogContextDatabase,
                  file: StaticString = #file,
                  function: StaticString = #function,
                  line: UInt = #line) {
    _DDLogMessage(message(), level: level, flag: DDLogFlag(level),
                 context: context, file: file, function: function, line: line)
}

// Advanced logging manager
class AdvancedLoggingManager {
    
    static let shared = AdvancedLoggingManager()
    
    private var userActionsLogger: DDFileLogger?
    private var networkLogger: DDFileLogger?
    private var databaseLogger: DDFileLogger?
    
    private init() {
        setupAdvancedLoggers()
    }
    
    private func setupAdvancedLoggers() {
        // Basic logger setup
        setupBasicLoggers()
        
        // Contextual file loggers
        setupContextualFileLoggers()
        
        // Network-specific logger
        setupNetworkLogger()
        
        DDLogInfo("Advanced logging system initialization completed")
    }
    
    private func setupBasicLoggers() {
        // OS log
        let osLogger = DDOSLogger.sharedInstance
        osLogger.logFormatter = AdvancedOSLogFormatter()
        DDLog.add(osLogger, with: .all)
        
        // Console log
        if let ttyLogger = DDTTYLogger.sharedInstance {
            ttyLogger.logFormatter = AdvancedConsoleFormatter()
            ttyLogger.colorsEnabled = true
            setupAdvancedColors(ttyLogger)
            DDLog.add(ttyLogger, with: .all)
        }
    }
    
    private func setupContextualFileLoggers() {
        // User action dedicated log
        userActionsLogger = createContextualLogger(
            filename: "user_actions",
            context: LogContextUserActions
        )
        
        // Database dedicated log
        databaseLogger = createContextualLogger(
            filename: "database",
            context: LogContextDatabase
        )
    }
    
    private func createContextualLogger(filename: String, context: Int) -> DDFileLogger {
        let fileLogger = DDFileLogger()
        
        // File name and path setup
        let documentsPath = FileManager.default.urls(for: .documentDirectory, 
                                                   in: .userDomainMask).first!
        let logsDirectory = documentsPath.appendingPathComponent("Logs/\(filename)")
        
        let logFileManager = DDLogFileManagerDefault(logsDirectory: logsDirectory.path)
        fileLogger.logFileManager = logFileManager
        
        // Rotation setup
        fileLogger.rollingFrequency = 60 * 60 * 24 // 24 hours
        fileLogger.logFileManager.maximumNumberOfLogFiles = 14 // 2 weeks
        fileLogger.maximumFileSize = 1024 * 1024 * 5 // 5MB
        
        // Custom formatter
        fileLogger.logFormatter = ContextualFileFormatter(context: context)
        
        // Apply context filter
        DDLog.add(fileLogger, with: DDLogLevel.all, context: context)
        
        return fileLogger
    }
    
    private func setupNetworkLogger() {
        networkLogger = DDFileLogger()
        
        // Network-specific directory
        let documentsPath = FileManager.default.urls(for: .documentDirectory, 
                                                   in: .userDomainMask).first!
        let networkLogsDirectory = documentsPath.appendingPathComponent("Logs/network")
        
        let logFileManager = DDLogFileManagerDefault(logsDirectory: networkLogsDirectory.path)
        networkLogger?.logFileManager = logFileManager
        
        // Frequent rotation (high volume network logs)
        networkLogger?.rollingFrequency = 60 * 60 * 12 // 12 hours
        networkLogger?.logFileManager.maximumNumberOfLogFiles = 28 // 2 weeks
        networkLogger?.maximumFileSize = 1024 * 1024 * 2 // 2MB
        
        networkLogger?.logFormatter = NetworkLogFormatter()
        
        if let logger = networkLogger {
            DDLog.add(logger, with: .all, context: LogContextNetworking)
        }
    }
    
    private func setupAdvancedColors(_ logger: DDTTYLogger) {
        // Error level
        logger.setForegroundColor(.white, backgroundColor: .red, for: .error)
        // Warning level
        logger.setForegroundColor(.black, backgroundColor: .yellow, for: .warning)
        // Info level
        logger.setForegroundColor(.blue, backgroundColor: nil, for: .info)
        // Debug level
        logger.setForegroundColor(.green, backgroundColor: nil, for: .debug)
        // Verbose level
        logger.setForegroundColor(.lightGray, backgroundColor: nil, for: .verbose)
    }
    
    // Log analysis methods
    func analyzeLogFiles() -> LogAnalysisResult {
        var result = LogAnalysisResult()
        
        // User action log analysis
        if let userLogger = userActionsLogger {
            result.userActionsCount = countLogEntries(in: userLogger)
        }
        
        // Network log analysis
        if let netLogger = networkLogger {
            result.networkRequestsCount = countLogEntries(in: netLogger)
        }
        
        // Database log analysis
        if let dbLogger = databaseLogger {
            result.databaseOperationsCount = countLogEntries(in: dbLogger)
        }
        
        return result
    }
    
    private func countLogEntries(in logger: DDFileLogger) -> Int {
        let logFiles = logger.logFileManager.sortedLogFilePaths
        var totalLines = 0
        
        for filePath in logFiles {
            do {
                let content = try String(contentsOfFile: filePath)
                totalLines += content.components(separatedBy: .newlines).count
            } catch {
                DDLogError("Log file read error: \(error)")
            }
        }
        
        return totalLines
    }
}

// Advanced formatter implementation
class AdvancedConsoleFormatter: NSObject, DDLogFormatter {
    
    let dateFormatter: DateFormatter = {
        let formatter = DateFormatter()
        formatter.dateFormat = "HH:mm:ss.SSS"
        return formatter
    }()
    
    func format(message logMessage: DDLogMessage) -> String? {
        let timestamp = dateFormatter.string(from: logMessage.timestamp)
        let logLevel = logLevelEmoji(logMessage.level)
        let context = contextString(logMessage.context)
        let fileName = (logMessage.fileName as NSString).lastPathComponent
        let function = logMessage.function ?? "unknown"
        let line = logMessage.line
        
        return "\(timestamp) \(logLevel) [\(context)] \(fileName):\(line) \(function) - \(logMessage.message)"
    }
    
    private func logLevelEmoji(_ level: DDLogLevel) -> String {
        switch level {
        case .error: return "🔴"
        case .warning: return "🟡"
        case .info: return "🔵"
        case .debug: return "🟢"
        case .verbose: return "⚪"
        default: return "⚫"
        }
    }
    
    private func contextString(_ context: Int) -> String {
        switch context {
        case LogContextUserActions: return "USER"
        case LogContextNetworking: return "NET"
        case LogContextDatabase: return "DB"
        case LogContextAuthentication: return "AUTH"
        default: return "GENERAL"
        }
    }
}

class ContextualFileFormatter: NSObject, DDLogFormatter {
    
    private let context: Int
    private let dateFormatter: DateFormatter = {
        let formatter = DateFormatter()
        formatter.dateFormat = "yyyy-MM-dd HH:mm:ss.SSS"
        return formatter
    }()
    
    init(context: Int) {
        self.context = context
        super.init()
    }
    
    func format(message logMessage: DDLogMessage) -> String? {
        guard logMessage.context == context else { return nil }
        
        let timestamp = dateFormatter.string(from: logMessage.timestamp)
        let logLevel = logLevelString(logMessage.level)
        let fileName = (logMessage.fileName as NSString).lastPathComponent
        let function = logMessage.function ?? "unknown"
        let line = logMessage.line
        
        return "\(timestamp) [\(logLevel)] [\(fileName):\(line)] \(function) - \(logMessage.message)"
    }
    
    private func logLevelString(_ level: DDLogLevel) -> String {
        switch level {
        case .error: return "ERROR"
        case .warning: return "WARN "
        case .info: return "INFO "
        case .debug: return "DEBUG"
        case .verbose: return "TRACE"
        default: return "UNKNOWN"
        }
    }
}

class NetworkLogFormatter: NSObject, DDLogFormatter {
    
    let dateFormatter: DateFormatter = {
        let formatter = DateFormatter()
        formatter.dateFormat = "yyyy-MM-dd HH:mm:ss.SSS"
        return formatter
    }()
    
    func format(message logMessage: DDLogMessage) -> String? {
        let timestamp = dateFormatter.string(from: logMessage.timestamp)
        let logLevel = logLevelString(logMessage.level)
        
        return "\(timestamp) [\(logLevel)] [NETWORK] \(logMessage.message)"
    }
    
    private func logLevelString(_ level: DDLogLevel) -> String {
        switch level {
        case .error: return "ERROR"
        case .warning: return "WARN "
        case .info: return "INFO "
        case .debug: return "DEBUG"
        case .verbose: return "TRACE"
        default: return "UNKNOWN"
        }
    }
}

class AdvancedOSLogFormatter: NSObject, DDLogFormatter {
    
    func format(message logMessage: DDLogMessage) -> String? {
        let context = contextString(logMessage.context)
        let fileName = (logMessage.fileName as NSString).lastPathComponent
        let function = logMessage.function ?? "unknown"
        let line = logMessage.line
        
        return "[\(context)] [\(fileName):\(line)] \(function) - \(logMessage.message)"
    }
    
    private func contextString(_ context: Int) -> String {
        switch context {
        case LogContextUserActions: return "USER"
        case LogContextNetworking: return "NET"
        case LogContextDatabase: return "DB"
        case LogContextAuthentication: return "AUTH"
        default: return "GENERAL"
        }
    }
}

// Log analysis result structure
struct LogAnalysisResult {
    var userActionsCount: Int = 0
    var networkRequestsCount: Int = 0
    var databaseOperationsCount: Int = 0
    var totalLogFiles: Int = 0
    var totalLogSize: Int64 = 0
    
    var summary: String {
        return """
        Log Analysis Results:
        - User Actions: \(userActionsCount) entries
        - Network Requests: \(networkRequestsCount) entries
        - Database Operations: \(databaseOperationsCount) entries
        - Total Log Files: \(totalLogFiles)
        - Total Log Size: \(ByteCountFormatter.string(fromByteCount: totalLogSize, countStyle: .file))
        """
    }
}

// Usage example
class AdvancedLoggingViewController: UIViewController {
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        // Advanced logging manager initialization
        _ = AdvancedLoggingManager.shared
        
        // Test contextual logging
        testContextualLogging()
    }
    
    private func testContextualLogging() {
        // User action logs
        DDLogUserAction("Button tap - navigate to settings")
        DDLogUserAction("Swipe gesture detected")
        
        // Network logs
        DDLogNetwork("HTTP GET /api/users - started")
        DDLogNetwork("HTTP GET /api/users - 200 OK - 250ms")
        
        // Database logs
        DDLogDatabase("SQLite SELECT FROM users WHERE id = 123")
        DDLogDatabase("Core Data save context - success")
        
        // General logs
        DDLogInfo("Screen display completed")
    }
    
    @IBAction func analyzeLogsButtonTapped(_ sender: UIButton) {
        let analysisResult = AdvancedLoggingManager.shared.analyzeLogFiles()
        
        let alert = UIAlertController(
            title: "Log Analysis Results",
            message: analysisResult.summary,
            preferredStyle: .alert
        )
        alert.addAction(UIAlertAction(title: "OK", style: .default))
        
        present(alert, animated: true)
        
        DDLogUserAction("Log analysis results displayed")
    }
}

Performance Monitoring and Crash Reporting

import CocoaLumberjack
import Foundation

// Performance monitoring logger
class PerformanceLogger {
    
    static let shared = PerformanceLogger()
    
    private let performanceQueue = DispatchQueue(label: "com.app.performance.logging", 
                                               qos: .utility)
    private var performanceMetrics: [String: PerformanceMetric] = [:]
    private let metricsLock = NSLock()
    
    private init() {
        setupPerformanceLogging()
    }
    
    private func setupPerformanceLogging() {
        // Performance-specific file logger
        let perfLogger = DDFileLogger()
        
        let documentsPath = FileManager.default.urls(for: .documentDirectory, 
                                                   in: .userDomainMask).first!
        let perfLogsDirectory = documentsPath.appendingPathComponent("Logs/performance")
        
        let logFileManager = DDLogFileManagerDefault(logsDirectory: perfLogsDirectory.path)
        perfLogger.logFileManager = logFileManager
        
        perfLogger.rollingFrequency = 60 * 60 * 24 // 24 hours
        perfLogger.logFileManager.maximumNumberOfLogFiles = 7 // 1 week
        perfLogger.maximumFileSize = 1024 * 1024 * 10 // 10MB
        perfLogger.logFormatter = PerformanceLogFormatter()
        
        DDLog.add(perfLogger, with: .info, context: LogContextPerformance)
        
        DDLogInfo("Performance monitoring logger initialization completed")
    }
    
    // Method execution time measurement
    func measureExecutionTime<T>(
        operationName: String,
        operation: () throws -> T
    ) rethrows -> T {
        let startTime = CFAbsoluteTimeGetCurrent()
        let result = try operation()
        let executionTime = CFAbsoluteTimeGetCurrent() - startTime
        
        logPerformance(operationName: operationName, 
                      executionTime: executionTime * 1000) // ms
        
        return result
    }
    
    // Asynchronous processing time measurement
    func measureAsyncExecutionTime<T>(
        operationName: String,
        operation: (@escaping (T) -> Void) -> Void,
        completion: @escaping (T, TimeInterval) -> Void
    ) {
        let startTime = CFAbsoluteTimeGetCurrent()
        
        operation { result in
            let executionTime = CFAbsoluteTimeGetCurrent() - startTime
            self.logPerformance(operationName: operationName, 
                              executionTime: executionTime * 1000)
            completion(result, executionTime)
        }
    }
    
    // Metrics recording
    func recordMetric(name: String, value: Double, unit: String = "") {
        metricsLock.lock()
        defer { metricsLock.unlock() }
        
        if var metric = performanceMetrics[name] {
            metric.addValue(value)
            performanceMetrics[name] = metric
        } else {
            performanceMetrics[name] = PerformanceMetric(name: name, unit: unit, value: value)
        }
        
        DDLogPerformance("Metric recorded: \(name) = \(value) \(unit)")
    }
    
    // Memory usage monitoring
    func logMemoryUsage(operation: String) {
        let memoryInfo = getMemoryUsage()
        
        recordMetric(name: "memory_used_mb", value: memoryInfo.used)
        recordMetric(name: "memory_available_mb", value: memoryInfo.available)
        
        DDLogPerformance("Memory usage [\(operation)]: Used \(String(format: "%.1f", memoryInfo.used))MB / Available \(String(format: "%.1f", memoryInfo.available))MB")
        
        // Memory warning threshold check
        if memoryInfo.usagePercentage > 85.0 {
            DDLogWarn("High memory usage warning: \(String(format: "%.1f", memoryInfo.usagePercentage))%")
        }
    }
    
    // CPU usage monitoring
    func logCPUUsage(operation: String) {
        let cpuUsage = getCPUUsage()
        
        recordMetric(name: "cpu_usage_percent", value: cpuUsage)
        
        DDLogPerformance("CPU usage [\(operation)]: \(String(format: "%.1f", cpuUsage))%")
        
        // CPU usage warning
        if cpuUsage > 80.0 {
            DDLogWarn("High CPU usage warning: \(String(format: "%.1f", cpuUsage))%")
        }
    }
    
    // Performance statistics report
    func generatePerformanceReport() -> String {
        metricsLock.lock()
        defer { metricsLock.unlock() }
        
        var report = "=== Performance Report ===\n"
        
        for (name, metric) in performanceMetrics.sorted(by: { $0.key < $1.key }) {
            report += "\(name): avg \(String(format: "%.2f", metric.average)) \(metric.unit) "
            report += "(min: \(String(format: "%.2f", metric.minimum)), "
            report += "max: \(String(format: "%.2f", metric.maximum)), "
            report += "count: \(metric.count))\n"
        }
        
        DDLogInfo("Performance report generated")
        return report
    }
    
    private func logPerformance(operationName: String, executionTime: TimeInterval) {
        performanceQueue.async {
            self.recordMetric(name: "\(operationName)_time_ms", 
                            value: executionTime, unit: "ms")
            
            DDLogPerformance("Execution time measurement: \(operationName) - \(String(format: "%.2f", executionTime))ms")
            
            // Slow processing warning
            if executionTime > 1000 { // 1 second or more
                DDLogWarn("Slow processing detected: \(operationName) - \(String(format: "%.2f", executionTime))ms")
            }
        }
    }
    
    private func getMemoryUsage() -> (used: Double, available: Double, usagePercentage: Double) {
        var info = mach_task_basic_info()
        var count = mach_msg_type_number_t(MemoryLayout<mach_task_basic_info>.size)/4
        
        let result: kern_return_t = withUnsafeMutablePointer(to: &info) {
            $0.withMemoryRebound(to: integer_t.self, capacity: 1) {
                task_info(mach_task_self_, task_flavor_t(MACH_TASK_BASIC_INFO), $0, &count)
            }
        }
        
        if result == KERN_SUCCESS {
            let usedMB = Double(info.resident_size) / 1024.0 / 1024.0
            let availableMB = Double(ProcessInfo.processInfo.physicalMemory) / 1024.0 / 1024.0
            let usagePercentage = (usedMB / availableMB) * 100.0
            
            return (used: usedMB, available: availableMB, usagePercentage: usagePercentage)
        } else {
            return (used: 0, available: 0, usagePercentage: 0)
        }
    }
    
    private func getCPUUsage() -> Double {
        var info = processor_info_array_t.allocate(capacity: 1)
        var numCpuInfo: mach_msg_type_number_t = 0
        var numCpus: natural_t = 0
        
        let result = host_processor_info(mach_host_self(), PROCESSOR_CPU_LOAD_INFO, 
                                       &numCpus, &info, &numCpuInfo)
        
        if result == KERN_SUCCESS {
            defer { info.deallocate() }
            
            let cpuLoadInfo = info.bindMemory(to: processor_cpu_load_info.self, capacity: Int(numCpus))
            
            var totalTicks: UInt32 = 0
            var idleTicks: UInt32 = 0
            
            for i in 0..<Int(numCpus) {
                totalTicks += cpuLoadInfo[i].cpu_ticks.0 + cpuLoadInfo[i].cpu_ticks.1 + 
                             cpuLoadInfo[i].cpu_ticks.2 + cpuLoadInfo[i].cpu_ticks.3
                idleTicks += cpuLoadInfo[i].cpu_ticks.2 // CPU_STATE_IDLE
            }
            
            let usage = Double(totalTicks - idleTicks) / Double(totalTicks) * 100.0
            return usage
        }
        
        return 0.0
    }
}

// Performance metrics structure
struct PerformanceMetric {
    let name: String
    let unit: String
    private var values: [Double] = []
    
    init(name: String, unit: String, value: Double) {
        self.name = name
        self.unit = unit
        self.values = [value]
    }
    
    mutating func addValue(_ value: Double) {
        values.append(value)
    }
    
    var average: Double {
        guard !values.isEmpty else { return 0 }
        return values.reduce(0, +) / Double(values.count)
    }
    
    var minimum: Double {
        return values.min() ?? 0
    }
    
    var maximum: Double {
        return values.max() ?? 0
    }
    
    var count: Int {
        return values.count
    }
}

// Crash report logger
class CrashReportLogger {
    
    static let shared = CrashReportLogger()
    
    private init() {
        setupCrashLogging()
        setupUncaughtExceptionHandler()
    }
    
    private func setupCrashLogging() {
        // Crash-specific file logger
        let crashLogger = DDFileLogger()
        
        let documentsPath = FileManager.default.urls(for: .documentDirectory, 
                                                   in: .userDomainMask).first!
        let crashLogsDirectory = documentsPath.appendingPathComponent("Logs/crashes")
        
        let logFileManager = DDLogFileManagerDefault(logsDirectory: crashLogsDirectory.path)
        crashLogger.logFileManager = logFileManager
        
        crashLogger.rollingFrequency = 0 // Rotate only on crash
        crashLogger.logFileManager.maximumNumberOfLogFiles = 50 // Keep 50 crash reports
        crashLogger.maximumFileSize = 1024 * 1024 * 5 // 5MB
        crashLogger.logFormatter = CrashLogFormatter()
        
        DDLog.add(crashLogger, with: .error, context: LogContextCrash)
    }
    
    private func setupUncaughtExceptionHandler() {
        NSSetUncaughtExceptionHandler { exception in
            CrashReportLogger.shared.logCrash(exception: exception)
        }
        
        // Fatal signal handler
        signal(SIGABRT) { signal in
            CrashReportLogger.shared.logFatalSignal(signal: signal)
        }
        signal(SIGILL) { signal in
            CrashReportLogger.shared.logFatalSignal(signal: signal)
        }
        signal(SIGSEGV) { signal in
            CrashReportLogger.shared.logFatalSignal(signal: signal)
        }
        signal(SIGFPE) { signal in
            CrashReportLogger.shared.logFatalSignal(signal: signal)
        }
        signal(SIGBUS) { signal in
            CrashReportLogger.shared.logFatalSignal(signal: signal)
        }
    }
    
    private func logCrash(exception: NSException) {
        let crashReport = generateCrashReport(exception: exception)
        DDLogCrash("=== CRASH REPORT ===")
        DDLogCrash(crashReport)
        DDLogCrash("=== END CRASH REPORT ===")
        
        // Flush crash log immediately
        DDLog.flushLog()
    }
    
    private func logFatalSignal(signal: Int32) {
        let crashReport = generateSignalCrashReport(signal: signal)
        DDLogCrash("=== FATAL SIGNAL REPORT ===")
        DDLogCrash(crashReport)
        DDLogCrash("=== END FATAL SIGNAL REPORT ===")
        
        // Flush crash log immediately
        DDLog.flushLog()
    }
    
    private func generateCrashReport(exception: NSException) -> String {
        var report = ""
        
        // Basic information
        report += "Exception Name: \(exception.name)\n"
        report += "Reason: \(exception.reason ?? "Unknown")\n"
        report += "User Info: \(exception.userInfo ?? [:])\n"
        
        // Device information
        report += "\n=== Device Information ===\n"
        report += getDeviceInformation()
        
        // Application information
        report += "\n=== Application Information ===\n"
        report += getApplicationInformation()
        
        // Stack trace
        report += "\n=== Stack Trace ===\n"
        if let callStack = exception.callStackSymbols {
            for (index, symbol) in callStack.enumerated() {
                report += "\(index): \(symbol)\n"
            }
        }
        
        // Memory information
        report += "\n=== Memory Information ===\n"
        let memoryInfo = PerformanceLogger.shared.getMemoryUsage()
        report += "Used Memory: \(String(format: "%.1f", memoryInfo.used))MB\n"
        report += "Available Memory: \(String(format: "%.1f", memoryInfo.available))MB\n"
        report += "Memory Usage: \(String(format: "%.1f", memoryInfo.usagePercentage))%\n"
        
        return report
    }
    
    private func generateSignalCrashReport(signal: Int32) -> String {
        var report = ""
        
        report += "Fatal Signal: \(signal) (\(signalName(signal)))\n"
        
        // Device and application information
        report += "\n=== Device Information ===\n"
        report += getDeviceInformation()
        
        report += "\n=== Application Information ===\n"
        report += getApplicationInformation()
        
        // Stack trace (if possible)
        report += "\n=== Stack Trace ===\n"
        let symbols = Thread.callStackSymbols
        for (index, symbol) in symbols.enumerated() {
            report += "\(index): \(symbol)\n"
        }
        
        return report
    }
    
    private func getDeviceInformation() -> String {
        var info = ""
        
        let device = UIDevice.current
        info += "Device: \(device.model)\n"
        info += "System Name: \(device.systemName)\n"
        info += "System Version: \(device.systemVersion)\n"
        info += "Identifier: \(device.identifierForVendor?.uuidString ?? "Unknown")\n"
        
        // Processor information
        var size = 0
        sysctlbyname("hw.machine", nil, &size, nil, 0)
        var machine = [CChar](repeating: 0, count: size)
        sysctlbyname("hw.machine", &machine, &size, nil, 0)
        info += "Hardware: \(String(cString: machine))\n"
        
        return info
    }
    
    private func getApplicationInformation() -> String {
        var info = ""
        
        let bundle = Bundle.main
        info += "App Name: \(bundle.object(forInfoDictionaryKey: "CFBundleName") ?? "Unknown")\n"
        info += "App Version: \(bundle.object(forInfoDictionaryKey: "CFBundleShortVersionString") ?? "Unknown")\n"
        info += "Build Number: \(bundle.object(forInfoDictionaryKey: "CFBundleVersion") ?? "Unknown")\n"
        info += "Bundle ID: \(bundle.bundleIdentifier ?? "Unknown")\n"
        
        // Application runtime
        let processInfo = ProcessInfo.processInfo
        info += "Process Start Time: \(processInfo.processName)\n"
        info += "System Uptime: \(processInfo.systemUptime) seconds\n"
        
        return info
    }
    
    private func signalName(_ signal: Int32) -> String {
        switch signal {
        case SIGABRT: return "SIGABRT"
        case SIGILL: return "SIGILL"
        case SIGSEGV: return "SIGSEGV"
        case SIGFPE: return "SIGFPE"
        case SIGBUS: return "SIGBUS"
        default: return "UNKNOWN"
        }
    }
}

// Dedicated log levels and macros
let LogContextPerformance = 5000
let LogContextCrash = 6000

func DDLogPerformance(_ message: @autoclosure () -> String,
                     level: DDLogLevel = .info,
                     context: Int = LogContextPerformance,
                     file: StaticString = #file,
                     function: StaticString = #function,
                     line: UInt = #line) {
    _DDLogMessage(message(), level: level, flag: DDLogFlag(level),
                 context: context, file: file, function: function, line: line)
}

func DDLogCrash(_ message: @autoclosure () -> String,
               level: DDLogLevel = .error,
               context: Int = LogContextCrash,
               file: StaticString = #file,
               function: StaticString = #function,
               line: UInt = #line) {
    _DDLogMessage(message(), level: level, flag: DDLogFlag(level),
                 context: context, file: file, function: function, line: line)
}

// Custom formatters
class PerformanceLogFormatter: NSObject, DDLogFormatter {
    
    let dateFormatter: DateFormatter = {
        let formatter = DateFormatter()
        formatter.dateFormat = "yyyy-MM-dd HH:mm:ss.SSS"
        return formatter
    }()
    
    func format(message logMessage: DDLogMessage) -> String? {
        let timestamp = dateFormatter.string(from: logMessage.timestamp)
        let logLevel = logLevelString(logMessage.level)
        
        return "\(timestamp) [\(logLevel)] [PERFORMANCE] \(logMessage.message)"
    }
    
    private func logLevelString(_ level: DDLogLevel) -> String {
        switch level {
        case .error: return "ERROR"
        case .warning: return "WARN "
        case .info: return "INFO "
        case .debug: return "DEBUG"
        case .verbose: return "TRACE"
        default: return "UNKNOWN"
        }
    }
}

class CrashLogFormatter: NSObject, DDLogFormatter {
    
    let dateFormatter: DateFormatter = {
        let formatter = DateFormatter()
        formatter.dateFormat = "yyyy-MM-dd HH:mm:ss.SSS"
        return formatter
    }()
    
    func format(message logMessage: DDLogMessage) -> String? {
        let timestamp = dateFormatter.string(from: logMessage.timestamp)
        
        return "\(timestamp) [CRASH] \(logMessage.message)"
    }
}

// Usage example
class PerformanceMonitoringViewController: UIViewController {
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        // Performance monitoring and crash reporting initialization
        _ = PerformanceLogger.shared
        _ = CrashReportLogger.shared
        
        // Start memory and CPU monitoring
        startPerformanceMonitoring()
        
        DDLogInfo("Performance monitoring screen load completed")
    }
    
    private func startPerformanceMonitoring() {
        // Periodic memory and CPU usage monitoring
        Timer.scheduledTimer(withTimeInterval: 30.0, repeats: true) { _ in
            PerformanceLogger.shared.logMemoryUsage(operation: "periodic_check")
            PerformanceLogger.shared.logCPUUsage(operation: "periodic_check")
        }
    }
    
    @IBAction func performHeavyOperationButtonTapped(_ sender: UIButton) {
        PerformanceLogger.shared.measureExecutionTime(operationName: "heavy_calculation") {
            // Heavy calculation simulation
            var result = 0.0
            for i in 0..<1_000_000 {
                result += sin(Double(i))
            }
            
            DDLogDebug("Heavy calculation completed: \(result)")
        }
        
        DDLogUserAction("Heavy operation button tapped")
    }
    
    @IBAction func generateCrashButtonTapped(_ sender: UIButton) {
        // Crash test (debug only)
        #if DEBUG
        fatalError("Test crash")
        #endif
    }
    
    @IBAction func showPerformanceReportButtonTapped(_ sender: UIButton) {
        let report = PerformanceLogger.shared.generatePerformanceReport()
        
        let alert = UIAlertController(
            title: "Performance Report",
            message: report,
            preferredStyle: .alert
        )
        alert.addAction(UIAlertAction(title: "OK", style: .default))
        
        present(alert, animated: true)
        
        DDLogUserAction("Performance report displayed")
    }
}