From 894cb72e235f1bfad1cbbc9fd5df4dc1254f5084 Mon Sep 17 00:00:00 2001 From: yicheng Date: Sun, 12 Aug 2018 10:30:21 +0800 Subject: [PATCH] Improved: use CocoaLumberjack instead of swiftlog --- ClashX.xcodeproj/project.pbxproj | 4 +- ClashX/AppDelegate.swift | 2 +- ClashX/Base.lproj/Main.storyboard | 19 + ClashX/Basic/Logger.swift | 37 +- ClashX/Models/ClashConfig.swift | 10 +- Podfile | 2 +- Podfile.lock | 10 +- .../CocoaLumberjack/Classes/CocoaLumberjack.h | 86 + .../Classes/CocoaLumberjack.swift | 95 + .../CocoaLumberjack/Classes/DDASLLogCapture.h | 41 + .../CocoaLumberjack/Classes/DDASLLogCapture.m | 230 ++ Pods/CocoaLumberjack/Classes/DDASLLogger.h | 58 + Pods/CocoaLumberjack/Classes/DDASLLogger.m | 121 + .../Classes/DDAbstractDatabaseLogger.h | 123 + .../Classes/DDAbstractDatabaseLogger.m | 660 +++ Pods/CocoaLumberjack/Classes/DDAssertMacros.h | 26 + Pods/CocoaLumberjack/Classes/DDFileLogger.h | 517 +++ Pods/CocoaLumberjack/Classes/DDFileLogger.m | 1500 +++++++ Pods/CocoaLumberjack/Classes/DDLegacyMacros.h | 75 + Pods/CocoaLumberjack/Classes/DDLog+LOGV.h | 83 + Pods/CocoaLumberjack/Classes/DDLog.h | 909 ++++ Pods/CocoaLumberjack/Classes/DDLog.m | 1403 +++++++ Pods/CocoaLumberjack/Classes/DDLogMacros.h | 101 + Pods/CocoaLumberjack/Classes/DDOSLogger.h | 37 + Pods/CocoaLumberjack/Classes/DDOSLogger.m | 81 + Pods/CocoaLumberjack/Classes/DDTTYLogger.h | 178 + Pods/CocoaLumberjack/Classes/DDTTYLogger.m | 1473 +++++++ Pods/CocoaLumberjack/LICENSE.txt | 18 + Pods/CocoaLumberjack/README.md | 196 + Pods/Manifest.lock | 10 +- Pods/Pods.xcodeproj/project.pbxproj | 3642 +++++++++-------- Pods/SwiftLog/LICENSE | 22 - Pods/SwiftLog/Log.swift | 159 - Pods/SwiftLog/README.md | 117 - .../CocoaLumberjack/CocoaLumberjack-dummy.m | 5 + .../CocoaLumberjack-prefix.pch} | 0 .../CocoaLumberjack-umbrella.h | 28 + .../CocoaLumberjack/CocoaLumberjack.modulemap | 6 + .../CocoaLumberjack.xcconfig} | 4 +- .../{SwiftLog => CocoaLumberjack}/Info.plist | 2 +- .../Pods-ClashX-acknowledgements.markdown | 47 +- .../Pods-ClashX-acknowledgements.plist | 59 +- .../Pods-ClashX/Pods-ClashX-frameworks.sh | 4 +- .../Pods-ClashX/Pods-ClashX.debug.xcconfig | 6 +- .../Pods-ClashX/Pods-ClashX.release.xcconfig | 6 +- .../SwiftLog/SwiftLog-dummy.m | 5 - .../SwiftLog/SwiftLog-umbrella.h | 16 - .../SwiftLog/SwiftLog.modulemap | 6 - 48 files changed, 10044 insertions(+), 2195 deletions(-) create mode 100644 Pods/CocoaLumberjack/Classes/CocoaLumberjack.h create mode 100644 Pods/CocoaLumberjack/Classes/CocoaLumberjack.swift create mode 100644 Pods/CocoaLumberjack/Classes/DDASLLogCapture.h create mode 100644 Pods/CocoaLumberjack/Classes/DDASLLogCapture.m create mode 100644 Pods/CocoaLumberjack/Classes/DDASLLogger.h create mode 100644 Pods/CocoaLumberjack/Classes/DDASLLogger.m create mode 100644 Pods/CocoaLumberjack/Classes/DDAbstractDatabaseLogger.h create mode 100644 Pods/CocoaLumberjack/Classes/DDAbstractDatabaseLogger.m create mode 100644 Pods/CocoaLumberjack/Classes/DDAssertMacros.h create mode 100644 Pods/CocoaLumberjack/Classes/DDFileLogger.h create mode 100644 Pods/CocoaLumberjack/Classes/DDFileLogger.m create mode 100644 Pods/CocoaLumberjack/Classes/DDLegacyMacros.h create mode 100644 Pods/CocoaLumberjack/Classes/DDLog+LOGV.h create mode 100644 Pods/CocoaLumberjack/Classes/DDLog.h create mode 100644 Pods/CocoaLumberjack/Classes/DDLog.m create mode 100644 Pods/CocoaLumberjack/Classes/DDLogMacros.h create mode 100644 Pods/CocoaLumberjack/Classes/DDOSLogger.h create mode 100644 Pods/CocoaLumberjack/Classes/DDOSLogger.m create mode 100644 Pods/CocoaLumberjack/Classes/DDTTYLogger.h create mode 100644 Pods/CocoaLumberjack/Classes/DDTTYLogger.m create mode 100644 Pods/CocoaLumberjack/LICENSE.txt create mode 100644 Pods/CocoaLumberjack/README.md delete mode 100644 Pods/SwiftLog/LICENSE delete mode 100644 Pods/SwiftLog/Log.swift delete mode 100644 Pods/SwiftLog/README.md create mode 100644 Pods/Target Support Files/CocoaLumberjack/CocoaLumberjack-dummy.m rename Pods/Target Support Files/{SwiftLog/SwiftLog-prefix.pch => CocoaLumberjack/CocoaLumberjack-prefix.pch} (100%) create mode 100644 Pods/Target Support Files/CocoaLumberjack/CocoaLumberjack-umbrella.h create mode 100644 Pods/Target Support Files/CocoaLumberjack/CocoaLumberjack.modulemap rename Pods/Target Support Files/{SwiftLog/SwiftLog.xcconfig => CocoaLumberjack/CocoaLumberjack.xcconfig} (79%) rename Pods/Target Support Files/{SwiftLog => CocoaLumberjack}/Info.plist (96%) delete mode 100644 Pods/Target Support Files/SwiftLog/SwiftLog-dummy.m delete mode 100644 Pods/Target Support Files/SwiftLog/SwiftLog-umbrella.h delete mode 100644 Pods/Target Support Files/SwiftLog/SwiftLog.modulemap diff --git a/ClashX.xcodeproj/project.pbxproj b/ClashX.xcodeproj/project.pbxproj index 3ca1bd3..f6ee96a 100644 --- a/ClashX.xcodeproj/project.pbxproj +++ b/ClashX.xcodeproj/project.pbxproj @@ -518,21 +518,21 @@ inputPaths = ( "${SRCROOT}/Pods/Target Support Files/Pods-ClashX/Pods-ClashX-frameworks.sh", "${BUILT_PRODUCTS_DIR}/Alamofire/Alamofire.framework", + "${BUILT_PRODUCTS_DIR}/CocoaLumberjack/CocoaLumberjack.framework", "${BUILT_PRODUCTS_DIR}/LetsMove/LetsMove.framework", "${BUILT_PRODUCTS_DIR}/RxCocoa/RxCocoa.framework", "${BUILT_PRODUCTS_DIR}/RxSwift/RxSwift.framework", "${PODS_ROOT}/Sparkle/Sparkle.framework", - "${BUILT_PRODUCTS_DIR}/SwiftLog/SwiftLog.framework", "${BUILT_PRODUCTS_DIR}/SwiftyJSON/SwiftyJSON.framework", ); name = "[CP] Embed Pods Frameworks"; outputPaths = ( "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Alamofire.framework", + "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/CocoaLumberjack.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/LetsMove.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/RxCocoa.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/RxSwift.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Sparkle.framework", - "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/SwiftLog.framework", "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/SwiftyJSON.framework", ); runOnlyForDeploymentPostprocessing = 0; diff --git a/ClashX/AppDelegate.swift b/ClashX/AppDelegate.swift index 30bef72..509e93b 100644 --- a/ClashX/AppDelegate.swift +++ b/ClashX/AppDelegate.swift @@ -204,7 +204,7 @@ class AppDelegate: NSObject, NSApplicationDelegate { } ApiRequest.shared.requestLog { (type, msg) in - Logger.log(msg: msg) + Logger.log(msg: msg,level: ClashLogLevel(rawValue: type) ?? .unknow) } } diff --git a/ClashX/Base.lproj/Main.storyboard b/ClashX/Base.lproj/Main.storyboard index 5fcccd8..3922cd6 100644 --- a/ClashX/Base.lproj/Main.storyboard +++ b/ClashX/Base.lproj/Main.storyboard @@ -117,6 +117,25 @@ + + + + + + + + + + + + + + + + + + + diff --git a/ClashX/Basic/Logger.swift b/ClashX/Basic/Logger.swift index 8f1b8cb..295d973 100644 --- a/ClashX/Basic/Logger.swift +++ b/ClashX/Basic/Logger.swift @@ -7,30 +7,39 @@ // import Foundation -import SwiftLog - +import CocoaLumberjack class Logger { static let shared = Logger() - let queue = DispatchQueue(label: "clash.logger") + var fileLogger:DDFileLogger = DDFileLogger() + private init() { - Log.logger.name = "ClashX" //default is "logfile" - Log.logger.maxFileSize = 2048 //default is 1024 - Log.logger.maxFileCount = 4 //default is 4 - Log.logger.directory = (NSHomeDirectory() as NSString).appendingPathComponent("/Library/Logs/ClashX") - Log.logger.printToConsole = false //default is true + DDLog.add(DDTTYLogger.sharedInstance) // TTY = Xcode console + fileLogger.rollingFrequency = TimeInterval(60*60*24) // 24 hours + fileLogger.logFileManager.maximumNumberOfLogFiles = 3 + DDLog.add(fileLogger) + } - private func logToFile(msg:String) { - self.queue.sync { - logw(msg) + private func logToFile(msg:String,level:ClashLogLevel) { + switch level { + case .debug: + DDLogDebug(msg) + case .error: + DDLogError(msg) + case .info: + DDLogInfo(msg) + case .warning: + DDLogWarn(msg) + case .unknow: + DDLogVerbose(msg) } } - static func log(msg:String) { - shared.logToFile(msg: msg) + static func log(msg:String ,level:ClashLogLevel = .unknow) { + shared.logToFile(msg: msg, level: level) } func logFilePath() -> String { - return Log.logger.currentPath + return fileLogger.logFileManager.sortedLogFilePaths.first ?? "" } } diff --git a/ClashX/Models/ClashConfig.swift b/ClashX/Models/ClashConfig.swift index 6bf65b0..6a8e8ff 100644 --- a/ClashX/Models/ClashConfig.swift +++ b/ClashX/Models/ClashConfig.swift @@ -13,12 +13,20 @@ enum ClashProxyMode: String,Codable { case direct = "Direct" } +enum ClashLogLevel:String,Codable { + case info = "info" + case warning = "warning" + case error = "error" + case debug = "debug" + case unknow = "unknow" +} + class ClashConfig:Codable { var port:Int var socketPort:Int var allowLan:Bool var mode:ClashProxyMode - var logLevel:String + var logLevel:ClashLogLevel private enum CodingKeys : String, CodingKey { case port, socketPort = "socket-port", allowLan = "allow-lan", mode, logLevel = "log-level" diff --git a/Podfile b/Podfile index f4b555c..af63490 100644 --- a/Podfile +++ b/Podfile @@ -6,6 +6,6 @@ target 'ClashX' do pod 'SwiftyJSON', '~> 4.0' pod 'RxSwift', '~> 4.0' pod 'RxCocoa', '~> 4.0' - pod 'SwiftLog', '~> 1.0.0' + pod 'CocoaLumberjack/Swift' end diff --git a/Podfile.lock b/Podfile.lock index 6e91848..2143c13 100644 --- a/Podfile.lock +++ b/Podfile.lock @@ -1,31 +1,33 @@ PODS: - Alamofire (4.7.2) + - CocoaLumberjack/Default (3.4.2) + - CocoaLumberjack/Swift (3.4.2): + - CocoaLumberjack/Default - LetsMove (1.24) - RxCocoa (4.2.0): - RxSwift (~> 4.0) - RxSwift (4.2.0) - Sparkle (1.19.0) - - SwiftLog (1.0.0) - SwiftyJSON (4.1.0) DEPENDENCIES: - Alamofire (~> 4.7) + - CocoaLumberjack/Swift - LetsMove - RxCocoa (~> 4.0) - RxSwift (~> 4.0) - Sparkle - - SwiftLog (~> 1.0.0) - SwiftyJSON (~> 4.0) SPEC CHECKSUMS: Alamofire: e4fa87002c137ba2d8d634d2c51fabcda0d5c223 + CocoaLumberjack: db7cc9e464771f12054c22ff6947c5a58d43a0fd LetsMove: fefe56bc7bc7fb7d37049e28a14f297961229fc5 RxCocoa: 0b54909c902e1e581212a03e690bbd94032d8baa RxSwift: 99e10317ddfcc7fbe01356aafd118fde4a0be104 Sparkle: 8486d2493599665e466b5076e72b28849776e32f - SwiftLog: f6c4f9a4ade06263555ab34b2c0f72025ffcda9e SwiftyJSON: c29297daf073d2aa016295d5809cdd68045c39b3 -PODFILE CHECKSUM: fc69ca2b623a93ac89161e5986883dc8da46138b +PODFILE CHECKSUM: 5688b80dca1be3b09d34bf937c676aec36197abe COCOAPODS: 1.4.0 diff --git a/Pods/CocoaLumberjack/Classes/CocoaLumberjack.h b/Pods/CocoaLumberjack/Classes/CocoaLumberjack.h new file mode 100644 index 0000000..2273121 --- /dev/null +++ b/Pods/CocoaLumberjack/Classes/CocoaLumberjack.h @@ -0,0 +1,86 @@ +// Software License Agreement (BSD License) +// +// Copyright (c) 2010-2016, Deusty, LLC +// All rights reserved. +// +// Redistribution and use of this software in source and binary forms, +// with or without modification, are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Neither the name of Deusty nor the names of its contributors may be used +// to endorse or promote products derived from this software without specific +// prior written permission of Deusty, LLC. + +/** + * Welcome to CocoaLumberjack! + * + * The project page has a wealth of documentation if you have any questions. + * https://github.com/CocoaLumberjack/CocoaLumberjack + * + * If you're new to the project you may wish to read "Getting Started" at: + * Documentation/GettingStarted.md + * + * Otherwise, here is a quick refresher. + * There are three steps to using the macros: + * + * Step 1: + * Import the header in your implementation or prefix file: + * + * #import + * + * Step 2: + * Define your logging level in your implementation file: + * + * // Log levels: off, error, warn, info, verbose + * static const DDLogLevel ddLogLevel = DDLogLevelVerbose; + * + * Step 2 [3rd party frameworks]: + * + * Define your LOG_LEVEL_DEF to a different variable/function than ddLogLevel: + * + * // #undef LOG_LEVEL_DEF // Undefine first only if needed + * #define LOG_LEVEL_DEF myLibLogLevel + * + * Define your logging level in your implementation file: + * + * // Log levels: off, error, warn, info, verbose + * static const DDLogLevel myLibLogLevel = DDLogLevelVerbose; + * + * Step 3: + * Replace your NSLog statements with DDLog statements according to the severity of the message. + * + * NSLog(@"Fatal error, no dohickey found!"); -> DDLogError(@"Fatal error, no dohickey found!"); + * + * DDLog works exactly the same as NSLog. + * This means you can pass it multiple variables just like NSLog. + **/ + +#import + +// Disable legacy macros +#ifndef DD_LEGACY_MACROS + #define DD_LEGACY_MACROS 0 +#endif + +// Core +#import "DDLog.h" + +// Main macros +#import "DDLogMacros.h" +#import "DDAssertMacros.h" + +// Capture ASL +#import "DDASLLogCapture.h" + +// Loggers +#import "DDTTYLogger.h" +#import "DDASLLogger.h" +#import "DDFileLogger.h" +#import "DDOSLogger.h" + +// CLI +#if __has_include("CLIColor.h") && TARGET_OS_OSX +#import "CLIColor.h" +#endif diff --git a/Pods/CocoaLumberjack/Classes/CocoaLumberjack.swift b/Pods/CocoaLumberjack/Classes/CocoaLumberjack.swift new file mode 100644 index 0000000..07573f9 --- /dev/null +++ b/Pods/CocoaLumberjack/Classes/CocoaLumberjack.swift @@ -0,0 +1,95 @@ +// Software License Agreement (BSD License) +// +// Copyright (c) 2014-2016, Deusty, LLC +// All rights reserved. +// +// Redistribution and use of this software in source and binary forms, +// with or without modification, are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Neither the name of Deusty nor the names of its contributors may be used +// to endorse or promote products derived from this software without specific +// prior written permission of Deusty, LLC. + +import Foundation + +extension DDLogFlag { + public static func from(_ logLevel: DDLogLevel) -> DDLogFlag { + return DDLogFlag(rawValue: logLevel.rawValue) + } + + public init(_ logLevel: DDLogLevel) { + self = DDLogFlag(rawValue: logLevel.rawValue) + } + + ///returns the log level, or the lowest equivalant. + public func toLogLevel() -> DDLogLevel { + if let ourValid = DDLogLevel(rawValue: rawValue) { + return ourValid + } else { + if contains(.verbose) { + return .verbose + } else if contains(.debug) { + return .debug + } else if contains(.info) { + return .info + } else if contains(.warning) { + return .warning + } else if contains(.error) { + return .error + } else { + return .off + } + } + } +} + +public var defaultDebugLevel = DDLogLevel.verbose + +public func resetDefaultDebugLevel() { + defaultDebugLevel = DDLogLevel.verbose +} + +public func _DDLogMessage(_ message: @autoclosure () -> String, level: DDLogLevel, flag: DDLogFlag, context: Int, file: StaticString, function: StaticString, line: UInt, tag: Any?, asynchronous: Bool, ddlog: DDLog) { + if level.rawValue & flag.rawValue != 0 { + // Tell the DDLogMessage constructor to copy the C strings that get passed to it. + let logMessage = DDLogMessage(message: message(), level: level, flag: flag, context: context, file: String(describing: file), function: String(describing: function), line: line, tag: tag, options: [.copyFile, .copyFunction], timestamp: nil) + ddlog.log(asynchronous: asynchronous, message: logMessage) + } +} + +public func DDLogDebug(_ message: @autoclosure () -> String, level: DDLogLevel = defaultDebugLevel, context: Int = 0, file: StaticString = #file, function: StaticString = #function, line: UInt = #line, tag: Any? = nil, asynchronous async: Bool = true, ddlog: DDLog = DDLog.sharedInstance) { + _DDLogMessage(message, level: level, flag: .debug, context: context, file: file, function: function, line: line, tag: tag, asynchronous: async, ddlog: ddlog) +} + +public func DDLogInfo(_ message: @autoclosure () -> String, level: DDLogLevel = defaultDebugLevel, context: Int = 0, file: StaticString = #file, function: StaticString = #function, line: UInt = #line, tag: Any? = nil, asynchronous async: Bool = true, ddlog: DDLog = DDLog.sharedInstance) { + _DDLogMessage(message, level: level, flag: .info, context: context, file: file, function: function, line: line, tag: tag, asynchronous: async, ddlog: ddlog) +} + +public func DDLogWarn(_ message: @autoclosure () -> String, level: DDLogLevel = defaultDebugLevel, context: Int = 0, file: StaticString = #file, function: StaticString = #function, line: UInt = #line, tag: Any? = nil, asynchronous async: Bool = true, ddlog: DDLog = DDLog.sharedInstance) { + _DDLogMessage(message, level: level, flag: .warning, context: context, file: file, function: function, line: line, tag: tag, asynchronous: async, ddlog: ddlog) +} + +public func DDLogVerbose(_ message: @autoclosure () -> String, level: DDLogLevel = defaultDebugLevel, context: Int = 0, file: StaticString = #file, function: StaticString = #function, line: UInt = #line, tag: Any? = nil, asynchronous async: Bool = true, ddlog: DDLog = DDLog.sharedInstance) { + _DDLogMessage(message, level: level, flag: .verbose, context: context, file: file, function: function, line: line, tag: tag, asynchronous: async, ddlog: ddlog) +} + +public func DDLogError(_ message: @autoclosure () -> String, level: DDLogLevel = defaultDebugLevel, context: Int = 0, file: StaticString = #file, function: StaticString = #function, line: UInt = #line, tag: Any? = nil, asynchronous async: Bool = false, ddlog: DDLog = DDLog.sharedInstance) { + _DDLogMessage(message, level: level, flag: .error, context: context, file: file, function: function, line: line, tag: tag, asynchronous: async, ddlog: ddlog) +} + +/// Returns a String of the current filename, without full path or extension. +/// +/// Analogous to the C preprocessor macro `THIS_FILE`. +public func CurrentFileName(_ fileName: StaticString = #file) -> String { + var str = String(describing: fileName) + if let idx = str.range(of: "/", options: .backwards)?.upperBound { + str = String(str[idx...]) + } + if let idx = str.range(of: ".", options: .backwards)?.lowerBound { + str = String(str[.. +#include +#include +#include + +static BOOL _cancel = YES; +static DDLogLevel _captureLevel = DDLogLevelVerbose; + +#ifdef __IPHONE_8_0 + #define DDASL_IOS_PIVOT_VERSION __IPHONE_8_0 +#endif +#ifdef __MAC_10_10 + #define DDASL_OSX_PIVOT_VERSION __MAC_10_10 +#endif + +@implementation DDASLLogCapture + +static aslmsg (*dd_asl_next)(aslresponse obj); +static void (*dd_asl_release)(aslresponse obj); + ++ (void)initialize +{ + #if (defined(DDASL_IOS_PIVOT_VERSION) && __IPHONE_OS_VERSION_MAX_ALLOWED >= DDASL_IOS_PIVOT_VERSION) || (defined(DDASL_OSX_PIVOT_VERSION) && __MAC_OS_X_VERSION_MAX_ALLOWED >= DDASL_OSX_PIVOT_VERSION) + #if __IPHONE_OS_VERSION_MIN_REQUIRED < DDASL_IOS_PIVOT_VERSION || __MAC_OS_X_VERSION_MIN_REQUIRED < DDASL_OSX_PIVOT_VERSION + #pragma GCC diagnostic push + #pragma GCC diagnostic ignored "-Wdeprecated-declarations" + // Building on falsely advertised SDK, targeting deprecated API + dd_asl_next = &aslresponse_next; + dd_asl_release = &aslresponse_free; + #pragma GCC diagnostic pop + #else + // Building on lastest, correct SDK, targeting latest API + dd_asl_next = &asl_next; + dd_asl_release = &asl_release; + #endif + #else + // Building on old SDKs, targeting deprecated API + dd_asl_next = &aslresponse_next; + dd_asl_release = &aslresponse_free; + #endif +} + ++ (void)start { + // Ignore subsequent calls + if (!_cancel) { + return; + } + + _cancel = NO; + + dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^(void) { + [self captureAslLogs]; + }); +} + ++ (void)stop { + _cancel = YES; +} + ++ (DDLogLevel)captureLevel { + return _captureLevel; +} + ++ (void)setCaptureLevel:(DDLogLevel)level { + _captureLevel = level; +} + +#pragma mark - Private methods + ++ (void)configureAslQuery:(aslmsg)query { + const char param[] = "7"; // ASL_LEVEL_DEBUG, which is everything. We'll rely on regular DDlog log level to filter + + asl_set_query(query, ASL_KEY_LEVEL, param, ASL_QUERY_OP_LESS_EQUAL | ASL_QUERY_OP_NUMERIC); + + // Don't retrieve logs from our own DDASLLogger + asl_set_query(query, kDDASLKeyDDLog, kDDASLDDLogValue, ASL_QUERY_OP_NOT_EQUAL); + +#if !TARGET_OS_IPHONE || TARGET_SIMULATOR + int processId = [[NSProcessInfo processInfo] processIdentifier]; + char pid[16]; + sprintf(pid, "%d", processId); + asl_set_query(query, ASL_KEY_PID, pid, ASL_QUERY_OP_EQUAL | ASL_QUERY_OP_NUMERIC); +#endif +} + ++ (void)aslMessageReceived:(aslmsg)msg { + const char* messageCString = asl_get( msg, ASL_KEY_MSG ); + if ( messageCString == NULL ) + return; + + int flag; + BOOL async; + + const char* levelCString = asl_get(msg, ASL_KEY_LEVEL); + switch (levelCString? atoi(levelCString) : 0) { + // By default all NSLog's with a ASL_LEVEL_WARNING level + case ASL_LEVEL_EMERG : + case ASL_LEVEL_ALERT : + case ASL_LEVEL_CRIT : flag = DDLogFlagError; async = NO; break; + case ASL_LEVEL_ERR : flag = DDLogFlagWarning; async = YES; break; + case ASL_LEVEL_WARNING : flag = DDLogFlagInfo; async = YES; break; + case ASL_LEVEL_NOTICE : flag = DDLogFlagDebug; async = YES; break; + case ASL_LEVEL_INFO : + case ASL_LEVEL_DEBUG : + default : flag = DDLogFlagVerbose; async = YES; break; + } + + if (!(_captureLevel & flag)) { + return; + } + + // NSString * sender = [NSString stringWithCString:asl_get(msg, ASL_KEY_SENDER) encoding:NSUTF8StringEncoding]; + NSString *message = @(messageCString); + + const char* secondsCString = asl_get( msg, ASL_KEY_TIME ); + const char* nanoCString = asl_get( msg, ASL_KEY_TIME_NSEC ); + NSTimeInterval seconds = secondsCString ? strtod(secondsCString, NULL) : [NSDate timeIntervalSinceReferenceDate] - NSTimeIntervalSince1970; + double nanoSeconds = nanoCString? strtod(nanoCString, NULL) : 0; + NSTimeInterval totalSeconds = seconds + (nanoSeconds / 1e9); + + NSDate *timeStamp = [NSDate dateWithTimeIntervalSince1970:totalSeconds]; + + DDLogMessage *logMessage = [[DDLogMessage alloc]initWithMessage:message + level:_captureLevel + flag:flag + context:0 + file:@"DDASLLogCapture" + function:0 + line:0 + tag:nil + options:0 + timestamp:timeStamp]; + + [DDLog log:async message:logMessage]; +} + ++ (void)captureAslLogs { + @autoreleasepool + { + /* + We use ASL_KEY_MSG_ID to see each message once, but there's no + obvious way to get the "next" ID. To bootstrap the process, we'll + search by timestamp until we've seen a message. + */ + + struct timeval timeval = { + .tv_sec = 0 + }; + gettimeofday(&timeval, NULL); + unsigned long long startTime = timeval.tv_sec; + __block unsigned long long lastSeenID = 0; + + /* + syslogd posts kNotifyASLDBUpdate (com.apple.system.logger.message) + through the notify API when it saves messages to the ASL database. + There is some coalescing - currently it is sent at most twice per + second - but there is no documented guarantee about this. In any + case, there may be multiple messages per notification. + + Notify notifications don't carry any payload, so we need to search + for the messages. + */ + int notifyToken = 0; // Can be used to unregister with notify_cancel(). + notify_register_dispatch(kNotifyASLDBUpdate, ¬ifyToken, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^(int token) + { + // At least one message has been posted; build a search query. + @autoreleasepool + { + aslmsg query = asl_new(ASL_TYPE_QUERY); + char stringValue[64]; + + if (lastSeenID > 0) { + snprintf(stringValue, sizeof stringValue, "%llu", lastSeenID); + asl_set_query(query, ASL_KEY_MSG_ID, stringValue, ASL_QUERY_OP_GREATER | ASL_QUERY_OP_NUMERIC); + } else { + snprintf(stringValue, sizeof stringValue, "%llu", startTime); + asl_set_query(query, ASL_KEY_TIME, stringValue, ASL_QUERY_OP_GREATER_EQUAL | ASL_QUERY_OP_NUMERIC); + } + + [self configureAslQuery:query]; + + // Iterate over new messages. + aslmsg msg; + aslresponse response = asl_search(NULL, query); + + while ((msg = dd_asl_next(response))) + { + [self aslMessageReceived:msg]; + + // Keep track of which messages we've seen. + lastSeenID = atoll(asl_get(msg, ASL_KEY_MSG_ID)); + } + dd_asl_release(response); + asl_free(query); + + if (_cancel) { + notify_cancel(token); + return; + } + + } + }); + } +} + +@end diff --git a/Pods/CocoaLumberjack/Classes/DDASLLogger.h b/Pods/CocoaLumberjack/Classes/DDASLLogger.h new file mode 100644 index 0000000..046636d --- /dev/null +++ b/Pods/CocoaLumberjack/Classes/DDASLLogger.h @@ -0,0 +1,58 @@ +// Software License Agreement (BSD License) +// +// Copyright (c) 2010-2016, Deusty, LLC +// All rights reserved. +// +// Redistribution and use of this software in source and binary forms, +// with or without modification, are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Neither the name of Deusty nor the names of its contributors may be used +// to endorse or promote products derived from this software without specific +// prior written permission of Deusty, LLC. + +#import + +// Disable legacy macros +#ifndef DD_LEGACY_MACROS + #define DD_LEGACY_MACROS 0 +#endif + +#import "DDLog.h" + +// Custom key set on messages sent to ASL +extern const char* const kDDASLKeyDDLog; + +// Value set for kDDASLKeyDDLog +extern const char* const kDDASLDDLogValue; + +/** + * This class provides a logger for the Apple System Log facility. + * + * As described in the "Getting Started" page, + * the traditional NSLog() function directs its output to two places: + * + * - Apple System Log + * - StdErr (if stderr is a TTY) so log statements show up in Xcode console + * + * To duplicate NSLog() functionality you can simply add this logger and a tty logger. + * However, if you instead choose to use file logging (for faster performance), + * you may choose to use a file logger and a tty logger. + **/ +@interface DDASLLogger : DDAbstractLogger + +/** + * Singleton method + * + * @return the shared instance + */ +@property (class, readonly, strong) DDASLLogger *sharedInstance; + +// Inherited from DDAbstractLogger + +// - (id )logFormatter; +// - (void)setLogFormatter:(id )formatter; + +@end diff --git a/Pods/CocoaLumberjack/Classes/DDASLLogger.m b/Pods/CocoaLumberjack/Classes/DDASLLogger.m new file mode 100644 index 0000000..61c1172 --- /dev/null +++ b/Pods/CocoaLumberjack/Classes/DDASLLogger.m @@ -0,0 +1,121 @@ +// Software License Agreement (BSD License) +// +// Copyright (c) 2010-2016, Deusty, LLC +// All rights reserved. +// +// Redistribution and use of this software in source and binary forms, +// with or without modification, are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Neither the name of Deusty nor the names of its contributors may be used +// to endorse or promote products derived from this software without specific +// prior written permission of Deusty, LLC. + +#import "DDASLLogger.h" +#import + +#if !__has_feature(objc_arc) +#error This file must be compiled with ARC. Use -fobjc-arc flag (or convert project to ARC). +#endif + +const char* const kDDASLKeyDDLog = "DDLog"; + +const char* const kDDASLDDLogValue = "1"; + +static DDASLLogger *sharedInstance; + +@interface DDASLLogger () { + aslclient _client; +} + +@end + + +@implementation DDASLLogger + ++ (instancetype)sharedInstance { + static dispatch_once_t DDASLLoggerOnceToken; + + dispatch_once(&DDASLLoggerOnceToken, ^{ + sharedInstance = [[[self class] alloc] init]; + }); + + return sharedInstance; +} + +- (instancetype)init { + if (sharedInstance != nil) { + return nil; + } + + if ((self = [super init])) { + // A default asl client is provided for the main thread, + // but background threads need to create their own client. + + _client = asl_open(NULL, "com.apple.console", 0); + } + + return self; +} + +- (void)logMessage:(DDLogMessage *)logMessage { + // Skip captured log messages + if ([logMessage->_fileName isEqualToString:@"DDASLLogCapture"]) { + return; + } + + NSString * message = _logFormatter ? [_logFormatter formatLogMessage:logMessage] : logMessage->_message; + + if (message) { + const char *msg = [message UTF8String]; + + size_t aslLogLevel; + switch (logMessage->_flag) { + // Note: By default ASL will filter anything above level 5 (Notice). + // So our mappings shouldn't go above that level. + case DDLogFlagError : aslLogLevel = ASL_LEVEL_CRIT; break; + case DDLogFlagWarning : aslLogLevel = ASL_LEVEL_ERR; break; + case DDLogFlagInfo : aslLogLevel = ASL_LEVEL_WARNING; break; // Regular NSLog's level + case DDLogFlagDebug : + case DDLogFlagVerbose : + default : aslLogLevel = ASL_LEVEL_NOTICE; break; + } + + static char const *const level_strings[] = { "0", "1", "2", "3", "4", "5", "6", "7" }; + + // NSLog uses the current euid to set the ASL_KEY_READ_UID. + uid_t const readUID = geteuid(); + + char readUIDString[16]; +#ifndef NS_BLOCK_ASSERTIONS + size_t l = snprintf(readUIDString, sizeof(readUIDString), "%d", readUID); +#else + snprintf(readUIDString, sizeof(readUIDString), "%d", readUID); +#endif + + NSAssert(l < sizeof(readUIDString), + @"Formatted euid is too long."); + NSAssert(aslLogLevel < (sizeof(level_strings) / sizeof(level_strings[0])), + @"Unhandled ASL log level."); + + aslmsg m = asl_new(ASL_TYPE_MSG); + if (m != NULL) { + if (asl_set(m, ASL_KEY_LEVEL, level_strings[aslLogLevel]) == 0 && + asl_set(m, ASL_KEY_MSG, msg) == 0 && + asl_set(m, ASL_KEY_READ_UID, readUIDString) == 0 && + asl_set(m, kDDASLKeyDDLog, kDDASLDDLogValue) == 0) { + asl_send(_client, m); + } + asl_free(m); + } + //TODO handle asl_* failures non-silently? + } +} + +- (NSString *)loggerName { + return @"cocoa.lumberjack.aslLogger"; +} + +@end diff --git a/Pods/CocoaLumberjack/Classes/DDAbstractDatabaseLogger.h b/Pods/CocoaLumberjack/Classes/DDAbstractDatabaseLogger.h new file mode 100644 index 0000000..208bb22 --- /dev/null +++ b/Pods/CocoaLumberjack/Classes/DDAbstractDatabaseLogger.h @@ -0,0 +1,123 @@ +// Software License Agreement (BSD License) +// +// Copyright (c) 2010-2016, Deusty, LLC +// All rights reserved. +// +// Redistribution and use of this software in source and binary forms, +// with or without modification, are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Neither the name of Deusty nor the names of its contributors may be used +// to endorse or promote products derived from this software without specific +// prior written permission of Deusty, LLC. + +// Disable legacy macros +#ifndef DD_LEGACY_MACROS + #define DD_LEGACY_MACROS 0 +#endif + +#import "DDLog.h" + +/** + * This class provides an abstract implementation of a database logger. + * + * That is, it provides the base implementation for a database logger to build atop of. + * All that is needed for a concrete database logger is to extend this class + * and override the methods in the implementation file that are prefixed with "db_". + **/ +@interface DDAbstractDatabaseLogger : DDAbstractLogger { + +@protected + NSUInteger _saveThreshold; + NSTimeInterval _saveInterval; + NSTimeInterval _maxAge; + NSTimeInterval _deleteInterval; + BOOL _deleteOnEverySave; + + BOOL _saveTimerSuspended; + NSUInteger _unsavedCount; + dispatch_time_t _unsavedTime; + dispatch_source_t _saveTimer; + dispatch_time_t _lastDeleteTime; + dispatch_source_t _deleteTimer; +} + +/** + * Specifies how often to save the data to disk. + * Since saving is an expensive operation (disk io) it is not done after every log statement. + * These properties allow you to configure how/when the logger saves to disk. + * + * A save is done when either (whichever happens first): + * + * - The number of unsaved log entries reaches saveThreshold + * - The amount of time since the oldest unsaved log entry was created reaches saveInterval + * + * You can optionally disable the saveThreshold by setting it to zero. + * If you disable the saveThreshold you are entirely dependent on the saveInterval. + * + * You can optionally disable the saveInterval by setting it to zero (or a negative value). + * If you disable the saveInterval you are entirely dependent on the saveThreshold. + * + * It's not wise to disable both saveThreshold and saveInterval. + * + * The default saveThreshold is 500. + * The default saveInterval is 60 seconds. + **/ +@property (assign, readwrite) NSUInteger saveThreshold; + +/** + * See the description for the `saveThreshold` property + */ +@property (assign, readwrite) NSTimeInterval saveInterval; + +/** + * It is likely you don't want the log entries to persist forever. + * Doing so would allow the database to grow infinitely large over time. + * + * The maxAge property provides a way to specify how old a log statement can get + * before it should get deleted from the database. + * + * The deleteInterval specifies how often to sweep for old log entries. + * Since deleting is an expensive operation (disk io) is is done on a fixed interval. + * + * An alternative to the deleteInterval is the deleteOnEverySave option. + * This specifies that old log entries should be deleted during every save operation. + * + * You can optionally disable the maxAge by setting it to zero (or a negative value). + * If you disable the maxAge then old log statements are not deleted. + * + * You can optionally disable the deleteInterval by setting it to zero (or a negative value). + * + * If you disable both deleteInterval and deleteOnEverySave then old log statements are not deleted. + * + * It's not wise to enable both deleteInterval and deleteOnEverySave. + * + * The default maxAge is 7 days. + * The default deleteInterval is 5 minutes. + * The default deleteOnEverySave is NO. + **/ +@property (assign, readwrite) NSTimeInterval maxAge; + +/** + * See the description for the `maxAge` property + */ +@property (assign, readwrite) NSTimeInterval deleteInterval; + +/** + * See the description for the `maxAge` property + */ +@property (assign, readwrite) BOOL deleteOnEverySave; + +/** + * Forces a save of any pending log entries (flushes log entries to disk). + **/ +- (void)savePendingLogEntries; + +/** + * Removes any log entries that are older than maxAge. + **/ +- (void)deleteOldLogEntries; + +@end diff --git a/Pods/CocoaLumberjack/Classes/DDAbstractDatabaseLogger.m b/Pods/CocoaLumberjack/Classes/DDAbstractDatabaseLogger.m new file mode 100644 index 0000000..7f4c232 --- /dev/null +++ b/Pods/CocoaLumberjack/Classes/DDAbstractDatabaseLogger.m @@ -0,0 +1,660 @@ +// Software License Agreement (BSD License) +// +// Copyright (c) 2010-2016, Deusty, LLC +// All rights reserved. +// +// Redistribution and use of this software in source and binary forms, +// with or without modification, are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Neither the name of Deusty nor the names of its contributors may be used +// to endorse or promote products derived from this software without specific +// prior written permission of Deusty, LLC. + +#import "DDAbstractDatabaseLogger.h" +#import + + +#if !__has_feature(objc_arc) +#error This file must be compiled with ARC. Use -fobjc-arc flag (or convert project to ARC). +#endif + +@interface DDAbstractDatabaseLogger () + +- (void)destroySaveTimer; +- (void)destroyDeleteTimer; + +@end + +#pragma mark - + +@implementation DDAbstractDatabaseLogger + +- (instancetype)init { + if ((self = [super init])) { + _saveThreshold = 500; + _saveInterval = 60; // 60 seconds + _maxAge = (60 * 60 * 24 * 7); // 7 days + _deleteInterval = (60 * 5); // 5 minutes + } + + return self; +} + +- (void)dealloc { + [self destroySaveTimer]; + [self destroyDeleteTimer]; +} + +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +#pragma mark Override Me +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +- (BOOL)db_log:(DDLogMessage *)logMessage { + // Override me and add your implementation. + // + // Return YES if an item was added to the buffer. + // Return NO if the logMessage was ignored. + + return NO; +} + +- (void)db_save { + // Override me and add your implementation. +} + +- (void)db_delete { + // Override me and add your implementation. +} + +- (void)db_saveAndDelete { + // Override me and add your implementation. +} + +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +#pragma mark Private API +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +- (void)performSaveAndSuspendSaveTimer { + if (_unsavedCount > 0) { + if (_deleteOnEverySave) { + [self db_saveAndDelete]; + } else { + [self db_save]; + } + } + + _unsavedCount = 0; + _unsavedTime = 0; + + if (_saveTimer && !_saveTimerSuspended) { + dispatch_suspend(_saveTimer); + _saveTimerSuspended = YES; + } +} + +- (void)performDelete { + if (_maxAge > 0.0) { + [self db_delete]; + + _lastDeleteTime = dispatch_time(DISPATCH_TIME_NOW, 0); + } +} + +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +#pragma mark Timers +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +- (void)destroySaveTimer { + if (_saveTimer) { + dispatch_source_cancel(_saveTimer); + + if (_saveTimerSuspended) { + // Must resume a timer before releasing it (or it will crash) + dispatch_resume(_saveTimer); + _saveTimerSuspended = NO; + } + + #if !OS_OBJECT_USE_OBJC + dispatch_release(_saveTimer); + #endif + _saveTimer = NULL; + } +} + +- (void)updateAndResumeSaveTimer { + if ((_saveTimer != NULL) && (_saveInterval > 0.0) && (_unsavedTime > 0.0)) { + uint64_t interval = (uint64_t)(_saveInterval * (NSTimeInterval) NSEC_PER_SEC); + dispatch_time_t startTime = dispatch_time(_unsavedTime, interval); + + dispatch_source_set_timer(_saveTimer, startTime, interval, 1ull * NSEC_PER_SEC); + + if (_saveTimerSuspended) { + dispatch_resume(_saveTimer); + _saveTimerSuspended = NO; + } + } +} + +- (void)createSuspendedSaveTimer { + if ((_saveTimer == NULL) && (_saveInterval > 0.0)) { + _saveTimer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, self.loggerQueue); + + dispatch_source_set_event_handler(_saveTimer, ^{ @autoreleasepool { + [self performSaveAndSuspendSaveTimer]; + } }); + + _saveTimerSuspended = YES; + } +} + +- (void)destroyDeleteTimer { + if (_deleteTimer) { + dispatch_source_cancel(_deleteTimer); + #if !OS_OBJECT_USE_OBJC + dispatch_release(_deleteTimer); + #endif + _deleteTimer = NULL; + } +} + +- (void)updateDeleteTimer { + if ((_deleteTimer != NULL) && (_deleteInterval > 0.0) && (_maxAge > 0.0)) { + uint64_t interval = (uint64_t)(_deleteInterval * (NSTimeInterval) NSEC_PER_SEC); + dispatch_time_t startTime; + + if (_lastDeleteTime > 0) { + startTime = dispatch_time(_lastDeleteTime, interval); + } else { + startTime = dispatch_time(DISPATCH_TIME_NOW, interval); + } + + dispatch_source_set_timer(_deleteTimer, startTime, interval, 1ull * NSEC_PER_SEC); + } +} + +- (void)createAndStartDeleteTimer { + if ((_deleteTimer == NULL) && (_deleteInterval > 0.0) && (_maxAge > 0.0)) { + _deleteTimer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, self.loggerQueue); + + if (_deleteTimer != NULL) { + dispatch_source_set_event_handler(_deleteTimer, ^{ @autoreleasepool { + [self performDelete]; + } }); + + [self updateDeleteTimer]; + + if (_deleteTimer != NULL) { + dispatch_resume(_deleteTimer); + } + } + } +} + +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +#pragma mark Configuration +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +- (NSUInteger)saveThreshold { + // The design of this method is taken from the DDAbstractLogger implementation. + // For extensive documentation please refer to the DDAbstractLogger implementation. + + // Note: The internal implementation MUST access the colorsEnabled variable directly, + // This method is designed explicitly for external access. + // + // Using "self." syntax to go through this method will cause immediate deadlock. + // This is the intended result. Fix it by accessing the ivar directly. + // Great strides have been take to ensure this is safe to do. Plus it's MUCH faster. + + NSAssert(![self isOnGlobalLoggingQueue], @"Core architecture requirement failure"); + NSAssert(![self isOnInternalLoggerQueue], @"MUST access ivar directly, NOT via self.* syntax."); + + dispatch_queue_t globalLoggingQueue = [DDLog loggingQueue]; + + __block NSUInteger result; + + dispatch_sync(globalLoggingQueue, ^{ + dispatch_sync(self.loggerQueue, ^{ + result = self->_saveThreshold; + }); + }); + + return result; +} + +- (void)setSaveThreshold:(NSUInteger)threshold { + dispatch_block_t block = ^{ + @autoreleasepool { + if (self->_saveThreshold != threshold) { + self->_saveThreshold = threshold; + + // Since the saveThreshold has changed, + // we check to see if the current unsavedCount has surpassed the new threshold. + // + // If it has, we immediately save the log. + + if ((self->_unsavedCount >= self->_saveThreshold) && (self->_saveThreshold > 0)) { + [self performSaveAndSuspendSaveTimer]; + } + } + } + }; + + // The design of the setter logic below is taken from the DDAbstractLogger implementation. + // For documentation please refer to the DDAbstractLogger implementation. + + if ([self isOnInternalLoggerQueue]) { + block(); + } else { + dispatch_queue_t globalLoggingQueue = [DDLog loggingQueue]; + NSAssert(![self isOnGlobalLoggingQueue], @"Core architecture requirement failure"); + + dispatch_async(globalLoggingQueue, ^{ + dispatch_async(self.loggerQueue, block); + }); + } +} + +- (NSTimeInterval)saveInterval { + // The design of this method is taken from the DDAbstractLogger implementation. + // For extensive documentation please refer to the DDAbstractLogger implementation. + + // Note: The internal implementation MUST access the colorsEnabled variable directly, + // This method is designed explicitly for external access. + // + // Using "self." syntax to go through this method will cause immediate deadlock. + // This is the intended result. Fix it by accessing the ivar directly. + // Great strides have been take to ensure this is safe to do. Plus it's MUCH faster. + + NSAssert(![self isOnGlobalLoggingQueue], @"Core architecture requirement failure"); + NSAssert(![self isOnInternalLoggerQueue], @"MUST access ivar directly, NOT via self.* syntax."); + + dispatch_queue_t globalLoggingQueue = [DDLog loggingQueue]; + + __block NSTimeInterval result; + + dispatch_sync(globalLoggingQueue, ^{ + dispatch_sync(self.loggerQueue, ^{ + result = self->_saveInterval; + }); + }); + + return result; +} + +- (void)setSaveInterval:(NSTimeInterval)interval { + dispatch_block_t block = ^{ + @autoreleasepool { + // C99 recommended floating point comparison macro + // Read: isLessThanOrGreaterThan(floatA, floatB) + + if (/* saveInterval != interval */ islessgreater(self->_saveInterval, interval)) { + self->_saveInterval = interval; + + // There are several cases we need to handle here. + // + // 1. If the saveInterval was previously enabled and it just got disabled, + // then we need to stop the saveTimer. (And we might as well release it.) + // + // 2. If the saveInterval was previously disabled and it just got enabled, + // then we need to setup the saveTimer. (Plus we might need to do an immediate save.) + // + // 3. If the saveInterval increased, then we need to reset the timer so that it fires at the later date. + // + // 4. If the saveInterval decreased, then we need to reset the timer so that it fires at an earlier date. + // (Plus we might need to do an immediate save.) + + if (self->_saveInterval > 0.0) { + if (self->_saveTimer == NULL) { + // Handles #2 + // + // Since the saveTimer uses the unsavedTime to calculate it's first fireDate, + // if a save is needed the timer will fire immediately. + + [self createSuspendedSaveTimer]; + [self updateAndResumeSaveTimer]; + } else { + // Handles #3 + // Handles #4 + // + // Since the saveTimer uses the unsavedTime to calculate it's first fireDate, + // if a save is needed the timer will fire immediately. + + [self updateAndResumeSaveTimer]; + } + } else if (self->_saveTimer) { + // Handles #1 + + [self destroySaveTimer]; + } + } + } + }; + + // The design of the setter logic below is taken from the DDAbstractLogger implementation. + // For documentation please refer to the DDAbstractLogger implementation. + + if ([self isOnInternalLoggerQueue]) { + block(); + } else { + dispatch_queue_t globalLoggingQueue = [DDLog loggingQueue]; + NSAssert(![self isOnGlobalLoggingQueue], @"Core architecture requirement failure"); + + dispatch_async(globalLoggingQueue, ^{ + dispatch_async(self.loggerQueue, block); + }); + } +} + +- (NSTimeInterval)maxAge { + // The design of this method is taken from the DDAbstractLogger implementation. + // For extensive documentation please refer to the DDAbstractLogger implementation. + + // Note: The internal implementation MUST access the colorsEnabled variable directly, + // This method is designed explicitly for external access. + // + // Using "self." syntax to go through this method will cause immediate deadlock. + // This is the intended result. Fix it by accessing the ivar directly. + // Great strides have been take to ensure this is safe to do. Plus it's MUCH faster. + + NSAssert(![self isOnGlobalLoggingQueue], @"Core architecture requirement failure"); + NSAssert(![self isOnInternalLoggerQueue], @"MUST access ivar directly, NOT via self.* syntax."); + + dispatch_queue_t globalLoggingQueue = [DDLog loggingQueue]; + + __block NSTimeInterval result; + + dispatch_sync(globalLoggingQueue, ^{ + dispatch_sync(self.loggerQueue, ^{ + result = self->_maxAge; + }); + }); + + return result; +} + +- (void)setMaxAge:(NSTimeInterval)interval { + dispatch_block_t block = ^{ + @autoreleasepool { + // C99 recommended floating point comparison macro + // Read: isLessThanOrGreaterThan(floatA, floatB) + + if (/* maxAge != interval */ islessgreater(self->_maxAge, interval)) { + NSTimeInterval oldMaxAge = self->_maxAge; + NSTimeInterval newMaxAge = interval; + + self->_maxAge = interval; + + // There are several cases we need to handle here. + // + // 1. If the maxAge was previously enabled and it just got disabled, + // then we need to stop the deleteTimer. (And we might as well release it.) + // + // 2. If the maxAge was previously disabled and it just got enabled, + // then we need to setup the deleteTimer. (Plus we might need to do an immediate delete.) + // + // 3. If the maxAge was increased, + // then we don't need to do anything. + // + // 4. If the maxAge was decreased, + // then we should do an immediate delete. + + BOOL shouldDeleteNow = NO; + + if (oldMaxAge > 0.0) { + if (newMaxAge <= 0.0) { + // Handles #1 + + [self destroyDeleteTimer]; + } else if (oldMaxAge > newMaxAge) { + // Handles #4 + shouldDeleteNow = YES; + } + } else if (newMaxAge > 0.0) { + // Handles #2 + shouldDeleteNow = YES; + } + + if (shouldDeleteNow) { + [self performDelete]; + + if (self->_deleteTimer) { + [self updateDeleteTimer]; + } else { + [self createAndStartDeleteTimer]; + } + } + } + } + }; + + // The design of the setter logic below is taken from the DDAbstractLogger implementation. + // For documentation please refer to the DDAbstractLogger implementation. + + if ([self isOnInternalLoggerQueue]) { + block(); + } else { + dispatch_queue_t globalLoggingQueue = [DDLog loggingQueue]; + NSAssert(![self isOnGlobalLoggingQueue], @"Core architecture requirement failure"); + + dispatch_async(globalLoggingQueue, ^{ + dispatch_async(self.loggerQueue, block); + }); + } +} + +- (NSTimeInterval)deleteInterval { + // The design of this method is taken from the DDAbstractLogger implementation. + // For extensive documentation please refer to the DDAbstractLogger implementation. + + // Note: The internal implementation MUST access the colorsEnabled variable directly, + // This method is designed explicitly for external access. + // + // Using "self." syntax to go through this method will cause immediate deadlock. + // This is the intended result. Fix it by accessing the ivar directly. + // Great strides have been take to ensure this is safe to do. Plus it's MUCH faster. + + NSAssert(![self isOnGlobalLoggingQueue], @"Core architecture requirement failure"); + NSAssert(![self isOnInternalLoggerQueue], @"MUST access ivar directly, NOT via self.* syntax."); + + dispatch_queue_t globalLoggingQueue = [DDLog loggingQueue]; + + __block NSTimeInterval result; + + dispatch_sync(globalLoggingQueue, ^{ + dispatch_sync(self.loggerQueue, ^{ + result = self->_deleteInterval; + }); + }); + + return result; +} + +- (void)setDeleteInterval:(NSTimeInterval)interval { + dispatch_block_t block = ^{ + @autoreleasepool { + // C99 recommended floating point comparison macro + // Read: isLessThanOrGreaterThan(floatA, floatB) + + if (/* deleteInterval != interval */ islessgreater(self->_deleteInterval, interval)) { + self->_deleteInterval = interval; + + // There are several cases we need to handle here. + // + // 1. If the deleteInterval was previously enabled and it just got disabled, + // then we need to stop the deleteTimer. (And we might as well release it.) + // + // 2. If the deleteInterval was previously disabled and it just got enabled, + // then we need to setup the deleteTimer. (Plus we might need to do an immediate delete.) + // + // 3. If the deleteInterval increased, then we need to reset the timer so that it fires at the later date. + // + // 4. If the deleteInterval decreased, then we need to reset the timer so that it fires at an earlier date. + // (Plus we might need to do an immediate delete.) + + if (self->_deleteInterval > 0.0) { + if (self->_deleteTimer == NULL) { + // Handles #2 + // + // Since the deleteTimer uses the lastDeleteTime to calculate it's first fireDate, + // if a delete is needed the timer will fire immediately. + + [self createAndStartDeleteTimer]; + } else { + // Handles #3 + // Handles #4 + // + // Since the deleteTimer uses the lastDeleteTime to calculate it's first fireDate, + // if a save is needed the timer will fire immediately. + + [self updateDeleteTimer]; + } + } else if (self->_deleteTimer) { + // Handles #1 + + [self destroyDeleteTimer]; + } + } + } + }; + + // The design of the setter logic below is taken from the DDAbstractLogger implementation. + // For documentation please refer to the DDAbstractLogger implementation. + + if ([self isOnInternalLoggerQueue]) { + block(); + } else { + dispatch_queue_t globalLoggingQueue = [DDLog loggingQueue]; + NSAssert(![self isOnGlobalLoggingQueue], @"Core architecture requirement failure"); + + dispatch_async(globalLoggingQueue, ^{ + dispatch_async(self.loggerQueue, block); + }); + } +} + +- (BOOL)deleteOnEverySave { + // The design of this method is taken from the DDAbstractLogger implementation. + // For extensive documentation please refer to the DDAbstractLogger implementation. + + // Note: The internal implementation MUST access the colorsEnabled variable directly, + // This method is designed explicitly for external access. + // + // Using "self." syntax to go through this method will cause immediate deadlock. + // This is the intended result. Fix it by accessing the ivar directly. + // Great strides have been take to ensure this is safe to do. Plus it's MUCH faster. + + NSAssert(![self isOnGlobalLoggingQueue], @"Core architecture requirement failure"); + NSAssert(![self isOnInternalLoggerQueue], @"MUST access ivar directly, NOT via self.* syntax."); + + dispatch_queue_t globalLoggingQueue = [DDLog loggingQueue]; + + __block BOOL result; + + dispatch_sync(globalLoggingQueue, ^{ + dispatch_sync(self.loggerQueue, ^{ + result = self->_deleteOnEverySave; + }); + }); + + return result; +} + +- (void)setDeleteOnEverySave:(BOOL)flag { + dispatch_block_t block = ^{ + self->_deleteOnEverySave = flag; + }; + + // The design of the setter logic below is taken from the DDAbstractLogger implementation. + // For documentation please refer to the DDAbstractLogger implementation. + + if ([self isOnInternalLoggerQueue]) { + block(); + } else { + dispatch_queue_t globalLoggingQueue = [DDLog loggingQueue]; + NSAssert(![self isOnGlobalLoggingQueue], @"Core architecture requirement failure"); + + dispatch_async(globalLoggingQueue, ^{ + dispatch_async(self.loggerQueue, block); + }); + } +} + +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +#pragma mark Public API +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +- (void)savePendingLogEntries { + dispatch_block_t block = ^{ + @autoreleasepool { + [self performSaveAndSuspendSaveTimer]; + } + }; + + if ([self isOnInternalLoggerQueue]) { + block(); + } else { + dispatch_async(self.loggerQueue, block); + } +} + +- (void)deleteOldLogEntries { + dispatch_block_t block = ^{ + @autoreleasepool { + [self performDelete]; + } + }; + + if ([self isOnInternalLoggerQueue]) { + block(); + } else { + dispatch_async(self.loggerQueue, block); + } +} + +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +#pragma mark DDLogger +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +- (void)didAddLogger { + // If you override me be sure to invoke [super didAddLogger]; + + [self createSuspendedSaveTimer]; + + [self createAndStartDeleteTimer]; +} + +- (void)willRemoveLogger { + // If you override me be sure to invoke [super willRemoveLogger]; + + [self performSaveAndSuspendSaveTimer]; + + [self destroySaveTimer]; + [self destroyDeleteTimer]; +} + +- (void)logMessage:(DDLogMessage *)logMessage { + if ([self db_log:logMessage]) { + BOOL firstUnsavedEntry = (++_unsavedCount == 1); + + if ((_unsavedCount >= _saveThreshold) && (_saveThreshold > 0)) { + [self performSaveAndSuspendSaveTimer]; + } else if (firstUnsavedEntry) { + _unsavedTime = dispatch_time(DISPATCH_TIME_NOW, 0); + [self updateAndResumeSaveTimer]; + } + } +} + +- (void)flush { + // This method is invoked by DDLog's flushLog method. + // + // It is called automatically when the application quits, + // or if the developer invokes DDLog's flushLog method prior to crashing or something. + + [self performSaveAndSuspendSaveTimer]; +} + +@end diff --git a/Pods/CocoaLumberjack/Classes/DDAssertMacros.h b/Pods/CocoaLumberjack/Classes/DDAssertMacros.h new file mode 100644 index 0000000..7678a9c --- /dev/null +++ b/Pods/CocoaLumberjack/Classes/DDAssertMacros.h @@ -0,0 +1,26 @@ +// Software License Agreement (BSD License) +// +// Copyright (c) 2010-2016, Deusty, LLC +// All rights reserved. +// +// Redistribution and use of this software in source and binary forms, +// with or without modification, are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Neither the name of Deusty nor the names of its contributors may be used +// to endorse or promote products derived from this software without specific +// prior written permission of Deusty, LLC. + +/** + * NSAsset replacement that will output a log message even when assertions are disabled. + **/ +#define DDAssert(condition, frmt, ...) \ + if (!(condition)) { \ + NSString *description = [NSString stringWithFormat:frmt, ## __VA_ARGS__]; \ + DDLogError(@"%@", description); \ + NSAssert(NO, description); \ + } +#define DDAssertCondition(condition) DDAssert(condition, @"Condition not satisfied: %s", #condition) + diff --git a/Pods/CocoaLumberjack/Classes/DDFileLogger.h b/Pods/CocoaLumberjack/Classes/DDFileLogger.h new file mode 100644 index 0000000..14eb892 --- /dev/null +++ b/Pods/CocoaLumberjack/Classes/DDFileLogger.h @@ -0,0 +1,517 @@ +// Software License Agreement (BSD License) +// +// Copyright (c) 2010-2016, Deusty, LLC +// All rights reserved. +// +// Redistribution and use of this software in source and binary forms, +// with or without modification, are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright notice, +// this list of conditions and the following disclaimer. +// +// * Neither the name of Deusty nor the names of its contributors may be used +// to endorse or promote products derived from this software without specific +// prior written permission of Deusty, LLC. + +// Disable legacy macros +#ifndef DD_LEGACY_MACROS + #define DD_LEGACY_MACROS 0 +#endif + +#import "DDLog.h" + +@class DDLogFileInfo; + +/** + * This class provides a logger to write log statements to a file. + **/ + + +// Default configuration and safety/sanity values. +// +// maximumFileSize -> kDDDefaultLogMaxFileSize +// rollingFrequency -> kDDDefaultLogRollingFrequency +// maximumNumberOfLogFiles -> kDDDefaultLogMaxNumLogFiles +// logFilesDiskQuota -> kDDDefaultLogFilesDiskQuota +// +// You should carefully consider the proper configuration values for your application. + +extern unsigned long long const kDDDefaultLogMaxFileSize; +extern NSTimeInterval const kDDDefaultLogRollingFrequency; +extern NSUInteger const kDDDefaultLogMaxNumLogFiles; +extern unsigned long long const kDDDefaultLogFilesDiskQuota; + + +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +#pragma mark - +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/** + * The LogFileManager protocol is designed to allow you to control all aspects of your log files. + * + * The primary purpose of this is to allow you to do something with the log files after they have been rolled. + * Perhaps you want to compress them to save disk space. + * Perhaps you want to upload them to an FTP server. + * Perhaps you want to run some analytics on the file. + * + * A default LogFileManager is, of course, provided. + * The default LogFileManager simply deletes old log files according to the maximumNumberOfLogFiles property. + * + * This protocol provides various methods to fetch the list of log files. + * + * There are two variants: sorted and unsorted. + * If sorting is not necessary, the unsorted variant is obviously faster. + * The sorted variant will return an array sorted by when the log files were created, + * with the most recently created log file at index 0, and the oldest log file at the end of the array. + * + * You can fetch only the log file paths (full path including name), log file names (name only), + * or an array of `DDLogFileInfo` objects. + * The `DDLogFileInfo` class is documented below, and provides a handy wrapper that + * gives you easy access to various file attributes such as the creation date or the file size. + */ +@protocol DDLogFileManager +@required + +// Public properties + +/** + * The maximum number of archived log files to keep on disk. + * For example, if this property is set to 3, + * then the LogFileManager will only keep 3 archived log files (plus the current active log file) on disk. + * Once the active log file is rolled/archived, then the oldest of the existing 3 rolled/archived log files is deleted. + * + * You may optionally disable this option by setting it to zero. + **/ +@property (readwrite, assign, atomic) NSUInteger maximumNumberOfLogFiles; + +/** + * The maximum space that logs can take. On rolling logfile all old logfiles that exceed logFilesDiskQuota will + * be deleted. + * + * You may optionally disable this option by setting it to zero. + **/ +@property (readwrite, assign, atomic) unsigned long long logFilesDiskQuota; + +// Public methods + +/** + * Returns the logs directory (path) + */ +@property (nonatomic, readonly, copy) NSString *logsDirectory; + +/** + * Returns an array of `NSString` objects, + * each of which is the filePath to an existing log file on disk. + **/ +@property (nonatomic, readonly, strong) NSArray *unsortedLogFilePaths; + +/** + * Returns an array of `NSString` objects, + * each of which is the fileName of an existing log file on disk. + **/ +@property (nonatomic, readonly, strong) NSArray *unsortedLogFileNames; + +/** + * Returns an array of `DDLogFileInfo` objects, + * each representing an existing log file on disk, + * and containing important information about the log file such as it's modification date and size. + **/ +@property (nonatomic, readonly, strong) NSArray *unsortedLogFileInfos; + +/** + * Just like the `unsortedLogFilePaths` method, but sorts the array. + * The items in the array are sorted by creation date. + * The first item in the array will be the most recently created log file. + **/ +@property (nonatomic, readonly, strong) NSArray *sortedLogFilePaths; + +/** + * Just like the `unsortedLogFileNames` method, but sorts the array. + * The items in the array are sorted by creation date. + * The first item in the array will be the most recently created log file. + **/ +@property (nonatomic, readonly, strong) NSArray *sortedLogFileNames; + +/** + * Just like the `unsortedLogFileInfos` method, but sorts the array. + * The items in the array are sorted by creation date. + * The first item in the array will be the most recently created log file. + **/ +@property (nonatomic, readonly, strong) NSArray *sortedLogFileInfos; + +// Private methods (only to be used by DDFileLogger) + +/** + * Generates a new unique log file path, and creates the corresponding log file. + **/ +- (NSString *)createNewLogFile; + +@optional + +// Notifications from DDFileLogger + +/** + * Called when a log file was archieved + */ +- (void)didArchiveLogFile:(NSString *)logFilePath NS_SWIFT_NAME(didArchiveLogFile(atPath:)); + +/** + * Called when the roll action was executed and the log was archieved + */ +- (void)didRollAndArchiveLogFile:(NSString *)logFilePath NS_SWIFT_NAME(didRollAndArchiveLogFile(atPath:)); + +@end + +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +#pragma mark - +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/** + * Default log file manager. + * + * All log files are placed inside the logsDirectory. + * If a specific logsDirectory isn't specified, the default directory is used. + * On Mac, this is in `~/Library/Logs/`. + * On iPhone, this is in `~/Library/Caches/Logs`. + * + * Log files are named `"