From e20f2b75f08884c080336d7aa06143371633bd8a Mon Sep 17 00:00:00 2001 From: Aparajita Fishman Date: Thu, 2 May 2013 17:24:29 -0400 Subject: [PATCH] Fixed: Lumberjack files were missing. --- .../XcodeCapp.xcodeproj/project.pbxproj | 46 +- Tools/XcodeCapp/XcodeCapp/AppController.m | 1 + .../XcodeCapp/Lumberjack/DDASLLogger.h | 41 + .../XcodeCapp/Lumberjack/DDASLLogger.m | 99 ++ Tools/XcodeCapp/XcodeCapp/Lumberjack/DDLog.h | 601 +++++++ Tools/XcodeCapp/XcodeCapp/Lumberjack/DDLog.m | 1085 ++++++++++++ .../XcodeCapp/Lumberjack/DDTTYLogger.h | 167 ++ .../XcodeCapp/Lumberjack/DDTTYLogger.m | 1490 +++++++++++++++++ 8 files changed, 3510 insertions(+), 20 deletions(-) create mode 100755 Tools/XcodeCapp/XcodeCapp/Lumberjack/DDASLLogger.h create mode 100755 Tools/XcodeCapp/XcodeCapp/Lumberjack/DDASLLogger.m create mode 100755 Tools/XcodeCapp/XcodeCapp/Lumberjack/DDLog.h create mode 100755 Tools/XcodeCapp/XcodeCapp/Lumberjack/DDLog.m create mode 100755 Tools/XcodeCapp/XcodeCapp/Lumberjack/DDTTYLogger.h create mode 100755 Tools/XcodeCapp/XcodeCapp/Lumberjack/DDTTYLogger.m diff --git a/Tools/XcodeCapp/XcodeCapp.xcodeproj/project.pbxproj b/Tools/XcodeCapp/XcodeCapp.xcodeproj/project.pbxproj index 5a46a21d7..f94572ee9 100644 --- a/Tools/XcodeCapp/XcodeCapp.xcodeproj/project.pbxproj +++ b/Tools/XcodeCapp/XcodeCapp.xcodeproj/project.pbxproj @@ -7,8 +7,6 @@ objects = { /* Begin PBXBuildFile section */ - E100444B172E25690062E953 /* DDLog.m in Sources */ = {isa = PBXBuildFile; fileRef = E1004448172E25690062E953 /* DDLog.m */; }; - E100444C172E25690062E953 /* DDTTYLogger.m in Sources */ = {isa = PBXBuildFile; fileRef = E100444A172E25690062E953 /* DDTTYLogger.m */; }; E10E4CD717298E5F004AB09E /* project.pbxproj in Resources */ = {isa = PBXBuildFile; fileRef = E164FE2A172188F300263CE3 /* project.pbxproj */; }; E164FDD31720E77100263CE3 /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E164FDD21720E77100263CE3 /* Cocoa.framework */; }; E164FDDF1720E77100263CE3 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = E164FDDE1720E77100263CE3 /* main.m */; }; @@ -36,6 +34,9 @@ E17A822D172700B90095CD83 /* XcodeCapp.iconset in Resources */ = {isa = PBXBuildFile; fileRef = E17A822C172700B90095CD83 /* XcodeCapp.iconset */; }; E17B55411732F57700809FFB /* Quartz.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E17B55401732F57700809FFB /* Quartz.framework */; }; E17B55431732F72700809FFB /* help.pdf in Resources */ = {isa = PBXBuildFile; fileRef = E17B55421732F72700809FFB /* help.pdf */; }; + E17B554D17330E2800809FFB /* DDASLLogger.m in Sources */ = {isa = PBXBuildFile; fileRef = E17B554817330E2800809FFB /* DDASLLogger.m */; }; + E17B554E17330E2800809FFB /* DDLog.m in Sources */ = {isa = PBXBuildFile; fileRef = E17B554A17330E2800809FFB /* DDLog.m */; }; + E17B554F17330E2800809FFB /* DDTTYLogger.m in Sources */ = {isa = PBXBuildFile; fileRef = E17B554C17330E2800809FFB /* DDTTYLogger.m */; }; E1A5594A172C5EE20088FB61 /* FindSourceFilesOperation.m in Sources */ = {isa = PBXBuildFile; fileRef = E1A55949172C5EE20088FB61 /* FindSourceFilesOperation.m */; }; E1A5594D172CA8D40088FB61 /* ProcessSourceOperation.m in Sources */ = {isa = PBXBuildFile; fileRef = E1A5594C172CA8D40088FB61 /* ProcessSourceOperation.m */; }; E1A55954172CBA8E0088FB61 /* Notifications.m in Sources */ = {isa = PBXBuildFile; fileRef = E1A55953172CBA8E0088FB61 /* Notifications.m */; }; @@ -68,10 +69,6 @@ /* End PBXCopyFilesBuildPhase section */ /* Begin PBXFileReference section */ - E1004447172E25690062E953 /* DDLog.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; lineEnding = 0; name = DDLog.h; path = ../../../../../Cocoa/CocoaLumberjack/Lumberjack/DDLog.h; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.objcpp; }; - E1004448172E25690062E953 /* DDLog.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; lineEnding = 0; name = DDLog.m; path = ../../../../../Cocoa/CocoaLumberjack/Lumberjack/DDLog.m; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.objc; }; - E1004449172E25690062E953 /* DDTTYLogger.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; lineEnding = 0; name = DDTTYLogger.h; path = ../../../../../Cocoa/CocoaLumberjack/Lumberjack/DDTTYLogger.h; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.objcpp; }; - E100444A172E25690062E953 /* DDTTYLogger.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; name = DDTTYLogger.m; path = ../../../../../Cocoa/CocoaLumberjack/Lumberjack/DDTTYLogger.m; sourceTree = ""; }; E132ED991724B71600D870AE /* XcodeCapp.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = XcodeCapp.h; sourceTree = ""; }; E132ED9A1724B71D00D870AE /* AppController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = AppController.h; sourceTree = ""; }; E164FDCF1720E77100263CE3 /* XcodeCapp.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = XcodeCapp.app; sourceTree = BUILT_PRODUCTS_DIR; }; @@ -107,6 +104,12 @@ E17A822C172700B90095CD83 /* XcodeCapp.iconset */ = {isa = PBXFileReference; lastKnownFileType = folder.iconset; name = XcodeCapp.iconset; path = Resources/XcodeCapp.iconset; sourceTree = ""; }; E17B55401732F57700809FFB /* Quartz.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Quartz.framework; path = System/Library/Frameworks/Quartz.framework; sourceTree = SDKROOT; }; E17B55421732F72700809FFB /* help.pdf */ = {isa = PBXFileReference; lastKnownFileType = image.pdf; name = help.pdf; path = Resources/help.pdf; sourceTree = ""; }; + E17B554717330E2800809FFB /* DDASLLogger.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DDASLLogger.h; sourceTree = ""; }; + E17B554817330E2800809FFB /* DDASLLogger.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = DDASLLogger.m; sourceTree = ""; }; + E17B554917330E2800809FFB /* DDLog.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DDLog.h; sourceTree = ""; }; + E17B554A17330E2800809FFB /* DDLog.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = DDLog.m; sourceTree = ""; }; + E17B554B17330E2800809FFB /* DDTTYLogger.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = DDTTYLogger.h; sourceTree = ""; }; + E17B554C17330E2800809FFB /* DDTTYLogger.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = DDTTYLogger.m; sourceTree = ""; }; E1A55948172C5EE20088FB61 /* FindSourceFilesOperation.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; lineEnding = 0; path = FindSourceFilesOperation.h; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.objcpp; }; E1A55949172C5EE20088FB61 /* FindSourceFilesOperation.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; lineEnding = 0; path = FindSourceFilesOperation.m; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.objc; }; E1A5594B172CA8D40088FB61 /* ProcessSourceOperation.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = ProcessSourceOperation.h; sourceTree = ""; }; @@ -133,17 +136,6 @@ /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ - E1004446172E252C0062E953 /* Lumberjack (logging) */ = { - isa = PBXGroup; - children = ( - E1004447172E25690062E953 /* DDLog.h */, - E1004448172E25690062E953 /* DDLog.m */, - E1004449172E25690062E953 /* DDTTYLogger.h */, - E100444A172E25690062E953 /* DDTTYLogger.m */, - ); - name = "Lumberjack (logging)"; - sourceTree = ""; - }; E164FDC61720E77100263CE3 = { isa = PBXGroup; children = ( @@ -197,8 +189,8 @@ E1A55953172CBA8E0088FB61 /* Notifications.m */, E1E167EF172F3B20003198BA /* DDLogLevel.h */, E1E167F0172F3B20003198BA /* DDLogLevel.m */, + E17B554617330E2800809FFB /* Lumberjack */, E164FE121720F3E100263CE3 /* Scripts */, - E1004446172E252C0062E953 /* Lumberjack (logging) */, E164FE071720F25400263CE3 /* Resources */, E164FDD91720E77100263CE3 /* Supporting Files */, ); @@ -248,6 +240,19 @@ path = Scripts; sourceTree = ""; }; + E17B554617330E2800809FFB /* Lumberjack */ = { + isa = PBXGroup; + children = ( + E17B554717330E2800809FFB /* DDASLLogger.h */, + E17B554817330E2800809FFB /* DDASLLogger.m */, + E17B554917330E2800809FFB /* DDLog.h */, + E17B554A17330E2800809FFB /* DDLog.m */, + E17B554B17330E2800809FFB /* DDTTYLogger.h */, + E17B554C17330E2800809FFB /* DDTTYLogger.m */, + ); + path = Lumberjack; + sourceTree = ""; + }; E1A55947172C5EA80088FB61 /* Operations */ = { isa = PBXGroup; children = ( @@ -344,9 +349,10 @@ E1A5594A172C5EE20088FB61 /* FindSourceFilesOperation.m in Sources */, E1A5594D172CA8D40088FB61 /* ProcessSourceOperation.m in Sources */, E1A55954172CBA8E0088FB61 /* Notifications.m in Sources */, - E100444B172E25690062E953 /* DDLog.m in Sources */, - E100444C172E25690062E953 /* DDTTYLogger.m in Sources */, E1E167F1172F3B20003198BA /* DDLogLevel.m in Sources */, + E17B554D17330E2800809FFB /* DDASLLogger.m in Sources */, + E17B554E17330E2800809FFB /* DDLog.m in Sources */, + E17B554F17330E2800809FFB /* DDTTYLogger.m in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/Tools/XcodeCapp/XcodeCapp/AppController.m b/Tools/XcodeCapp/XcodeCapp/AppController.m index 94f4ecbef..507c561a7 100644 --- a/Tools/XcodeCapp/XcodeCapp/AppController.m +++ b/Tools/XcodeCapp/XcodeCapp/AppController.m @@ -145,6 +145,7 @@ AppController *SharedAppControllerInstance = nil; NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults]; [defaults registerDefaults:appDefaults]; + [defaults synchronize]; [defaults addObserver:self forKeyPath:kDefaultMaxRecentProjects diff --git a/Tools/XcodeCapp/XcodeCapp/Lumberjack/DDASLLogger.h b/Tools/XcodeCapp/XcodeCapp/Lumberjack/DDASLLogger.h new file mode 100755 index 000000000..0f2e9633e --- /dev/null +++ b/Tools/XcodeCapp/XcodeCapp/Lumberjack/DDASLLogger.h @@ -0,0 +1,41 @@ +#import +#import + +#import "DDLog.h" + +/** + * Welcome to Cocoa Lumberjack! + * + * The project page has a wealth of documentation if you have any questions. + * https://github.com/robbiehanson/CocoaLumberjack + * + * If you're new to the project you may wish to read the "Getting Started" wiki. + * https://github.com/robbiehanson/CocoaLumberjack/wiki/GettingStarted + * + * + * This class provides a logger for the Apple System Log facility. + * + * As described in the "Getting Started" page, + * the traditional NSLog() function directs it's 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 +{ + aslclient client; +} + ++ (DDASLLogger *)sharedInstance; + +// Inherited from DDAbstractLogger + +// - (id )logFormatter; +// - (void)setLogFormatter:(id )formatter; + +@end diff --git a/Tools/XcodeCapp/XcodeCapp/Lumberjack/DDASLLogger.m b/Tools/XcodeCapp/XcodeCapp/Lumberjack/DDASLLogger.m new file mode 100755 index 000000000..0c35f2fcf --- /dev/null +++ b/Tools/XcodeCapp/XcodeCapp/Lumberjack/DDASLLogger.m @@ -0,0 +1,99 @@ +#import "DDASLLogger.h" + +#import + +/** + * Welcome to Cocoa Lumberjack! + * + * The project page has a wealth of documentation if you have any questions. + * https://github.com/robbiehanson/CocoaLumberjack + * + * If you're new to the project you may wish to read the "Getting Started" wiki. + * https://github.com/robbiehanson/CocoaLumberjack/wiki/GettingStarted +**/ + +#if ! __has_feature(objc_arc) +#warning This file must be compiled with ARC. Use -fobjc-arc flag (or convert project to ARC). +#endif + + +@implementation DDASLLogger + +static DDASLLogger *sharedInstance; + +/** + * The runtime sends initialize to each class in a program exactly one time just before the class, + * or any class that inherits from it, is sent its first message from within the program. (Thus the + * method may never be invoked if the class is not used.) The runtime sends the initialize message to + * classes in a thread-safe manner. Superclasses receive this message before their subclasses. + * + * This method may also be called directly (assumably by accident), hence the safety mechanism. +**/ ++ (void)initialize +{ + static BOOL initialized = NO; + if (!initialized) + { + initialized = YES; + + sharedInstance = [[DDASLLogger alloc] init]; + } +} + ++ (DDASLLogger *)sharedInstance +{ + return sharedInstance; +} + +- (id)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 +{ + NSString *logMsg = logMessage->logMsg; + + if (formatter) + { + logMsg = [formatter formatLogMessage:logMessage]; + } + + if (logMsg) + { + const char *msg = [logMsg UTF8String]; + + int aslLogLevel; + switch (logMessage->logFlag) + { + // Note: By default ASL will filter anything above level 5 (Notice). + // So our mappings shouldn't go above that level. + + case LOG_FLAG_ERROR : aslLogLevel = ASL_LEVEL_CRIT; break; + case LOG_FLAG_WARN : aslLogLevel = ASL_LEVEL_ERR; break; + case LOG_FLAG_INFO : aslLogLevel = ASL_LEVEL_WARNING; break; + default : aslLogLevel = ASL_LEVEL_NOTICE; break; + } + + asl_log(client, NULL, aslLogLevel, "%s", msg); + } +} + +- (NSString *)loggerName +{ + return @"cocoa.lumberjack.aslLogger"; +} + +@end diff --git a/Tools/XcodeCapp/XcodeCapp/Lumberjack/DDLog.h b/Tools/XcodeCapp/XcodeCapp/Lumberjack/DDLog.h new file mode 100755 index 000000000..5da184938 --- /dev/null +++ b/Tools/XcodeCapp/XcodeCapp/Lumberjack/DDLog.h @@ -0,0 +1,601 @@ +#import + +/** + * Welcome to Cocoa Lumberjack! + * + * The project page has a wealth of documentation if you have any questions. + * https://github.com/robbiehanson/CocoaLumberjack + * + * If you're new to the project you may wish to read the "Getting Started" wiki. + * https://github.com/robbiehanson/CocoaLumberjack/wiki/GettingStarted + * + * Otherwise, here is a quick refresher. + * There are three steps to using the macros: + * + * Step 1: + * Import the header in your implementation file: + * + * #import "DDLog.h" + * + * Step 2: + * Define your logging level in your implementation file: + * + * // Log levels: off, error, warn, info, verbose + * static const int ddLogLevel = LOG_LEVEL_VERBOSE; + * + * 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. +**/ + + +@class DDLogMessage; + +@protocol DDLogger; +@protocol DDLogFormatter; + +/** + * This is the single macro that all other macros below compile into. + * This big multiline macro makes all the other macros easier to read. +**/ + +#define LOG_MACRO(isAsynchronous, lvl, flg, ctx, atag, fnct, frmt, ...) \ + [DDLog log:isAsynchronous \ + level:lvl \ + flag:flg \ + context:ctx \ + file:__FILE__ \ + function:fnct \ + line:__LINE__ \ + tag:atag \ + format:(frmt), ##__VA_ARGS__] + +/** + * Define the Objective-C and C versions of the macro. + * These automatically inject the proper function name for either an objective-c method or c function. + * + * We also define shorthand versions for asynchronous and synchronous logging. +**/ + +#define LOG_OBJC_MACRO(async, lvl, flg, ctx, frmt, ...) \ + LOG_MACRO(async, lvl, flg, ctx, nil, sel_getName(_cmd), frmt, ##__VA_ARGS__) + +#define LOG_C_MACRO(async, lvl, flg, ctx, frmt, ...) \ + LOG_MACRO(async, lvl, flg, ctx, nil, __FUNCTION__, frmt, ##__VA_ARGS__) + +#define SYNC_LOG_OBJC_MACRO(lvl, flg, ctx, frmt, ...) \ + LOG_OBJC_MACRO( NO, lvl, flg, ctx, frmt, ##__VA_ARGS__) + +#define ASYNC_LOG_OBJC_MACRO(lvl, flg, ctx, frmt, ...) \ + LOG_OBJC_MACRO(YES, lvl, flg, ctx, frmt, ##__VA_ARGS__) + +#define SYNC_LOG_C_MACRO(lvl, flg, ctx, frmt, ...) \ + LOG_C_MACRO( NO, lvl, flg, ctx, frmt, ##__VA_ARGS__) + +#define ASYNC_LOG_C_MACRO(lvl, flg, ctx, frmt, ...) \ + LOG_C_MACRO(YES, lvl, flg, ctx, frmt, ##__VA_ARGS__) + +/** + * Define version of the macro that only execute if the logLevel is above the threshold. + * The compiled versions essentially look like this: + * + * if (logFlagForThisLogMsg & ddLogLevel) { execute log message } + * + * As shown further below, Lumberjack actually uses a bitmask as opposed to primitive log levels. + * This allows for a great amount of flexibility and some pretty advanced fine grained logging techniques. + * + * Note that when compiler optimizations are enabled (as they are for your release builds), + * the log messages above your logging threshold will automatically be compiled out. + * + * (If the compiler sees ddLogLevel declared as a constant, the compiler simply checks to see if the 'if' statement + * would execute, and if not it strips it from the binary.) + * + * We also define shorthand versions for asynchronous and synchronous logging. +**/ + +#define LOG_MAYBE(async, lvl, flg, ctx, fnct, frmt, ...) \ + do { if(lvl & flg) LOG_MACRO(async, lvl, flg, ctx, nil, fnct, frmt, ##__VA_ARGS__); } while(0) + +#define LOG_OBJC_MAYBE(async, lvl, flg, ctx, frmt, ...) \ + LOG_MAYBE(async, lvl, flg, ctx, sel_getName(_cmd), frmt, ##__VA_ARGS__) + +#define LOG_C_MAYBE(async, lvl, flg, ctx, frmt, ...) \ + LOG_MAYBE(async, lvl, flg, ctx, __FUNCTION__, frmt, ##__VA_ARGS__) + +#define SYNC_LOG_OBJC_MAYBE(lvl, flg, ctx, frmt, ...) \ + LOG_OBJC_MAYBE( NO, lvl, flg, ctx, frmt, ##__VA_ARGS__) + +#define ASYNC_LOG_OBJC_MAYBE(lvl, flg, ctx, frmt, ...) \ + LOG_OBJC_MAYBE(YES, lvl, flg, ctx, frmt, ##__VA_ARGS__) + +#define SYNC_LOG_C_MAYBE(lvl, flg, ctx, frmt, ...) \ + LOG_C_MAYBE( NO, lvl, flg, ctx, frmt, ##__VA_ARGS__) + +#define ASYNC_LOG_C_MAYBE(lvl, flg, ctx, frmt, ...) \ + LOG_C_MAYBE(YES, lvl, flg, ctx, frmt, ##__VA_ARGS__) + +/** + * Define versions of the macros that also accept tags. + * + * The DDLogMessage object includes a 'tag' ivar that may be used for a variety of purposes. + * It may be used to pass custom information to loggers or formatters. + * Or it may be used by 3rd party extensions to the framework. + * + * Thes macros just make it a little easier to extend logging functionality. +**/ + +#define LOG_OBJC_TAG_MACRO(async, lvl, flg, ctx, tag, frmt, ...) \ + LOG_MACRO(async, lvl, flg, ctx, tag, sel_getName(_cmd), frmt, ##__VA_ARGS__) + +#define LOG_C_TAG_MACRO(async, lvl, flg, ctx, tag, frmt, ...) \ + LOG_MACRO(async, lvl, flg, ctx, tag, __FUNCTION__, frmt, ##__VA_ARGS__) + +#define LOG_TAG_MAYBE(async, lvl, flg, ctx, tag, fnct, frmt, ...) \ + do { if(lvl & flg) LOG_MACRO(async, lvl, flg, ctx, tag, fnct, frmt, ##__VA_ARGS__); } while(0) + +#define LOG_OBJC_TAG_MAYBE(async, lvl, flg, ctx, tag, frmt, ...) \ + LOG_TAG_MAYBE(async, lvl, flg, ctx, tag, sel_getName(_cmd), frmt, ##__VA_ARGS__) + +#define LOG_C_TAG_MAYBE(async, lvl, flg, ctx, tag, frmt, ...) \ + LOG_TAG_MAYBE(async, lvl, flg, ctx, tag, __FUNCTION__, frmt, ##__VA_ARGS__) + +/** + * Define the standard options. + * + * We default to only 4 levels because it makes it easier for beginners + * to make the transition to a logging framework. + * + * More advanced users may choose to completely customize the levels (and level names) to suite their needs. + * For more information on this see the "Custom Log Levels" page: + * https://github.com/robbiehanson/CocoaLumberjack/wiki/CustomLogLevels + * + * Advanced users may also notice that we're using a bitmask. + * This is to allow for custom fine grained logging: + * https://github.com/robbiehanson/CocoaLumberjack/wiki/FineGrainedLogging + * + * -- Flags -- + * + * Typically you will use the LOG_LEVELS (see below), but the flags may be used directly in certain situations. + * For example, say you have a lot of warning log messages, and you wanted to disable them. + * However, you still needed to see your error and info log messages. + * You could accomplish that with the following: + * + * static const int ddLogLevel = LOG_FLAG_ERROR | LOG_FLAG_INFO; + * + * Flags may also be consulted when writing custom log formatters, + * as the DDLogMessage class captures the individual flag that caused the log message to fire. + * + * -- Levels -- + * + * Log levels are simply the proper bitmask of the flags. + * + * -- Booleans -- + * + * The booleans may be used when your logging code involves more than one line. + * For example: + * + * if (LOG_VERBOSE) { + * for (id sprocket in sprockets) + * DDLogVerbose(@"sprocket: %@", [sprocket description]) + * } + * + * -- Async -- + * + * Defines the default asynchronous options. + * The default philosophy for asynchronous logging is very simple: + * + * Log messages with errors should be executed synchronously. + * After all, an error just occurred. The application could be unstable. + * + * All other log messages, such as debug output, are executed asynchronously. + * After all, if it wasn't an error, then it was just informational output, + * or something the application was easily able to recover from. + * + * -- Changes -- + * + * You are strongly discouraged from modifying this file. + * If you do, you make it more difficult on yourself to merge future bug fixes and improvements from the project. + * Instead, create your own MyLogging.h or ApplicationNameLogging.h or CompanyLogging.h + * + * For an example of customizing your logging experience, see the "Custom Log Levels" page: + * https://github.com/robbiehanson/CocoaLumberjack/wiki/CustomLogLevels +**/ + +#define LOG_FLAG_ERROR (1 << 0) // 0...0001 +#define LOG_FLAG_WARN (1 << 1) // 0...0010 +#define LOG_FLAG_INFO (1 << 2) // 0...0100 +#define LOG_FLAG_VERBOSE (1 << 3) // 0...1000 + +#define LOG_LEVEL_OFF 0 +#define LOG_LEVEL_ERROR (LOG_FLAG_ERROR) // 0...0001 +#define LOG_LEVEL_WARN (LOG_FLAG_ERROR | LOG_FLAG_WARN) // 0...0011 +#define LOG_LEVEL_INFO (LOG_FLAG_ERROR | LOG_FLAG_WARN | LOG_FLAG_INFO) // 0...0111 +#define LOG_LEVEL_VERBOSE (LOG_FLAG_ERROR | LOG_FLAG_WARN | LOG_FLAG_INFO | LOG_FLAG_VERBOSE) // 0...1111 + +#define LOG_ERROR (ddLogLevel & LOG_FLAG_ERROR) +#define LOG_WARN (ddLogLevel & LOG_FLAG_WARN) +#define LOG_INFO (ddLogLevel & LOG_FLAG_INFO) +#define LOG_VERBOSE (ddLogLevel & LOG_FLAG_VERBOSE) + +#define LOG_ASYNC_ENABLED YES + +#define LOG_ASYNC_ERROR ( NO && LOG_ASYNC_ENABLED) +#define LOG_ASYNC_WARN (YES && LOG_ASYNC_ENABLED) +#define LOG_ASYNC_INFO (YES && LOG_ASYNC_ENABLED) +#define LOG_ASYNC_VERBOSE (YES && LOG_ASYNC_ENABLED) + +#define DDLogError(frmt, ...) LOG_OBJC_MAYBE(LOG_ASYNC_ERROR, ddLogLevel, LOG_FLAG_ERROR, 0, frmt, ##__VA_ARGS__) +#define DDLogWarn(frmt, ...) LOG_OBJC_MAYBE(LOG_ASYNC_WARN, ddLogLevel, LOG_FLAG_WARN, 0, frmt, ##__VA_ARGS__) +#define DDLogInfo(frmt, ...) LOG_OBJC_MAYBE(LOG_ASYNC_INFO, ddLogLevel, LOG_FLAG_INFO, 0, frmt, ##__VA_ARGS__) +#define DDLogVerbose(frmt, ...) LOG_OBJC_MAYBE(LOG_ASYNC_VERBOSE, ddLogLevel, LOG_FLAG_VERBOSE, 0, frmt, ##__VA_ARGS__) + +#define DDLogCError(frmt, ...) LOG_C_MAYBE(LOG_ASYNC_ERROR, ddLogLevel, LOG_FLAG_ERROR, 0, frmt, ##__VA_ARGS__) +#define DDLogCWarn(frmt, ...) LOG_C_MAYBE(LOG_ASYNC_WARN, ddLogLevel, LOG_FLAG_WARN, 0, frmt, ##__VA_ARGS__) +#define DDLogCInfo(frmt, ...) LOG_C_MAYBE(LOG_ASYNC_INFO, ddLogLevel, LOG_FLAG_INFO, 0, frmt, ##__VA_ARGS__) +#define DDLogCVerbose(frmt, ...) LOG_C_MAYBE(LOG_ASYNC_VERBOSE, ddLogLevel, LOG_FLAG_VERBOSE, 0, frmt, ##__VA_ARGS__) + +/** + * The THIS_FILE macro gives you an NSString of the file name. + * For simplicity and clarity, the file name does not include the full path or file extension. + * + * For example: DDLogWarn(@"%@: Unable to find thingy", THIS_FILE) -> @"MyViewController: Unable to find thingy" +**/ + +NSString *DDExtractFileNameWithoutExtension(const char *filePath, BOOL copy); + +#define THIS_FILE (DDExtractFileNameWithoutExtension(__FILE__, NO)) + +/** + * The THIS_METHOD macro gives you the name of the current objective-c method. + * + * For example: DDLogWarn(@"%@ - Requires non-nil strings", THIS_METHOD) -> @"setMake:model: requires non-nil strings" + * + * Note: This does NOT work in straight C functions (non objective-c). + * Instead you should use the predefined __FUNCTION__ macro. +**/ + +#define THIS_METHOD NSStringFromSelector(_cmd) + + +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +#pragma mark - +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +@interface DDLog : NSObject + +/** + * Provides access to the underlying logging queue. + * This may be helpful to Logger classes for things like thread synchronization. +**/ + ++ (dispatch_queue_t)loggingQueue; + +/** + * Logging Primitive. + * + * This method is used by the macros above. + * It is suggested you stick with the macros as they're easier to use. +**/ + ++ (void)log:(BOOL)synchronous + level:(int)level + flag:(int)flag + context:(int)context + file:(const char *)file + function:(const char *)function + line:(int)line + tag:(id)tag + format:(NSString *)format, ... __attribute__ ((format (__NSString__, 9, 10))); + +/** + * Logging Primitive. + * + * This method can be used if you have a prepared va_list. +**/ + ++ (void)log:(BOOL)asynchronous + level:(int)level + flag:(int)flag + context:(int)context + file:(const char *)file + function:(const char *)function + line:(int)line + tag:(id)tag + format:(NSString *)format + args:(va_list)argList; + + +/** + * Since logging can be asynchronous, there may be times when you want to flush the logs. + * The framework invokes this automatically when the application quits. +**/ + ++ (void)flushLog; + +/** + * Loggers + * + * If you want your log statements to go somewhere, + * you should create and add a logger. +**/ + ++ (void)addLogger:(id )logger; ++ (void)removeLogger:(id )logger; + ++ (void)removeAllLoggers; + +/** + * Registered Dynamic Logging + * + * These methods allow you to obtain a list of classes that are using registered dynamic logging, + * and also provides methods to get and set their log level during run time. +**/ + ++ (NSArray *)registeredClasses; ++ (NSArray *)registeredClassNames; + ++ (int)logLevelForClass:(Class)aClass; ++ (int)logLevelForClassWithName:(NSString *)aClassName; + ++ (void)setLogLevel:(int)logLevel forClass:(Class)aClass; ++ (void)setLogLevel:(int)logLevel forClassWithName:(NSString *)aClassName; + +@end + +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +#pragma mark - +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +@protocol DDLogger +@required + +- (void)logMessage:(DDLogMessage *)logMessage; + +/** + * Formatters may optionally be added to any logger. + * + * If no formatter is set, the logger simply logs the message as it is given in logMessage, + * or it may use its own built in formatting style. +**/ +- (id )logFormatter; +- (void)setLogFormatter:(id )formatter; + +@optional + +/** + * Since logging is asynchronous, adding and removing loggers is also asynchronous. + * In other words, the loggers are added and removed at appropriate times with regards to log messages. + * + * - Loggers will not receive log messages that were executed prior to when they were added. + * - Loggers will not receive log messages that were executed after they were removed. + * + * These methods are executed in the logging thread/queue. + * This is the same thread/queue that will execute every logMessage: invocation. + * Loggers may use these methods for thread synchronization or other setup/teardown tasks. +**/ +- (void)didAddLogger; +- (void)willRemoveLogger; + +/** + * Some loggers may buffer IO for optimization purposes. + * For example, a database logger may only save occasionaly as the disk IO is slow. + * In such loggers, this method should be implemented to flush any pending IO. + * + * This allows invocations of DDLog's flushLog method to be propogated to loggers that need it. + * + * Note that DDLog's flushLog method is invoked automatically when the application quits, + * and it may be also invoked manually by the developer prior to application crashes, or other such reasons. +**/ +- (void)flush; + +/** + * Each logger is executed concurrently with respect to the other loggers. + * Thus, a dedicated dispatch queue is used for each logger. + * Logger implementations may optionally choose to provide their own dispatch queue. +**/ +- (dispatch_queue_t)loggerQueue; + +/** + * If the logger implementation does not choose to provide its own queue, + * one will automatically be created for it. + * The created queue will receive its name from this method. + * This may be helpful for debugging or profiling reasons. +**/ +- (NSString *)loggerName; + +@end + +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +#pragma mark - +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +@protocol DDLogFormatter +@required + +/** + * Formatters may optionally be added to any logger. + * This allows for increased flexibility in the logging environment. + * For example, log messages for log files may be formatted differently than log messages for the console. + * + * For more information about formatters, see the "Custom Formatters" page: + * https://github.com/robbiehanson/CocoaLumberjack/wiki/CustomFormatters + * + * The formatter may also optionally filter the log message by returning nil, + * in which case the logger will not log the message. +**/ +- (NSString *)formatLogMessage:(DDLogMessage *)logMessage; + +@optional + +/** + * A single formatter instance can be added to multiple loggers. + * These methods provides hooks to notify the formatter of when it's added/removed. + * + * This is primarily for thread-safety. + * If a formatter is explicitly not thread-safe, it may wish to throw an exception if added to multiple loggers. + * Or if a formatter has potentially thread-unsafe code (e.g. NSDateFormatter), + * it could possibly use these hooks to switch to thread-safe versions of the code. +**/ +- (void)didAddToLogger:(id )logger; +- (void)willRemoveFromLogger:(id )logger; + +@end + +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +#pragma mark - +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +@protocol DDRegisteredDynamicLogging + +/** + * Implement these methods to allow a file's log level to be managed from a central location. + * + * This is useful if you'd like to be able to change log levels for various parts + * of your code from within the running application. + * + * Imagine pulling up the settings for your application, + * and being able to configure the logging level on a per file basis. + * + * The implementation can be very straight-forward: + * + * + (int)ddLogLevel + * { + * return ddLogLevel; + * } + * + * + (void)ddSetLogLevel:(int)logLevel + * { + * ddLogLevel = logLevel; + * } +**/ + ++ (int)ddLogLevel; ++ (void)ddSetLogLevel:(int)logLevel; + +@end + +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +#pragma mark - +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/** + * The DDLogMessage class encapsulates information about the log message. + * If you write custom loggers or formatters, you will be dealing with objects of this class. +**/ + +enum { + DDLogMessageCopyFile = 1 << 0, + DDLogMessageCopyFunction = 1 << 1 +}; +typedef int DDLogMessageOptions; + +@interface DDLogMessage : NSObject +{ + +// The public variables below can be accessed directly (for speed). +// For example: logMessage->logLevel + +@public + int logLevel; + int logFlag; + int logContext; + NSString *logMsg; + NSDate *timestamp; + char *file; + char *function; + int lineNumber; + mach_port_t machThreadID; + char *queueLabel; + NSString *threadName; + + // For 3rd party extensions to the framework, where flags and contexts aren't enough. + id tag; + + // For 3rd party extensions that manually create DDLogMessage instances. + DDLogMessageOptions options; +} + +/** + * Standard init method for a log message object. + * Used by the logging primitives. (And the macros use the logging primitives.) + * + * If you find need to manually create logMessage objects, there is one thing you should be aware of: + * + * If no flags are passed, the method expects the file and function parameters to be string literals. + * That is, it expects the given strings to exist for the duration of the object's lifetime, + * and it expects the given strings to be immutable. + * In other words, it does not copy these strings, it simply points to them. + * This is due to the fact that __FILE__ and __FUNCTION__ are usually used to specify these parameters, + * so it makes sense to optimize and skip the unnecessary allocations. + * However, if you need them to be copied you may use the options parameter to specify this. + * Options is a bitmask which supports DDLogMessageCopyFile and DDLogMessageCopyFunction. +**/ +- (id)initWithLogMsg:(NSString *)logMsg + level:(int)logLevel + flag:(int)logFlag + context:(int)logContext + file:(const char *)file + function:(const char *)function + line:(int)line + tag:(id)tag + options:(DDLogMessageOptions)optionsMask; + +/** + * Returns the threadID as it appears in NSLog. + * That is, it is a hexadecimal value which is calculated from the machThreadID. +**/ +- (NSString *)threadID; + +/** + * Convenience property to get just the file name, as the file variable is generally the full file path. + * This method does not include the file extension, which is generally unwanted for logging purposes. +**/ +- (NSString *)fileName; + +/** + * Returns the function variable in NSString form. +**/ +- (NSString *)methodName; + +@end + +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +#pragma mark - +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/** + * The DDLogger protocol specifies that an optional formatter can be added to a logger. + * Most (but not all) loggers will want to support formatters. + * + * However, writting getters and setters in a thread safe manner, + * while still maintaining maximum speed for the logging process, is a difficult task. + * + * To do it right, the implementation of the getter/setter has strict requiremenets: + * - Must NOT require the logMessage method to acquire a lock. + * - Must NOT require the logMessage method to access an atomic property (also a lock of sorts). + * + * To simplify things, an abstract logger is provided that implements the getter and setter. + * + * Logger implementations may simply extend this class, + * and they can ACCESS THE FORMATTER VARIABLE DIRECTLY from within their logMessage method! +**/ + +@interface DDAbstractLogger : NSObject +{ + id formatter; + + dispatch_queue_t loggerQueue; +} + +- (id )logFormatter; +- (void)setLogFormatter:(id )formatter; + +// For thread-safety assertions +- (BOOL)isOnGlobalLoggingQueue; +- (BOOL)isOnInternalLoggerQueue; + +@end diff --git a/Tools/XcodeCapp/XcodeCapp/Lumberjack/DDLog.m b/Tools/XcodeCapp/XcodeCapp/Lumberjack/DDLog.m new file mode 100755 index 000000000..1cc0895f1 --- /dev/null +++ b/Tools/XcodeCapp/XcodeCapp/Lumberjack/DDLog.m @@ -0,0 +1,1085 @@ +#import "DDLog.h" + +#import +#import +#import +#import +#import + + +/** + * Welcome to Cocoa Lumberjack! + * + * The project page has a wealth of documentation if you have any questions. + * https://github.com/robbiehanson/CocoaLumberjack + * + * If you're new to the project you may wish to read the "Getting Started" wiki. + * https://github.com/robbiehanson/CocoaLumberjack/wiki/GettingStarted + * +**/ + +#if ! __has_feature(objc_arc) +#warning This file must be compiled with ARC. Use -fobjc-arc flag (or convert project to ARC). +#endif + +// We probably shouldn't be using DDLog() statements within the DDLog implementation. +// But we still want to leave our log statements for any future debugging, +// and to allow other developers to trace the implementation (which is a great learning tool). +// +// So we use a primitive logging macro around NSLog. +// We maintain the NS prefix on the macros to be explicit about the fact that we're using NSLog. + +#define DD_DEBUG NO + +#define NSLogDebug(frmt, ...) do{ if(DD_DEBUG) NSLog((frmt), ##__VA_ARGS__); } while(0) + +// Specifies the maximum queue size of the logging thread. +// +// Since most logging is asynchronous, its possible for rogue threads to flood the logging queue. +// That is, to issue an abundance of log statements faster than the logging thread can keepup. +// Typically such a scenario occurs when log statements are added haphazardly within large loops, +// but may also be possible if relatively slow loggers are being used. +// +// This property caps the queue size at a given number of outstanding log statements. +// If a thread attempts to issue a log statement when the queue is already maxed out, +// the issuing thread will block until the queue size drops below the max again. + +#define LOG_MAX_QUEUE_SIZE 1000 // Should not exceed INT32_MAX + +// The "global logging queue" refers to [DDLog loggingQueue]. +// It is the queue that all log statements go through. +// +// The logging queue sets a flag via dispatch_queue_set_specific using this key. +// We can check for this key via dispatch_get_specific() to see if we're on the "global logging queue". + +static void *const GlobalLoggingQueueIdentityKey = (void *)&GlobalLoggingQueueIdentityKey; + + +@interface DDLoggerNode : NSObject { +@public + id logger; + dispatch_queue_t loggerQueue; +} + ++ (DDLoggerNode *)nodeWithLogger:(id )logger loggerQueue:(dispatch_queue_t)loggerQueue; + +@end + + +@interface DDLog (PrivateAPI) + ++ (void)lt_addLogger:(id )logger; ++ (void)lt_removeLogger:(id )logger; ++ (void)lt_removeAllLoggers; ++ (void)lt_log:(DDLogMessage *)logMessage; ++ (void)lt_flush; + +@end + +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +#pragma mark - +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +@implementation DDLog + +// An array used to manage all the individual loggers. +// The array is only modified on the loggingQueue/loggingThread. +static NSMutableArray *loggers; + +// All logging statements are added to the same queue to ensure FIFO operation. +static dispatch_queue_t loggingQueue; + +// Individual loggers are executed concurrently per log statement. +// Each logger has it's own associated queue, and a dispatch group is used for synchrnoization. +static dispatch_group_t loggingGroup; + +// In order to prevent to queue from growing infinitely large, +// a maximum size is enforced (LOG_MAX_QUEUE_SIZE). +static dispatch_semaphore_t queueSemaphore; + +// Minor optimization for uniprocessor machines +static unsigned int numProcessors; + +/** + * The runtime sends initialize to each class in a program exactly one time just before the class, + * or any class that inherits from it, is sent its first message from within the program. (Thus the + * method may never be invoked if the class is not used.) The runtime sends the initialize message to + * classes in a thread-safe manner. Superclasses receive this message before their subclasses. + * + * This method may also be called directly (assumably by accident), hence the safety mechanism. +**/ ++ (void)initialize +{ + static BOOL initialized = NO; + if (!initialized) + { + initialized = YES; + + loggers = [[NSMutableArray alloc] initWithCapacity:4]; + + NSLogDebug(@"DDLog: Using grand central dispatch"); + + loggingQueue = dispatch_queue_create("cocoa.lumberjack", NULL); + loggingGroup = dispatch_group_create(); + + void *nonNullValue = GlobalLoggingQueueIdentityKey; // Whatever, just not null + dispatch_queue_set_specific(loggingQueue, GlobalLoggingQueueIdentityKey, nonNullValue, NULL); + + queueSemaphore = dispatch_semaphore_create(LOG_MAX_QUEUE_SIZE); + + // Figure out how many processors are available. + // This may be used later for an optimization on uniprocessor machines. + + host_basic_info_data_t hostInfo; + mach_msg_type_number_t infoCount; + + infoCount = HOST_BASIC_INFO_COUNT; + host_info(mach_host_self(), HOST_BASIC_INFO, (host_info_t)&hostInfo, &infoCount); + + unsigned int result = (unsigned int)(hostInfo.max_cpus); + unsigned int one = (unsigned int)(1); + + numProcessors = MAX(result, one); + + NSLogDebug(@"DDLog: numProcessors = %u", numProcessors); + + + #if TARGET_OS_IPHONE + NSString *notificationName = @"UIApplicationWillTerminateNotification"; + #else + NSString *notificationName = @"NSApplicationWillTerminateNotification"; + #endif + + [NSNotificationCenter.defaultCenter addObserver:self + selector:@selector(applicationWillTerminate:) + name:notificationName + object:nil]; + } +} + +/** + * Provides access to the logging queue. +**/ ++ (dispatch_queue_t)loggingQueue +{ + return loggingQueue; +} + +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +#pragma mark Notifications +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ++ (void)applicationWillTerminate:(NSNotification *)notification +{ + [self flushLog]; +} + +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +#pragma mark Logger Management +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ++ (void)addLogger:(id )logger +{ + if (logger == nil) return; + + dispatch_async(loggingQueue, ^{ @autoreleasepool { + + [self lt_addLogger:logger]; + }}); +} + ++ (void)removeLogger:(id )logger +{ + if (logger == nil) return; + + dispatch_async(loggingQueue, ^{ @autoreleasepool { + + [self lt_removeLogger:logger]; + }}); +} + ++ (void)removeAllLoggers +{ + dispatch_async(loggingQueue, ^{ @autoreleasepool { + + [self lt_removeAllLoggers]; + }}); +} + +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +#pragma mark Master Logging +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ++ (void)queueLogMessage:(DDLogMessage *)logMessage asynchronously:(BOOL)asyncFlag +{ + // We have a tricky situation here... + // + // In the common case, when the queueSize is below the maximumQueueSize, + // we want to simply enqueue the logMessage. And we want to do this as fast as possible, + // which means we don't want to block and we don't want to use any locks. + // + // However, if the queueSize gets too big, we want to block. + // But we have very strict requirements as to when we block, and how long we block. + // + // The following example should help illustrate our requirements: + // + // Imagine that the maximum queue size is configured to be 5, + // and that there are already 5 log messages queued. + // Let us call these 5 queued log messages A, B, C, D, and E. (A is next to be executed) + // + // Now if our thread issues a log statement (let us call the log message F), + // it should block before the message is added to the queue. + // Furthermore, it should be unblocked immediately after A has been unqueued. + // + // The requirements are strict in this manner so that we block only as long as necessary, + // and so that blocked threads are unblocked in the order in which they were blocked. + // + // Returning to our previous example, let us assume that log messages A through E are still queued. + // Our aforementioned thread is blocked attempting to queue log message F. + // Now assume we have another separate thread that attempts to issue log message G. + // It should block until log messages A and B have been unqueued. + + + // We are using a counting semaphore provided by GCD. + // The semaphore is initialized with our LOG_MAX_QUEUE_SIZE value. + // Everytime we want to queue a log message we decrement this value. + // If the resulting value is less than zero, + // the semaphore function waits in FIFO order for a signal to occur before returning. + // + // A dispatch semaphore is an efficient implementation of a traditional counting semaphore. + // Dispatch semaphores call down to the kernel only when the calling thread needs to be blocked. + // If the calling semaphore does not need to block, no kernel call is made. + + dispatch_semaphore_wait(queueSemaphore, DISPATCH_TIME_FOREVER); + + // We've now sure we won't overflow the queue. + // It is time to queue our log message. + + dispatch_block_t logBlock = ^{ @autoreleasepool { + + [self lt_log:logMessage]; + }}; + + if (asyncFlag) + dispatch_async(loggingQueue, logBlock); + else + dispatch_sync(loggingQueue, logBlock); +} + ++ (void)log:(BOOL)asynchronous + level:(int)level + flag:(int)flag + context:(int)context + file:(const char *)file + function:(const char *)function + line:(int)line + tag:(id)tag + format:(NSString *)format, ... +{ + va_list args; + if (format) + { + va_start(args, format); + + NSString *logMsg = [[NSString alloc] initWithFormat:format arguments:args]; + DDLogMessage *logMessage = [[DDLogMessage alloc] initWithLogMsg:logMsg + level:level + flag:flag + context:context + file:file + function:function + line:line + tag:tag + options:0]; + + [self queueLogMessage:logMessage asynchronously:asynchronous]; + + va_end(args); + } +} + ++ (void)log:(BOOL)asynchronous + level:(int)level + flag:(int)flag + context:(int)context + file:(const char *)file + function:(const char *)function + line:(int)line + tag:(id)tag + format:(NSString *)format + args:(va_list)args +{ + if (format) + { + NSString *logMsg = [[NSString alloc] initWithFormat:format arguments:args]; + DDLogMessage *logMessage = [[DDLogMessage alloc] initWithLogMsg:logMsg + level:level + flag:flag + context:context + file:file + function:function + line:line + tag:tag + options:0]; + + [self queueLogMessage:logMessage asynchronously:asynchronous]; + } +} + ++ (void)flushLog +{ + dispatch_sync(loggingQueue, ^{ @autoreleasepool { + + [self lt_flush]; + }}); +} + +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +#pragma mark Registered Dynamic Logging +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + ++ (BOOL)isRegisteredClass:(Class)class +{ + SEL getterSel = @selector(ddLogLevel); + SEL setterSel = @selector(ddSetLogLevel:); + +#if TARGET_OS_IPHONE && !TARGET_IPHONE_SIMULATOR + + // Issue #6 (GoogleCode) - Crashes on iOS 4.2.1 and iPhone 4 + // + // Crash caused by class_getClassMethod(2). + // + // "It's a bug with UIAccessibilitySafeCategory__NSObject so it didn't pop up until + // users had VoiceOver enabled [...]. I was able to work around it by searching the + // result of class_copyMethodList() instead of calling class_getClassMethod()" + + BOOL result = NO; + + unsigned int methodCount, i; + Method *methodList = class_copyMethodList(object_getClass(class), &methodCount); + + if (methodList != NULL) + { + BOOL getterFound = NO; + BOOL setterFound = NO; + + for (i = 0; i < methodCount; ++i) + { + SEL currentSel = method_getName(methodList[i]); + + if (currentSel == getterSel) + { + getterFound = YES; + } + else if (currentSel == setterSel) + { + setterFound = YES; + } + + if (getterFound && setterFound) + { + result = YES; + break; + } + } + + free(methodList); + } + + return result; + +#else + + // Issue #24 (GitHub) - Crashing in in ARC+Simulator + // + // The method +[DDLog isRegisteredClass] will crash a project when using it with ARC + Simulator. + // For running in the Simulator, it needs to execute the non-iOS code. + + Method getter = class_getClassMethod(class, getterSel); + Method setter = class_getClassMethod(class, setterSel); + + if ((getter != NULL) && (setter != NULL)) + { + return YES; + } + + return NO; + +#endif +} + ++ (NSArray *)registeredClasses +{ + int numClasses, i; + + // We're going to get the list of all registered classes. + // The Objective-C runtime library automatically registers all the classes defined in your source code. + // + // To do this we use the following method (documented in the Objective-C Runtime Reference): + // + // int objc_getClassList(Class *buffer, int bufferLen) + // + // We can pass (NULL, 0) to obtain the total number of + // registered class definitions without actually retrieving any class definitions. + // This allows us to allocate the minimum amount of memory needed for the application. + + numClasses = objc_getClassList(NULL, 0); + + // The numClasses method now tells us how many classes we have. + // So we can allocate our buffer, and get pointers to all the class definitions. + + Class *classes = (Class *)malloc(sizeof(Class) * numClasses); + if (classes == NULL) return nil; + + numClasses = objc_getClassList(classes, numClasses); + + // We can now loop through the classes, and test each one to see if it is a DDLogging class. + + NSMutableArray *result = [NSMutableArray arrayWithCapacity:numClasses]; + + for (i = 0; i < numClasses; i++) + { + Class class = classes[i]; + + if ([self isRegisteredClass:class]) + { + [result addObject:class]; + } + } + + free(classes); + + return result; +} + ++ (NSArray *)registeredClassNames +{ + NSArray *registeredClasses = [self registeredClasses]; + NSMutableArray *result = [NSMutableArray arrayWithCapacity:[registeredClasses count]]; + + for (Class class in registeredClasses) + { + [result addObject:NSStringFromClass(class)]; + } + + return result; +} + ++ (int)logLevelForClass:(Class)aClass +{ + if ([self isRegisteredClass:aClass]) + { + return [aClass ddLogLevel]; + } + + return -1; +} + ++ (int)logLevelForClassWithName:(NSString *)aClassName +{ + Class aClass = NSClassFromString(aClassName); + + return [self logLevelForClass:aClass]; +} + ++ (void)setLogLevel:(int)logLevel forClass:(Class)aClass +{ + if ([self isRegisteredClass:aClass]) + { + [aClass ddSetLogLevel:logLevel]; + } +} + ++ (void)setLogLevel:(int)logLevel forClassWithName:(NSString *)aClassName +{ + Class aClass = NSClassFromString(aClassName); + + [self setLogLevel:logLevel forClass:aClass]; +} + +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +#pragma mark Logging Thread +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +/** + * This method should only be run on the logging thread/queue. +**/ ++ (void)lt_addLogger:(id )logger +{ + // Add to loggers array. + // Need to create loggerQueue if loggerNode doesn't provide one. + + dispatch_queue_t loggerQueue = NULL; + + if ([logger respondsToSelector:@selector(loggerQueue)]) + { + // Logger may be providing its own queue + + loggerQueue = [logger loggerQueue]; + } + + if (loggerQueue == nil) + { + // Automatically create queue for the logger. + // Use the logger name as the queue name if possible. + + const char *loggerQueueName = NULL; + if ([logger respondsToSelector:@selector(loggerName)]) + { + loggerQueueName = [[logger loggerName] UTF8String]; + } + + loggerQueue = dispatch_queue_create(loggerQueueName, NULL); + } + + DDLoggerNode *loggerNode = [DDLoggerNode nodeWithLogger:logger loggerQueue:loggerQueue]; + [loggers addObject:loggerNode]; + + if ([logger respondsToSelector:@selector(didAddLogger)]) + { + dispatch_async(loggerNode->loggerQueue, ^{ @autoreleasepool { + + [logger didAddLogger]; + }}); + } +} + +/** + * This method should only be run on the logging thread/queue. +**/ ++ (void)lt_removeLogger:(id )logger +{ + // Find associated loggerNode in list of added loggers + + DDLoggerNode *loggerNode = nil; + + for (DDLoggerNode *node in loggers) + { + if (node->logger == logger) + { + loggerNode = node; + break; + } + } + + if (loggerNode == nil) + { + NSLogDebug(@"DDLog: Request to remove logger which wasn't added"); + return; + } + + // Notify logger + + if ([logger respondsToSelector:@selector(willRemoveLogger)]) + { + dispatch_async(loggerNode->loggerQueue, ^{ @autoreleasepool { + + [logger willRemoveLogger]; + }}); + } + + // Remove from loggers array + + [loggers removeObject:loggerNode]; +} + +/** + * This method should only be run on the logging thread/queue. +**/ ++ (void)lt_removeAllLoggers +{ + // Notify all loggers + + for (DDLoggerNode *loggerNode in loggers) + { + if ([loggerNode->logger respondsToSelector:@selector(willRemoveLogger)]) + { + dispatch_async(loggerNode->loggerQueue, ^{ @autoreleasepool { + + [loggerNode->logger willRemoveLogger]; + }}); + } + } + + // Remove all loggers from array + + [loggers removeAllObjects]; +} + +/** + * This method should only be run on the logging thread/queue. +**/ ++ (void)lt_log:(DDLogMessage *)logMessage +{ + // Execute the given log message on each of our loggers. + + if (numProcessors > 1) + { + // Execute each logger concurrently, each within its own queue. + // All blocks are added to same group. + // After each block has been queued, wait on group. + // + // The waiting ensures that a slow logger doesn't end up with a large queue of pending log messages. + // This would defeat the purpose of the efforts we made earlier to restrict the max queue size. + + for (DDLoggerNode *loggerNode in loggers) + { + dispatch_group_async(loggingGroup, loggerNode->loggerQueue, ^{ @autoreleasepool { + + [loggerNode->logger logMessage:logMessage]; + + }}); + } + + dispatch_group_wait(loggingGroup, DISPATCH_TIME_FOREVER); + } + else + { + // Execute each logger serialy, each within its own queue. + + for (DDLoggerNode *loggerNode in loggers) + { + dispatch_sync(loggerNode->loggerQueue, ^{ @autoreleasepool { + + [loggerNode->logger logMessage:logMessage]; + + }}); + } + } + + // If our queue got too big, there may be blocked threads waiting to add log messages to the queue. + // Since we've now dequeued an item from the log, we may need to unblock the next thread. + + // We are using a counting semaphore provided by GCD. + // The semaphore is initialized with our LOG_MAX_QUEUE_SIZE value. + // When a log message is queued this value is decremented. + // When a log message is dequeued this value is incremented. + // If the value ever drops below zero, + // the queueing thread blocks and waits in FIFO order for us to signal it. + // + // A dispatch semaphore is an efficient implementation of a traditional counting semaphore. + // Dispatch semaphores call down to the kernel only when the calling thread needs to be blocked. + // If the calling semaphore does not need to block, no kernel call is made. + + dispatch_semaphore_signal(queueSemaphore); +} + +/** + * This method should only be run on the background logging thread. +**/ ++ (void)lt_flush +{ + // All log statements issued before the flush method was invoked have now been executed. + // + // Now we need to propogate the flush request to any loggers that implement the flush method. + // This is designed for loggers that buffer IO. + + for (DDLoggerNode *loggerNode in loggers) + { + if ([loggerNode->logger respondsToSelector:@selector(flush)]) + { + dispatch_group_async(loggingGroup, loggerNode->loggerQueue, ^{ @autoreleasepool { + + [loggerNode->logger flush]; + + }}); + } + } + + dispatch_group_wait(loggingGroup, DISPATCH_TIME_FOREVER); +} + +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +#pragma mark Utilities +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +NSString *DDExtractFileNameWithoutExtension(const char *filePath, BOOL copy) +{ + if (filePath == NULL) return nil; + + char *lastSlash = NULL; + char *lastDot = NULL; + + char *p = (char *)filePath; + + while (*p != '\0') + { + if (*p == '/') + lastSlash = p; + else if (*p == '.') + lastDot = p; + + p++; + } + + char *subStr; + NSUInteger subLen; + + if (lastSlash) + { + if (lastDot) + { + // lastSlash -> lastDot + subStr = lastSlash + 1; + subLen = lastDot - subStr; + } + else + { + // lastSlash -> endOfString + subStr = lastSlash + 1; + subLen = p - subStr; + } + } + else + { + if (lastDot) + { + // startOfString -> lastDot + subStr = (char *)filePath; + subLen = lastDot - subStr; + } + else + { + // startOfString -> endOfString + subStr = (char *)filePath; + subLen = p - subStr; + } + } + + if (copy) + { + return [[NSString alloc] initWithBytes:subStr + length:subLen + encoding:NSUTF8StringEncoding]; + } + else + { + // We can take advantage of the fact that __FILE__ is a string literal. + // Specifically, we don't need to waste time copying the string. + // We can just tell NSString to point to a range within the string literal. + + return [[NSString alloc] initWithBytesNoCopy:subStr + length:subLen + encoding:NSUTF8StringEncoding + freeWhenDone:NO]; + } +} + +@end + +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +#pragma mark - +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +@implementation DDLoggerNode + +- (id)initWithLogger:(id )aLogger loggerQueue:(dispatch_queue_t)aLoggerQueue +{ + if ((self = [super init])) + { + logger = aLogger; + + if (aLoggerQueue) { + loggerQueue = aLoggerQueue; + #if !OS_OBJECT_USE_OBJC + dispatch_retain(loggerQueue); + #endif + } + } + return self; +} + ++ (DDLoggerNode *)nodeWithLogger:(id )logger loggerQueue:(dispatch_queue_t)loggerQueue +{ + return [[DDLoggerNode alloc] initWithLogger:logger loggerQueue:loggerQueue]; +} + +- (void)dealloc +{ + #if !OS_OBJECT_USE_OBJC + if (loggerQueue) dispatch_release(loggerQueue); + #endif +} + +@end + +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +#pragma mark - +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +@implementation DDLogMessage + +static char *dd_str_copy(const char *str) +{ + if (str == NULL) return NULL; + + size_t length = strlen(str); + char * result = malloc(length + 1); + if (result == NULL) return NULL; + strncpy(result, str, length); + result[length] = 0; + + return result; +} + +- (id)initWithLogMsg:(NSString *)msg + level:(int)level + flag:(int)flag + context:(int)context + file:(const char *)aFile + function:(const char *)aFunction + line:(int)line + tag:(id)aTag + options:(DDLogMessageOptions)optionsMask +{ + if ((self = [super init])) + { + logMsg = msg; + logLevel = level; + logFlag = flag; + logContext = context; + lineNumber = line; + tag = aTag; + options = optionsMask; + + if (options & DDLogMessageCopyFile) + file = dd_str_copy(aFile); + else + file = (char *)aFile; + + if (options & DDLogMessageCopyFunction) + function = dd_str_copy(aFunction); + else + function = (char *)aFunction; + + timestamp = [[NSDate alloc] init]; + + machThreadID = pthread_mach_thread_np(pthread_self()); + + #pragma clang diagnostic push + #pragma clang diagnostic ignored "-Wdeprecated-declarations" + // The documentation for dispatch_get_current_queue() states: + // + // > [This method is] "recommended for debugging and logging purposes only"... + // + // Well that's exactly how we're using it here. Literally for logging purposes only. + // However, Apple has decided to deprecate this method anyway. + // However they have not given us an alternate version of dispatch_queue_get_label() that + // automatically uses the current queue, thus dispatch_get_current_queue() is still required. + // + // If dispatch_get_current_queue() disappears, without a dispatch_queue_get_label() alternative, + // Apple will have effectively taken away our ability to properly log the name of executing dispatch queue. + + dispatch_queue_t currentQueue = dispatch_get_current_queue(); + #pragma clang diagnostic pop + + queueLabel = dd_str_copy(dispatch_queue_get_label(currentQueue)); + + threadName = [[NSThread currentThread] name]; + } + return self; +} + +- (NSString *)threadID +{ + return [[NSString alloc] initWithFormat:@"%x", machThreadID]; +} + +- (NSString *)fileName +{ + return DDExtractFileNameWithoutExtension(file, NO); +} + +- (NSString *)methodName +{ + if (function == NULL) + return nil; + else + return [[NSString alloc] initWithUTF8String:function]; +} + +- (void)dealloc +{ + if (file && (options & DDLogMessageCopyFile)) + free(file); + + if (function && (options & DDLogMessageCopyFunction)) + free(function); + + if (queueLabel) + free(queueLabel); +} + +@end + +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +#pragma mark - +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +@implementation DDAbstractLogger + +- (id)init +{ + if ((self = [super init])) + { + const char *loggerQueueName = NULL; + if ([self respondsToSelector:@selector(loggerName)]) + { + loggerQueueName = [[self loggerName] UTF8String]; + } + + loggerQueue = dispatch_queue_create(loggerQueueName, NULL); + + // We're going to use dispatch_queue_set_specific() to "mark" our loggerQueue. + // Later we can use dispatch_get_specific() to determine if we're executing on our loggerQueue. + // The documentation states: + // + // > Keys are only compared as pointers and are never dereferenced. + // > Thus, you can use a pointer to a static variable for a specific subsystem or + // > any other value that allows you to identify the value uniquely. + // > Specifying a pointer to a string constant is not recommended. + // + // So we're going to use the very convenient key of "self", + // which also works when multiple logger classes extend this class, as each will have a different "self" key. + // + // This is used primarily for thread-safety assertions (via the isOnInternalLoggerQueue method below). + + void *key = (__bridge void *)self; + void *nonNullValue = (__bridge void *)self; + + dispatch_queue_set_specific(loggerQueue, key, nonNullValue, NULL); + } + return self; +} + +- (void)dealloc +{ + #if !OS_OBJECT_USE_OBJC + if (loggerQueue) dispatch_release(loggerQueue); + #endif +} + +- (void)logMessage:(DDLogMessage *)logMessage +{ + // Override me +} + +- (id )logFormatter +{ + // This method must be thread safe and intuitive. + // Therefore if somebody executes the following code: + // + // [logger setLogFormatter:myFormatter]; + // formatter = [logger logFormatter]; + // + // They would expect formatter to equal myFormatter. + // This functionality must be ensured by the getter and setter method. + // + // The thread safety must not come at a cost to the performance of the logMessage method. + // This method is likely called sporadically, while the logMessage method is called repeatedly. + // This means, the implementation of this method: + // - Must NOT require the logMessage method to acquire a lock. + // - Must NOT require the logMessage method to access an atomic property (also a lock of sorts). + // + // Thread safety is ensured by executing access to the formatter variable on the loggerQueue. + // This is the same queue that the logMessage method operates on. + // + // Note: The last time I benchmarked the performance of direct access vs atomic property access, + // direct access was over twice as fast on the desktop and over 6 times as fast on the iPhone. + // + // Furthermore, consider the following code: + // + // DDLogVerbose(@"log msg 1"); + // DDLogVerbose(@"log msg 2"); + // [logger setFormatter:myFormatter]; + // DDLogVerbose(@"log msg 3"); + // + // Our intuitive requirement means that the new formatter will only apply to the 3rd log message. + // This must remain true even when using asynchronous logging. + // We must keep in mind the various queue's that are in play here: + // + // loggerQueue : Our own private internal queue that the logMessage method runs on. + // Operations are added to this queue from the global loggingQueue. + // + // globalLoggingQueue : The queue that all log messages go through before they arrive in our loggerQueue. + // + // All log statements go through the serial gloabalLoggingQueue before they arrive at our loggerQueue. + // Thus this method also goes through the serial globalLoggingQueue to ensure intuitive operation. + + // IMPORTANT NOTE: + // + // Methods within the DDLogger implementation MUST access the formatter ivar 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 id result; + + dispatch_sync(globalLoggingQueue, ^{ + dispatch_sync(loggerQueue, ^{ + result = formatter; + }); + }); + + return result; +} + +- (void)setLogFormatter:(id )logFormatter +{ + // The design of this method is documented extensively in the logFormatter message (above in code). + + NSAssert(![self isOnGlobalLoggingQueue], @"Core architecture requirement failure"); + NSAssert(![self isOnInternalLoggerQueue], @"MUST access ivar directly, NOT via self.* syntax."); + + dispatch_block_t block = ^{ @autoreleasepool { + + if (formatter != logFormatter) + { + if ([formatter respondsToSelector:@selector(willRemoveFromLogger:)]) { + [formatter willRemoveFromLogger:self]; + } + + formatter = logFormatter; + + if ([formatter respondsToSelector:@selector(didAddToLogger:)]) { + [formatter didAddToLogger:self]; + } + } + }}; + + dispatch_queue_t globalLoggingQueue = [DDLog loggingQueue]; + + dispatch_async(globalLoggingQueue, ^{ + dispatch_async(loggerQueue, block); + }); +} + +- (dispatch_queue_t)loggerQueue +{ + return loggerQueue; +} + +- (NSString *)loggerName +{ + return NSStringFromClass([self class]); +} + +- (BOOL)isOnGlobalLoggingQueue +{ + return (dispatch_get_specific(GlobalLoggingQueueIdentityKey) != NULL); +} + +- (BOOL)isOnInternalLoggerQueue +{ + void *key = (__bridge void *)self; + return (dispatch_get_specific(key) != NULL); +} + +@end diff --git a/Tools/XcodeCapp/XcodeCapp/Lumberjack/DDTTYLogger.h b/Tools/XcodeCapp/XcodeCapp/Lumberjack/DDTTYLogger.h new file mode 100755 index 000000000..4cbd2e8e9 --- /dev/null +++ b/Tools/XcodeCapp/XcodeCapp/Lumberjack/DDTTYLogger.h @@ -0,0 +1,167 @@ +#import +#if TARGET_OS_IPHONE +#import +#else +#import +#endif + +#import "DDLog.h" + +/** + * Welcome to Cocoa Lumberjack! + * + * The project page has a wealth of documentation if you have any questions. + * https://github.com/robbiehanson/CocoaLumberjack + * + * If you're new to the project you may wish to read the "Getting Started" wiki. + * https://github.com/robbiehanson/CocoaLumberjack/wiki/GettingStarted + * + * + * This class provides a logger for Terminal output or Xcode console output, + * depending on where you are running your code. + * + * As described in the "Getting Started" page, + * the traditional NSLog() function directs it's output to two places: + * + * - Apple System Log (so it shows up in Console.app) + * - 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 an asl logger. + * However, if you instead choose to use file logging (for faster performance), + * you may choose to use only a file logger and a tty logger. +**/ + +@interface DDTTYLogger : DDAbstractLogger +{ + NSCalendar *calendar; + NSUInteger calendarUnitFlags; + + NSString *appName; + char *app; + size_t appLen; + + NSString *processID; + char *pid; + size_t pidLen; + + BOOL colorsEnabled; + NSMutableArray *colorProfilesArray; + NSMutableDictionary *colorProfilesDict; +} + ++ (DDTTYLogger *)sharedInstance; + +/* Inherited from the DDLogger protocol: + * + * Formatters may optionally be added to any logger. + * + * If no formatter is set, the logger simply logs the message as it is given in logMessage, + * or it may use its own built in formatting style. + * + * More information about formatters can be found here: + * https://github.com/robbiehanson/CocoaLumberjack/wiki/CustomFormatters + * + * The actual implementation of these methods is inherited from DDAbstractLogger. + +- (id )logFormatter; +- (void)setLogFormatter:(id )formatter; + +*/ + +/** + * Want to use different colors for different log levels? + * Enable this property. + * + * If you run the application via the Terminal (not Xcode), + * the logger will map colors to xterm-256color or xterm-color (if available). + * + * Xcode does NOT natively support colors in the Xcode debugging console. + * You'll need to install the XcodeColors plugin to see colors in the Xcode console. + * https://github.com/robbiehanson/XcodeColors + * + * The default value if NO. +**/ +@property (readwrite, assign) BOOL colorsEnabled; + +/** + * The default color set (foregroundColor, backgroundColor) is: + * + * - LOG_FLAG_ERROR = (red, nil) + * - LOG_FLAG_WARN = (orange, nil) + * + * You can customize the colors however you see fit. + * Please note that you are passing a flag, NOT a level. + * + * GOOD : [ttyLogger setForegroundColor:pink backgroundColor:nil forFlag:LOG_FLAG_INFO]; // <- Good :) + * BAD : [ttyLogger setForegroundColor:pink backgroundColor:nil forFlag:LOG_LEVEL_INFO]; // <- BAD! :( + * + * LOG_FLAG_INFO = 0...00100 + * LOG_LEVEL_INFO = 0...00111 <- Would match LOG_FLAG_INFO and LOG_FLAG_WARN and LOG_FLAG_ERROR + * + * If you run the application within Xcode, then the XcodeColors plugin is required. + * + * If you run the application from a shell, then DDTTYLogger will automatically map the given color to + * the closest available color. (xterm-256color or xterm-color which have 256 and 16 supported colors respectively.) + * + * This method invokes setForegroundColor:backgroundColor:forFlag:context: and passes the default context (0). +**/ +#if TARGET_OS_IPHONE +- (void)setForegroundColor:(UIColor *)txtColor backgroundColor:(UIColor *)bgColor forFlag:(int)mask; +#else +- (void)setForegroundColor:(NSColor *)txtColor backgroundColor:(NSColor *)bgColor forFlag:(int)mask; +#endif + +/** + * Just like setForegroundColor:backgroundColor:flag, but allows you to specify a particular logging context. + * + * A logging context is often used to identify log messages coming from a 3rd party framework, + * although logging context's can be used for many different functions. + * + * Logging context's are explained in further detail here: + * https://github.com/robbiehanson/CocoaLumberjack/wiki/CustomContext +**/ +#if TARGET_OS_IPHONE +- (void)setForegroundColor:(UIColor *)txtColor backgroundColor:(UIColor *)bgColor forFlag:(int)mask context:(int)ctxt; +#else +- (void)setForegroundColor:(NSColor *)txtColor backgroundColor:(NSColor *)bgColor forFlag:(int)mask context:(int)ctxt; +#endif + +/** + * Similar to the methods above, but allows you to map DDLogMessage->tag to a particular color profile. + * For example, you could do something like this: + * + * static NSString *const PurpleTag = @"PurpleTag"; + * + * #define DDLogPurple(frmt, ...) LOG_OBJC_TAG_MACRO(NO, 0, 0, 0, PurpleTag, frmt, ##__VA_ARGS__) + * + * And then in your applicationDidFinishLaunching, or wherever you configure Lumberjack: + * + * #if TARGET_OS_IPHONE + * UIColor *purple = [UIColor colorWithRed:(64/255.0) green:(0/255.0) blue:(128/255.0) alpha:1.0]; + * #else + * NSColor *purple = [NSColor colorWithCalibratedRed:(64/255.0) green:(0/255.0) blue:(128/255.0) alpha:1.0]; + * + * [[DDTTYLogger sharedInstance] setForegroundColor:purple backgroundColor:nil forTag:PurpleTag]; + * [DDLog addLogger:[DDTTYLogger sharedInstance]]; + * + * This would essentially give you a straight NSLog replacement that prints in purple: + * + * DDLogPurple(@"I'm a purple log message!"); +**/ +#if TARGET_OS_IPHONE +- (void)setForegroundColor:(UIColor *)txtColor backgroundColor:(UIColor *)bgColor forTag:(id )tag; +#else +- (void)setForegroundColor:(NSColor *)txtColor backgroundColor:(NSColor *)bgColor forTag:(id )tag; +#endif + +/** + * Clearing color profiles. +**/ +- (void)clearColorsForFlag:(int)mask; +- (void)clearColorsForFlag:(int)mask context:(int)context; +- (void)clearColorsForTag:(id )tag; +- (void)clearColorsForAllFlags; +- (void)clearColorsForAllTags; +- (void)clearAllColors; + +@end diff --git a/Tools/XcodeCapp/XcodeCapp/Lumberjack/DDTTYLogger.m b/Tools/XcodeCapp/XcodeCapp/Lumberjack/DDTTYLogger.m new file mode 100755 index 000000000..8b4c097d4 --- /dev/null +++ b/Tools/XcodeCapp/XcodeCapp/Lumberjack/DDTTYLogger.m @@ -0,0 +1,1490 @@ +#import "DDTTYLogger.h" + +#import +#import + +/** + * Welcome to Cocoa Lumberjack! + * + * The project page has a wealth of documentation if you have any questions. + * https://github.com/robbiehanson/CocoaLumberjack + * + * If you're new to the project you may wish to read the "Getting Started" wiki. + * https://github.com/robbiehanson/CocoaLumberjack/wiki/GettingStarted +**/ + +#if ! __has_feature(objc_arc) +#warning This file must be compiled with ARC. Use -fobjc-arc flag (or convert project to ARC). +#endif + +// We probably shouldn't be using DDLog() statements within the DDLog implementation. +// But we still want to leave our log statements for any future debugging, +// and to allow other developers to trace the implementation (which is a great learning tool). +// +// So we use primitive logging macros around NSLog. +// We maintain the NS prefix on the macros to be explicit about the fact that we're using NSLog. + +#define LOG_LEVEL 2 + +#define NSLogError(frmt, ...) do{ if(LOG_LEVEL >= 1) NSLog((frmt), ##__VA_ARGS__); } while(0) +#define NSLogWarn(frmt, ...) do{ if(LOG_LEVEL >= 2) NSLog((frmt), ##__VA_ARGS__); } while(0) +#define NSLogInfo(frmt, ...) do{ if(LOG_LEVEL >= 3) NSLog((frmt), ##__VA_ARGS__); } while(0) +#define NSLogVerbose(frmt, ...) do{ if(LOG_LEVEL >= 4) NSLog((frmt), ##__VA_ARGS__); } while(0) + +// Xcode does NOT natively support colors in the Xcode debugging console. +// You'll need to install the XcodeColors plugin to see colors in the Xcode console. +// https://github.com/robbiehanson/XcodeColors +// +// The following is documentation from the XcodeColors project: +// +// +// How to apply color formatting to your log statements: +// +// To set the foreground color: +// Insert the ESCAPE_SEQ into your string, followed by "fg124,12,255;" where r=124, g=12, b=255. +// +// To set the background color: +// Insert the ESCAPE_SEQ into your string, followed by "bg12,24,36;" where r=12, g=24, b=36. +// +// To reset the foreground color (to default value): +// Insert the ESCAPE_SEQ into your string, followed by "fg;" +// +// To reset the background color (to default value): +// Insert the ESCAPE_SEQ into your string, followed by "bg;" +// +// To reset the foreground and background color (to default values) in one operation: +// Insert the ESCAPE_SEQ into your string, followed by ";" + +#define XCODE_COLORS_ESCAPE_SEQ "\033[" + +#define XCODE_COLORS_RESET_FG XCODE_COLORS_ESCAPE_SEQ "fg;" // Clear any foreground color +#define XCODE_COLORS_RESET_BG XCODE_COLORS_ESCAPE_SEQ "bg;" // Clear any background color +#define XCODE_COLORS_RESET XCODE_COLORS_ESCAPE_SEQ ";" // Clear any foreground or background color + +// Some simple defines to make life easier on ourself + +#if TARGET_OS_IPHONE + #define MakeColor(r, g, b) [UIColor colorWithRed:(r/255.0f) green:(g/255.0f) blue:(b/255.0f) alpha:1.0f] +#else + #define MakeColor(r, g, b) [NSColor colorWithCalibratedRed:(r/255.0f) green:(g/255.0f) blue:(b/255.0f) alpha:1.0f] +#endif + +#if TARGET_OS_IPHONE + #define OSColor UIColor +#else + #define OSColor NSColor +#endif + +// If running in a shell, not all RGB colors will be supported. +// In this case we automatically map to the closest available color. +// In order to provide this mapping, we have a hard-coded set of the standard RGB values available in the shell. +// However, not every shell is the same, and Apple likes to think different even when it comes to shell colors. +// +// Map to standard Terminal.app colors (1), or +// map to standard xterm colors (0). + +#define MAP_TO_TERMINAL_APP_COLORS 1 + + +@interface DDTTYLoggerColorProfile : NSObject { +@public + int mask; + int context; + + uint8_t fg_r; + uint8_t fg_g; + uint8_t fg_b; + + uint8_t bg_r; + uint8_t bg_g; + uint8_t bg_b; + + NSUInteger fgCodeIndex; + NSString *fgCodeRaw; + + NSUInteger bgCodeIndex; + NSString *bgCodeRaw; + + char fgCode[24]; + size_t fgCodeLen; + + char bgCode[24]; + size_t bgCodeLen; + + char resetCode[8]; + size_t resetCodeLen; +} + +- (id)initWithForegroundColor:(OSColor *)fgColor backgroundColor:(OSColor *)bgColor flag:(int)mask context:(int)ctxt; + +@end + +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +#pragma mark - +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +@implementation DDTTYLogger + +static BOOL isaColorTTY; +static BOOL isaColor256TTY; +static BOOL isaXcodeColorTTY; + +static NSArray *codes_fg = nil; +static NSArray *codes_bg = nil; +static NSArray *colors = nil; + +static DDTTYLogger *sharedInstance; + +/** + * Initializes the colors array, as well as the codes_fg and codes_bg arrays, for 16 color mode. + * + * This method is used when the application is running from within a shell that only supports 16 color mode. + * This method is not invoked if the application is running within Xcode, or via normal UI app launch. +**/ ++ (void)initialize_colors_16 +{ + if (codes_fg || codes_bg || colors) return; + + NSMutableArray *m_codes_fg = [NSMutableArray arrayWithCapacity:16]; + NSMutableArray *m_codes_bg = [NSMutableArray arrayWithCapacity:16]; + NSMutableArray *m_colors = [NSMutableArray arrayWithCapacity:16]; + + // In a standard shell only 16 colors are supported. + // + // More information about ansi escape codes can be found online. + // http://en.wikipedia.org/wiki/ANSI_escape_code + + [m_codes_fg addObject:@"30m"]; // normal - black + [m_codes_fg addObject:@"31m"]; // normal - red + [m_codes_fg addObject:@"32m"]; // normal - green + [m_codes_fg addObject:@"33m"]; // normal - yellow + [m_codes_fg addObject:@"34m"]; // normal - blue + [m_codes_fg addObject:@"35m"]; // normal - magenta + [m_codes_fg addObject:@"36m"]; // normal - cyan + [m_codes_fg addObject:@"37m"]; // normal - gray + [m_codes_fg addObject:@"1;30m"]; // bright - darkgray + [m_codes_fg addObject:@"1;31m"]; // bright - red + [m_codes_fg addObject:@"1;32m"]; // bright - green + [m_codes_fg addObject:@"1;33m"]; // bright - yellow + [m_codes_fg addObject:@"1;34m"]; // bright - blue + [m_codes_fg addObject:@"1;35m"]; // bright - magenta + [m_codes_fg addObject:@"1;36m"]; // bright - cyan + [m_codes_fg addObject:@"1;37m"]; // bright - white + + [m_codes_bg addObject:@"40m"]; // normal - black + [m_codes_bg addObject:@"41m"]; // normal - red + [m_codes_bg addObject:@"42m"]; // normal - green + [m_codes_bg addObject:@"43m"]; // normal - yellow + [m_codes_bg addObject:@"44m"]; // normal - blue + [m_codes_bg addObject:@"45m"]; // normal - magenta + [m_codes_bg addObject:@"46m"]; // normal - cyan + [m_codes_bg addObject:@"47m"]; // normal - gray + [m_codes_bg addObject:@"1;40m"]; // bright - darkgray + [m_codes_bg addObject:@"1;41m"]; // bright - red + [m_codes_bg addObject:@"1;42m"]; // bright - green + [m_codes_bg addObject:@"1;43m"]; // bright - yellow + [m_codes_bg addObject:@"1;44m"]; // bright - blue + [m_codes_bg addObject:@"1;45m"]; // bright - magenta + [m_codes_bg addObject:@"1;46m"]; // bright - cyan + [m_codes_bg addObject:@"1;47m"]; // bright - white + +#if MAP_TO_TERMINAL_APP_COLORS + + // Standard Terminal.app colors: + // + // These are the default colors used by Apple's Terminal.app. + + [m_colors addObject:MakeColor( 0, 0, 0)]; // normal - black + [m_colors addObject:MakeColor(194, 54, 33)]; // normal - red + [m_colors addObject:MakeColor( 37, 188, 36)]; // normal - green + [m_colors addObject:MakeColor(173, 173, 39)]; // normal - yellow + [m_colors addObject:MakeColor( 73, 46, 225)]; // normal - blue + [m_colors addObject:MakeColor(211, 56, 211)]; // normal - magenta + [m_colors addObject:MakeColor( 51, 187, 200)]; // normal - cyan + [m_colors addObject:MakeColor(203, 204, 205)]; // normal - gray + [m_colors addObject:MakeColor(129, 131, 131)]; // bright - darkgray + [m_colors addObject:MakeColor(252, 57, 31)]; // bright - red + [m_colors addObject:MakeColor( 49, 231, 34)]; // bright - green + [m_colors addObject:MakeColor(234, 236, 35)]; // bright - yellow + [m_colors addObject:MakeColor( 88, 51, 255)]; // bright - blue + [m_colors addObject:MakeColor(249, 53, 248)]; // bright - magenta + [m_colors addObject:MakeColor( 20, 240, 240)]; // bright - cyan + [m_colors addObject:MakeColor(233, 235, 235)]; // bright - white + +#else + + // Standard xterm colors: + // + // These are the default colors used by most xterm shells. + + [m_colors addObject:MakeColor( 0, 0, 0)]; // normal - black + [m_colors addObject:MakeColor(205, 0, 0)]; // normal - red + [m_colors addObject:MakeColor( 0, 205, 0)]; // normal - green + [m_colors addObject:MakeColor(205, 205, 0)]; // normal - yellow + [m_colors addObject:MakeColor( 0, 0, 238)]; // normal - blue + [m_colors addObject:MakeColor(205, 0, 205)]; // normal - magenta + [m_colors addObject:MakeColor( 0, 205, 205)]; // normal - cyan + [m_colors addObject:MakeColor(229, 229, 229)]; // normal - gray + [m_colors addObject:MakeColor(127, 127, 127)]; // bright - darkgray + [m_colors addObject:MakeColor(255, 0, 0)]; // bright - red + [m_colors addObject:MakeColor( 0, 255, 0)]; // bright - green + [m_colors addObject:MakeColor(255, 255, 0)]; // bright - yellow + [m_colors addObject:MakeColor( 92, 92, 255)]; // bright - blue + [m_colors addObject:MakeColor(255, 0, 255)]; // bright - magenta + [m_colors addObject:MakeColor( 0, 255, 255)]; // bright - cyan + [m_colors addObject:MakeColor(255, 255, 255)]; // bright - white + +#endif + + codes_fg = [m_codes_fg copy]; + codes_bg = [m_codes_bg copy]; + colors = [m_colors copy]; + + NSAssert([codes_fg count] == [codes_bg count], @"Invalid colors/codes array(s)"); + NSAssert([codes_fg count] == [colors count], @"Invalid colors/codes array(s)"); +} + +/** + * Initializes the colors array, as well as the codes_fg and codes_bg arrays, for 256 color mode. + * + * This method is used when the application is running from within a shell that supports 256 color mode. + * This method is not invoked if the application is running within Xcode, or via normal UI app launch. +**/ ++ (void)initialize_colors_256 +{ + if (codes_fg || codes_bg || colors) return; + + NSMutableArray *m_codes_fg = [NSMutableArray arrayWithCapacity:(256-16)]; + NSMutableArray *m_codes_bg = [NSMutableArray arrayWithCapacity:(256-16)]; + NSMutableArray *m_colors = [NSMutableArray arrayWithCapacity:(256-16)]; + + #if MAP_TO_TERMINAL_APP_COLORS + + // Standard Terminal.app colors: + // + // These are the colors the Terminal.app uses in xterm-256color mode. + // In this mode, the terminal supports 256 different colors, specified by 256 color codes. + // + // The first 16 color codes map to the original 16 color codes supported by the earlier xterm-color mode. + // These are actually configurable, and thus we ignore them for the purposes of mapping, + // as we can't rely on them being constant. They are largely duplicated anyway. + // + // The next 216 color codes are designed to run the spectrum, with several shades of every color. + // While the color codes are standardized, the actual RGB values for each color code is not. + // Apple's Terminal.app uses different RGB values from that of a standard xterm. + // Apple's choices in colors are designed to be a little nicer on the eyes. + // + // The last 24 color codes represent a grayscale. + // + // Unfortunately, unlike the standard xterm color chart, + // Apple's RGB values cannot be calculated using a simple formula (at least not that I know of). + // Also, I don't know of any ways to programmatically query the shell for the RGB values. + // So this big giant color chart had to be made by hand. + // + // More information about ansi escape codes can be found online. + // http://en.wikipedia.org/wiki/ANSI_escape_code + + // Colors + + [m_colors addObject:MakeColor( 47, 49, 49)]; + [m_colors addObject:MakeColor( 60, 42, 144)]; + [m_colors addObject:MakeColor( 66, 44, 183)]; + [m_colors addObject:MakeColor( 73, 46, 222)]; + [m_colors addObject:MakeColor( 81, 50, 253)]; + [m_colors addObject:MakeColor( 88, 51, 255)]; + + [m_colors addObject:MakeColor( 42, 128, 37)]; + [m_colors addObject:MakeColor( 42, 127, 128)]; + [m_colors addObject:MakeColor( 44, 126, 169)]; + [m_colors addObject:MakeColor( 56, 125, 209)]; + [m_colors addObject:MakeColor( 59, 124, 245)]; + [m_colors addObject:MakeColor( 66, 123, 255)]; + + [m_colors addObject:MakeColor( 51, 163, 41)]; + [m_colors addObject:MakeColor( 39, 162, 121)]; + [m_colors addObject:MakeColor( 42, 161, 162)]; + [m_colors addObject:MakeColor( 53, 160, 202)]; + [m_colors addObject:MakeColor( 45, 159, 240)]; + [m_colors addObject:MakeColor( 58, 158, 255)]; + + [m_colors addObject:MakeColor( 31, 196, 37)]; + [m_colors addObject:MakeColor( 48, 196, 115)]; + [m_colors addObject:MakeColor( 39, 195, 155)]; + [m_colors addObject:MakeColor( 49, 195, 195)]; + [m_colors addObject:MakeColor( 32, 194, 235)]; + [m_colors addObject:MakeColor( 53, 193, 255)]; + + [m_colors addObject:MakeColor( 50, 229, 35)]; + [m_colors addObject:MakeColor( 40, 229, 109)]; + [m_colors addObject:MakeColor( 27, 229, 149)]; + [m_colors addObject:MakeColor( 49, 228, 189)]; + [m_colors addObject:MakeColor( 33, 228, 228)]; + [m_colors addObject:MakeColor( 53, 227, 255)]; + + [m_colors addObject:MakeColor( 27, 254, 30)]; + [m_colors addObject:MakeColor( 30, 254, 103)]; + [m_colors addObject:MakeColor( 45, 254, 143)]; + [m_colors addObject:MakeColor( 38, 253, 182)]; + [m_colors addObject:MakeColor( 38, 253, 222)]; + [m_colors addObject:MakeColor( 42, 253, 252)]; + + [m_colors addObject:MakeColor(140, 48, 40)]; + [m_colors addObject:MakeColor(136, 51, 136)]; + [m_colors addObject:MakeColor(135, 52, 177)]; + [m_colors addObject:MakeColor(134, 52, 217)]; + [m_colors addObject:MakeColor(135, 56, 248)]; + [m_colors addObject:MakeColor(134, 53, 255)]; + + [m_colors addObject:MakeColor(125, 125, 38)]; + [m_colors addObject:MakeColor(124, 125, 125)]; + [m_colors addObject:MakeColor(122, 124, 166)]; + [m_colors addObject:MakeColor(123, 124, 207)]; + [m_colors addObject:MakeColor(123, 122, 247)]; + [m_colors addObject:MakeColor(124, 121, 255)]; + + [m_colors addObject:MakeColor(119, 160, 35)]; + [m_colors addObject:MakeColor(117, 160, 120)]; + [m_colors addObject:MakeColor(117, 160, 160)]; + [m_colors addObject:MakeColor(115, 159, 201)]; + [m_colors addObject:MakeColor(116, 158, 240)]; + [m_colors addObject:MakeColor(117, 157, 255)]; + + [m_colors addObject:MakeColor(113, 195, 39)]; + [m_colors addObject:MakeColor(110, 194, 114)]; + [m_colors addObject:MakeColor(111, 194, 154)]; + [m_colors addObject:MakeColor(108, 194, 194)]; + [m_colors addObject:MakeColor(109, 193, 234)]; + [m_colors addObject:MakeColor(108, 192, 255)]; + + [m_colors addObject:MakeColor(105, 228, 30)]; + [m_colors addObject:MakeColor(103, 228, 109)]; + [m_colors addObject:MakeColor(105, 228, 148)]; + [m_colors addObject:MakeColor(100, 227, 188)]; + [m_colors addObject:MakeColor( 99, 227, 227)]; + [m_colors addObject:MakeColor( 99, 226, 253)]; + + [m_colors addObject:MakeColor( 92, 253, 34)]; + [m_colors addObject:MakeColor( 96, 253, 103)]; + [m_colors addObject:MakeColor( 97, 253, 142)]; + [m_colors addObject:MakeColor( 88, 253, 182)]; + [m_colors addObject:MakeColor( 93, 253, 221)]; + [m_colors addObject:MakeColor( 88, 254, 251)]; + + [m_colors addObject:MakeColor(177, 53, 34)]; + [m_colors addObject:MakeColor(174, 54, 131)]; + [m_colors addObject:MakeColor(172, 55, 172)]; + [m_colors addObject:MakeColor(171, 57, 213)]; + [m_colors addObject:MakeColor(170, 55, 249)]; + [m_colors addObject:MakeColor(170, 57, 255)]; + + [m_colors addObject:MakeColor(165, 123, 37)]; + [m_colors addObject:MakeColor(163, 123, 123)]; + [m_colors addObject:MakeColor(162, 123, 164)]; + [m_colors addObject:MakeColor(161, 122, 205)]; + [m_colors addObject:MakeColor(161, 121, 241)]; + [m_colors addObject:MakeColor(161, 121, 255)]; + + [m_colors addObject:MakeColor(158, 159, 33)]; + [m_colors addObject:MakeColor(157, 158, 118)]; + [m_colors addObject:MakeColor(157, 158, 159)]; + [m_colors addObject:MakeColor(155, 157, 199)]; + [m_colors addObject:MakeColor(155, 157, 239)]; + [m_colors addObject:MakeColor(154, 156, 255)]; + + [m_colors addObject:MakeColor(152, 193, 40)]; + [m_colors addObject:MakeColor(151, 193, 113)]; + [m_colors addObject:MakeColor(150, 193, 153)]; + [m_colors addObject:MakeColor(150, 192, 193)]; + [m_colors addObject:MakeColor(148, 192, 232)]; + [m_colors addObject:MakeColor(149, 191, 253)]; + + [m_colors addObject:MakeColor(146, 227, 28)]; + [m_colors addObject:MakeColor(144, 227, 108)]; + [m_colors addObject:MakeColor(144, 227, 147)]; + [m_colors addObject:MakeColor(144, 227, 187)]; + [m_colors addObject:MakeColor(142, 226, 227)]; + [m_colors addObject:MakeColor(142, 225, 252)]; + + [m_colors addObject:MakeColor(138, 253, 36)]; + [m_colors addObject:MakeColor(137, 253, 102)]; + [m_colors addObject:MakeColor(136, 253, 141)]; + [m_colors addObject:MakeColor(138, 254, 181)]; + [m_colors addObject:MakeColor(135, 255, 220)]; + [m_colors addObject:MakeColor(133, 255, 250)]; + + [m_colors addObject:MakeColor(214, 57, 30)]; + [m_colors addObject:MakeColor(211, 59, 126)]; + [m_colors addObject:MakeColor(209, 57, 168)]; + [m_colors addObject:MakeColor(208, 55, 208)]; + [m_colors addObject:MakeColor(207, 58, 247)]; + [m_colors addObject:MakeColor(206, 61, 255)]; + + [m_colors addObject:MakeColor(204, 121, 32)]; + [m_colors addObject:MakeColor(202, 121, 121)]; + [m_colors addObject:MakeColor(201, 121, 161)]; + [m_colors addObject:MakeColor(200, 120, 202)]; + [m_colors addObject:MakeColor(200, 120, 241)]; + [m_colors addObject:MakeColor(198, 119, 255)]; + + [m_colors addObject:MakeColor(198, 157, 37)]; + [m_colors addObject:MakeColor(196, 157, 116)]; + [m_colors addObject:MakeColor(195, 156, 157)]; + [m_colors addObject:MakeColor(195, 156, 197)]; + [m_colors addObject:MakeColor(194, 155, 236)]; + [m_colors addObject:MakeColor(193, 155, 255)]; + + [m_colors addObject:MakeColor(191, 192, 36)]; + [m_colors addObject:MakeColor(190, 191, 112)]; + [m_colors addObject:MakeColor(189, 191, 152)]; + [m_colors addObject:MakeColor(189, 191, 191)]; + [m_colors addObject:MakeColor(188, 190, 230)]; + [m_colors addObject:MakeColor(187, 190, 253)]; + + [m_colors addObject:MakeColor(185, 226, 28)]; + [m_colors addObject:MakeColor(184, 226, 106)]; + [m_colors addObject:MakeColor(183, 225, 146)]; + [m_colors addObject:MakeColor(183, 225, 186)]; + [m_colors addObject:MakeColor(182, 225, 225)]; + [m_colors addObject:MakeColor(181, 224, 252)]; + + [m_colors addObject:MakeColor(178, 255, 35)]; + [m_colors addObject:MakeColor(178, 255, 101)]; + [m_colors addObject:MakeColor(177, 254, 141)]; + [m_colors addObject:MakeColor(176, 254, 180)]; + [m_colors addObject:MakeColor(176, 254, 220)]; + [m_colors addObject:MakeColor(175, 253, 249)]; + + [m_colors addObject:MakeColor(247, 56, 30)]; + [m_colors addObject:MakeColor(245, 57, 122)]; + [m_colors addObject:MakeColor(243, 59, 163)]; + [m_colors addObject:MakeColor(244, 60, 204)]; + [m_colors addObject:MakeColor(242, 59, 241)]; + [m_colors addObject:MakeColor(240, 55, 255)]; + + [m_colors addObject:MakeColor(241, 119, 36)]; + [m_colors addObject:MakeColor(240, 120, 118)]; + [m_colors addObject:MakeColor(238, 119, 158)]; + [m_colors addObject:MakeColor(237, 119, 199)]; + [m_colors addObject:MakeColor(237, 118, 238)]; + [m_colors addObject:MakeColor(236, 118, 255)]; + + [m_colors addObject:MakeColor(235, 154, 36)]; + [m_colors addObject:MakeColor(235, 154, 114)]; + [m_colors addObject:MakeColor(234, 154, 154)]; + [m_colors addObject:MakeColor(232, 154, 194)]; + [m_colors addObject:MakeColor(232, 153, 234)]; + [m_colors addObject:MakeColor(232, 153, 255)]; + + [m_colors addObject:MakeColor(230, 190, 30)]; + [m_colors addObject:MakeColor(229, 189, 110)]; + [m_colors addObject:MakeColor(228, 189, 150)]; + [m_colors addObject:MakeColor(227, 189, 190)]; + [m_colors addObject:MakeColor(227, 189, 229)]; + [m_colors addObject:MakeColor(226, 188, 255)]; + + [m_colors addObject:MakeColor(224, 224, 35)]; + [m_colors addObject:MakeColor(223, 224, 105)]; + [m_colors addObject:MakeColor(222, 224, 144)]; + [m_colors addObject:MakeColor(222, 223, 184)]; + [m_colors addObject:MakeColor(222, 223, 224)]; + [m_colors addObject:MakeColor(220, 223, 253)]; + + [m_colors addObject:MakeColor(217, 253, 28)]; + [m_colors addObject:MakeColor(217, 253, 99)]; + [m_colors addObject:MakeColor(216, 252, 139)]; + [m_colors addObject:MakeColor(216, 252, 179)]; + [m_colors addObject:MakeColor(215, 252, 218)]; + [m_colors addObject:MakeColor(215, 251, 250)]; + + [m_colors addObject:MakeColor(255, 61, 30)]; + [m_colors addObject:MakeColor(255, 60, 118)]; + [m_colors addObject:MakeColor(255, 58, 159)]; + [m_colors addObject:MakeColor(255, 56, 199)]; + [m_colors addObject:MakeColor(255, 55, 238)]; + [m_colors addObject:MakeColor(255, 59, 255)]; + + [m_colors addObject:MakeColor(255, 117, 29)]; + [m_colors addObject:MakeColor(255, 117, 115)]; + [m_colors addObject:MakeColor(255, 117, 155)]; + [m_colors addObject:MakeColor(255, 117, 195)]; + [m_colors addObject:MakeColor(255, 116, 235)]; + [m_colors addObject:MakeColor(254, 116, 255)]; + + [m_colors addObject:MakeColor(255, 152, 27)]; + [m_colors addObject:MakeColor(255, 152, 111)]; + [m_colors addObject:MakeColor(254, 152, 152)]; + [m_colors addObject:MakeColor(255, 152, 192)]; + [m_colors addObject:MakeColor(254, 151, 231)]; + [m_colors addObject:MakeColor(253, 151, 253)]; + + [m_colors addObject:MakeColor(255, 187, 33)]; + [m_colors addObject:MakeColor(253, 187, 107)]; + [m_colors addObject:MakeColor(252, 187, 148)]; + [m_colors addObject:MakeColor(253, 187, 187)]; + [m_colors addObject:MakeColor(254, 187, 227)]; + [m_colors addObject:MakeColor(252, 186, 252)]; + + [m_colors addObject:MakeColor(252, 222, 34)]; + [m_colors addObject:MakeColor(251, 222, 103)]; + [m_colors addObject:MakeColor(251, 222, 143)]; + [m_colors addObject:MakeColor(250, 222, 182)]; + [m_colors addObject:MakeColor(251, 221, 222)]; + [m_colors addObject:MakeColor(252, 221, 252)]; + + [m_colors addObject:MakeColor(251, 252, 15)]; + [m_colors addObject:MakeColor(251, 252, 97)]; + [m_colors addObject:MakeColor(249, 252, 137)]; + [m_colors addObject:MakeColor(247, 252, 177)]; + [m_colors addObject:MakeColor(247, 253, 217)]; + [m_colors addObject:MakeColor(254, 255, 255)]; + + // Grayscale + + [m_colors addObject:MakeColor( 52, 53, 53)]; + [m_colors addObject:MakeColor( 57, 58, 59)]; + [m_colors addObject:MakeColor( 66, 67, 67)]; + [m_colors addObject:MakeColor( 75, 76, 76)]; + [m_colors addObject:MakeColor( 83, 85, 85)]; + [m_colors addObject:MakeColor( 92, 93, 94)]; + + [m_colors addObject:MakeColor(101, 102, 102)]; + [m_colors addObject:MakeColor(109, 111, 111)]; + [m_colors addObject:MakeColor(118, 119, 119)]; + [m_colors addObject:MakeColor(126, 127, 128)]; + [m_colors addObject:MakeColor(134, 136, 136)]; + [m_colors addObject:MakeColor(143, 144, 145)]; + + [m_colors addObject:MakeColor(151, 152, 153)]; + [m_colors addObject:MakeColor(159, 161, 161)]; + [m_colors addObject:MakeColor(167, 169, 169)]; + [m_colors addObject:MakeColor(176, 177, 177)]; + [m_colors addObject:MakeColor(184, 185, 186)]; + [m_colors addObject:MakeColor(192, 193, 194)]; + + [m_colors addObject:MakeColor(200, 201, 202)]; + [m_colors addObject:MakeColor(208, 209, 210)]; + [m_colors addObject:MakeColor(216, 218, 218)]; + [m_colors addObject:MakeColor(224, 226, 226)]; + [m_colors addObject:MakeColor(232, 234, 234)]; + [m_colors addObject:MakeColor(240, 242, 242)]; + + // Color codes + + int index = 16; + + while (index < 256) + { + [m_codes_fg addObject:[NSString stringWithFormat:@"38;5;%dm", index]]; + [m_codes_bg addObject:[NSString stringWithFormat:@"48;5;%dm", index]]; + + index++; + } + + #else + + // Standard xterm colors: + // + // These are the colors xterm shells use in xterm-256color mode. + // In this mode, the shell supports 256 different colors, specified by 256 color codes. + // + // The first 16 color codes map to the original 16 color codes supported by the earlier xterm-color mode. + // These are generally configurable, and thus we ignore them for the purposes of mapping, + // as we can't rely on them being constant. They are largely duplicated anyway. + // + // The next 216 color codes are designed to run the spectrum, with several shades of every color. + // The last 24 color codes represent a grayscale. + // + // While the color codes are standardized, the actual RGB values for each color code is not. + // However most standard xterms follow a well known color chart, + // which can easily be calculated using the simple formula below. + // + // More information about ansi escape codes can be found online. + // http://en.wikipedia.org/wiki/ANSI_escape_code + + int index = 16; + + int r; // red + int g; // green + int b; // blue + + int ri; // r increment + int gi; // g increment + int bi; // b increment + + // Calculate xterm colors (using standard algorithm) + + int r = 0; + int g = 0; + int b = 0; + + for (ri = 0; ri < 6; ri++) + { + r = (ri == 0) ? 0 : 95 + (40 * (ri - 1)); + + for (gi = 0; gi < 6; gi++) + { + g = (gi == 0) ? 0 : 95 + (40 * (gi - 1)); + + for (bi = 0; bi < 6; bi++) + { + b = (bi == 0) ? 0 : 95 + (40 * (bi - 1)); + + [m_codes_fg addObject:[NSString stringWithFormat:@"38;5;%dm", index]]; + [m_codes_bg addObject:[NSString stringWithFormat:@"48;5;%dm", index]]; + [m_colors addObject:MakeColor(r, g, b)]; + + index++; + } + } + } + + // Calculate xterm grayscale (using standard algorithm) + + r = 8; + g = 8; + b = 8; + + while (index < 256) + { + [m_codes_fg addObject:[NSString stringWithFormat:@"38;5;%dm", index]]; + [m_codes_bg addObject:[NSString stringWithFormat:@"48;5;%dm", index]]; + [m_colors addObject:MakeColor(r, g, b)]; + + r += 10; + g += 10; + b += 10; + + index++; + } + + #endif + + codes_fg = [m_codes_fg copy]; + codes_bg = [m_codes_bg copy]; + colors = [m_colors copy]; + + NSAssert([codes_fg count] == [codes_bg count], @"Invalid colors/codes array(s)"); + NSAssert([codes_fg count] == [colors count], @"Invalid colors/codes array(s)"); +} + ++ (void)getRed:(CGFloat *)rPtr green:(CGFloat *)gPtr blue:(CGFloat *)bPtr fromColor:(OSColor *)color +{ + #if TARGET_OS_IPHONE + + // iOS + + if ([color respondsToSelector:@selector(getRed:green:blue:alpha:)]) + { + [color getRed:rPtr green:gPtr blue:bPtr alpha:NULL]; + } + else + { + // The method getRed:green:blue:alpha: was only available starting iOS 5. + // So in iOS 4 and earlier, we have to jump through hoops. + + CGColorSpaceRef rgbColorSpace = CGColorSpaceCreateDeviceRGB(); + + unsigned char pixel[4]; + CGContextRef context = CGBitmapContextCreate(&pixel, 1, 1, 8, 4, rgbColorSpace, kCGImageAlphaNoneSkipLast); + + CGContextSetFillColorWithColor(context, [color CGColor]); + CGContextFillRect(context, CGRectMake(0, 0, 1, 1)); + + if (rPtr) { *rPtr = pixel[0] / 255.0f; } + if (gPtr) { *gPtr = pixel[1] / 255.0f; } + if (bPtr) { *bPtr = pixel[2] / 255.0f; } + + CGContextRelease(context); + CGColorSpaceRelease(rgbColorSpace); + } + + #else + + // Mac OS X + + [color getRed:rPtr green:gPtr blue:bPtr alpha:NULL]; + + #endif +} + +/** + * Maps the given color to the closest available color supported by the shell. + * The shell may support 256 colors, or only 16. + * + * This method loops through the known supported color set, and calculates the closest color. + * The array index of that color, within the colors array, is then returned. + * This array index may also be used as the index within the codes_fg and codes_bg arrays. +**/ ++ (NSUInteger)codeIndexForColor:(OSColor *)inColor +{ + CGFloat inR, inG, inB; + [self getRed:&inR green:&inG blue:&inB fromColor:inColor]; + + NSUInteger bestIndex = 0; + CGFloat lowestDistance = 100.0f; + + NSUInteger i = 0; + for (OSColor *color in colors) + { + // Calculate Euclidean distance (lower value means closer to given color) + + CGFloat r, g, b; + [self getRed:&r green:&g blue:&b fromColor:color]; + + #if CGFLOAT_IS_DOUBLE + CGFloat distance = sqrt(pow(r-inR, 2.0) + pow(g-inG, 2.0) + pow(b-inB, 2.0)); + #else + CGFloat distance = sqrtf(powf(r-inR, 2.0f) + powf(g-inG, 2.0f) + powf(b-inB, 2.0f)); + #endif + + NSLogVerbose(@"DDTTYLogger: %3lu : %.3f,%.3f,%.3f & %.3f,%.3f,%.3f = %.6f", + (unsigned long)i, inR, inG, inB, r, g, b, distance); + + if (distance < lowestDistance) + { + bestIndex = i; + lowestDistance = distance; + + NSLogVerbose(@"DDTTYLogger: New best index = %lu", (unsigned long)bestIndex); + } + + i++; + } + + return bestIndex; +} + +/** + * The runtime sends initialize to each class in a program exactly one time just before the class, + * or any class that inherits from it, is sent its first message from within the program. (Thus the + * method may never be invoked if the class is not used.) The runtime sends the initialize message to + * classes in a thread-safe manner. Superclasses receive this message before their subclasses. + * + * This method may also be called directly (assumably by accident), hence the safety mechanism. +**/ ++ (void)initialize +{ + static BOOL initialized = NO; + if (!initialized) + { + initialized = YES; + + char *term = getenv("TERM"); + if (term) + { + if (strcasestr(term, "color") != NULL) + { + isaColorTTY = YES; + isaColor256TTY = (strcasestr(term, "256") != NULL); + + if (isaColor256TTY) + [self initialize_colors_256]; + else + [self initialize_colors_16]; + } + } + else + { + // Xcode does NOT natively support colors in the Xcode debugging console. + // You'll need to install the XcodeColors plugin to see colors in the Xcode console. + // + // PS - Please read the header file before diving into the source code. + + char *xcode_colors = getenv("XcodeColors"); + if (xcode_colors && (strcmp(xcode_colors, "YES") == 0)) + { + isaXcodeColorTTY = YES; + } + } + + NSLogInfo(@"DDTTYLogger: isaColorTTY = %@", (isaColorTTY ? @"YES" : @"NO")); + NSLogInfo(@"DDTTYLogger: isaColor256TTY: %@", (isaColor256TTY ? @"YES" : @"NO")); + NSLogInfo(@"DDTTYLogger: isaXcodeColorTTY: %@", (isaXcodeColorTTY ? @"YES" : @"NO")); + + sharedInstance = [[DDTTYLogger alloc] init]; + } +} + ++ (DDTTYLogger *)sharedInstance +{ + return sharedInstance; +} + +- (id)init +{ + if (sharedInstance != nil) + { + return nil; + } + + if ((self = [super init])) + { + calendar = [NSCalendar autoupdatingCurrentCalendar]; + + calendarUnitFlags = 0; + calendarUnitFlags |= NSYearCalendarUnit; + calendarUnitFlags |= NSMonthCalendarUnit; + calendarUnitFlags |= NSDayCalendarUnit; + calendarUnitFlags |= NSHourCalendarUnit; + calendarUnitFlags |= NSMinuteCalendarUnit; + calendarUnitFlags |= NSSecondCalendarUnit; + + // Initialze 'app' variable (char *) + + appName = [[NSProcessInfo processInfo] processName]; + + appLen = [appName lengthOfBytesUsingEncoding:NSUTF8StringEncoding]; + app = (char *)malloc(appLen + 1); + if (app == NULL) return nil; + + [appName getCString:app maxLength:(appLen+1) encoding:NSUTF8StringEncoding]; + + // Initialize 'pid' variable (char *) + + processID = [NSString stringWithFormat:@"%i", (int)getpid()]; + + pidLen = [processID lengthOfBytesUsingEncoding:NSUTF8StringEncoding]; + pid = (char *)malloc(pidLen + 1); + if (pid == NULL) return nil; + + BOOL processedID = [processID getCString:pid maxLength:(pidLen+1) encoding:NSUTF8StringEncoding]; + if (NO == processedID) return nil; + + // Initialize color stuff + + colorsEnabled = NO; + colorProfilesArray = [[NSMutableArray alloc] initWithCapacity:8]; + colorProfilesDict = [[NSMutableDictionary alloc] initWithCapacity:8]; + } + return self; +} + +- (void)loadDefaultColorProfiles +{ + [self setForegroundColor:MakeColor(214, 57, 30) backgroundColor:nil forFlag:LOG_FLAG_ERROR]; + [self setForegroundColor:MakeColor(204, 121, 32) backgroundColor:nil forFlag:LOG_FLAG_WARN]; +} + +- (BOOL)colorsEnabled +{ + // 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(loggerQueue, ^{ + result = colorsEnabled; + }); + }); + + return result; +} + +- (void)setColorsEnabled:(BOOL)newColorsEnabled +{ + dispatch_block_t block = ^{ @autoreleasepool { + + colorsEnabled = newColorsEnabled; + + if ([colorProfilesArray count] == 0) { + [self loadDefaultColorProfiles]; + } + }}; + + // 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]; + + dispatch_async(globalLoggingQueue, ^{ + dispatch_async(loggerQueue, block); + }); +} + +- (void)setForegroundColor:(OSColor *)txtColor backgroundColor:(OSColor *)bgColor forFlag:(int)mask +{ + [self setForegroundColor:txtColor backgroundColor:bgColor forFlag:mask context:0]; +} + +- (void)setForegroundColor:(OSColor *)txtColor backgroundColor:(OSColor *)bgColor forFlag:(int)mask context:(int)ctxt +{ + dispatch_block_t block = ^{ @autoreleasepool { + + DDTTYLoggerColorProfile *newColorProfile = + [[DDTTYLoggerColorProfile alloc] initWithForegroundColor:txtColor + backgroundColor:bgColor + flag:mask + context:ctxt]; + + NSLogInfo(@"DDTTYLogger: newColorProfile: %@", newColorProfile); + + NSUInteger i = 0; + for (DDTTYLoggerColorProfile *colorProfile in colorProfilesArray) + { + if ((colorProfile->mask == mask) && (colorProfile->context == ctxt)) + { + break; + } + + i++; + } + + if (i < [colorProfilesArray count]) + [colorProfilesArray replaceObjectAtIndex:i withObject:newColorProfile]; + else + [colorProfilesArray addObject:newColorProfile]; + }}; + + // 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(loggerQueue, block); + }); + } +} + +- (void)setForegroundColor:(OSColor *)txtColor backgroundColor:(OSColor *)bgColor forTag:(id )tag +{ + NSAssert([(id )tag conformsToProtocol:@protocol(NSCopying)], @"Invalid tag"); + + dispatch_block_t block = ^{ @autoreleasepool { + + DDTTYLoggerColorProfile *newColorProfile = + [[DDTTYLoggerColorProfile alloc] initWithForegroundColor:txtColor + backgroundColor:bgColor + flag:0 + context:0]; + + NSLogInfo(@"DDTTYLogger: newColorProfile: %@", newColorProfile); + + [colorProfilesDict setObject:newColorProfile forKey:tag]; + }}; + + // 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(loggerQueue, block); + }); + } +} + +- (void)clearColorsForFlag:(int)mask +{ + [self clearColorsForFlag:mask context:0]; +} + +- (void)clearColorsForFlag:(int)mask context:(int)context +{ + dispatch_block_t block = ^{ @autoreleasepool { + + NSUInteger i = 0; + for (DDTTYLoggerColorProfile *colorProfile in colorProfilesArray) + { + if ((colorProfile->mask == mask) && (colorProfile->context == context)) + { + break; + } + + i++; + } + + if (i < [colorProfilesArray count]) + { + [colorProfilesArray removeObjectAtIndex:i]; + } + }}; + + // 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(loggerQueue, block); + }); + } +} + +- (void)clearColorsForTag:(id )tag +{ + NSAssert([(id )tag conformsToProtocol:@protocol(NSCopying)], @"Invalid tag"); + + dispatch_block_t block = ^{ @autoreleasepool { + + [colorProfilesDict removeObjectForKey:tag]; + }}; + + // 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(loggerQueue, block); + }); + } +} + +- (void)clearColorsForAllFlags +{ + dispatch_block_t block = ^{ @autoreleasepool { + + [colorProfilesArray removeAllObjects]; + }}; + + // 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(loggerQueue, block); + }); + } +} + +- (void)clearColorsForAllTags +{ + dispatch_block_t block = ^{ @autoreleasepool { + + [colorProfilesDict removeAllObjects]; + }}; + + // 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(loggerQueue, block); + }); + } +} + +- (void)clearAllColors +{ + dispatch_block_t block = ^{ @autoreleasepool { + + [colorProfilesArray removeAllObjects]; + [colorProfilesDict removeAllObjects]; + }}; + + // 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(loggerQueue, block); + }); + } +} + +- (void)logMessage:(DDLogMessage *)logMessage +{ + NSString *logMsg = logMessage->logMsg; + BOOL isFormatted = NO; + + if (formatter) + { + logMsg = [formatter formatLogMessage:logMessage]; + isFormatted = logMsg != logMessage->logMsg; + } + + if (logMsg) + { + // Search for a color profile associated with the log message + + DDTTYLoggerColorProfile *colorProfile = nil; + + if (colorsEnabled) + { + if (logMessage->tag) + { + colorProfile = [colorProfilesDict objectForKey:logMessage->tag]; + } + if (colorProfile == nil) + { + for (DDTTYLoggerColorProfile *cp in colorProfilesArray) + { + if ((logMessage->logFlag & cp->mask) && (logMessage->logContext == cp->context)) + { + colorProfile = cp; + break; + } + } + } + } + + // Convert log message to C string. + // + // We use the stack instead of the heap for speed if possible. + // But we're extra cautious to avoid a stack overflow. + + NSUInteger msgLen = [logMsg lengthOfBytesUsingEncoding:NSUTF8StringEncoding]; + const BOOL useStack = msgLen < (1024 * 4); + + char msgStack[useStack ? (msgLen + 1) : 1]; // Analyzer doesn't like zero-size array, hence the 1 + char *msg = useStack ? msgStack : (char *)malloc(msgLen + 1); + if (msg == NULL) return; + + BOOL logMsgEnc = [logMsg getCString:msg maxLength:(msgLen + 1) encoding:NSUTF8StringEncoding]; + if (!logMsgEnc) + { + if (!useStack && msg != NULL) free(msg); + return; + } + + // Write the log message to STDERR + + if (isFormatted) + { + // The log message has already been formatted. + + struct iovec v[5]; + + if (colorProfile) + { + v[0].iov_base = colorProfile->fgCode; + v[0].iov_len = colorProfile->fgCodeLen; + + v[1].iov_base = colorProfile->bgCode; + v[1].iov_len = colorProfile->bgCodeLen; + + v[4].iov_base = colorProfile->resetCode; + v[4].iov_len = colorProfile->resetCodeLen; + } + else + { + v[0].iov_base = ""; + v[0].iov_len = 0; + + v[1].iov_base = ""; + v[1].iov_len = 0; + + v[4].iov_base = ""; + v[4].iov_len = 0; + } + + v[2].iov_base = (char *)msg; + v[2].iov_len = msgLen; + + v[3].iov_base = "\n"; + v[3].iov_len = (msg[msgLen] == '\n') ? 0 : 1; + + writev(STDERR_FILENO, v, 5); + } + else + { + // The log message is unformatted, so apply standard NSLog style formatting. + + int len; + + // Calculate timestamp. + // The technique below is faster than using NSDateFormatter. + + NSDateComponents *components = [calendar components:calendarUnitFlags fromDate:logMessage->timestamp]; + + NSTimeInterval epoch = [logMessage->timestamp timeIntervalSinceReferenceDate]; + int milliseconds = (int)((epoch - floor(epoch)) * 1000); + + char ts[24]; + len = snprintf(ts, 24, "%04ld-%02ld-%02ld %02ld:%02ld:%02ld:%03d", // yyyy-MM-dd HH:mm:ss:SSS + (long)components.year, + (long)components.month, + (long)components.day, + (long)components.hour, + (long)components.minute, + (long)components.second, milliseconds); + + size_t tsLen = MIN(24-1, len); + + // Calculate thread ID + // + // How many characters do we need for the thread id? + // logMessage->machThreadID is of type mach_port_t, which is an unsigned int. + // + // 1 hex char = 4 bits + // 8 hex chars for 32 bit, plus ending '\0' = 9 + + char tid[9]; + len = snprintf(tid, 9, "%x", logMessage->machThreadID); + + size_t tidLen = MIN(9-1, len); + + // Here is our format: "%s %s[%i:%s] %s", timestamp, appName, processID, threadID, logMsg + + struct iovec v[13]; + + if (colorProfile) + { + v[0].iov_base = colorProfile->fgCode; + v[0].iov_len = colorProfile->fgCodeLen; + + v[1].iov_base = colorProfile->bgCode; + v[1].iov_len = colorProfile->bgCodeLen; + + v[12].iov_base = colorProfile->resetCode; + v[12].iov_len = colorProfile->resetCodeLen; + } + else + { + v[0].iov_base = ""; + v[0].iov_len = 0; + + v[1].iov_base = ""; + v[1].iov_len = 0; + + v[12].iov_base = ""; + v[12].iov_len = 0; + } + + v[2].iov_base = ts; + v[2].iov_len = tsLen; + + v[3].iov_base = " "; + v[3].iov_len = 1; + + v[4].iov_base = app; + v[4].iov_len = appLen; + + v[5].iov_base = "["; + v[5].iov_len = 1; + + v[6].iov_base = pid; + v[6].iov_len = pidLen; + + v[7].iov_base = ":"; + v[7].iov_len = 1; + + v[8].iov_base = tid; + v[8].iov_len = MIN((size_t)8, tidLen); // snprintf doesn't return what you might think + + v[9].iov_base = "] "; + v[9].iov_len = 2; + + v[10].iov_base = (char *)msg; + v[10].iov_len = msgLen; + + v[11].iov_base = "\n"; + v[11].iov_len = (msg[msgLen] == '\n') ? 0 : 1; + + writev(STDERR_FILENO, v, 13); + } + + if (!useStack) { + free(msg); + } + } +} + +- (NSString *)loggerName +{ + return @"cocoa.lumberjack.ttyLogger"; +} + +@end + +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +@implementation DDTTYLoggerColorProfile + +- (id)initWithForegroundColor:(OSColor *)fgColor backgroundColor:(OSColor *)bgColor flag:(int)aMask context:(int)ctxt +{ + if ((self = [super init])) + { + mask = aMask; + context = ctxt; + + CGFloat r, g, b; + + if (fgColor) + { + [DDTTYLogger getRed:&r green:&g blue:&b fromColor:fgColor]; + + fg_r = (uint8_t)(r * 255.0f); + fg_g = (uint8_t)(g * 255.0f); + fg_b = (uint8_t)(b * 255.0f); + } + if (bgColor) + { + [DDTTYLogger getRed:&r green:&g blue:&b fromColor:bgColor]; + + bg_r = (uint8_t)(r * 255.0f); + bg_g = (uint8_t)(g * 255.0f); + bg_b = (uint8_t)(b * 255.0f); + } + + if (fgColor && isaColorTTY) + { + // Map foreground color to closest available shell color + + fgCodeIndex = [DDTTYLogger codeIndexForColor:fgColor]; + fgCodeRaw = [codes_fg objectAtIndex:fgCodeIndex]; + + NSString *escapeSeq = @"\033["; + + NSUInteger len1 = [escapeSeq lengthOfBytesUsingEncoding:NSUTF8StringEncoding]; + NSUInteger len2 = [fgCodeRaw lengthOfBytesUsingEncoding:NSUTF8StringEncoding]; + + BOOL escapeSeqEnc = [escapeSeq getCString:(fgCode) maxLength:(len1+1) encoding:NSUTF8StringEncoding]; + BOOL fgCodeRawEsc = [fgCodeRaw getCString:(fgCode+len1) maxLength:(len2+1) encoding:NSUTF8StringEncoding]; + if (!escapeSeqEnc || !fgCodeRawEsc) return nil; + + fgCodeLen = len1+len2; + } + else if (fgColor && isaXcodeColorTTY) + { + // Convert foreground color to color code sequence + + const char *escapeSeq = XCODE_COLORS_ESCAPE_SEQ; + + int result = snprintf(fgCode, 24, "%sfg%u,%u,%u;", escapeSeq, fg_r, fg_g, fg_b); + fgCodeLen = MIN(result, (24-1)); + } + else + { + // No foreground color or no color support + + fgCode[0] = '\0'; + fgCodeLen = 0; + } + + if (bgColor && isaColorTTY) + { + // Map background color to closest available shell color + + bgCodeIndex = [DDTTYLogger codeIndexForColor:bgColor]; + bgCodeRaw = [codes_bg objectAtIndex:bgCodeIndex]; + + NSString *escapeSeq = @"\033["; + + NSUInteger len1 = [escapeSeq lengthOfBytesUsingEncoding:NSUTF8StringEncoding]; + NSUInteger len2 = [bgCodeRaw lengthOfBytesUsingEncoding:NSUTF8StringEncoding]; + + BOOL escapeSeqEnc = [escapeSeq getCString:(bgCode) maxLength:(len1+1) encoding:NSUTF8StringEncoding]; + BOOL bgCodeRawEsc = [bgCodeRaw getCString:(bgCode+len1) maxLength:(len2+1) encoding:NSUTF8StringEncoding]; + if (!escapeSeqEnc || !bgCodeRawEsc) return nil; + + bgCodeLen = len1+len2; + } + else if (bgColor && isaXcodeColorTTY) + { + // Convert background color to color code sequence + + const char *escapeSeq = XCODE_COLORS_ESCAPE_SEQ; + + int result = snprintf(bgCode, 24, "%sbg%u,%u,%u;", escapeSeq, bg_r, bg_g, bg_b); + bgCodeLen = MIN(result, (24-1)); + } + else + { + // No background color or no color support + + bgCode[0] = '\0'; + bgCodeLen = 0; + } + + if (isaColorTTY) + { + resetCodeLen = snprintf(resetCode, 8, "\033[0m"); + } + else if (isaXcodeColorTTY) + { + resetCodeLen = snprintf(resetCode, 8, XCODE_COLORS_RESET); + } + else + { + resetCode[0] = '\0'; + resetCodeLen = 0; + } + } + return self; +} + +- (NSString *)description +{ + return [NSString stringWithFormat: + @"", + self, mask, context, fg_r, fg_g, fg_b, bg_r, bg_g, bg_b, fgCodeRaw, bgCodeRaw]; +} + +@end