jens@0: // jens@0: // Logging.m jens@0: // MYUtilities jens@0: // jens@0: // Created by Jens Alfke on 1/5/08. jens@0: // Copyright 2008 Jens Alfke. All rights reserved. jens@0: // jens@0: jens@0: #import "Logging.h" jens@11: #import "CollectionUtils.h" jens@11: jens@11: #include jens@0: #include jens@0: #include jens@11: #include jens@0: jens@0: jens@0: NSString* LOC( NSString *key ) // Localized string lookup jens@0: { jens@0: NSString *value = [[NSBundle mainBundle] localizedStringForKey:key value:nil table:nil]; jens@0: if( value == key ) { jens@0: Warn(@"No localized string for '%@' in Localizable.strings!",key); jens@0: value = [key uppercaseString]; jens@0: } jens@0: return value; jens@0: } jens@0: jens@0: jens@11: typedef enum { jens@11: kLoggingToOther, jens@11: kLoggingToFile, jens@11: kLoggingToTTY, jens@11: kLoggingToColorTTY jens@11: } MYLoggingTo; jens@11: jens@11: jens@1: int _gShouldLog = -1; jens@11: static MYLoggingTo sLoggingTo; jens@1: static NSMutableSet *sEnabledDomains; jens@0: jens@0: jens@11: /** Does the file descriptor connect to console output, i.e. a terminal or Xcode? */ jens@11: static MYLoggingTo getLoggingMode( int fd ) jens@0: { jens@11: if( isatty(fd) ) { jens@11: const char *term = getenv("TERM"); jens@11: if( term && (strstr(term,"ANSI") || strstr(term,"ansi") || strstr(term,"color")) ) jens@11: return kLoggingToColorTTY; jens@11: else jens@11: return kLoggingToTTY; jens@11: } else { jens@11: char path[MAXPATHLEN]; jens@11: if( fcntl(fd, F_GETPATH, path) == 0 ) jens@11: return kLoggingToFile; jens@11: else jens@11: return kLoggingToOther; jens@11: } jens@0: } jens@0: jens@0: jens@1: static void InitLogging() jens@0: { jens@1: if( _gShouldLog != -1 ) jens@1: return; jens@1: jens@1: NSAutoreleasePool *pool = [NSAutoreleasePool new]; jens@1: _gShouldLog = NO; jens@1: sEnabledDomains = [[NSMutableSet alloc] init]; jens@1: NSDictionary *dflts = [[NSUserDefaults standardUserDefaults] dictionaryRepresentation]; jens@1: for( NSString *key in dflts ) { jens@1: if( [key hasPrefix: @"Log"] ) { jens@1: BOOL value = [[NSUserDefaults standardUserDefaults] boolForKey: key]; jens@1: if( key.length==3 ) jens@1: _gShouldLog = value; jens@1: else if( value ) jens@1: [sEnabledDomains addObject: [key substringFromIndex: 3]]; jens@1: } jens@1: } jens@11: sLoggingTo = getLoggingMode(STDERR_FILENO); jens@1: jens@11: Log(@"Logging mode %i enabled in domains: {%@}", jens@11: sLoggingTo, jens@1: [[[sEnabledDomains allObjects] sortedArrayUsingSelector: @selector(caseInsensitiveCompare:)] jens@1: componentsJoinedByString: @", "]); jens@1: [pool drain]; jens@1: } jens@1: jens@1: jens@1: BOOL EnableLog( BOOL enable ) jens@1: { jens@1: if( _gShouldLog == -1 ) jens@1: InitLogging(); jens@1: BOOL old = _gShouldLog; jens@1: _gShouldLog = enable; jens@1: return old; jens@1: } jens@1: jens@1: BOOL _WillLogTo( NSString *domain ) jens@1: { jens@1: if( _gShouldLog == -1 ) jens@1: InitLogging(); jens@1: return _gShouldLog && [sEnabledDomains containsObject: domain]; jens@1: } jens@1: jens@1: BOOL _EnableLogTo( NSString *domain, BOOL enable ) jens@1: { jens@1: if( _gShouldLog == -1 ) jens@1: InitLogging(); jens@1: BOOL old = [sEnabledDomains containsObject: domain]; jens@1: if( enable ) jens@1: [sEnabledDomains addObject: domain]; jens@1: else jens@1: [sEnabledDomains removeObject: domain]; jens@1: return old; jens@1: } jens@1: jens@1: jens@11: #define kWarningPrefix @"\007WARNING*** " jens@11: jens@11: #define COLOR(STR) (sLoggingTo==kLoggingToColorTTY ?@"\033["#STR"m" :@"") jens@11: jens@11: jens@1: static void _Logv( NSString *prefix, NSString *msg, va_list args ) jens@1: { jens@11: if( sLoggingTo > kLoggingToOther ) { jens@0: NSAutoreleasePool *pool = [NSAutoreleasePool new]; jens@0: static NSDateFormatter *sTimestampFormat; jens@0: if( ! sTimestampFormat ) { jens@0: sTimestampFormat = [[NSDateFormatter alloc] init]; jens@0: sTimestampFormat.dateFormat = @"HH:mm:ss.SSS"; jens@0: } jens@0: NSDate *now = [[NSDate alloc] init]; jens@0: NSString *timestamp = [sTimestampFormat stringFromDate: now]; jens@0: [now release]; jens@1: NSString *separator = prefix.length ?@": " :@""; jens@0: msg = [[NSString alloc] initWithFormat: msg arguments: args]; jens@11: NSString *prefixColor = (prefix==kWarningPrefix) ?COLOR(91) :COLOR(93); jens@11: NSString *msgColor = (prefix==kWarningPrefix) ?@"" :COLOR(0); jens@11: NSString *finalMsg = [[NSString alloc] initWithFormat: @"%@%@| %@%@%@%@%@\n", jens@11: COLOR(30),timestamp, jens@11: prefixColor,prefix,separator, jens@11: msgColor,msg]; jens@0: fputs([finalMsg UTF8String], stderr); jens@0: [finalMsg release]; jens@0: [msg release]; jens@0: [pool drain]; jens@11: } else { jens@11: if( prefix.length ) jens@11: msg = $sprintf(@"%@: %@", prefix,msg); jens@0: NSLogv(msg,args); jens@11: } jens@0: } jens@0: jens@0: jens@0: void AlwaysLog( NSString *msg, ... ) jens@0: { jens@0: va_list args; jens@0: va_start(args,msg); jens@1: _Logv(@"",msg,args); jens@0: va_end(args); jens@0: } jens@0: jens@0: jens@0: void _Log( NSString *msg, ... ) jens@0: { jens@1: if( _gShouldLog == -1 ) jens@1: InitLogging(); jens@1: if( _gShouldLog ) { jens@0: va_list args; jens@0: va_start(args,msg); jens@1: _Logv(@"",msg,args); jens@0: va_end(args); jens@0: } jens@0: } jens@0: jens@0: jens@1: void _LogTo( NSString *domain, NSString *msg, ... ) jens@1: { jens@1: if( _gShouldLog == -1 ) jens@1: InitLogging(); jens@1: if( _gShouldLog && [sEnabledDomains containsObject: domain] ) { jens@1: va_list args; jens@1: va_start(args,msg); jens@1: _Logv(domain, msg, args); jens@1: va_end(args); jens@1: } jens@1: } jens@1: jens@1: jens@0: void Warn( NSString *msg, ... ) jens@0: { jens@0: va_list args; jens@0: va_start(args,msg); jens@11: _Logv(kWarningPrefix,msg,args); jens@0: va_end(args); jens@0: } jens@0: jens@0: jens@11: jens@11: jens@11: /* jens@11: Copyright (c) 2008, Jens Alfke . All rights reserved. jens@11: jens@11: Redistribution and use in source and binary forms, with or without modification, are permitted jens@11: provided that the following conditions are met: jens@11: jens@11: * Redistributions of source code must retain the above copyright notice, this list of conditions jens@11: and the following disclaimer. jens@11: * Redistributions in binary form must reproduce the above copyright notice, this list of conditions jens@11: and the following disclaimer in the documentation and/or other materials provided with the jens@11: distribution. jens@11: jens@11: THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR jens@11: IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND jens@11: FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRI- jens@11: BUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES jens@11: (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR jens@11: PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN jens@11: CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF jens@11: THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. jens@11: */