├── README ├── test.m ├── MWLogging.m ├── run_tests.sh └── MWLogging.h /README: -------------------------------------------------------------------------------- 1 | MWLogging - simple wrapper macros around the ASL (Apple System Log). 2 | 3 | See the blog post: 4 | 5 | http://doing-it-wrong.mikeweller.com/2012/07/youre-doing-it-wrong-1-nslogdebug-ios.html 6 | -------------------------------------------------------------------------------- /test.m: -------------------------------------------------------------------------------- 1 | #import "MWLogging.h" 2 | 3 | /* clang sanity_check_main.m MWLogging.m -framework Foundation && ./a.out */ 4 | int main(int argc, char **argv) { 5 | @autoreleasepool { 6 | 7 | /* This will cause a system-wide broadcast on OSX so we enable 8 | this only when we need it for our tests */ 9 | #if EMERGENCY_ENABLED 10 | MWLogEmergency(@"Emergency test message"); 11 | #endif 12 | 13 | MWLogAlert(@"Alert"); 14 | MWLogCritical(@"Critical"); 15 | MWLogError(@"Error"); 16 | MWLogWarning(@"Warning"); 17 | MWLogNotice(@"Notice"); 18 | MWLogInfo(@"Info"); 19 | MWLogDebug(@"Debug"); 20 | 21 | return 0; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /MWLogging.m: -------------------------------------------------------------------------------- 1 | 2 | // We need all the log functions visible so we set this to DEBUG 3 | #ifdef MW_COMPILE_TIME_LOG_LEVEL 4 | #undef MW_COMPILE_TIME_LOG_LEVEL 5 | #define MW_COMPILE_TIME_LOG_LEVEL ASL_LEVEL_DEBUG 6 | #endif 7 | 8 | #define MW_COMPILE_TIME_LOG_LEVEL ASL_LEVEL_DEBUG 9 | 10 | #import "MWLogging.h" 11 | 12 | static void AddStderrOnce() 13 | { 14 | static dispatch_once_t onceToken; 15 | dispatch_once(&onceToken, ^{ 16 | asl_add_log_file(NULL, STDERR_FILENO); 17 | }); 18 | } 19 | 20 | #define __MW_MAKE_LOG_FUNCTION(LEVEL, NAME) \ 21 | void NAME (NSString *format, ...) \ 22 | { \ 23 | AddStderrOnce(); \ 24 | va_list args; \ 25 | va_start(args, format); \ 26 | NSString *message = [[NSString alloc] initWithFormat:format arguments:args]; \ 27 | asl_log(NULL, NULL, (LEVEL), "%s", [message UTF8String]); \ 28 | va_end(args); \ 29 | } 30 | 31 | __MW_MAKE_LOG_FUNCTION(ASL_LEVEL_EMERG, MWLogEmergency) 32 | __MW_MAKE_LOG_FUNCTION(ASL_LEVEL_ALERT, MWLogAlert) 33 | __MW_MAKE_LOG_FUNCTION(ASL_LEVEL_CRIT, MWLogCritical) 34 | __MW_MAKE_LOG_FUNCTION(ASL_LEVEL_ERR, MWLogError) 35 | __MW_MAKE_LOG_FUNCTION(ASL_LEVEL_WARNING, MWLogWarning) 36 | __MW_MAKE_LOG_FUNCTION(ASL_LEVEL_NOTICE, MWLogNotice) 37 | __MW_MAKE_LOG_FUNCTION(ASL_LEVEL_INFO, MWLogInfo) 38 | __MW_MAKE_LOG_FUNCTION(ASL_LEVEL_DEBUG, MWLogDebug) 39 | 40 | #undef __MW_MAKE_LOG_FUNCTION 41 | -------------------------------------------------------------------------------- /run_tests.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # A dirty bash script to test that our compile-time log levels work 4 | # correctly 5 | 6 | set -e 7 | set -u 8 | set -o pipefail 9 | 10 | SRCROOT=$(dirname "$0") 11 | CC=$(xcrun -find clang) 12 | 13 | test_compile_time_level_output_count() { 14 | log_level="$1" 15 | expected_line_count="$2" 16 | search_string="$3" 17 | expected_search_string_count="$4" 18 | 19 | if [[ "$log_level" == "ASL_LEVEL_EMERGENCY" ]]; then 20 | emergency_enabled="1" 21 | else 22 | emergency_enabled="0" 23 | fi 24 | 25 | echo -n "Testing $log_level compile-time level works..." 26 | 27 | binary_name="$TMPDIR/MWLogging_tests.out" 28 | 29 | "$CC" "$SRCROOT/test.m" "$SRCROOT/MWLogging.m" \ 30 | -o "$binary_name" \ 31 | -framework Foundation \ 32 | "-DMW_COMPILE_TIME_LOG_LEVEL=$log_level" \ 33 | "-DEMERGENCY_ENABLED=$emergency_enabled" 34 | 35 | output=$("$binary_name" 2>&1) 36 | actual_line_count=$(echo "$output" | wc -l) 37 | 38 | if (( "$actual_line_count" != "$expected_line_count" )); then 39 | echo "[FAILED]" 40 | echo "Failed test for $log_level, expected $expected_line_count lines with log level $log_level but got $actual_line_count" >&2 41 | exit 1 42 | fi 43 | 44 | # silly grep and its non-zero return value... we || true to work around 45 | actual_search_string_count=$(echo "$output" | grep "$search_string" | wc -l || true) 46 | 47 | if (( "$actual_search_string_count" != "$expected_search_string_count" )); then 48 | echo "[FAILED]" 49 | echo "Failed test for $log_level, expected $search_string $expected_search_string_count times but got $actual_search_string_count" >&2 50 | exit 1 51 | fi 52 | 53 | echo "[PASSED]" 54 | } 55 | 56 | test_compile_time_level_output_count "ASL_LEVEL_EMERGENCY" 1 "" "1" 57 | test_compile_time_level_output_count "ASL_LEVEL_ALERT" 1 "" "1" 58 | test_compile_time_level_output_count "ASL_LEVEL_CRIT" 2 "" "1" 59 | test_compile_time_level_output_count "ASL_LEVEL_ERR" 3 "" "1" 60 | test_compile_time_level_output_count "ASL_LEVEL_WARNING" 4 "" "1" 61 | test_compile_time_level_output_count "ASL_LEVEL_NOTICE" 5 "" "1" 62 | test_compile_time_level_output_count "ASL_LEVEL_INFO" 6 "" "1" 63 | test_compile_time_level_output_count "ASL_LEVEL_DEBUG" 7 "" "1" 64 | 65 | echo "All tests passed" 66 | -------------------------------------------------------------------------------- /MWLogging.h: -------------------------------------------------------------------------------- 1 | /* 2 | MWLogging. Simple wrapper macros/functions around ASL (Apple System 3 | Log) 4 | 5 | We support a compile-time log level through 6 | MW_COMPILE_TIME_LOG_LEVEL. This will turn the associated log calls 7 | into NOPs. 8 | 9 | The log levels are the constants defined in asl.h: 10 | 11 | #define ASL_LEVEL_EMERG 0 12 | #define ASL_LEVEL_ALERT 1 13 | #define ASL_LEVEL_CRIT 2 14 | #define ASL_LEVEL_ERR 3 15 | #define ASL_LEVEL_WARNING 4 16 | #define ASL_LEVEL_NOTICE 5 17 | #define ASL_LEVEL_INFO 6 18 | #define ASL_LEVEL_DEBUG 7 19 | 20 | For a description of when to use each level, see here: 21 | 22 | http://developer.apple.com/library/mac/#documentation/MacOSX/Conceptual/BPSystemStartup/Chapters/LoggingErrorsAndWarnings.html#//apple_ref/doc/uid/10000172i-SW8-SW1 23 | 24 | Emergency (level 0) - The highest priority, usually reserved for 25 | catastrophic failures and reboot notices. 26 | 27 | Alert (level 1) - A serious failure in a key system. 28 | 29 | Critical (level 2) - A failure in a key system. 30 | 31 | Error (level 3) - Something has failed. 32 | 33 | Warning (level 4) - Something is amiss and might fail if not 34 | corrected. 35 | 36 | Notice (level 5) - Things of moderate interest to the user or 37 | administrator. 38 | 39 | Info (level 6) - The lowest priority that you would normally log, and 40 | purely informational in nature. 41 | 42 | Debug (level 7) - The lowest priority, and normally not logged except 43 | for messages from the kernel. 44 | 45 | 46 | Note that by default the iOS syslog/console will only record items up 47 | to level ASL_LEVEL_NOTICE. 48 | 49 | */ 50 | 51 | /** @todo 52 | 53 | We want better multithread support. Default NULL client uses 54 | locking. Perhaps we can check for [NSThread mainThread] and associate 55 | an asl client object to that thread. Then we can specify 56 | ASL_OPT_STDERR and not need an extra call to add stderr. 57 | 58 | */ 59 | 60 | #import 61 | 62 | // By default, in non-debug mode we want to disable any logging 63 | // statements except NOTICE and above. 64 | #ifndef MW_COMPILE_TIME_LOG_LEVEL 65 | #ifdef NDEBUG 66 | #define MW_COMPILE_TIME_LOG_LEVEL ASL_LEVEL_NOTICE 67 | #else 68 | #define MW_COMPILE_TIME_LOG_LEVEL ASL_LEVEL_DEBUG 69 | #endif 70 | #endif 71 | 72 | #include 73 | 74 | #if MW_COMPILE_TIME_LOG_LEVEL >= ASL_LEVEL_EMERG 75 | void MWLogEmergency(NSString *format, ...); 76 | #else 77 | #define MWLogEmergency(...) 78 | #endif 79 | 80 | #if MW_COMPILE_TIME_LOG_LEVEL >= ASL_LEVEL_ALERT 81 | void MWLogAlert(NSString *format, ...); 82 | #else 83 | #define MWLogAlert(...) 84 | #endif 85 | 86 | #if MW_COMPILE_TIME_LOG_LEVEL >= ASL_LEVEL_CRIT 87 | void MWLogCritical(NSString *format, ...); 88 | #else 89 | #define MWLogCritical(...) 90 | #endif 91 | 92 | #if MW_COMPILE_TIME_LOG_LEVEL >= ASL_LEVEL_ERR 93 | void MWLogError(NSString *format, ...); 94 | #else 95 | #define MWLogError(...) 96 | #endif 97 | 98 | #if MW_COMPILE_TIME_LOG_LEVEL >= ASL_LEVEL_WARNING 99 | void MWLogWarning(NSString *format, ...); 100 | #else 101 | #define MWLogWarning(...) 102 | #endif 103 | 104 | #if MW_COMPILE_TIME_LOG_LEVEL >= ASL_LEVEL_NOTICE 105 | void MWLogNotice(NSString *format, ...); 106 | #else 107 | #define MWLogNotice(...) 108 | #endif 109 | 110 | #if MW_COMPILE_TIME_LOG_LEVEL >= ASL_LEVEL_INFO 111 | void MWLogInfo(NSString *format, ...); 112 | #else 113 | #define MWLogInfo(...) 114 | #endif 115 | 116 | #if MW_COMPILE_TIME_LOG_LEVEL >= ASL_LEVEL_DEBUG 117 | void MWLogDebug(NSString *format, ...); 118 | #else 119 | #define MWLogDebug(...) 120 | #endif 121 | 122 | --------------------------------------------------------------------------------