.gitattributes .idea/dictionaries/cocoalumberjack.xml Benchmarking/BaseNSLogging.h Benchmarking/BaseNSLogging.m Benchmarking/Benchmarking.xcodeproj/project.pbxproj Benchmarking/Benchmarking.xcodeproj/xcshareddata/IDETemplateMacros.plist Benchmarking/Benchmarking.xcodeproj/xcshareddata/xcschemes/Benchmarking.xcscheme Benchmarking/DynamicLogging.h Benchmarking/DynamicLogging.m Benchmarking/PerformanceTesting.h Benchmarking/PerformanceTesting.m Benchmarking/Results/Lumberjack Benchmark (PowerMac).ograph Benchmarking/Results/Lumberjack Benchmark (iMac).ograph Benchmarking/Results/Lumberjack Benchmark (iPad).ograph Benchmarking/Results/Lumberjack Benchmark (iPhone 3GS).ograph Benchmarking/StaticLogging.h Benchmarking/StaticLogging.m Benchmarking/main.m CocoaLumberjack.podspec Configs/App-Debug.xcconfig Configs/App-Release.xcconfig Configs/App-Shared.xcconfig Configs/Module-Debug.xcconfig Configs/Module-Release.xcconfig Configs/Module-Shared.xcconfig Dangerfile.swift Demos/Benchmark/Desktop/BenchmarkMac.xcodeproj/project.pbxproj Demos/Benchmark/Desktop/BenchmarkMac.xcodeproj/xcshareddata/IDETemplateMacros.plist Demos/Benchmark/Desktop/BenchmarkMac.xcodeproj/xcshareddata/xcschemes/BenchmarkMac.xcscheme Demos/Benchmark/Desktop/BenchmarkMac/AppDelegate.h Demos/Benchmark/Desktop/BenchmarkMac/AppDelegate.m Demos/Benchmark/Desktop/BenchmarkMac/BenchmarkMac-Info.plist Demos/Benchmark/Desktop/BenchmarkMac/BenchmarkMac-Prefix.pch Demos/Benchmark/Desktop/BenchmarkMac/en.lproj/Credits.rtf Demos/Benchmark/Desktop/BenchmarkMac/en.lproj/InfoPlist.strings Demos/Benchmark/Desktop/BenchmarkMac/en.lproj/MainMenu.xib Demos/Benchmark/Desktop/BenchmarkMac/main.m Demos/Benchmark/Mobile/BenchmarkIPhone-Info.plist Demos/Benchmark/Mobile/BenchmarkIPhone.xcodeproj/project.pbxproj Demos/Benchmark/Mobile/BenchmarkIPhone.xcodeproj/xcshareddata/IDETemplateMacros.plist Demos/Benchmark/Mobile/BenchmarkIPhone.xcodeproj/xcshareddata/xcschemes/BenchmarkIPhone.xcscheme Demos/Benchmark/Mobile/BenchmarkIPhoneViewController.xib Demos/Benchmark/Mobile/BenchmarkIPhone_Prefix.pch Demos/Benchmark/Mobile/Classes/BenchmarkIPhoneAppDelegate.h Demos/Benchmark/Mobile/Classes/BenchmarkIPhoneAppDelegate.m Demos/Benchmark/Mobile/Classes/BenchmarkIPhoneViewController.h Demos/Benchmark/Mobile/Classes/BenchmarkIPhoneViewController.m Demos/Benchmark/Mobile/MainWindow.xib Demos/Benchmark/Mobile/main.m Demos/CLI/CLI.xcodeproj/project.pbxproj Demos/CLI/CLI.xcodeproj/xcshareddata/IDETemplateMacros.plist Demos/CLI/CLI.xcodeproj/xcshareddata/xcschemes/CLI.xcscheme Demos/CLI/CLI/CLI-Prefix.pch Demos/CLI/CLI/CLI.1 Demos/CLI/CLI/main.m Demos/CaptureASL/CaptureASL.xcodeproj/project.pbxproj Demos/CaptureASL/CaptureASL.xcodeproj/xcshareddata/IDETemplateMacros.plist Demos/CaptureASL/CaptureASL.xcodeproj/xcshareddata/xcschemes/CaptureASL.xcscheme Demos/CaptureASL/CaptureASL/AppDelegate.h Demos/CaptureASL/CaptureASL/AppDelegate.m Demos/CaptureASL/CaptureASL/CaptureASL-Info.plist Demos/CaptureASL/CaptureASL/CaptureASL-Prefix.pch Demos/CaptureASL/CaptureASL/Images.xcassets/AppIcon.appiconset/Contents.json Demos/CaptureASL/CaptureASL/Images.xcassets/LaunchImage.launchimage/Contents.json Demos/CaptureASL/CaptureASL/Main.storyboard Demos/CaptureASL/CaptureASL/ViewController.h Demos/CaptureASL/CaptureASL/ViewController.m Demos/CaptureASL/CaptureASL/en.lproj/InfoPlist.strings Demos/CaptureASL/CaptureASL/main.m Demos/ContextFilter/ContextFilter-Info.plist Demos/ContextFilter/ContextFilter.xcodeproj/project.pbxproj Demos/ContextFilter/ContextFilter.xcodeproj/xcshareddata/IDETemplateMacros.plist Demos/ContextFilter/ContextFilter.xcodeproj/xcshareddata/xcschemes/ContextFilter.xcscheme Demos/ContextFilter/ContextFilterAppDelegate.h Demos/ContextFilter/ContextFilterAppDelegate.m Demos/ContextFilter/ContextFilter_Prefix.pch Demos/ContextFilter/English.lproj/InfoPlist.strings Demos/ContextFilter/English.lproj/MainMenu.xib Demos/ContextFilter/MyContextFilter.h Demos/ContextFilter/MyContextFilter.m Demos/ContextFilter/ThirdPartyFramework.h Demos/ContextFilter/ThirdPartyFramework.m Demos/ContextFilter/main.m Demos/CoreDataLogger/CoreDataLogger.xcodeproj/project.pbxproj Demos/CoreDataLogger/CoreDataLogger.xcodeproj/project.xcworkspace/contents.xcworkspacedata Demos/CoreDataLogger/CoreDataLogger.xcodeproj/xcshareddata/IDETemplateMacros.plist Demos/CoreDataLogger/CoreDataLogger.xcodeproj/xcshareddata/xcschemes/CoreDataLogger.xcscheme Demos/CoreDataLogger/CoreDataLogger/CoreDataLogger-Info.plist Demos/CoreDataLogger/CoreDataLogger/CoreDataLogger-Prefix.pch Demos/CoreDataLogger/CoreDataLogger/CoreDataLogger.h Demos/CoreDataLogger/CoreDataLogger/CoreDataLogger.m Demos/CoreDataLogger/CoreDataLogger/CoreDataLogger.xcdatamodeld/.xccurrentversion Demos/CoreDataLogger/CoreDataLogger/CoreDataLoggerAppDelegate.h Demos/CoreDataLogger/CoreDataLogger/CoreDataLoggerAppDelegate.m Demos/CoreDataLogger/CoreDataLogger/Log.xcdatamodeld/.xccurrentversion Demos/CoreDataLogger/CoreDataLogger/Log.xcdatamodeld/CocoaBotLog.xcdatamodel/elements Demos/CoreDataLogger/CoreDataLogger/Log.xcdatamodeld/CocoaBotLog.xcdatamodel/layout Demos/CoreDataLogger/CoreDataLogger/LogEntry.h Demos/CoreDataLogger/CoreDataLogger/LogEntry.m Demos/CoreDataLogger/CoreDataLogger/en.lproj/Credits.rtf Demos/CoreDataLogger/CoreDataLogger/en.lproj/InfoPlist.strings Demos/CoreDataLogger/CoreDataLogger/en.lproj/MainMenu.xib Demos/CoreDataLogger/CoreDataLogger/main.m Demos/CustomFormatters/CustomFormatters-Info.plist Demos/CustomFormatters/CustomFormatters.xcodeproj/project.pbxproj Demos/CustomFormatters/CustomFormatters.xcodeproj/xcshareddata/IDETemplateMacros.plist Demos/CustomFormatters/CustomFormatters.xcodeproj/xcshareddata/xcschemes/CustomFormatters.xcscheme Demos/CustomFormatters/CustomFormattersAppDelegate.h Demos/CustomFormatters/CustomFormattersAppDelegate.m Demos/CustomFormatters/CustomFormatters_Prefix.pch Demos/CustomFormatters/English.lproj/InfoPlist.strings Demos/CustomFormatters/English.lproj/MainMenu.xib Demos/CustomFormatters/TestFormatter.h Demos/CustomFormatters/TestFormatter.m Demos/CustomFormatters/main.m Demos/CustomLogLevels/CustomLogLevels-Info.plist Demos/CustomLogLevels/CustomLogLevels.xcodeproj/project.pbxproj Demos/CustomLogLevels/CustomLogLevels.xcodeproj/xcshareddata/IDETemplateMacros.plist Demos/CustomLogLevels/CustomLogLevels.xcodeproj/xcshareddata/xcschemes/CustomLogLevels.xcscheme Demos/CustomLogLevels/CustomLogLevelsAppDelegate.h Demos/CustomLogLevels/CustomLogLevelsAppDelegate.m Demos/CustomLogLevels/CustomLogLevels_Prefix.pch Demos/CustomLogLevels/English.lproj/InfoPlist.strings Demos/CustomLogLevels/English.lproj/MainMenu.xib Demos/CustomLogLevels/MYLog.h Demos/CustomLogLevels/main.m Demos/Demos.xcworkspace/contents.xcworkspacedata Demos/Demos.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist Demos/DispatchQueueLogger/DispatchQueueLogger.xcodeproj/project.pbxproj Demos/DispatchQueueLogger/DispatchQueueLogger.xcodeproj/xcshareddata/IDETemplateMacros.plist Demos/DispatchQueueLogger/DispatchQueueLogger.xcodeproj/xcshareddata/xcschemes/DispatchQueueLogger.xcscheme Demos/DispatchQueueLogger/DispatchQueueLogger/AppDelegate.h Demos/DispatchQueueLogger/DispatchQueueLogger/AppDelegate.m Demos/DispatchQueueLogger/DispatchQueueLogger/DispatchQueueLogger-Info.plist Demos/DispatchQueueLogger/DispatchQueueLogger/DispatchQueueLogger-Prefix.pch Demos/DispatchQueueLogger/DispatchQueueLogger/en.lproj/Credits.rtf Demos/DispatchQueueLogger/DispatchQueueLogger/en.lproj/InfoPlist.strings Demos/DispatchQueueLogger/DispatchQueueLogger/en.lproj/MainMenu.xib Demos/DispatchQueueLogger/DispatchQueueLogger/main.m Demos/FineGrainedLogging/English.lproj/InfoPlist.strings Demos/FineGrainedLogging/English.lproj/MainMenu.xib Demos/FineGrainedLogging/FineGrainedLogging-Info.plist Demos/FineGrainedLogging/FineGrainedLogging.xcodeproj/project.pbxproj Demos/FineGrainedLogging/FineGrainedLogging.xcodeproj/xcshareddata/IDETemplateMacros.plist Demos/FineGrainedLogging/FineGrainedLogging.xcodeproj/xcshareddata/xcschemes/FineGrainedLogging.xcscheme Demos/FineGrainedLogging/FineGrainedLoggingAppDelegate.h Demos/FineGrainedLogging/FineGrainedLoggingAppDelegate.m Demos/FineGrainedLogging/FineGrainedLogging_Prefix.pch Demos/FineGrainedLogging/MYLog.h Demos/FineGrainedLogging/TimerOne.h Demos/FineGrainedLogging/TimerOne.m Demos/FineGrainedLogging/TimerTwo.h Demos/FineGrainedLogging/TimerTwo.m Demos/FineGrainedLogging/main.m Demos/GlobalLogLevel/English.lproj/InfoPlist.strings Demos/GlobalLogLevel/English.lproj/MainMenu.xib Demos/GlobalLogLevel/GlobalLogLevel-Info.plist Demos/GlobalLogLevel/GlobalLogLevel.xcodeproj/project.pbxproj Demos/GlobalLogLevel/GlobalLogLevel.xcodeproj/project.xcworkspace/contents.xcworkspacedata Demos/GlobalLogLevel/GlobalLogLevel.xcodeproj/xcshareddata/IDETemplateMacros.plist Demos/GlobalLogLevel/GlobalLogLevel.xcodeproj/xcshareddata/xcschemes/GlobalLogLevel.xcscheme Demos/GlobalLogLevel/GlobalLogLevelAppDelegate.h Demos/GlobalLogLevel/GlobalLogLevelAppDelegate.m Demos/GlobalLogLevel/GlobalLogLevel_Prefix.pch Demos/GlobalLogLevel/MyLogging.h Demos/GlobalLogLevel/Stuff.h Demos/GlobalLogLevel/Stuff.m Demos/GlobalLogLevel/main.m Demos/LogFileCompressor/CompressingLogFileManager.h Demos/LogFileCompressor/CompressingLogFileManager.m Demos/LogFileCompressor/English.lproj/InfoPlist.strings Demos/LogFileCompressor/English.lproj/MainMenu.xib Demos/LogFileCompressor/LogFileCompressor-Info.plist Demos/LogFileCompressor/LogFileCompressor.xcodeproj/project.pbxproj Demos/LogFileCompressor/LogFileCompressor.xcodeproj/xcshareddata/IDETemplateMacros.plist Demos/LogFileCompressor/LogFileCompressor.xcodeproj/xcshareddata/xcschemes/LogFileCompressor.xcscheme Demos/LogFileCompressor/LogFileCompressorAppDelegate.h Demos/LogFileCompressor/LogFileCompressorAppDelegate.m Demos/LogFileCompressor/LogFileCompressor_Prefix.pch Demos/LogFileCompressor/main.m Demos/NonArcTest/NonArcTest.xcodeproj/project.pbxproj Demos/NonArcTest/NonArcTest.xcodeproj/xcshareddata/xcschemes/NonArcTest.xcscheme Demos/NonArcTest/NonArcTest/AppDelegate.h Demos/NonArcTest/NonArcTest/AppDelegate.m Demos/NonArcTest/NonArcTest/NonArcTest-Info.plist Demos/NonArcTest/NonArcTest/NonArcTest-Prefix.pch Demos/NonArcTest/NonArcTest/en.lproj/Credits.rtf Demos/NonArcTest/NonArcTest/en.lproj/InfoPlist.strings Demos/NonArcTest/NonArcTest/en.lproj/MainMenu.xib Demos/NonArcTest/NonArcTest/main.m Demos/OverflowTestMac/English.lproj/InfoPlist.strings Demos/OverflowTestMac/English.lproj/MainMenu.xib Demos/OverflowTestMac/OverflowTestMac-Info.plist Demos/OverflowTestMac/OverflowTestMac.xcodeproj/project.pbxproj Demos/OverflowTestMac/OverflowTestMac.xcodeproj/xcshareddata/IDETemplateMacros.plist Demos/OverflowTestMac/OverflowTestMac.xcodeproj/xcshareddata/xcschemes/OverflowTestMac.xcscheme Demos/OverflowTestMac/OverflowTestMacAppDelegate.h Demos/OverflowTestMac/OverflowTestMacAppDelegate.m Demos/OverflowTestMac/OverflowTestMac_Prefix.pch Demos/OverflowTestMac/SlowLogger.h Demos/OverflowTestMac/SlowLogger.m Demos/OverflowTestMac/main.m Demos/PerUserLogLevels/PerUserLogLevels.xcodeproj/project.pbxproj Demos/PerUserLogLevels/PerUserLogLevels.xcodeproj/xcshareddata/IDETemplateMacros.plist Demos/PerUserLogLevels/PerUserLogLevels.xcodeproj/xcshareddata/xcschemes/PerUserLogLevels.xcscheme Demos/PerUserLogLevels/PerUserLogLevels/AppDelegate.h Demos/PerUserLogLevels/PerUserLogLevels/AppDelegate.m Demos/PerUserLogLevels/PerUserLogLevels/PerUserLogLevels-Info.plist Demos/PerUserLogLevels/PerUserLogLevels/PerUserLogLevels-Prefix.pch Demos/PerUserLogLevels/PerUserLogLevels/en.lproj/Credits.rtf Demos/PerUserLogLevels/PerUserLogLevels/en.lproj/InfoPlist.strings Demos/PerUserLogLevels/PerUserLogLevels/en.lproj/MainMenu.xib Demos/PerUserLogLevels/PerUserLogLevels/main.m Demos/PerUserLogLevels/Scripts/LumberjackUser.bash Demos/RegisteredDynamicLogging/Desktop/RegisteredLoggingTest.xcodeproj/project.pbxproj Demos/RegisteredDynamicLogging/Desktop/RegisteredLoggingTest.xcodeproj/xcshareddata/IDETemplateMacros.plist Demos/RegisteredDynamicLogging/Desktop/RegisteredLoggingTest.xcodeproj/xcshareddata/xcschemes/RegisteredLoggingTest (Desktop).xcscheme Demos/RegisteredDynamicLogging/Desktop/RegisteredLoggingTest/Lions.h Demos/RegisteredDynamicLogging/Desktop/RegisteredLoggingTest/Lions.m Demos/RegisteredDynamicLogging/Desktop/RegisteredLoggingTest/RegisteredLoggingTest-Info.plist Demos/RegisteredDynamicLogging/Desktop/RegisteredLoggingTest/RegisteredLoggingTest-Prefix.pch Demos/RegisteredDynamicLogging/Desktop/RegisteredLoggingTest/RegisteredLoggingTestAppDelegate.h Demos/RegisteredDynamicLogging/Desktop/RegisteredLoggingTest/RegisteredLoggingTestAppDelegate.m Demos/RegisteredDynamicLogging/Desktop/RegisteredLoggingTest/Tigers.h Demos/RegisteredDynamicLogging/Desktop/RegisteredLoggingTest/Tigers.m Demos/RegisteredDynamicLogging/Desktop/RegisteredLoggingTest/en.lproj/Credits.rtf Demos/RegisteredDynamicLogging/Desktop/RegisteredLoggingTest/en.lproj/InfoPlist.strings Demos/RegisteredDynamicLogging/Desktop/RegisteredLoggingTest/en.lproj/MainMenu.xib Demos/RegisteredDynamicLogging/Desktop/RegisteredLoggingTest/main.m Demos/RegisteredDynamicLogging/Mobile/RegisteredLoggingTest.xcodeproj/project.pbxproj Demos/RegisteredDynamicLogging/Mobile/RegisteredLoggingTest.xcodeproj/xcshareddata/IDETemplateMacros.plist Demos/RegisteredDynamicLogging/Mobile/RegisteredLoggingTest.xcodeproj/xcshareddata/xcschemes/RegisteredLoggingTest (Mobile).xcscheme Demos/RegisteredDynamicLogging/Mobile/RegisteredLoggingTest/Lions.h Demos/RegisteredDynamicLogging/Mobile/RegisteredLoggingTest/Lions.m Demos/RegisteredDynamicLogging/Mobile/RegisteredLoggingTest/RegisteredLoggingTest-Info.plist Demos/RegisteredDynamicLogging/Mobile/RegisteredLoggingTest/RegisteredLoggingTest-Prefix.pch Demos/RegisteredDynamicLogging/Mobile/RegisteredLoggingTest/RegisteredLoggingTestAppDelegate.h Demos/RegisteredDynamicLogging/Mobile/RegisteredLoggingTest/RegisteredLoggingTestAppDelegate.m Demos/RegisteredDynamicLogging/Mobile/RegisteredLoggingTest/RegisteredLoggingTestViewController.h Demos/RegisteredDynamicLogging/Mobile/RegisteredLoggingTest/RegisteredLoggingTestViewController.m Demos/RegisteredDynamicLogging/Mobile/RegisteredLoggingTest/Tigers.h Demos/RegisteredDynamicLogging/Mobile/RegisteredLoggingTest/Tigers.m Demos/RegisteredDynamicLogging/Mobile/RegisteredLoggingTest/en.lproj/InfoPlist.strings Demos/RegisteredDynamicLogging/Mobile/RegisteredLoggingTest/en.lproj/MainWindow.xib Demos/RegisteredDynamicLogging/Mobile/RegisteredLoggingTest/en.lproj/RegisteredLoggingTestViewController.xib Demos/RegisteredDynamicLogging/Mobile/RegisteredLoggingTest/main.m Demos/RollingTestMac/English.lproj/InfoPlist.strings Demos/RollingTestMac/English.lproj/MainMenu.xib Demos/RollingTestMac/RollingTestMac-Info.plist Demos/RollingTestMac/RollingTestMac.xcodeproj/project.pbxproj Demos/RollingTestMac/RollingTestMac.xcodeproj/xcshareddata/IDETemplateMacros.plist Demos/RollingTestMac/RollingTestMac.xcodeproj/xcshareddata/xcschemes/RollingTestMac.xcscheme Demos/RollingTestMac/RollingTestMacAppDelegate.h Demos/RollingTestMac/RollingTestMacAppDelegate.m Demos/RollingTestMac/RollingTestMac_Prefix.pch Demos/RollingTestMac/main.m Demos/SQLiteLogger/FMDB/FMDatabase.h Demos/SQLiteLogger/FMDB/FMDatabase.m Demos/SQLiteLogger/FMDB/FMDatabaseAdditions.h Demos/SQLiteLogger/FMDB/FMDatabaseAdditions.m Demos/SQLiteLogger/FMDB/FMResultSet.h Demos/SQLiteLogger/FMDB/FMResultSet.m Demos/SQLiteLogger/FMDBLogger.h Demos/SQLiteLogger/FMDBLogger.m Demos/SQLiteLogger/SQLiteLogger.xcodeproj/project.pbxproj Demos/SQLiteLogger/SQLiteLogger.xcodeproj/project.xcworkspace/contents.xcworkspacedata Demos/SQLiteLogger/SQLiteLogger.xcodeproj/xcshareddata/IDETemplateMacros.plist Demos/SQLiteLogger/SQLiteLogger.xcodeproj/xcshareddata/xcschemes/SQLiteLogger.xcscheme Demos/SQLiteLogger/SQLiteLogger/SQLiteLogger-Info.plist Demos/SQLiteLogger/SQLiteLogger/SQLiteLogger-Prefix.pch Demos/SQLiteLogger/SQLiteLogger/SQLiteLoggerAppDelegate.h Demos/SQLiteLogger/SQLiteLogger/SQLiteLoggerAppDelegate.m Demos/SQLiteLogger/SQLiteLogger/en.lproj/Credits.rtf Demos/SQLiteLogger/SQLiteLogger/en.lproj/InfoPlist.strings Demos/SQLiteLogger/SQLiteLogger/en.lproj/MainMenu.xib Demos/SQLiteLogger/SQLiteLogger/main.m Demos/TestXcodeColors/Desktop/TestXcodeColors.xcodeproj/project.pbxproj Demos/TestXcodeColors/Desktop/TestXcodeColors.xcodeproj/xcshareddata/IDETemplateMacros.plist Demos/TestXcodeColors/Desktop/TestXcodeColors.xcodeproj/xcshareddata/xcschemes/TestXcodeColors (Desktop).xcscheme Demos/TestXcodeColors/Desktop/TestXcodeColors/AppDelegate.h Demos/TestXcodeColors/Desktop/TestXcodeColors/AppDelegate.m Demos/TestXcodeColors/Desktop/TestXcodeColors/TestXcodeColors-Info.plist Demos/TestXcodeColors/Desktop/TestXcodeColors/TestXcodeColors-Prefix.pch Demos/TestXcodeColors/Desktop/TestXcodeColors/en.lproj/Credits.rtf Demos/TestXcodeColors/Desktop/TestXcodeColors/en.lproj/InfoPlist.strings Demos/TestXcodeColors/Desktop/TestXcodeColors/en.lproj/MainMenu.xib Demos/TestXcodeColors/Desktop/TestXcodeColors/main.m Demos/TestXcodeColors/Mobile/TextXcodeColors.xcodeproj/project.pbxproj Demos/TestXcodeColors/Mobile/TextXcodeColors.xcodeproj/xcshareddata/IDETemplateMacros.plist Demos/TestXcodeColors/Mobile/TextXcodeColors.xcodeproj/xcshareddata/xcschemes/TextXcodeColors (Mobile).xcscheme Demos/TestXcodeColors/Mobile/TextXcodeColors/AppDelegate.h Demos/TestXcodeColors/Mobile/TextXcodeColors/AppDelegate.m Demos/TestXcodeColors/Mobile/TextXcodeColors/TextXcodeColors-Info.plist Demos/TestXcodeColors/Mobile/TextXcodeColors/TextXcodeColors-Prefix.pch Demos/TestXcodeColors/Mobile/TextXcodeColors/ViewController.h Demos/TestXcodeColors/Mobile/TextXcodeColors/ViewController.m Demos/TestXcodeColors/Mobile/TextXcodeColors/en.lproj/InfoPlist.strings Demos/TestXcodeColors/Mobile/TextXcodeColors/en.lproj/ViewController.xib Demos/TestXcodeColors/Mobile/TextXcodeColors/main.m Demos/UniversalApp/Classes/UniversalAppAppDelegate.h Demos/UniversalApp/Classes/UniversalAppAppDelegate.m Demos/UniversalApp/Classes/UniversalAppViewController.h Demos/UniversalApp/Classes/UniversalAppViewController.m Demos/UniversalApp/MainWindow.xib Demos/UniversalApp/UniversalApp-Info.plist Demos/UniversalApp/UniversalApp.xcodeproj/project.pbxproj Demos/UniversalApp/UniversalApp.xcodeproj/xcshareddata/IDETemplateMacros.plist Demos/UniversalApp/UniversalApp.xcodeproj/xcshareddata/xcschemes/UniversalApp.xcscheme Demos/UniversalApp/UniversalAppViewController.xib Demos/UniversalApp/UniversalApp_Prefix.pch Demos/UniversalApp/main.m Demos/WebServerIPhone/Classes/MyHTTPConnection.h Demos/WebServerIPhone/Classes/MyHTTPConnection.m Demos/WebServerIPhone/Classes/WebServerIPhoneAppDelegate.h Demos/WebServerIPhone/Classes/WebServerIPhoneAppDelegate.m Demos/WebServerIPhone/Classes/WebServerIPhoneViewController.h Demos/WebServerIPhone/Classes/WebServerIPhoneViewController.m Demos/WebServerIPhone/Classes/WebSocketLogger.h Demos/WebServerIPhone/Classes/WebSocketLogger.m Demos/WebServerIPhone/MainWindow.xib Demos/WebServerIPhone/Vendor/CocoaAsyncSocket/GCDAsyncSocket.h Demos/WebServerIPhone/Vendor/CocoaAsyncSocket/GCDAsyncSocket.m Demos/WebServerIPhone/Vendor/CocoaHTTPServer/Categories/DDData.h Demos/WebServerIPhone/Vendor/CocoaHTTPServer/Categories/DDData.m Demos/WebServerIPhone/Vendor/CocoaHTTPServer/Categories/DDNumber.h Demos/WebServerIPhone/Vendor/CocoaHTTPServer/Categories/DDNumber.m Demos/WebServerIPhone/Vendor/CocoaHTTPServer/Categories/DDRange.h Demos/WebServerIPhone/Vendor/CocoaHTTPServer/Categories/DDRange.m Demos/WebServerIPhone/Vendor/CocoaHTTPServer/HTTPAuthenticationRequest.h Demos/WebServerIPhone/Vendor/CocoaHTTPServer/HTTPAuthenticationRequest.m Demos/WebServerIPhone/Vendor/CocoaHTTPServer/HTTPConnection.h Demos/WebServerIPhone/Vendor/CocoaHTTPServer/HTTPConnection.m Demos/WebServerIPhone/Vendor/CocoaHTTPServer/HTTPLogging.h Demos/WebServerIPhone/Vendor/CocoaHTTPServer/HTTPMessage.h Demos/WebServerIPhone/Vendor/CocoaHTTPServer/HTTPMessage.m Demos/WebServerIPhone/Vendor/CocoaHTTPServer/HTTPResponse.h Demos/WebServerIPhone/Vendor/CocoaHTTPServer/HTTPServer.h Demos/WebServerIPhone/Vendor/CocoaHTTPServer/HTTPServer.m Demos/WebServerIPhone/Vendor/CocoaHTTPServer/Mime/MultipartFormDataParser.h Demos/WebServerIPhone/Vendor/CocoaHTTPServer/Mime/MultipartFormDataParser.m Demos/WebServerIPhone/Vendor/CocoaHTTPServer/Mime/MultipartMessageHeader.h Demos/WebServerIPhone/Vendor/CocoaHTTPServer/Mime/MultipartMessageHeader.m Demos/WebServerIPhone/Vendor/CocoaHTTPServer/Mime/MultipartMessageHeaderField.h Demos/WebServerIPhone/Vendor/CocoaHTTPServer/Mime/MultipartMessageHeaderField.m Demos/WebServerIPhone/Vendor/CocoaHTTPServer/Responses/HTTPAsyncFileResponse.h Demos/WebServerIPhone/Vendor/CocoaHTTPServer/Responses/HTTPAsyncFileResponse.m Demos/WebServerIPhone/Vendor/CocoaHTTPServer/Responses/HTTPDataResponse.h Demos/WebServerIPhone/Vendor/CocoaHTTPServer/Responses/HTTPDataResponse.m Demos/WebServerIPhone/Vendor/CocoaHTTPServer/Responses/HTTPDynamicFileResponse.h Demos/WebServerIPhone/Vendor/CocoaHTTPServer/Responses/HTTPDynamicFileResponse.m Demos/WebServerIPhone/Vendor/CocoaHTTPServer/Responses/HTTPErrorResponse.h Demos/WebServerIPhone/Vendor/CocoaHTTPServer/Responses/HTTPErrorResponse.m Demos/WebServerIPhone/Vendor/CocoaHTTPServer/Responses/HTTPFileResponse.h Demos/WebServerIPhone/Vendor/CocoaHTTPServer/Responses/HTTPFileResponse.m Demos/WebServerIPhone/Vendor/CocoaHTTPServer/Responses/HTTPRedirectResponse.h Demos/WebServerIPhone/Vendor/CocoaHTTPServer/Responses/HTTPRedirectResponse.m Demos/WebServerIPhone/Vendor/CocoaHTTPServer/WebSocket.h Demos/WebServerIPhone/Vendor/CocoaHTTPServer/WebSocket.m Demos/WebServerIPhone/Web/jquery-1.4.2.min.js Demos/WebServerIPhone/Web/styles.css Demos/WebServerIPhone/WebServerIPhone-Info.plist Demos/WebServerIPhone/WebServerIPhone.xcodeproj/project.pbxproj Demos/WebServerIPhone/WebServerIPhone.xcodeproj/xcshareddata/IDETemplateMacros.plist Demos/WebServerIPhone/WebServerIPhone.xcodeproj/xcshareddata/xcschemes/WebServerIPhone.xcscheme Demos/WebServerIPhone/WebServerIPhoneViewController.xib Demos/WebServerIPhone/WebServerIPhone_Prefix.pch Demos/WebServerIPhone/main.m Documentation/CocoaLumberjack.mdj Integration/Integration.xcodeproj/project.pbxproj Integration/Integration.xcodeproj/project.xcworkspace/contents.xcworkspacedata Integration/Integration.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist Integration/Integration.xcodeproj/xcshareddata/IDETemplateMacros.plist Integration/Integration.xcodeproj/xcshareddata/xcschemes/iOSFrameworkIntegration.xcscheme Integration/Integration.xcodeproj/xcshareddata/xcschemes/iOSStaticLibraryIntegration.xcscheme Integration/Integration.xcodeproj/xcshareddata/xcschemes/iOSSwiftIntegration.xcscheme Integration/Integration.xcodeproj/xcshareddata/xcschemes/macOSSwiftIntegration.xcscheme Integration/Integration.xcodeproj/xcshareddata/xcschemes/tvOSSwiftIntegration.xcscheme Integration/Integration.xcodeproj/xcshareddata/xcschemes/watchOSSwiftIntegration (Notification).xcscheme Integration/Integration.xcodeproj/xcshareddata/xcschemes/watchOSSwiftIntegration.xcscheme Integration/Sources/AppDelegate.h Integration/Sources/AppDelegate.m Integration/Sources/AppDelegate.swift Integration/Sources/Formatter.swift Integration/Sources/ViewController.h Integration/Sources/ViewController.m Integration/Sources/ViewController.swift Integration/Sources/main.m Integration/iOSFrameworkIntegration/Info.plist Integration/iOSStaticLibraryIntegration/Info.plist Integration/iOSSwiftIntegration/Info.plist Integration/macOSSwiftIntegration/Info.plist Integration/macOSSwiftIntegration/macOSSwiftIntegration.entitlements Integration/macOSSwiftIntegration/main.swift Integration/tvOSSwiftIntegration/Info.plist Integration/watchOSSwiftIntegration Extension/Assets.xcassets/Complication.complicationset/Circular.imageset/Contents.json Integration/watchOSSwiftIntegration Extension/Assets.xcassets/Complication.complicationset/Contents.json Integration/watchOSSwiftIntegration Extension/Assets.xcassets/Complication.complicationset/Extra Large.imageset/Contents.json Integration/watchOSSwiftIntegration Extension/Assets.xcassets/Complication.complicationset/Graphic Bezel.imageset/Contents.json Integration/watchOSSwiftIntegration Extension/Assets.xcassets/Complication.complicationset/Graphic Circular.imageset/Contents.json Integration/watchOSSwiftIntegration Extension/Assets.xcassets/Complication.complicationset/Graphic Corner.imageset/Contents.json Integration/watchOSSwiftIntegration Extension/Assets.xcassets/Complication.complicationset/Graphic Large Rectangular.imageset/Contents.json Integration/watchOSSwiftIntegration Extension/Assets.xcassets/Complication.complicationset/Modular.imageset/Contents.json Integration/watchOSSwiftIntegration Extension/Assets.xcassets/Complication.complicationset/Utilitarian.imageset/Contents.json Integration/watchOSSwiftIntegration Extension/Assets.xcassets/Contents.json Integration/watchOSSwiftIntegration Extension/ExtensionDelegate.swift Integration/watchOSSwiftIntegration Extension/Info.plist Integration/watchOSSwiftIntegration Extension/InterfaceController.swift Integration/watchOSSwiftIntegration Extension/NotificationController.swift Integration/watchOSSwiftIntegration Extension/PushNotificationPayload.apns Integration/watchOSSwiftIntegration/Base.lproj/Interface.storyboard Integration/watchOSSwiftIntegration/Info.plist LICENSE Lumberjack.xcodeproj/project.pbxproj Lumberjack.xcodeproj/project.xcworkspace/contents.xcworkspacedata Lumberjack.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist Lumberjack.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings Lumberjack.xcodeproj/xcshareddata/IDETemplateMacros.plist Lumberjack.xcodeproj/xcshareddata/xcschemes/CocoaLumberjack-Static.xcscheme Lumberjack.xcodeproj/xcshareddata/xcschemes/CocoaLumberjack.xcscheme Lumberjack.xcodeproj/xcshareddata/xcschemes/CocoaLumberjackSwift.xcscheme Lumberjack.xcodeproj/xcshareddata/xcschemes/Lumberjack (OS X/ iOS 8+).xcscheme Package.resolved Package.swift Package@swift-5.0.swift Package@swift-5.1.swift Package@swift-5.2.swift Scripts/ci-select-xcode.sh Scripts/update-copyright.sh Sources/CocoaLumberjack/CLI/CLIColor.m Sources/CocoaLumberjack/DDASLLogCapture.m Sources/CocoaLumberjack/DDASLLogger.m Sources/CocoaLumberjack/DDAbstractDatabaseLogger.m Sources/CocoaLumberjack/DDFileLogger+Internal.h Sources/CocoaLumberjack/DDFileLogger.m Sources/CocoaLumberjack/DDLog.m Sources/CocoaLumberjack/DDLoggerNames.m Sources/CocoaLumberjack/DDOSLogger.m Sources/CocoaLumberjack/DDTTYLogger.m Sources/CocoaLumberjack/Extensions/DDContextFilterLogFormatter+Deprecated.m Sources/CocoaLumberjack/Extensions/DDContextFilterLogFormatter.m Sources/CocoaLumberjack/Extensions/DDDispatchQueueLogFormatter.m Sources/CocoaLumberjack/Extensions/DDFileLogger+Buffering.m Sources/CocoaLumberjack/Extensions/DDMultiFormatter.m Sources/CocoaLumberjack/Supporting Files/CocoaLumberjack-Info.plist Sources/CocoaLumberjack/Supporting Files/CocoaLumberjack.h Sources/CocoaLumberjack/Supporting Files/DDLegacyMacros.h Sources/CocoaLumberjack/include/CocoaLumberjack/CLIColor.h Sources/CocoaLumberjack/include/CocoaLumberjack/DDASLLogCapture.h Sources/CocoaLumberjack/include/CocoaLumberjack/DDASLLogger.h Sources/CocoaLumberjack/include/CocoaLumberjack/DDAbstractDatabaseLogger.h Sources/CocoaLumberjack/include/CocoaLumberjack/DDAssertMacros.h Sources/CocoaLumberjack/include/CocoaLumberjack/DDContextFilterLogFormatter+Deprecated.h Sources/CocoaLumberjack/include/CocoaLumberjack/DDContextFilterLogFormatter.h Sources/CocoaLumberjack/include/CocoaLumberjack/DDDispatchQueueLogFormatter.h Sources/CocoaLumberjack/include/CocoaLumberjack/DDFileLogger+Buffering.h Sources/CocoaLumberjack/include/CocoaLumberjack/DDFileLogger.h Sources/CocoaLumberjack/include/CocoaLumberjack/DDLog+LOGV.h Sources/CocoaLumberjack/include/CocoaLumberjack/DDLog.h Sources/CocoaLumberjack/include/CocoaLumberjack/DDLogMacros.h Sources/CocoaLumberjack/include/CocoaLumberjack/DDLoggerNames.h Sources/CocoaLumberjack/include/CocoaLumberjack/DDMultiFormatter.h Sources/CocoaLumberjack/include/CocoaLumberjack/DDOSLogger.h Sources/CocoaLumberjack/include/CocoaLumberjack/DDTTYLogger.h Sources/CocoaLumberjackSwift/CocoaLumberjack.swift Sources/CocoaLumberjackSwift/DDAssert.swift Sources/CocoaLumberjackSwift/DDLog+Combine.swift Sources/CocoaLumberjackSwift/Supporting Files/CocoaLumberjackSwift-Info.plist Sources/CocoaLumberjackSwift/Supporting Files/CocoaLumberjackSwift.h Sources/CocoaLumberjackSwiftLogBackend/DDLogHandler.swift Sources/CocoaLumberjackSwiftSupport/empty.c Sources/CocoaLumberjackSwiftSupport/include/CocoaLumberjackSwiftSupport/SwiftLogLevel.h Tests/CocoaLumberjackSwiftLogBackendTests/DDLogHandlerTests.swift Tests/CocoaLumberjackSwiftTests/DDLogCombineTests.swift Tests/CocoaLumberjackTests/DDAtomicCounterTests.m Tests/CocoaLumberjackTests/DDBasicLoggingTests.m Tests/CocoaLumberjackTests/DDContextFilterLogFormatter+DeprecatedTests.m Tests/CocoaLumberjackTests/DDContextFilterLogFormatterTests.m Tests/CocoaLumberjackTests/DDFileLoggerPerformanceTests.m Tests/CocoaLumberjackTests/DDFileLoggerTests.m Tests/CocoaLumberjackTests/DDLogFileManagerTests.m Tests/CocoaLumberjackTests/DDLogMessageTests.m Tests/CocoaLumberjackTests/DDLogTests.m Tests/CocoaLumberjackTests/DDOSLoggingTests.m Tests/CocoaLumberjackTests/DDSMocking.h Tests/CocoaLumberjackTests/DDSMocking.m Tests/CocoaLumberjackTests/DDSampleFileManager.h Tests/CocoaLumberjackTests/DDSampleFileManager.m Tests/Info.plist Tests/Tests.xcodeproj/project.pbxproj Tests/Tests.xcodeproj/project.xcworkspace/contents.xcworkspacedata Tests/Tests.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist Tests/Tests.xcodeproj/xcshareddata/IDETemplateMacros.plist Tests/Tests.xcodeproj/xcshareddata/xcbaselines/432B533F1AAE425D00843E69.xcbaseline/AB613018-19D0-4E38-845A-D0F077AAF499.plist Tests/Tests.xcodeproj/xcshareddata/xcbaselines/432B533F1AAE425D00843E69.xcbaseline/Info.plist Tests/Tests.xcodeproj/xcshareddata/xcschemes/OS X Tests.xcscheme Tests/Tests.xcodeproj/xcshareddata/xcschemes/Swift Tests.xcscheme Tests/Tests.xcodeproj/xcshareddata/xcschemes/iOS Tests.xcscheme Xcode/Lumberjack.xcworkspace/contents.xcworkspacedata Xcode/Lumberjack.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist Xcode/Lumberjack/CocoaLumberjack-Prefix.pch uncrustify.cfg <<<<<< network # path=./CocoaLumberjackSwift.framework.coverage.txt /Users/runner/work/CocoaLumberjack/CocoaLumberjack/Sources/CocoaLumberjackSwift/CocoaLumberjack.swift: 1| |// Software License Agreement (BSD License) 2| |// 3| |// Copyright (c) 2010-2021, Deusty, LLC 4| |// All rights reserved. 5| |// 6| |// Redistribution and use of this software in source and binary forms, 7| |// with or without modification, are permitted provided that the following conditions are met: 8| |// 9| |// * Redistributions of source code must retain the above copyright notice, 10| |// this list of conditions and the following disclaimer. 11| |// 12| |// * Neither the name of Deusty nor the names of its contributors may be used 13| |// to endorse or promote products derived from this software without specific 14| |// prior written permission of Deusty, LLC. 15| | 16| |@_exported import CocoaLumberjack 17| |#if SWIFT_PACKAGE 18| |import CocoaLumberjackSwiftSupport 19| |#endif 20| | 21| |extension DDLogFlag { 22| 0| public static func from(_ logLevel: DDLogLevel) -> DDLogFlag { 23| 0| return DDLogFlag(rawValue: logLevel.rawValue) 24| 0| } 25| | 26| 0| public init(_ logLevel: DDLogLevel) { 27| 0| self = DDLogFlag(rawValue: logLevel.rawValue) 28| 0| } 29| | 30| | /// Returns the log level, or the lowest equivalent. 31| 0| public func toLogLevel() -> DDLogLevel { 32| 0| if let ourValid = DDLogLevel(rawValue: rawValue) { 33| 0| return ourValid 34| 0| } else { 35| 0| if contains(.verbose) { 36| 0| return .verbose 37| 0| } else if contains(.debug) { 38| 0| return .debug 39| 0| } else if contains(.info) { 40| 0| return .info 41| 0| } else if contains(.warning) { 42| 0| return .warning 43| 0| } else if contains(.error) { 44| 0| return .error 45| 0| } else { 46| 0| return .off 47| 0| } 48| 0| } 49| 0| } 50| |} 51| | 52| |/// The log level that can dynamically limit log messages (vs. the static DDDefaultLogLevel). This log level will only be checked, if the message passes the `DDDefaultLogLevel`. 53| |public var dynamicLogLevel = DDLogLevel.all 54| | 55| |/// Resets the `dynamicLogLevel` to `.all`. 56| |/// - SeeAlso: `dynamicLogLevel` 57| |@inlinable 58| 0|public func resetDynamicLogLevel() { 59| 0| dynamicLogLevel = .all 60| 0|} 61| | 62| |@available(*, deprecated, message: "Please use dynamicLogLevel", renamed: "dynamicLogLevel") 63| |public var defaultDebugLevel: DDLogLevel { 64| 0| get { 65| 0| return dynamicLogLevel 66| 0| } 67| 0| set { 68| 0| dynamicLogLevel = newValue 69| 0| } 70| |} 71| | 72| |@available(*, deprecated, message: "Please use resetDynamicLogLevel", renamed: "resetDynamicLogLevel") 73| 0|public func resetDefaultDebugLevel() { 74| 0| resetDynamicLogLevel() 75| 0|} 76| | 77| |/// If `true`, all logs (except errors) are logged asynchronously by default. 78| |public var asyncLoggingEnabled = true 79| | 80| |@inlinable 81| |public func _DDLogMessage(_ message: @autoclosure () -> Any, 82| | level: DDLogLevel, 83| | flag: DDLogFlag, 84| | context: Int, 85| | file: StaticString, 86| | function: StaticString, 87| | line: UInt, 88| | tag: Any?, 89| | asynchronous: Bool, 90| 10| ddlog: DDLog) { 91| 10| // The `dynamicLogLevel` will always be checked here (instead of being passed in). 92| 10| // We cannot "mix" it with the `DDDefaultLogLevel`, because otherwise the compiler won't strip strings that are not logged. 93| 10| if level.rawValue & flag.rawValue != 0 && dynamicLogLevel.rawValue & flag.rawValue != 0 { 94| 10| // Tell the DDLogMessage constructor to copy the C strings that get passed to it. 95| 10| let logMessage = DDLogMessage(message: String(describing: message()), 96| 10| level: level, 97| 10| flag: flag, 98| 10| context: context, 99| 10| file: String(describing: file), 100| 10| function: String(describing: function), 101| 10| line: line, 102| 10| tag: tag, 103| 10| options: [.copyFile, .copyFunction], 104| 10| timestamp: nil) 105| 10| ddlog.log(asynchronous: asynchronous, message: logMessage) 106| 10| } 107| 10|} 108| | 109| |@inlinable 110| |public func DDLogDebug(_ message: @autoclosure () -> Any, 111| | level: DDLogLevel = DDDefaultLogLevel, 112| | context: Int = 0, 113| | file: StaticString = #file, 114| | function: StaticString = #function, 115| | line: UInt = #line, 116| | tag: Any? = nil, 117| | asynchronous async: Bool = asyncLoggingEnabled, 118| 2| ddlog: DDLog = .sharedInstance) { 119| 2| _DDLogMessage(message(), level: level, flag: .debug, context: context, file: file, function: function, line: line, tag: tag, asynchronous: async, ddlog: ddlog) 120| 2|} 121| | 122| |@inlinable 123| |public func DDLogInfo(_ message: @autoclosure () -> Any, 124| | level: DDLogLevel = DDDefaultLogLevel, 125| | context: Int = 0, 126| | file: StaticString = #file, 127| | function: StaticString = #function, 128| | line: UInt = #line, 129| | tag: Any? = nil, 130| | asynchronous async: Bool = asyncLoggingEnabled, 131| 2| ddlog: DDLog = .sharedInstance) { 132| 2| _DDLogMessage(message(), level: level, flag: .info, context: context, file: file, function: function, line: line, tag: tag, asynchronous: async, ddlog: ddlog) 133| 2|} 134| | 135| |@inlinable 136| |public func DDLogWarn(_ message: @autoclosure () -> Any, 137| | level: DDLogLevel = DDDefaultLogLevel, 138| | context: Int = 0, 139| | file: StaticString = #file, 140| | function: StaticString = #function, 141| | line: UInt = #line, 142| | tag: Any? = nil, 143| | asynchronous async: Bool = asyncLoggingEnabled, 144| 2| ddlog: DDLog = .sharedInstance) { 145| 2| _DDLogMessage(message(), level: level, flag: .warning, context: context, file: file, function: function, line: line, tag: tag, asynchronous: async, ddlog: ddlog) 146| 2|} 147| | 148| |@inlinable 149| |public func DDLogVerbose(_ message: @autoclosure () -> Any, 150| | level: DDLogLevel = DDDefaultLogLevel, 151| | context: Int = 0, 152| | file: StaticString = #file, 153| | function: StaticString = #function, 154| | line: UInt = #line, 155| | tag: Any? = nil, 156| | asynchronous async: Bool = asyncLoggingEnabled, 157| 2| ddlog: DDLog = .sharedInstance) { 158| 2| _DDLogMessage(message(), level: level, flag: .verbose, context: context, file: file, function: function, line: line, tag: tag, asynchronous: async, ddlog: ddlog) 159| 2|} 160| | 161| |@inlinable 162| |public func DDLogError(_ message: @autoclosure () -> Any, 163| | level: DDLogLevel = DDDefaultLogLevel, 164| | context: Int = 0, 165| | file: StaticString = #file, 166| | function: StaticString = #function, 167| | line: UInt = #line, 168| | tag: Any? = nil, 169| | asynchronous async: Bool = false, 170| 2| ddlog: DDLog = .sharedInstance) { 171| 2| _DDLogMessage(message(), level: level, flag: .error, context: context, file: file, function: function, line: line, tag: tag, asynchronous: async, ddlog: ddlog) 172| 2|} 173| | 174| |/// Returns a String of the current filename, without full path or extension. 175| |/// 176| |/// Analogous to the C preprocessor macro `THIS_FILE`. 177| 0|public func currentFileName(_ fileName: StaticString = #file) -> String { 178| 0| var str = String(describing: fileName) 179| 0| if let idx = str.range(of: "/", options: .backwards)?.upperBound { 180| 0| str = String(str[idx...]) 181| 0| } 182| 0| if let idx = str.range(of: ".", options: .backwards)?.lowerBound { 183| 0| str = String(str[.. String { 192| 0| return currentFileName(fileName) 193| 0|} /Users/runner/work/CocoaLumberjack/CocoaLumberjack/Sources/CocoaLumberjackSwift/DDAssert.swift: 1| |// Software License Agreement (BSD License) 2| |// 3| |// Copyright (c) 2010-2021, Deusty, LLC 4| |// All rights reserved. 5| |// 6| |// Redistribution and use of this software in source and binary forms, 7| |// with or without modification, are permitted provided that the following conditions are met: 8| |// 9| |// * Redistributions of source code must retain the above copyright notice, 10| |// this list of conditions and the following disclaimer. 11| |// 12| |// * Neither the name of Deusty nor the names of its contributors may be used 13| |// to endorse or promote products derived from this software without specific 14| |// prior written permission of Deusty, LLC. 15| | 16| |#if SWIFT_PACKAGE 17| |import CocoaLumberjack 18| |import CocoaLumberjackSwiftSupport 19| |#endif 20| | 21| |/** 22| | * Replacement for Swift's `assert` function that will output a log message even when assertions 23| | * are disabled. 24| | * 25| | * - Parameters: 26| | * - condition: The condition to test. Unlike `Swift.assert`, `condition` is always evaluated, 27| | * even when assertions are disabled. 28| | * - message: A string to log (using `DDLogError`) if `condition` evaluates to `false`. 29| | * The default is an empty string. 30| | */ 31| |@inlinable 32| 0|public func DDAssert(_ condition: @autoclosure () -> Bool, _ message: @autoclosure () -> String = "", level: DDLogLevel = DDDefaultLogLevel, context: Int = 0, file: StaticString = #file, function: StaticString = #function, line: UInt = #line, tag: Any? = nil, asynchronous async: Bool = false, ddlog: DDLog = DDLog.sharedInstance) { 33| 0| if !condition() { 34| 0| DDLogError(message(), level: level, context: context, file: file, function: function, line: line, tag: tag, asynchronous: async, ddlog: ddlog) 35| 0| Swift.assertionFailure(message(), file: file, line: line) 36| 0| } 37| 0|} 38| | 39| |/** 40| | * Replacement for Swift's `assertionFailure` function that will output a log message even 41| | * when assertions are disabled. 42| | * 43| | * - Parameters: 44| | * - message: A string to log (using `DDLogError`). The default is an empty string. 45| | */ 46| |@inlinable 47| 0|public func DDAssertionFailure(_ message: @autoclosure () -> String = "", level: DDLogLevel = DDDefaultLogLevel, context: Int = 0, file: StaticString = #file, function: StaticString = #function, line: UInt = #line, tag: Any? = nil, asynchronous async: Bool = false, ddlog: DDLog = DDLog.sharedInstance) { 48| 0| DDLogError(message(), level: level, context: context, file: file, function: function, line: line, tag: tag, asynchronous: async, ddlog: ddlog) 49| 0| Swift.assertionFailure(message(), file: file, line: line) 50| 0|} /Users/runner/work/CocoaLumberjack/CocoaLumberjack/Sources/CocoaLumberjackSwift/DDLog+Combine.swift: 1| |// Software License Agreement (BSD License) 2| |// 3| |// Copyright (c) 2010-2021, Deusty, LLC 4| |// All rights reserved. 5| |// 6| |// Redistribution and use of this software in source and binary forms, 7| |// with or without modification, are permitted provided that the following conditions are met: 8| |// 9| |// * Redistributions of source code must retain the above copyright notice, 10| |// this list of conditions and the following disclaimer. 11| |// 12| |// * Neither the name of Deusty nor the names of its contributors may be used 13| |// to endorse or promote products derived from this software without specific 14| |// prior written permission of Deusty, LLC. 15| | 16| |#if canImport(Combine) 17| | 18| |import Combine 19| | 20| |#if SWIFT_PACKAGE 21| |import CocoaLumberjack 22| |import CocoaLumberjackSwiftSupport 23| |#endif 24| | 25| |@available(OSX 10.15, iOS 13.0, tvOS 13.0, watchOS 6.0, *) 26| |extension DDLog { 27| | 28| | /** 29| | * Creates a message publisher. 30| | * 31| | * The publisher will add and remove loggers as subscriptions are added and removed. 32| | * 33| | * The level that you provide here is a preemptive filter (for performance). 34| | * That is, the level specified here will be used to filter out logMessages so that 35| | * the logger is never even invoked for the messages. 36| | * 37| | * More information: 38| | * See -[DDLog addLogger:with:] 39| | * 40| | * - Parameter logLevel: preemptive filter of the message returned by the publisher. All levels are sent by default 41| | * - Returns: A MessagePublisher that emits LogMessages filtered by the specified logLevel 42| | **/ 43| 5| public func messagePublisher(with logLevel: DDLogLevel = .all) -> MessagePublisher { 44| 5| return MessagePublisher(log: self, with: logLevel) 45| 5| } 46| | 47| | // MARK: - MessagePublisher 48| | 49| | @available(OSX 10.15, iOS 13.0, tvOS 13.0, watchOS 6.0, *) 50| | public struct MessagePublisher: Combine.Publisher { 51| | 52| | public typealias Output = DDLogMessage 53| | public typealias Failure = Never 54| | 55| | private let log: DDLog 56| | private let logLevel: DDLogLevel 57| | 58| 5| public init(log: DDLog, with logLevel: DDLogLevel) { 59| 5| self.log = log 60| 5| self.logLevel = logLevel 61| 5| } 62| | 63| 5| public func receive(subscriber: S) where S: Subscriber, S.Failure == Failure, S.Input == Output { 64| 5| 65| 5| let subscription = Subscription(log: self.log, with: logLevel, subscriber: subscriber) 66| 5| subscriber.receive(subscription: subscription) 67| 5| } 68| | } 69| | 70| | // MARK: - Subscription 71| | 72| | private final class Subscription: NSObject, DDLogger, Combine.Subscription 73| | where S.Input == DDLogMessage 74| | { 75| | private var subscriber: S? 76| | private weak var log: DDLog? 77| | 78| | //Not used but DDLogger requires it. The preferred way to achieve this is to use the `map()` Combine operator of the publisher. 79| | //ie: 80| | // DDLog.sharedInstance.messagePublisher() 81| | // .map { [format log message] } 82| | // .sink(receiveValue: { [process log message] }) 83| | // 84| | var logFormatter: DDLogFormatter? = nil 85| | 86| 5| let combineIdentifier = CombineIdentifier() 87| | 88| 5| init(log: DDLog, with logLevel: DDLogLevel, subscriber: S) { 89| 5| 90| 5| self.subscriber = subscriber 91| 5| self.log = log 92| 5| 93| 5| super.init() 94| 5| 95| 5| log.add(self, with: logLevel) 96| 5| } 97| | 98| 5| func request(_ demand: Subscribers.Demand) { 99| 5| //The log messages are endless until canceled, so we won't do any demand management. 100| 5| //Combine operators can be used to deal with it as needed. 101| 5| } 102| | 103| 5| func cancel() { 104| 5| self.log?.remove(self) 105| 5| self.subscriber = nil 106| 5| } 107| | 108| 7| func log(message logMessage: DDLogMessage) { 109| 7| _ = self.subscriber?.receive(logMessage) 110| 7| } 111| | } 112| |} 113| | 114| |@available(OSX 10.15, iOS 13.0, tvOS 13.0, watchOS 6.0, *) 115| |extension Publisher where Output == DDLogMessage { 116| | 117| 1| public func formatted(with formatter: DDLogFormatter) -> Publishers.CompactMap { 118| 2| return compactMap { formatter.format(message: $0) } 119| 1| } 120| |} 121| | 122| |#endif <<<<<< EOF # path=./SwiftTests.xctest.coverage.txt 1| |// Software License Agreement (BSD License) 2| |// 3| |// Copyright (c) 2010-2021, Deusty, LLC 4| |// All rights reserved. 5| |// 6| |// Redistribution and use of this software in source and binary forms, 7| |// with or without modification, are permitted provided that the following conditions are met: 8| |// 9| |// * Redistributions of source code must retain the above copyright notice, 10| |// this list of conditions and the following disclaimer. 11| |// 12| |// * Neither the name of Deusty nor the names of its contributors may be used 13| |// to endorse or promote products derived from this software without specific 14| |// prior written permission of Deusty, LLC. 15| | 16| |#if canImport(Combine) 17| | 18| |@testable import CocoaLumberjackSwift 19| |import Combine 20| |import XCTest 21| | 22| |@available(OSX 10.15, iOS 13.0, tvOS 13.0, watchOS 6.0, *) 23| |final class DDLogCombineTests: XCTestCase { 24| | 25| 7| private var subscriptions = Set() 26| | 27| 1| private var logFormatter: DDLogFileFormatterDefault { 28| 1| //let's return a formatter that doesn't change based where the 29| 1| //test is being run. 30| 1| let formatter = DateFormatter() 31| 1| formatter.dateFormat = "yyyy/MM/dd HH:mm:ss:SSS" 32| 1| formatter.calendar = Calendar(identifier: .gregorian) 33| 1| formatter.locale = Locale(identifier: "en_US_POSIX") 34| 1| formatter.timeZone = TimeZone(secondsFromGMT: 0) 35| 1| return DDLogFileFormatterDefault(dateFormatter: formatter) 36| 1| } 37| | 38| 7| override func setUp() { 39| 7| super.setUp() 40| 7| DDLog.removeAllLoggers() 41| 7| } 42| | 43| 7| override func tearDown() { 44| 7| DDLog.removeAllLoggers() 45| 7| self.subscriptions.removeAll() 46| 7| super.tearDown() 47| 7| } 48| | 49| 1| func testMessagePublisherWithDDLogLevelAll() { 50| 1| DDLog.sharedInstance.messagePublisher() 51| 1| .sink(receiveValue: { _ in }) 52| 1| .store(in: &self.subscriptions) 53| 1| 54| 1| XCTAssertEqual(DDLog.allLoggers.count, 1) 55| 1| XCTAssertEqual(DDLog.allLoggersWithLevel.count, 1) 56| 1| XCTAssertEqual(DDLog.allLoggersWithLevel.last?.level, .all) 57| 1| } 58| | 59| 1| func testMessagePublisherWithSpecifiedLevelMask() { 60| 1| DDLog.sharedInstance.messagePublisher(with: .error) 61| 1| .sink(receiveValue: { _ in }) 62| 1| .store(in: &self.subscriptions) 63| 1| 64| 1| XCTAssertEqual(DDLog.allLoggers.count, 1) 65| 1| XCTAssertEqual(DDLog.allLoggersWithLevel.count, 1) 66| 1| XCTAssertEqual(DDLog.allLoggersWithLevel.last?.level, .error) 67| 1| } 68| | 69| 1| func testMessagePublisherRemovedWhenSubscriptionIsCanceled() { 70| 1| let sub = DDLog.sharedInstance.messagePublisher() 71| 1| .sink(receiveValue: { _ in }) 72| 1| 73| 1| XCTAssertEqual(DDLog.allLoggers.count, 1) 74| 1| XCTAssertEqual(DDLog.allLoggersWithLevel.count, 1) 75| 1| XCTAssertEqual(DDLog.allLoggersWithLevel.last?.level, .all) 76| 1| 77| 1| sub.cancel() 78| 1| 79| 1| XCTAssertTrue(DDLog.allLoggers.isEmpty) 80| 1| XCTAssertTrue(DDLog.allLoggersWithLevel.isEmpty) 81| 1| } 82| | 83| 1| func testReceivedValuesWithDDLogLevelAll() { 84| 1| var receivedValue = [DDLogMessage]() 85| 1| 86| 1| DDLog.sharedInstance.messagePublisher() 87| 5| .sink(receiveValue: { receivedValue.append($0) }) 88| 1| .store(in: &self.subscriptions) 89| 1| 90| 1| DDLogError("Error") 91| 1| DDLogWarn("Warn") 92| 1| DDLogInfo("Info") 93| 1| DDLogDebug("Debug") 94| 1| DDLogVerbose("Verbose") 95| 1| 96| 1| DDLog.flushLog() 97| 1| 98| 5| let messages = receivedValue.map { $0.message } 99| 1| XCTAssertEqual(messages, ["Error", 100| 1| "Warn", 101| 1| "Info", 102| 1| "Debug", 103| 1| "Verbose"]) 104| 1| 105| 5| let levels = receivedValue.map { $0.flag } 106| 1| XCTAssertEqual(levels, [.error, 107| 1| .warning, 108| 1| .info, 109| 1| .debug, 110| 1| .verbose]) 111| 1| } 112| | 113| 1| func testReceivedValuesWithDDLogLevelWarning() { 114| 1| var receivedValue = [DDLogMessage]() 115| 1| 116| 1| DDLog.sharedInstance.messagePublisher(with: .warning) 117| 2| .sink(receiveValue: { receivedValue.append($0) }) 118| 1| .store(in: &self.subscriptions) 119| 1| 120| 1| DDLogError("Error") 121| 1| DDLogWarn("Warn") 122| 1| DDLogInfo("Info") 123| 1| DDLogDebug("Debug") 124| 1| DDLogVerbose("Verbose") 125| 1| 126| 1| DDLog.flushLog() 127| 1| 128| 2| let messages = receivedValue.map { $0.message } 129| 1| XCTAssertEqual(messages, ["Error", "Warn"]) 130| 1| 131| 2| let levels = receivedValue.map { $0.flag } 132| 1| XCTAssertEqual(levels, [.error, .warning]) 133| 1| } 134| | 135| 1| func testFormatted() { 136| 1| let subject = PassthroughSubject() 137| 1| 138| 1| var receivedValue = [String]() 139| 1| 140| 1| subject 141| 1| .formatted(with: self.logFormatter) 142| 2| .sink(receiveValue: { receivedValue.append($0) }) 143| 1| .store(in: &self.subscriptions) 144| 1| 145| 1| subject.send(DDLogMessage(message: "An error occurred", 146| 1| level: .all, 147| 1| flag: .error, 148| 1| context: 42, 149| 1| file: "Combine.swift", 150| 1| function: "PerformFailure", 151| 1| line: 67, 152| 1| tag: nil, 153| 1| options: [], 154| 1| timestamp: Date(timeIntervalSinceReferenceDate: 100))) 155| 1| 156| 1| subject.send(DDLogMessage(message: "WARNING: this is incorrect", 157| 1| level: .all, 158| 1| flag: .warning, 159| 1| context: 23, 160| 1| file: "Combine.swift", 161| 1| function: "PerformWarning", 162| 1| line: 90, 163| 1| tag: nil, 164| 1| options: [], 165| 1| timestamp: Date(timeIntervalSinceReferenceDate: 200))) 166| 1| 167| 1| XCTAssertEqual(receivedValue, ["2001/01/01 00:01:40:000 An error occurred", 168| 1| "2001/01/01 00:03:20:000 WARNING: this is incorrect"]) 169| 1| } 170| | 171| 1| func testQOSNameInstanciation() { 172| 1| let name = "UI" 173| 1| let qos : qos_class_t = { 174| 1| switch DDQualityOfServiceName(rawValue: name) { 175| 1| case DDQualityOfServiceName.userInteractive: 176| 1| return QOS_CLASS_USER_INTERACTIVE 177| 1| default: 178| 0| return QOS_CLASS_UNSPECIFIED 179| 1| } 180| 1| }() 181| 1| 182| 1| XCTAssertEqual(qos, QOS_CLASS_USER_INTERACTIVE) 183| 1| } 184| |} 185| | 186| |#endif <<<<<< EOF # path=./CocoaLumberjack.framework.coverage.txt /Users/runner/work/CocoaLumberjack/CocoaLumberjack/Sources/CocoaLumberjack/DDASLLogCapture.m: 1| |// Software License Agreement (BSD License) 2| |// 3| |// Copyright (c) 2010-2021, Deusty, LLC 4| |// All rights reserved. 5| |// 6| |// Redistribution and use of this software in source and binary forms, 7| |// with or without modification, are permitted provided that the following conditions are met: 8| |// 9| |// * Redistributions of source code must retain the above copyright notice, 10| |// this list of conditions and the following disclaimer. 11| |// 12| |// * Neither the name of Deusty nor the names of its contributors may be used 13| |// to endorse or promote products derived from this software without specific 14| |// prior written permission of Deusty, LLC. 15| | 16| |#import 17| | 18| |#if !TARGET_OS_WATCH 19| | 20| |#include 21| |#include 22| |#include 23| |#include 24| | 25| |#import 26| | 27| |// Disable legacy macros 28| |#ifndef DD_LEGACY_MACROS 29| | #define DD_LEGACY_MACROS 0 30| |#endif 31| | 32| |static BOOL _cancel = YES; 33| |static DDLogLevel _captureLevel = DDLogLevelVerbose; 34| | 35| |#pragma clang diagnostic push 36| |#pragma clang diagnostic ignored "-Wdeprecated-implementations" 37| |@implementation DDASLLogCapture 38| |#pragma clang diagnostic pop 39| | 40| 0|+ (void)start { 41| 0| // Ignore subsequent calls 42| 0| if (!_cancel) { 43| 0| return; 44| 0| } 45| 0| 46| 0| _cancel = NO; 47| 0| 48| 0| dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^(void) { 49| 0| [self captureAslLogs]; 50| 0| }); 51| 0|} 52| | 53| 0|+ (void)stop { 54| 0| _cancel = YES; 55| 0|} 56| | 57| 0|+ (DDLogLevel)captureLevel { 58| 0| return _captureLevel; 59| 0|} 60| | 61| 0|+ (void)setCaptureLevel:(DDLogLevel)level { 62| 0| _captureLevel = level; 63| 0|} 64| | 65| |#pragma mark - Private methods 66| | 67| 0|+ (void)configureAslQuery:(aslmsg)query { 68| 0| const char param[] = "7"; // ASL_LEVEL_DEBUG, which is everything. We'll rely on regular DDlog log level to filter 69| 0| 70| 0| asl_set_query(query, ASL_KEY_LEVEL, param, ASL_QUERY_OP_LESS_EQUAL | ASL_QUERY_OP_NUMERIC); 71| 0| 72| 0| // Don't retrieve logs from our own DDASLLogger 73| 0| asl_set_query(query, kDDASLKeyDDLog, kDDASLDDLogValue, ASL_QUERY_OP_NOT_EQUAL); 74| 0| 75| |#if !TARGET_OS_IPHONE || (defined(TARGET_SIMULATOR) && TARGET_SIMULATOR) 76| | int processId = [[NSProcessInfo processInfo] processIdentifier]; 77| | char pid[16]; 78| | snprintf(pid, sizeof(pid), "%d", processId); 79| | asl_set_query(query, ASL_KEY_PID, pid, ASL_QUERY_OP_EQUAL | ASL_QUERY_OP_NUMERIC); 80| |#endif 81| |} 82| | 83| 0|+ (void)aslMessageReceived:(aslmsg)msg { 84| 0| const char* messageCString = asl_get( msg, ASL_KEY_MSG ); 85| 0| if ( messageCString == NULL ) 86| 0| return; 87| 0| 88| 0| DDLogFlag flag; 89| 0| BOOL async; 90| 0| 91| 0| const char* levelCString = asl_get(msg, ASL_KEY_LEVEL); 92| 0| switch (levelCString? atoi(levelCString) : 0) { 93| 0| // By default all NSLog's with a ASL_LEVEL_WARNING level 94| 0| case ASL_LEVEL_EMERG : 95| 0| case ASL_LEVEL_ALERT : 96| 0| case ASL_LEVEL_CRIT : flag = DDLogFlagError; async = NO; break; 97| 0| case ASL_LEVEL_ERR : flag = DDLogFlagWarning; async = YES; break; 98| 0| case ASL_LEVEL_WARNING : flag = DDLogFlagInfo; async = YES; break; 99| 0| case ASL_LEVEL_NOTICE : flag = DDLogFlagDebug; async = YES; break; 100| 0| case ASL_LEVEL_INFO : 101| 0| case ASL_LEVEL_DEBUG : 102| 0| default : flag = DDLogFlagVerbose; async = YES; break; 103| 0| } 104| 0| 105| 0| if (!(_captureLevel & flag)) { 106| 0| return; 107| 0| } 108| 0| 109| 0| // NSString * sender = [NSString stringWithCString:asl_get(msg, ASL_KEY_SENDER) encoding:NSUTF8StringEncoding]; 110| 0| NSString *message = @(messageCString); 111| 0| 112| 0| const char* secondsCString = asl_get( msg, ASL_KEY_TIME ); 113| 0| const char* nanoCString = asl_get( msg, ASL_KEY_TIME_NSEC ); 114| 0| NSTimeInterval seconds = secondsCString ? strtod(secondsCString, NULL) : [NSDate timeIntervalSinceReferenceDate] - NSTimeIntervalSince1970; 115| 0| double nanoSeconds = nanoCString? strtod(nanoCString, NULL) : 0; 116| 0| NSTimeInterval totalSeconds = seconds + (nanoSeconds / 1e9); 117| 0| 118| 0| NSDate *timeStamp = [NSDate dateWithTimeIntervalSince1970:totalSeconds]; 119| 0| 120| 0| DDLogMessage *logMessage = [[DDLogMessage alloc] initWithMessage:message 121| 0| level:_captureLevel 122| 0| flag:flag 123| 0| context:0 124| 0| file:@"DDASLLogCapture" 125| 0| function:nil 126| 0| line:0 127| 0| tag:nil 128| 0| options:0 129| 0| timestamp:timeStamp]; 130| 0| 131| 0| [DDLog log:async message:logMessage]; 132| 0|} 133| | 134| 0|+ (void)captureAslLogs { 135| 0| @autoreleasepool 136| 0| { 137| 0| /* 138| 0| We use ASL_KEY_MSG_ID to see each message once, but there's no 139| 0| obvious way to get the "next" ID. To bootstrap the process, we'll 140| 0| search by timestamp until we've seen a message. 141| 0| */ 142| 0| 143| 0| struct timeval timeval = { 144| 0| .tv_sec = 0 145| 0| }; 146| 0| gettimeofday(&timeval, NULL); 147| 0| unsigned long long startTime = (unsigned long long)timeval.tv_sec; 148| 0| __block unsigned long long lastSeenID = 0; 149| 0| 150| 0| /* 151| 0| syslogd posts kNotifyASLDBUpdate (com.apple.system.logger.message) 152| 0| through the notify API when it saves messages to the ASL database. 153| 0| There is some coalescing - currently it is sent at most twice per 154| 0| second - but there is no documented guarantee about this. In any 155| 0| case, there may be multiple messages per notification. 156| 0| 157| 0| Notify notifications don't carry any payload, so we need to search 158| 0| for the messages. 159| 0| */ 160| 0| int notifyToken = 0; // Can be used to unregister with notify_cancel(). 161| 0| notify_register_dispatch(kNotifyASLDBUpdate, ¬ifyToken, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^(int token) 162| 0| { 163| 0| // At least one message has been posted; build a search query. 164| 0| @autoreleasepool 165| 0| { 166| 0| aslmsg query = asl_new(ASL_TYPE_QUERY); 167| 0| char stringValue[64]; 168| 0| 169| 0| if (lastSeenID > 0) { 170| 0| snprintf(stringValue, sizeof stringValue, "%llu", lastSeenID); 171| 0| asl_set_query(query, ASL_KEY_MSG_ID, stringValue, ASL_QUERY_OP_GREATER | ASL_QUERY_OP_NUMERIC); 172| 0| } else { 173| 0| snprintf(stringValue, sizeof stringValue, "%llu", startTime); 174| 0| asl_set_query(query, ASL_KEY_TIME, stringValue, ASL_QUERY_OP_GREATER_EQUAL | ASL_QUERY_OP_NUMERIC); 175| 0| } 176| 0| 177| 0| [self configureAslQuery:query]; 178| 0| 179| 0| // Iterate over new messages. 180| 0| aslmsg msg; 181| 0| aslresponse response = asl_search(NULL, query); 182| 0| 183| 0| while ((msg = asl_next(response))) 184| 0| { 185| 0| [self aslMessageReceived:msg]; 186| 0| 187| 0| // Keep track of which messages we've seen. 188| 0| lastSeenID = (unsigned long long)atoll(asl_get(msg, ASL_KEY_MSG_ID)); 189| 0| } 190| 0| asl_release(response); 191| 0| asl_free(query); 192| 0| 193| 0| if (_cancel) { 194| 0| notify_cancel(token); 195| 0| return; 196| 0| } 197| 0| 198| 0| } 199| 0| }); 200| 0| } 201| 0|} 202| | 203| |@end 204| | 205| |#endif /Users/runner/work/CocoaLumberjack/CocoaLumberjack/Sources/CocoaLumberjack/DDASLLogger.m: 1| |// Software License Agreement (BSD License) 2| |// 3| |// Copyright (c) 2010-2021, Deusty, LLC 4| |// All rights reserved. 5| |// 6| |// Redistribution and use of this software in source and binary forms, 7| |// with or without modification, are permitted provided that the following conditions are met: 8| |// 9| |// * Redistributions of source code must retain the above copyright notice, 10| |// this list of conditions and the following disclaimer. 11| |// 12| |// * Neither the name of Deusty nor the names of its contributors may be used 13| |// to endorse or promote products derived from this software without specific 14| |// prior written permission of Deusty, LLC. 15| | 16| |#import 17| | 18| |#if !TARGET_OS_WATCH 19| | 20| |#if !__has_feature(objc_arc) 21| |#error This file must be compiled with ARC. Use -fobjc-arc flag (or convert project to ARC). 22| |#endif 23| | 24| |#import 25| | 26| |#import 27| | 28| |const char* const kDDASLKeyDDLog = "DDLog"; 29| |const char* const kDDASLDDLogValue = "1"; 30| | 31| |#pragma clang diagnostic push 32| |#pragma clang diagnostic ignored "-Wdeprecated" 33| |static DDASLLogger *sharedInstance; 34| |#pragma clang diagnostic pop 35| | 36| |@interface DDASLLogger () { 37| | aslclient _client; 38| |} 39| | 40| |@end 41| | 42| | 43| |#pragma clang diagnostic push 44| |#pragma clang diagnostic ignored "-Wdeprecated-implementations" 45| |@implementation DDASLLogger 46| |#pragma clang diagnostic pop 47| | 48| 0|+ (instancetype)sharedInstance { 49| 0| static dispatch_once_t DDASLLoggerOnceToken; 50| 0| 51| 0| dispatch_once(&DDASLLoggerOnceToken, ^{ 52| 0| sharedInstance = [[[self class] alloc] init]; 53| 0| }); 54| 0| 55| 0| return sharedInstance; 56| 0|} 57| | 58| 0|- (instancetype)init { 59| 0| if (sharedInstance != nil) { 60| 0| return nil; 61| 0| } 62| 0| 63| 0| if ((self = [super init])) { 64| 0| // A default asl client is provided for the main thread, 65| 0| // but background threads need to create their own client. 66| 0| 67| 0| _client = asl_open(NULL, "com.apple.console", 0); 68| 0| } 69| 0| 70| 0| return self; 71| 0|} 72| | 73| 0|- (DDLoggerName)loggerName { 74| 0| return DDLoggerNameASL; 75| 0|} 76| | 77| 0|- (void)logMessage:(DDLogMessage *)logMessage { 78| 0| // Skip captured log messages 79| 0| if ([logMessage->_fileName isEqualToString:@"DDASLLogCapture"]) { 80| 0| return; 81| 0| } 82| 0| 83| 0| NSString * message = _logFormatter ? [_logFormatter formatLogMessage:logMessage] : logMessage->_message; 84| 0| 85| 0| if (message) { 86| 0| const char *msg = [message UTF8String]; 87| 0| 88| 0| size_t aslLogLevel; 89| 0| switch (logMessage->_flag) { 90| 0| // Note: By default ASL will filter anything above level 5 (Notice). 91| 0| // So our mappings shouldn't go above that level. 92| 0| case DDLogFlagError : aslLogLevel = ASL_LEVEL_CRIT; break; 93| 0| case DDLogFlagWarning : aslLogLevel = ASL_LEVEL_ERR; break; 94| 0| case DDLogFlagInfo : aslLogLevel = ASL_LEVEL_WARNING; break; // Regular NSLog's level 95| 0| case DDLogFlagDebug : 96| 0| case DDLogFlagVerbose : 97| 0| default : aslLogLevel = ASL_LEVEL_NOTICE; break; 98| 0| } 99| 0| 100| 0| static char const *const level_strings[] = { "0", "1", "2", "3", "4", "5", "6", "7" }; 101| 0| 102| 0| // NSLog uses the current euid to set the ASL_KEY_READ_UID. 103| 0| uid_t const readUID = geteuid(); 104| 0| 105| 0| char readUIDString[16]; 106| 0|#ifndef NS_BLOCK_ASSERTIONS 107| 0| size_t l = (size_t)snprintf(readUIDString, sizeof(readUIDString), "%d", readUID); 108| |#else 109| | snprintf(readUIDString, sizeof(readUIDString), "%d", readUID); 110| |#endif 111| | 112| 0| NSAssert(l < sizeof(readUIDString), 113| 0| @"Formatted euid is too long."); 114| 0| NSAssert(aslLogLevel < (sizeof(level_strings) / sizeof(level_strings[0])), 115| 0| @"Unhandled ASL log level."); 116| 0| 117| 0| aslmsg m = asl_new(ASL_TYPE_MSG); 118| 0| if (m != NULL) { 119| 0| if (asl_set(m, ASL_KEY_LEVEL, level_strings[aslLogLevel]) == 0 && 120| 0| asl_set(m, ASL_KEY_MSG, msg) == 0 && 121| 0| asl_set(m, ASL_KEY_READ_UID, readUIDString) == 0 && 122| 0| asl_set(m, kDDASLKeyDDLog, kDDASLDDLogValue) == 0) { 123| 0| asl_send(_client, m); 124| 0| } 125| 0| asl_free(m); 126| 0| } 127| 0| //TODO handle asl_* failures non-silently? 128| 0| } 129| 0|} 130| | 131| |@end 132| | 133| |#endif /Users/runner/work/CocoaLumberjack/CocoaLumberjack/Sources/CocoaLumberjack/DDAbstractDatabaseLogger.m: 1| |// Software License Agreement (BSD License) 2| |// 3| |// Copyright (c) 2010-2021, Deusty, LLC 4| |// All rights reserved. 5| |// 6| |// Redistribution and use of this software in source and binary forms, 7| |// with or without modification, are permitted provided that the following conditions are met: 8| |// 9| |// * Redistributions of source code must retain the above copyright notice, 10| |// this list of conditions and the following disclaimer. 11| |// 12| |// * Neither the name of Deusty nor the names of its contributors may be used 13| |// to endorse or promote products derived from this software without specific 14| |// prior written permission of Deusty, LLC. 15| | 16| |#if !__has_feature(objc_arc) 17| |#error This file must be compiled with ARC. Use -fobjc-arc flag (or convert project to ARC). 18| |#endif 19| | 20| |#import 21| | 22| |@interface DDAbstractDatabaseLogger () 23| | 24| |- (void)destroySaveTimer; 25| |- (void)updateAndResumeSaveTimer; 26| |- (void)createSuspendedSaveTimer; 27| |- (void)destroyDeleteTimer; 28| |- (void)updateDeleteTimer; 29| |- (void)createAndStartDeleteTimer; 30| | 31| |@end 32| | 33| |#pragma mark - 34| | 35| |@implementation DDAbstractDatabaseLogger 36| | 37| 0|- (instancetype)init { 38| 0| if ((self = [super init])) { 39| 0| _saveThreshold = 500; 40| 0| _saveInterval = 60; // 60 seconds 41| 0| _maxAge = (60 * 60 * 24 * 7); // 7 days 42| 0| _deleteInterval = (60 * 5); // 5 minutes 43| 0| } 44| 0| 45| 0| return self; 46| 0|} 47| | 48| 0|- (void)dealloc { 49| 0| [self destroySaveTimer]; 50| 0| [self destroyDeleteTimer]; 51| 0|} 52| | 53| |//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 54| |#pragma mark Override Me 55| |//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 56| | 57| 0|- (BOOL)db_log:(__unused DDLogMessage *)logMessage { 58| 0| // Override me and add your implementation. 59| 0| // 60| 0| // Return YES if an item was added to the buffer. 61| 0| // Return NO if the logMessage was ignored. 62| 0| 63| 0| return NO; 64| 0|} 65| | 66| 0|- (void)db_save { 67| 0| // Override me and add your implementation. 68| 0|} 69| | 70| 0|- (void)db_delete { 71| 0| // Override me and add your implementation. 72| 0|} 73| | 74| 0|- (void)db_saveAndDelete { 75| 0| // Override me and add your implementation. 76| 0|} 77| | 78| |//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 79| |#pragma mark Private API 80| |//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 81| | 82| 0|- (void)performSaveAndSuspendSaveTimer { 83| 0| if (_unsavedCount > 0) { 84| 0| if (_deleteOnEverySave) { 85| 0| [self db_saveAndDelete]; 86| 0| } else { 87| 0| [self db_save]; 88| 0| } 89| 0| } 90| 0| 91| 0| _unsavedCount = 0; 92| 0| _unsavedTime = 0; 93| 0| 94| 0| if (_saveTimer != NULL && _saveTimerSuspended == 0) { 95| 0| dispatch_suspend(_saveTimer); 96| 0| _saveTimerSuspended = 1; 97| 0| } 98| 0|} 99| | 100| 0|- (void)performDelete { 101| 0| if (_maxAge > 0.0) { 102| 0| [self db_delete]; 103| 0| 104| 0| _lastDeleteTime = dispatch_time(DISPATCH_TIME_NOW, 0); 105| 0| } 106| 0|} 107| | 108| |//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 109| |#pragma mark Timers 110| |//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 111| | 112| 0|- (void)destroySaveTimer { 113| 0| if (_saveTimer != NULL) { 114| 0| dispatch_source_cancel(_saveTimer); 115| 0| 116| 0| // Must activate a timer before releasing it (or it will crash) 117| 0| if (@available(macOS 10.12, iOS 10.0, tvOS 10.0, watchOS 3.0, *)) { 118| 0| if (_saveTimerSuspended < 0) { 119| 0| dispatch_activate(_saveTimer); 120| 0| } else if (_saveTimerSuspended > 0) { 121| 0| dispatch_resume(_saveTimer); 122| 0| } 123| 0| } else { 124| 0| if (_saveTimerSuspended != 0) { 125| 0| dispatch_resume(_saveTimer); 126| 0| } 127| 0| } 128| 0| 129| | #if !OS_OBJECT_USE_OBJC 130| | dispatch_release(_saveTimer); 131| | #endif 132| | _saveTimer = NULL; 133| 0| _saveTimerSuspended = 0; 134| 0| } 135| 0|} 136| | 137| 0|- (void)updateAndResumeSaveTimer { 138| 0| if ((_saveTimer != NULL) && (_saveInterval > 0.0) && (_unsavedTime > 0)) { 139| 0| uint64_t interval = (uint64_t)(_saveInterval * (NSTimeInterval) NSEC_PER_SEC); 140| 0| dispatch_time_t startTime = dispatch_time(_unsavedTime, (int64_t)interval); 141| 0| 142| 0| dispatch_source_set_timer(_saveTimer, startTime, interval, 1ull * NSEC_PER_SEC); 143| 0| 144| 0| if (@available(macOS 10.12, iOS 10.0, tvOS 10.0, watchOS 3.0, *)) { 145| 0| if (_saveTimerSuspended < 0) { 146| 0| dispatch_activate(_saveTimer); 147| 0| _saveTimerSuspended = 0; 148| 0| } else if (_saveTimerSuspended > 0) { 149| 0| dispatch_resume(_saveTimer); 150| 0| _saveTimerSuspended = 0; 151| 0| } 152| 0| } else { 153| 0| if (_saveTimerSuspended != 0) { 154| 0| dispatch_resume(_saveTimer); 155| 0| _saveTimerSuspended = 0; 156| 0| } 157| 0| } 158| 0| } 159| 0|} 160| | 161| 0|- (void)createSuspendedSaveTimer { 162| 0| if ((_saveTimer == NULL) && (_saveInterval > 0.0)) { 163| 0| _saveTimer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, self.loggerQueue); 164| 0| 165| 0| dispatch_source_set_event_handler(_saveTimer, ^{ @autoreleasepool { 166| 0| [self performSaveAndSuspendSaveTimer]; 167| 0| } }); 168| 0| 169| 0| _saveTimerSuspended = -1; 170| 0| } 171| 0|} 172| | 173| 0|- (void)destroyDeleteTimer { 174| 0| if (_deleteTimer != NULL) { 175| 0| dispatch_source_cancel(_deleteTimer); 176| | #if !OS_OBJECT_USE_OBJC 177| | dispatch_release(_deleteTimer); 178| | #endif 179| | _deleteTimer = NULL; 180| 0| } 181| 0|} 182| | 183| 0|- (void)updateDeleteTimer { 184| 0| if ((_deleteTimer != NULL) && (_deleteInterval > 0.0) && (_maxAge > 0.0)) { 185| 0| int64_t interval = (int64_t)(_deleteInterval * (NSTimeInterval) NSEC_PER_SEC); 186| 0| dispatch_time_t startTime; 187| 0| 188| 0| if (_lastDeleteTime > 0) { 189| 0| startTime = dispatch_time(_lastDeleteTime, interval); 190| 0| } else { 191| 0| startTime = dispatch_time(DISPATCH_TIME_NOW, interval); 192| 0| } 193| 0| 194| 0| dispatch_source_set_timer(_deleteTimer, startTime, (uint64_t)interval, 1ull * NSEC_PER_SEC); 195| 0| } 196| 0|} 197| | 198| 0|- (void)createAndStartDeleteTimer { 199| 0| if ((_deleteTimer == NULL) && (_deleteInterval > 0.0) && (_maxAge > 0.0)) { 200| 0| _deleteTimer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, self.loggerQueue); 201| 0| 202| 0| if (_deleteTimer != NULL) { 203| 0| dispatch_source_set_event_handler(_deleteTimer, ^{ @autoreleasepool { 204| 0| [self performDelete]; 205| 0| } }); 206| 0| 207| 0| [self updateDeleteTimer]; 208| 0| 209| 0| // We are sure that -updateDeleteTimer did call dispatch_source_set_timer() 210| 0| // since it has the same guards on _deleteInterval and _maxAge 211| 0| if (@available(macOS 10.12, iOS 10.0, tvOS 10.0, watchOS 3.0, *)) 212| 0| dispatch_activate(_deleteTimer); 213| 0| else 214| 0| dispatch_resume(_deleteTimer); 215| 0| } 216| 0| } 217| 0|} 218| | 219| |//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 220| |#pragma mark Configuration 221| |//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 222| | 223| 0|- (NSUInteger)saveThreshold { 224| 0| // The design of this method is taken from the DDAbstractLogger implementation. 225| 0| // For extensive documentation please refer to the DDAbstractLogger implementation. 226| 0| 227| 0| // Note: The internal implementation MUST access the colorsEnabled variable directly, 228| 0| // This method is designed explicitly for external access. 229| 0| // 230| 0| // Using "self." syntax to go through this method will cause immediate deadlock. 231| 0| // This is the intended result. Fix it by accessing the ivar directly. 232| 0| // Great strides have been take to ensure this is safe to do. Plus it's MUCH faster. 233| 0| 234| 0| NSAssert(![self isOnGlobalLoggingQueue], @"Core architecture requirement failure"); 235| 0| NSAssert(![self isOnInternalLoggerQueue], @"MUST access ivar directly, NOT via self.* syntax."); 236| 0| 237| 0| dispatch_queue_t globalLoggingQueue = [DDLog loggingQueue]; 238| 0| 239| 0| __block NSUInteger result; 240| 0| 241| 0| dispatch_sync(globalLoggingQueue, ^{ 242| 0| dispatch_sync(self.loggerQueue, ^{ 243| 0| result = self->_saveThreshold; 244| 0| }); 245| 0| }); 246| 0| 247| 0| return result; 248| 0|} 249| | 250| 0|- (void)setSaveThreshold:(NSUInteger)threshold { 251| 0| dispatch_block_t block = ^{ 252| 0| @autoreleasepool { 253| 0| if (self->_saveThreshold != threshold) { 254| 0| self->_saveThreshold = threshold; 255| 0| 256| 0| // Since the saveThreshold has changed, 257| 0| // we check to see if the current unsavedCount has surpassed the new threshold. 258| 0| // 259| 0| // If it has, we immediately save the log. 260| 0| 261| 0| if ((self->_unsavedCount >= self->_saveThreshold) && (self->_saveThreshold > 0)) { 262| 0| [self performSaveAndSuspendSaveTimer]; 263| 0| } 264| 0| } 265| 0| } 266| 0| }; 267| 0| 268| 0| // The design of the setter logic below is taken from the DDAbstractLogger implementation. 269| 0| // For documentation please refer to the DDAbstractLogger implementation. 270| 0| 271| 0| if ([self isOnInternalLoggerQueue]) { 272| 0| block(); 273| 0| } else { 274| 0| dispatch_queue_t globalLoggingQueue = [DDLog loggingQueue]; 275| 0| NSAssert(![self isOnGlobalLoggingQueue], @"Core architecture requirement failure"); 276| 0| 277| 0| dispatch_async(globalLoggingQueue, ^{ 278| 0| dispatch_async(self.loggerQueue, block); 279| 0| }); 280| 0| } 281| 0|} 282| | 283| 0|- (NSTimeInterval)saveInterval { 284| 0| // The design of this method is taken from the DDAbstractLogger implementation. 285| 0| // For extensive documentation please refer to the DDAbstractLogger implementation. 286| 0| 287| 0| // Note: The internal implementation MUST access the colorsEnabled variable directly, 288| 0| // This method is designed explicitly for external access. 289| 0| // 290| 0| // Using "self." syntax to go through this method will cause immediate deadlock. 291| 0| // This is the intended result. Fix it by accessing the ivar directly. 292| 0| // Great strides have been take to ensure this is safe to do. Plus it's MUCH faster. 293| 0| 294| 0| NSAssert(![self isOnGlobalLoggingQueue], @"Core architecture requirement failure"); 295| 0| NSAssert(![self isOnInternalLoggerQueue], @"MUST access ivar directly, NOT via self.* syntax."); 296| 0| 297| 0| dispatch_queue_t globalLoggingQueue = [DDLog loggingQueue]; 298| 0| 299| 0| __block NSTimeInterval result; 300| 0| 301| 0| dispatch_sync(globalLoggingQueue, ^{ 302| 0| dispatch_sync(self.loggerQueue, ^{ 303| 0| result = self->_saveInterval; 304| 0| }); 305| 0| }); 306| 0| 307| 0| return result; 308| 0|} 309| | 310| 0|- (void)setSaveInterval:(NSTimeInterval)interval { 311| 0| dispatch_block_t block = ^{ 312| 0| @autoreleasepool { 313| 0| // C99 recommended floating point comparison macro 314| 0| // Read: isLessThanOrGreaterThan(floatA, floatB) 315| 0| 316| 0| if (/* saveInterval != interval */ islessgreater(self->_saveInterval, interval)) { 317| 0| self->_saveInterval = interval; 318| 0| 319| 0| // There are several cases we need to handle here. 320| 0| // 321| 0| // 1. If the saveInterval was previously enabled and it just got disabled, 322| 0| // then we need to stop the saveTimer. (And we might as well release it.) 323| 0| // 324| 0| // 2. If the saveInterval was previously disabled and it just got enabled, 325| 0| // then we need to setup the saveTimer. (Plus we might need to do an immediate save.) 326| 0| // 327| 0| // 3. If the saveInterval increased, then we need to reset the timer so that it fires at the later date. 328| 0| // 329| 0| // 4. If the saveInterval decreased, then we need to reset the timer so that it fires at an earlier date. 330| 0| // (Plus we might need to do an immediate save.) 331| 0| 332| 0| if (self->_saveInterval > 0.0) { 333| 0| if (self->_saveTimer == NULL) { 334| 0| // Handles #2 335| 0| // 336| 0| // Since the saveTimer uses the unsavedTime to calculate it's first fireDate, 337| 0| // if a save is needed the timer will fire immediately. 338| 0| 339| 0| [self createSuspendedSaveTimer]; 340| 0| [self updateAndResumeSaveTimer]; 341| 0| } else { 342| 0| // Handles #3 343| 0| // Handles #4 344| 0| // 345| 0| // Since the saveTimer uses the unsavedTime to calculate it's first fireDate, 346| 0| // if a save is needed the timer will fire immediately. 347| 0| 348| 0| [self updateAndResumeSaveTimer]; 349| 0| } 350| 0| } else if (self->_saveTimer) { 351| 0| // Handles #1 352| 0| 353| 0| [self destroySaveTimer]; 354| 0| } 355| 0| } 356| 0| } 357| 0| }; 358| 0| 359| 0| // The design of the setter logic below is taken from the DDAbstractLogger implementation. 360| 0| // For documentation please refer to the DDAbstractLogger implementation. 361| 0| 362| 0| if ([self isOnInternalLoggerQueue]) { 363| 0| block(); 364| 0| } else { 365| 0| dispatch_queue_t globalLoggingQueue = [DDLog loggingQueue]; 366| 0| NSAssert(![self isOnGlobalLoggingQueue], @"Core architecture requirement failure"); 367| 0| 368| 0| dispatch_async(globalLoggingQueue, ^{ 369| 0| dispatch_async(self.loggerQueue, block); 370| 0| }); 371| 0| } 372| 0|} 373| | 374| 0|- (NSTimeInterval)maxAge { 375| 0| // The design of this method is taken from the DDAbstractLogger implementation. 376| 0| // For extensive documentation please refer to the DDAbstractLogger implementation. 377| 0| 378| 0| // Note: The internal implementation MUST access the colorsEnabled variable directly, 379| 0| // This method is designed explicitly for external access. 380| 0| // 381| 0| // Using "self." syntax to go through this method will cause immediate deadlock. 382| 0| // This is the intended result. Fix it by accessing the ivar directly. 383| 0| // Great strides have been take to ensure this is safe to do. Plus it's MUCH faster. 384| 0| 385| 0| NSAssert(![self isOnGlobalLoggingQueue], @"Core architecture requirement failure"); 386| 0| NSAssert(![self isOnInternalLoggerQueue], @"MUST access ivar directly, NOT via self.* syntax."); 387| 0| 388| 0| dispatch_queue_t globalLoggingQueue = [DDLog loggingQueue]; 389| 0| 390| 0| __block NSTimeInterval result; 391| 0| 392| 0| dispatch_sync(globalLoggingQueue, ^{ 393| 0| dispatch_sync(self.loggerQueue, ^{ 394| 0| result = self->_maxAge; 395| 0| }); 396| 0| }); 397| 0| 398| 0| return result; 399| 0|} 400| | 401| 0|- (void)setMaxAge:(NSTimeInterval)interval { 402| 0| dispatch_block_t block = ^{ 403| 0| @autoreleasepool { 404| 0| // C99 recommended floating point comparison macro 405| 0| // Read: isLessThanOrGreaterThan(floatA, floatB) 406| 0| 407| 0| if (/* maxAge != interval */ islessgreater(self->_maxAge, interval)) { 408| 0| NSTimeInterval oldMaxAge = self->_maxAge; 409| 0| NSTimeInterval newMaxAge = interval; 410| 0| 411| 0| self->_maxAge = interval; 412| 0| 413| 0| // There are several cases we need to handle here. 414| 0| // 415| 0| // 1. If the maxAge was previously enabled and it just got disabled, 416| 0| // then we need to stop the deleteTimer. (And we might as well release it.) 417| 0| // 418| 0| // 2. If the maxAge was previously disabled and it just got enabled, 419| 0| // then we need to setup the deleteTimer. (Plus we might need to do an immediate delete.) 420| 0| // 421| 0| // 3. If the maxAge was increased, 422| 0| // then we don't need to do anything. 423| 0| // 424| 0| // 4. If the maxAge was decreased, 425| 0| // then we should do an immediate delete. 426| 0| 427| 0| BOOL shouldDeleteNow = NO; 428| 0| 429| 0| if (oldMaxAge > 0.0) { 430| 0| if (newMaxAge <= 0.0) { 431| 0| // Handles #1 432| 0| 433| 0| [self destroyDeleteTimer]; 434| 0| } else if (oldMaxAge > newMaxAge) { 435| 0| // Handles #4 436| 0| shouldDeleteNow = YES; 437| 0| } 438| 0| } else if (newMaxAge > 0.0) { 439| 0| // Handles #2 440| 0| shouldDeleteNow = YES; 441| 0| } 442| 0| 443| 0| if (shouldDeleteNow) { 444| 0| [self performDelete]; 445| 0| 446| 0| if (self->_deleteTimer) { 447| 0| [self updateDeleteTimer]; 448| 0| } else { 449| 0| [self createAndStartDeleteTimer]; 450| 0| } 451| 0| } 452| 0| } 453| 0| } 454| 0| }; 455| 0| 456| 0| // The design of the setter logic below is taken from the DDAbstractLogger implementation. 457| 0| // For documentation please refer to the DDAbstractLogger implementation. 458| 0| 459| 0| if ([self isOnInternalLoggerQueue]) { 460| 0| block(); 461| 0| } else { 462| 0| dispatch_queue_t globalLoggingQueue = [DDLog loggingQueue]; 463| 0| NSAssert(![self isOnGlobalLoggingQueue], @"Core architecture requirement failure"); 464| 0| 465| 0| dispatch_async(globalLoggingQueue, ^{ 466| 0| dispatch_async(self.loggerQueue, block); 467| 0| }); 468| 0| } 469| 0|} 470| | 471| 0|- (NSTimeInterval)deleteInterval { 472| 0| // The design of this method is taken from the DDAbstractLogger implementation. 473| 0| // For extensive documentation please refer to the DDAbstractLogger implementation. 474| 0| 475| 0| // Note: The internal implementation MUST access the colorsEnabled variable directly, 476| 0| // This method is designed explicitly for external access. 477| 0| // 478| 0| // Using "self." syntax to go through this method will cause immediate deadlock. 479| 0| // This is the intended result. Fix it by accessing the ivar directly. 480| 0| // Great strides have been take to ensure this is safe to do. Plus it's MUCH faster. 481| 0| 482| 0| NSAssert(![self isOnGlobalLoggingQueue], @"Core architecture requirement failure"); 483| 0| NSAssert(![self isOnInternalLoggerQueue], @"MUST access ivar directly, NOT via self.* syntax."); 484| 0| 485| 0| dispatch_queue_t globalLoggingQueue = [DDLog loggingQueue]; 486| 0| 487| 0| __block NSTimeInterval result; 488| 0| 489| 0| dispatch_sync(globalLoggingQueue, ^{ 490| 0| dispatch_sync(self.loggerQueue, ^{ 491| 0| result = self->_deleteInterval; 492| 0| }); 493| 0| }); 494| 0| 495| 0| return result; 496| 0|} 497| | 498| 0|- (void)setDeleteInterval:(NSTimeInterval)interval { 499| 0| dispatch_block_t block = ^{ 500| 0| @autoreleasepool { 501| 0| // C99 recommended floating point comparison macro 502| 0| // Read: isLessThanOrGreaterThan(floatA, floatB) 503| 0| 504| 0| if (/* deleteInterval != interval */ islessgreater(self->_deleteInterval, interval)) { 505| 0| self->_deleteInterval = interval; 506| 0| 507| 0| // There are several cases we need to handle here. 508| 0| // 509| 0| // 1. If the deleteInterval was previously enabled and it just got disabled, 510| 0| // then we need to stop the deleteTimer. (And we might as well release it.) 511| 0| // 512| 0| // 2. If the deleteInterval was previously disabled and it just got enabled, 513| 0| // then we need to setup the deleteTimer. (Plus we might need to do an immediate delete.) 514| 0| // 515| 0| // 3. If the deleteInterval increased, then we need to reset the timer so that it fires at the later date. 516| 0| // 517| 0| // 4. If the deleteInterval decreased, then we need to reset the timer so that it fires at an earlier date. 518| 0| // (Plus we might need to do an immediate delete.) 519| 0| 520| 0| if (self->_deleteInterval > 0.0) { 521| 0| if (self->_deleteTimer == NULL) { 522| 0| // Handles #2 523| 0| // 524| 0| // Since the deleteTimer uses the lastDeleteTime to calculate it's first fireDate, 525| 0| // if a delete is needed the timer will fire immediately. 526| 0| 527| 0| [self createAndStartDeleteTimer]; 528| 0| } else { 529| 0| // Handles #3 530| 0| // Handles #4 531| 0| // 532| 0| // Since the deleteTimer uses the lastDeleteTime to calculate it's first fireDate, 533| 0| // if a save is needed the timer will fire immediately. 534| 0| 535| 0| [self updateDeleteTimer]; 536| 0| } 537| 0| } else if (self->_deleteTimer) { 538| 0| // Handles #1 539| 0| 540| 0| [self destroyDeleteTimer]; 541| 0| } 542| 0| } 543| 0| } 544| 0| }; 545| 0| 546| 0| // The design of the setter logic below is taken from the DDAbstractLogger implementation. 547| 0| // For documentation please refer to the DDAbstractLogger implementation. 548| 0| 549| 0| if ([self isOnInternalLoggerQueue]) { 550| 0| block(); 551| 0| } else { 552| 0| dispatch_queue_t globalLoggingQueue = [DDLog loggingQueue]; 553| 0| NSAssert(![self isOnGlobalLoggingQueue], @"Core architecture requirement failure"); 554| 0| 555| 0| dispatch_async(globalLoggingQueue, ^{ 556| 0| dispatch_async(self.loggerQueue, block); 557| 0| }); 558| 0| } 559| 0|} 560| | 561| 0|- (BOOL)deleteOnEverySave { 562| 0| // The design of this method is taken from the DDAbstractLogger implementation. 563| 0| // For extensive documentation please refer to the DDAbstractLogger implementation. 564| 0| 565| 0| // Note: The internal implementation MUST access the colorsEnabled variable directly, 566| 0| // This method is designed explicitly for external access. 567| 0| // 568| 0| // Using "self." syntax to go through this method will cause immediate deadlock. 569| 0| // This is the intended result. Fix it by accessing the ivar directly. 570| 0| // Great strides have been take to ensure this is safe to do. Plus it's MUCH faster. 571| 0| 572| 0| NSAssert(![self isOnGlobalLoggingQueue], @"Core architecture requirement failure"); 573| 0| NSAssert(![self isOnInternalLoggerQueue], @"MUST access ivar directly, NOT via self.* syntax."); 574| 0| 575| 0| dispatch_queue_t globalLoggingQueue = [DDLog loggingQueue]; 576| 0| 577| 0| __block BOOL result; 578| 0| 579| 0| dispatch_sync(globalLoggingQueue, ^{ 580| 0| dispatch_sync(self.loggerQueue, ^{ 581| 0| result = self->_deleteOnEverySave; 582| 0| }); 583| 0| }); 584| 0| 585| 0| return result; 586| 0|} 587| | 588| 0|- (void)setDeleteOnEverySave:(BOOL)flag { 589| 0| dispatch_block_t block = ^{ 590| 0| self->_deleteOnEverySave = flag; 591| 0| }; 592| 0| 593| 0| // The design of the setter logic below is taken from the DDAbstractLogger implementation. 594| 0| // For documentation please refer to the DDAbstractLogger implementation. 595| 0| 596| 0| if ([self isOnInternalLoggerQueue]) { 597| 0| block(); 598| 0| } else { 599| 0| dispatch_queue_t globalLoggingQueue = [DDLog loggingQueue]; 600| 0| NSAssert(![self isOnGlobalLoggingQueue], @"Core architecture requirement failure"); 601| 0| 602| 0| dispatch_async(globalLoggingQueue, ^{ 603| 0| dispatch_async(self.loggerQueue, block); 604| 0| }); 605| 0| } 606| 0|} 607| | 608| |//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 609| |#pragma mark Public API 610| |//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 611| | 612| 0|- (void)savePendingLogEntries { 613| 0| dispatch_block_t block = ^{ 614| 0| @autoreleasepool { 615| 0| [self performSaveAndSuspendSaveTimer]; 616| 0| } 617| 0| }; 618| 0| 619| 0| if ([self isOnInternalLoggerQueue]) { 620| 0| block(); 621| 0| } else { 622| 0| dispatch_async(self.loggerQueue, block); 623| 0| } 624| 0|} 625| | 626| 0|- (void)deleteOldLogEntries { 627| 0| dispatch_block_t block = ^{ 628| 0| @autoreleasepool { 629| 0| [self performDelete]; 630| 0| } 631| 0| }; 632| 0| 633| 0| if ([self isOnInternalLoggerQueue]) { 634| 0| block(); 635| 0| } else { 636| 0| dispatch_async(self.loggerQueue, block); 637| 0| } 638| 0|} 639| | 640| |//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 641| |#pragma mark DDLogger 642| |//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 643| | 644| 0|- (void)didAddLogger { 645| 0| // If you override me be sure to invoke [super didAddLogger]; 646| 0| 647| 0| [self createSuspendedSaveTimer]; 648| 0| 649| 0| [self createAndStartDeleteTimer]; 650| 0|} 651| | 652| 0|- (void)willRemoveLogger { 653| 0| // If you override me be sure to invoke [super willRemoveLogger]; 654| 0| 655| 0| [self performSaveAndSuspendSaveTimer]; 656| 0| 657| 0| [self destroySaveTimer]; 658| 0| [self destroyDeleteTimer]; 659| 0|} 660| | 661| 0|- (void)logMessage:(DDLogMessage *)logMessage { 662| 0| if ([self db_log:logMessage]) { 663| 0| BOOL firstUnsavedEntry = (++_unsavedCount == 1); 664| 0| 665| 0| if ((_unsavedCount >= _saveThreshold) && (_saveThreshold > 0)) { 666| 0| [self performSaveAndSuspendSaveTimer]; 667| 0| } else if (firstUnsavedEntry) { 668| 0| _unsavedTime = dispatch_time(DISPATCH_TIME_NOW, 0); 669| 0| [self updateAndResumeSaveTimer]; 670| 0| } 671| 0| } 672| 0|} 673| | 674| 0|- (void)flush { 675| 0| // This method is invoked by DDLog's flushLog method. 676| 0| // 677| 0| // It is called automatically when the application quits, 678| 0| // or if the developer invokes DDLog's flushLog method prior to crashing or something. 679| 0| 680| 0| [self performSaveAndSuspendSaveTimer]; 681| 0|} 682| | 683| |@end /Users/runner/work/CocoaLumberjack/CocoaLumberjack/Sources/CocoaLumberjack/DDFileLogger.m: 1| |// Software License Agreement (BSD License) 2| |// 3| |// Copyright (c) 2010-2021, Deusty, LLC 4| |// All rights reserved. 5| |// 6| |// Redistribution and use of this software in source and binary forms, 7| |// with or without modification, are permitted provided that the following conditions are met: 8| |// 9| |// * Redistributions of source code must retain the above copyright notice, 10| |// this list of conditions and the following disclaimer. 11| |// 12| |// * Neither the name of Deusty nor the names of its contributors may be used 13| |// to endorse or promote products derived from this software without specific 14| |// prior written permission of Deusty, LLC. 15| | 16| |#if !__has_feature(objc_arc) 17| |#error This file must be compiled with ARC. Use -fobjc-arc flag (or convert project to ARC). 18| |#endif 19| | 20| |#import 21| | 22| |#import "DDFileLogger+Internal.h" 23| | 24| |// We probably shouldn't be using DDLog() statements within the DDLog implementation. 25| |// But we still want to leave our log statements for any future debugging, 26| |// and to allow other developers to trace the implementation (which is a great learning tool). 27| |// 28| |// So we use primitive logging macros around NSLog. 29| |// We maintain the NS prefix on the macros to be explicit about the fact that we're using NSLog. 30| | 31| |#ifndef DD_NSLOG_LEVEL 32| 0| #define DD_NSLOG_LEVEL 2 33| |#endif 34| | 35| 0|#define NSLogError(frmt, ...) do{ if(DD_NSLOG_LEVEL >= 1) NSLog((frmt), ##__VA_ARGS__); } while(0) 36| |#define NSLogWarn(frmt, ...) do{ if(DD_NSLOG_LEVEL >= 2) NSLog((frmt), ##__VA_ARGS__); } while(0) 37| 0|#define NSLogInfo(frmt, ...) do{ if(DD_NSLOG_LEVEL >= 3) NSLog((frmt), ##__VA_ARGS__); } while(0) 38| 0|#define NSLogDebug(frmt, ...) do{ if(DD_NSLOG_LEVEL >= 4) NSLog((frmt), ##__VA_ARGS__); } while(0) 39| 0|#define NSLogVerbose(frmt, ...) do{ if(DD_NSLOG_LEVEL >= 5) NSLog((frmt), ##__VA_ARGS__); } while(0) 40| | 41| | 42| |#if TARGET_OS_IPHONE 43| |BOOL doesAppRunInBackground(void); 44| |#endif 45| | 46| |unsigned long long const kDDDefaultLogMaxFileSize = 1024 * 1024; // 1 MB 47| |NSTimeInterval const kDDDefaultLogRollingFrequency = 60 * 60 * 24; // 24 Hours 48| |NSUInteger const kDDDefaultLogMaxNumLogFiles = 5; // 5 Files 49| |unsigned long long const kDDDefaultLogFilesDiskQuota = 20 * 1024 * 1024; // 20 MB 50| | 51| |NSTimeInterval const kDDRollingLeeway = 1.0; // 1s 52| | 53| |//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 54| |#pragma mark - 55| |//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 56| | 57| |@interface DDLogFileManagerDefault () { 58| | NSDateFormatter *_fileDateFormatter; 59| | NSUInteger _maximumNumberOfLogFiles; 60| | unsigned long long _logFilesDiskQuota; 61| | NSString *_logsDirectory; 62| |#if TARGET_OS_IPHONE 63| | NSFileProtectionType _defaultFileProtectionLevel; 64| |#endif 65| |} 66| | 67| |@end 68| | 69| |@implementation DDLogFileManagerDefault 70| | 71| |@synthesize maximumNumberOfLogFiles = _maximumNumberOfLogFiles; 72| |@synthesize logFilesDiskQuota = _logFilesDiskQuota; 73| | 74| 0|- (instancetype)init { 75| 0| return [self initWithLogsDirectory:nil]; 76| 0|} 77| | 78| 0|- (instancetype)initWithLogsDirectory:(nullable NSString *)aLogsDirectory { 79| 0| if ((self = [super init])) { 80| 0| _maximumNumberOfLogFiles = kDDDefaultLogMaxNumLogFiles; 81| 0| _logFilesDiskQuota = kDDDefaultLogFilesDiskQuota; 82| 0| 83| 0| _fileDateFormatter = [[NSDateFormatter alloc] init]; 84| 0| [_fileDateFormatter setLocale:[NSLocale localeWithLocaleIdentifier:@"en_US_POSIX"]]; 85| 0| [_fileDateFormatter setTimeZone:[NSTimeZone timeZoneForSecondsFromGMT:0]]; 86| 0| [_fileDateFormatter setDateFormat: @"yyyy'-'MM'-'dd'--'HH'-'mm'-'ss'-'SSS'"]; 87| 0| 88| 0| if (aLogsDirectory.length > 0) { 89| 0| _logsDirectory = [aLogsDirectory copy]; 90| 0| } else { 91| 0| _logsDirectory = [[self defaultLogsDirectory] copy]; 92| 0| } 93| 0| 94| 0| NSLogVerbose(@"DDFileLogManagerDefault: logsDirectory:\n%@", [self logsDirectory]); 95| 0| NSLogVerbose(@"DDFileLogManagerDefault: sortedLogFileNames:\n%@", [self sortedLogFileNames]); 96| 0| } 97| 0| 98| 0| return self; 99| 0|} 100| | 101| |#if TARGET_OS_IPHONE 102| |- (instancetype)initWithLogsDirectory:(NSString *)logsDirectory 103| 0| defaultFileProtectionLevel:(NSFileProtectionType)fileProtectionLevel { 104| 0| 105| 0| if ((self = [self initWithLogsDirectory:logsDirectory])) { 106| 0| if ([fileProtectionLevel isEqualToString:NSFileProtectionNone] || 107| 0| [fileProtectionLevel isEqualToString:NSFileProtectionComplete] || 108| 0| [fileProtectionLevel isEqualToString:NSFileProtectionCompleteUnlessOpen] || 109| 0| [fileProtectionLevel isEqualToString:NSFileProtectionCompleteUntilFirstUserAuthentication]) { 110| 0| _defaultFileProtectionLevel = fileProtectionLevel; 111| 0| } 112| 0| } 113| 0| 114| 0| return self; 115| 0|} 116| | 117| |#endif 118| | 119| 0|- (void)deleteOldFilesForConfigurationChange { 120| 0| dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ 121| 0| @autoreleasepool { 122| 0| // See method header for queue reasoning. 123| 0| [self deleteOldLogFiles]; 124| 0| } 125| 0| }); 126| 0|} 127| | 128| 0|- (void)setLogFilesDiskQuota:(unsigned long long)logFilesDiskQuota { 129| 0| if (_logFilesDiskQuota != logFilesDiskQuota) { 130| 0| _logFilesDiskQuota = logFilesDiskQuota; 131| 0| NSLogInfo(@"DDFileLogManagerDefault: Responding to configuration change: logFilesDiskQuota"); 132| 0| [self deleteOldFilesForConfigurationChange]; 133| 0| } 134| 0|} 135| | 136| 0|- (void)setMaximumNumberOfLogFiles:(NSUInteger)maximumNumberOfLogFiles { 137| 0| if (_maximumNumberOfLogFiles != maximumNumberOfLogFiles) { 138| 0| _maximumNumberOfLogFiles = maximumNumberOfLogFiles; 139| 0| NSLogInfo(@"DDFileLogManagerDefault: Responding to configuration change: maximumNumberOfLogFiles"); 140| 0| [self deleteOldFilesForConfigurationChange]; 141| 0| } 142| 0|} 143| | 144| |#if TARGET_OS_IPHONE 145| 0|- (NSFileProtectionType)logFileProtection { 146| 0| if (_defaultFileProtectionLevel.length > 0) { 147| 0| return _defaultFileProtectionLevel; 148| 0| } else if (doesAppRunInBackground()) { 149| 0| return NSFileProtectionCompleteUntilFirstUserAuthentication; 150| 0| } else { 151| 0| return NSFileProtectionCompleteUnlessOpen; 152| 0| } 153| 0|} 154| |#endif 155| | 156| |//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 157| |#pragma mark File Deleting 158| |//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 159| | 160| |/** 161| | * Deletes archived log files that exceed the maximumNumberOfLogFiles or logFilesDiskQuota configuration values. 162| | * Method may take a while to execute since we're performing IO. It's not critical that this is synchronized with 163| | * log output, since the files we're deleting are all archived and not in use, therefore this method is called on a 164| | * background queue. 165| | **/ 166| 0|- (void)deleteOldLogFiles { 167| 0| NSLogVerbose(@"DDLogFileManagerDefault: deleteOldLogFiles"); 168| 0| 169| 0| NSArray *sortedLogFileInfos = [self sortedLogFileInfos]; 170| 0| NSUInteger firstIndexToDelete = NSNotFound; 171| 0| 172| 0| const unsigned long long diskQuota = self.logFilesDiskQuota; 173| 0| const NSUInteger maxNumLogFiles = self.maximumNumberOfLogFiles; 174| 0| 175| 0| if (diskQuota) { 176| 0| unsigned long long used = 0; 177| 0| 178| 0| for (NSUInteger i = 0; i < sortedLogFileInfos.count; i++) { 179| 0| DDLogFileInfo *info = sortedLogFileInfos[i]; 180| 0| used += info.fileSize; 181| 0| 182| 0| if (used > diskQuota) { 183| 0| firstIndexToDelete = i; 184| 0| break; 185| 0| } 186| 0| } 187| 0| } 188| 0| 189| 0| if (maxNumLogFiles) { 190| 0| if (firstIndexToDelete == NSNotFound) { 191| 0| firstIndexToDelete = maxNumLogFiles; 192| 0| } else { 193| 0| firstIndexToDelete = MIN(firstIndexToDelete, maxNumLogFiles); 194| 0| } 195| 0| } 196| 0| 197| 0| if (firstIndexToDelete == 0) { 198| 0| // Do we consider the first file? 199| 0| // We are only supposed to be deleting archived files. 200| 0| // In most cases, the first file is likely the log file that is currently being written to. 201| 0| // So in most cases, we do not want to consider this file for deletion. 202| 0| 203| 0| if (sortedLogFileInfos.count > 0) { 204| 0| DDLogFileInfo *logFileInfo = sortedLogFileInfos[0]; 205| 0| 206| 0| if (!logFileInfo.isArchived) { 207| 0| // Don't delete active file. 208| 0| ++firstIndexToDelete; 209| 0| } 210| 0| } 211| 0| } 212| 0| 213| 0| if (firstIndexToDelete != NSNotFound) { 214| 0| // removing all log files starting with firstIndexToDelete 215| 0| 216| 0| for (NSUInteger i = firstIndexToDelete; i < sortedLogFileInfos.count; i++) { 217| 0| DDLogFileInfo *logFileInfo = sortedLogFileInfos[i]; 218| 0| 219| 0| NSError *error = nil; 220| 0| BOOL success = [[NSFileManager defaultManager] removeItemAtPath:logFileInfo.filePath error:&error]; 221| 0| if (success) { 222| 0| NSLogInfo(@"DDLogFileManagerDefault: Deleting file: %@", logFileInfo.fileName); 223| 0| } else { 224| 0| NSLogError(@"DDLogFileManagerDefault: Error deleting file %@", error); 225| 0| } 226| 0| } 227| 0| } 228| 0|} 229| | 230| |//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 231| |#pragma mark Log Files 232| |//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 233| | 234| |/** 235| | * Returns the path to the default logs directory. 236| | * If the logs directory doesn't exist, this method automatically creates it. 237| | **/ 238| 0|- (NSString *)defaultLogsDirectory { 239| 0| 240| 0|#if TARGET_OS_IPHONE 241| 0| NSArray *paths = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES); 242| 0| NSString *baseDir = paths.firstObject; 243| 0| NSString *logsDirectory = [baseDir stringByAppendingPathComponent:@"Logs"]; 244| |#else 245| | NSString *appName = [[NSProcessInfo processInfo] processName]; 246| | NSArray *paths = NSSearchPathForDirectoriesInDomains(NSLibraryDirectory, NSUserDomainMask, YES); 247| | NSString *basePath = ([paths count] > 0) ? paths[0] : NSTemporaryDirectory(); 248| | NSString *logsDirectory = [[basePath stringByAppendingPathComponent:@"Logs"] stringByAppendingPathComponent:appName]; 249| |#endif 250| | 251| 0| return logsDirectory; 252| 0|} 253| | 254| 0|- (NSString *)logsDirectory { 255| 0| // We could do this check once, during initialization, and not bother again. 256| 0| // But this way the code continues to work if the directory gets deleted while the code is running. 257| 0| 258| 0| NSAssert(_logsDirectory.length > 0, @"Directory must be set."); 259| 0| 260| 0| NSError *err = nil; 261| 0| BOOL success = [[NSFileManager defaultManager] createDirectoryAtPath:_logsDirectory 262| 0| withIntermediateDirectories:YES 263| 0| attributes:nil 264| 0| error:&err]; 265| 0| if (success == NO) { 266| 0| NSLogError(@"DDFileLogManagerDefault: Error creating logsDirectory: %@", err); 267| 0| } 268| 0| 269| 0| return _logsDirectory; 270| 0|} 271| | 272| 0|- (BOOL)isLogFile:(NSString *)fileName { 273| 0| NSString *appName = [self applicationName]; 274| 0| 275| 0| // We need to add a space to the name as otherwise we could match applications that have the name prefix. 276| 0| BOOL hasProperPrefix = [fileName hasPrefix:[appName stringByAppendingString:@" "]]; 277| 0| BOOL hasProperSuffix = [fileName hasSuffix:@".log"]; 278| 0| 279| 0| return (hasProperPrefix && hasProperSuffix); 280| 0|} 281| | 282| |// if you change formatter, then change sortedLogFileInfos method also accordingly 283| 0|- (NSDateFormatter *)logFileDateFormatter { 284| 0| return _fileDateFormatter; 285| 0|} 286| | 287| 0|- (NSArray *)unsortedLogFilePaths { 288| 0| NSString *logsDirectory = [self logsDirectory]; 289| 0| NSArray *fileNames = [[NSFileManager defaultManager] contentsOfDirectoryAtPath:logsDirectory error:nil]; 290| 0| 291| 0| NSMutableArray *unsortedLogFilePaths = [NSMutableArray arrayWithCapacity:[fileNames count]]; 292| 0| 293| 0| for (NSString *fileName in fileNames) { 294| 0| // Filter out any files that aren't log files. (Just for extra safety) 295| 0| 296| |#if TARGET_IPHONE_SIMULATOR 297| | // This is only used on the iPhone simulator for backward compatibility reason. 298| | // 299| | // In case of iPhone simulator there can be 'archived' extension. isLogFile: 300| | // method knows nothing about it. Thus removing it for this method. 301| | NSString *theFileName = [fileName stringByReplacingOccurrencesOfString:@".archived" 302| | withString:@""]; 303| | 304| | if ([self isLogFile:theFileName]) 305| |#else 306| | 307| 0| if ([self isLogFile:fileName]) 308| 0|#endif 309| 0| { 310| 0| NSString *filePath = [logsDirectory stringByAppendingPathComponent:fileName]; 311| 0| 312| 0| [unsortedLogFilePaths addObject:filePath]; 313| 0| } 314| 0| } 315| 0| 316| 0| return unsortedLogFilePaths; 317| 0|} 318| | 319| 0|- (NSArray *)unsortedLogFileNames { 320| 0| NSArray *unsortedLogFilePaths = [self unsortedLogFilePaths]; 321| 0| 322| 0| NSMutableArray *unsortedLogFileNames = [NSMutableArray arrayWithCapacity:[unsortedLogFilePaths count]]; 323| 0| 324| 0| for (NSString *filePath in unsortedLogFilePaths) { 325| 0| [unsortedLogFileNames addObject:[filePath lastPathComponent]]; 326| 0| } 327| 0| 328| 0| return unsortedLogFileNames; 329| 0|} 330| | 331| 0|- (NSArray *)unsortedLogFileInfos { 332| 0| NSArray *unsortedLogFilePaths = [self unsortedLogFilePaths]; 333| 0| 334| 0| NSMutableArray *unsortedLogFileInfos = [NSMutableArray arrayWithCapacity:[unsortedLogFilePaths count]]; 335| 0| 336| 0| for (NSString *filePath in unsortedLogFilePaths) { 337| 0| DDLogFileInfo *logFileInfo = [[DDLogFileInfo alloc] initWithFilePath:filePath]; 338| 0| 339| 0| [unsortedLogFileInfos addObject:logFileInfo]; 340| 0| } 341| 0| 342| 0| return unsortedLogFileInfos; 343| 0|} 344| | 345| 0|- (NSArray *)sortedLogFilePaths { 346| 0| NSArray *sortedLogFileInfos = [self sortedLogFileInfos]; 347| 0| 348| 0| NSMutableArray *sortedLogFilePaths = [NSMutableArray arrayWithCapacity:[sortedLogFileInfos count]]; 349| 0| 350| 0| for (DDLogFileInfo *logFileInfo in sortedLogFileInfos) { 351| 0| [sortedLogFilePaths addObject:[logFileInfo filePath]]; 352| 0| } 353| 0| 354| 0| return sortedLogFilePaths; 355| 0|} 356| | 357| 0|- (NSArray *)sortedLogFileNames { 358| 0| NSArray *sortedLogFileInfos = [self sortedLogFileInfos]; 359| 0| 360| 0| NSMutableArray *sortedLogFileNames = [NSMutableArray arrayWithCapacity:[sortedLogFileInfos count]]; 361| 0| 362| 0| for (DDLogFileInfo *logFileInfo in sortedLogFileInfos) { 363| 0| [sortedLogFileNames addObject:[logFileInfo fileName]]; 364| 0| } 365| 0| 366| 0| return sortedLogFileNames; 367| 0|} 368| | 369| 0|- (NSArray *)sortedLogFileInfos { 370| 0| return [[self unsortedLogFileInfos] sortedArrayUsingComparator:^NSComparisonResult(DDLogFileInfo *obj1, 371| 0| DDLogFileInfo *obj2) { 372| 0| NSDate *date1 = [NSDate new]; 373| 0| NSDate *date2 = [NSDate new]; 374| 0| 375| 0| NSArray *arrayComponent = [[obj1 fileName] componentsSeparatedByString:@" "]; 376| 0| if (arrayComponent.count > 0) { 377| 0| NSString *stringDate = arrayComponent.lastObject; 378| 0| stringDate = [stringDate stringByReplacingOccurrencesOfString:@".log" withString:@""]; 379| |#if TARGET_IPHONE_SIMULATOR 380| | // This is only used on the iPhone simulator for backward compatibility reason. 381| | stringDate = [stringDate stringByReplacingOccurrencesOfString:@".archived" withString:@""]; 382| |#endif 383| 0| date1 = [[self logFileDateFormatter] dateFromString:stringDate] ?: [obj1 creationDate]; 384| 0| } 385| 0| 386| 0| arrayComponent = [[obj2 fileName] componentsSeparatedByString:@" "]; 387| 0| if (arrayComponent.count > 0) { 388| 0| NSString *stringDate = arrayComponent.lastObject; 389| 0| stringDate = [stringDate stringByReplacingOccurrencesOfString:@".log" withString:@""]; 390| |#if TARGET_IPHONE_SIMULATOR 391| | // This is only used on the iPhone simulator for backward compatibility reason. 392| | stringDate = [stringDate stringByReplacingOccurrencesOfString:@".archived" withString:@""]; 393| |#endif 394| 0| date2 = [[self logFileDateFormatter] dateFromString:stringDate] ?: [obj2 creationDate]; 395| 0| } 396| 0| 397| 0| return [date2 compare:date1 ?: [NSDate new]]; 398| 0| }]; 399| 0| 400| 0|} 401| | 402| |//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 403| |#pragma mark Creation 404| |//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 405| | 406| |//if you change newLogFileName , then change isLogFile method also accordingly 407| 0|- (NSString *)newLogFileName { 408| 0| NSString *appName = [self applicationName]; 409| 0| 410| 0| NSDateFormatter *dateFormatter = [self logFileDateFormatter]; 411| 0| NSString *formattedDate = [dateFormatter stringFromDate:[NSDate date]]; 412| 0| 413| 0| return [NSString stringWithFormat:@"%@ %@.log", appName, formattedDate]; 414| 0|} 415| | 416| 0|- (nullable NSString *)logFileHeader { 417| 0| return nil; 418| 0|} 419| | 420| 0|- (NSData *)logFileHeaderData { 421| 0| NSString *fileHeaderStr = [self logFileHeader]; 422| 0| 423| 0| if (fileHeaderStr.length == 0) { 424| 0| return nil; 425| 0| } 426| 0| 427| 0| if (![fileHeaderStr hasSuffix:@"\n"]) { 428| 0| fileHeaderStr = [fileHeaderStr stringByAppendingString:@"\n"]; 429| 0| } 430| 0| 431| 0| return [fileHeaderStr dataUsingEncoding:NSUTF8StringEncoding]; 432| 0|} 433| | 434| 0|- (NSString *)createNewLogFileWithError:(NSError *__autoreleasing _Nullable *)error { 435| 0| static NSUInteger MAX_ALLOWED_ERROR = 5; 436| 0| 437| 0| NSString *fileName = [self newLogFileName]; 438| 0| NSString *logsDirectory = [self logsDirectory]; 439| 0| NSData *fileHeader = [self logFileHeaderData]; 440| 0| if (fileHeader == nil) { 441| 0| fileHeader = [NSData new]; 442| 0| } 443| 0| 444| 0| NSUInteger attempt = 1; 445| 0| NSUInteger criticalErrors = 0; 446| 0| NSError *lastCriticalError; 447| 0| 448| 0| do { 449| 0| if (criticalErrors >= MAX_ALLOWED_ERROR) { 450| 0| NSLogError(@"DDLogFileManagerDefault: Bailing file creation, encountered %ld errors.", 451| 0| (unsigned long)criticalErrors); 452| 0| *error = lastCriticalError; 453| 0| return nil; 454| 0| } 455| 0| 456| 0| NSString *actualFileName = fileName; 457| 0| if (attempt > 1) { 458| 0| NSString *extension = [actualFileName pathExtension]; 459| 0| 460| 0| actualFileName = [actualFileName stringByDeletingPathExtension]; 461| 0| actualFileName = [actualFileName stringByAppendingFormat:@" %lu", (unsigned long)attempt]; 462| 0| 463| 0| if (extension.length) { 464| 0| actualFileName = [actualFileName stringByAppendingPathExtension:extension]; 465| 0| } 466| 0| } 467| 0| 468| 0| NSString *filePath = [logsDirectory stringByAppendingPathComponent:actualFileName]; 469| 0| 470| 0| NSError *currentError = nil; 471| 0| BOOL success = [fileHeader writeToFile:filePath options:NSDataWritingAtomic error:¤tError]; 472| 0| 473| 0|#if TARGET_OS_IPHONE 474| 0| if (success) { 475| 0| // When creating log file on iOS we're setting NSFileProtectionKey attribute to NSFileProtectionCompleteUnlessOpen. 476| 0| // 477| 0| // But in case if app is able to launch from background we need to have an ability to open log file any time we 478| 0| // want (even if device is locked). Thats why that attribute have to be changed to 479| 0| // NSFileProtectionCompleteUntilFirstUserAuthentication. 480| 0| NSDictionary *attributes = @{NSFileProtectionKey: [self logFileProtection]}; 481| 0| success = [[NSFileManager defaultManager] setAttributes:attributes 482| 0| ofItemAtPath:filePath 483| 0| error:¤tError]; 484| 0| } 485| 0|#endif 486| 0| 487| 0| if (success) { 488| 0| NSLogVerbose(@"DDLogFileManagerDefault: Created new log file: %@", actualFileName); 489| 0| dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{ 490| 0| // Since we just created a new log file, we may need to delete some old log files 491| 0| [self deleteOldLogFiles]; 492| 0| }); 493| 0| return filePath; 494| 0| } else if (currentError.code == NSFileWriteFileExistsError) { 495| 0| attempt++; 496| 0| continue; 497| 0| } else { 498| 0| NSLogError(@"DDLogFileManagerDefault: Critical error while creating log file: %@", currentError); 499| 0| criticalErrors++; 500| 0| lastCriticalError = currentError; 501| 0| continue; 502| 0| } 503| 0| 504| 0| return filePath; 505| 0| } while (YES); 506| 0|} 507| | 508| |//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 509| |#pragma mark Utility 510| |//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 511| | 512| 0|- (NSString *)applicationName { 513| 0| static NSString *_appName; 514| 0| static dispatch_once_t onceToken; 515| 0| 516| 0| dispatch_once(&onceToken, ^{ 517| 0| _appName = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"CFBundleIdentifier"]; 518| 0| 519| 0| if (_appName.length == 0) { 520| 0| _appName = [[NSProcessInfo processInfo] processName]; 521| 0| } 522| 0| 523| 0| if (_appName.length == 0) { 524| 0| _appName = @""; 525| 0| } 526| 0| }); 527| 0| 528| 0| return _appName; 529| 0|} 530| | 531| |@end 532| | 533| |//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 534| |#pragma mark - 535| |//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 536| | 537| |@interface DDLogFileFormatterDefault () { 538| | NSDateFormatter *_dateFormatter; 539| |} 540| | 541| |@end 542| | 543| |@implementation DDLogFileFormatterDefault 544| | 545| 0|- (instancetype)init { 546| 0| return [self initWithDateFormatter:nil]; 547| 0|} 548| | 549| 1|- (instancetype)initWithDateFormatter:(nullable NSDateFormatter *)aDateFormatter { 550| 1| if ((self = [super init])) { 551| 1| if (aDateFormatter) { 552| 1| _dateFormatter = aDateFormatter; 553| 1| } else { 554| 0| _dateFormatter = [[NSDateFormatter alloc] init]; 555| 0| [_dateFormatter setFormatterBehavior:NSDateFormatterBehavior10_4]; // 10.4+ style 556| 0| [_dateFormatter setLocale:[NSLocale localeWithLocaleIdentifier:@"en_US_POSIX"]]; 557| 0| [_dateFormatter setTimeZone:[NSTimeZone timeZoneForSecondsFromGMT:0]]; 558| 0| [_dateFormatter setDateFormat:@"yyyy/MM/dd HH:mm:ss:SSS"]; 559| 0| } 560| 1| } 561| 1| 562| 1| return self; 563| 1|} 564| | 565| 2|- (NSString *)formatLogMessage:(DDLogMessage *)logMessage { 566| 2| NSString *dateAndTime = [_dateFormatter stringFromDate:logMessage->_timestamp]; 567| 2| 568| 2| return [NSString stringWithFormat:@"%@ %@", dateAndTime, logMessage->_message]; 569| 2|} 570| | 571| |@end 572| | 573| |//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 574| |#pragma mark - 575| |//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 576| | 577| |@interface DDFileLogger () { 578| | id _logFileManager; 579| | 580| | DDLogFileInfo *_currentLogFileInfo; 581| | NSFileHandle *_currentLogFileHandle; 582| | 583| | dispatch_source_t _currentLogFileVnode; 584| | 585| | NSTimeInterval _rollingFrequency; 586| | dispatch_source_t _rollingTimer; 587| | 588| | unsigned long long _maximumFileSize; 589| | 590| | dispatch_queue_t _completionQueue; 591| |} 592| | 593| |@end 594| | 595| |#pragma clang diagnostic push 596| |#pragma clang diagnostic ignored "-Wincomplete-implementation" 597| |@implementation DDFileLogger 598| |#pragma clang diagnostic pop 599| | 600| 0|- (instancetype)init { 601| 0| DDLogFileManagerDefault *defaultLogFileManager = [[DDLogFileManagerDefault alloc] init]; 602| 0| return [self initWithLogFileManager:defaultLogFileManager completionQueue:nil]; 603| 0|} 604| | 605| 0|- (instancetype)initWithLogFileManager:(id)logFileManager { 606| 0| return [self initWithLogFileManager:logFileManager completionQueue:nil]; 607| 0|} 608| | 609| |- (instancetype)initWithLogFileManager:(id )aLogFileManager 610| 0| completionQueue:(nullable dispatch_queue_t)dispatchQueue { 611| 0| if ((self = [super init])) { 612| 0| _completionQueue = dispatchQueue ?: dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); 613| 0| 614| 0| _maximumFileSize = kDDDefaultLogMaxFileSize; 615| 0| _rollingFrequency = kDDDefaultLogRollingFrequency; 616| 0| _automaticallyAppendNewlineForCustomFormatters = YES; 617| 0| 618| 0| _logFileManager = aLogFileManager; 619| 0| _logFormatter = [DDLogFileFormatterDefault new]; 620| 0| } 621| 0| 622| 0| return self; 623| 0|} 624| | 625| 0|- (void)lt_cleanup { 626| 0| NSAssert([self isOnInternalLoggerQueue], @"lt_ methods should be on logger queue."); 627| 0| 628| 0| if (@available(macOS 10.15, iOS 13.0, tvOS 13.0, watchOS 6.0, *)) { 629| 0| __autoreleasing NSError *error; 630| 0| BOOL synchronized = [_currentLogFileHandle synchronizeAndReturnError:&error]; 631| 0| if (!synchronized) { 632| 0| NSLogError(@"DDFileLogger: Failed to synchronize file: %@", error); 633| 0| } 634| 0| BOOL closed = [_currentLogFileHandle closeAndReturnError:&error]; 635| 0| if (!closed) { 636| 0| NSLogError(@"DDFileLogger: Failed to close file: %@", error); 637| 0| } 638| 0| } else { 639| 0| [_currentLogFileHandle synchronizeFile]; 640| 0| [_currentLogFileHandle closeFile]; 641| 0| } 642| 0| 643| 0| if (_currentLogFileVnode) { 644| 0| dispatch_source_cancel(_currentLogFileVnode); 645| 0| _currentLogFileVnode = NULL; 646| 0| } 647| 0| 648| 0| if (_rollingTimer) { 649| 0| dispatch_source_cancel(_rollingTimer); 650| 0| _rollingTimer = NULL; 651| 0| } 652| 0|} 653| | 654| 0|- (void)dealloc { 655| 0| if (self.isOnInternalLoggerQueue) { 656| 0| [self lt_cleanup]; 657| 0| } else { 658| 0| dispatch_sync(self.loggerQueue, ^{ 659| 0| [self lt_cleanup]; 660| 0| }); 661| 0| } 662| 0|} 663| | 664| |//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 665| |#pragma mark Properties 666| |//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 667| | 668| 0|- (unsigned long long)maximumFileSize { 669| 0| __block unsigned long long result; 670| 0| 671| 0| dispatch_block_t block = ^{ 672| 0| result = self->_maximumFileSize; 673| 0| }; 674| 0| 675| 0| // The design of this method is taken from the DDAbstractLogger implementation. 676| 0| // For extensive documentation please refer to the DDAbstractLogger implementation. 677| 0| 678| 0| // Note: The internal implementation MUST access the maximumFileSize variable directly, 679| 0| // This method is designed explicitly for external access. 680| 0| // 681| 0| // Using "self." syntax to go through this method will cause immediate deadlock. 682| 0| // This is the intended result. Fix it by accessing the ivar directly. 683| 0| // Great strides have been take to ensure this is safe to do. Plus it's MUCH faster. 684| 0| 685| 0| NSAssert(![self isOnGlobalLoggingQueue], @"Core architecture requirement failure"); 686| 0| NSAssert(![self isOnInternalLoggerQueue], @"MUST access ivar directly, NOT via self.* syntax."); 687| 0| 688| 0| dispatch_queue_t globalLoggingQueue = [DDLog loggingQueue]; 689| 0| 690| 0| dispatch_sync(globalLoggingQueue, ^{ 691| 0| dispatch_sync(self.loggerQueue, block); 692| 0| }); 693| 0| 694| 0| return result; 695| 0|} 696| | 697| 0|- (void)setMaximumFileSize:(unsigned long long)newMaximumFileSize { 698| 0| dispatch_block_t block = ^{ 699| 0| @autoreleasepool { 700| 0| self->_maximumFileSize = newMaximumFileSize; 701| 0| [self lt_maybeRollLogFileDueToSize]; 702| 0| } 703| 0| }; 704| 0| 705| 0| // The design of this method is taken from the DDAbstractLogger implementation. 706| 0| // For extensive documentation please refer to the DDAbstractLogger implementation. 707| 0| 708| 0| // Note: The internal implementation MUST access the maximumFileSize variable directly, 709| 0| // This method is designed explicitly for external access. 710| 0| // 711| 0| // Using "self." syntax to go through this method will cause immediate deadlock. 712| 0| // This is the intended result. Fix it by accessing the ivar directly. 713| 0| // Great strides have been take to ensure this is safe to do. Plus it's MUCH faster. 714| 0| 715| 0| NSAssert(![self isOnGlobalLoggingQueue], @"Core architecture requirement failure"); 716| 0| NSAssert(![self isOnInternalLoggerQueue], @"MUST access ivar directly, NOT via self.* syntax."); 717| 0| 718| 0| dispatch_queue_t globalLoggingQueue = [DDLog loggingQueue]; 719| 0| 720| 0| dispatch_async(globalLoggingQueue, ^{ 721| 0| dispatch_async(self.loggerQueue, block); 722| 0| }); 723| 0|} 724| | 725| 0|- (NSTimeInterval)rollingFrequency { 726| 0| __block NSTimeInterval result; 727| 0| 728| 0| dispatch_block_t block = ^{ 729| 0| result = self->_rollingFrequency; 730| 0| }; 731| 0| 732| 0| // The design of this method is taken from the DDAbstractLogger implementation. 733| 0| // For extensive documentation please refer to the DDAbstractLogger implementation. 734| 0| 735| 0| // Note: The internal implementation should access the rollingFrequency variable directly, 736| 0| // This method is designed explicitly for external access. 737| 0| // 738| 0| // Using "self." syntax to go through this method will cause immediate deadlock. 739| 0| // This is the intended result. Fix it by accessing the ivar directly. 740| 0| // Great strides have been take to ensure this is safe to do. Plus it's MUCH faster. 741| 0| 742| 0| NSAssert(![self isOnGlobalLoggingQueue], @"Core architecture requirement failure"); 743| 0| NSAssert(![self isOnInternalLoggerQueue], @"MUST access ivar directly, NOT via self.* syntax."); 744| 0| 745| 0| dispatch_queue_t globalLoggingQueue = [DDLog loggingQueue]; 746| 0| 747| 0| dispatch_sync(globalLoggingQueue, ^{ 748| 0| dispatch_sync(self.loggerQueue, block); 749| 0| }); 750| 0| 751| 0| return result; 752| 0|} 753| | 754| 0|- (void)setRollingFrequency:(NSTimeInterval)newRollingFrequency { 755| 0| dispatch_block_t block = ^{ 756| 0| @autoreleasepool { 757| 0| self->_rollingFrequency = newRollingFrequency; 758| 0| [self lt_maybeRollLogFileDueToAge]; 759| 0| } 760| 0| }; 761| 0| 762| 0| // The design of this method is taken from the DDAbstractLogger implementation. 763| 0| // For extensive documentation please refer to the DDAbstractLogger implementation. 764| 0| 765| 0| // Note: The internal implementation should access the rollingFrequency variable directly, 766| 0| // This method is designed explicitly for external access. 767| 0| // 768| 0| // Using "self." syntax to go through this method will cause immediate deadlock. 769| 0| // This is the intended result. Fix it by accessing the ivar directly. 770| 0| // Great strides have been take to ensure this is safe to do. Plus it's MUCH faster. 771| 0| 772| 0| NSAssert(![self isOnGlobalLoggingQueue], @"Core architecture requirement failure"); 773| 0| NSAssert(![self isOnInternalLoggerQueue], @"MUST access ivar directly, NOT via self.* syntax."); 774| 0| 775| 0| dispatch_queue_t globalLoggingQueue = [DDLog loggingQueue]; 776| 0| 777| 0| dispatch_async(globalLoggingQueue, ^{ 778| 0| dispatch_async(self.loggerQueue, block); 779| 0| }); 780| 0|} 781| | 782| |//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 783| |#pragma mark File Rolling 784| |//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 785| | 786| 0|- (void)lt_scheduleTimerToRollLogFileDueToAge { 787| 0| NSAssert([self isOnInternalLoggerQueue], @"lt_ methods should be on logger queue."); 788| 0| 789| 0| if (_rollingTimer) { 790| 0| dispatch_source_cancel(_rollingTimer); 791| 0| _rollingTimer = NULL; 792| 0| } 793| 0| 794| 0| if (_currentLogFileInfo == nil || _rollingFrequency <= 0.0) { 795| 0| return; 796| 0| } 797| 0| 798| 0| NSDate *logFileCreationDate = [_currentLogFileInfo creationDate]; 799| 0| NSTimeInterval frequency = MIN(_rollingFrequency, DBL_MAX - [logFileCreationDate timeIntervalSinceReferenceDate]); 800| 0| NSDate *logFileRollingDate = [logFileCreationDate dateByAddingTimeInterval:frequency]; 801| 0| 802| 0| NSLogVerbose(@"DDFileLogger: scheduleTimerToRollLogFileDueToAge"); 803| 0| NSLogVerbose(@"DDFileLogger: logFileCreationDate : %@", logFileCreationDate); 804| 0| NSLogVerbose(@"DDFileLogger: actual rollingFrequency: %f", frequency); 805| 0| NSLogVerbose(@"DDFileLogger: logFileRollingDate : %@", logFileRollingDate); 806| 0| 807| 0| _rollingTimer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, _loggerQueue); 808| 0| 809| 0| __weak __auto_type weakSelf = self; 810| 0| dispatch_source_set_event_handler(_rollingTimer, ^{ @autoreleasepool { 811| 0| [weakSelf lt_maybeRollLogFileDueToAge]; 812| 0| } }); 813| 0| 814| | #if !OS_OBJECT_USE_OBJC 815| | dispatch_source_t theRollingTimer = _rollingTimer; 816| | dispatch_source_set_cancel_handler(_rollingTimer, ^{ 817| | dispatch_release(theRollingTimer); 818| | }); 819| | #endif 820| | 821| 0| static NSTimeInterval const kDDMaxTimerDelay = LLONG_MAX / NSEC_PER_SEC; 822| 0| int64_t delay = (int64_t)(MIN([logFileRollingDate timeIntervalSinceNow], kDDMaxTimerDelay) * (NSTimeInterval) NSEC_PER_SEC); 823| 0| dispatch_time_t fireTime = dispatch_time(DISPATCH_TIME_NOW, delay); 824| 0| 825| 0| dispatch_source_set_timer(_rollingTimer, fireTime, DISPATCH_TIME_FOREVER, (uint64_t)kDDRollingLeeway * NSEC_PER_SEC); 826| 0| 827| 0| if (@available(macOS 10.12, iOS 10.0, tvOS 10.0, watchOS 3.0, *)) 828| 0| dispatch_activate(_rollingTimer); 829| 0| else 830| 0| dispatch_resume(_rollingTimer); 831| 0|} 832| | 833| 0|- (void)rollLogFile { 834| 0| [self rollLogFileWithCompletionBlock:nil]; 835| 0|} 836| | 837| 0|- (void)rollLogFileWithCompletionBlock:(nullable void (^)(void))completionBlock { 838| 0| // This method is public. 839| 0| // We need to execute the rolling on our logging thread/queue. 840| 0| 841| 0| dispatch_block_t block = ^{ 842| 0| @autoreleasepool { 843| 0| [self lt_rollLogFileNow]; 844| 0| 845| 0| if (completionBlock) { 846| 0| dispatch_async(self->_completionQueue, ^{ 847| 0| completionBlock(); 848| 0| }); 849| 0| } 850| 0| } 851| 0| }; 852| 0| 853| 0| // The design of this method is taken from the DDAbstractLogger implementation. 854| 0| // For extensive documentation please refer to the DDAbstractLogger implementation. 855| 0| 856| 0| if ([self isOnInternalLoggerQueue]) { 857| 0| block(); 858| 0| } else { 859| 0| dispatch_queue_t globalLoggingQueue = [DDLog loggingQueue]; 860| 0| NSAssert(![self isOnGlobalLoggingQueue], @"Core architecture requirement failure"); 861| 0| 862| 0| dispatch_async(globalLoggingQueue, ^{ 863| 0| dispatch_async(self.loggerQueue, block); 864| 0| }); 865| 0| } 866| 0|} 867| | 868| 0|- (void)lt_rollLogFileNow { 869| 0| NSAssert([self isOnInternalLoggerQueue], @"lt_ methods should be on logger queue."); 870| 0| NSLogVerbose(@"DDFileLogger: rollLogFileNow"); 871| 0| 872| 0| if (_currentLogFileHandle == nil) { 873| 0| return; 874| 0| } 875| 0| 876| 0| if (@available(macOS 10.15, iOS 13.0, tvOS 13.0, watchOS 6.0, *)) { 877| 0| __autoreleasing NSError *error; 878| 0| BOOL synchronized = [_currentLogFileHandle synchronizeAndReturnError:&error]; 879| 0| if (!synchronized) { 880| 0| NSLogError(@"DDFileLogger: Failed to synchronize file: %@", error); 881| 0| } 882| 0| BOOL closed = [_currentLogFileHandle closeAndReturnError:&error]; 883| 0| if (!closed) { 884| 0| NSLogError(@"DDFileLogger: Failed to close file: %@", error); 885| 0| } 886| 0| } else { 887| 0| [_currentLogFileHandle synchronizeFile]; 888| 0| [_currentLogFileHandle closeFile]; 889| 0| } 890| 0| _currentLogFileHandle = nil; 891| 0| 892| 0| _currentLogFileInfo.isArchived = YES; 893| 0| 894| 0| const BOOL logFileManagerRespondsToNewArchiveSelector = [_logFileManager respondsToSelector:@selector(didArchiveLogFile:wasRolled:)]; 895| 0| const BOOL logFileManagerRespondsToSelector = (logFileManagerRespondsToNewArchiveSelector 896| 0| || [_logFileManager respondsToSelector:@selector(didRollAndArchiveLogFile:)]); 897| 0| NSString *archivedFilePath = (logFileManagerRespondsToSelector) ? [_currentLogFileInfo.filePath copy] : nil; 898| 0| _currentLogFileInfo = nil; 899| 0| 900| 0| if (logFileManagerRespondsToSelector) { 901| 0| dispatch_block_t block; 902| 0| if (logFileManagerRespondsToNewArchiveSelector) { 903| 0| block = ^{ 904| 0| [self->_logFileManager didArchiveLogFile:archivedFilePath wasRolled:YES]; 905| 0| }; 906| 0| } else { 907| 0| block = ^{ 908| 0|#pragma clang diagnostic push 909| 0|#pragma clang diagnostic ignored "-Wdeprecated-declarations" 910| 0| [self->_logFileManager didRollAndArchiveLogFile:archivedFilePath]; 911| 0|#pragma clang diagnostic pop 912| 0| }; 913| 0| } 914| 0| dispatch_async(_completionQueue, block); 915| 0| } 916| 0| 917| 0| if (_currentLogFileVnode) { 918| 0| dispatch_source_cancel(_currentLogFileVnode); 919| 0| _currentLogFileVnode = nil; 920| 0| } 921| 0| 922| 0| if (_rollingTimer) { 923| 0| dispatch_source_cancel(_rollingTimer); 924| 0| _rollingTimer = nil; 925| 0| } 926| 0|} 927| | 928| 0|- (void)lt_maybeRollLogFileDueToAge { 929| 0| NSAssert([self isOnInternalLoggerQueue], @"lt_ methods should be on logger queue."); 930| 0| 931| 0| if (_rollingFrequency > 0.0 && (_currentLogFileInfo.age + kDDRollingLeeway) >= _rollingFrequency) { 932| 0| NSLogVerbose(@"DDFileLogger: Rolling log file due to age..."); 933| 0| [self lt_rollLogFileNow]; 934| 0| } else { 935| 0| [self lt_scheduleTimerToRollLogFileDueToAge]; 936| 0| } 937| 0|} 938| | 939| 0|- (void)lt_maybeRollLogFileDueToSize { 940| 0| NSAssert([self isOnInternalLoggerQueue], @"lt_ methods should be on logger queue."); 941| 0| 942| 0| // This method is called from logMessage. 943| 0| // Keep it FAST. 944| 0| 945| 0| // Note: Use direct access to maximumFileSize variable. 946| 0| // We specifically wrote our own getter/setter method to allow us to do this (for performance reasons). 947| 0| 948| 0| if (_maximumFileSize > 0) { 949| 0| unsigned long long fileSize; 950| 0| if (@available(macOS 10.15, iOS 13.0, tvOS 13.0, watchOS 6.0, *)) { 951| 0| __autoreleasing NSError *error; 952| 0| BOOL succeed = [_currentLogFileHandle getOffset:&fileSize error:&error]; 953| 0| if (!succeed) { 954| 0| NSLogError(@"DDFileLogger: Failed to get offset: %@", error); 955| 0| return; 956| 0| } 957| 0| } else { 958| 0| fileSize = [_currentLogFileHandle offsetInFile]; 959| 0| } 960| 0| 961| 0| if (fileSize >= _maximumFileSize) { 962| 0| NSLogVerbose(@"DDFileLogger: Rolling log file due to size (%qu)...", fileSize); 963| 0| 964| 0| [self lt_rollLogFileNow]; 965| 0| } 966| 0| } 967| 0|} 968| | 969| |//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 970| |#pragma mark File Logging 971| |//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 972| | 973| 0|- (BOOL)lt_shouldLogFileBeArchived:(DDLogFileInfo *)mostRecentLogFileInfo { 974| 0| NSAssert([self isOnInternalLoggerQueue], @"lt_ methods should be on logger queue."); 975| 0| 976| 0| if ([self shouldArchiveRecentLogFileInfo:mostRecentLogFileInfo]) { 977| 0| return YES; 978| 0| } else if (_maximumFileSize > 0 && mostRecentLogFileInfo.fileSize >= _maximumFileSize) { 979| 0| return YES; 980| 0| } else if (_rollingFrequency > 0.0 && mostRecentLogFileInfo.age >= _rollingFrequency) { 981| 0| return YES; 982| 0| } 983| 0| 984| 0|#if TARGET_OS_IPHONE 985| 0| // When creating log file on iOS we're setting NSFileProtectionKey attribute to NSFileProtectionCompleteUnlessOpen. 986| 0| // 987| 0| // But in case if app is able to launch from background we need to have an ability to open log file any time we 988| 0| // want (even if device is locked). Thats why that attribute have to be changed to 989| 0| // NSFileProtectionCompleteUntilFirstUserAuthentication. 990| 0| // 991| 0| // If previous log was created when app wasn't running in background, but now it is - we archive it and create 992| 0| // a new one. 993| 0| // 994| 0| // If user has overwritten to NSFileProtectionNone there is no need to create a new one. 995| 0| if (doesAppRunInBackground()) { 996| 0| NSFileProtectionType key = mostRecentLogFileInfo.fileAttributes[NSFileProtectionKey]; 997| 0| BOOL isUntilFirstAuth = [key isEqualToString:NSFileProtectionCompleteUntilFirstUserAuthentication]; 998| 0| BOOL isNone = [key isEqualToString:NSFileProtectionNone]; 999| 0| 1000| 0| if (key != nil && !isUntilFirstAuth && !isNone) { 1001| 0| return YES; 1002| 0| } 1003| 0| } 1004| 0|#endif 1005| 0| 1006| 0| return NO; 1007| 0|} 1008| | 1009| |/** 1010| | * Returns the log file that should be used. 1011| | * If there is an existing log file that is suitable, within the 1012| | * constraints of maximumFileSize and rollingFrequency, then it is returned. 1013| | * 1014| | * Otherwise a new file is created and returned. 1015| | **/ 1016| 0|- (DDLogFileInfo *)currentLogFileInfo { 1017| 0| // The design of this method is taken from the DDAbstractLogger implementation. 1018| 0| // For extensive documentation please refer to the DDAbstractLogger implementation. 1019| 0| // Do not access this method on any Lumberjack queue, will deadlock. 1020| 0| 1021| 0| NSAssert(![self isOnGlobalLoggingQueue], @"Core architecture requirement failure"); 1022| 0| NSAssert(![self isOnInternalLoggerQueue], @"MUST access ivar directly, NOT via self.* syntax."); 1023| 0| 1024| 0| __block DDLogFileInfo *info = nil; 1025| 0| dispatch_block_t block = ^{ 1026| 0| info = [self lt_currentLogFileInfo]; 1027| 0| }; 1028| 0| 1029| 0| dispatch_queue_t globalLoggingQueue = [DDLog loggingQueue]; 1030| 0| 1031| 0| dispatch_sync(globalLoggingQueue, ^{ 1032| 0| dispatch_sync(self->_loggerQueue, block); 1033| 0| }); 1034| 0| 1035| 0| return info; 1036| 0|} 1037| | 1038| 0|- (DDLogFileInfo *)lt_currentLogFileInfo { 1039| 0| NSAssert([self isOnInternalLoggerQueue], @"lt_ methods should be on logger queue."); 1040| 0| 1041| 0| // Get the current log file info ivar (might be nil). 1042| 0| DDLogFileInfo *newCurrentLogFile = _currentLogFileInfo; 1043| 0| 1044| 0| // Check if we're resuming and if so, get the first of the sorted log file infos. 1045| 0| BOOL isResuming = newCurrentLogFile == nil; 1046| 0| if (isResuming) { 1047| 0| NSArray *sortedLogFileInfos = [_logFileManager sortedLogFileInfos]; 1048| 0| newCurrentLogFile = sortedLogFileInfos.firstObject; 1049| 0| } 1050| 0| 1051| 0| // Check if the file we've found is still valid. Otherwise create a new one. 1052| 0| if (newCurrentLogFile != nil && [self lt_shouldUseLogFile:newCurrentLogFile isResuming:isResuming]) { 1053| 0| if (isResuming) { 1054| 0| NSLogVerbose(@"DDFileLogger: Resuming logging with file %@", newCurrentLogFile.fileName); 1055| 0| } 1056| 0| _currentLogFileInfo = newCurrentLogFile; 1057| 0| } else { 1058| 0| NSString *currentLogFilePath; 1059| 0| if ([_logFileManager respondsToSelector:@selector(createNewLogFileWithError:)]) { 1060| 0| __autoreleasing NSError *error; 1061| 0| currentLogFilePath = [_logFileManager createNewLogFileWithError:&error]; 1062| 0| if (!currentLogFilePath) { 1063| 0| NSLogError(@"DDFileLogger: Failed to create new log file: %@", error); 1064| 0| } 1065| 0| } else { 1066| 0| #pragma clang diagnostic push 1067| 0| #pragma clang diagnostic ignored "-Wdeprecated-declarations" 1068| 0| NSAssert([_logFileManager respondsToSelector:@selector(createNewLogFile)], 1069| 0| @"Invalid log file manager! Responds neither to `-createNewLogFileWithError:` nor `-createNewLogFile`!"); 1070| 0| currentLogFilePath = [_logFileManager createNewLogFile]; 1071| 0| #pragma clang diagnostic pop 1072| 0| } 1073| 0| // Use static factory method here, since it checks for nil (and is unavailable to Swift). 1074| 0| _currentLogFileInfo = [DDLogFileInfo logFileWithPath:currentLogFilePath]; 1075| 0| } 1076| 0| 1077| 0| return _currentLogFileInfo; 1078| 0|} 1079| | 1080| 0|- (BOOL)lt_shouldUseLogFile:(nonnull DDLogFileInfo *)logFileInfo isResuming:(BOOL)isResuming { 1081| 0| NSAssert([self isOnInternalLoggerQueue], @"lt_ methods should be on logger queue."); 1082| 0| NSParameterAssert(logFileInfo); 1083| 0| 1084| 0| // Check if the log file is archived. We must not use archived log files. 1085| 0| if (logFileInfo.isArchived) { 1086| 0| return NO; 1087| 0| } 1088| 0| 1089| 0| // If we're resuming, we need to check if the log file is allowed for reuse or needs to be archived. 1090| 0| if (isResuming && (_doNotReuseLogFiles || [self lt_shouldLogFileBeArchived:logFileInfo])) { 1091| 0| logFileInfo.isArchived = YES; 1092| 0| 1093| 0| const BOOL logFileManagerRespondsToNewArchiveSelector = [_logFileManager respondsToSelector:@selector(didArchiveLogFile:wasRolled:)]; 1094| 0| if (logFileManagerRespondsToNewArchiveSelector || [_logFileManager respondsToSelector:@selector(didArchiveLogFile:)]) { 1095| 0| NSString *archivedFilePath = [logFileInfo.filePath copy]; 1096| 0| dispatch_block_t block; 1097| 0| if (logFileManagerRespondsToNewArchiveSelector) { 1098| 0| block = ^{ 1099| 0| [self->_logFileManager didArchiveLogFile:archivedFilePath wasRolled:NO]; 1100| 0| }; 1101| 0| } else { 1102| 0| block = ^{ 1103| 0| #pragma clang diagnostic push 1104| 0| #pragma clang diagnostic ignored "-Wdeprecated-declarations" 1105| 0| [self->_logFileManager didArchiveLogFile:archivedFilePath]; 1106| 0| #pragma clang diagnostic pop 1107| 0| }; 1108| 0| } 1109| 0| dispatch_async(_completionQueue, block); 1110| 0| } 1111| 0| 1112| 0| return NO; 1113| 0| } 1114| 0| 1115| 0| // All checks have passed. It's valid. 1116| 0| return YES; 1117| 0|} 1118| | 1119| 0|- (void)lt_monitorCurrentLogFileForExternalChanges { 1120| 0| NSAssert([self isOnInternalLoggerQueue], @"lt_ methods should be on logger queue."); 1121| 0| NSAssert(_currentLogFileHandle, @"Can not monitor without handle."); 1122| 0| 1123| 0| _currentLogFileVnode = dispatch_source_create(DISPATCH_SOURCE_TYPE_VNODE, 1124| 0| (uintptr_t)[_currentLogFileHandle fileDescriptor], 1125| 0| DISPATCH_VNODE_DELETE | DISPATCH_VNODE_RENAME | DISPATCH_VNODE_REVOKE, 1126| 0| _loggerQueue); 1127| 0| 1128| 0| __weak __auto_type weakSelf = self; 1129| 0| dispatch_source_set_event_handler(_currentLogFileVnode, ^{ @autoreleasepool { 1130| 0| NSLogInfo(@"DDFileLogger: Current logfile was moved. Rolling it and creating a new one"); 1131| 0| [weakSelf lt_rollLogFileNow]; 1132| 0| } }); 1133| 0| 1134| |#if !OS_OBJECT_USE_OBJC 1135| | dispatch_source_t vnode = _currentLogFileVnode; 1136| | dispatch_source_set_cancel_handler(_currentLogFileVnode, ^{ 1137| | dispatch_release(vnode); 1138| | }); 1139| |#endif 1140| | 1141| 0| if (@available(macOS 10.12, iOS 10.0, tvOS 10.0, watchOS 3.0, *)) { 1142| 0| dispatch_activate(_currentLogFileVnode); 1143| 0| } else { 1144| 0| dispatch_resume(_currentLogFileVnode); 1145| 0| } 1146| 0|} 1147| | 1148| 0|- (NSFileHandle *)lt_currentLogFileHandle { 1149| 0| NSAssert([self isOnInternalLoggerQueue], @"lt_ methods should be on logger queue."); 1150| 0| 1151| 0| if (!_currentLogFileHandle) { 1152| 0| NSString *logFilePath = [[self lt_currentLogFileInfo] filePath]; 1153| 0| _currentLogFileHandle = [NSFileHandle fileHandleForWritingAtPath:logFilePath]; 1154| 0| if (@available(macOS 10.15, iOS 13.0, tvOS 13.0, watchOS 6.0, *)) { 1155| 0| __autoreleasing NSError *error; 1156| 0| BOOL succeed = [_currentLogFileHandle seekToEndReturningOffset:nil error:&error]; 1157| 0| if (!succeed) { 1158| 0| NSLogError(@"DDFileLogger: Failed to seek to end of file: %@", error); 1159| 0| } 1160| 0| } else { 1161| 0| [_currentLogFileHandle seekToEndOfFile]; 1162| 0| } 1163| 0| 1164| 0| if (_currentLogFileHandle) { 1165| 0| [self lt_scheduleTimerToRollLogFileDueToAge]; 1166| 0| [self lt_monitorCurrentLogFileForExternalChanges]; 1167| 0| } 1168| 0| } 1169| 0| 1170| 0| return _currentLogFileHandle; 1171| 0|} 1172| | 1173| |//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 1174| |#pragma mark DDLogger Protocol 1175| |//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 1176| | 1177| |static int exception_count = 0; 1178| | 1179| 0|- (void)logMessage:(DDLogMessage *)logMessage { 1180| 0| // Don't need to check for isOnInternalLoggerQueue, -lt_dataForMessage: will do it for us. 1181| 0| NSData *data = [self lt_dataForMessage:logMessage]; 1182| 0| 1183| 0| if (data.length == 0) { 1184| 0| return; 1185| 0| } 1186| 0| 1187| 0| [self lt_logData:data]; 1188| 0|} 1189| | 1190| 0|- (void)willLogMessage:(DDLogFileInfo *)logFileInfo {} 1191| | 1192| 0|- (void)didLogMessage:(DDLogFileInfo *)logFileInfo { 1193| 0| [self lt_maybeRollLogFileDueToSize]; 1194| 0|} 1195| | 1196| 0|- (BOOL)shouldArchiveRecentLogFileInfo:(__unused DDLogFileInfo *)recentLogFileInfo { 1197| 0| return NO; 1198| 0|} 1199| | 1200| 0|- (void)willRemoveLogger { 1201| 0| [self lt_rollLogFileNow]; 1202| 0|} 1203| | 1204| 0|- (void)flush { 1205| 0| // This method is public. 1206| 0| // We need to execute the rolling on our logging thread/queue. 1207| 0| 1208| 0| dispatch_block_t block = ^{ 1209| 0| @autoreleasepool { 1210| 0| [self lt_flush]; 1211| 0| } 1212| 0| }; 1213| 0| 1214| 0| // The design of this method is taken from the DDAbstractLogger implementation. 1215| 0| // For extensive documentation please refer to the DDAbstractLogger implementation. 1216| 0| 1217| 0| if ([self isOnInternalLoggerQueue]) { 1218| 0| block(); 1219| 0| } else { 1220| 0| dispatch_queue_t globalLoggingQueue = [DDLog loggingQueue]; 1221| 0| NSAssert(![self isOnGlobalLoggingQueue], @"Core architecture requirement failure"); 1222| 0| 1223| 0| dispatch_sync(globalLoggingQueue, ^{ 1224| 0| dispatch_sync(self.loggerQueue, block); 1225| 0| }); 1226| 0| } 1227| 0|} 1228| | 1229| 0|- (void)lt_flush { 1230| 0| NSAssert([self isOnInternalLoggerQueue], @"flush should only be executed on internal queue."); 1231| 0| if (@available(macOS 10.15, iOS 13.0, tvOS 13.0, watchOS 6.0, *)) { 1232| 0| __autoreleasing NSError *error; 1233| 0| BOOL succeed = [_currentLogFileHandle synchronizeAndReturnError:&error]; 1234| 0| if (!succeed) { 1235| 0| NSLogError(@"DDFileLogger: Failed to synchronize file: %@", error); 1236| 0| } 1237| 0| } else { 1238| 0| [_currentLogFileHandle synchronizeFile]; 1239| 0| } 1240| 0|} 1241| | 1242| 0|- (DDLoggerName)loggerName { 1243| 0| return DDLoggerNameFile; 1244| 0|} 1245| | 1246| |@end 1247| | 1248| |@implementation DDFileLogger (Internal) 1249| | 1250| 0|- (void)logData:(NSData *)data { 1251| 0| // This method is public. 1252| 0| // We need to execute the rolling on our logging thread/queue. 1253| 0| 1254| 0| dispatch_block_t block = ^{ 1255| 0| @autoreleasepool { 1256| 0| [self lt_logData:data]; 1257| 0| } 1258| 0| }; 1259| 0| 1260| 0| // The design of this method is taken from the DDAbstractLogger implementation. 1261| 0| // For extensive documentation please refer to the DDAbstractLogger implementation. 1262| 0| 1263| 0| if ([self isOnInternalLoggerQueue]) { 1264| 0| block(); 1265| 0| } else { 1266| 0| dispatch_queue_t globalLoggingQueue = [DDLog loggingQueue]; 1267| 0| NSAssert(![self isOnGlobalLoggingQueue], @"Core architecture requirement failure"); 1268| 0| 1269| 0| dispatch_sync(globalLoggingQueue, ^{ 1270| 0| dispatch_sync(self.loggerQueue, block); 1271| 0| }); 1272| 0| } 1273| 0|} 1274| | 1275| 0|- (void)lt_deprecationCatchAll {} 1276| | 1277| 0|- (NSMethodSignature *)methodSignatureForSelector:(SEL)aSelector { 1278| 0| if (aSelector == @selector(willLogMessage) || aSelector == @selector(didLogMessage)) { 1279| 0| // Ignore calls to deprecated methods. 1280| 0| return [self methodSignatureForSelector:@selector(lt_deprecationCatchAll)]; 1281| 0| } 1282| 0| 1283| 0| return [super methodSignatureForSelector:aSelector]; 1284| 0|} 1285| | 1286| 0|- (void)forwardInvocation:(NSInvocation *)anInvocation { 1287| 0| if (anInvocation.selector != @selector(lt_deprecationCatchAll)) { 1288| 0| [super forwardInvocation:anInvocation]; 1289| 0| } 1290| 0|} 1291| | 1292| 0|- (void)lt_logData:(NSData *)data { 1293| 0| static BOOL implementsDeprecatedWillLog = NO; 1294| 0| static BOOL implementsDeprecatedDidLog = NO; 1295| 0| 1296| 0| static dispatch_once_t onceToken; 1297| 0| dispatch_once(&onceToken, ^{ 1298| 0| implementsDeprecatedWillLog = [self respondsToSelector:@selector(willLogMessage)]; 1299| 0| implementsDeprecatedDidLog = [self respondsToSelector:@selector(didLogMessage)]; 1300| 0| }); 1301| 0| 1302| 0| NSAssert([self isOnInternalLoggerQueue], @"logMessage should only be executed on internal queue."); 1303| 0| 1304| 0| if (data.length == 0) { 1305| 0| return; 1306| 0| } 1307| 0| 1308| 0| @try { 1309| 0| if (implementsDeprecatedWillLog) { 1310| 0|#pragma clang diagnostic push 1311| 0|#pragma clang diagnostic ignored "-Wdeprecated-declarations" 1312| 0| [self willLogMessage]; 1313| 0|#pragma clang diagnostic pop 1314| 0| } else { 1315| 0| [self willLogMessage:_currentLogFileInfo]; 1316| 0| } 1317| 0| 1318| 0| NSFileHandle *handle = [self lt_currentLogFileHandle]; 1319| 0| if (@available(macOS 10.15, iOS 13.0, tvOS 13.0, watchOS 6.0, *)) { 1320| 0| __autoreleasing NSError *error; 1321| 0| BOOL sought = [handle seekToEndReturningOffset:nil error:&error]; 1322| 0| if (!sought) { 1323| 0| NSLogError(@"DDFileLogger: Failed to seek to end of file: %@", error); 1324| 0| } 1325| 0| BOOL written = [handle writeData:data error:&error]; 1326| 0| if (!written) { 1327| 0| NSLogError(@"DDFileLogger: Failed to write data: %@", error); 1328| 0| } 1329| 0| } else { 1330| 0| [handle seekToEndOfFile]; 1331| 0| [handle writeData:data]; 1332| 0| } 1333| 0| 1334| 0| if (implementsDeprecatedDidLog) { 1335| 0|#pragma clang diagnostic push 1336| 0|#pragma clang diagnostic ignored "-Wdeprecated-declarations" 1337| 0| [self didLogMessage]; 1338| 0|#pragma clang diagnostic pop 1339| 0| } else { 1340| 0| [self didLogMessage:_currentLogFileInfo]; 1341| 0| } 1342| 0| 1343| 0| } @catch (NSException *exception) { 1344| 0| exception_count++; 1345| 0| 1346| 0| if (exception_count <= 10) { 1347| 0| NSLogError(@"DDFileLogger.logMessage: %@", exception); 1348| 0| 1349| 0| if (exception_count == 10) { 1350| 0| NSLogError(@"DDFileLogger.logMessage: Too many exceptions -- will not log any more of them."); 1351| 0| } 1352| 0| } 1353| 0| } 1354| 0|} 1355| | 1356| 0|- (NSData *)lt_dataForMessage:(DDLogMessage *)logMessage { 1357| 0| NSAssert([self isOnInternalLoggerQueue], @"logMessage should only be executed on internal queue."); 1358| 0| 1359| 0| NSString *message = logMessage->_message; 1360| 0| BOOL isFormatted = NO; 1361| 0| 1362| 0| if (_logFormatter != nil) { 1363| 0| message = [_logFormatter formatLogMessage:logMessage]; 1364| 0| isFormatted = message != logMessage->_message; 1365| 0| } 1366| 0| 1367| 0| if (message.length == 0) { 1368| 0| return nil; 1369| 0| } 1370| 0| 1371| 0| BOOL shouldFormat = !isFormatted || _automaticallyAppendNewlineForCustomFormatters; 1372| 0| if (shouldFormat && ![message hasSuffix:@"\n"]) { 1373| 0| message = [message stringByAppendingString:@"\n"]; 1374| 0| } 1375| 0| 1376| 0| return [message dataUsingEncoding:NSUTF8StringEncoding]; 1377| 0|} 1378| | 1379| |@end 1380| | 1381| |//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 1382| |#pragma mark - 1383| |//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 1384| | 1385| |static NSString * const kDDXAttrArchivedName = @"lumberjack.log.archived"; 1386| | 1387| |@interface DDLogFileInfo () { 1388| | __strong NSString *_filePath; 1389| | __strong NSString *_fileName; 1390| | 1391| | __strong NSDictionary *_fileAttributes; 1392| | 1393| | __strong NSDate *_creationDate; 1394| | __strong NSDate *_modificationDate; 1395| | 1396| | unsigned long long _fileSize; 1397| |} 1398| | 1399| |#if TARGET_IPHONE_SIMULATOR 1400| | 1401| |// Old implementation of extended attributes on the simulator. 1402| | 1403| |- (BOOL)_hasExtensionAttributeWithName:(NSString *)attrName; 1404| |- (void)_removeExtensionAttributeWithName:(NSString *)attrName; 1405| | 1406| |#endif 1407| | 1408| |@end 1409| | 1410| | 1411| |@implementation DDLogFileInfo 1412| | 1413| |@synthesize filePath; 1414| | 1415| |@dynamic fileName; 1416| |@dynamic fileAttributes; 1417| |@dynamic creationDate; 1418| |@dynamic modificationDate; 1419| |@dynamic fileSize; 1420| |@dynamic age; 1421| | 1422| |@dynamic isArchived; 1423| | 1424| |#pragma mark Lifecycle 1425| | 1426| 0|+ (instancetype)logFileWithPath:(NSString *)aFilePath { 1427| 0| if (!aFilePath) return nil; 1428| 0| return [[self alloc] initWithFilePath:aFilePath]; 1429| 0|} 1430| | 1431| 0|- (instancetype)initWithFilePath:(NSString *)aFilePath { 1432| 0| NSParameterAssert(aFilePath); 1433| 0| if ((self = [super init])) { 1434| 0| filePath = [aFilePath copy]; 1435| 0| } 1436| 0| 1437| 0| return self; 1438| 0|} 1439| | 1440| |//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 1441| |#pragma mark Standard Info 1442| |//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 1443| | 1444| 0|- (NSDictionary *)fileAttributes { 1445| 0| if (_fileAttributes == nil && filePath != nil) { 1446| 0| NSError *error = nil; 1447| 0| _fileAttributes = [[NSFileManager defaultManager] attributesOfItemAtPath:filePath error:&error]; 1448| 0| 1449| 0| if (error) { 1450| 0| NSLogError(@"DDLogFileInfo: Failed to read file attributes: %@", error); 1451| 0| } 1452| 0| } 1453| 0| 1454| 0| return _fileAttributes ?: @{}; 1455| 0|} 1456| | 1457| 0|- (NSString *)fileName { 1458| 0| if (_fileName == nil) { 1459| 0| _fileName = [filePath lastPathComponent]; 1460| 0| } 1461| 0| 1462| 0| return _fileName; 1463| 0|} 1464| | 1465| 0|- (NSDate *)modificationDate { 1466| 0| if (_modificationDate == nil) { 1467| 0| _modificationDate = self.fileAttributes[NSFileModificationDate]; 1468| 0| } 1469| 0| 1470| 0| return _modificationDate; 1471| 0|} 1472| | 1473| 0|- (NSDate *)creationDate { 1474| 0| if (_creationDate == nil) { 1475| 0| _creationDate = self.fileAttributes[NSFileCreationDate]; 1476| 0| } 1477| 0| 1478| 0| return _creationDate; 1479| 0|} 1480| | 1481| 0|- (unsigned long long)fileSize { 1482| 0| if (_fileSize == 0) { 1483| 0| _fileSize = [self.fileAttributes[NSFileSize] unsignedLongLongValue]; 1484| 0| } 1485| 0| 1486| 0| return _fileSize; 1487| 0|} 1488| | 1489| 0|- (NSTimeInterval)age { 1490| 0| return -[[self creationDate] timeIntervalSinceNow]; 1491| 0|} 1492| | 1493| 0|- (NSString *)description { 1494| 0| return [@{ @"filePath": self.filePath ? : @"", 1495| 0| @"fileName": self.fileName ? : @"", 1496| 0| @"fileAttributes": self.fileAttributes ? : @"", 1497| 0| @"creationDate": self.creationDate ? : @"", 1498| 0| @"modificationDate": self.modificationDate ? : @"", 1499| 0| @"fileSize": @(self.fileSize), 1500| 0| @"age": @(self.age), 1501| 0| @"isArchived": @(self.isArchived) } description]; 1502| 0|} 1503| | 1504| |//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 1505| |#pragma mark Archiving 1506| |//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 1507| | 1508| 0|- (BOOL)isArchived { 1509| 0| return [self hasExtendedAttributeWithName:kDDXAttrArchivedName]; 1510| 0|} 1511| | 1512| 0|- (void)setIsArchived:(BOOL)flag { 1513| 0| if (flag) { 1514| 0| [self addExtendedAttributeWithName:kDDXAttrArchivedName]; 1515| 0| } else { 1516| 0| [self removeExtendedAttributeWithName:kDDXAttrArchivedName]; 1517| 0| } 1518| 0|} 1519| | 1520| |//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 1521| |#pragma mark Changes 1522| |//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 1523| | 1524| 0|- (void)reset { 1525| 0| _fileName = nil; 1526| 0| _fileAttributes = nil; 1527| 0| _creationDate = nil; 1528| 0| _modificationDate = nil; 1529| 0|} 1530| | 1531| 0|- (void)renameFile:(NSString *)newFileName { 1532| 0| // This method is only used on the iPhone simulator, where normal extended attributes are broken. 1533| 0| // See full explanation in the header file. 1534| 0| 1535| 0| if (![newFileName isEqualToString:[self fileName]]) { 1536| 0| NSFileManager* fileManager = [NSFileManager defaultManager]; 1537| 0| NSString *fileDir = [filePath stringByDeletingLastPathComponent]; 1538| 0| NSString *newFilePath = [fileDir stringByAppendingPathComponent:newFileName]; 1539| 0| 1540| 0| // We only want to assert when we're not using the simulator, as we're "archiving" a log file with this method in the sim 1541| 0| // (in which case the file might not exist anymore and neither does it parent folder). 1542| 0|#if defined(DEBUG) && (!defined(TARGET_IPHONE_SIMULATOR) || !TARGET_IPHONE_SIMULATOR) 1543| 0| BOOL directory = NO; 1544| 0| [fileManager fileExistsAtPath:fileDir isDirectory:&directory]; 1545| 0| NSAssert(directory, @"Containing directory must exist."); 1546| 0|#endif 1547| 0| 1548| 0| NSError *error = nil; 1549| 0| 1550| 0| BOOL success = [fileManager removeItemAtPath:newFilePath error:&error]; 1551| 0| if (!success && error.code != NSFileNoSuchFileError) { 1552| 0| NSLogError(@"DDLogFileInfo: Error deleting archive (%@): %@", self.fileName, error); 1553| 0| } 1554| 0| 1555| 0| success = [fileManager moveItemAtPath:filePath toPath:newFilePath error:&error]; 1556| 0| 1557| 0| // When a log file is deleted, moved or renamed on the simulator, we attempt to rename it as a 1558| 0| // result of "archiving" it, but since the file doesn't exist anymore, needless error logs are printed 1559| 0| // We therefore ignore this error, and assert that the directory we are copying into exists (which 1560| 0| // is the only other case where this error code can come up). 1561| |#if TARGET_IPHONE_SIMULATOR 1562| | if (!success && error.code != NSFileNoSuchFileError) 1563| |#else 1564| 0| if (!success) 1565| 0|#endif 1566| 0| { 1567| 0| NSLogError(@"DDLogFileInfo: Error renaming file (%@): %@", self.fileName, error); 1568| 0| } 1569| 0| 1570| 0| filePath = newFilePath; 1571| 0| [self reset]; 1572| 0| } 1573| 0|} 1574| | 1575| |//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 1576| |#pragma mark Attribute Management 1577| |//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 1578| | 1579| |#if TARGET_IPHONE_SIMULATOR 1580| | 1581| |// Old implementation of extended attributes on the simulator. 1582| | 1583| |// Extended attributes were not working properly on the simulator 1584| |// due to misuse of setxattr() function. 1585| |// Now that this is fixed in the new implementation, we want to keep 1586| |// backward compatibility with previous simulator installations. 1587| | 1588| |static NSString * const kDDExtensionSeparator = @"."; 1589| | 1590| |static NSString *_xattrToExtensionName(NSString *attrName) { 1591| | static NSDictionary* _xattrToExtensionNameMap; 1592| | static dispatch_once_t _token; 1593| | dispatch_once(&_token, ^{ 1594| | _xattrToExtensionNameMap = @{ kDDXAttrArchivedName: @"archived" }; 1595| | }); 1596| | return [_xattrToExtensionNameMap objectForKey:attrName]; 1597| |} 1598| | 1599| |- (BOOL)_hasExtensionAttributeWithName:(NSString *)attrName { 1600| | // This method is only used on the iPhone simulator for backward compatibility reason. 1601| | 1602| | // Split the file name into components. File name may have various format, but generally 1603| | // structure is same: 1604| | // 1605| | // . and .archived. 1606| | // or 1607| | // and .archived 1608| | // 1609| | // So we want to search for the attrName in the components (ignoring the first array index). 1610| | 1611| | NSArray *components = [[self fileName] componentsSeparatedByString:kDDExtensionSeparator]; 1612| | 1613| | // Watch out for file names without an extension 1614| | 1615| | for (NSUInteger i = 1; i < components.count; i++) { 1616| | NSString *attr = components[i]; 1617| | 1618| | if ([attrName isEqualToString:attr]) { 1619| | return YES; 1620| | } 1621| | } 1622| | 1623| | return NO; 1624| |} 1625| | 1626| |- (void)_removeExtensionAttributeWithName:(NSString *)attrName { 1627| | // This method is only used on the iPhone simulator for backward compatibility reason. 1628| | 1629| | if ([attrName length] == 0) { 1630| | return; 1631| | } 1632| | 1633| | // Example: 1634| | // attrName = "archived" 1635| | // 1636| | // "mylog.archived.txt" -> "mylog.txt" 1637| | // "mylog.archived" -> "mylog" 1638| | 1639| | NSArray *components = [[self fileName] componentsSeparatedByString:kDDExtensionSeparator]; 1640| | 1641| | NSUInteger count = [components count]; 1642| | 1643| | NSUInteger estimatedNewLength = [[self fileName] length]; 1644| | NSMutableString *newFileName = [NSMutableString stringWithCapacity:estimatedNewLength]; 1645| | 1646| | if (count > 0) { 1647| | [newFileName appendString:components.firstObject]; 1648| | } 1649| | 1650| | BOOL found = NO; 1651| | 1652| | NSUInteger i; 1653| | 1654| | for (i = 1; i < count; i++) { 1655| | NSString *attr = components[i]; 1656| | 1657| | if ([attrName isEqualToString:attr]) { 1658| | found = YES; 1659| | } else { 1660| | [newFileName appendString:kDDExtensionSeparator]; 1661| | [newFileName appendString:attr]; 1662| | } 1663| | } 1664| | 1665| | if (found) { 1666| | [self renameFile:newFileName]; 1667| | } 1668| |} 1669| | 1670| |#endif /* if TARGET_IPHONE_SIMULATOR */ 1671| | 1672| 0|- (BOOL)hasExtendedAttributeWithName:(NSString *)attrName { 1673| 0| const char *path = [filePath fileSystemRepresentation]; 1674| 0| const char *name = [attrName UTF8String]; 1675| 0| BOOL hasExtendedAttribute = NO; 1676| 0| char buffer[1]; 1677| 0| 1678| 0| ssize_t result = getxattr(path, name, buffer, 1, 0, 0); 1679| 0| 1680| 0| // Fast path 1681| 0| if (result > 0 && buffer[0] == '\1') { 1682| 0| hasExtendedAttribute = YES; 1683| 0| } 1684| 0| // Maintain backward compatibility, but fix it for future checks 1685| 0| else if (result >= 0) { 1686| 0| hasExtendedAttribute = YES; 1687| 0| 1688| 0| [self addExtendedAttributeWithName:attrName]; 1689| 0| } 1690| |#if TARGET_IPHONE_SIMULATOR 1691| | else if ([self _hasExtensionAttributeWithName:_xattrToExtensionName(attrName)]) { 1692| | hasExtendedAttribute = YES; 1693| | 1694| | [self addExtendedAttributeWithName:attrName]; 1695| | } 1696| |#endif 1697| | 1698| 0| return hasExtendedAttribute; 1699| 0|} 1700| | 1701| 0|- (void)addExtendedAttributeWithName:(NSString *)attrName { 1702| 0| const char *path = [filePath fileSystemRepresentation]; 1703| 0| const char *name = [attrName UTF8String]; 1704| 0| 1705| 0| int result = setxattr(path, name, "\1", 1, 0, 0); 1706| 0| 1707| 0| if (result < 0) { 1708| 0| if (errno != ENOENT) { 1709| 0| NSLogError(@"DDLogFileInfo: setxattr(%@, %@): error = %s", 1710| 0| attrName, 1711| 0| filePath, 1712| 0| strerror(errno)); 1713| 0| } else { 1714| 0| NSLogDebug(@"DDLogFileInfo: File does not exist in setxattr(%@, %@): error = %s", 1715| 0| attrName, 1716| 0| filePath, 1717| 0| strerror(errno)); 1718| 0| } 1719| 0| } 1720| |#if TARGET_IPHONE_SIMULATOR 1721| | else { 1722| | [self _removeExtensionAttributeWithName:_xattrToExtensionName(attrName)]; 1723| | } 1724| |#endif 1725| |} 1726| | 1727| 0|- (void)removeExtendedAttributeWithName:(NSString *)attrName { 1728| 0| const char *path = [filePath fileSystemRepresentation]; 1729| 0| const char *name = [attrName UTF8String]; 1730| 0| 1731| 0| int result = removexattr(path, name, 0); 1732| 0| 1733| 0| if (result < 0 && errno != ENOATTR) { 1734| 0| NSLogError(@"DDLogFileInfo: removexattr(%@, %@): error = %s", 1735| 0| attrName, 1736| 0| self.fileName, 1737| 0| strerror(errno)); 1738| 0| } 1739| 0| 1740| |#if TARGET_IPHONE_SIMULATOR 1741| | [self _removeExtensionAttributeWithName:_xattrToExtensionName(attrName)]; 1742| |#endif 1743| |} 1744| | 1745| |//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 1746| |#pragma mark Comparisons 1747| |//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 1748| | 1749| 0|- (BOOL)isEqual:(id)object { 1750| 0| if ([object isKindOfClass:[self class]]) { 1751| 0| DDLogFileInfo *another = (DDLogFileInfo *)object; 1752| 0| 1753| 0| return [filePath isEqualToString:[another filePath]]; 1754| 0| } 1755| 0| 1756| 0| return NO; 1757| 0|} 1758| | 1759| 0|- (NSUInteger)hash { 1760| 0| return [filePath hash]; 1761| 0|} 1762| | 1763| 0|- (NSComparisonResult)reverseCompareByCreationDate:(DDLogFileInfo *)another { 1764| 0| __auto_type us = [self creationDate]; 1765| 0| __auto_type them = [another creationDate]; 1766| 0| return [them compare:us]; 1767| 0|} 1768| | 1769| 0|- (NSComparisonResult)reverseCompareByModificationDate:(DDLogFileInfo *)another { 1770| 0| __auto_type us = [self modificationDate]; 1771| 0| __auto_type them = [another modificationDate]; 1772| 0| return [them compare:us]; 1773| 0|} 1774| | 1775| |@end 1776| | 1777| |#if TARGET_OS_IPHONE 1778| |/** 1779| | * When creating log file on iOS we're setting NSFileProtectionKey attribute to NSFileProtectionCompleteUnlessOpen. 1780| | * 1781| | * But in case if app is able to launch from background we need to have an ability to open log file any time we 1782| | * want (even if device is locked). Thats why that attribute have to be changed to 1783| | * NSFileProtectionCompleteUntilFirstUserAuthentication. 1784| | */ 1785| 0|BOOL doesAppRunInBackground() { 1786| 0| BOOL answer = NO; 1787| 0| 1788| 0| NSArray *backgroundModes = [[NSBundle mainBundle] objectForInfoDictionaryKey:@"UIBackgroundModes"]; 1789| 0| 1790| 0| for (NSString *mode in backgroundModes) { 1791| 0| if (mode.length > 0) { 1792| 0| answer = YES; 1793| 0| break; 1794| 0| } 1795| 0| } 1796| 0| 1797| 0| return answer; 1798| 0|} 1799| | 1800| |#endif /Users/runner/work/CocoaLumberjack/CocoaLumberjack/Sources/CocoaLumberjack/DDLog.m: 1| |// Software License Agreement (BSD License) 2| |// 3| |// Copyright (c) 2010-2021, Deusty, LLC 4| |// All rights reserved. 5| |// 6| |// Redistribution and use of this software in source and binary forms, 7| |// with or without modification, are permitted provided that the following conditions are met: 8| |// 9| |// * Redistributions of source code must retain the above copyright notice, 10| |// this list of conditions and the following disclaimer. 11| |// 12| |// * Neither the name of Deusty nor the names of its contributors may be used 13| |// to endorse or promote products derived from this software without specific 14| |// prior written permission of Deusty, LLC. 15| | 16| |#if !__has_feature(objc_arc) 17| |#error This file must be compiled with ARC. Use -fobjc-arc flag (or convert project to ARC). 18| |#endif 19| | 20| |#import 21| |#import 22| |#import 23| | 24| |#if TARGET_OS_IOS 25| | #import 26| | #import 27| |#elif !defined(DD_CLI) && __has_include() 28| | #import 29| |#endif 30| | 31| |// Disable legacy macros 32| |#ifndef DD_LEGACY_MACROS 33| | #define DD_LEGACY_MACROS 0 34| |#endif 35| | 36| |#import 37| | 38| |// We probably shouldn't be using DDLog() statements within the DDLog implementation. 39| |// But we still want to leave our log statements for any future debugging, 40| |// and to allow other developers to trace the implementation (which is a great learning tool). 41| |// 42| |// So we use a primitive logging macro around NSLog. 43| |// We maintain the NS prefix on the macros to be explicit about the fact that we're using NSLog. 44| | 45| |#ifndef DD_DEBUG 46| 6| #define DD_DEBUG 0 47| |#endif 48| | 49| 6|#define NSLogDebug(frmt, ...) do{ if(DD_DEBUG) NSLog((frmt), ##__VA_ARGS__); } while(0) 50| | 51| |// The "global logging queue" refers to [DDLog loggingQueue]. 52| |// It is the queue that all log statements go through. 53| |// 54| |// The logging queue sets a flag via dispatch_queue_set_specific using this key. 55| |// We can check for this key via dispatch_get_specific() to see if we're on the "global logging queue". 56| | 57| |static void *const GlobalLoggingQueueIdentityKey = (void *)&GlobalLoggingQueueIdentityKey; 58| | 59| |@interface DDLoggerNode : NSObject 60| |{ 61| | // Direct accessors to be used only for performance 62| | @public 63| | id _logger; 64| | DDLogLevel _level; 65| | dispatch_queue_t _loggerQueue; 66| |} 67| | 68| |@property (nonatomic, readonly) id logger; 69| |@property (nonatomic, readonly) DDLogLevel level; 70| |@property (nonatomic, readonly) dispatch_queue_t loggerQueue; 71| | 72| |+ (instancetype)nodeWithLogger:(id )logger 73| | loggerQueue:(dispatch_queue_t)loggerQueue 74| | level:(DDLogLevel)level; 75| | 76| |@end 77| | 78| | 79| |//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 80| |#pragma mark - 81| |//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 82| | 83| |@interface DDLog () 84| | 85| |// An array used to manage all the individual loggers. 86| |// The array is only modified on the loggingQueue/loggingThread. 87| |@property (nonatomic, strong) NSMutableArray *_loggers; 88| | 89| |@end 90| | 91| |@implementation DDLog 92| | 93| |// All logging statements are added to the same queue to ensure FIFO operation. 94| |static dispatch_queue_t _loggingQueue; 95| | 96| |// Individual loggers are executed concurrently per log statement. 97| |// Each logger has it's own associated queue, and a dispatch group is used for synchronization. 98| |static dispatch_group_t _loggingGroup; 99| | 100| |// Minor optimization for uniprocessor machines 101| |static NSUInteger _numProcessors; 102| | 103| |/** 104| | * Returns the singleton `DDLog`. 105| | * The instance is used by `DDLog` class methods. 106| | * 107| | * @return The singleton `DDLog`. 108| | */ 109| 42|+ (instancetype)sharedInstance { 110| 42| static id sharedInstance = nil; 111| 42| 112| 42| static dispatch_once_t onceToken; 113| 42| dispatch_once(&onceToken, ^{ 114| 1| sharedInstance = [[self alloc] init]; 115| 1| }); 116| 42| 117| 42| return sharedInstance; 118| 42|} 119| | 120| |/** 121| | * The runtime sends initialize to each class in a program exactly one time just before the class, 122| | * or any class that inherits from it, is sent its first message from within the program. (Thus the 123| | * method may never be invoked if the class is not used.) The runtime sends the initialize message to 124| | * classes in a thread-safe manner. Superclasses receive this message before their subclasses. 125| | * 126| | * This method may also be called directly, hence the safety mechanism. 127| | **/ 128| 1|+ (void)initialize { 129| 1| static dispatch_once_t DDLogOnceToken; 130| 1| 131| 1| dispatch_once(&DDLogOnceToken, ^{ 132| 1| NSLogDebug(@"DDLog: Using grand central dispatch"); 133| 1| 134| 1| _loggingQueue = dispatch_queue_create("cocoa.lumberjack", NULL); 135| 1| _loggingGroup = dispatch_group_create(); 136| 1| 137| 1| void *nonNullValue = GlobalLoggingQueueIdentityKey; // Whatever, just not null 138| 1| dispatch_queue_set_specific(_loggingQueue, GlobalLoggingQueueIdentityKey, nonNullValue, NULL); 139| 1| 140| 1| // Figure out how many processors are available. 141| 1| // This may be used later for an optimization on uniprocessor machines. 142| 1| 143| 1| _numProcessors = MAX([NSProcessInfo processInfo].processorCount, (NSUInteger) 1); 144| 1| 145| 1| NSLogDebug(@"DDLog: numProcessors = %@", @(_numProcessors)); 146| 1| }); 147| 1|} 148| | 149| |/** 150| | * The `DDLog` initializer. 151| | * Static variables are set only once. 152| | * 153| | * @return An initialized `DDLog` instance. 154| | */ 155| 1|- (instancetype)init { 156| 1| self = [super init]; 157| 1| 158| 1| if (self) { 159| 1| self._loggers = [[NSMutableArray alloc] initWithCapacity:4]; 160| 1| 161| 1|#if TARGET_OS_IOS 162| 1| NSString *notificationName = UIApplicationWillTerminateNotification; 163| |#else 164| | NSString *notificationName = nil; 165| | 166| | // On Command Line Tool apps AppKit may not be available 167| |#if !defined(DD_CLI) && __has_include() 168| | if (NSApp) { 169| | notificationName = NSApplicationWillTerminateNotification; 170| | } 171| |#endif 172| | 173| | if (!notificationName) { 174| | // If there is no NSApp -> we are running Command Line Tool app. 175| | // In this case terminate notification wouldn't be fired, so we use workaround. 176| | __weak __auto_type weakSelf = self; 177| | atexit_b (^{ 178| | [weakSelf applicationWillTerminate:nil]; 179| | }); 180| | } 181| | 182| |#endif /* if TARGET_OS_IOS */ 183| | 184| 1| if (notificationName) { 185| 1| [[NSNotificationCenter defaultCenter] addObserver:self 186| 1| selector:@selector(applicationWillTerminate:) 187| 1| name:notificationName 188| 1| object:nil]; 189| 1| } 190| 1| } 191| 1| 192| 1| return self; 193| 1|} 194| | 195| |/** 196| | * Provides access to the logging queue. 197| | **/ 198| 0|+ (dispatch_queue_t)loggingQueue { 199| 0| return _loggingQueue; 200| 0|} 201| | 202| |//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 203| |#pragma mark Notifications 204| |//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 205| | 206| 0|- (void)applicationWillTerminate:(NSNotification * __attribute__((unused)))notification { 207| 0| [self flushLog]; 208| 0|} 209| | 210| |//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 211| |#pragma mark Logger Management 212| |//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 213| | 214| 0|+ (void)addLogger:(id )logger { 215| 0| [self.sharedInstance addLogger:logger]; 216| 0|} 217| | 218| 0|- (void)addLogger:(id )logger { 219| 0| [self addLogger:logger withLevel:DDLogLevelAll]; // DDLogLevelAll has all bits set 220| 0|} 221| | 222| 0|+ (void)addLogger:(id )logger withLevel:(DDLogLevel)level { 223| 0| [self.sharedInstance addLogger:logger withLevel:level]; 224| 0|} 225| | 226| 5|- (void)addLogger:(id )logger withLevel:(DDLogLevel)level { 227| 5| if (!logger) { 228| 0| return; 229| 0| } 230| 5| 231| 5| dispatch_async(_loggingQueue, ^{ @autoreleasepool { 232| 5| [self lt_addLogger:logger level:level]; 233| 5| } }); 234| 5|} 235| | 236| 0|+ (void)removeLogger:(id )logger { 237| 0| [self.sharedInstance removeLogger:logger]; 238| 0|} 239| | 240| 5|- (void)removeLogger:(id )logger { 241| 5| if (!logger) { 242| 0| return; 243| 0| } 244| 5| 245| 5| dispatch_async(_loggingQueue, ^{ @autoreleasepool { 246| 5| [self lt_removeLogger:logger]; 247| 5| } }); 248| 5|} 249| | 250| 14|+ (void)removeAllLoggers { 251| 14| [self.sharedInstance removeAllLoggers]; 252| 14|} 253| | 254| 14|- (void)removeAllLoggers { 255| 14| dispatch_async(_loggingQueue, ^{ @autoreleasepool { 256| 14| [self lt_removeAllLoggers]; 257| 14| } }); 258| 14|} 259| | 260| 4|+ (NSArray> *)allLoggers { 261| 4| return [self.sharedInstance allLoggers]; 262| 4|} 263| | 264| 4|- (NSArray> *)allLoggers { 265| 4| __block NSArray *theLoggers; 266| 4| 267| 4| dispatch_sync(_loggingQueue, ^{ @autoreleasepool { 268| 4| theLoggers = [self lt_allLoggers]; 269| 4| } }); 270| 4| 271| 4| return theLoggers; 272| 4|} 273| | 274| 7|+ (NSArray *)allLoggersWithLevel { 275| 7| return [self.sharedInstance allLoggersWithLevel]; 276| 7|} 277| | 278| 7|- (NSArray *)allLoggersWithLevel { 279| 7| __block NSArray *theLoggersWithLevel; 280| 7| 281| 7| dispatch_sync(_loggingQueue, ^{ @autoreleasepool { 282| 7| theLoggersWithLevel = [self lt_allLoggersWithLevel]; 283| 7| } }); 284| 7| 285| 7| return theLoggersWithLevel; 286| 7|} 287| | 288| |//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 289| |#pragma mark - Master Logging 290| |//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 291| | 292| 10|- (void)queueLogMessage:(DDLogMessage *)logMessage asynchronously:(BOOL)asyncFlag { 293| 10| // We have a tricky situation here... 294| 10| // 295| 10| // In the common case, when the queueSize is below the maximumQueueSize, 296| 10| // we want to simply enqueue the logMessage. And we want to do this as fast as possible, 297| 10| // which means we don't want to block and we don't want to use any locks. 298| 10| // 299| 10| // However, if the queueSize gets too big, we want to block. 300| 10| // But we have very strict requirements as to when we block, and how long we block. 301| 10| // 302| 10| // The following example should help illustrate our requirements: 303| 10| // 304| 10| // Imagine that the maximum queue size is configured to be 5, 305| 10| // and that there are already 5 log messages queued. 306| 10| // Let us call these 5 queued log messages A, B, C, D, and E. (A is next to be executed) 307| 10| // 308| 10| // Now if our thread issues a log statement (let us call the log message F), 309| 10| // it should block before the message is added to the queue. 310| 10| // Furthermore, it should be unblocked immediately after A has been unqueued. 311| 10| // 312| 10| // The requirements are strict in this manner so that we block only as long as necessary, 313| 10| // and so that blocked threads are unblocked in the order in which they were blocked. 314| 10| // 315| 10| // Returning to our previous example, let us assume that log messages A through E are still queued. 316| 10| // Our aforementioned thread is blocked attempting to queue log message F. 317| 10| // Now assume we have another separate thread that attempts to issue log message G. 318| 10| // It should block until log messages A and B have been unqueued. 319| 10| 320| 10| dispatch_block_t logBlock = ^{ 321| 10| // We're now sure we won't overflow the queue. 322| 10| // It is time to queue our log message. 323| 10| @autoreleasepool { 324| 10| [self lt_log:logMessage]; 325| 10| } 326| 10| }; 327| 10| 328| 10| if (asyncFlag) { 329| 8| dispatch_async(_loggingQueue, logBlock); 330| 8| } else if (dispatch_get_specific(GlobalLoggingQueueIdentityKey)) { 331| 0| // We've logged an error message while on the logging queue... 332| 0| logBlock(); 333| 2| } else { 334| 2| dispatch_sync(_loggingQueue, logBlock); 335| 2| } 336| 10|} 337| | 338| |+ (void)log:(BOOL)asynchronous 339| | level:(DDLogLevel)level 340| | flag:(DDLogFlag)flag 341| | context:(NSInteger)context 342| | file:(const char *)file 343| | function:(const char *)function 344| | line:(NSUInteger)line 345| | tag:(id)tag 346| 0| format:(NSString *)format, ... { 347| 0| va_list args; 348| 0| 349| 0| if (format) { 350| 0| va_start(args, format); 351| 0| 352| 0| NSString *message = [[NSString alloc] initWithFormat:format arguments:args]; 353| 0| 354| 0| va_end(args); 355| 0| 356| 0| va_start(args, format); 357| 0| 358| 0| [self log:asynchronous 359| 0| message:message 360| 0| level:level 361| 0| flag:flag 362| 0| context:context 363| 0| file:file 364| 0| function:function 365| 0| line:line 366| 0| tag:tag]; 367| 0| 368| 0| va_end(args); 369| 0| } 370| 0|} 371| | 372| |- (void)log:(BOOL)asynchronous 373| | level:(DDLogLevel)level 374| | flag:(DDLogFlag)flag 375| | context:(NSInteger)context 376| | file:(const char *)file 377| | function:(const char *)function 378| | line:(NSUInteger)line 379| | tag:(id)tag 380| 0| format:(NSString *)format, ... { 381| 0| va_list args; 382| 0| 383| 0| if (format) { 384| 0| va_start(args, format); 385| 0| 386| 0| NSString *message = [[NSString alloc] initWithFormat:format arguments:args]; 387| 0| 388| 0| va_end(args); 389| 0| 390| 0| va_start(args, format); 391| 0| 392| 0| [self log:asynchronous 393| 0| message:message 394| 0| level:level 395| 0| flag:flag 396| 0| context:context 397| 0| file:file 398| 0| function:function 399| 0| line:line 400| 0| tag:tag]; 401| 0| 402| 0| va_end(args); 403| 0| } 404| 0|} 405| | 406| |+ (void)log:(BOOL)asynchronous 407| | level:(DDLogLevel)level 408| | flag:(DDLogFlag)flag 409| | context:(NSInteger)context 410| | file:(const char *)file 411| | function:(const char *)function 412| | line:(NSUInteger)line 413| | tag:(id)tag 414| | format:(NSString *)format 415| 0| args:(va_list)args { 416| 0| [self.sharedInstance log:asynchronous level:level flag:flag context:context file:file function:function line:line tag:tag format:format args:args]; 417| 0|} 418| | 419| |- (void)log:(BOOL)asynchronous 420| | level:(DDLogLevel)level 421| | flag:(DDLogFlag)flag 422| | context:(NSInteger)context 423| | file:(const char *)file 424| | function:(const char *)function 425| | line:(NSUInteger)line 426| | tag:(id)tag 427| | format:(NSString *)format 428| 0| args:(va_list)args { 429| 0| if (format) { 430| 0| NSString *message = [[NSString alloc] initWithFormat:format arguments:args]; 431| 0| [self log:asynchronous 432| 0| message:message 433| 0| level:level 434| 0| flag:flag 435| 0| context:context 436| 0| file:file 437| 0| function:function 438| 0| line:line 439| 0| tag:tag]; 440| 0| } 441| 0|} 442| | 443| |+ (void)log:(BOOL)asynchronous 444| | message:(NSString *)message 445| | level:(DDLogLevel)level 446| | flag:(DDLogFlag)flag 447| | context:(NSInteger)context 448| | file:(const char *)file 449| | function:(const char *)function 450| | line:(NSUInteger)line 451| 0| tag:(id)tag { 452| 0| [self.sharedInstance log:asynchronous message:message level:level flag:flag context:context file:file function:function line:line tag:tag]; 453| 0|} 454| | 455| |- (void)log:(BOOL)asynchronous 456| | message:(NSString *)message 457| | level:(DDLogLevel)level 458| | flag:(DDLogFlag)flag 459| | context:(NSInteger)context 460| | file:(const char *)file 461| | function:(const char *)function 462| | line:(NSUInteger)line 463| 0| tag:(id)tag { 464| 0| DDLogMessage *logMessage = [[DDLogMessage alloc] initWithMessage:message 465| 0| level:level 466| 0| flag:flag 467| 0| context:context 468| 0| file:[NSString stringWithFormat:@"%s", file] 469| 0| function:[NSString stringWithFormat:@"%s", function] 470| 0| line:line 471| 0| tag:tag 472| 0| options:(DDLogMessageOptions)0 473| 0| timestamp:nil]; 474| 0| 475| 0| [self queueLogMessage:logMessage asynchronously:asynchronous]; 476| 0|} 477| | 478| 0|+ (void)log:(BOOL)asynchronous message:(DDLogMessage *)logMessage { 479| 0| [self.sharedInstance log:asynchronous message:logMessage]; 480| 0|} 481| | 482| 10|- (void)log:(BOOL)asynchronous message:(DDLogMessage *)logMessage { 483| 10| [self queueLogMessage:logMessage asynchronously:asynchronous]; 484| 10|} 485| | 486| 2|+ (void)flushLog { 487| 2| [self.sharedInstance flushLog]; 488| 2|} 489| | 490| 2|- (void)flushLog { 491| 2| NSAssert(!dispatch_get_specific(GlobalLoggingQueueIdentityKey), 492| 2| @"This method shouldn't be run on the logging thread/queue that make flush fast enough"); 493| 2| 494| 2| dispatch_sync(_loggingQueue, ^{ @autoreleasepool { 495| 2| [self lt_flush]; 496| 2| } }); 497| 2|} 498| | 499| |//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 500| |#pragma mark Registered Dynamic Logging 501| |//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 502| | 503| 0|+ (BOOL)isRegisteredClass:(Class)class { 504| 0| SEL getterSel = @selector(ddLogLevel); 505| 0| SEL setterSel = @selector(ddSetLogLevel:); 506| 0| 507| 0|#if TARGET_OS_IPHONE && !TARGET_OS_SIMULATOR 508| 0| 509| 0| // Issue #6 (GoogleCode) - Crashes on iOS 4.2.1 and iPhone 4 510| 0| // 511| 0| // Crash caused by class_getClassMethod(2). 512| 0| // 513| 0| // "It's a bug with UIAccessibilitySafeCategory__NSObject so it didn't pop up until 514| 0| // users had VoiceOver enabled [...]. I was able to work around it by searching the 515| 0| // result of class_copyMethodList() instead of calling class_getClassMethod()" 516| 0| 517| 0| BOOL result = NO; 518| 0| 519| 0| unsigned int methodCount, i; 520| 0| Method *methodList = class_copyMethodList(object_getClass(class), &methodCount); 521| 0| 522| 0| if (methodList != NULL) { 523| 0| BOOL getterFound = NO; 524| 0| BOOL setterFound = NO; 525| 0| 526| 0| for (i = 0; i < methodCount; ++i) { 527| 0| SEL currentSel = method_getName(methodList[i]); 528| 0| 529| 0| if (currentSel == getterSel) { 530| 0| getterFound = YES; 531| 0| } else if (currentSel == setterSel) { 532| 0| setterFound = YES; 533| 0| } 534| 0| 535| 0| if (getterFound && setterFound) { 536| 0| result = YES; 537| 0| break; 538| 0| } 539| 0| } 540| 0| 541| 0| free(methodList); 542| 0| } 543| 0| 544| 0| return result; 545| 0| 546| |#else /* if TARGET_OS_IPHONE && !TARGET_OS_SIMULATOR */ 547| | 548| | // Issue #24 (GitHub) - Crashing in in ARC+Simulator 549| | // 550| | // The method +[DDLog isRegisteredClass] will crash a project when using it with ARC + Simulator. 551| | // For running in the Simulator, it needs to execute the non-iOS code. 552| | 553| | Method getter = class_getClassMethod(class, getterSel); 554| | Method setter = class_getClassMethod(class, setterSel); 555| | 556| | if ((getter != NULL) && (setter != NULL)) { 557| | return YES; 558| | } 559| | 560| | return NO; 561| | 562| |#endif /* if TARGET_OS_IPHONE && !TARGET_OS_SIMULATOR */ 563| |} 564| | 565| 0|+ (NSArray *)registeredClasses { 566| 0| 567| 0| // We're going to get the list of all registered classes. 568| 0| // The Objective-C runtime library automatically registers all the classes defined in your source code. 569| 0| // 570| 0| // To do this we use the following method (documented in the Objective-C Runtime Reference): 571| 0| // 572| 0| // int objc_getClassList(Class *buffer, int bufferLen) 573| 0| // 574| 0| // We can pass (NULL, 0) to obtain the total number of 575| 0| // registered class definitions without actually retrieving any class definitions. 576| 0| // This allows us to allocate the minimum amount of memory needed for the application. 577| 0| 578| 0| NSUInteger numClasses = 0; 579| 0| Class *classes = NULL; 580| 0| 581| 0| while (numClasses == 0) { 582| 0| 583| 0| numClasses = (NSUInteger)MAX(objc_getClassList(NULL, 0), 0); 584| 0| 585| 0| // numClasses now tells us how many classes we have (but it might change) 586| 0| // So we can allocate our buffer, and get pointers to all the class definitions. 587| 0| 588| 0| NSUInteger bufferSize = numClasses; 589| 0| 590| 0| classes = numClasses ? (Class *)calloc(bufferSize, sizeof(Class)) : NULL; 591| 0| if (classes == NULL) { 592| 0| return @[]; //no memory or classes? 593| 0| } 594| 0| 595| 0| numClasses = (NSUInteger)MAX(objc_getClassList(classes, (int)bufferSize),0); 596| 0| 597| 0| if (numClasses > bufferSize || numClasses == 0) { 598| 0| //apparently more classes added between calls (or a problem); try again 599| 0| free(classes); 600| 0| classes = NULL; 601| 0| numClasses = 0; 602| 0| } 603| 0| } 604| 0| 605| 0| // We can now loop through the classes, and test each one to see if it is a DDLogging class. 606| 0| 607| 0| NSMutableArray *result = [NSMutableArray arrayWithCapacity:numClasses]; 608| 0| 609| 0| for (NSUInteger i = 0; i < numClasses; i++) { 610| 0| Class class = classes[i]; 611| 0| 612| 0| if ([self isRegisteredClass:class]) { 613| 0| [result addObject:class]; 614| 0| } 615| 0| } 616| 0| 617| 0| free(classes); 618| 0| 619| 0| return result; 620| 0|} 621| | 622| 0|+ (NSArray *)registeredClassNames { 623| 0| NSArray *registeredClasses = [self registeredClasses]; 624| 0| NSMutableArray *result = [NSMutableArray arrayWithCapacity:[registeredClasses count]]; 625| 0| 626| 0| for (Class class in registeredClasses) { 627| 0| [result addObject:NSStringFromClass(class)]; 628| 0| } 629| 0| return result; 630| 0|} 631| | 632| 0|+ (DDLogLevel)levelForClass:(Class)aClass { 633| 0| if ([self isRegisteredClass:aClass]) { 634| 0| return [aClass ddLogLevel]; 635| 0| } 636| 0| return (DDLogLevel)-1; 637| 0|} 638| | 639| 0|+ (DDLogLevel)levelForClassWithName:(NSString *)aClassName { 640| 0| Class aClass = NSClassFromString(aClassName); 641| 0| 642| 0| return [self levelForClass:aClass]; 643| 0|} 644| | 645| 0|+ (void)setLevel:(DDLogLevel)level forClass:(Class)aClass { 646| 0| if ([self isRegisteredClass:aClass]) { 647| 0| [aClass ddSetLogLevel:level]; 648| 0| } 649| 0|} 650| | 651| 0|+ (void)setLevel:(DDLogLevel)level forClassWithName:(NSString *)aClassName { 652| 0| Class aClass = NSClassFromString(aClassName); 653| 0| [self setLevel:level forClass:aClass]; 654| 0|} 655| | 656| |//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 657| |#pragma mark Logging Thread 658| |//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 659| | 660| 5|- (void)lt_addLogger:(id )logger level:(DDLogLevel)level { 661| 5| // Add to loggers array. 662| 5| // Need to create loggerQueue if loggerNode doesn't provide one. 663| 5| 664| 5| for (DDLoggerNode *node in self._loggers) { 665| 0| if (node->_logger == logger 666| 0| && node->_level == level) { 667| 0| // Exactly same logger already added, exit 668| 0| return; 669| 0| } 670| 0| } 671| 5| 672| 5| NSAssert(dispatch_get_specific(GlobalLoggingQueueIdentityKey), 673| 5| @"This method should only be run on the logging thread/queue"); 674| 5| 675| 5| dispatch_queue_t loggerQueue = NULL; 676| 5| if ([logger respondsToSelector:@selector(loggerQueue)]) { 677| 0| // Logger may be providing its own queue 678| 0| loggerQueue = logger.loggerQueue; 679| 0| } 680| 5| 681| 5| if (loggerQueue == nil) { 682| 5| // Automatically create queue for the logger. 683| 5| // Use the logger name as the queue name if possible. 684| 5| const char *loggerQueueName = NULL; 685| 5| 686| 5| if ([logger respondsToSelector:@selector(loggerName)]) { 687| 0| loggerQueueName = logger.loggerName.UTF8String; 688| 0| } 689| 5| 690| 5| loggerQueue = dispatch_queue_create(loggerQueueName, NULL); 691| 5| } 692| 5| 693| 5| DDLoggerNode *loggerNode = [DDLoggerNode nodeWithLogger:logger loggerQueue:loggerQueue level:level]; 694| 5| [self._loggers addObject:loggerNode]; 695| 5| 696| 5| if ([logger respondsToSelector:@selector(didAddLoggerInQueue:)]) { 697| 0| dispatch_async(loggerNode->_loggerQueue, ^{ @autoreleasepool { 698| 0| [logger didAddLoggerInQueue:loggerNode->_loggerQueue]; 699| 0| } }); 700| 5| } else if ([logger respondsToSelector:@selector(didAddLogger)]) { 701| 0| dispatch_async(loggerNode->_loggerQueue, ^{ @autoreleasepool { 702| 0| [logger didAddLogger]; 703| 0| } }); 704| 0| } 705| 5|} 706| | 707| 5|- (void)lt_removeLogger:(id )logger { 708| 5| // Find associated loggerNode in list of added loggers 709| 5| 710| 5| NSAssert(dispatch_get_specific(GlobalLoggingQueueIdentityKey), 711| 5| @"This method should only be run on the logging thread/queue"); 712| 5| 713| 5| DDLoggerNode *loggerNode = nil; 714| 5| 715| 5| for (DDLoggerNode *node in self._loggers) { 716| 1| if (node->_logger == logger) { 717| 1| loggerNode = node; 718| 1| break; 719| 1| } 720| 1| } 721| 5| 722| 5| if (loggerNode == nil) { 723| 4| NSLogDebug(@"DDLog: Request to remove logger which wasn't added"); 724| 4| return; 725| 4| } 726| 1| 727| 1| // Notify logger 728| 1| if ([logger respondsToSelector:@selector(willRemoveLogger)]) { 729| 0| dispatch_async(loggerNode->_loggerQueue, ^{ @autoreleasepool { 730| 0| [logger willRemoveLogger]; 731| 0| } }); 732| 0| } 733| 1| 734| 1| // Remove from loggers array 735| 1| [self._loggers removeObject:loggerNode]; 736| 1|} 737| | 738| 14|- (void)lt_removeAllLoggers { 739| 14| NSAssert(dispatch_get_specific(GlobalLoggingQueueIdentityKey), 740| 14| @"This method should only be run on the logging thread/queue"); 741| 14| 742| 14| // Notify all loggers 743| 14| for (DDLoggerNode *loggerNode in self._loggers) { 744| 4| if ([loggerNode->_logger respondsToSelector:@selector(willRemoveLogger)]) { 745| 0| dispatch_async(loggerNode->_loggerQueue, ^{ @autoreleasepool { 746| 0| [loggerNode->_logger willRemoveLogger]; 747| 0| } }); 748| 0| } 749| 4| } 750| 14| 751| 14| // Remove all loggers from array 752| 14| 753| 14| [self._loggers removeAllObjects]; 754| 14|} 755| | 756| 4|- (NSArray *)lt_allLoggers { 757| 4| NSAssert(dispatch_get_specific(GlobalLoggingQueueIdentityKey), 758| 4| @"This method should only be run on the logging thread/queue"); 759| 4| 760| 4| NSMutableArray *theLoggers = [NSMutableArray new]; 761| 4| 762| 4| for (DDLoggerNode *loggerNode in self._loggers) { 763| 3| [theLoggers addObject:loggerNode->_logger]; 764| 3| } 765| 4| 766| 4| return [theLoggers copy]; 767| 4|} 768| | 769| 7|- (NSArray *)lt_allLoggersWithLevel { 770| 7| NSAssert(dispatch_get_specific(GlobalLoggingQueueIdentityKey), 771| 7| @"This method should only be run on the logging thread/queue"); 772| 7| 773| 7| NSMutableArray *theLoggersWithLevel = [NSMutableArray new]; 774| 7| 775| 7| for (DDLoggerNode *loggerNode in self._loggers) { 776| 6| [theLoggersWithLevel addObject:[DDLoggerInformation informationWithLogger:loggerNode->_logger 777| 6| andLevel:loggerNode->_level]]; 778| 6| } 779| 7| 780| 7| return [theLoggersWithLevel copy]; 781| 7|} 782| | 783| 10|- (void)lt_log:(DDLogMessage *)logMessage { 784| 10| // Execute the given log message on each of our loggers. 785| 10| 786| 10| NSAssert(dispatch_get_specific(GlobalLoggingQueueIdentityKey), 787| 10| @"This method should only be run on the logging thread/queue"); 788| 10| 789| 10| if (_numProcessors > 1) { 790| 10| // Execute each logger concurrently, each within its own queue. 791| 10| // All blocks are added to same group. 792| 10| // After each block has been queued, wait on group. 793| 10| // 794| 10| // The waiting ensures that a slow logger doesn't end up with a large queue of pending log messages. 795| 10| // This would defeat the purpose of the efforts we made earlier to restrict the max queue size. 796| 10| 797| 10| for (DDLoggerNode *loggerNode in self._loggers) { 798| 10| // skip the loggers that shouldn't write this message based on the log level 799| 10| 800| 10| if (!(logMessage->_flag & loggerNode->_level)) { 801| 3| continue; 802| 3| } 803| 7| 804| 7| dispatch_group_async(_loggingGroup, loggerNode->_loggerQueue, ^{ @autoreleasepool { 805| 7| [loggerNode->_logger logMessage:logMessage]; 806| 7| } }); 807| 7| } 808| 10| 809| 10| dispatch_group_wait(_loggingGroup, DISPATCH_TIME_FOREVER); 810| 10| } else { 811| 0| // Execute each logger serially, each within its own queue. 812| 0| 813| 0| for (DDLoggerNode *loggerNode in self._loggers) { 814| 0| // skip the loggers that shouldn't write this message based on the log level 815| 0| 816| 0| if (!(logMessage->_flag & loggerNode->_level)) { 817| 0| continue; 818| 0| } 819| 0| 820| |#if DD_DEBUG 821| | // we must assure that we aren not on loggerNode->_loggerQueue. 822| | if (loggerNode->_loggerQueue == NULL) { 823| | // tell that we can't dispatch logger node on queue that is NULL. 824| | NSLogDebug(@"DDLog: current node has loggerQueue == NULL"); 825| | } 826| | else { 827| | dispatch_async(loggerNode->_loggerQueue, ^{ 828| | if (dispatch_get_specific(GlobalLoggingQueueIdentityKey)) { 829| | // tell that we somehow on logging queue? 830| | NSLogDebug(@"DDLog: current node has loggerQueue == globalLoggingQueue"); 831| | } 832| | }); 833| | } 834| |#endif 835| | // next, we must check that node is OK. 836| 0| dispatch_sync(loggerNode->_loggerQueue, ^{ @autoreleasepool { 837| 0| [loggerNode->_logger logMessage:logMessage]; 838| 0| } }); 839| 0| } 840| 0| } 841| 10|} 842| | 843| 2|- (void)lt_flush { 844| 2| // All log statements issued before the flush method was invoked have now been executed. 845| 2| // 846| 2| // Now we need to propagate the flush request to any loggers that implement the flush method. 847| 2| // This is designed for loggers that buffer IO. 848| 2| 849| 2| NSAssert(dispatch_get_specific(GlobalLoggingQueueIdentityKey), 850| 2| @"This method should only be run on the logging thread/queue"); 851| 2| 852| 2| for (DDLoggerNode *loggerNode in self._loggers) { 853| 2| if ([loggerNode->_logger respondsToSelector:@selector(flush)]) { 854| 0| dispatch_group_async(_loggingGroup, loggerNode->_loggerQueue, ^{ @autoreleasepool { 855| 0| [loggerNode->_logger flush]; 856| 0| } }); 857| 0| } 858| 2| } 859| 2| 860| 2| dispatch_group_wait(_loggingGroup, DISPATCH_TIME_FOREVER); 861| 2|} 862| | 863| |//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 864| |#pragma mark Utilities 865| |//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 866| | 867| 0|NSString * __nullable DDExtractFileNameWithoutExtension(const char *filePath, BOOL copy) { 868| 0| if (filePath == NULL) { 869| 0| return nil; 870| 0| } 871| 0| 872| 0| char *lastSlash = NULL; 873| 0| char *lastDot = NULL; 874| 0| 875| 0| char *p = (char *)filePath; 876| 0| 877| 0| while (*p != '\0') { 878| 0| if (*p == '/') { 879| 0| lastSlash = p; 880| 0| } else if (*p == '.') { 881| 0| lastDot = p; 882| 0| } 883| 0| 884| 0| p++; 885| 0| } 886| 0| 887| 0| char *subStr; 888| 0| NSUInteger subLen; 889| 0| 890| 0| if (lastSlash) { 891| 0| if (lastDot) { 892| 0| // lastSlash -> lastDot 893| 0| subStr = lastSlash + 1; 894| 0| subLen = (NSUInteger)(lastDot - subStr); 895| 0| } else { 896| 0| // lastSlash -> endOfString 897| 0| subStr = lastSlash + 1; 898| 0| subLen = (NSUInteger)(p - subStr); 899| 0| } 900| 0| } else { 901| 0| if (lastDot) { 902| 0| // startOfString -> lastDot 903| 0| subStr = (char *)filePath; 904| 0| subLen = (NSUInteger)(lastDot - subStr); 905| 0| } else { 906| 0| // startOfString -> endOfString 907| 0| subStr = (char *)filePath; 908| 0| subLen = (NSUInteger)(p - subStr); 909| 0| } 910| 0| } 911| 0| 912| 0| if (copy) { 913| 0| return [[NSString alloc] initWithBytes:subStr 914| 0| length:subLen 915| 0| encoding:NSUTF8StringEncoding]; 916| 0| } else { 917| 0| // We can take advantage of the fact that __FILE__ is a string literal. 918| 0| // Specifically, we don't need to waste time copying the string. 919| 0| // We can just tell NSString to point to a range within the string literal. 920| 0| 921| 0| return [[NSString alloc] initWithBytesNoCopy:subStr 922| 0| length:subLen 923| 0| encoding:NSUTF8StringEncoding 924| 0| freeWhenDone:NO]; 925| 0| } 926| 0|} 927| | 928| |@end 929| | 930| |//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 931| |#pragma mark - 932| |//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 933| | 934| |@implementation DDLoggerNode 935| | 936| 5|- (instancetype)initWithLogger:(id )logger loggerQueue:(dispatch_queue_t)loggerQueue level:(DDLogLevel)level { 937| 5| if ((self = [super init])) { 938| 5| _logger = logger; 939| 5| 940| 5| if (loggerQueue) { 941| 5| _loggerQueue = loggerQueue; 942| | #if !OS_OBJECT_USE_OBJC 943| | dispatch_retain(loggerQueue); 944| | #endif 945| | } 946| 5| 947| 5| _level = level; 948| 5| } 949| 5| return self; 950| 5|} 951| | 952| 5|+ (instancetype)nodeWithLogger:(id )logger loggerQueue:(dispatch_queue_t)loggerQueue level:(DDLogLevel)level { 953| 5| return [[self alloc] initWithLogger:logger loggerQueue:loggerQueue level:level]; 954| 5|} 955| | 956| 5|- (void)dealloc { 957| | #if !OS_OBJECT_USE_OBJC 958| | if (_loggerQueue) { 959| | dispatch_release(_loggerQueue); 960| | } 961| | #endif 962| |} 963| | 964| |@end 965| | 966| |//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 967| |#pragma mark - 968| |//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 969| | 970| |@implementation DDLogMessage 971| | 972| 0|- (instancetype)init { 973| 0| self = [super init]; 974| 0| return self; 975| 0|} 976| | 977| |- (instancetype)initWithMessage:(NSString *)message 978| | level:(DDLogLevel)level 979| | flag:(DDLogFlag)flag 980| | context:(NSInteger)context 981| | file:(NSString *)file 982| | function:(NSString *)function 983| | line:(NSUInteger)line 984| | tag:(id)tag 985| | options:(DDLogMessageOptions)options 986| 12| timestamp:(NSDate *)timestamp { 987| 12| if ((self = [super init])) { 988| 12| BOOL copyMessage = (options & DDLogMessageDontCopyMessage) == 0; 989| 12| _message = copyMessage ? [message copy] : message; 990| 12| _level = level; 991| 12| _flag = flag; 992| 12| _context = context; 993| 12| 994| 12| BOOL copyFile = (options & DDLogMessageCopyFile) != 0; 995| 12| _file = copyFile ? [file copy] : file; 996| 12| 997| 12| BOOL copyFunction = (options & DDLogMessageCopyFunction) != 0; 998| 12| _function = copyFunction ? [function copy] : function; 999| 12| 1000| 12| _line = line; 1001| 12| _representedObject = tag; 1002| 12|#if DD_LEGACY_MESSAGE_TAG 1003| 12|#pragma clang diagnostic push 1004| 12|#pragma clang diagnostic ignored "-Wdeprecated-declarations" 1005| 12| _tag = tag; 1006| 12|#pragma clang diagnostic pop 1007| 12|#endif 1008| 12| _options = options; 1009| 12| _timestamp = timestamp ?: [NSDate new]; 1010| 12| 1011| 12| __uint64_t tid; 1012| 12| if (pthread_threadid_np(NULL, &tid) == 0) { 1013| 12| _threadID = [[NSString alloc] initWithFormat:@"%llu", tid]; 1014| 12| } else { 1015| 0| _threadID = @"missing threadId"; 1016| 0| } 1017| 12| _threadName = NSThread.currentThread.name; 1018| 12| 1019| 12| // Get the file name without extension 1020| 12| _fileName = [_file lastPathComponent]; 1021| 12| NSUInteger dotLocation = [_fileName rangeOfString:@"." options:NSBackwardsSearch].location; 1022| 12| if (dotLocation != NSNotFound) 1023| 12| { 1024| 12| _fileName = [_fileName substringToIndex:dotLocation]; 1025| 12| } 1026| 12| 1027| 12| // Try to get the current queue's label 1028| 12| _queueLabel = [[NSString alloc] initWithFormat:@"%s", dispatch_queue_get_label(DISPATCH_CURRENT_QUEUE_LABEL)]; 1029| 12| _qos = (NSUInteger) qos_class_self(); 1030| 12| } 1031| 12| return self; 1032| 12|} 1033| | 1034| 0|- (BOOL)isEqual:(id)other { 1035| 0| if (other == self) { 1036| 0| return YES; 1037| 0| } else if (![super isEqual:other] || ![other isKindOfClass:[self class]]) { 1038| 0| return NO; 1039| 0| } else { 1040| 0| __auto_type otherMsg = (DDLogMessage *)other; 1041| 0| return [otherMsg->_message isEqualToString:_message] 1042| 0| && otherMsg->_level == _level 1043| 0| && otherMsg->_flag == _flag 1044| 0| && otherMsg->_context == _context 1045| 0| && [otherMsg->_file isEqualToString:_file] 1046| 0| && [otherMsg->_fileName isEqualToString:_fileName] 1047| 0| && [otherMsg->_function isEqualToString:_function] 1048| 0| && otherMsg->_line == _line 1049| 0| && (([otherMsg->_representedObject respondsToSelector:@selector(isEqual:)] && [otherMsg->_representedObject isEqual:_representedObject]) || otherMsg->_representedObject == _representedObject) 1050| 0| && otherMsg->_options == _options 1051| 0| && [otherMsg->_timestamp isEqualToDate:_timestamp] 1052| 0| && [otherMsg->_threadID isEqualToString:_threadID] // If the thread ID is the same, the name will likely be the same as well. 1053| 0| && [otherMsg->_queueLabel isEqualToString:_queueLabel] 1054| 0| && otherMsg->_qos == _qos; 1055| 0| } 1056| 0|} 1057| | 1058| 0|- (NSUInteger)hash { 1059| 0| return [super hash] 1060| 0| ^ _message.hash 1061| 0| ^ _level 1062| 0| ^ _flag 1063| 0| ^ _context 1064| 0| ^ _file.hash 1065| 0| ^ _fileName.hash 1066| 0| ^ _function.hash 1067| 0| ^ _line 1068| 0| ^ ([_representedObject respondsToSelector:@selector(hash)] ? [_representedObject hash] : 0) 1069| 0| ^ _options 1070| 0| ^ _timestamp.hash 1071| 0| ^ _threadID.hash 1072| 0| ^ _queueLabel.hash 1073| 0| ^ _qos; 1074| 0|} 1075| | 1076| 0|- (id)copyWithZone:(NSZone * __attribute__((unused)))zone { 1077| 0| DDLogMessage *newMessage = [DDLogMessage new]; 1078| 0| 1079| 0| newMessage->_message = _message; 1080| 0| newMessage->_level = _level; 1081| 0| newMessage->_flag = _flag; 1082| 0| newMessage->_context = _context; 1083| 0| newMessage->_file = _file; 1084| 0| newMessage->_fileName = _fileName; 1085| 0| newMessage->_function = _function; 1086| 0| newMessage->_line = _line; 1087| 0| newMessage->_representedObject = _representedObject; 1088| 0|#if DD_LEGACY_MESSAGE_TAG 1089| 0|#pragma clang diagnostic push 1090| 0|#pragma clang diagnostic ignored "-Wdeprecated-declarations" 1091| 0| newMessage->_tag = _tag; 1092| 0|#pragma clang diagnostic pop 1093| 0|#endif 1094| 0| newMessage->_options = _options; 1095| 0| newMessage->_timestamp = _timestamp; 1096| 0| newMessage->_threadID = _threadID; 1097| 0| newMessage->_threadName = _threadName; 1098| 0| newMessage->_queueLabel = _queueLabel; 1099| 0| newMessage->_qos = _qos; 1100| 0| 1101| 0| return newMessage; 1102| 0|} 1103| | 1104| |// ensure compatibility even when built with DD_LEGACY_MESSAGE_TAG to 0. 1105| 0|- (id)tag { 1106| 0| return _representedObject; 1107| 0|} 1108| | 1109| |@end 1110| | 1111| | 1112| |//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 1113| |#pragma mark - 1114| |//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 1115| | 1116| |@implementation DDAbstractLogger 1117| | 1118| 0|- (instancetype)init { 1119| 0| if ((self = [super init])) { 1120| 0| const char *loggerQueueName = NULL; 1121| 0| 1122| 0| if ([self respondsToSelector:@selector(loggerName)]) { 1123| 0| loggerQueueName = self.loggerName.UTF8String; 1124| 0| } 1125| 0| 1126| 0| _loggerQueue = dispatch_queue_create(loggerQueueName, NULL); 1127| 0| 1128| 0| // We're going to use dispatch_queue_set_specific() to "mark" our loggerQueue. 1129| 0| // Later we can use dispatch_get_specific() to determine if we're executing on our loggerQueue. 1130| 0| // The documentation states: 1131| 0| // 1132| 0| // > Keys are only compared as pointers and are never dereferenced. 1133| 0| // > Thus, you can use a pointer to a static variable for a specific subsystem or 1134| 0| // > any other value that allows you to identify the value uniquely. 1135| 0| // > Specifying a pointer to a string constant is not recommended. 1136| 0| // 1137| 0| // So we're going to use the very convenient key of "self", 1138| 0| // which also works when multiple logger classes extend this class, as each will have a different "self" key. 1139| 0| // 1140| 0| // This is used primarily for thread-safety assertions (via the isOnInternalLoggerQueue method below). 1141| 0| 1142| 0| void *key = (__bridge void *)self; 1143| 0| void *nonNullValue = (__bridge void *)self; 1144| 0| 1145| 0| dispatch_queue_set_specific(_loggerQueue, key, nonNullValue, NULL); 1146| 0| } 1147| 0| 1148| 0| return self; 1149| 0|} 1150| | 1151| 0|- (void)dealloc { 1152| | #if !OS_OBJECT_USE_OBJC 1153| | 1154| | if (_loggerQueue) { 1155| | dispatch_release(_loggerQueue); 1156| | } 1157| | 1158| | #endif 1159| |} 1160| | 1161| 0|- (void)logMessage:(DDLogMessage * __attribute__((unused)))logMessage { 1162| 0| // Override me 1163| 0|} 1164| | 1165| 0|- (id )logFormatter { 1166| 0| // This method must be thread safe and intuitive. 1167| 0| // Therefore if somebody executes the following code: 1168| 0| // 1169| 0| // [logger setLogFormatter:myFormatter]; 1170| 0| // formatter = [logger logFormatter]; 1171| 0| // 1172| 0| // They would expect formatter to equal myFormatter. 1173| 0| // This functionality must be ensured by the getter and setter method. 1174| 0| // 1175| 0| // The thread safety must not come at a cost to the performance of the logMessage method. 1176| 0| // This method is likely called sporadically, while the logMessage method is called repeatedly. 1177| 0| // This means, the implementation of this method: 1178| 0| // - Must NOT require the logMessage method to acquire a lock. 1179| 0| // - Must NOT require the logMessage method to access an atomic property (also a lock of sorts). 1180| 0| // 1181| 0| // Thread safety is ensured by executing access to the formatter variable on the loggerQueue. 1182| 0| // This is the same queue that the logMessage method operates on. 1183| 0| // 1184| 0| // Note: The last time I benchmarked the performance of direct access vs atomic property access, 1185| 0| // direct access was over twice as fast on the desktop and over 6 times as fast on the iPhone. 1186| 0| // 1187| 0| // Furthermore, consider the following code: 1188| 0| // 1189| 0| // DDLogVerbose(@"log msg 1"); 1190| 0| // DDLogVerbose(@"log msg 2"); 1191| 0| // [logger setFormatter:myFormatter]; 1192| 0| // DDLogVerbose(@"log msg 3"); 1193| 0| // 1194| 0| // Our intuitive requirement means that the new formatter will only apply to the 3rd log message. 1195| 0| // This must remain true even when using asynchronous logging. 1196| 0| // We must keep in mind the various queue's that are in play here: 1197| 0| // 1198| 0| // loggerQueue : Our own private internal queue that the logMessage method runs on. 1199| 0| // Operations are added to this queue from the global loggingQueue. 1200| 0| // 1201| 0| // globalLoggingQueue : The queue that all log messages go through before they arrive in our loggerQueue. 1202| 0| // 1203| 0| // All log statements go through the serial globalLoggingQueue before they arrive at our loggerQueue. 1204| 0| // Thus this method also goes through the serial globalLoggingQueue to ensure intuitive operation. 1205| 0| 1206| 0| // IMPORTANT NOTE: 1207| 0| // 1208| 0| // Methods within the DDLogger implementation MUST access the formatter ivar directly. 1209| 0| // This method is designed explicitly for external access. 1210| 0| // 1211| 0| // Using "self." syntax to go through this method will cause immediate deadlock. 1212| 0| // This is the intended result. Fix it by accessing the ivar directly. 1213| 0| // Great strides have been take to ensure this is safe to do. Plus it's MUCH faster. 1214| 0| 1215| 0| NSAssert(![self isOnGlobalLoggingQueue], @"Core architecture requirement failure"); 1216| 0| NSAssert(![self isOnInternalLoggerQueue], @"MUST access ivar directly, NOT via self.* syntax."); 1217| 0| 1218| 0| dispatch_queue_t globalLoggingQueue = [DDLog loggingQueue]; 1219| 0| 1220| 0| __block id result; 1221| 0| 1222| 0| dispatch_sync(globalLoggingQueue, ^{ 1223| 0| dispatch_sync(self->_loggerQueue, ^{ 1224| 0| result = self->_logFormatter; 1225| 0| }); 1226| 0| }); 1227| 0| 1228| 0| return result; 1229| 0|} 1230| | 1231| 0|- (void)setLogFormatter:(id )logFormatter { 1232| 0| // The design of this method is documented extensively in the logFormatter message (above in code). 1233| 0| 1234| 0| NSAssert(![self isOnGlobalLoggingQueue], @"Core architecture requirement failure"); 1235| 0| NSAssert(![self isOnInternalLoggerQueue], @"MUST access ivar directly, NOT via self.* syntax."); 1236| 0| 1237| 0| dispatch_block_t block = ^{ 1238| 0| @autoreleasepool { 1239| 0| if (self->_logFormatter != logFormatter) { 1240| 0| if ([self->_logFormatter respondsToSelector:@selector(willRemoveFromLogger:)]) { 1241| 0| [self->_logFormatter willRemoveFromLogger:self]; 1242| 0| } 1243| 0| 1244| 0| self->_logFormatter = logFormatter; 1245| 0| 1246| 0| if ([self->_logFormatter respondsToSelector:@selector(didAddToLogger:inQueue:)]) { 1247| 0| [self->_logFormatter didAddToLogger:self inQueue:self->_loggerQueue]; 1248| 0| } else if ([self->_logFormatter respondsToSelector:@selector(didAddToLogger:)]) { 1249| 0| [self->_logFormatter didAddToLogger:self]; 1250| 0| } 1251| 0| } 1252| 0| } 1253| 0| }; 1254| 0| 1255| 0| dispatch_async(DDLog.loggingQueue, ^{ 1256| 0| dispatch_async(self->_loggerQueue, block); 1257| 0| }); 1258| 0|} 1259| | 1260| 0|- (dispatch_queue_t)loggerQueue { 1261| 0| return _loggerQueue; 1262| 0|} 1263| | 1264| 0|- (NSString *)loggerName { 1265| 0| return NSStringFromClass([self class]); 1266| 0|} 1267| | 1268| 0|- (BOOL)isOnGlobalLoggingQueue { 1269| 0| return (dispatch_get_specific(GlobalLoggingQueueIdentityKey) != NULL); 1270| 0|} 1271| | 1272| 0|- (BOOL)isOnInternalLoggerQueue { 1273| 0| void *key = (__bridge void *)self; 1274| 0| 1275| 0| return (dispatch_get_specific(key) != NULL); 1276| 0|} 1277| | 1278| |@end 1279| | 1280| |//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 1281| |#pragma mark - 1282| |//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 1283| | 1284| |@interface DDLoggerInformation() 1285| |{ 1286| | // Direct accessors to be used only for performance 1287| | @public 1288| | id _logger; 1289| | DDLogLevel _level; 1290| |} 1291| | 1292| |@end 1293| | 1294| |@implementation DDLoggerInformation 1295| | 1296| 6|- (instancetype)initWithLogger:(id )logger andLevel:(DDLogLevel)level { 1297| 6| if ((self = [super init])) { 1298| 6| _logger = logger; 1299| 6| _level = level; 1300| 6| } 1301| 6| return self; 1302| 6|} 1303| | 1304| 6|+ (instancetype)informationWithLogger:(id )logger andLevel:(DDLogLevel)level { 1305| 6| return [[self alloc] initWithLogger:logger andLevel:level]; 1306| 6|} 1307| | 1308| |@end /Users/runner/work/CocoaLumberjack/CocoaLumberjack/Sources/CocoaLumberjack/DDOSLogger.m: 1| |// Software License Agreement (BSD License) 2| |// 3| |// Copyright (c) 2010-2021, Deusty, LLC 4| |// All rights reserved. 5| |// 6| |// Redistribution and use of this software in source and binary forms, 7| |// with or without modification, are permitted provided that the following conditions are met: 8| |// 9| |// * Redistributions of source code must retain the above copyright notice, 10| |// this list of conditions and the following disclaimer. 11| |// 12| |// * Neither the name of Deusty nor the names of its contributors may be used 13| |// to endorse or promote products derived from this software without specific 14| |// prior written permission of Deusty, LLC. 15| | 16| |#import 17| | 18| |#import 19| | 20| |@interface DDOSLogger () { 21| | NSString *_subsystem; 22| | NSString *_category; 23| |} 24| | 25| |@property (copy, nonatomic, readonly, nullable) NSString *subsystem; 26| |@property (copy, nonatomic, readonly, nullable) NSString *category; 27| |@property (strong, nonatomic, readwrite, nonnull) os_log_t logger; 28| | 29| |@end 30| | 31| |@implementation DDOSLogger 32| | 33| |@synthesize subsystem = _subsystem; 34| |@synthesize category = _category; 35| | 36| |#pragma mark - Initialization 37| | 38| |/** 39| | * Assertion 40| | * Swift: (String, String)? 41| | */ 42| 0|- (instancetype)initWithSubsystem:(NSString *)subsystem category:(NSString *)category { 43| 0| NSAssert((subsystem == nil) == (category == nil), @"Either both subsystem and category or neither should be nil."); 44| 0| if (self = [super init]) { 45| 0| _subsystem = [subsystem copy]; 46| 0| _category = [category copy]; 47| 0| } 48| 0| return self; 49| 0|} 50| | 51| |static DDOSLogger *sharedInstance; 52| | 53| 0|- (instancetype)init { 54| 0| return [self initWithSubsystem:nil category:nil]; 55| 0|} 56| | 57| 0|+ (instancetype)sharedInstance { 58| 0| static dispatch_once_t DDOSLoggerOnceToken; 59| 0| 60| 0| dispatch_once(&DDOSLoggerOnceToken, ^{ 61| 0| sharedInstance = [[[self class] alloc] init]; 62| 0| }); 63| 0| 64| 0| return sharedInstance; 65| 0|} 66| | 67| |#pragma mark - os_log 68| | 69| 0|- (os_log_t)getLogger { 70| 0| if (self.subsystem == nil || self.category == nil) { 71| 0| return OS_LOG_DEFAULT; 72| 0| } 73| 0| return os_log_create(self.subsystem.UTF8String, self.category.UTF8String); 74| 0|} 75| | 76| 0|- (os_log_t)logger { 77| 0| if (_logger == nil) { 78| 0| _logger = [self getLogger]; 79| 0| } 80| 0| return _logger; 81| 0|} 82| | 83| |#pragma mark - DDLogger 84| | 85| 0|- (DDLoggerName)loggerName { 86| 0| return DDLoggerNameOS; 87| 0|} 88| | 89| 0|- (void)logMessage:(DDLogMessage *)logMessage { 90| 0| // Skip captured log messages 91| 0| if ([logMessage->_fileName isEqualToString:@"DDASLLogCapture"]) { 92| 0| return; 93| 0| } 94| 0| 95| 0| if (@available(iOS 10.0, macOS 10.12, tvOS 10.0, watchOS 3.0, *)) { 96| 0| NSString * message = _logFormatter ? [_logFormatter formatLogMessage:logMessage] : logMessage->_message; 97| 0| if (message != nil) { 98| 0| const char *msg = [message UTF8String]; 99| 0| __auto_type logger = [self logger]; 100| 0| switch (logMessage->_flag) { 101| 0| case DDLogFlagError : 102| 0| os_log_error(logger, "%{public}s", msg); 103| 0| break; 104| 0| case DDLogFlagWarning: 105| 0| case DDLogFlagInfo : 106| 0| os_log_info(logger, "%{public}s", msg); 107| 0| break; 108| 0| case DDLogFlagDebug : 109| 0| case DDLogFlagVerbose: 110| 0| default : 111| 0| os_log_debug(logger, "%{public}s", msg); 112| 0| break; 113| 0| } 114| 0| } 115| 0| } 116| 0|} 117| | 118| |@end /Users/runner/work/CocoaLumberjack/CocoaLumberjack/Sources/CocoaLumberjack/DDTTYLogger.m: 1| |// Software License Agreement (BSD License) 2| |// 3| |// Copyright (c) 2010-2021, Deusty, LLC 4| |// All rights reserved. 5| |// 6| |// Redistribution and use of this software in source and binary forms, 7| |// with or without modification, are permitted provided that the following conditions are met: 8| |// 9| |// * Redistributions of source code must retain the above copyright notice, 10| |// this list of conditions and the following disclaimer. 11| |// 12| |// * Neither the name of Deusty nor the names of its contributors may be used 13| |// to endorse or promote products derived from this software without specific 14| |// prior written permission of Deusty, LLC. 15| | 16| |#if !__has_feature(objc_arc) 17| |#error This file must be compiled with ARC. Use -fobjc-arc flag (or convert project to ARC). 18| |#endif 19| | 20| |#import 21| | 22| |#import 23| | 24| |// We probably shouldn't be using DDLog() statements within the DDLog implementation. 25| |// But we still want to leave our log statements for any future debugging, 26| |// and to allow other developers to trace the implementation (which is a great learning tool). 27| |// 28| |// So we use primitive logging macros around NSLog. 29| |// We maintain the NS prefix on the macros to be explicit about the fact that we're using NSLog. 30| | 31| |#ifndef DD_NSLOG_LEVEL 32| 0| #define DD_NSLOG_LEVEL 2 33| |#endif 34| | 35| |#define NSLogError(frmt, ...) do{ if(DD_NSLOG_LEVEL >= 1) NSLog((frmt), ##__VA_ARGS__); } while(0) 36| 0|#define NSLogWarn(frmt, ...) do{ if(DD_NSLOG_LEVEL >= 2) NSLog((frmt), ##__VA_ARGS__); } while(0) 37| 0|#define NSLogInfo(frmt, ...) do{ if(DD_NSLOG_LEVEL >= 3) NSLog((frmt), ##__VA_ARGS__); } while(0) 38| |#define NSLogDebug(frmt, ...) do{ if(DD_NSLOG_LEVEL >= 4) NSLog((frmt), ##__VA_ARGS__); } while(0) 39| 0|#define NSLogVerbose(frmt, ...) do{ if(DD_NSLOG_LEVEL >= 5) NSLog((frmt), ##__VA_ARGS__); } while(0) 40| | 41| |// Xcode does NOT natively support colors in the Xcode debugging console. 42| |// You'll need to install the XcodeColors plugin to see colors in the Xcode console. 43| |// https://github.com/robbiehanson/XcodeColors 44| |// 45| |// The following is documentation from the XcodeColors project: 46| |// 47| |// 48| |// How to apply color formatting to your log statements: 49| |// 50| |// To set the foreground color: 51| |// Insert the ESCAPE_SEQ into your string, followed by "fg124,12,255;" where r=124, g=12, b=255. 52| |// 53| |// To set the background color: 54| |// Insert the ESCAPE_SEQ into your string, followed by "bg12,24,36;" where r=12, g=24, b=36. 55| |// 56| |// To reset the foreground color (to default value): 57| |// Insert the ESCAPE_SEQ into your string, followed by "fg;" 58| |// 59| |// To reset the background color (to default value): 60| |// Insert the ESCAPE_SEQ into your string, followed by "bg;" 61| |// 62| |// To reset the foreground and background color (to default values) in one operation: 63| |// Insert the ESCAPE_SEQ into your string, followed by ";" 64| | 65| 0|#define XCODE_COLORS_ESCAPE_SEQ "\033[" 66| | 67| |#define XCODE_COLORS_RESET_FG XCODE_COLORS_ESCAPE_SEQ "fg;" // Clear any foreground color 68| |#define XCODE_COLORS_RESET_BG XCODE_COLORS_ESCAPE_SEQ "bg;" // Clear any background color 69| |#define XCODE_COLORS_RESET XCODE_COLORS_ESCAPE_SEQ ";" // Clear any foreground or background color 70| | 71| |// If running in a shell, not all RGB colors will be supported. 72| |// In this case we automatically map to the closest available color. 73| |// In order to provide this mapping, we have a hard-coded set of the standard RGB values available in the shell. 74| |// However, not every shell is the same, and Apple likes to think different even when it comes to shell colors. 75| |// 76| |// Map to standard Terminal.app colors (1), or 77| |// map to standard xterm colors (0). 78| | 79| |#define MAP_TO_TERMINAL_APP_COLORS 1 80| | 81| |typedef struct { 82| | uint8_t r; 83| | uint8_t g; 84| | uint8_t b; 85| |} DDRGBColor; 86| | 87| |@interface DDTTYLoggerColorProfile : NSObject { 88| | @public 89| | DDLogFlag mask; 90| | NSInteger context; 91| | 92| | uint8_t fg_r; 93| | uint8_t fg_g; 94| | uint8_t fg_b; 95| | 96| | uint8_t bg_r; 97| | uint8_t bg_g; 98| | uint8_t bg_b; 99| | 100| | NSUInteger fgCodeIndex; 101| | NSString *fgCodeRaw; 102| | 103| | NSUInteger bgCodeIndex; 104| | NSString *bgCodeRaw; 105| | 106| | char fgCode[24]; 107| | size_t fgCodeLen; 108| | 109| | char bgCode[24]; 110| | size_t bgCodeLen; 111| | 112| | char resetCode[8]; 113| | size_t resetCodeLen; 114| |} 115| | 116| |- (nullable instancetype)initWithForegroundColor:(nullable DDColor *)fgColor backgroundColor:(nullable DDColor *)bgColor flag:(DDLogFlag)mask context:(NSInteger)ctxt; 117| | 118| |@end 119| | 120| |//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 121| |#pragma mark - 122| |//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 123| | 124| |@interface DDTTYLogger () { 125| | NSString *_appName; 126| | char *_app; 127| | size_t _appLen; 128| | 129| | NSString *_processID; 130| | char *_pid; 131| | size_t _pidLen; 132| | 133| | BOOL _colorsEnabled; 134| | NSMutableArray *_colorProfilesArray; 135| | NSMutableDictionary *_colorProfilesDict; 136| |} 137| | 138| |@end 139| | 140| | 141| |@implementation DDTTYLogger 142| | 143| |static BOOL isaColorTTY; 144| |static BOOL isaColor256TTY; 145| |static BOOL isaXcodeColorTTY; 146| | 147| |static NSArray *codes_fg = nil; 148| |static NSArray *codes_bg = nil; 149| |static NSArray *colors = nil; 150| | 151| |static DDTTYLogger *sharedInstance; 152| | 153| |/** 154| | * Initializes the colors array, as well as the codes_fg and codes_bg arrays, for 16 color mode. 155| | * 156| | * This method is used when the application is running from within a shell that only supports 16 color mode. 157| | * This method is not invoked if the application is running within Xcode, or via normal UI app launch. 158| | **/ 159| 0|+ (void)initialize_colors_16 { 160| 0| if (codes_fg || codes_bg || colors) { 161| 0| return; 162| 0| } 163| 0| 164| 0| NSMutableArray *m_colors = [NSMutableArray arrayWithCapacity:16]; 165| 0| 166| 0| // In a standard shell only 16 colors are supported. 167| 0| // 168| 0| // More information about ansi escape codes can be found online. 169| 0| // http://en.wikipedia.org/wiki/ANSI_escape_code 170| 0| codes_fg = @[ 171| 0| @"30m", // normal - black 172| 0| @"31m", // normal - red 173| 0| @"32m", // normal - green 174| 0| @"33m", // normal - yellow 175| 0| @"34m", // normal - blue 176| 0| @"35m", // normal - magenta 177| 0| @"36m", // normal - cyan 178| 0| @"37m", // normal - gray 179| 0| @"1;30m", // bright - darkgray 180| 0| @"1;31m", // bright - red 181| 0| @"1;32m", // bright - green 182| 0| @"1;33m", // bright - yellow 183| 0| @"1;34m", // bright - blue 184| 0| @"1;35m", // bright - magenta 185| 0| @"1;36m", // bright - cyan 186| 0| @"1;37m", // bright - white 187| 0| ]; 188| 0| 189| 0| codes_bg = @[ 190| 0| @"40m", // normal - black 191| 0| @"41m", // normal - red 192| 0| @"42m", // normal - green 193| 0| @"43m", // normal - yellow 194| 0| @"44m", // normal - blue 195| 0| @"45m", // normal - magenta 196| 0| @"46m", // normal - cyan 197| 0| @"47m", // normal - gray 198| 0| @"1;40m", // bright - darkgray 199| 0| @"1;41m", // bright - red 200| 0| @"1;42m", // bright - green 201| 0| @"1;43m", // bright - yellow 202| 0| @"1;44m", // bright - blue 203| 0| @"1;45m", // bright - magenta 204| 0| @"1;46m", // bright - cyan 205| 0| @"1;47m", // bright - white 206| 0| ]; 207| 0| 208| 0| 209| 0|#if MAP_TO_TERMINAL_APP_COLORS 210| 0| 211| 0| // Standard Terminal.app colors: 212| 0| // 213| 0| // These are the default colors used by Apple's Terminal.app. 214| 0| DDRGBColor rgbColors[] = { 215| 0| { 0, 0, 0}, // normal - black 216| 0| {194, 54, 33}, // normal - red 217| 0| { 37, 188, 36}, // normal - green 218| 0| {173, 173, 39}, // normal - yellow 219| 0| { 73, 46, 225}, // normal - blue 220| 0| {211, 56, 211}, // normal - magenta 221| 0| { 51, 187, 200}, // normal - cyan 222| 0| {203, 204, 205}, // normal - gray 223| 0| {129, 131, 131}, // bright - darkgray 224| 0| {252, 57, 31}, // bright - red 225| 0| { 49, 231, 34}, // bright - green 226| 0| {234, 236, 35}, // bright - yellow 227| 0| { 88, 51, 255}, // bright - blue 228| 0| {249, 53, 248}, // bright - magenta 229| 0| { 20, 240, 240}, // bright - cyan 230| 0| {233, 235, 235}, // bright - white 231| 0| }; 232| 0| 233| |#else /* if MAP_TO_TERMINAL_APP_COLORS */ 234| | 235| | // Standard xterm colors: 236| | // 237| | // These are the default colors used by most xterm shells. 238| | 239| | DDRGBColor rgbColors[] = { 240| | { 0, 0, 0}, // normal - black 241| | {205, 0, 0}, // normal - red 242| | { 0, 205, 0}, // normal - green 243| | {205, 205, 0}, // normal - yellow 244| | { 0, 0, 238}, // normal - blue 245| | {205, 0, 205}, // normal - magenta 246| | { 0, 205, 205}, // normal - cyan 247| | {229, 229, 229}, // normal - gray 248| | {127, 127, 127}, // bright - darkgray 249| | {255, 0, 0}, // bright - red 250| | { 0, 255, 0}, // bright - green 251| | {255, 255, 0}, // bright - yellow 252| | { 92, 92, 255}, // bright - blue 253| | {255, 0, 255}, // bright - magenta 254| | { 0, 255, 255}, // bright - cyan 255| | {255, 255, 255}, // bright - white 256| | }; 257| |#endif /* if MAP_TO_TERMINAL_APP_COLORS */ 258| | 259| 0| for (size_t i = 0; i < sizeof(rgbColors) / sizeof(rgbColors[0]); ++i) { 260| 0| [m_colors addObject:DDMakeColor(rgbColors[i].r, rgbColors[i].g, rgbColors[i].b)]; 261| 0| } 262| 0| colors = [m_colors copy]; 263| 0| 264| 0| NSAssert([codes_fg count] == [codes_bg count], @"Invalid colors/codes array(s)"); 265| 0| NSAssert([codes_fg count] == [colors count], @"Invalid colors/codes array(s)"); 266| 0|} 267| | 268| |/** 269| | * Initializes the colors array, as well as the codes_fg and codes_bg arrays, for 256 color mode. 270| | * 271| | * This method is used when the application is running from within a shell that supports 256 color mode. 272| | * This method is not invoked if the application is running within Xcode, or via normal UI app launch. 273| | **/ 274| 0|+ (void)initialize_colors_256 { 275| 0| if (codes_fg || codes_bg || colors) { 276| 0| return; 277| 0| } 278| 0| 279| 0| NSMutableArray *m_codes_fg = [NSMutableArray arrayWithCapacity:(256 - 16)]; 280| 0| NSMutableArray *m_codes_bg = [NSMutableArray arrayWithCapacity:(256 - 16)]; 281| 0| NSMutableArray *m_colors = [NSMutableArray arrayWithCapacity:(256 - 16)]; 282| 0| 283| 0| #if MAP_TO_TERMINAL_APP_COLORS 284| 0| 285| 0| // Standard Terminal.app colors: 286| 0| // 287| 0| // These are the colors the Terminal.app uses in xterm-256color mode. 288| 0| // In this mode, the terminal supports 256 different colors, specified by 256 color codes. 289| 0| // 290| 0| // The first 16 color codes map to the original 16 color codes supported by the earlier xterm-color mode. 291| 0| // These are actually configurable, and thus we ignore them for the purposes of mapping, 292| 0| // as we can't rely on them being constant. They are largely duplicated anyway. 293| 0| // 294| 0| // The next 216 color codes are designed to run the spectrum, with several shades of every color. 295| 0| // While the color codes are standardized, the actual RGB values for each color code is not. 296| 0| // Apple's Terminal.app uses different RGB values from that of a standard xterm. 297| 0| // Apple's choices in colors are designed to be a little nicer on the eyes. 298| 0| // 299| 0| // The last 24 color codes represent a grayscale. 300| 0| // 301| 0| // Unfortunately, unlike the standard xterm color chart, 302| 0| // Apple's RGB values cannot be calculated using a simple formula (at least not that I know of). 303| 0| // Also, I don't know of any ways to programmatically query the shell for the RGB values. 304| 0| // So this big giant color chart had to be made by hand. 305| 0| // 306| 0| // More information about ansi escape codes can be found online. 307| 0| // http://en.wikipedia.org/wiki/ANSI_escape_code 308| 0| 309| 0| // Colors 310| 0| DDRGBColor rgbColors[] = { 311| 0| { 47, 49, 49}, 312| 0| { 60, 42, 144}, 313| 0| { 66, 44, 183}, 314| 0| { 73, 46, 222}, 315| 0| { 81, 50, 253}, 316| 0| { 88, 51, 255}, 317| 0| 318| 0| { 42, 128, 37}, 319| 0| { 42, 127, 128}, 320| 0| { 44, 126, 169}, 321| 0| { 56, 125, 209}, 322| 0| { 59, 124, 245}, 323| 0| { 66, 123, 255}, 324| 0| 325| 0| { 51, 163, 41}, 326| 0| { 39, 162, 121}, 327| 0| { 42, 161, 162}, 328| 0| { 53, 160, 202}, 329| 0| { 45, 159, 240}, 330| 0| { 58, 158, 255}, 331| 0| 332| 0| { 31, 196, 37}, 333| 0| { 48, 196, 115}, 334| 0| { 39, 195, 155}, 335| 0| { 49, 195, 195}, 336| 0| { 32, 194, 235}, 337| 0| { 53, 193, 255}, 338| 0| 339| 0| { 50, 229, 35}, 340| 0| { 40, 229, 109}, 341| 0| { 27, 229, 149}, 342| 0| { 49, 228, 189}, 343| 0| { 33, 228, 228}, 344| 0| { 53, 227, 255}, 345| 0| 346| 0| { 27, 254, 30}, 347| 0| { 30, 254, 103}, 348| 0| { 45, 254, 143}, 349| 0| { 38, 253, 182}, 350| 0| { 38, 253, 222}, 351| 0| { 42, 253, 252}, 352| 0| 353| 0| {140, 48, 40}, 354| 0| {136, 51, 136}, 355| 0| {135, 52, 177}, 356| 0| {134, 52, 217}, 357| 0| {135, 56, 248}, 358| 0| {134, 53, 255}, 359| 0| 360| 0| {125, 125, 38}, 361| 0| {124, 125, 125}, 362| 0| {122, 124, 166}, 363| 0| {123, 124, 207}, 364| 0| {123, 122, 247}, 365| 0| {124, 121, 255}, 366| 0| 367| 0| {119, 160, 35}, 368| 0| {117, 160, 120}, 369| 0| {117, 160, 160}, 370| 0| {115, 159, 201}, 371| 0| {116, 158, 240}, 372| 0| {117, 157, 255}, 373| 0| 374| 0| {113, 195, 39}, 375| 0| {110, 194, 114}, 376| 0| {111, 194, 154}, 377| 0| {108, 194, 194}, 378| 0| {109, 193, 234}, 379| 0| {108, 192, 255}, 380| 0| 381| 0| {105, 228, 30}, 382| 0| {103, 228, 109}, 383| 0| {105, 228, 148}, 384| 0| {100, 227, 188}, 385| 0| { 99, 227, 227}, 386| 0| { 99, 226, 253}, 387| 0| 388| 0| { 92, 253, 34}, 389| 0| { 96, 253, 103}, 390| 0| { 97, 253, 142}, 391| 0| { 88, 253, 182}, 392| 0| { 93, 253, 221}, 393| 0| { 88, 254, 251}, 394| 0| 395| 0| {177, 53, 34}, 396| 0| {174, 54, 131}, 397| 0| {172, 55, 172}, 398| 0| {171, 57, 213}, 399| 0| {170, 55, 249}, 400| 0| {170, 57, 255}, 401| 0| 402| 0| {165, 123, 37}, 403| 0| {163, 123, 123}, 404| 0| {162, 123, 164}, 405| 0| {161, 122, 205}, 406| 0| {161, 121, 241}, 407| 0| {161, 121, 255}, 408| 0| 409| 0| {158, 159, 33}, 410| 0| {157, 158, 118}, 411| 0| {157, 158, 159}, 412| 0| {155, 157, 199}, 413| 0| {155, 157, 239}, 414| 0| {154, 156, 255}, 415| 0| 416| 0| {152, 193, 40}, 417| 0| {151, 193, 113}, 418| 0| {150, 193, 153}, 419| 0| {150, 192, 193}, 420| 0| {148, 192, 232}, 421| 0| {149, 191, 253}, 422| 0| 423| 0| {146, 227, 28}, 424| 0| {144, 227, 108}, 425| 0| {144, 227, 147}, 426| 0| {144, 227, 187}, 427| 0| {142, 226, 227}, 428| 0| {142, 225, 252}, 429| 0| 430| 0| {138, 253, 36}, 431| 0| {137, 253, 102}, 432| 0| {136, 253, 141}, 433| 0| {138, 254, 181}, 434| 0| {135, 255, 220}, 435| 0| {133, 255, 250}, 436| 0| 437| 0| {214, 57, 30}, 438| 0| {211, 59, 126}, 439| 0| {209, 57, 168}, 440| 0| {208, 55, 208}, 441| 0| {207, 58, 247}, 442| 0| {206, 61, 255}, 443| 0| 444| 0| {204, 121, 32}, 445| 0| {202, 121, 121}, 446| 0| {201, 121, 161}, 447| 0| {200, 120, 202}, 448| 0| {200, 120, 241}, 449| 0| {198, 119, 255}, 450| 0| 451| 0| {198, 157, 37}, 452| 0| {196, 157, 116}, 453| 0| {195, 156, 157}, 454| 0| {195, 156, 197}, 455| 0| {194, 155, 236}, 456| 0| {193, 155, 255}, 457| 0| 458| 0| {191, 192, 36}, 459| 0| {190, 191, 112}, 460| 0| {189, 191, 152}, 461| 0| {189, 191, 191}, 462| 0| {188, 190, 230}, 463| 0| {187, 190, 253}, 464| 0| 465| 0| {185, 226, 28}, 466| 0| {184, 226, 106}, 467| 0| {183, 225, 146}, 468| 0| {183, 225, 186}, 469| 0| {182, 225, 225}, 470| 0| {181, 224, 252}, 471| 0| 472| 0| {178, 255, 35}, 473| 0| {178, 255, 101}, 474| 0| {177, 254, 141}, 475| 0| {176, 254, 180}, 476| 0| {176, 254, 220}, 477| 0| {175, 253, 249}, 478| 0| 479| 0| {247, 56, 30}, 480| 0| {245, 57, 122}, 481| 0| {243, 59, 163}, 482| 0| {244, 60, 204}, 483| 0| {242, 59, 241}, 484| 0| {240, 55, 255}, 485| 0| 486| 0| {241, 119, 36}, 487| 0| {240, 120, 118}, 488| 0| {238, 119, 158}, 489| 0| {237, 119, 199}, 490| 0| {237, 118, 238}, 491| 0| {236, 118, 255}, 492| 0| 493| 0| {235, 154, 36}, 494| 0| {235, 154, 114}, 495| 0| {234, 154, 154}, 496| 0| {232, 154, 194}, 497| 0| {232, 153, 234}, 498| 0| {232, 153, 255}, 499| 0| 500| 0| {230, 190, 30}, 501| 0| {229, 189, 110}, 502| 0| {228, 189, 150}, 503| 0| {227, 189, 190}, 504| 0| {227, 189, 229}, 505| 0| {226, 188, 255}, 506| 0| 507| 0| {224, 224, 35}, 508| 0| {223, 224, 105}, 509| 0| {222, 224, 144}, 510| 0| {222, 223, 184}, 511| 0| {222, 223, 224}, 512| 0| {220, 223, 253}, 513| 0| 514| 0| {217, 253, 28}, 515| 0| {217, 253, 99}, 516| 0| {216, 252, 139}, 517| 0| {216, 252, 179}, 518| 0| {215, 252, 218}, 519| 0| {215, 251, 250}, 520| 0| 521| 0| {255, 61, 30}, 522| 0| {255, 60, 118}, 523| 0| {255, 58, 159}, 524| 0| {255, 56, 199}, 525| 0| {255, 55, 238}, 526| 0| {255, 59, 255}, 527| 0| 528| 0| {255, 117, 29}, 529| 0| {255, 117, 115}, 530| 0| {255, 117, 155}, 531| 0| {255, 117, 195}, 532| 0| {255, 116, 235}, 533| 0| {254, 116, 255}, 534| 0| 535| 0| {255, 152, 27}, 536| 0| {255, 152, 111}, 537| 0| {254, 152, 152}, 538| 0| {255, 152, 192}, 539| 0| {254, 151, 231}, 540| 0| {253, 151, 253}, 541| 0| 542| 0| {255, 187, 33}, 543| 0| {253, 187, 107}, 544| 0| {252, 187, 148}, 545| 0| {253, 187, 187}, 546| 0| {254, 187, 227}, 547| 0| {252, 186, 252}, 548| 0| 549| 0| {252, 222, 34}, 550| 0| {251, 222, 103}, 551| 0| {251, 222, 143}, 552| 0| {250, 222, 182}, 553| 0| {251, 221, 222}, 554| 0| {252, 221, 252}, 555| 0| 556| 0| {251, 252, 15}, 557| 0| {251, 252, 97}, 558| 0| {249, 252, 137}, 559| 0| {247, 252, 177}, 560| 0| {247, 253, 217}, 561| 0| {254, 255, 255}, 562| 0| 563| 0| // Grayscale 564| 0| 565| 0| { 52, 53, 53}, 566| 0| { 57, 58, 59}, 567| 0| { 66, 67, 67}, 568| 0| { 75, 76, 76}, 569| 0| { 83, 85, 85}, 570| 0| { 92, 93, 94}, 571| 0| 572| 0| {101, 102, 102}, 573| 0| {109, 111, 111}, 574| 0| {118, 119, 119}, 575| 0| {126, 127, 128}, 576| 0| {134, 136, 136}, 577| 0| {143, 144, 145}, 578| 0| 579| 0| {151, 152, 153}, 580| 0| {159, 161, 161}, 581| 0| {167, 169, 169}, 582| 0| {176, 177, 177}, 583| 0| {184, 185, 186}, 584| 0| {192, 193, 194}, 585| 0| 586| 0| {200, 201, 202}, 587| 0| {208, 209, 210}, 588| 0| {216, 218, 218}, 589| 0| {224, 226, 226}, 590| 0| {232, 234, 234}, 591| 0| {240, 242, 242}, 592| 0| }; 593| 0| 594| 0| for (size_t i = 0; i < sizeof(rgbColors) / sizeof(rgbColors[0]); ++i) { 595| 0| [m_colors addObject:DDMakeColor(rgbColors[i].r, rgbColors[i].g, rgbColors[i].b)]; 596| 0| } 597| 0| 598| 0| // Color codes 599| 0| 600| 0| int index = 16; 601| 0| 602| 0| while (index < 256) { 603| 0| [m_codes_fg addObject:[NSString stringWithFormat:@"38;5;%dm", index]]; 604| 0| [m_codes_bg addObject:[NSString stringWithFormat:@"48;5;%dm", index]]; 605| 0| 606| 0| index++; 607| 0| } 608| 0| 609| | #else /* if MAP_TO_TERMINAL_APP_COLORS */ 610| | 611| | // Standard xterm colors: 612| | // 613| | // These are the colors xterm shells use in xterm-256color mode. 614| | // In this mode, the shell supports 256 different colors, specified by 256 color codes. 615| | // 616| | // The first 16 color codes map to the original 16 color codes supported by the earlier xterm-color mode. 617| | // These are generally configurable, and thus we ignore them for the purposes of mapping, 618| | // as we can't rely on them being constant. They are largely duplicated anyway. 619| | // 620| | // The next 216 color codes are designed to run the spectrum, with several shades of every color. 621| | // The last 24 color codes represent a grayscale. 622| | // 623| | // While the color codes are standardized, the actual RGB values for each color code is not. 624| | // However most standard xterms follow a well known color chart, 625| | // which can easily be calculated using the simple formula below. 626| | // 627| | // More information about ansi escape codes can be found online. 628| | // http://en.wikipedia.org/wiki/ANSI_escape_code 629| | 630| | int index = 16; 631| | 632| | int r; // red 633| | int g; // green 634| | int b; // blue 635| | 636| | int ri; // r increment 637| | int gi; // g increment 638| | int bi; // b increment 639| | 640| | // Calculate xterm colors (using standard algorithm) 641| | 642| | int r = 0; 643| | int g = 0; 644| | int b = 0; 645| | 646| | for (ri = 0; ri < 6; ri++) { 647| | r = (ri == 0) ? 0 : 95 + (40 * (ri - 1)); 648| | 649| | for (gi = 0; gi < 6; gi++) { 650| | g = (gi == 0) ? 0 : 95 + (40 * (gi - 1)); 651| | 652| | for (bi = 0; bi < 6; bi++) { 653| | b = (bi == 0) ? 0 : 95 + (40 * (bi - 1)); 654| | 655| | [m_codes_fg addObject:[NSString stringWithFormat:@"38;5;%dm", index]]; 656| | [m_codes_bg addObject:[NSString stringWithFormat:@"48;5;%dm", index]]; 657| | [m_colors addObject:DDMakeColor(r, g, b)]; 658| | 659| | index++; 660| | } 661| | } 662| | } 663| | 664| | // Calculate xterm grayscale (using standard algorithm) 665| | 666| | r = 8; 667| | g = 8; 668| | b = 8; 669| | 670| | while (index < 256) { 671| | [m_codes_fg addObject:[NSString stringWithFormat:@"38;5;%dm", index]]; 672| | [m_codes_bg addObject:[NSString stringWithFormat:@"48;5;%dm", index]]; 673| | [m_colors addObject:DDMakeColor(r, g, b)]; 674| | 675| | r += 10; 676| | g += 10; 677| | b += 10; 678| | 679| | index++; 680| | } 681| | 682| | #endif /* if MAP_TO_TERMINAL_APP_COLORS */ 683| | 684| 0| codes_fg = [m_codes_fg copy]; 685| 0| codes_bg = [m_codes_bg copy]; 686| 0| colors = [m_colors copy]; 687| 0| 688| 0| NSAssert([codes_fg count] == [codes_bg count], @"Invalid colors/codes array(s)"); 689| 0| NSAssert([codes_fg count] == [colors count], @"Invalid colors/codes array(s)"); 690| 0|} 691| | 692| 0|+ (void)getRed:(CGFloat *)rPtr green:(CGFloat *)gPtr blue:(CGFloat *)bPtr fromColor:(DDColor *)color { 693| 0| #if TARGET_OS_IPHONE 694| 0| 695| 0| // iOS 696| 0| 697| 0| BOOL done = NO; 698| 0| 699| 0| if ([color respondsToSelector:@selector(getRed:green:blue:alpha:)]) { 700| 0| done = [color getRed:rPtr green:gPtr blue:bPtr alpha:NULL]; 701| 0| } 702| 0| 703| 0| if (!done) { 704| 0| // The method getRed:green:blue:alpha: was only available starting iOS 5. 705| 0| // So in iOS 4 and earlier, we have to jump through hoops. 706| 0| 707| 0| CGColorSpaceRef rgbColorSpace = CGColorSpaceCreateDeviceRGB(); 708| 0| 709| 0| unsigned char pixel[4]; 710| 0| CGContextRef context = CGBitmapContextCreate(&pixel, 1, 1, 8, 4, rgbColorSpace, (CGBitmapInfo)(kCGBitmapAlphaInfoMask & kCGImageAlphaNoneSkipLast)); 711| 0| 712| 0| CGContextSetFillColorWithColor(context, [color CGColor]); 713| 0| CGContextFillRect(context, CGRectMake(0, 0, 1, 1)); 714| 0| 715| 0| if (rPtr) { 716| 0| *rPtr = pixel[0] / 255.0f; 717| 0| } 718| 0| 719| 0| if (gPtr) { 720| 0| *gPtr = pixel[1] / 255.0f; 721| 0| } 722| 0| 723| 0| if (bPtr) { 724| 0| *bPtr = pixel[2] / 255.0f; 725| 0| } 726| 0| 727| 0| CGContextRelease(context); 728| 0| CGColorSpaceRelease(rgbColorSpace); 729| 0| } 730| 0| 731| | #elif defined(DD_CLI) || !__has_include() 732| | 733| | // OS X without AppKit 734| | 735| | [color getRed:rPtr green:gPtr blue:bPtr alpha:NULL]; 736| | 737| | #else /* if TARGET_OS_IPHONE */ 738| | 739| | // OS X with AppKit 740| | 741| | NSColor *safeColor = [color colorUsingColorSpaceName:NSCalibratedRGBColorSpace]; 742| | 743| | [safeColor getRed:rPtr green:gPtr blue:bPtr alpha:NULL]; 744| | #endif /* if TARGET_OS_IPHONE */ 745| |} 746| | 747| |/** 748| | * Maps the given color to the closest available color supported by the shell. 749| | * The shell may support 256 colors, or only 16. 750| | * 751| | * This method loops through the known supported color set, and calculates the closest color. 752| | * The array index of that color, within the colors array, is then returned. 753| | * This array index may also be used as the index within the codes_fg and codes_bg arrays. 754| | **/ 755| 0|+ (NSUInteger)codeIndexForColor:(DDColor *)inColor { 756| 0| CGFloat inR, inG, inB; 757| 0| 758| 0| [self getRed:&inR green:&inG blue:&inB fromColor:inColor]; 759| 0| 760| 0| NSUInteger bestIndex = 0; 761| 0| CGFloat lowestDistance = 100.0f; 762| 0| 763| 0| NSUInteger i = 0; 764| 0| 765| 0| for (DDColor *color in colors) { 766| 0| // Calculate Euclidean distance (lower value means closer to given color) 767| 0| 768| 0| CGFloat r, g, b; 769| 0| [self getRed:&r green:&g blue:&b fromColor:color]; 770| 0| 771| 0| #if CGFLOAT_IS_DOUBLE 772| 0| CGFloat distance = sqrt(pow(r - inR, 2.0) + pow(g - inG, 2.0) + pow(b - inB, 2.0)); 773| | #else 774| | CGFloat distance = sqrtf(powf(r - inR, 2.0f) + powf(g - inG, 2.0f) + powf(b - inB, 2.0f)); 775| | #endif 776| | 777| 0| NSLogVerbose(@"DDTTYLogger: %3lu : %.3f,%.3f,%.3f & %.3f,%.3f,%.3f = %.6f", 778| 0| (unsigned long)i, inR, inG, inB, r, g, b, distance); 779| 0| 780| 0| if (distance < lowestDistance) { 781| 0| bestIndex = i; 782| 0| lowestDistance = distance; 783| 0| 784| 0| NSLogVerbose(@"DDTTYLogger: New best index = %lu", (unsigned long)bestIndex); 785| 0| } 786| 0| 787| 0| i++; 788| 0| } 789| 0| 790| 0| return bestIndex; 791| 0|} 792| | 793| 0|+ (instancetype)sharedInstance { 794| 0| static dispatch_once_t DDTTYLoggerOnceToken; 795| 0| 796| 0| dispatch_once(&DDTTYLoggerOnceToken, ^{ 797| 0| // Xcode does NOT natively support colors in the Xcode debugging console. 798| 0| // You'll need to install the XcodeColors plugin to see colors in the Xcode console. 799| 0| // 800| 0| // PS - Please read the header file before diving into the source code. 801| 0| 802| 0| char *xcode_colors = getenv("XcodeColors"); 803| 0| char *term = getenv("TERM"); 804| 0| 805| 0| if (xcode_colors && (strcmp(xcode_colors, "YES") == 0)) { 806| 0| isaXcodeColorTTY = YES; 807| 0| } else if (term) { 808| 0| if (strcasestr(term, "color") != NULL) { 809| 0| isaColorTTY = YES; 810| 0| isaColor256TTY = (strcasestr(term, "256") != NULL); 811| 0| 812| 0| if (isaColor256TTY) { 813| 0| [self initialize_colors_256]; 814| 0| } else { 815| 0| [self initialize_colors_16]; 816| 0| } 817| 0| } 818| 0| } 819| 0| 820| 0| NSLogInfo(@"DDTTYLogger: isaColorTTY = %@", (isaColorTTY ? @"YES" : @"NO")); 821| 0| NSLogInfo(@"DDTTYLogger: isaColor256TTY: %@", (isaColor256TTY ? @"YES" : @"NO")); 822| 0| NSLogInfo(@"DDTTYLogger: isaXcodeColorTTY: %@", (isaXcodeColorTTY ? @"YES" : @"NO")); 823| 0| 824| 0| sharedInstance = [[self alloc] init]; 825| 0| }); 826| 0| 827| 0| return sharedInstance; 828| 0|} 829| | 830| 0|- (instancetype)init { 831| 0| if (sharedInstance != nil) { 832| 0| return nil; 833| 0| } 834| 0| 835| 0| if (@available(iOS 10.0, macOS 10.12, tvOS 10.0, watchOS 3.0, *)) { 836| 0| NSLogWarn(@"CocoaLumberjack: Warning: Usage of DDTTYLogger detected when DDOSLogger is available and can be used! Please consider migrating to DDOSLogger."); 837| 0| } 838| 0| 839| 0| if ((self = [super init])) { 840| 0| // Initialize 'app' variable (char *) 841| 0| 842| 0| _appName = [[NSProcessInfo processInfo] processName]; 843| 0| 844| 0| _appLen = [_appName lengthOfBytesUsingEncoding:NSUTF8StringEncoding]; 845| 0| 846| 0| if (_appLen == 0) { 847| 0| _appName = @""; 848| 0| _appLen = [_appName lengthOfBytesUsingEncoding:NSUTF8StringEncoding]; 849| 0| } 850| 0| 851| 0| _app = (char *)calloc(_appLen + 1, sizeof(char)); 852| 0| 853| 0| if (_app == NULL) { 854| 0| return nil; 855| 0| } 856| 0| 857| 0| BOOL processedAppName = [_appName getCString:_app maxLength:(_appLen + 1) encoding:NSUTF8StringEncoding]; 858| 0| 859| 0| if (NO == processedAppName) { 860| 0| free(_app); 861| 0| return nil; 862| 0| } 863| 0| 864| 0| // Initialize 'pid' variable (char *) 865| 0| 866| 0| _processID = [NSString stringWithFormat:@"%i", (int)getpid()]; 867| 0| 868| 0| _pidLen = [_processID lengthOfBytesUsingEncoding:NSUTF8StringEncoding]; 869| 0| _pid = (char *)calloc(_pidLen + 1, sizeof(char)); 870| 0| 871| 0| if (_pid == NULL) { 872| 0| free(_app); 873| 0| return nil; 874| 0| } 875| 0| 876| 0| BOOL processedID = [_processID getCString:_pid maxLength:(_pidLen + 1) encoding:NSUTF8StringEncoding]; 877| 0| 878| 0| if (NO == processedID) { 879| 0| free(_app); 880| 0| free(_pid); 881| 0| return nil; 882| 0| } 883| 0| 884| 0| // Initialize color stuff 885| 0| 886| 0| _colorsEnabled = NO; 887| 0| _colorProfilesArray = [[NSMutableArray alloc] initWithCapacity:8]; 888| 0| _colorProfilesDict = [[NSMutableDictionary alloc] initWithCapacity:8]; 889| 0| 890| 0| _automaticallyAppendNewlineForCustomFormatters = YES; 891| 0| } 892| 0| 893| 0| return self; 894| 0|} 895| | 896| 0|- (DDLoggerName)loggerName { 897| 0| return DDLoggerNameTTY; 898| 0|} 899| | 900| 0|- (void)loadDefaultColorProfiles { 901| 0| [self setForegroundColor:DDMakeColor(214, 57, 30) backgroundColor:nil forFlag:DDLogFlagError]; 902| 0| [self setForegroundColor:DDMakeColor(204, 121, 32) backgroundColor:nil forFlag:DDLogFlagWarning]; 903| 0|} 904| | 905| 0|- (BOOL)colorsEnabled { 906| 0| // The design of this method is taken from the DDAbstractLogger implementation. 907| 0| // For extensive documentation please refer to the DDAbstractLogger implementation. 908| 0| 909| 0| // Note: The internal implementation MUST access the colorsEnabled variable directly, 910| 0| // This method is designed explicitly for external access. 911| 0| // 912| 0| // Using "self." syntax to go through this method will cause immediate deadlock. 913| 0| // This is the intended result. Fix it by accessing the ivar directly. 914| 0| // Great strides have been take to ensure this is safe to do. Plus it's MUCH faster. 915| 0| 916| 0| NSAssert(![self isOnGlobalLoggingQueue], @"Core architecture requirement failure"); 917| 0| NSAssert(![self isOnInternalLoggerQueue], @"MUST access ivar directly, NOT via self.* syntax."); 918| 0| 919| 0| dispatch_queue_t globalLoggingQueue = [DDLog loggingQueue]; 920| 0| 921| 0| __block BOOL result; 922| 0| 923| 0| dispatch_sync(globalLoggingQueue, ^{ 924| 0| dispatch_sync(self.loggerQueue, ^{ 925| 0| result = self->_colorsEnabled; 926| 0| }); 927| 0| }); 928| 0| 929| 0| return result; 930| 0|} 931| | 932| 0|- (void)setColorsEnabled:(BOOL)newColorsEnabled { 933| 0| dispatch_block_t block = ^{ 934| 0| @autoreleasepool { 935| 0| self->_colorsEnabled = newColorsEnabled; 936| 0| 937| 0| if ([self->_colorProfilesArray count] == 0) { 938| 0| [self loadDefaultColorProfiles]; 939| 0| } 940| 0| } 941| 0| }; 942| 0| 943| 0| // The design of this method is taken from the DDAbstractLogger implementation. 944| 0| // For extensive documentation please refer to the DDAbstractLogger implementation. 945| 0| 946| 0| // Note: The internal implementation MUST access the colorsEnabled variable directly, 947| 0| // This method is designed explicitly for external access. 948| 0| // 949| 0| // Using "self." syntax to go through this method will cause immediate deadlock. 950| 0| // This is the intended result. Fix it by accessing the ivar directly. 951| 0| // Great strides have been take to ensure this is safe to do. Plus it's MUCH faster. 952| 0| 953| 0| NSAssert(![self isOnGlobalLoggingQueue], @"Core architecture requirement failure"); 954| 0| NSAssert(![self isOnInternalLoggerQueue], @"MUST access ivar directly, NOT via self.* syntax."); 955| 0| 956| 0| dispatch_queue_t globalLoggingQueue = [DDLog loggingQueue]; 957| 0| 958| 0| dispatch_async(globalLoggingQueue, ^{ 959| 0| dispatch_async(self.loggerQueue, block); 960| 0| }); 961| 0|} 962| | 963| 0|- (void)setForegroundColor:(DDColor *)txtColor backgroundColor:(DDColor *)bgColor forFlag:(DDLogFlag)mask { 964| 0| [self setForegroundColor:txtColor backgroundColor:bgColor forFlag:mask context:LOG_CONTEXT_ALL]; 965| 0|} 966| | 967| 0|- (void)setForegroundColor:(DDColor *)txtColor backgroundColor:(DDColor *)bgColor forFlag:(DDLogFlag)mask context:(NSInteger)ctxt { 968| 0| dispatch_block_t block = ^{ 969| 0| @autoreleasepool { 970| 0| DDTTYLoggerColorProfile *newColorProfile = 971| 0| [[DDTTYLoggerColorProfile alloc] initWithForegroundColor:txtColor 972| 0| backgroundColor:bgColor 973| 0| flag:mask 974| 0| context:ctxt]; 975| 0| 976| 0| NSLogInfo(@"DDTTYLogger: newColorProfile: %@", newColorProfile); 977| 0| 978| 0| NSUInteger i = 0; 979| 0| 980| 0| for (DDTTYLoggerColorProfile *colorProfile in self->_colorProfilesArray) { 981| 0| if ((colorProfile->mask == mask) && (colorProfile->context == ctxt)) { 982| 0| break; 983| 0| } 984| 0| 985| 0| i++; 986| 0| } 987| 0| 988| 0| if (i < [self->_colorProfilesArray count]) { 989| 0| self->_colorProfilesArray[i] = newColorProfile; 990| 0| } else { 991| 0| [self->_colorProfilesArray addObject:newColorProfile]; 992| 0| } 993| 0| } 994| 0| }; 995| 0| 996| 0| // The design of the setter logic below is taken from the DDAbstractLogger implementation. 997| 0| // For documentation please refer to the DDAbstractLogger implementation. 998| 0| 999| 0| if ([self isOnInternalLoggerQueue]) { 1000| 0| block(); 1001| 0| } else { 1002| 0| dispatch_queue_t globalLoggingQueue = [DDLog loggingQueue]; 1003| 0| NSAssert(![self isOnGlobalLoggingQueue], @"Core architecture requirement failure"); 1004| 0| 1005| 0| dispatch_async(globalLoggingQueue, ^{ 1006| 0| dispatch_async(self.loggerQueue, block); 1007| 0| }); 1008| 0| } 1009| 0|} 1010| | 1011| 0|- (void)setForegroundColor:(DDColor *)txtColor backgroundColor:(DDColor *)bgColor forTag:(id )tag { 1012| 0| NSAssert([(id < NSObject >) tag conformsToProtocol: @protocol(NSCopying)], @"Invalid tag"); 1013| 0| 1014| 0| dispatch_block_t block = ^{ 1015| 0| @autoreleasepool { 1016| 0| DDTTYLoggerColorProfile *newColorProfile = 1017| 0| [[DDTTYLoggerColorProfile alloc] initWithForegroundColor:txtColor 1018| 0| backgroundColor:bgColor 1019| 0| flag:(DDLogFlag)0 1020| 0| context:0]; 1021| 0| 1022| 0| NSLogInfo(@"DDTTYLogger: newColorProfile: %@", newColorProfile); 1023| 0| 1024| 0| self->_colorProfilesDict[tag] = newColorProfile; 1025| 0| } 1026| 0| }; 1027| 0| 1028| 0| // The design of the setter logic below is taken from the DDAbstractLogger implementation. 1029| 0| // For documentation please refer to the DDAbstractLogger implementation. 1030| 0| 1031| 0| if ([self isOnInternalLoggerQueue]) { 1032| 0| block(); 1033| 0| } else { 1034| 0| dispatch_queue_t globalLoggingQueue = [DDLog loggingQueue]; 1035| 0| NSAssert(![self isOnGlobalLoggingQueue], @"Core architecture requirement failure"); 1036| 0| 1037| 0| dispatch_async(globalLoggingQueue, ^{ 1038| 0| dispatch_async(self.loggerQueue, block); 1039| 0| }); 1040| 0| } 1041| 0|} 1042| | 1043| 0|- (void)clearColorsForFlag:(DDLogFlag)mask { 1044| 0| [self clearColorsForFlag:mask context:0]; 1045| 0|} 1046| | 1047| 0|- (void)clearColorsForFlag:(DDLogFlag)mask context:(NSInteger)context { 1048| 0| dispatch_block_t block = ^{ 1049| 0| @autoreleasepool { 1050| 0| NSUInteger i = 0; 1051| 0| 1052| 0| for (DDTTYLoggerColorProfile *colorProfile in self->_colorProfilesArray) { 1053| 0| if ((colorProfile->mask == mask) && (colorProfile->context == context)) { 1054| 0| break; 1055| 0| } 1056| 0| 1057| 0| i++; 1058| 0| } 1059| 0| 1060| 0| if (i < [self->_colorProfilesArray count]) { 1061| 0| [self->_colorProfilesArray removeObjectAtIndex:i]; 1062| 0| } 1063| 0| } 1064| 0| }; 1065| 0| 1066| 0| // The design of the setter logic below is taken from the DDAbstractLogger implementation. 1067| 0| // For documentation please refer to the DDAbstractLogger implementation. 1068| 0| 1069| 0| if ([self isOnInternalLoggerQueue]) { 1070| 0| block(); 1071| 0| } else { 1072| 0| dispatch_queue_t globalLoggingQueue = [DDLog loggingQueue]; 1073| 0| NSAssert(![self isOnGlobalLoggingQueue], @"Core architecture requirement failure"); 1074| 0| 1075| 0| dispatch_async(globalLoggingQueue, ^{ 1076| 0| dispatch_async(self.loggerQueue, block); 1077| 0| }); 1078| 0| } 1079| 0|} 1080| | 1081| 0|- (void)clearColorsForTag:(id )tag { 1082| 0| NSAssert([(id < NSObject >) tag conformsToProtocol: @protocol(NSCopying)], @"Invalid tag"); 1083| 0| 1084| 0| dispatch_block_t block = ^{ 1085| 0| @autoreleasepool { 1086| 0| [self->_colorProfilesDict removeObjectForKey:tag]; 1087| 0| } 1088| 0| }; 1089| 0| 1090| 0| // The design of the setter logic below is taken from the DDAbstractLogger implementation. 1091| 0| // For documentation please refer to the DDAbstractLogger implementation. 1092| 0| 1093| 0| if ([self isOnInternalLoggerQueue]) { 1094| 0| block(); 1095| 0| } else { 1096| 0| dispatch_queue_t globalLoggingQueue = [DDLog loggingQueue]; 1097| 0| NSAssert(![self isOnGlobalLoggingQueue], @"Core architecture requirement failure"); 1098| 0| 1099| 0| dispatch_async(globalLoggingQueue, ^{ 1100| 0| dispatch_async(self.loggerQueue, block); 1101| 0| }); 1102| 0| } 1103| 0|} 1104| | 1105| 0|- (void)clearColorsForAllFlags { 1106| 0| dispatch_block_t block = ^{ 1107| 0| @autoreleasepool { 1108| 0| [self->_colorProfilesArray removeAllObjects]; 1109| 0| } 1110| 0| }; 1111| 0| 1112| 0| // The design of the setter logic below is taken from the DDAbstractLogger implementation. 1113| 0| // For documentation please refer to the DDAbstractLogger implementation. 1114| 0| 1115| 0| if ([self isOnInternalLoggerQueue]) { 1116| 0| block(); 1117| 0| } else { 1118| 0| dispatch_queue_t globalLoggingQueue = [DDLog loggingQueue]; 1119| 0| NSAssert(![self isOnGlobalLoggingQueue], @"Core architecture requirement failure"); 1120| 0| 1121| 0| dispatch_async(globalLoggingQueue, ^{ 1122| 0| dispatch_async(self.loggerQueue, block); 1123| 0| }); 1124| 0| } 1125| 0|} 1126| | 1127| 0|- (void)clearColorsForAllTags { 1128| 0| dispatch_block_t block = ^{ 1129| 0| @autoreleasepool { 1130| 0| [self->_colorProfilesDict removeAllObjects]; 1131| 0| } 1132| 0| }; 1133| 0| 1134| 0| // The design of the setter logic below is taken from the DDAbstractLogger implementation. 1135| 0| // For documentation please refer to the DDAbstractLogger implementation. 1136| 0| 1137| 0| if ([self isOnInternalLoggerQueue]) { 1138| 0| block(); 1139| 0| } else { 1140| 0| dispatch_queue_t globalLoggingQueue = [DDLog loggingQueue]; 1141| 0| NSAssert(![self isOnGlobalLoggingQueue], @"Core architecture requirement failure"); 1142| 0| 1143| 0| dispatch_async(globalLoggingQueue, ^{ 1144| 0| dispatch_async(self.loggerQueue, block); 1145| 0| }); 1146| 0| } 1147| 0|} 1148| | 1149| 0|- (void)clearAllColors { 1150| 0| dispatch_block_t block = ^{ 1151| 0| @autoreleasepool { 1152| 0| [self->_colorProfilesArray removeAllObjects]; 1153| 0| [self->_colorProfilesDict removeAllObjects]; 1154| 0| } 1155| 0| }; 1156| 0| 1157| 0| // The design of the setter logic below is taken from the DDAbstractLogger implementation. 1158| 0| // For documentation please refer to the DDAbstractLogger implementation. 1159| 0| 1160| 0| if ([self isOnInternalLoggerQueue]) { 1161| 0| block(); 1162| 0| } else { 1163| 0| dispatch_queue_t globalLoggingQueue = [DDLog loggingQueue]; 1164| 0| NSAssert(![self isOnGlobalLoggingQueue], @"Core architecture requirement failure"); 1165| 0| 1166| 0| dispatch_async(globalLoggingQueue, ^{ 1167| 0| dispatch_async(self.loggerQueue, block); 1168| 0| }); 1169| 0| } 1170| 0|} 1171| | 1172| 0|- (void)logMessage:(DDLogMessage *)logMessage { 1173| 0| NSString *logMsg = logMessage->_message; 1174| 0| BOOL isFormatted = NO; 1175| 0| 1176| 0| if (_logFormatter) { 1177| 0| logMsg = [_logFormatter formatLogMessage:logMessage]; 1178| 0| isFormatted = logMsg != logMessage->_message; 1179| 0| } 1180| 0| 1181| 0| if (logMsg) { 1182| 0| // Search for a color profile associated with the log message 1183| 0| 1184| 0| DDTTYLoggerColorProfile *colorProfile = nil; 1185| 0| 1186| 0| if (_colorsEnabled) { 1187| 0| if (logMessage->_representedObject) { 1188| 0| colorProfile = _colorProfilesDict[logMessage->_representedObject]; 1189| 0| } 1190| 0| 1191| 0| if (colorProfile == nil) { 1192| 0| for (DDTTYLoggerColorProfile *cp in _colorProfilesArray) { 1193| 0| if (logMessage->_flag & cp->mask) { 1194| 0| // Color profile set for this context? 1195| 0| if (logMessage->_context == cp->context) { 1196| 0| colorProfile = cp; 1197| 0| 1198| 0| // Stop searching 1199| 0| break; 1200| 0| } 1201| 0| 1202| 0| // Check if LOG_CONTEXT_ALL was specified as a default color for this flag 1203| 0| if (cp->context == LOG_CONTEXT_ALL) { 1204| 0| colorProfile = cp; 1205| 0| 1206| 0| // We don't break to keep searching for more specific color profiles for the context 1207| 0| } 1208| 0| } 1209| 0| } 1210| 0| } 1211| 0| } 1212| 0| 1213| 0| // Convert log message to C string. 1214| 0| // 1215| 0| // We use the stack instead of the heap for speed if possible. 1216| 0| // But we're extra cautious to avoid a stack overflow. 1217| 0| 1218| 0| NSUInteger msgLen = [logMsg lengthOfBytesUsingEncoding:NSUTF8StringEncoding]; 1219| 0| const BOOL useStack = msgLen < (1024 * 4); 1220| 0| 1221| 0| char *msg; 1222| 0| if (useStack) { 1223| 0| msg = (char *)alloca(msgLen + 1); 1224| 0| } else { 1225| 0| msg = (char *)calloc(msgLen + 1, sizeof(char)); 1226| 0| } 1227| 0| if (msg == NULL) { 1228| 0| return; 1229| 0| } 1230| 0| 1231| 0| BOOL logMsgEnc = [logMsg getCString:msg maxLength:(msgLen + 1) encoding:NSUTF8StringEncoding]; 1232| 0| if (!logMsgEnc) { 1233| 0| if (!useStack) { 1234| 0| free(msg); 1235| 0| } 1236| 0| return; 1237| 0| } 1238| 0| 1239| 0| // Write the log message to STDERR 1240| 0| 1241| 0| if (isFormatted) { 1242| 0| // The log message has already been formatted. 1243| 0| const int iovec_len = (_automaticallyAppendNewlineForCustomFormatters) ? 5 : 4; 1244| 0| struct iovec v[iovec_len]; 1245| 0| 1246| 0| if (colorProfile) { 1247| 0| v[0].iov_base = colorProfile->fgCode; 1248| 0| v[0].iov_len = colorProfile->fgCodeLen; 1249| 0| 1250| 0| v[1].iov_base = colorProfile->bgCode; 1251| 0| v[1].iov_len = colorProfile->bgCodeLen; 1252| 0| 1253| 0| v[iovec_len - 1].iov_base = colorProfile->resetCode; 1254| 0| v[iovec_len - 1].iov_len = colorProfile->resetCodeLen; 1255| 0| } else { 1256| 0| v[0].iov_base = ""; 1257| 0| v[0].iov_len = 0; 1258| 0| 1259| 0| v[1].iov_base = ""; 1260| 0| v[1].iov_len = 0; 1261| 0| 1262| 0| v[iovec_len - 1].iov_base = ""; 1263| 0| v[iovec_len - 1].iov_len = 0; 1264| 0| } 1265| 0| 1266| 0| v[2].iov_base = msg; 1267| 0| v[2].iov_len = msgLen; 1268| 0| 1269| 0| if (iovec_len == 5) { 1270| 0| v[3].iov_base = "\n"; 1271| 0| v[3].iov_len = (msg[msgLen] == '\n') ? 0 : 1; 1272| 0| } 1273| 0| 1274| 0| writev(STDERR_FILENO, v, iovec_len); 1275| 0| } else { 1276| 0| // The log message is unformatted, so apply standard NSLog style formatting. 1277| 0| 1278| 0| int len; 1279| 0| char ts[24] = ""; 1280| 0| size_t tsLen = 0; 1281| 0| 1282| 0| // Calculate timestamp. 1283| 0| // The technique below is faster than using NSDateFormatter. 1284| 0| if (logMessage->_timestamp) { 1285| 0| NSTimeInterval epoch = [logMessage->_timestamp timeIntervalSince1970]; 1286| 0| struct tm tm; 1287| 0| time_t time = (time_t)epoch; 1288| 0| (void)localtime_r(&time, &tm); 1289| 0| int milliseconds = (int)((epoch - floor(epoch)) * 1000.0); 1290| 0| 1291| 0| len = snprintf(ts, 24, "%04d-%02d-%02d %02d:%02d:%02d:%03d", // yyyy-MM-dd HH:mm:ss:SSS 1292| 0| tm.tm_year + 1900, 1293| 0| tm.tm_mon + 1, 1294| 0| tm.tm_mday, 1295| 0| tm.tm_hour, 1296| 0| tm.tm_min, 1297| 0| tm.tm_sec, milliseconds); 1298| 0| 1299| 0| tsLen = (NSUInteger)MAX(MIN(24 - 1, len), 0); 1300| 0| } 1301| 0| 1302| 0| // Calculate thread ID 1303| 0| // 1304| 0| // How many characters do we need for the thread id? 1305| 0| // logMessage->machThreadID is of type mach_port_t, which is an unsigned int. 1306| 0| // 1307| 0| // 1 hex char = 4 bits 1308| 0| // 8 hex chars for 32 bit, plus ending '\0' = 9 1309| 0| 1310| 0| char tid[9]; 1311| 0| len = snprintf(tid, 9, "%s", [logMessage->_threadID cStringUsingEncoding:NSUTF8StringEncoding]); 1312| 0| 1313| 0| size_t tidLen = (NSUInteger)MAX(MIN(9 - 1, len), 0); 1314| 0| 1315| 0| // Here is our format: "%s %s[%i:%s] %s", timestamp, appName, processID, threadID, logMsg 1316| 0| 1317| 0| struct iovec v[13]; 1318| 0| 1319| 0| if (colorProfile) { 1320| 0| v[0].iov_base = colorProfile->fgCode; 1321| 0| v[0].iov_len = colorProfile->fgCodeLen; 1322| 0| 1323| 0| v[1].iov_base = colorProfile->bgCode; 1324| 0| v[1].iov_len = colorProfile->bgCodeLen; 1325| 0| 1326| 0| v[12].iov_base = colorProfile->resetCode; 1327| 0| v[12].iov_len = colorProfile->resetCodeLen; 1328| 0| } else { 1329| 0| v[0].iov_base = ""; 1330| 0| v[0].iov_len = 0; 1331| 0| 1332| 0| v[1].iov_base = ""; 1333| 0| v[1].iov_len = 0; 1334| 0| 1335| 0| v[12].iov_base = ""; 1336| 0| v[12].iov_len = 0; 1337| 0| } 1338| 0| 1339| 0| v[2].iov_base = ts; 1340| 0| v[2].iov_len = tsLen; 1341| 0| 1342| 0| v[3].iov_base = " "; 1343| 0| v[3].iov_len = 1; 1344| 0| 1345| 0| v[4].iov_base = _app; 1346| 0| v[4].iov_len = _appLen; 1347| 0| 1348| 0| v[5].iov_base = "["; 1349| 0| v[5].iov_len = 1; 1350| 0| 1351| 0| v[6].iov_base = _pid; 1352| 0| v[6].iov_len = _pidLen; 1353| 0| 1354| 0| v[7].iov_base = ":"; 1355| 0| v[7].iov_len = 1; 1356| 0| 1357| 0| v[8].iov_base = tid; 1358| 0| v[8].iov_len = MIN((size_t)8, tidLen); // snprintf doesn't return what you might think 1359| 0| 1360| 0| v[9].iov_base = "] "; 1361| 0| v[9].iov_len = 2; 1362| 0| 1363| 0| v[10].iov_base = (char *)msg; 1364| 0| v[10].iov_len = msgLen; 1365| 0| 1366| 0| v[11].iov_base = "\n"; 1367| 0| v[11].iov_len = (msg[msgLen] == '\n') ? 0 : 1; 1368| 0| 1369| 0| writev(STDERR_FILENO, v, 13); 1370| 0| } 1371| 0| 1372| 0| if (!useStack) { 1373| 0| free(msg); 1374| 0| } 1375| 0| } 1376| 0|} 1377| | 1378| |@end 1379| | 1380| |//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 1381| | 1382| |@implementation DDTTYLoggerColorProfile 1383| | 1384| 0|- (instancetype)initWithForegroundColor:(DDColor *)fgColor backgroundColor:(DDColor *)bgColor flag:(DDLogFlag)aMask context:(NSInteger)ctxt { 1385| 0| if ((self = [super init])) { 1386| 0| mask = aMask; 1387| 0| context = ctxt; 1388| 0| 1389| 0| CGFloat r, g, b; 1390| 0| 1391| 0| if (fgColor) { 1392| 0| [DDTTYLogger getRed:&r green:&g blue:&b fromColor:fgColor]; 1393| 0| 1394| 0| fg_r = (uint8_t)(r * 255.0f); 1395| 0| fg_g = (uint8_t)(g * 255.0f); 1396| 0| fg_b = (uint8_t)(b * 255.0f); 1397| 0| } 1398| 0| 1399| 0| if (bgColor) { 1400| 0| [DDTTYLogger getRed:&r green:&g blue:&b fromColor:bgColor]; 1401| 0| 1402| 0| bg_r = (uint8_t)(r * 255.0f); 1403| 0| bg_g = (uint8_t)(g * 255.0f); 1404| 0| bg_b = (uint8_t)(b * 255.0f); 1405| 0| } 1406| 0| 1407| 0| if (fgColor && isaColorTTY) { 1408| 0| // Map foreground color to closest available shell color 1409| 0| 1410| 0| fgCodeIndex = [DDTTYLogger codeIndexForColor:fgColor]; 1411| 0| fgCodeRaw = codes_fg[fgCodeIndex]; 1412| 0| 1413| 0| NSString *escapeSeq = @"\033["; 1414| 0| 1415| 0| NSUInteger len1 = [escapeSeq lengthOfBytesUsingEncoding:NSUTF8StringEncoding]; 1416| 0| NSUInteger len2 = [fgCodeRaw lengthOfBytesUsingEncoding:NSUTF8StringEncoding]; 1417| 0| 1418| 0| BOOL escapeSeqEnc = [escapeSeq getCString:(fgCode) maxLength:(len1 + 1) encoding:NSUTF8StringEncoding]; 1419| 0| BOOL fgCodeRawEsc = [fgCodeRaw getCString:(fgCode + len1) maxLength:(len2 + 1) encoding:NSUTF8StringEncoding]; 1420| 0| 1421| 0| if (!escapeSeqEnc || !fgCodeRawEsc) { 1422| 0| return nil; 1423| 0| } 1424| 0| 1425| 0| fgCodeLen = len1 + len2; 1426| 0| } else if (fgColor && isaXcodeColorTTY) { 1427| 0| // Convert foreground color to color code sequence 1428| 0| 1429| 0| const char *escapeSeq = XCODE_COLORS_ESCAPE_SEQ; 1430| 0| 1431| 0| int result = snprintf(fgCode, 24, "%sfg%u,%u,%u;", escapeSeq, fg_r, fg_g, fg_b); 1432| 0| fgCodeLen = (NSUInteger)MAX(MIN(result, (24 - 1)), 0); 1433| 0| } else { 1434| 0| // No foreground color or no color support 1435| 0| 1436| 0| fgCode[0] = '\0'; 1437| 0| fgCodeLen = 0; 1438| 0| } 1439| 0| 1440| 0| if (bgColor && isaColorTTY) { 1441| 0| // Map background color to closest available shell color 1442| 0| 1443| 0| bgCodeIndex = [DDTTYLogger codeIndexForColor:bgColor]; 1444| 0| bgCodeRaw = codes_bg[bgCodeIndex]; 1445| 0| 1446| 0| NSString *escapeSeq = @"\033["; 1447| 0| 1448| 0| NSUInteger len1 = [escapeSeq lengthOfBytesUsingEncoding:NSUTF8StringEncoding]; 1449| 0| NSUInteger len2 = [bgCodeRaw lengthOfBytesUsingEncoding:NSUTF8StringEncoding]; 1450| 0| 1451| 0| BOOL escapeSeqEnc = [escapeSeq getCString:(bgCode) maxLength:(len1 + 1) encoding:NSUTF8StringEncoding]; 1452| 0| BOOL bgCodeRawEsc = [bgCodeRaw getCString:(bgCode + len1) maxLength:(len2 + 1) encoding:NSUTF8StringEncoding]; 1453| 0| 1454| 0| if (!escapeSeqEnc || !bgCodeRawEsc) { 1455| 0| return nil; 1456| 0| } 1457| 0| 1458| 0| bgCodeLen = len1 + len2; 1459| 0| } else if (bgColor && isaXcodeColorTTY) { 1460| 0| // Convert background color to color code sequence 1461| 0| 1462| 0| const char *escapeSeq = XCODE_COLORS_ESCAPE_SEQ; 1463| 0| 1464| 0| int result = snprintf(bgCode, 24, "%sbg%u,%u,%u;", escapeSeq, bg_r, bg_g, bg_b); 1465| 0| bgCodeLen = (NSUInteger)MAX(MIN(result, (24 - 1)), 0); 1466| 0| } else { 1467| 0| // No background color or no color support 1468| 0| 1469| 0| bgCode[0] = '\0'; 1470| 0| bgCodeLen = 0; 1471| 0| } 1472| 0| 1473| 0| if (isaColorTTY) { 1474| 0| resetCodeLen = (NSUInteger)MAX(snprintf(resetCode, 8, "\033[0m"), 0); 1475| 0| } else if (isaXcodeColorTTY) { 1476| 0| resetCodeLen = (NSUInteger)MAX(snprintf(resetCode, 8, XCODE_COLORS_RESET), 0); 1477| 0| } else { 1478| 0| resetCode[0] = '\0'; 1479| 0| resetCodeLen = 0; 1480| 0| } 1481| 0| } 1482| 0| 1483| 0| return self; 1484| 0|} 1485| | 1486| 0|- (NSString *)description { 1487| 0| return [NSString stringWithFormat: 1488| 0| @"", 1489| 0| self, (int)mask, (long)context, fg_r, fg_g, fg_b, bg_r, bg_g, bg_b, fgCodeRaw, bgCodeRaw]; 1490| 0|} 1491| | 1492| |@end /Users/runner/work/CocoaLumberjack/CocoaLumberjack/Sources/CocoaLumberjack/Extensions/DDContextFilterLogFormatter+Deprecated.m: 1| |// Software License Agreement (BSD License) 2| |// 3| |// Copyright (c) 2010-2021, Deusty, LLC 4| |// All rights reserved. 5| |// 6| |// Redistribution and use of this software in source and binary forms, 7| |// with or without modification, are permitted provided that the following conditions are met: 8| |// 9| |// * Redistributions of source code must retain the above copyright notice, 10| |// this list of conditions and the following disclaimer. 11| |// 12| |// * Neither the name of Deusty nor the names of its contributors may be used 13| |// to endorse or promote products derived from this software without specific 14| |// prior written permission of Deusty, LLC. 15| | 16| |#import 17| | 18| |@implementation DDContextAllowlistFilterLogFormatter (Deprecated) 19| | 20| 0|- (void)addToWhitelist:(NSInteger)loggingContext { 21| 0| [self addToAllowlist:loggingContext]; 22| 0|} 23| | 24| 0|- (void)removeFromWhitelist:(NSInteger)loggingContext { 25| 0| [self removeFromAllowlist:loggingContext]; 26| 0|} 27| | 28| 0|- (NSArray *)whitelist { 29| 0| return [self allowlist]; 30| 0|} 31| | 32| 0|- (BOOL)isOnWhitelist:(NSInteger)loggingContext { 33| 0| return [self isOnAllowlist:loggingContext]; 34| 0|} 35| | 36| |@end 37| | 38| | 39| |@implementation DDContextDenylistFilterLogFormatter (Deprecated) 40| | 41| 0|- (void)addToBlacklist:(NSInteger)loggingContext { 42| 0| [self addToDenylist:loggingContext]; 43| 0|} 44| | 45| 0|- (void)removeFromBlacklist:(NSInteger)loggingContext { 46| 0| [self removeFromDenylist:loggingContext]; 47| 0|} 48| | 49| 0|- (NSArray *)blacklist { 50| 0| return [self denylist]; 51| 0|} 52| | 53| 0|- (BOOL)isOnBlacklist:(NSInteger)loggingContext { 54| 0| return [self isOnDenylist:loggingContext]; 55| 0|} 56| | 57| |@end /Users/runner/work/CocoaLumberjack/CocoaLumberjack/Sources/CocoaLumberjack/Extensions/DDContextFilterLogFormatter.m: 1| |// Software License Agreement (BSD License) 2| |// 3| |// Copyright (c) 2010-2021, Deusty, LLC 4| |// All rights reserved. 5| |// 6| |// Redistribution and use of this software in source and binary forms, 7| |// with or without modification, are permitted provided that the following conditions are met: 8| |// 9| |// * Redistributions of source code must retain the above copyright notice, 10| |// this list of conditions and the following disclaimer. 11| |// 12| |// * Neither the name of Deusty nor the names of its contributors may be used 13| |// to endorse or promote products derived from this software without specific 14| |// prior written permission of Deusty, LLC. 15| | 16| |#if !__has_feature(objc_arc) 17| |#error This file must be compiled with ARC. Use -fobjc-arc flag (or convert project to ARC). 18| |#endif 19| | 20| |#import 21| | 22| |#import 23| | 24| |@interface DDLoggingContextSet : NSObject 25| | 26| |@property (readonly, copy, nonnull) NSArray *currentSet; 27| | 28| |- (void)addToSet:(NSInteger)loggingContext; 29| |- (void)removeFromSet:(NSInteger)loggingContext; 30| | 31| |- (BOOL)isInSet:(NSInteger)loggingContext; 32| | 33| |@end 34| | 35| |//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 36| |#pragma mark - 37| |//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 38| | 39| |@interface DDContextAllowlistFilterLogFormatter () { 40| | DDLoggingContextSet *_contextSet; 41| |} 42| |@end 43| | 44| |@implementation DDContextAllowlistFilterLogFormatter 45| | 46| 0|- (instancetype)init { 47| 0| if ((self = [super init])) { 48| 0| _contextSet = [[DDLoggingContextSet alloc] init]; 49| 0| } 50| 0| return self; 51| 0|} 52| | 53| 0|- (void)addToAllowlist:(NSInteger)loggingContext { 54| 0| [_contextSet addToSet:loggingContext]; 55| 0|} 56| | 57| 0|- (void)removeFromAllowlist:(NSInteger)loggingContext { 58| 0| [_contextSet removeFromSet:loggingContext]; 59| 0|} 60| | 61| 0|- (NSArray *)allowlist { 62| 0| return [_contextSet currentSet]; 63| 0|} 64| | 65| 0|- (BOOL)isOnAllowlist:(NSInteger)loggingContext { 66| 0| return [_contextSet isInSet:loggingContext]; 67| 0|} 68| | 69| 0|- (NSString *)formatLogMessage:(DDLogMessage *)logMessage { 70| 0| if ([self isOnAllowlist:logMessage->_context]) { 71| 0| return logMessage->_message; 72| 0| } else { 73| 0| return nil; 74| 0| } 75| 0|} 76| | 77| |@end 78| | 79| | 80| |@interface DDContextDenylistFilterLogFormatter () { 81| | DDLoggingContextSet *_contextSet; 82| |} 83| |@end 84| | 85| |@implementation DDContextDenylistFilterLogFormatter 86| | 87| 0|- (instancetype)init { 88| 0| if ((self = [super init])) { 89| 0| _contextSet = [[DDLoggingContextSet alloc] init]; 90| 0| } 91| 0| return self; 92| 0|} 93| | 94| 0|- (void)addToDenylist:(NSInteger)loggingContext { 95| 0| [_contextSet addToSet:loggingContext]; 96| 0|} 97| | 98| 0|- (void)removeFromDenylist:(NSInteger)loggingContext { 99| 0| [_contextSet removeFromSet:loggingContext]; 100| 0|} 101| | 102| 0|- (NSArray *)denylist { 103| 0| return [_contextSet currentSet]; 104| 0|} 105| | 106| 0|- (BOOL)isOnDenylist:(NSInteger)loggingContext { 107| 0| return [_contextSet isInSet:loggingContext]; 108| 0|} 109| | 110| 0|- (NSString *)formatLogMessage:(DDLogMessage *)logMessage { 111| 0| if ([self isOnDenylist:logMessage->_context]) { 112| 0| return nil; 113| 0| } else { 114| 0| return logMessage->_message; 115| 0| } 116| 0|} 117| | 118| |@end 119| | 120| |//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 121| |#pragma mark - 122| |//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 123| | 124| |@interface DDLoggingContextSet () { 125| | pthread_mutex_t _mutex; 126| | NSMutableSet *_set; 127| |} 128| |@end 129| | 130| |@implementation DDLoggingContextSet 131| | 132| 0|- (instancetype)init { 133| 0| if ((self = [super init])) { 134| 0| _set = [[NSMutableSet alloc] init]; 135| 0| pthread_mutex_init(&_mutex, NULL); 136| 0| } 137| 0| 138| 0| return self; 139| 0|} 140| | 141| 0|- (void)dealloc { 142| 0| pthread_mutex_destroy(&_mutex); 143| 0|} 144| | 145| 0|- (void)addToSet:(NSInteger)loggingContext { 146| 0| pthread_mutex_lock(&_mutex); 147| 0| { 148| 0| [_set addObject:@(loggingContext)]; 149| 0| } 150| 0| pthread_mutex_unlock(&_mutex); 151| 0|} 152| | 153| 0|- (void)removeFromSet:(NSInteger)loggingContext { 154| 0| pthread_mutex_lock(&_mutex); 155| 0| { 156| 0| [_set removeObject:@(loggingContext)]; 157| 0| } 158| 0| pthread_mutex_unlock(&_mutex); 159| 0|} 160| | 161| 0|- (NSArray *)currentSet { 162| 0| NSArray *result = nil; 163| 0| 164| 0| pthread_mutex_lock(&_mutex); 165| 0| { 166| 0| result = [_set allObjects]; 167| 0| } 168| 0| pthread_mutex_unlock(&_mutex); 169| 0| 170| 0| return result; 171| 0|} 172| | 173| 0|- (BOOL)isInSet:(NSInteger)loggingContext { 174| 0| BOOL result = NO; 175| 0| 176| 0| pthread_mutex_lock(&_mutex); 177| 0| { 178| 0| result = [_set containsObject:@(loggingContext)]; 179| 0| } 180| 0| pthread_mutex_unlock(&_mutex); 181| 0| 182| 0| return result; 183| 0|} 184| | 185| |@end /Users/runner/work/CocoaLumberjack/CocoaLumberjack/Sources/CocoaLumberjack/Extensions/DDDispatchQueueLogFormatter.m: 1| |// Software License Agreement (BSD License) 2| |// 3| |// Copyright (c) 2010-2021, Deusty, LLC 4| |// All rights reserved. 5| |// 6| |// Redistribution and use of this software in source and binary forms, 7| |// with or without modification, are permitted provided that the following conditions are met: 8| |// 9| |// * Redistributions of source code must retain the above copyright notice, 10| |// this list of conditions and the following disclaimer. 11| |// 12| |// * Neither the name of Deusty nor the names of its contributors may be used 13| |// to endorse or promote products derived from this software without specific 14| |// prior written permission of Deusty, LLC. 15| | 16| |#if !__has_feature(objc_arc) 17| |#error This file must be compiled with ARC. Use -fobjc-arc flag (or convert project to ARC). 18| |#endif 19| | 20| |#import 21| |#import 22| |#import 23| | 24| |#import 25| | 26| |DDQualityOfServiceName const DDQualityOfServiceUserInteractive = @"UI"; 27| |DDQualityOfServiceName const DDQualityOfServiceUserInitiated = @"IN"; 28| |DDQualityOfServiceName const DDQualityOfServiceDefault = @"DF"; 29| |DDQualityOfServiceName const DDQualityOfServiceUtility = @"UT"; 30| |DDQualityOfServiceName const DDQualityOfServiceBackground = @"BG"; 31| |DDQualityOfServiceName const DDQualityOfServiceUnspecified = @"UN"; 32| | 33| 0|static DDQualityOfServiceName _qos_name(NSUInteger qos) { 34| 0| switch ((qos_class_t) qos) { 35| 0| case QOS_CLASS_USER_INTERACTIVE: return DDQualityOfServiceUserInteractive; 36| 0| case QOS_CLASS_USER_INITIATED: return DDQualityOfServiceUserInitiated; 37| 0| case QOS_CLASS_DEFAULT: return DDQualityOfServiceDefault; 38| 0| case QOS_CLASS_UTILITY: return DDQualityOfServiceUtility; 39| 0| case QOS_CLASS_BACKGROUND: return DDQualityOfServiceBackground; 40| 0| default: return DDQualityOfServiceUnspecified; 41| 0| } 42| 0|} 43| | 44| |#pragma mark - DDDispatchQueueLogFormatter 45| | 46| |@interface DDDispatchQueueLogFormatter () { 47| | NSDateFormatter *_dateFormatter; // Use [self stringFromDate] 48| | 49| | pthread_mutex_t _mutex; 50| | 51| | NSUInteger _minQueueLength; // _prefix == Only access via atomic property 52| | NSUInteger _maxQueueLength; // _prefix == Only access via atomic property 53| | NSMutableDictionary *_replacements; // _prefix == Only access from within spinlock 54| |} 55| |@end 56| | 57| | 58| |@implementation DDDispatchQueueLogFormatter 59| | 60| 0|- (instancetype)init { 61| 0| if ((self = [super init])) { 62| 0| _dateFormatter = [self createDateFormatter]; 63| 0| 64| 0| pthread_mutex_init(&_mutex, NULL); 65| 0| _replacements = [[NSMutableDictionary alloc] init]; 66| 0| 67| 0| // Set default replacements: 68| 0| _replacements[@"com.apple.main-thread"] = @"main"; 69| 0| } 70| 0| 71| 0| return self; 72| 0|} 73| | 74| 0|- (instancetype)initWithMode:(DDDispatchQueueLogFormatterMode)mode { 75| 0| return [self init]; 76| 0|} 77| | 78| 0|- (void)dealloc { 79| 0| pthread_mutex_destroy(&_mutex); 80| 0|} 81| | 82| |//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 83| |#pragma mark Configuration 84| |//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 85| | 86| |@synthesize minQueueLength = _minQueueLength; 87| |@synthesize maxQueueLength = _maxQueueLength; 88| | 89| 0|- (NSString *)replacementStringForQueueLabel:(NSString *)longLabel { 90| 0| NSString *result = nil; 91| 0| 92| 0| pthread_mutex_lock(&_mutex); 93| 0| { 94| 0| result = _replacements[longLabel]; 95| 0| } 96| 0| pthread_mutex_unlock(&_mutex); 97| 0| 98| 0| return result; 99| 0|} 100| | 101| 0|- (void)setReplacementString:(NSString *)shortLabel forQueueLabel:(NSString *)longLabel { 102| 0| pthread_mutex_lock(&_mutex); 103| 0| { 104| 0| if (shortLabel) { 105| 0| _replacements[longLabel] = shortLabel; 106| 0| } else { 107| 0| [_replacements removeObjectForKey:longLabel]; 108| 0| } 109| 0| } 110| 0| pthread_mutex_unlock(&_mutex); 111| 0|} 112| | 113| |//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 114| |#pragma mark DDLogFormatter 115| |//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// 116| | 117| 0|- (NSDateFormatter *)createDateFormatter { 118| 0| NSDateFormatter *formatter = [[NSDateFormatter alloc] init]; 119| 0| [self configureDateFormatter:formatter]; 120| 0| return formatter; 121| 0|} 122| | 123| 0|- (void)configureDateFormatter:(NSDateFormatter *)dateFormatter { 124| 0| [dateFormatter setFormatterBehavior:NSDateFormatterBehavior10_4]; 125| 0| [dateFormatter setDateFormat:@"yyyy-MM-dd HH:mm:ss:SSS"]; 126| 0| [dateFormatter setLocale:[NSLocale localeWithLocaleIdentifier:@"en_US_POSIX"]]; 127| 0| [dateFormatter setCalendar:[[NSCalendar alloc] initWithCalendarIdentifier:NSCalendarIdentifierGregorian]]; 128| 0|} 129| | 130| 0|- (NSString *)stringFromDate:(NSDate *)date { 131| 0| return [_dateFormatter stringFromDate:date]; 132| 0|} 133| | 134| 0|- (NSString *)queueThreadLabelForLogMessage:(DDLogMessage *)logMessage { 135| 0| // As per the DDLogFormatter contract, this method is always invoked on the same thread/dispatch_queue 136| 0| 137| 0| NSUInteger minQueueLength = self.minQueueLength; 138| 0| NSUInteger maxQueueLength = self.maxQueueLength; 139| 0| 140| 0| // Get the name of the queue, thread, or machID (whichever we are to use). 141| 0| 142| 0| NSString *queueThreadLabel = nil; 143| 0| 144| 0| BOOL useQueueLabel = YES; 145| 0| BOOL useThreadName = NO; 146| 0| 147| 0| if (logMessage->_queueLabel) { 148| 0| // If you manually create a thread, it's dispatch_queue will have one of the thread names below. 149| 0| // Since all such threads have the same name, we'd prefer to use the threadName or the machThreadID. 150| 0| 151| 0| NSArray *names = @[ 152| 0| @"com.apple.root.low-priority", 153| 0| @"com.apple.root.default-priority", 154| 0| @"com.apple.root.high-priority", 155| 0| @"com.apple.root.low-overcommit-priority", 156| 0| @"com.apple.root.default-overcommit-priority", 157| 0| @"com.apple.root.high-overcommit-priority", 158| 0| @"com.apple.root.default-qos.overcommit" 159| 0| ]; 160| 0| 161| 0| for (NSString * name in names) { 162| 0| if ([logMessage->_queueLabel isEqualToString:name]) { 163| 0| useQueueLabel = NO; 164| 0| useThreadName = [logMessage->_threadName length] > 0; 165| 0| break; 166| 0| } 167| 0| } 168| 0| } else { 169| 0| useQueueLabel = NO; 170| 0| useThreadName = [logMessage->_threadName length] > 0; 171| 0| } 172| 0| 173| 0| if (useQueueLabel || useThreadName) { 174| 0| NSString *fullLabel; 175| 0| NSString *abrvLabel; 176| 0| 177| 0| if (useQueueLabel) { 178| 0| fullLabel = logMessage->_queueLabel; 179| 0| } else { 180| 0| fullLabel = logMessage->_threadName; 181| 0| } 182| 0| 183| 0| pthread_mutex_lock(&_mutex); 184| 0| { 185| 0| abrvLabel = _replacements[fullLabel]; 186| 0| } 187| 0| pthread_mutex_unlock(&_mutex); 188| 0| 189| 0| if (abrvLabel) { 190| 0| queueThreadLabel = abrvLabel; 191| 0| } else { 192| 0| queueThreadLabel = fullLabel; 193| 0| } 194| 0| } else { 195| 0| queueThreadLabel = logMessage->_threadID; 196| 0| } 197| 0| 198| 0| // Now use the thread label in the output 199| 0| 200| 0| NSUInteger labelLength = [queueThreadLabel length]; 201| 0| 202| 0| // labelLength > maxQueueLength : truncate 203| 0| // labelLength < minQueueLength : padding 204| 0| // : exact 205| 0| 206| 0| if ((maxQueueLength > 0) && (labelLength > maxQueueLength)) { 207| 0| // Truncate 208| 0| 209| 0| return [queueThreadLabel substringToIndex:maxQueueLength]; 210| 0| } else if (labelLength < minQueueLength) { 211| 0| // Padding 212| 0| 213| 0| NSUInteger numSpaces = minQueueLength - labelLength; 214| 0| 215| 0| char spaces[numSpaces + 1]; 216| 0| memset(spaces, ' ', numSpaces); 217| 0| spaces[numSpaces] = '\0'; 218| 0| 219| 0| return [NSString stringWithFormat:@"%@%s", queueThreadLabel, spaces]; 220| 0| } else { 221| 0| // Exact 222| 0| 223| 0| return queueThreadLabel; 224| 0| } 225| 0|} 226| | 227| 0|- (NSString *)formatLogMessage:(DDLogMessage *)logMessage { 228| 0| NSString *timestamp = [self stringFromDate:(logMessage->_timestamp)]; 229| 0| NSString *queueThreadLabel = [self queueThreadLabelForLogMessage:logMessage]; 230| 0| 231| 0| return [NSString stringWithFormat:@"%@ [%@ (QOS:%@)] %@", timestamp, queueThreadLabel, _qos_name(logMessage->_qos), logMessage->_message]; 232| 0|} 233| | 234| |@end 235| | 236| |#pragma mark - DDAtomicCounter 237| | 238| |@interface DDAtomicCounter() { 239| | atomic_int_fast32_t _value; 240| |} 241| |@end 242| | 243| |#pragma clang diagnostic push 244| |#pragma clang diagnostic ignored "-Wdeprecated-implementations" 245| |@implementation DDAtomicCounter 246| |#pragma clang diagnostic pop 247| | 248| 0|- (instancetype)initWithDefaultValue:(int32_t)defaultValue { 249| 0| if ((self = [super init])) { 250| 0| atomic_init(&_value, defaultValue); 251| 0| } 252| 0| return self; 253| 0|} 254| | 255| 0|- (int32_t)value { 256| 0| return atomic_load_explicit(&_value, memory_order_relaxed); 257| 0|} 258| | 259| 0|- (int32_t)increment { 260| 0| int32_t old = atomic_fetch_add_explicit(&_value, 1, memory_order_relaxed); 261| 0| return (old + 1); 262| 0|} 263| | 264| 0|- (int32_t)decrement { 265| 0| int32_t old = atomic_fetch_sub_explicit(&_value, 1, memory_order_relaxed); 266| 0| return (old - 1); 267| 0|} 268| | 269| |@end /Users/runner/work/CocoaLumberjack/CocoaLumberjack/Sources/CocoaLumberjack/Extensions/DDFileLogger+Buffering.m: 1| |// Software License Agreement (BSD License) 2| |// 3| |// Copyright (c) 2010-2021, Deusty, LLC 4| |// All rights reserved. 5| |// 6| |// Redistribution and use of this software in source and binary forms, 7| |// with or without modification, are permitted provided that the following conditions are met: 8| |// 9| |// * Redistributions of source code must retain the above copyright notice, 10| |// this list of conditions and the following disclaimer. 11| |// 12| |// * Neither the name of Deusty nor the names of its contributors may be used 13| |// to endorse or promote products derived from this software without specific 14| |// prior written permission of Deusty, LLC. 15| | 16| |#import 17| | 18| |#import 19| |#import "../DDFileLogger+Internal.h" 20| | 21| |static const NSUInteger kDDDefaultBufferSize = 4096; // 4 kB, block f_bsize on iphone7 22| |static const NSUInteger kDDMaxBufferSize = 1048576; // ~1 mB, f_iosize on iphone7 23| | 24| |// Reads attributes from base file system to determine buffer size. 25| |// see statfs in sys/mount.h for descriptions of f_iosize and f_bsize. 26| |// f_bsize == "default", and f_iosize == "max" 27| 0|static inline NSUInteger p_DDGetDefaultBufferSizeBytesMax(const BOOL max) { 28| 0| struct statfs *mountedFileSystems = NULL; 29| 0| int count = getmntinfo(&mountedFileSystems, 0); 30| 0| 31| 0| for (int i = 0; i < count; i++) { 32| 0| struct statfs mounted = mountedFileSystems[i]; 33| 0| const char *name = mounted.f_mntonname; 34| 0| 35| 0| // We can use 2 as max here, since any length > 1 will fail the if-statement. 36| 0| if (strnlen(name, 2) == 1 && *name == '/') { 37| 0| return max ? (NSUInteger)mounted.f_iosize : (NSUInteger)mounted.f_bsize; 38| 0| } 39| 0| } 40| 0| 41| 0| return max ? kDDMaxBufferSize : kDDDefaultBufferSize; 42| 0|} 43| | 44| 0|static NSUInteger DDGetMaxBufferSizeBytes() { 45| 0| static NSUInteger maxBufferSize = 0; 46| 0| static dispatch_once_t onceToken; 47| 0| dispatch_once(&onceToken, ^{ 48| 0| maxBufferSize = p_DDGetDefaultBufferSizeBytesMax(YES); 49| 0| }); 50| 0| return maxBufferSize; 51| 0|} 52| | 53| 0|static NSUInteger DDGetDefaultBufferSizeBytes() { 54| 0| static NSUInteger defaultBufferSize = 0; 55| 0| static dispatch_once_t onceToken; 56| 0| dispatch_once(&onceToken, ^{ 57| 0| defaultBufferSize = p_DDGetDefaultBufferSizeBytesMax(NO); 58| 0| }); 59| 0| return defaultBufferSize; 60| 0|} 61| | 62| |@interface DDBufferedProxy : NSProxy 63| | 64| |@property (nonatomic) DDFileLogger *fileLogger; 65| |@property (nonatomic) NSOutputStream *buffer; 66| | 67| |@property (nonatomic) NSUInteger maxBufferSizeBytes; 68| |@property (nonatomic) NSUInteger currentBufferSizeBytes; 69| | 70| |@end 71| | 72| |@implementation DDBufferedProxy 73| | 74| 0|- (instancetype)initWithFileLogger:(DDFileLogger *)fileLogger { 75| 0| _fileLogger = fileLogger; 76| 0| _maxBufferSizeBytes = DDGetDefaultBufferSizeBytes(); 77| 0| [self flushBuffer]; 78| 0| 79| 0| return self; 80| 0|} 81| | 82| 0|- (void)dealloc { 83| 0| dispatch_block_t block = ^{ 84| 0| [self lt_sendBufferedDataToFileLogger]; 85| 0| self.fileLogger = nil; 86| 0| }; 87| 0| 88| 0| if ([self->_fileLogger isOnInternalLoggerQueue]) { 89| 0| block(); 90| 0| } else { 91| 0| dispatch_sync(self->_fileLogger.loggerQueue, block); 92| 0| } 93| 0|} 94| | 95| |#pragma mark - Buffering 96| | 97| 0|- (void)flushBuffer { 98| 0| [_buffer close]; 99| 0| _buffer = [NSOutputStream outputStreamToMemory]; 100| 0| [_buffer open]; 101| 0| _currentBufferSizeBytes = 0; 102| 0|} 103| | 104| 0|- (void)lt_sendBufferedDataToFileLogger { 105| 0| NSData *data = [_buffer propertyForKey:NSStreamDataWrittenToMemoryStreamKey]; 106| 0| [_fileLogger lt_logData:data]; 107| 0| [self flushBuffer]; 108| 0|} 109| | 110| |#pragma mark - Logging 111| | 112| 0|- (void)logMessage:(DDLogMessage *)logMessage { 113| 0| // Don't need to check for isOnInternalLoggerQueue, -lt_dataForMessage: will do it for us. 114| 0| NSData *data = [_fileLogger lt_dataForMessage:logMessage]; 115| 0| 116| 0| if (data.length == 0) { 117| 0| return; 118| 0| } 119| 0| 120| 0| [data enumerateByteRangesUsingBlock:^(const void * __nonnull bytes, NSRange byteRange, BOOL * __nonnull __unused stop) { 121| 0| NSUInteger bytesLength = byteRange.length; 122| |#ifdef NS_BLOCK_ASSERTIONS 123| | __unused 124| |#endif 125| | NSInteger written = [_buffer write:bytes maxLength:bytesLength]; 126| 0| NSAssert(written > 0 && (NSUInteger)written == bytesLength, @"Failed to write to memory buffer."); 127| 0| 128| 0| _currentBufferSizeBytes += bytesLength; 129| 0| 130| 0| if (_currentBufferSizeBytes >= _maxBufferSizeBytes) { 131| 0| [self lt_sendBufferedDataToFileLogger]; 132| 0| } 133| 0| }]; 134| 0|} 135| | 136| 0|- (void)flush { 137| 0| // This method is public. 138| 0| // We need to execute the rolling on our logging thread/queue. 139| 0| 140| 0| dispatch_block_t block = ^{ 141| 0| @autoreleasepool { 142| 0| [self lt_sendBufferedDataToFileLogger]; 143| 0| [self.fileLogger flush]; 144| 0| } 145| 0| }; 146| 0| 147| 0| // The design of this method is taken from the DDAbstractLogger implementation. 148| 0| // For extensive documentation please refer to the DDAbstractLogger implementation. 149| 0| 150| 0| if ([self.fileLogger isOnInternalLoggerQueue]) { 151| 0| block(); 152| 0| } else { 153| 0| dispatch_queue_t globalLoggingQueue = [DDLog loggingQueue]; 154| 0| NSAssert(![self.fileLogger isOnGlobalLoggingQueue], @"Core architecture requirement failure"); 155| 0| 156| 0| dispatch_sync(globalLoggingQueue, ^{ 157| 0| dispatch_sync(self.fileLogger.loggerQueue, block); 158| 0| }); 159| 0| } 160| 0|} 161| | 162| |#pragma mark - Properties 163| | 164| 0|- (void)setMaxBufferSizeBytes:(NSUInteger)newBufferSizeBytes { 165| 0| _maxBufferSizeBytes = MIN(newBufferSizeBytes, DDGetMaxBufferSizeBytes()); 166| 0|} 167| | 168| |#pragma mark - Wrapping 169| | 170| 0|- (DDFileLogger *)wrapWithBuffer { 171| 0| return (DDFileLogger *)self; 172| 0|} 173| | 174| 0|- (DDFileLogger *)unwrapFromBuffer { 175| 0| return (DDFileLogger *)self.fileLogger; 176| 0|} 177| | 178| |#pragma mark - NSProxy 179| | 180| 0|- (NSMethodSignature *)methodSignatureForSelector:(SEL)sel { 181| 0| return [self.fileLogger methodSignatureForSelector:sel]; 182| 0|} 183| | 184| 0|- (BOOL)respondsToSelector:(SEL)aSelector { 185| 0| return [self.fileLogger respondsToSelector:aSelector]; 186| 0|} 187| | 188| 0|- (void)forwardInvocation:(NSInvocation *)invocation { 189| 0| [invocation invokeWithTarget:self.fileLogger]; 190| 0|} 191| | 192| |@end 193| | 194| |@implementation DDFileLogger (Buffering) 195| | 196| 0|- (instancetype)wrapWithBuffer { 197| 0| return (DDFileLogger *)[[DDBufferedProxy alloc] initWithFileLogger:self]; 198| 0|} 199| | 200| 0|- (instancetype)unwrapFromBuffer { 201| 0| return self; 202| 0|} 203| | 204| |@end /Users/runner/work/CocoaLumberjack/CocoaLumberjack/Sources/CocoaLumberjack/Extensions/DDMultiFormatter.m: 1| |// Software License Agreement (BSD License) 2| |// 3| |// Copyright (c) 2010-2021, Deusty, LLC 4| |// All rights reserved. 5| |// 6| |// Redistribution and use of this software in source and binary forms, 7| |// with or without modification, are permitted provided that the following conditions are met: 8| |// 9| |// * Redistributions of source code must retain the above copyright notice, 10| |// this list of conditions and the following disclaimer. 11| |// 12| |// * Neither the name of Deusty nor the names of its contributors may be used 13| |// to endorse or promote products derived from this software without specific 14| |// prior written permission of Deusty, LLC. 15| | 16| |#if !__has_feature(objc_arc) 17| |#error This file must be compiled with ARC. Use -fobjc-arc flag (or convert project to ARC). 18| |#endif 19| | 20| |#import 21| | 22| |@interface DDMultiFormatter () { 23| | dispatch_queue_t _queue; 24| | NSMutableArray *_formatters; 25| |} 26| | 27| |- (DDLogMessage *)logMessageForLine:(NSString *)line originalMessage:(DDLogMessage *)message; 28| | 29| |@end 30| | 31| | 32| |@implementation DDMultiFormatter 33| | 34| 0|- (instancetype)init { 35| 0| self = [super init]; 36| 0| 37| 0| if (self) { 38| 0| _queue = dispatch_queue_create("cocoa.lumberjack.multiformatter", DISPATCH_QUEUE_CONCURRENT); 39| 0| _formatters = [NSMutableArray new]; 40| 0| } 41| 0| 42| 0| return self; 43| 0|} 44| | 45| |#pragma mark Processing 46| | 47| 0|- (NSString *)formatLogMessage:(DDLogMessage *)logMessage { 48| 0| __block NSString *line = logMessage->_message; 49| 0| 50| 0| dispatch_sync(_queue, ^{ 51| 0| for (id formatter in self->_formatters) { 52| 0| DDLogMessage *message = [self logMessageForLine:line originalMessage:logMessage]; 53| 0| line = [formatter formatLogMessage:message]; 54| 0| 55| 0| if (!line) { 56| 0| break; 57| 0| } 58| 0| } 59| 0| }); 60| 0| 61| 0| return line; 62| 0|} 63| | 64| 0|- (DDLogMessage *)logMessageForLine:(NSString *)line originalMessage:(DDLogMessage *)message { 65| 0| DDLogMessage *newMessage = [message copy]; 66| 0| 67| 0| newMessage->_message = line; 68| 0| return newMessage; 69| 0|} 70| | 71| |#pragma mark Formatters 72| | 73| 0|- (NSArray *)formatters { 74| 0| __block NSArray *formatters; 75| 0| 76| 0| dispatch_sync(_queue, ^{ 77| 0| formatters = [self->_formatters copy]; 78| 0| }); 79| 0| 80| 0| return formatters; 81| 0|} 82| | 83| 0|- (void)addFormatter:(id)formatter { 84| 0| dispatch_barrier_async(_queue, ^{ 85| 0| [self->_formatters addObject:formatter]; 86| 0| }); 87| 0|} 88| | 89| 0|- (void)removeFormatter:(id)formatter { 90| 0| dispatch_barrier_async(_queue, ^{ 91| 0| [self->_formatters removeObject:formatter]; 92| 0| }); 93| 0|} 94| | 95| 0|- (void)removeAllFormatters { 96| 0| dispatch_barrier_async(_queue, ^{ 97| 0| [self->_formatters removeAllObjects]; 98| 0| }); 99| 0|} 100| | 101| 0|- (BOOL)isFormattingWithFormatter:(id)formatter { 102| 0| __block BOOL hasFormatter; 103| 0| 104| 0| dispatch_sync(_queue, ^{ 105| 0| hasFormatter = [self->_formatters containsObject:formatter]; 106| 0| }); 107| 0| 108| 0| return hasFormatter; 109| 0|} 110| | 111| |@end /Users/runner/work/CocoaLumberjack/CocoaLumberjack/Sources/CocoaLumberjack/include/CocoaLumberjack/DDTTYLogger.h: 1| |// Software License Agreement (BSD License) 2| |// 3| |// Copyright (c) 2010-2021, Deusty, LLC 4| |// All rights reserved. 5| |// 6| |// Redistribution and use of this software in source and binary forms, 7| |// with or without modification, are permitted provided that the following conditions are met: 8| |// 9| |// * Redistributions of source code must retain the above copyright notice, 10| |// this list of conditions and the following disclaimer. 11| |// 12| |// * Neither the name of Deusty nor the names of its contributors may be used 13| |// to endorse or promote products derived from this software without specific 14| |// prior written permission of Deusty, LLC. 15| | 16| |// Disable legacy macros 17| |#ifndef DD_LEGACY_MACROS 18| | #define DD_LEGACY_MACROS 0 19| |#endif 20| | 21| |#import 22| | 23| 0|#define LOG_CONTEXT_ALL INT_MAX 24| | 25| |#pragma clang diagnostic push 26| |#pragma clang diagnostic ignored "-Wunused-function" 27| |#if !(TARGET_OS_OSX) 28| | // iOS or tvOS or watchOS 29| | #import 30| | typedef UIColor DDColor; 31| 0| static inline DDColor* _Nonnull DDMakeColor(CGFloat r, CGFloat g, CGFloat b) {return [DDColor colorWithRed:(r/255.0f) green:(g/255.0f) blue:(b/255.0f) alpha:1.0f];} 32| |#elif defined(DD_CLI) || !__has_include() 33| | // OS X CLI 34| | #import 35| | typedef CLIColor DDColor; 36| | static inline DDColor* _Nonnull DDMakeColor(CGFloat r, CGFloat g, CGFloat b) {return [DDColor colorWithCalibratedRed:(r/255.0f) green:(g/255.0f) blue:(b/255.0f) alpha:1.0f];} 37| |#else 38| | // OS X with AppKit 39| | #import 40| | typedef NSColor DDColor; 41| | static inline DDColor * _Nonnull DDMakeColor(CGFloat r, CGFloat g, CGFloat b) {return [DDColor colorWithCalibratedRed:(r/255.0f) green:(g/255.0f) blue:(b/255.0f) alpha:1.0f];} 42| |#endif 43| |#pragma clang diagnostic pop 44| | 45| |NS_ASSUME_NONNULL_BEGIN 46| | 47| |/** 48| | * This class provides a logger for Terminal output or Xcode console output, 49| | * depending on where you are running your code. 50| | * 51| | * As described in the "Getting Started" page, 52| | * the traditional NSLog() function directs it's output to two places: 53| | * 54| | * - Apple System Log (so it shows up in Console.app) 55| | * - StdErr (if stderr is a TTY, so log statements show up in Xcode console) 56| | * 57| | * To duplicate NSLog() functionality you can simply add this logger and an asl logger. 58| | * However, if you instead choose to use file logging (for faster performance), 59| | * you may choose to use only a file logger and a tty logger. 60| | **/ 61| |@interface DDTTYLogger : DDAbstractLogger 62| | 63| |/** 64| | * Singleton instance. Returns `nil` if the initialization of the DDTTYLogger fails. 65| | */ 66| |@property (nonatomic, class, readonly, strong, nullable) DDTTYLogger *sharedInstance; 67| | 68| |/* Inherited from the DDLogger protocol: 69| | * 70| | * Formatters may optionally be added to any logger. 71| | * 72| | * If no formatter is set, the logger simply logs the message as it is given in logMessage, 73| | * or it may use its own built in formatting style. 74| | * 75| | * More information about formatters can be found here: 76| | * Documentation/CustomFormatters.md 77| | * 78| | * The actual implementation of these methods is inherited from DDAbstractLogger. 79| | 80| | - (id )logFormatter; 81| | - (void)setLogFormatter:(id )formatter; 82| | 83| | */ 84| | 85| |/** 86| | * Want to use different colors for different log levels? 87| | * Enable this property. 88| | * 89| | * If you run the application via the Terminal (not Xcode), 90| | * the logger will map colors to xterm-256color or xterm-color (if available). 91| | * 92| | * Xcode does NOT natively support colors in the Xcode debugging console. 93| | * You'll need to install the XcodeColors plugin to see colors in the Xcode console. 94| | * https://github.com/robbiehanson/XcodeColors 95| | * 96| | * The default value is NO. 97| | **/ 98| |@property (readwrite, assign) BOOL colorsEnabled; 99| | 100| |/** 101| | * When using a custom formatter you can set the `logMessage` method not to append 102| | * `\n` character after each output. This allows for some greater flexibility with 103| | * custom formatters. Default value is YES. 104| | **/ 105| |@property (nonatomic, readwrite, assign) BOOL automaticallyAppendNewlineForCustomFormatters; 106| | 107| |/** 108| | Using this initializer is not supported. Please use `DDTTYLogger.sharedInstance`. 109| | **/ 110| |- (instancetype)init NS_UNAVAILABLE; 111| | 112| |/** 113| | * The default color set (foregroundColor, backgroundColor) is: 114| | * 115| | * - DDLogFlagError = (red, nil) 116| | * - DDLogFlagWarning = (orange, nil) 117| | * 118| | * You can customize the colors however you see fit. 119| | * Please note that you are passing a flag, NOT a level. 120| | * 121| | * GOOD : [ttyLogger setForegroundColor:pink backgroundColor:nil forFlag:DDLogFlagInfo]; // <- Good :) 122| | * BAD : [ttyLogger setForegroundColor:pink backgroundColor:nil forFlag:DDLogLevelInfo]; // <- BAD! :( 123| | * 124| | * DDLogFlagInfo = 0...00100 125| | * DDLogLevelInfo = 0...00111 <- Would match DDLogFlagInfo and DDLogFlagWarning and DDLogFlagError 126| | * 127| | * If you run the application within Xcode, then the XcodeColors plugin is required. 128| | * 129| | * If you run the application from a shell, then DDTTYLogger will automatically map the given color to 130| | * the closest available color. (xterm-256color or xterm-color which have 256 and 16 supported colors respectively.) 131| | * 132| | * This method invokes setForegroundColor:backgroundColor:forFlag:context: and applies it to `LOG_CONTEXT_ALL`. 133| | **/ 134| |- (void)setForegroundColor:(nullable DDColor *)txtColor backgroundColor:(nullable DDColor *)bgColor forFlag:(DDLogFlag)mask; 135| | 136| |/** 137| | * Just like setForegroundColor:backgroundColor:flag, but allows you to specify a particular logging context. 138| | * 139| | * A logging context is often used to identify log messages coming from a 3rd party framework, 140| | * although logging context's can be used for many different functions. 141| | * 142| | * Use LOG_CONTEXT_ALL to set the default color for all contexts that have no specific color set defined. 143| | * 144| | * Logging context's are explained in further detail here: 145| | * Documentation/CustomContext.md 146| | **/ 147| |- (void)setForegroundColor:(nullable DDColor *)txtColor backgroundColor:(nullable DDColor *)bgColor forFlag:(DDLogFlag)mask context:(NSInteger)ctxt; 148| | 149| |/** 150| | * Similar to the methods above, but allows you to map DDLogMessage->tag to a particular color profile. 151| | * For example, you could do something like this: 152| | * 153| | * static NSString *const PurpleTag = @"PurpleTag"; 154| | * 155| | * #define DDLogPurple(frmt, ...) LOG_OBJC_TAG_MACRO(NO, 0, 0, 0, PurpleTag, frmt, ##__VA_ARGS__) 156| | * 157| | * And then where you configure CocoaLumberjack: 158| | * 159| | * purple = DDMakeColor((64/255.0), (0/255.0), (128/255.0)); 160| | * 161| | * or any UIColor/NSColor constructor. 162| | * 163| | * Note: For CLI OS X projects that don't link with AppKit use CLIColor objects instead 164| | * 165| | * [[DDTTYLogger sharedInstance] setForegroundColor:purple backgroundColor:nil forTag:PurpleTag]; 166| | * [DDLog addLogger:[DDTTYLogger sharedInstance]]; 167| | * 168| | * This would essentially give you a straight NSLog replacement that prints in purple: 169| | * 170| | * DDLogPurple(@"I'm a purple log message!"); 171| | **/ 172| |- (void)setForegroundColor:(nullable DDColor *)txtColor backgroundColor:(nullable DDColor *)bgColor forTag:(id )tag; 173| | 174| |/** 175| | * Clearing color profiles. 176| | **/ 177| |- (void)clearColorsForFlag:(DDLogFlag)mask; 178| |- (void)clearColorsForFlag:(DDLogFlag)mask context:(NSInteger)context; 179| |- (void)clearColorsForTag:(id )tag; 180| |- (void)clearColorsForAllFlags; 181| |- (void)clearColorsForAllTags; 182| |- (void)clearAllColors; 183| | 184| |@end 185| | 186| |NS_ASSUME_NONNULL_END <<<<<< EOF # path=fixes ./Package@swift-5.1.swift:3,5 ./Integration/watchOSSwiftIntegration Extension/InterfaceController.swift:15,18,20,24,25,29,30,34,35 ./Integration/watchOSSwiftIntegration Extension/NotificationController.swift:15,19,21,25,27,28,32,33,37,38,43,44 ./Integration/watchOSSwiftIntegration Extension/ExtensionDelegate.swift:15,18,20,23,24,27,28,32,33,60,61,62,63 ./Integration/macOSSwiftIntegration/main.swift:15,18,23,24 ./Integration/Sources/ViewController.swift:15,18,20,27,28,30,34,35,39,47,48,54,56,58,64,70,72,73 ./Integration/Sources/AppDelegate.h:15,17,19,21 ./Integration/Sources/ViewController.m:15,18,20,23,25,29,30,33,38,39,44,50,51,56,57 ./Integration/Sources/main.m:15,18,22,23 ./Integration/Sources/Formatter.swift:15,18,21,26,28,29,32,47,48,50,51 ./Integration/Sources/AppDelegate.m:15,18,21,23,32,33 ./Integration/Sources/AppDelegate.swift:15,17,20,22,31,32 ./Integration/Sources/ViewController.h:15,17,19 ./Tests/CocoaLumberjackTests/DDSampleFileManager.h:15,18,20,22,24,26,28 ./Tests/CocoaLumberjackTests/DDOSLoggingTests.m:15,17,20,23,25,29,30,33,34,49,50,51 ./Tests/CocoaLumberjackTests/DDLogFileManagerTests.m:15,17,19,21,25,27,31,32,36,37,40,41,47,51,53,54,56 ./Tests/CocoaLumberjackTests/DDBasicLoggingTests.m:15,17,21,24,26,28,34,35,43,45,51,55,59,62,64,65,73,74,77,80,82,83,87,93,98,99,103,106,112,117,118,122,124,130,135,137,138,140,142,144,147,149,152,154,156,164,165,171,172,175,181,185,189,192,195,196,198,199,203,205,210,211,213,215,219,221,223,226,228,230,232,234,235,240,241,242,244,246,248,250,252,256,257,261,262,266,267,273,274,281,282 ./Tests/CocoaLumberjackTests/DDFileLoggerPerformanceTests.m:15,17,20,22,25,27,29,31,33,37,38,41,42,48,50,51,57,59,60,64,69,71,72,78,80,81,85,90,92,93,101,103,106,107,110,111,114,115,118,120,121,125,132,134,137,138,141,142,145,146,149,151,152,156,158,161,162,165,166,169,170,173,175,176,180,183,185,188,189,192,193,196,197,200,202,203 ./Tests/CocoaLumberjackTests/DDSMocking.m:15,17,20,22,26,28,29,32,33,36,37,39,41,46,48,49,52,53,57,58,61,62,65,66,69,70,73,74,77,78,80,86,88,93,94,97,98,102,103,107,108,115,116,126,130,131,134,135,136,139,140,143,144 ./Tests/CocoaLumberjackTests/DDAtomicCounterTests.m:15,17,19,22,24,35,36,44,55,58,59,64,65,73,79,84,93,96,97,102,103 ./Tests/CocoaLumberjackTests/DDFileLoggerTests.m:15,17,21,23,25,27,32,33,35,37,43,44,47,57,65,66,70,73,74,93,94,97,110,112,113,117,128,129,132,136,138,143,145,146,149,151,156,159,163,166,170,171,174,176,180,182,187,189,193,195,202,204,209,210,220,225,227,234,235,239,242,245,248,249,253,259,261,265,268,269,273,279,281,285,288,289 ./Tests/CocoaLumberjackTests/DDSampleFileManager.m:15,17,19,21,23,25,30,32,33,36,37,40,41,44,45 ./Tests/CocoaLumberjackTests/DDContextFilterLogFormatter+DeprecatedTests.m:15,18,30,31,37,39,43,44,48,49,54,59,64,65,68,69,75,77,81,82,86,87,92,97,102,103 ./Tests/CocoaLumberjackTests/DDContextFilterLogFormatterTests.m:15,18,30,31,35,37,41,42,46,47,52,57,62,63,65,66,70,72,76,77,81,82,87,92,97,98 ./Tests/CocoaLumberjackTests/DDLogTests.m:15,18,21,26,29,30,36,40,41,45,46,47,49,54,55,60,61,69,70,76,77,82,83,92,93 ./Tests/CocoaLumberjackTests/DDLogMessageTests.m:15,17,20,22,29,42,43,55,56,69,70,83,84,96,97,99,100,104,106,110,111,115,116,118,146,147,153,154,157,158,161,162,165,166,169,170,173,174,178,179,184,185,192,193,200,201,208,209,216,217,239,240 ./Tests/CocoaLumberjackTests/DDSMocking.h:15,17,19,22,24,29,35 ./Tests/CocoaLumberjackSwiftLogBackendTests/DDLogHandlerTests.swift:15,20,25,26,28,32,33,34,37,39,43,44,48,49,75,76,99,100,112,113,133,136,139,155,171,172,173 ./Tests/CocoaLumberjackSwiftTests/DDLogCombineTests.swift:15,17,21,24,26,36,37,41,42,47,48,53,57,58,63,67,68,72,76,78,81,82,85,89,95,97,104,111,112,115,119,125,127,130,133,134,137,139,144,155,166,169,170,179,181,183,184,185 ./Package@swift-5.0.swift:3,5 ./Package@swift-5.2.swift:3,5 ./Package.swift:3,5 ./Sources/CocoaLumberjackSwiftSupport/include/CocoaLumberjackSwiftSupport/SwiftLogLevel.h:15,18,20,25,27 ./Sources/CocoaLumberjack/DDFileLogger+Internal.h:15,17,19,21,23,26,28,30 ./Sources/CocoaLumberjack/DDTTYLogger.m:15,19,21,23,30,34,40,64,66,70,78,80,86,91,95,99,102,105,108,111,114,115,117,119,123,128,132,136,137,139,140,142,146,150,152,162,163,165,188,207,208,210,232,234,238,258,261,263,266,267,277,278,282,284,308,317,324,331,338,345,352,359,366,373,380,387,394,401,408,415,422,429,436,443,450,457,464,471,478,485,492,499,506,513,520,527,534,541,548,555,562,564,571,578,585,593,596,597,599,601,605,607,608,610,629,631,635,639,641,645,648,651,654,658,660,661,662,663,665,669,674,678,680,681,683,687,690,691,694,696,698,701,702,706,708,711,714,717,718,721,722,725,726,729,730,732,734,736,738,740,742,745,746,757,759,762,764,767,770,776,779,783,785,786,788,789,791,792,795,801,804,811,816,817,818,819,823,826,828,829,833,834,837,838,841,843,845,849,850,852,855,856,858,862,863,865,867,870,874,875,877,882,883,885,889,891,892,894,895,898,899,903,904,908,915,918,920,922,928,930,931,936,939,940,942,945,952,955,957,961,962,965,966,975,977,979,983,984,986,987,992,993,995,998,1004,1008,1009,1010,1013,1021,1023,1025,1027,1030,1036,1040,1041,1042,1045,1046,1051,1055,1056,1058,1059,1062,1063,1065,1068,1074,1078,1079,1080,1083,1087,1089,1092,1098,1102,1103,1104,1109,1111,1114,1120,1124,1125,1126,1131,1133,1136,1142,1146,1147,1148,1154,1156,1159,1165,1169,1170,1171,1175,1179,1180,1183,1185,1189,1190,1197,1200,1201,1205,1207,1208,1209,1210,1211,1212,1217,1220,1226,1229,1230,1235,1237,1238,1240,1245,1249,1252,1258,1261,1264,1265,1268,1272,1273,1277,1281,1290,1298,1300,1301,1309,1312,1314,1316,1318,1322,1325,1331,1334,1337,1338,1341,1344,1347,1350,1353,1356,1359,1362,1365,1368,1370,1371,1374,1375,1376,1377,1379,1381,1383,1388,1390,1393,1397,1398,1401,1405,1406,1409,1412,1414,1417,1420,1423,1424,1428,1430,1435,1438,1439,1442,1445,1447,1450,1453,1456,1457,1461,1463,1468,1471,1472,1480,1481,1482,1484,1485,1490,1491 ./Sources/CocoaLumberjack/DDOSLogger.m:15,17,19,23,24,28,30,32,35,37,47,49,50,52,55,56,59,63,65,66,68,72,74,75,79,81,82,84,87,88,93,94,113,114,115,116,117 ./Sources/CocoaLumberjack/include/CocoaLumberjack/DDLog.h:15,17,27,31,34,40,45,47,111,120,125,130,135,141,150,155,160,165,170,175,181,191,199,209,210,214,220,226,232,258,284,312,340,351,362,368,374,384,391,398,436,474,479,484,489,494,499,504,509,514,521,526,531,538,545,553,561,563,567,574,581,589,591,604,617,622,634,641,649,651,655,661,674,676,687,699,704,706,710,715,740,742,746,750,768,774,795,796,801,839,843,865,867,871,889,894,895,898,900,905,910,912,916,918,921,924,926 ./Sources/CocoaLumberjack/include/CocoaLumberjack/DDMultiFormatter.h:15,17,22,24,26,32,37,42,47,52,57,59 ./Sources/CocoaLumberjack/include/CocoaLumberjack/DDContextFilterLogFormatter+Deprecated.h:15,17,19,42,44,52,60,66,74,76,77,84,86,94,102,108,116,118 ./Sources/CocoaLumberjack/include/CocoaLumberjack/DDAbstractDatabaseLogger.h:15,20,22,24,33,40,47,48,71,76,104,109,114,119,124,126 ./Sources/CocoaLumberjack/include/CocoaLumberjack/DDLoggerNames.h:15,17,19,21,24,26,29 ./Sources/CocoaLumberjack/include/CocoaLumberjack/DDLogMacros.h:15,20,22,29,36,51,62,84,87,96 ./Sources/CocoaLumberjack/include/CocoaLumberjack/DDASLLogCapture.h:15,17,19,21,27,32,37,43,45 ./Sources/CocoaLumberjack/include/CocoaLumberjack/DDFileLogger.h:15,20,22,24,26,30,31,40,45,46,50,76,78,88,96,98,103,109,115,122,129,136,143,145,152,154,161,163,168,174,180,182,186,201,206,212,228,254,262,270,278,280,283,285,289,294,296,300,312,317,322,324,328,333,338,345,354,359,364,369,374,380,387,423,428,433,441,448,455,461,463,466,475,477,481,497,500,502,505,507,509,511,513,516,519,521,524,527,529 ./Sources/CocoaLumberjack/include/CocoaLumberjack/DDContextFilterLogFormatter.h:15,17,22,24,26,47,52,59,66,71,78,80,81,86,88,95,102,107,114,116 ./Sources/CocoaLumberjack/include/CocoaLumberjack/DDTTYLogger.h:15,20,22,24,44,46,62,67,79,82,84,99,106,111,135,148,173,183,185 ./Sources/CocoaLumberjack/include/CocoaLumberjack/DDAssertMacros.h:15,24,26 ./Sources/CocoaLumberjack/include/CocoaLumberjack/DDFileLogger+Buffering.h:15,17,19,21,24,26 ./Sources/CocoaLumberjack/include/CocoaLumberjack/DDDispatchQueueLogFormatter.h:15,17,22,24,26,43,67,74,113,119,126,146,166,177,182,184,189,194,199,204,209,211,213,216,221,223,227 ./Sources/CocoaLumberjack/include/CocoaLumberjack/DDOSLogger.h:15,17,22,24,26,32,39,42,46,52,54 ./Sources/CocoaLumberjack/include/CocoaLumberjack/DDLog+LOGV.h:15,20,22,29,36,52,74 ./Sources/CocoaLumberjack/include/CocoaLumberjack/CLIColor.h:15,17,19,22,24,29,39,49,51,53 ./Sources/CocoaLumberjack/include/CocoaLumberjack/DDASLLogger.h:15,17,22,24,26,29,32,48,55,57,60,62 ./Sources/CocoaLumberjack/DDASLLogger.m:15,17,19,23,25,27,30,35,38,39,41,42,47,50,54,56,57,61,62,66,68,69,71,72,75,76,81,82,84,87,98,99,101,104,111,116,124,126,128,129,130,132 ./Sources/CocoaLumberjack/CLI/CLIColor.m:15,17,19,21,24,25,27,28,30,38,39,43,46,49,52,53,54,56 ./Sources/CocoaLumberjack/Extensions/DDContextFilterLogFormatter.m:15,19,21,23,25,27,30,32,34,38,41,43,45,49,51,52,55,56,59,60,63,64,67,68,74,75,76,78,79,82,84,86,90,92,93,96,97,100,101,104,105,108,109,115,116,117,119,123,127,129,131,136,137,139,140,143,144,147,149,151,152,155,157,159,160,163,165,167,169,171,172,175,177,179,181,183,184 ./Sources/CocoaLumberjack/Extensions/DDDispatchQueueLogFormatter.m:15,19,23,25,32,41,42,43,45,48,50,54,56,57,59,63,66,69,70,72,73,76,77,80,81,85,88,91,93,95,97,99,100,103,108,109,111,112,116,121,122,128,129,132,133,136,139,141,143,146,150,160,166,167,171,172,176,181,182,184,186,188,193,196,197,199,201,205,208,212,214,218,222,224,225,226,230,232,233,235,237,240,242,247,251,253,254,257,258,262,263,267,268 ./Sources/CocoaLumberjack/Extensions/DDFileLogger+Buffering.m:15,17,20,23,30,34,38,39,40,42,43,51,52,60,61,63,66,69,71,73,78,80,81,87,92,93,94,96,102,103,108,109,111,115,118,119,127,129,132,134,135,139,144,146,149,155,159,160,161,163,166,167,169,172,173,176,177,179,182,183,186,187,190,191,193,195,198,199,202,203 ./Sources/CocoaLumberjack/Extensions/DDMultiFormatter.m:15,19,21,25,26,28,30,31,33,36,40,41,43,44,46,49,54,57,58,60,62,63,66,69,70,72,75,79,81,82,87,88,93,94,99,100,103,107,109,110 ./Sources/CocoaLumberjack/Extensions/DDContextFilterLogFormatter+Deprecated.m:15,17,19,22,23,26,27,30,31,34,35,37,38,40,43,44,47,48,51,52,55,56 ./Sources/CocoaLumberjack/DDLog.m:15,19,23,30,35,37,44,48,50,56,58,60,66,67,71,75,77,78,82,84,88,90,92,95,99,102,111,116,118,119,130,133,136,139,142,144,147,148,157,160,165,170,172,180,181,183,189,190,191,193,194,200,201,205,208,209,213,216,217,220,221,224,225,229,230,234,235,238,239,243,244,248,249,252,253,258,259,262,263,266,270,272,273,276,277,280,284,286,287,291,319,325,327,335,336,337,348,351,353,355,357,367,369,370,371,382,385,387,389,391,401,403,404,405,417,418,440,441,442,453,454,474,476,477,480,481,484,485,488,489,493,497,498,502,506,508,516,518,521,525,528,533,534,538,539,540,542,543,545,547,552,555,558,559,561,563,564,566,577,580,582,584,587,589,593,594,596,602,603,604,606,608,611,614,615,616,618,620,621,625,628,630,631,635,637,638,641,643,644,648,649,650,654,655,659,663,669,670,671,674,679,680,685,688,689,691,692,695,704,705,706,709,712,714,719,720,721,725,726,732,733,736,737,741,748,749,750,752,754,755,759,761,764,765,767,768,772,774,778,779,781,782,785,788,796,799,802,803,807,808,812,815,818,819,825,831,833,839,840,841,842,848,851,857,858,859,861,862,866,870,871,874,876,882,883,885,886,889,899,909,910,911,920,925,926,927,929,933,935,939,945,946,948,950,951,954,955,960,962,963,965,969,971,975,976,993,996,999,1010,1016,1018,1023,1025,1026,1030,1032,1033,1055,1056,1057,1074,1075,1078,1100,1102,1103,1107,1108,1110,1111,1115,1117,1121,1124,1125,1127,1141,1144,1146,1147,1149,1150,1153,1156,1157,1159,1160,1163,1164,1205,1214,1217,1219,1221,1227,1229,1230,1233,1236,1242,1243,1245,1250,1251,1252,1254,1258,1259,1262,1263,1266,1267,1270,1271,1274,1276,1277,1279,1283,1285,1290,1291,1293,1295,1300,1302,1303,1306,1307 ./Sources/CocoaLumberjack/Supporting Files/DDLegacyMacros.h:15,22,25,29,35,43,45,51,62,65,68,74 ./Sources/CocoaLumberjack/Supporting Files/CocoaLumberjack.h:15,59,61,64,67,72,75,79,82,85,90,97,100 ./Sources/CocoaLumberjack/DDLoggerNames.m:15,17 ./Sources/CocoaLumberjack/DDAbstractDatabaseLogger.m:15,19,21,23,30,32,34,36,43,44,46,47,51,52,56,62,64,65,68,69,72,73,76,77,81,88,89,90,93,97,98,99,103,105,106,107,111,115,122,126,127,128,134,135,136,141,143,151,156,157,158,159,160,164,168,170,171,172,180,181,182,187,192,193,195,196,197,201,206,208,215,216,217,218,222,226,233,236,238,240,246,248,249,255,260,263,264,265,267,270,276,280,281,282,286,293,296,298,300,306,308,309,315,318,331,338,347,349,352,354,355,356,358,361,367,371,372,373,377,384,387,389,391,397,399,400,406,410,412,426,428,432,437,441,442,445,450,451,452,453,455,458,464,468,469,470,474,481,484,486,488,494,496,497,503,506,519,526,534,536,539,541,542,543,545,548,554,558,559,560,564,571,574,576,578,584,586,587,592,595,601,605,606,607,611,616,618,623,624,625,630,632,637,638,639,643,646,648,650,651,654,656,659,660,664,670,671,672,673,679,681,682 ./Sources/CocoaLumberjack/DDFileLogger.m:15,19,21,23,30,34,40,41,45,50,52,56,65,66,68,70,73,76,77,82,87,92,93,96,97,99,100,104,111,112,113,115,116,118,124,126,127,133,134,135,141,142,143,152,153,155,159,168,171,174,177,181,185,186,187,188,194,195,196,202,205,209,210,211,212,215,218,225,226,227,228,229,233,239,250,252,253,257,259,267,268,270,271,274,278,280,281,285,286,290,292,295,303,306,309,311,313,314,315,317,318,321,323,326,327,329,330,333,335,338,340,341,343,344,347,349,352,353,355,356,359,361,364,365,367,368,374,384,385,395,396,399,400,401,405,409,412,414,415,418,419,422,425,426,429,430,432,433,436,442,443,447,454,455,459,462,465,466,467,469,472,484,486,502,503,506,507,511,515,518,521,522,525,527,529,530,532,536,539,540,542,544,547,548,559,560,561,563,564,567,569,570,572,576,579,582,584,587,589,591,592,594,599,603,604,607,608,613,617,620,621,623,624,627,633,637,641,642,646,647,651,652,653,661,662,663,667,670,674,677,684,687,689,693,695,696,702,704,707,714,717,719,723,724,727,731,734,741,744,746,750,752,753,759,761,764,771,774,776,780,781,785,788,792,793,796,797,801,806,808,813,820,824,826,831,832,835,836,840,844,849,850,852,855,861,865,866,867,871,874,875,881,885,889,891,893,899,913,915,916,920,921,925,926,927,930,936,937,938,941,944,947,956,959,960,963,965,966,967,968,972,975,982,983,999,1002,1003,1005,1007,1008,1020,1023,1028,1030,1034,1036,1037,1040,1043,1049,1050,1055,1064,1072,1075,1076,1078,1079,1083,1087,1088,1092,1108,1110,1111,1113,1114,1117,1118,1122,1127,1133,1140,1145,1146,1147,1150,1159,1162,1163,1167,1168,1169,1171,1172,1176,1178,1182,1185,1186,1188,1189,1191,1194,1195,1198,1199,1202,1203,1207,1211,1213,1216,1222,1226,1227,1228,1236,1239,1240,1241,1244,1245,1247,1249,1253,1257,1259,1262,1268,1272,1273,1274,1276,1281,1282,1284,1285,1289,1290,1291,1295,1301,1303,1306,1307,1316,1317,1324,1328,1332,1333,1341,1342,1345,1348,1351,1352,1353,1354,1355,1358,1361,1365,1366,1369,1370,1374,1375,1377,1378,1380,1384,1386,1390,1392,1395,1397,1398,1400,1402,1405,1407,1409,1410,1412,1414,1421,1423,1425,1429,1430,1435,1436,1438,1439,1443,1448,1451,1452,1453,1455,1456,1460,1461,1463,1464,1468,1469,1471,1472,1476,1477,1479,1480,1484,1485,1487,1488,1491,1492,1502,1503,1507,1510,1511,1517,1518,1519,1523,1529,1530,1534,1539,1547,1549,1553,1554,1556,1566,1568,1569,1572,1573,1574,1578,1580,1582,1587,1589,1597,1598,1601,1610,1612,1614,1617,1620,1621,1622,1624,1625,1628,1631,1632,1638,1640,1642,1645,1648,1649,1651,1653,1656,1662,1663,1664,1667,1668,1669,1671,1677,1679,1683,1687,1689,1693,1695,1697,1699,1700,1704,1706,1718,1719,1723,1725,1726,1730,1732,1738,1739,1743,1744,1748,1752,1754,1755,1757,1758,1761,1762,1767,1768,1773,1774,1776,1787,1789,1794,1795,1796,1798,1799 ./Sources/CocoaLumberjack/DDASLLogCapture.m:15,17,19,24,26,31,34,39,44,45,47,51,52,55,56,59,60,63,64,66,69,71,74,81,82,87,90,103,104,107,108,111,117,119,130,132,133,136,142,149,156,162,165,168,175,176,178,182,184,186,189,192,196,197,198,200,201,202,204 ./Sources/CocoaLumberjackSwiftLogBackend/DDLogHandler.swift:15,18,28,29,30,31,41,42,53,54,59,60,66,67,68,79,106,107,110,111,112,123,128,129,138,139,144,149,154,155,160,161,165,166,183,184,185,204,205,206,211,212,232,233,234,235,253,254 ./Sources/CocoaLumberjackSwift/DDAssert.swift:15,20,36,37,38,50 ./Sources/CocoaLumberjackSwift/CocoaLumberjack.swift:15,20,24,25,28,29,47,48,49,50,51,54,60,61,66,69,70,71,75,76,79,106,107,108,120,121,133,134,146,147,159,160,172,173,181,184,186,187,193 ./Sources/CocoaLumberjackSwift/Supporting Files/CocoaLumberjackSwift.h:15,18 ./Sources/CocoaLumberjackSwift/DDLog+Combine.swift:15,17,19,24,27,45,46,48,51,54,57,61,62,64,67,68,69,71,74,77,85,87,89,92,94,96,97,101,102,106,107,110,111,112,113,116,119,120,121 ./Dangerfile.swift:3,9,12,15,16,26,28,29,32,33,36,37,38,41,46,50,51,56,57,61,62,66,67,70,78,79,84,87,88,123,128,129,130,131,132,133,169,190,196,197,201,207 ./Benchmarking/DynamicLogging.m:7,11,13,16,18,20,22,23,25,27,28,30,32,34,36,37,38,40,42,44,46,47,48,50,52,54,56,57,58,60,66,68,70,72,74,76,78,80,82,83,84,86,88,90,92,94,96,98,100,102,104,105,106 ./Benchmarking/PerformanceTesting.m:7,10,14,19,38,40,42,44,46,48,51,52,54,56,58,61,63,64,66,67,72,74,77,78,87,89,91,92,97,99,103,104,106,108,110,112,115,117,120,124,125,127,130,132,134,136,138,140,146,147,149,152,154,155,157,158,160,165,167,172,173,174,175,176,178,180,182,185,196,206,216,228,234,239,251,253,254,256,258,293,297,300,302,304,306,308,311,312,313,315,316,318,319,321,326,328,331,332,335,337,339,341,342,346,348,351,353,355,357,360,362,364,366,369,371,373,374,376,382,384,390,392,398,399,405,407,409,410,412,415,417,418 ./Benchmarking/BaseNSLogging.h:7,9,11,17 ./Benchmarking/StaticLogging.h:7,9,11,17 ./Benchmarking/main.m:7,9,11,15,17 ./Benchmarking/DynamicLogging.h:7,9,11,17 ./Benchmarking/PerformanceTesting.h:7,9,14,19,21,23,25 ./Benchmarking/BaseNSLogging.m:7,10,15,17,19,21,23,25,27,28,29,31,33,35,37,38,39,41,43,45,47,48,49,51,57,59,61,63,65,67,69,71,73,74,75,77,79,81,83,85,87,89,91,93,95,96,97 ./Benchmarking/StaticLogging.m:7,11,13,16,18,20,22,24,26,27,28,30,32,34,36,37,38,40,42,44,46,47,48,50,56,58,60,62,64,66,68,70,72,73,74,76,78,80,82,84,86,88,90,92,94,95,96 ./Demos/Benchmark/Desktop/BenchmarkMac/AppDelegate.h:7,9,11,13 ./Demos/Benchmark/Desktop/BenchmarkMac/main.m:7,9,11,13 ./Demos/Benchmark/Desktop/BenchmarkMac/AppDelegate.m:7,10,12,14,16,18,19 ./Demos/Benchmark/Mobile/Classes/BenchmarkIPhoneAppDelegate.h:7,9,11,13,16,17,20 ./Demos/Benchmark/Mobile/Classes/BenchmarkIPhoneViewController.h:7,9,11 ./Demos/Benchmark/Mobile/Classes/BenchmarkIPhoneViewController.m:7,9,11 ./Demos/Benchmark/Mobile/Classes/BenchmarkIPhoneAppDelegate.m:7,11,13,16,18,21,23,24 ./Demos/Benchmark/Mobile/main.m:7,9,14,15 ./Demos/SQLiteLogger/SQLiteLogger/SQLiteLoggerAppDelegate.m:7,11,14,16,18,20,23,25,26,28,30,32,38,40,42,43,45,47,50,52,54,57,58 ./Demos/SQLiteLogger/SQLiteLogger/main.m:7,9,11,13 ./Demos/SQLiteLogger/SQLiteLogger/SQLiteLoggerAppDelegate.h:7,9,11,16,17,19 ./Demos/SQLiteLogger/FMDBLogger.m:7,10,15,19,26,27,29,31,35,37,39,41,46,48,49,51,55,57,59,61,63,65,68,69,71,72,73,75,77,80,82,84,86,87,89,91,97,100,102,103,104,105,107,109,111,112,114,116,118,120,122,124,125,132,135,137,138,140,143,146,148,149,151,152,154,156,177,179,181,184,186,187,189,191,195,196,198,200,202,203,205,207,212,213,215,217,219,221,224,225,226,227,229,231,235,236,238,240,242,244,246,249,250,251,252,254,256,259,261,263,266,267,268 ./Demos/SQLiteLogger/FMDBLogger.h:7,10,12,14,18,20,21,27,47 ./Demos/SQLiteLogger/FMDB/FMDatabase.h:4,6,19,20,21,24,33,37,38,40,42,46,48,53,57,62,65,68,71,74,77,80,83,86,89,90,92,94,96,101,102,103,106,109,112,115,116,118 ./Demos/SQLiteLogger/FMDB/FMResultSet.h:3,7,15,18,22,26,27,28,30,32,35,38,40,43,46,49,52,55,58,61,64,67,70,73,77,85,86,89,92 ./Demos/SQLiteLogger/FMDB/FMDatabaseAdditions.m:8,11,13,24,25,28,29,32,33,36,37,40,41,44,45,48,49,52,53,54,57,67,69,70,74,77,79,80,83,86,88,89,90,93,106,107,110,112,113 ./Demos/SQLiteLogger/FMDB/FMDatabase.m:3,5,8,9,12,20,21,23,24,28,29,32,36,38,39,42,43,46,47,50,51,55,56,61,62,64,65,72,74,76,77,79,82,85,86,100,101,104,105,107,110,111,113,116,119,120,122,123,127,130,135,136,137,138,142,143,146,147,151,152,158,159,160,165,166,168,172,173,178,179,184,185,187,192,193,195,198,199,201,205,206,208,209,212,216,218,219,222,223,226,228,229,232,233,235,239,241,243,245,247,248,253,254,258,260,261,263,266,267,271,274,276,279,282,285,288,291,294,297,298,301,302,303,305,309,310,312,314,318,321,322,326,327,330,335,339,346,347,349,350,357,359,360,362,365,366,368,369,373,375,378,381,382,385,386,388,390,391,397,398,400,404,407,408,409,415,417,419,421,423,424,428,430,433,434,437,438,440,444,445,447,451,454,455,459,460,463,465,472,479,480,482,483,490,492,493,496,499,500,502,503,505,506,507,511,513,516,519,520,521,524,525,527,529,530,536,537,545,554,555,557,562,563,566,570,575,580,581,583,585,586,589,591,593,595,596,600,606,607,609,611,612,613,617,619,622,623,624,625,628,629,633,635,638,639,644,646,647,652,654,655,660,662,663,668,670,671,674,677,678,681,684,685,688,689,692,693,696,699,700,703,706,707,710,713,714,715,718,721,722,723,726,727,729,731,734,735,738,739,740,743,744,749,750,751,752,754,755,756,758,762,763,768,769,770,775,776,777,781,782,783,786,787,790,791,794,795,800,801,802,805,806,810,811,812,815,816,817,819 ./Demos/SQLiteLogger/FMDB/FMDatabaseAdditions.h:8,11,12,20,24,25,30 ./Demos/SQLiteLogger/FMDB/FMResultSet.m:4,8,9,14,16,18,20,23,25,26,30,31,34,37,40,42,43,48,53,54,56,59,60,62,67,69,70,72,74,77,79,83,85,86,87,88,90,92,95,98,99,105,106,108,111,112,114,115,117,123,125,134,135,137,139,143,144,147,151,156,161,162,164,165,168,169,171,172,175,176,178,181,182,184,186,189,190,192,194,195,196,197,200,201,204,205,208,209,212,213,216,217,220,221,224,225,228,229,232,233,236,237,239,242,243,245,249,250,252,253,256,257,260,261,263,266,267,269,270,271,274,275,277,280,281,283,285,287,289,290,291,294,295,297,300,301,303,305,307,308,309,312,313,316,317,319,322,323,325,326,329,330,333,335,338,341,344,348,349,352,353,355,356,359,360,361,365,366,369,370,371,374,375,380,381,384,385,390,391,394,395,400,401,402,403,404 ./Demos/FineGrainedLogging/MYLog.h:7,9,12,15,18,20 ./Demos/FineGrainedLogging/FineGrainedLoggingAppDelegate.h:7,9,12,14,17,19,20,22 ./Demos/FineGrainedLogging/TimerOne.m:7,10,13,15,17,19,21,27,33,35,36,38,40,41,43,45,46,48,50,52,54,55,56 ./Demos/FineGrainedLogging/TimerTwo.h:7,9,11,14,15 ./Demos/FineGrainedLogging/FineGrainedLoggingAppDelegate.m:7,9,11,14,16,18,20,23,26,27 ./Demos/FineGrainedLogging/main.m:7,9,11,13 ./Demos/FineGrainedLogging/TimerOne.h:7,9,11,14,15 ./Demos/FineGrainedLogging/TimerTwo.m:7,10,13,15,17,19,21,27,33,35,36,38,40,41,43,45,46,48,50,52,54,55 ./Demos/CustomLogLevels/MYLog.h:7,9,21,23,29,31,38,45 ./Demos/CustomLogLevels/CustomLogLevelsAppDelegate.m:7,9,11,14,16,18,20,24,31,32 ./Demos/CustomLogLevels/main.m:7,9,11,13 ./Demos/CustomLogLevels/CustomLogLevelsAppDelegate.h:7,9,12,13,15 ./Demos/ContextFilter/ContextFilterAppDelegate.m:7,12,15,18,20,43,44,46,48,50,52,54,56,58,61,63,64,66,68,69 ./Demos/ContextFilter/MyContextFilter.m:7,10,12,14,16,19,21,25,26,27 ./Demos/ContextFilter/ThirdPartyFramework.m:7,10,17,19,25,28,29,31,33,35,36,38,40,41 ./Demos/ContextFilter/ContextFilterAppDelegate.h:7,9,12,13,15 ./Demos/ContextFilter/main.m:7,9,11,13 ./Demos/ContextFilter/MyContextFilter.h:7,10,12 ./Demos/ContextFilter/ThirdPartyFramework.h:7,9,11,13,15 ./Demos/DispatchQueueLogger/DispatchQueueLogger/AppDelegate.h:7,9,11,13 ./Demos/DispatchQueueLogger/DispatchQueueLogger/main.m:7,9,11,13 ./Demos/DispatchQueueLogger/DispatchQueueLogger/AppDelegate.m:7,11,14,16,20,21,23,25,27,29,31,33,37,41,43,44,46,48,52,62,64,66,70,71,73,74,76,78,80,83,85,86,87,88 ./Demos/LogFileCompressor/LogFileCompressorAppDelegate.h:7,9,11,13,15,17,18,20 ./Demos/LogFileCompressor/CompressingLogFileManager.h:7,10,12,15,16 ./Demos/LogFileCompressor/LogFileCompressorAppDelegate.m:7,9,12,15,19,21,23,25,27,29,32,34,38,44,45,47,49,50 ./Demos/LogFileCompressor/main.m:7,9,11,13 ./Demos/LogFileCompressor/CompressingLogFileManager.m:7,10,17,19,24,26,28,30,32,34,37,39,43,45,47,49,51,52,54,56,58,62,64,66,67,69,71,72,74,76,81,82,84,86,90,91,93,95,97,100,104,105,108,110,112,114,116,117,119,120,122,123,125,127,129,131,132,134,136,138,143,145,147,148,152,155,157,159,160,161,163,165,167,179,180,182,184,186,196,198,201,204,206,208,211,216,222,224,242,245,248,250,254,256,276,279,284,285,287,289,293,296,299,307,310,313,316,321,328,331,333,336,338,340,342,344,346,347,349,357,360,363,365,366,368,370,372,375,376,377,379,381,384,386,388,391,393,396,402,404,406,409,411,414,419,428,431,434,436,438,441,442,443,444,445,447,451,453,455,457,459,460,462,469,471,473,475,477,479,480,482,489,491,493,495,496,498,499 ./Demos/CustomFormatters/CustomFormattersAppDelegate.h:7,9,12,13,15 ./Demos/CustomFormatters/TestFormatter.h:7,10,12 ./Demos/CustomFormatters/main.m:7,9,11,13 ./Demos/CustomFormatters/CustomFormattersAppDelegate.m:7,9,12,15,17,19,21,23,25,28,31,33,38,39 ./Demos/CustomFormatters/TestFormatter.m:7,9,15,17,20,21 ./Demos/GlobalLogLevel/MyLogging.h:7,9 ./Demos/GlobalLogLevel/GlobalLogLevelAppDelegate.m:7,11,13,15,17,19,24,25,27,30,35,37,39,41,42 ./Demos/GlobalLogLevel/Stuff.m:7,10,12,14,19,20 ./Demos/GlobalLogLevel/GlobalLogLevelAppDelegate.h:7,9,12,13,15 ./Demos/GlobalLogLevel/main.m:7,9,11,13 ./Demos/GlobalLogLevel/Stuff.h:7,9,11,13 ./Demos/CLI/CLI/main.m:7,10,12,14,16,28,30 ./Demos/PerUserLogLevels/PerUserLogLevels/AppDelegate.h:7,9,11,13 ./Demos/PerUserLogLevels/PerUserLogLevels/main.m:7,9,11,13 ./Demos/PerUserLogLevels/PerUserLogLevels/AppDelegate.m:7,10,24,25,34,35,37,39,41,43,47,48 ./Demos/WebServerIPhone/Classes/MyHTTPConnection.m:7,18,20,22,32,35,40,42,48,49,51,53,55,56,57,59,61,63,64,65,69,75,78,80,81,86,88,91,93,95,97,99,100,102,103,107,115,117,121,127,129,133,135,137,139,143,145,149,151,154,156,159,161,164,165,167,169,170,172,174,175,179,187,189,192,193,196,197,202,204,206,209,211,220,223,228,229,232,233,238,240,243,246,249,250,252,253 ./Demos/WebServerIPhone/Classes/WebServerIPhoneAppDelegate.m:7,10,12,15,18,20,22,25,27,30,33,38,43,47,49,52,54,55,56,58,63,66,71,73,80,83,85,87,89,94,96,101,107,110,111,112,114,121,123,124 ./Demos/WebServerIPhone/Classes/WebSocketLogger.h:7,11,13,15,18,19,21,23,25,27,28 ./Demos/WebServerIPhone/Classes/WebServerIPhoneViewController.h:7,9,11 ./Demos/WebServerIPhone/Classes/MyHTTPConnection.h:7,10,12 ./Demos/WebServerIPhone/Classes/WebSocketLogger.m:7,10,12,14,16,19,21,23,24,26,28,29,33,35,37,39,42,43,45,47,49,52,55,56,60,62,64,67,69,70,72,74,76,77,79,81,83,85,86,88,89,90,92,96,98,100,102,106,108,109,111,113,115,119,121,122,123 ./Demos/WebServerIPhone/Classes/WebServerIPhoneAppDelegate.h:7,9,13,15,17,19,22,23,25,28 ./Demos/WebServerIPhone/Classes/WebServerIPhoneViewController.m:7,9,11 ./Demos/WebServerIPhone/main.m:7,9,14,15 ./Demos/WebServerIPhone/Vendor/CocoaHTTPServer/HTTPConnection.h:2,8,9,11,15,17,21,22,25,29,31,35,37,41,43,46,48,51,53,58,63,65,66,68,71,73,76,79,84,87,89,95,99,105,108,110,113,115 ./Demos/WebServerIPhone/Vendor/CocoaHTTPServer/HTTPLogging.h:48,50,54,56,62,64,68,70,73,75,77,79,81,83 ./Demos/WebServerIPhone/Vendor/CocoaHTTPServer/HTTPMessage.h:4,6,11,14,15,17,19,20,22,24,26,28,30,32,35,37,40,42,44,47 ./Demos/WebServerIPhone/Vendor/CocoaHTTPServer/WebSocket.h:2,5,6,8,10,12,15,17,21,23,24,26,28,36,43,52,60,68,77,79,83,95,98,100,102,104 ./Demos/WebServerIPhone/Vendor/CocoaHTTPServer/HTTPAuthenticationRequest.h:2,7,9,10,12,15,17,26,28,31,34,44 ./Demos/WebServerIPhone/Vendor/CocoaHTTPServer/Responses/HTTPAsyncFileResponse.h:3,5,13,15,17,22,24,26,35,36,39,41 ./Demos/WebServerIPhone/Vendor/CocoaHTTPServer/Responses/HTTPErrorResponse.m:2,4,6,8,10,11,13,14,17,18,21,22,25,26,29,30,33,34,37 ./Demos/WebServerIPhone/Vendor/CocoaHTTPServer/Responses/HTTPDataResponse.m:3,7,11,12,14,16,18,20,23,25,26,28,30,31,32,34,36,38,40,41,43,45,47,48,50,52,54,55,57,59,62,64,66,68,69,71,73,75,77,78 ./Demos/WebServerIPhone/Vendor/CocoaHTTPServer/Responses/HTTPRedirectResponse.h:3,4,6,8,9,11 ./Demos/WebServerIPhone/Vendor/CocoaHTTPServer/Responses/HTTPDynamicFileResponse.m:4,8,12,14,15,17,22,24,26,29,31,32,34,36,38,39,41,44,46,48,49,51,54,56,57,59,61,63,65,66,68,70,73,76,79,82,89,92,95,97,99,101,103,105,107,111,113,115,117,121,123,124,126,129,132,136,139,142,144,147,150,152,155,157,159,161,163,165,167,169,172,174,176,179,180,181,194,197,199,201,208,210,212,216,217,218,219,220,222,223,225,227,228,229,232,234,237,240,242,256,259,262,264,267,268,270,272,275,277,280,281,283,284,286,288,289,290,291 ./Demos/WebServerIPhone/Vendor/CocoaHTTPServer/Responses/HTTPFileResponse.m:4,7,11,15,17,18,20,22,24,26,28,32,34,36,37,40,42,44,45,48,50,53,55,56,58,60,63,64,66,68,71,73,76,77,79,81,82,84,86,91,92,94,97,98,100,101,103,105,107,108,110,112,114,115,117,119,121,125,126,128,131,133,135,136,137,139,141,143,147,148,153,155,157,160,162,165,167,169,172,173,174,176,178,180,182,184,186,189,191,193,196,198,200,202,204,205,206,208,210,212,214,215,217,219,220,222,224,226,228,230,231,234,235,236 ./Demos/WebServerIPhone/Vendor/CocoaHTTPServer/Responses/HTTPAsyncFileResponse.m:4,7,11,17,19,25,27,29,35,37,41,43,60,62,64,66,68,70,74,76,78,79,82,84,86,87,90,92,95,97,98,100,102,105,106,108,115,118,121,124,125,127,129,131,134,135,136,138,140,142,145,146,147,149,151,153,156,158,161,162,163,165,167,170,172,174,175,177,180,181,183,185,190,192,194,197,200,202,204,206,209,211,214,216,218,221,223,224,225,227,229,231,234,236,239,241,243,246,248,250,253,256,257,259,264,266,270,272,278,280,282,283,285,287,292,293,295,298,299,301,302,304,306,308,309,311,313,315,316,318,320,322,326,327,330,333,335,337,338,339,341,343,345,347,349,351,354,356,358,360,364,365,367,369,373,375,376,377,379,381,383,385,386,388,390,391,393,395,397,398,400,402,404,406,409,413,416,417,418,420,422,426,429,430 ./Demos/WebServerIPhone/Vendor/CocoaHTTPServer/Responses/HTTPRedirectResponse.m:3,7,11,12,14,16,18,20,22,24,25,27,29,30,32,34,35,37,39,40,42,44,46,47,49,51,52,54,56,58,59,61,63,65,66,68,70,71,72 ./Demos/WebServerIPhone/Vendor/CocoaHTTPServer/Responses/HTTPDataResponse.h:3,4,6,9,10,12 ./Demos/WebServerIPhone/Vendor/CocoaHTTPServer/Responses/HTTPErrorResponse.h:2,5,6,8 ./Demos/WebServerIPhone/Vendor/CocoaHTTPServer/Responses/HTTPFileResponse.h:3,5,6,8,10,14,16,20,21,24 ./Demos/WebServerIPhone/Vendor/CocoaHTTPServer/Responses/HTTPDynamicFileResponse.h:4,40,42,45,46,51 ./Demos/WebServerIPhone/Vendor/CocoaHTTPServer/HTTPServer.h:2,5,19,20,22,25,31,37,45,51,53,54,68,80,91,110,122,143,156,162,168,188,197,199,201,204 ./Demos/WebServerIPhone/Vendor/CocoaHTTPServer/HTTPMessage.m:2,6,7,9,11,13,15,17,18,20,22,27,29,30,32,34,39,41,42,44,46,48,49,50,52,54,55,57,59,60,62,64,65,67,69,70,72,74,75,77,79,80,82,84,85,87,89,90,92,96,97,99,101,102,104,106,107,109,111,112 ./Demos/WebServerIPhone/Vendor/CocoaHTTPServer/HTTPConnection.m:14,18,21,23,25,31,33,35,41,43,47,55,62,71,79,97,109,114,118,120,123,129,132,137,138,145,151,161,163,166,170,173,175,176,181,183,185,188,190,191,195,202,204,206,208,213,215,217,218,222,225,230,233,235,237,239,240,245,247,251,254,256,258,259,260,264,270,272,285,288,291,293,294,303,305,312,315,318,320,321,325,336,338,340,342,343,349,351,353,355,356,360,366,368,371,373,374,383,385,388,390,391,397,399,402,404,405,410,412,415,419,421,422,427,429,432,434,436,438,441,442,444,448,449,452,455,456,458,460,465,466,469,473,476,483,485,489,490,491,493,495,499,501,504,506,508,511,513,515,517,519,521,524,525,528,530,532,535,537,539,542,543,546,549,552,553,555,556,557,562,564,567,569,570,575,577,580,582,583,587,593,595,597,600,602,603,609,611,616,617,622,626,628,630,633,635,637,640,644,647,651,653,654,655,657,658,663,665,670,671,683,686,689,692,695,698,700,702,705,707,710,712,717,718,719,720,721,722,724,725,737,739,741,744,747,749,750,751,753,754,761,763,780,782,784,787,790,793,795,797,799,801,803,806,809,811,813,815,817,820,822,824,826,828,831,834,836,839,841,845,848,850,852,854,858,860,862,864,868,871,873,874,875,876,878,880,882,884,887,889,891,893,895,896,897,898,900,902,904,905,907,909,911,912,918,920,922,924,927,928,931,934,937,938,941,944,946,948,950,952,954,956,958,962,964,968,970,974,976,997,998,999,1001,1002,1006,1009,1010,1013,1015,1018,1020,1023,1024,1026,1027,1034,1036,1039,1041,1044,1048,1050,1051,1058,1060,1063,1082,1084,1088,1091,1093,1096,1098,1102,1105,1107,1110,1111,1113,1115,1118,1121,1123,1124,1130,1132,1133,1138,1144,1146,1147,1149,1151,1153,1155,1156,1157,1159,1161,1163,1164,1167,1169,1171,1173,1174,1177,1179,1183,1185,1187,1189,1190,1191,1193,1195,1199,1201,1203,1205,1207,1209,1211,1214,1215,1217,1219,1221,1223,1225,1226,1227,1229,1232,1234,1237,1239,1241,1245,1247,1250,1253,1255,1257,1259,1262,1264,1266,1269,1271,1274,1275,1277,1280,1281,1282,1284,1286,1288,1291,1293,1295,1297,1299,1301,1304,1305,1307,1310,1314,1317,1319,1321,1323,1325,1327,1329,1330,1331,1332,1333,1334,1335,1343,1345,1348,1350,1351,1353,1354,1362,1364,1378,1380,1382,1385,1387,1389,1391,1393,1395,1396,1398,1401,1403,1405,1408,1410,1413,1414,1416,1419,1420,1421,1422,1430,1432,1446,1448,1450,1452,1456,1458,1461,1463,1465,1467,1470,1471,1472,1473,1481,1483,1497,1499,1501,1503,1507,1509,1512,1514,1516,1518,1520,1521,1523,1525,1529,1532,1534,1537,1539,1541,1543,1545,1546,1548,1552,1554,1555,1556,1557,1561,1567,1569,1571,1573,1574,1576,1578,1579,1584,1586,1589,1591,1596,1598,1601,1602,1606,1609,1612,1613,1615,1625,1627,1629,1631,1632,1644,1646,1648,1649,1651,1654,1655,1658,1661,1663,1665,1667,1669,1671,1672,1673,1676,1677,1678,1680,1681,1690,1692,1694,1696,1698,1700,1702,1705,1707,1708,1710,1711,1713,1715,1728,1730,1731,1735,1740,1742,1743,1749,1758,1759,1764,1768,1769,1773,1778,1782,1784,1787,1790,1791,1792,1797,1801,1803,1807,1809,1811,1813,1815,1816,1819,1820,1821,1828,1832,1834,1839,1842,1843,1848,1849,1854,1860,1862,1867,1870,1871,1875,1876,1881,1885,1887,1891,1894,1895,1896,1900,1905,1919,1921,1924,1926,1932,1935,1937,1938,1944,1946,1949,1953,1956,1959,1961,1964,1966,1968,1970,1971,1972,1974,1975,1981,1983,2002,2006,2009,2012,2014,2017,2019,2021,2023,2024,2025,2027,2028,2032,2038,2040,2044,2046,2048,2050,2054,2058,2061,2063,2068,2069,2071,2073,2076,2079,2082,2085,2089,2091,2093,2095,2097,2099,2102,2105,2106,2108,2111,2114,2115,2116,2118,2120,2123,2125,2128,2131,2132,2134,2137,2140,2141,2142,2145,2146,2149,2154,2155,2157,2160,2163,2165,2168,2170,2175,2177,2183,2187,2188,2190,2194,2195,2197,2200,2201,2202,2204,2206,2224,2226,2230,2232,2236,2238,2240,2243,2244,2246,2249,2253,2255,2258,2263,2264,2266,2268,2270,2273,2275,2278,2280,2284,2286,2289,2293,2294,2296,2298,2301,2303,2305,2308,2309,2311,2316,2317,2319,2321,2325,2328,2329,2331,2335,2340,2342,2344,2345,2347,2349,2352,2354,2356,2358,2360,2364,2366,2368,2369,2370,2372,2374,2377,2378,2379,2380,2385,2387,2389,2392,2395,2397,2401,2404,2406,2409,2411,2414,2417,2419,2422,2425,2427,2430,2432,2433,2435,2436,2438,2441,2443,2444,2445,2447,2450,2453,2456,2458,2460,2464,2467,2469,2471,2474,2476,2480,2482,2485,2488,2489,2490,2491,2492,2497,2499,2501,2503,2504,2508,2516,2518,2524,2526,2528,2531,2532,2534,2536,2538,2540,2542,2544,2549,2550,2552,2553,2559,2561,2567,2569,2571,2574,2575,2578,2579,2583,2590,2592,2597,2599,2601,2605,2606,2612,2614,2620,2621,2623,2626,2629,2631,2633,2635,2638,2640,2645,2646,2648,2649,2651,2653,2661,2664,2666,2667,2670,2674,2675,2677,2681,2683,2687,2689,2691,2694,2696,2697,2699,2701,2703,2706,2708,2709,2711,2716,2717,2719,2720,2722,2726,2727 ./Demos/WebServerIPhone/Vendor/CocoaHTTPServer/WebSocket.m:7,11,14,16,18,24,26,28,34,36,40,43,47,56,63,65,67,68,70,72,73,75,77,78,80,84,86,90,92,97,98,100,123,127,130,132,135,138,141,142,144,146,147,149,152,154,157,160,161,163,165,166,168,171,173,175,176,180,182,184,186,188,190,191,193,195,197,200,201,204,207,211,213,215,216,218,220,224,227,228,230,232,236,238,239,241,245,246,250,256,259,261,264,266,268,270,273,275,276,282,285,287,290,291,295,297,299,301,303,304,306,308,310,312,314,316,318,320,321,322,324,326,328,331,333,335,337,339,341,343,344,346,347,352,353,355,357,380,381,402,403,407,410,419,422,425,428,432,433,435,436,438,441,442,444,445,447,449,453,456,459,461,463,465,467,469,471,472,473,475,477,482,484,487,489,491,492,494,496,499,502,505,507,512,514,516,518,520,524,526,528,534,535,536,537,541,543,545,550,553,556,558,559,560,562,565,566,568,570,572,574,577,583,585,591,593,598,599,601,603,607,608,610,612,613,615,617,622,625,627,628,629,631,633,638,641,643,644,647,648,650,652,656,658,660,661,665,684,686,688,690,694,696,699,701,703,705,708,709,711,714,716,719,721,724,725,727,734,736,738,740,742,744,746,748,749,751,756,758,760,763,765,772,774,776,778,781,783,786,787,790,792,794,796,798,800,802,803,806,807,808,810,812,814,815 ./Demos/WebServerIPhone/Vendor/CocoaHTTPServer/HTTPResponse.h:2,3,5,11,18,29,35,37,57,63,69,77,85,87,88 ./Demos/WebServerIPhone/Vendor/CocoaHTTPServer/Mime/MultipartFormDataParser.h:1,3,10,12,15,17,21,30,34,39,44,52,53,55,57,64 ./Demos/WebServerIPhone/Vendor/CocoaHTTPServer/Mime/MultipartMessageHeader.h:8,10,11,23,28,31 ./Demos/WebServerIPhone/Vendor/CocoaHTTPServer/Mime/MultipartMessageHeaderField.m:1,4,7,13,14,18,22,23,27,28,32,35,38,41,47,48,55,56,60,65,70,72,73,79,85,87,89,90,99,100,101,106,111,113,118,124,128,133,137,138,140,145,147,148,153,156,159,160,164,169,170,172,173,176,177,179,186,188,189,194,198,201,208,210,211 ./Demos/WebServerIPhone/Vendor/CocoaHTTPServer/Mime/MultipartFormDataParser.m:1,5,7,13,19,20,24,25,28,31,34,36,38,39,43,44,47,51,55,57,61,65,67,68,69,76,78,82,83,87,89,93,99,108,113,116,117,118,121,122,136,141,142,148,152,154,155,157,160,168,172,173,175,177,186,187,193,195,196,200,209,214,215,217,219,223,228,231,232,234,237,241,243,248,252,257,259,262,265,267,268,272,274,279,280,284,286,292,293,295,296,297,300,306,309,314,317,323,324,325,328,329,330,333,338,345,348,350,351,360,362,364,369,374,376,377,378,379,384,389,392,394,396,397,398,404,410,413,415,417,418,419,424,431,432,437,438,450,452,453,454,457,458,464,468,472,473,474,475,478,480,491,494,500,502,504,508,511,512,515,516,520,522,524,526,527,528 ./Demos/WebServerIPhone/Vendor/CocoaHTTPServer/Mime/MultipartMessageHeader.m:7,10,12,15,21,25,26,29,30,34,35,37,40,44,48,56,60,61,67,69,70,76,77,79,80,83,84,85 ./Demos/WebServerIPhone/Vendor/CocoaHTTPServer/Mime/MultipartMessageHeaderField.h:1,3,7,12,13,17,20,22 ./Demos/WebServerIPhone/Vendor/CocoaHTTPServer/HTTPAuthenticationRequest.m:3,7,12,13,15,17,19,21,24,26,27,30,32,33,35,38,40,41,43,48,54,56,57,61,62,64,65,66,70,73,74,77,78,81,82,85,86,89,90,93,94,97,98,101,102,105,106,109,110,113,114,118,128,131,134,135,139,142,145,146,149,150,160,163,166,167,171,174,180,182,184,186,187,189,192,193,194 ./Demos/WebServerIPhone/Vendor/CocoaHTTPServer/Categories/DDNumber.m:2,3,5,7,9,12,13,15,17,19,24,25,27,29,32,33,35,37,39,44,45,47,49,52,53,55,58,60,65,66,68,70,73,74,76,79,81,86,87 ./Demos/WebServerIPhone/Vendor/CocoaHTTPServer/Categories/DDData.m:3,4,6,12,14,16,19,20,22,24,27,28,30,32,35,37,39,40,42,43,45,55,57,60,65,66,72,74,81,82,85,88,91,92,94,95,97,100,109,111,115,123,125,128,130,136,137,139,141,146,149,150,152,153,154,156,157 ./Demos/WebServerIPhone/Vendor/CocoaHTTPServer/Categories/DDRange.h:10,13,15,20,22,28,29,32,33,36,37,40,41,46,48,50,53,55 ./Demos/WebServerIPhone/Vendor/CocoaHTTPServer/Categories/DDNumber.h:2,3,5,8,11 ./Demos/WebServerIPhone/Vendor/CocoaHTTPServer/Categories/DDRange.m:3,5,7,10,12,13,15,17,19,21,22,25,27,28,30,32,33,35,37,40,43,46,49,52,54,55,57,61,63,65,67,69,71,73,75,77,78,80,81,83,85,87,88,90,94,95,97,100,102,103 ./Demos/WebServerIPhone/Vendor/CocoaHTTPServer/Categories/DDData.h:2,4,6,8,10,13 ./Demos/WebServerIPhone/Vendor/CocoaHTTPServer/HTTPServer.m:6,10,13,15,17,23,25,27,33,35,39,41,44,47,49,53,55,61,63,65,69,72,74,77,80,83,86,90,92,95,101,105,108,114,120,122,124,125,131,133,136,139,141,146,148,149,153,160,162,166,168,169,171,173,176,178,182,183,185,189,190,191,199,201,205,207,208,210,212,216,217,222,224,228,230,231,233,235,239,240,241,248,250,254,256,257,259,261,268,270,271,273,275,279,280,286,288,292,294,295,297,299,301,305,306,307,314,316,320,322,323,325,327,329,331,333,335,336,340,342,344,346,347,349,351,355,356,357,363,365,369,371,372,374,376,380,381,382,387,389,393,395,396,398,400,402,404,406,409,414,418,420,422,423,424,428,430,432,435,437,440,442,445,447,449,451,454,456,457,459,461,462,464,466,468,471,475,477,481,483,486,490,492,495,497,498,500,502,506,508,509,511,513,516,518,519,523,528,530,534,536,537,542,544,548,550,551,555,557,562,568,570,571,573,579,581,582,586,588,590,592,594,597,602,604,608,612,614,616,619,620,621,623,625,627,629,631,633,636,638,640,641,642,648,650,652,656,657,663,667,669,670,676,680,683,684,688,694,696,698,701,703,704,710,712,714,717,719,720,724,735,737,739,741,744,746,752,753,755,757,759,768,770,771,772,773,775,776,777,779,781,783,785,786,788,790,795,796 ./Demos/WebServerIPhone/Vendor/CocoaAsyncSocket/GCDAsyncSocket.m:10,12,16,29,34,40,42,48,50,52,58,60,61,63,65,71,74,76,81,83,86,88,90,95,97,99,106,113,114,117,120,130,132,155,157,163,167,169,172,179,185,187,195,198,201,203,205,217,219,221,224,235,239,248,266,277,288,297,307,317,323,325,329,347,349,352,355,356,358,360,363,365,368,370,373,375,377,379,381,383,386,389,391,392,394,397,398,400,402,404,406,409,412,415,418,419,420,422,424,425,427,429,430,432,435,436,438,440,442,446,447,448,450,452,453,455,457,458,460,463,464,466,468,469,471,474,475,477,481,490,502,510,512,514,518,520,522,524,532,534,541,543,548,550,555,559,560,562,563,568,571,573,575,577,579,580,581,590,592,594,596,598,602,605,607,615,620,627,629,632,634,639,640,641,643,644,655,658,660,662,664,672,674,676,678,680,682,683,691,693,694,695,708,711,712,714,716,718,719,759,761,764,769,770,772,773,782,785,820,822,825,827,830,831,835,837,840,841,844,847,850,852,854,857,859,861,864,866,870,871,875,877,879,881,883,887,888,890,891,892,894,897,898,913,915,918,921,924,927,929,931,933,935,937,938,940,941,943,944,945,947,951,956,962,965,967,969,971,976,978,979,980,982,986,992,995,998,1000,1002,1004,1006,1008,1009,1010,1012,1016,1018,1020,1022,1023,1025,1027,1028,1030,1032,1033,1035,1037,1040,1044,1048,1050,1057,1062,1064,1066,1067,1084,1086,1089,1092,1095,1097,1099,1100,1102,1104,1106,1108,1110,1114,1115,1117,1122,1127,1129,1130,1134,1136,1138,1140,1142,1144,1148,1150,1151,1152,1154,1158,1161,1167,1168,1169,1171,1173,1174,1176,1178,1179,1181,1183,1185,1187,1189,1193,1195,1196,1197,1199,1201,1206,1209,1212,1218,1219,1220,1222,1224,1225,1227,1229,1230,1232,1234,1237,1239,1242,1247,1250,1251,1252,1254,1256,1258,1263,1266,1269,1275,1276,1277,1279,1281,1282,1284,1286,1287,1289,1291,1293,1295,1297,1299,1303,1305,1306,1307,1309,1311,1313,1319,1324,1325,1327,1329,1331,1333,1335,1337,1341,1343,1344,1345,1347,1349,1351,1357,1362,1363,1365,1367,1369,1371,1373,1375,1379,1381,1382,1383,1385,1387,1389,1395,1400,1401,1403,1405,1407,1410,1415,1417,1418,1420,1422,1424,1426,1428,1433,1434,1438,1440,1442,1443,1445,1447,1450,1453,1456,1458,1460,1462,1465,1467,1468,1470,1472,1475,1478,1482,1483,1487,1490,1494,1495,1497,1500,1503,1507,1508,1510,1513,1516,1520,1521,1524,1526,1528,1530,1533,1535,1536,1538,1541,1543,1544,1547,1549,1552,1554,1555,1557,1560,1562,1563,1567,1569,1572,1574,1576,1579,1581,1582,1584,1587,1589,1590,1592,1595,1597,1598,1601,1603,1605,1608,1610,1612,1613,1614,1616,1618,1620,1623,1626,1627,1629,1631,1633,1636,1637,1639,1640,1641,1643,1645,1647,1650,1652,1654,1657,1659,1662,1664,1669,1673,1676,1677,1679,1681,1684,1686,1688,1691,1693,1696,1698,1703,1707,1710,1711,1713,1716,1721,1723,1725,1728,1729,1731,1732,1734,1736,1740,1742,1744,1747,1749,1751,1754,1755,1757,1759,1761,1764,1766,1768,1771,1772,1774,1775,1777,1780,1783,1784,1786,1789,1791,1793,1795,1797,1799,1801,1803,1806,1807,1809,1813,1818,1820,1822,1824,1827,1829,1831,1833,1834,1839,1843,1844,1846,1847,1851,1858,1860,1862,1864,1867,1869,1870,1872,1874,1877,1879,1880,1882,1884,1887,1889,1890,1893,1895,1897,1900,1902,1903,1905,1908,1910,1912,1914,1917,1919,1920,1922,1924,1927,1929,1930,1932,1934,1937,1939,1940,1943,1944,1948,1950,1951,1953,1955,1956,1961,1963,1964,1970,1972,1976,1979,1981,1983,1985,1988,1990,1991,1993,1995,1997,1998,2001,2003,2005,2009,2012,2015,2018,2020,2023,2028,2030,2033,2034,2036,2037,2039,2041,2042,2044,2046,2047,2052,2054,2058,2061,2063,2065,2068,2070,2072,2074,2076,2078,2079,2081,2083,2085,2086,2087,2088,2090,2093,2095,2096,2099,2101,2104,2106,2107,2109,2112,2114,2115,2117,2119,2121,2122,2125,2127,2129,2130,2132,2134,2137,2142,2144,2147,2148,2150,2151,2153,2155,2159,2161,2164,2165,2167,2175,2183,2187,2189,2191,2193,2198,2200,2202,2204,2206,2208,2210,2214,2216,2220,2221,2223,2225,2227,2228,2229,2230,2232,2234,2237,2239,2241,2244,2245,2246,2248,2250,2253,2255,2257,2261,2262,2264,2267,2269,2271,2274,2275,2277,2279,2282,2283,2285,2288,2290,2291,2292,2302,2304,2306,2307,2309,2311,2315,2316,2319,2320,2322,2324,2326,2329,2331,2333,2335,2337,2341,2343,2345,2347,2351,2353,2355,2357,2361,2362,2364,2367,2369,2370,2372,2374,2376,2378,2381,2384,2385,2387,2390,2393,2395,2396,2397,2399,2402,2404,2406,2409,2412,2414,2417,2419,2421,2423,2426,2428,2430,2432,2433,2435,2437,2439,2440,2442,2444,2448,2449,2451,2453,2458,2467,2470,2472,2475,2476,2478,2481,2482,2487,2489,2492,2493,2495,2498,2499,2501,2504,2505,2508,2510,2513,2515,2517,2519,2521,2523,2525,2529,2531,2534,2535,2537,2539,2541,2544,2547,2549,2550,2552,2554,2556,2559,2560,2562,2564,2566,2567,2569,2571,2575,2576,2578,2579,2581,2583,2585,2587,2590,2598,2601,2603,2604,2605,2607,2609,2611,2614,2615,2621,2623,2625,2627,2629,2631,2632,2633,2635,2637,2640,2641,2645,2647,2649,2651,2652,2654,2657,2660,2662,2664,2666,2668,2670,2675,2677,2682,2683,2684,2687,2690,2692,2695,2697,2703,2705,2706,2708,2713,2715,2717,2719,2723,2724,2726,2730,2731,2733,2735,2738,2740,2742,2743,2745,2748,2750,2752,2753,2755,2758,2760,2762,2763,2765,2768,2770,2772,2773,2775,2778,2779,2783,2787,2789,2791,2793,2795,2798,2799,2800,2801,2803,2805,2807,2809,2811,2813,2818,2819,2821,2823,2825,2828,2830,2831,2833,2835,2837,2840,2842,2843,2845,2847,2849,2852,2854,2855,2862,2864,2866,2868,2870,2872,2874,2876,2877,2879,2881,2882,2883,2885,2887,2889,2890,2891,2893,2895,2896,2897,2901,2903,2905,2907,2908,2910,2912,2914,2915,2917,2920,2922,2923,2925,2929,2931,2932,2934,2937,2939,2940,2942,2945,2947,2948,2950,2954,2956,2958,2959,2964,2968,2970,2972,2973,2978,2982,2984,2986,2987,2992,2996,2998,3000,3001,3003,3007,3009,3011,3012,3014,3016,3018,3019,3023,3025,3027,3031,3036,3038,3039,3041,3043,3047,3052,3054,3055,3057,3059,3064,3066,3068,3070,3072,3078,3080,3081,3082,3084,3086,3091,3093,3095,3097,3100,3106,3108,3109,3110,3112,3114,3119,3121,3123,3125,3127,3133,3135,3136,3137,3139,3141,3146,3148,3150,3152,3155,3161,3163,3164,3165,3167,3170,3172,3173,3175,3178,3180,3181,3183,3186,3188,3189,3191,3194,3196,3197,3199,3202,3204,3205,3207,3210,3212,3213,3215,3218,3220,3221,3223,3226,3228,3229,3231,3234,3236,3238,3240,3241,3243,3246,3248,3250,3252,3253,3255,3258,3260,3262,3264,3265,3267,3270,3272,3274,3276,3277,3279,3282,3284,3286,3288,3289,3291,3294,3296,3298,3300,3301,3303,3306,3308,3310,3312,3313,3315,3318,3320,3322,3324,3325,3327,3329,3332,3335,3337,3339,3340,3341,3343,3346,3348,3350,3351,3353,3358,3360,3361,3363,3365,3368,3371,3373,3375,3376,3377,3379,3382,3384,3386,3387,3389,3394,3396,3397,3399,3401,3403,3405,3407,3411,3413,3414,3415,3417,3419,3421,3423,3425,3429,3431,3432,3433,3435,3437,3439,3441,3443,3447,3449,3450,3451,3455,3469,3472,3474,3477,3480,3482,3483,3485,3487,3489,3491,3492,3493,3495,3497,3500,3505,3508,3513,3516,3518,3520,3523,3528,3531,3536,3539,3541,3543,3546,3548,3551,3553,3555,3558,3560,3562,3564,3566,3568,3570,3572,3574,3576,3578,3580,3581,3582,3584,3586,3589,3591,3593,3595,3597,3599,3601,3603,3605,3607,3609,3611,3612,3613,3614,3616,3617,3619,3620,3621,3624,3625,3627,3630,3632,3634,3636,3639,3645,3647,3649,3653,3655,3657,3662,3664,3666,3671,3673,3676,3678,3680,3682,3687,3689,3692,3694,3697,3700,3703,3706,3707,3709,3711,3713,3718,3720,3721,3723,3725,3726,3728,3730,3732,3734,3736,3737,3739,3741,3743,3746,3747,3748,3750,3752,3754,3757,3758,3759,3761,3763,3765,3768,3769,3770,3772,3774,3776,3779,3780,3781,3785,3787,3789,3790,3795,3797,3798,3804,3808,3809,3817,3819,3821,3823,3826,3828,3831,3832,3834,3836,3837,3843,3847,3851,3852,3860,3862,3864,3866,3869,3871,3874,3875,3877,3879,3880,3886,3888,3889,3891,3893,3894,3901,3905,3909,3913,3914,3922,3924,3926,3928,3931,3933,3936,3937,3939,3941,3943,3945,3947,3951,3953,3955,3959,3962,3966,3971,3973,3978,3980,3981,3993,3996,3999,4001,4005,4006,4008,4010,4013,4016,4018,4020,4023,4026,4027,4029,4031,4033,4035,4036,4038,4040,4041,4043,4045,4055,4057,4060,4063,4064,4065,4066,4067,4068,4070,4072,4074,4076,4079,4081,4082,4084,4086,4088,4090,4092,4094,4096,4099,4101,4103,4104,4106,4107,4109,4110,4113,4115,4117,4127,4129,4132,4135,4137,4139,4141,4144,4146,4148,4150,4152,4155,4158,4160,4162,4163,4165,4167,4169,4171,4173,4174,4176,4177,4179,4180,4182,4184,4187,4189,4191,4193,4195,4209,4211,4212,4214,4217,4219,4225,4227,4229,4230,4232,4233,4236,4238,4240,4242,4248,4250,4252,4254,4256,4258,4272,4274,4286,4289,4291,4292,4294,4296,4297,4299,4301,4303,4305,4308,4310,4312,4313,4315,4317,4319,4321,4323,4325,4328,4330,4332,4333,4335,4338,4340,4342,4344,4345,4346,4348,4349,4352,4354,4358,4360,4366,4368,4370,4372,4374,4376,4378,4380,4381,4383,4385,4387,4390,4392,4395,4397,4399,4402,4404,4406,4408,4410,4412,4414,4416,4418,4421,4423,4425,4426,4427,4429,4435,4437,4438,4439,4440,4444,4447,4449,4451,4457,4460,4462,4471,4473,4476,4478,4480,4482,4485,4487,4489,4491,4492,4493,4495,4497,4498,4503,4505,4507,4509,4511,4513,4515,4517,4518,4520,4522,4524,4526,4528,4531,4533,4535,4537,4539,4541,4544,4545,4550,4552,4554,4556,4564,4567,4571,4574,4576,4578,4579,4581,4585,4587,4592,4594,4596,4597,4601,4603,4605,4606,4607,4610,4612,4613,4615,4617,4620,4622,4627,4629,4631,4634,4636,4638,4640,4645,4647,4652,4653,4655,4657,4658,4659,4660,4662,4664,4666,4670,4672,4675,4677,4679,4681,4683,4685,4688,4690,4693,4695,4697,4699,4702,4704,4708,4712,4714,4716,4719,4721,4723,4727,4731,4733,4737,4739,4741,4744,4747,4750,4752,4756,4758,4760,4764,4765,4766,4768,4771,4773,4775,4776,4777,4779,4781,4783,4785,4787,4794,4796,4798,4801,4803,4806,4810,4812,4815,4816,4818,4819,4820,4821,4822,4823,4824,4826,4830,4832,4833,4835,4837,4839,4841,4843,4844,4846,4848,4850,4853,4855,4858,4859,4860,4862,4864,4866,4868,4870,4872,4874,4877,4878,4879,4881,4882,4884,4886,4890,4892,4894,4896,4898,4899,4902,4904,4907,4909,4911,4915,4916,4918,4926,4928,4930,4932,4935,4937,4939,4945,4947,4952,4954,4956,4958,4961,4963,4965,4967,4969,4972,4973,4975,4977,4978,4980,4982,4983,4984,4986,4988,4990,4993,4995,4997,4999,5001,5003,5005,5006,5008,5010,5012,5014,5016,5017,5018,5019,5021,5023,5025,5026,5028,5030,5034,5036,5038,5042,5044,5047,5049,5051,5052,5054,5056,5057,5059,5062,5064,5067,5068,5070,5071,5073,5075,5078,5079,5081,5082,5084,5086,5088,5090,5093,5101,5103,5106,5107,5108,5110,5115,5117,5119,5122,5124,5126,5130,5132,5136,5138,5140,5141,5142,5144,5146,5148,5150,5154,5158,5160,5162,5164,5165,5166,5167,5171,5173,5175,5177,5179,5181,5183,5186,5188,5191,5192,5194,5196,5198,5200,5202,5206,5208,5210,5213,5217,5219,5221,5226,5228,5229,5241,5244,5245,5248,5250,5254,5255,5257,5259,5262,5265,5267,5269,5272,5275,5276,5278,5280,5282,5284,5285,5287,5289,5290,5291,5292,5293,5295,5297,5299,5301,5303,5305,5307,5310,5312,5315,5317,5319,5320,5322,5323,5325,5327,5329,5331,5334,5336,5338,5339,5341,5343,5345,5347,5349,5351,5354,5356,5358,5359,5361,5364,5366,5368,5370,5371,5372,5374,5375,5377,5381,5383,5385,5387,5391,5393,5395,5397,5399,5400,5403,5405,5407,5409,5411,5416,5417,5419,5421,5423,5465,5467,5470,5472,5474,5476,5478,5481,5483,5486,5487,5489,5491,5493,5495,5497,5498,5501,5502,5503,5505,5509,5511,5513,5515,5516,5518,5521,5524,5526,5528,5532,5534,5536,5538,5541,5543,5545,5546,5548,5549,5550,5551,5552,5553,5555,5556,5558,5562,5564,5566,5568,5570,5572,5573,5576,5579,5581,5583,5585,5587,5588,5590,5592,5593,5594,5603,5605,5607,5609,5611,5612,5613,5615,5617,5619,5623,5626,5627,5629,5631,5633,5635,5636,5638,5641,5643,5645,5647,5649,5651,5652,5653,5655,5657,5659,5662,5664,5667,5668,5669,5670,5672,5674,5676,5677,5679,5680,5682,5684,5686,5687,5689,5692,5694,5697,5698,5700,5701,5703,5705,5708,5709,5711,5712,5714,5716,5718,5720,5723,5731,5733,5736,5737,5738,5740,5745,5747,5749,5752,5754,5756,5760,5762,5766,5768,5770,5771,5772,5774,5776,5778,5780,5784,5788,5790,5792,5794,5795,5796,5797,5801,5803,5805,5807,5815,5817,5818,5820,5822,5824,5827,5829,5832,5834,5835,5836,5838,5844,5846,5848,5850,5853,5855,5859,5863,5867,5871,5873,5875,5879,5881,5885,5886,5887,5888,5892,5894,5896,5898,5900,5902,5907,5909,5912,5913,5916,5919,5923,5925,5927,5929,5935,5937,5940,5942,5945,5947,5949,5950,5954,5956,5958,5960,5964,5966,5969,5971,5973,5977,5979,5981,5983,5987,5988,5991,5993,5995,5997,5999,6000,6002,6004,6006,6009,6011,6013,6018,6020,6022,6024,6026,6029,6032,6034,6036,6039,6040,6042,6044,6045,6046,6048,6051,6054,6056,6057,6059,6061,6066,6068,6071,6072,6075,6078,6080,6082,6084,6086,6088,6089,6091,6093,6095,6097,6099,6101,6102,6104,6107,6110,6112,6113,6115,6117,6119,6121,6122,6124,6126,6128,6130,6131,6133,6135,6137,6139,6142,6144,6146,6148,6153,6155,6158,6159,6161,6164,6167,6168,6170,6173,6176,6177,6180,6183,6184,6197,6199,6201,6204,6206,6209,6212,6215,6216,6217,6219,6222,6226,6228,6231,6234,6235,6237,6238,6240,6243,6247,6249,6252,6255,6256,6258,6259,6261,6264,6268,6270,6273,6276,6277,6279,6280,6282,6285,6289,6291,6294,6297,6298,6300,6301,6303,6306,6308,6311,6314,6315,6316,6318,6320,6322,6325,6327,6329,6332,6334,6336,6338,6340,6342,6344,6346,6347,6348,6349,6351,6354,6356,6361,6364,6367,6369,6371,6373,6375,6376,6378,6381,6382,6383,6385,6388,6390,6394,6396,6400,6403,6405,6410,6414,6416,6420,6423,6425,6429,6431,6432,6434,6437,6438,6439,6441,6443,6446,6450,6453,6456,6457,6460,6463,6464,6465,6467,6471,6473,6476,6479,6480,6482,6487,6489,6491,6493,6495,6499,6500,6502,6504,6506,6507,6509,6511,6515,6517,6519,6521,6524,6526,6528,6530,6532,6535,6536,6539,6542,6544,6546,6550,6552,6554,6555,6556,6558,6562,6564,6566,6568,6570,6573,6575,6577,6579,6581,6584,6585,6588,6591,6592,6593,6595,6597,6599,6602,6604,6605,6606,6608,6610,6612,6614,6616,6619,6620,6623,6627,6629,6631,6634,6635,6637,6640,6641,6643,6646,6647,6650,6653,6656,6659,6678,6680,6683,6684,6686,6689,6690,6692,6693,6695,6699,6701,6703,6706,6712,6713,6715,6717,6719,6727,6729,6732,6734,6735,6736,6738,6741,6743,6746,6749,6750,6752,6755,6757,6760,6763,6764,6766,6768,6770,6772,6774,6776,6779,6781,6784,6786,6789,6790,6792,6795,6797,6799,6801,6803,6805,6807,6808,6810,6812,6815,6817,6819,6821,6823,6825,6827,6828,6829,6830,6831,6833,6835,6837,6839,6841,6843,6846,6848,6851,6853,6856,6857,6859,6862,6864,6866,6868,6870,6872,6874,6875,6877,6879,6882,6884,6886,6888,6890,6892,6894,6895,6896,6897,6898,6900,6902,6904,6905,6907,6910,6911,6913,6915,6918,6919,6921,6924,6925,6927,6929,6932,6937,6939,6941,6943,6947,6949,6953,6954,6956,6957,6959,6960,6962,6964,6967,6973,6977,6979,6981,6982,6986,6988,6990,6991,6993,6994,6996,6998,7001,7003,7005,7011,7013,7014,7016,7017,7019,7021,7024,7026,7028,7033,7035,7036,7037,7039,7041,7044,7047,7049,7051,7054,7056,7059,7060,7061,7063,7064,7066,7070,7075,7077,7079,7081,7083,7085,7089,7091,7092,7093,7098,7100,7102,7108,7113,7114,7115,7120,7123,7124,7129,7131,7132,7137,7142,7143,7148,7150,7153,7154,7159,7160,7165,7167,7170,7171,7173,7174,7179,7181,7184,7185,7187,7188,7190,7195,7197,7200,7201,7204,7206,7207,7212,7214,7217,7218,7221,7223,7224,7226,7228,7231,7232,7234,7236,7239,7241,7243,7244,7246,7248,7250,7251,7252,7254,7255,7260,7262,7264,7267,7268,7270,7271,7273,7277,7279,7281,7284,7285,7287,7288,7290,7292,7294,7296,7299,7300,7302,7303,7305,7309,7311,7313,7315,7317,7318,7320,7321,7323,7325,7327,7329,7330,7332,7333,7335,7337,7338,7340,7342,7343,7345,7347,7352,7353,7355,7357,7362,7363,7365,7367,7369,7371,7373,7376,7379,7381,7382,7384,7386,7389,7392,7394,7395,7396,7397,7399,7400,7402,7404,7405,7407,7409,7410,7412,7414,7415,7417,7419,7420 ./Demos/WebServerIPhone/Vendor/CocoaAsyncSocket/GCDAsyncSocket.h:10,15,19,21,23,25,27,31,33,37,41,43,45,49,51,53,55,59,61,64,67,77,79,91,95,97,118,120,124,128,132,147,150,153,160,162,171,189,191,199,209,247,259,265,305,307,328,335,342,348,350,361,368,371,380,387,394,396,409,416,437,461,470,493,516,551,581,624,630,632,651,657,659,713,715,748,823,853,865,867,889,916,918,920,928,930,932,939,947,949,953,956,976,988,994,1000,1007,1012,1018,1033,1048,1056,1064,1073 ./Demos/UniversalApp/Classes/UniversalAppViewController.h:7,9,11 ./Demos/UniversalApp/Classes/UniversalAppAppDelegate.h:7,9,11,15,16,19,21 ./Demos/UniversalApp/Classes/UniversalAppViewController.m:7,9,11 ./Demos/UniversalApp/Classes/UniversalAppAppDelegate.m:7,10,12,14,17,18,21,23,26,28,32,34,35,36,42,43,44,50,51,52,57,58,59,64,65,66,72,73,74,77,82,83,84,85,86 ./Demos/UniversalApp/main.m:7,9,14,15 ./Demos/OverflowTestMac/SlowLogger.h:7,10,12 ./Demos/OverflowTestMac/OverflowTestMacAppDelegate.m:7,9,12,15,17,19,21,32,36,39,42,45,47,49,51,52,54,55,57,59,61,63,65,66,67,68,70,72,74,76,78,79,80,81 ./Demos/OverflowTestMac/OverflowTestMacAppDelegate.h:7,9,12,13,15 ./Demos/OverflowTestMac/SlowLogger.m:7,9,11,13,15,16 ./Demos/OverflowTestMac/main.m:7,9,11,13 ./Demos/CaptureASL/CaptureASL/AppDelegate.h:7,9,11,13,15 ./Demos/CaptureASL/CaptureASL/ViewController.m:7,10,12,15,16,18,20,23,24,26,28,29,31,34,43,45,46 ./Demos/CaptureASL/CaptureASL/main.m:7,10,12,15,16 ./Demos/CaptureASL/CaptureASL/AppDelegate.m:7,10,12,14,16,18,20,21,23,24,26,28,32,34,36,37 ./Demos/CaptureASL/CaptureASL/ViewController.h:7,9,11,14 ./Demos/CoreDataLogger/CoreDataLogger/CoreDataLoggerAppDelegate.m:7,11,14,16,18,20,23,25,26,28,30,32,38,40,42,43,45,47,50,52,54,57,58 ./Demos/CoreDataLogger/CoreDataLogger/CoreDataLogger.h:7,10,12,15,20,21,27,37,46 ./Demos/CoreDataLogger/CoreDataLogger/LogEntry.h:7,10,12,17 ./Demos/CoreDataLogger/CoreDataLogger/main.m:7,9,11,13 ./Demos/CoreDataLogger/CoreDataLogger/CoreDataLogger.m:7,10,15,19,21,23,25,27,30,32,33,37,39,41,44,46,48,50,51,53,55,61,64,66,67,68,69,71,73,74,78,80,82,84,85,89,90,92,94,96,99,101,103,104,106,113,114,116,118,120,121,124,127,128,130,132,133,135,138,140,142,143,145,146,148,151,153,157,158,160,163,164,165,166,170,172,174,176,178,179,181,183,186,188,190,192,193,195,197,199,201,202,203,205,207,208,210,211,213,218,219,223,225,227,229,230,233,238,239,241,242,244,246,249,251,255,257,258,259,260,262,264,268,269,272,275,278,283,285,287,289,291,293,294,295,297,299,300,301,303,305,306,308,310,311,313,316,317 ./Demos/CoreDataLogger/CoreDataLogger/CoreDataLoggerAppDelegate.h:7,9,11,16,17,19 ./Demos/CoreDataLogger/CoreDataLogger/LogEntry.m:7,9,11,16 ./Demos/TestXcodeColors/Desktop/TestXcodeColors/AppDelegate.h:7,9,11,13 ./Demos/TestXcodeColors/Desktop/TestXcodeColors/main.m:7,9,11,13 ./Demos/TestXcodeColors/Desktop/TestXcodeColors/AppDelegate.m:7,10,13,17,19,21,23,25,27,29,31,33,37,42,45,51,53,55,57,63,65,67,69,71,72,74,81,83,85,86,88,94,96,98,99 ./Demos/TestXcodeColors/Mobile/TextXcodeColors/AppDelegate.h:7,9,11,13,16 ./Demos/TestXcodeColors/Mobile/TextXcodeColors/ViewController.m:7,9,11,13 ./Demos/TestXcodeColors/Mobile/TextXcodeColors/main.m:7,9,11,13,16,17 ./Demos/TestXcodeColors/Mobile/TextXcodeColors/AppDelegate.m:7,11,14,18,20,22,25,27,29,31,33,35,39,44,47,53,55,57,59,65,67,69,71,73,75,80,85,88,93,95,97,98,100,101,103,110,112,114,115,117,123,125,127,128 ./Demos/TestXcodeColors/Mobile/TextXcodeColors/ViewController.h:7,9,11,13,14,16 ./Demos/RollingTestMac/RollingTestMacAppDelegate.h:7,9,11,13,15,17,18,20 ./Demos/RollingTestMac/RollingTestMacAppDelegate.m:7,10,13,16,18,20,23,25,27,29,35,37,47,48,50,52,55,57,59,60,62,63 ./Demos/RollingTestMac/main.m:7,9,11,13 ./Demos/NonArcTest/NonArcTest/AppDelegate.h:7,9,11,13 ./Demos/NonArcTest/NonArcTest/main.m:7,9,11,13 ./Demos/NonArcTest/NonArcTest/AppDelegate.m:7,10,13,15,17,19,21,22,24,27,29,30 ./Demos/RegisteredDynamicLogging/Desktop/RegisteredLoggingTest/Tigers.h:7,9,11,13 ./Demos/RegisteredDynamicLogging/Desktop/RegisteredLoggingTest/RegisteredLoggingTestAppDelegate.m:7,12,15,17,19,21,23,26,29,32,34,35,38,39 ./Demos/RegisteredDynamicLogging/Desktop/RegisteredLoggingTest/Lions.h:7,9,11,13 ./Demos/RegisteredDynamicLogging/Desktop/RegisteredLoggingTest/main.m:7,9,11,13 ./Demos/RegisteredDynamicLogging/Desktop/RegisteredLoggingTest/RegisteredLoggingTestAppDelegate.h:7,9,12,13,15 ./Demos/RegisteredDynamicLogging/Desktop/RegisteredLoggingTest/Tigers.m:7,10,13,15,17,19,20,22,24,25,27,32,33 ./Demos/RegisteredDynamicLogging/Desktop/RegisteredLoggingTest/Lions.m:7,10,13,15,17,19,20,22,24,25,27,32,33 ./Demos/RegisteredDynamicLogging/Mobile/RegisteredLoggingTest/RegisteredLoggingTestViewController.h:7,9,11 ./Demos/RegisteredDynamicLogging/Mobile/RegisteredLoggingTest/Tigers.h:7,9,11,13 ./Demos/RegisteredDynamicLogging/Mobile/RegisteredLoggingTest/RegisteredLoggingTestAppDelegate.m:7,13,16,18,21,23,25,28,31,34,36,37,40,44,45 ./Demos/RegisteredDynamicLogging/Mobile/RegisteredLoggingTest/Lions.h:7,9,11,13 ./Demos/RegisteredDynamicLogging/Mobile/RegisteredLoggingTest/RegisteredLoggingTestViewController.m:7,9,11 ./Demos/RegisteredDynamicLogging/Mobile/RegisteredLoggingTest/main.m:7,9,11,15,16 ./Demos/RegisteredDynamicLogging/Mobile/RegisteredLoggingTest/RegisteredLoggingTestAppDelegate.h:7,9,11,13,16 ./Demos/RegisteredDynamicLogging/Mobile/RegisteredLoggingTest/Tigers.m:7,10,13,15,17,19,20,22,24,25,27,32,33 ./Demos/RegisteredDynamicLogging/Mobile/RegisteredLoggingTest/Lions.m:7,10,13,15,17,19,20,22,24,25,27,32,33 <<<<<< EOF