1*9712c20fSFrederick Mayle// 2*9712c20fSFrederick Mayle// GTMLogger.m 3*9712c20fSFrederick Mayle// 4*9712c20fSFrederick Mayle// Copyright 2007-2008 Google LLC 5*9712c20fSFrederick Mayle// 6*9712c20fSFrederick Mayle// Licensed under the Apache License, Version 2.0 (the "License"); you may not 7*9712c20fSFrederick Mayle// use this file except in compliance with the License. You may obtain a copy 8*9712c20fSFrederick Mayle// of the License at 9*9712c20fSFrederick Mayle// 10*9712c20fSFrederick Mayle// http://www.apache.org/licenses/LICENSE-2.0 11*9712c20fSFrederick Mayle// 12*9712c20fSFrederick Mayle// Unless required by applicable law or agreed to in writing, software 13*9712c20fSFrederick Mayle// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT 14*9712c20fSFrederick Mayle// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 15*9712c20fSFrederick Mayle// License for the specific language governing permissions and limitations under 16*9712c20fSFrederick Mayle// the License. 17*9712c20fSFrederick Mayle// 18*9712c20fSFrederick Mayle 19*9712c20fSFrederick Mayle#import "GTMLogger.h" 20*9712c20fSFrederick Mayle#import <fcntl.h> 21*9712c20fSFrederick Mayle#import <unistd.h> 22*9712c20fSFrederick Mayle#import <stdlib.h> 23*9712c20fSFrederick Mayle#import <pthread.h> 24*9712c20fSFrederick Mayle 25*9712c20fSFrederick Mayle 26*9712c20fSFrederick Mayle#if !defined(__clang__) && (__GNUC__*10+__GNUC_MINOR__ >= 42) 27*9712c20fSFrederick Mayle// Some versions of GCC (4.2 and below AFAIK) aren't great about supporting 28*9712c20fSFrederick Mayle// -Wmissing-format-attribute 29*9712c20fSFrederick Mayle// when the function is anything more complex than foo(NSString *fmt, ...). 30*9712c20fSFrederick Mayle// You see the error inside the function when you turn ... into va_args and 31*9712c20fSFrederick Mayle// attempt to call another function (like vsprintf for example). 32*9712c20fSFrederick Mayle// So we just shut off the warning for this file. We reenable it at the end. 33*9712c20fSFrederick Mayle#pragma GCC diagnostic ignored "-Wmissing-format-attribute" 34*9712c20fSFrederick Mayle#endif // !__clang__ 35*9712c20fSFrederick Mayle 36*9712c20fSFrederick Mayle// Reference to the shared GTMLogger instance. This is not a singleton, it's 37*9712c20fSFrederick Mayle// just an easy reference to one shared instance. 38*9712c20fSFrederick Maylestatic GTMLogger *gSharedLogger = nil; 39*9712c20fSFrederick Mayle 40*9712c20fSFrederick Mayle 41*9712c20fSFrederick Mayle@implementation GTMLogger 42*9712c20fSFrederick Mayle 43*9712c20fSFrederick Mayle// Returns a pointer to the shared logger instance. If none exists, a standard 44*9712c20fSFrederick Mayle// logger is created and returned. 45*9712c20fSFrederick Mayle+ (id)sharedLogger { 46*9712c20fSFrederick Mayle @synchronized(self) { 47*9712c20fSFrederick Mayle if (gSharedLogger == nil) { 48*9712c20fSFrederick Mayle gSharedLogger = [[self standardLogger] retain]; 49*9712c20fSFrederick Mayle } 50*9712c20fSFrederick Mayle } 51*9712c20fSFrederick Mayle return [[gSharedLogger retain] autorelease]; 52*9712c20fSFrederick Mayle} 53*9712c20fSFrederick Mayle 54*9712c20fSFrederick Mayle+ (void)setSharedLogger:(GTMLogger *)logger { 55*9712c20fSFrederick Mayle @synchronized(self) { 56*9712c20fSFrederick Mayle [gSharedLogger autorelease]; 57*9712c20fSFrederick Mayle gSharedLogger = [logger retain]; 58*9712c20fSFrederick Mayle } 59*9712c20fSFrederick Mayle} 60*9712c20fSFrederick Mayle 61*9712c20fSFrederick Mayle+ (id)standardLogger { 62*9712c20fSFrederick Mayle // Don't trust NSFileHandle not to throw 63*9712c20fSFrederick Mayle @try { 64*9712c20fSFrederick Mayle id<GTMLogWriter> writer = [NSFileHandle fileHandleWithStandardOutput]; 65*9712c20fSFrederick Mayle id<GTMLogFormatter> fr = [[[GTMLogStandardFormatter alloc] init] 66*9712c20fSFrederick Mayle autorelease]; 67*9712c20fSFrederick Mayle id<GTMLogFilter> filter = [[[GTMLogLevelFilter alloc] init] autorelease]; 68*9712c20fSFrederick Mayle return [[[self alloc] initWithWriter:writer 69*9712c20fSFrederick Mayle formatter:fr 70*9712c20fSFrederick Mayle filter:filter] autorelease]; 71*9712c20fSFrederick Mayle } 72*9712c20fSFrederick Mayle @catch (id e) { 73*9712c20fSFrederick Mayle // Ignored 74*9712c20fSFrederick Mayle } 75*9712c20fSFrederick Mayle return nil; 76*9712c20fSFrederick Mayle} 77*9712c20fSFrederick Mayle 78*9712c20fSFrederick Mayle+ (id)standardLoggerWithStderr { 79*9712c20fSFrederick Mayle // Don't trust NSFileHandle not to throw 80*9712c20fSFrederick Mayle @try { 81*9712c20fSFrederick Mayle id me = [self standardLogger]; 82*9712c20fSFrederick Mayle [me setWriter:[NSFileHandle fileHandleWithStandardError]]; 83*9712c20fSFrederick Mayle return me; 84*9712c20fSFrederick Mayle } 85*9712c20fSFrederick Mayle @catch (id e) { 86*9712c20fSFrederick Mayle // Ignored 87*9712c20fSFrederick Mayle } 88*9712c20fSFrederick Mayle return nil; 89*9712c20fSFrederick Mayle} 90*9712c20fSFrederick Mayle 91*9712c20fSFrederick Mayle+ (id)standardLoggerWithStdoutAndStderr { 92*9712c20fSFrederick Mayle // We're going to take advantage of the GTMLogger to GTMLogWriter adaptor 93*9712c20fSFrederick Mayle // and create a composite logger that an outer "standard" logger can use 94*9712c20fSFrederick Mayle // as a writer. Our inner loggers should apply no formatting since the main 95*9712c20fSFrederick Mayle // logger does that and we want the caller to be able to change formatters 96*9712c20fSFrederick Mayle // or add writers without knowing the inner structure of our composite. 97*9712c20fSFrederick Mayle 98*9712c20fSFrederick Mayle // Don't trust NSFileHandle not to throw 99*9712c20fSFrederick Mayle @try { 100*9712c20fSFrederick Mayle GTMLogBasicFormatter *formatter = [[[GTMLogBasicFormatter alloc] init] 101*9712c20fSFrederick Mayle autorelease]; 102*9712c20fSFrederick Mayle GTMLogger *stdoutLogger = 103*9712c20fSFrederick Mayle [self loggerWithWriter:[NSFileHandle fileHandleWithStandardOutput] 104*9712c20fSFrederick Mayle formatter:formatter 105*9712c20fSFrederick Mayle filter:[[[GTMLogMaximumLevelFilter alloc] 106*9712c20fSFrederick Mayle initWithMaximumLevel:kGTMLoggerLevelInfo] 107*9712c20fSFrederick Mayle autorelease]]; 108*9712c20fSFrederick Mayle GTMLogger *stderrLogger = 109*9712c20fSFrederick Mayle [self loggerWithWriter:[NSFileHandle fileHandleWithStandardError] 110*9712c20fSFrederick Mayle formatter:formatter 111*9712c20fSFrederick Mayle filter:[[[GTMLogMininumLevelFilter alloc] 112*9712c20fSFrederick Mayle initWithMinimumLevel:kGTMLoggerLevelError] 113*9712c20fSFrederick Mayle autorelease]]; 114*9712c20fSFrederick Mayle GTMLogger *compositeWriter = 115*9712c20fSFrederick Mayle [self loggerWithWriter:[NSArray arrayWithObjects: 116*9712c20fSFrederick Mayle stdoutLogger, stderrLogger, nil] 117*9712c20fSFrederick Mayle formatter:formatter 118*9712c20fSFrederick Mayle filter:[[[GTMLogNoFilter alloc] init] autorelease]]; 119*9712c20fSFrederick Mayle GTMLogger *outerLogger = [self standardLogger]; 120*9712c20fSFrederick Mayle [outerLogger setWriter:compositeWriter]; 121*9712c20fSFrederick Mayle return outerLogger; 122*9712c20fSFrederick Mayle } 123*9712c20fSFrederick Mayle @catch (id e) { 124*9712c20fSFrederick Mayle // Ignored 125*9712c20fSFrederick Mayle } 126*9712c20fSFrederick Mayle return nil; 127*9712c20fSFrederick Mayle} 128*9712c20fSFrederick Mayle 129*9712c20fSFrederick Mayle+ (id)standardLoggerWithPath:(NSString *)path { 130*9712c20fSFrederick Mayle @try { 131*9712c20fSFrederick Mayle NSFileHandle *fh = [NSFileHandle fileHandleForLoggingAtPath:path mode:0644]; 132*9712c20fSFrederick Mayle if (fh == nil) return nil; 133*9712c20fSFrederick Mayle id me = [self standardLogger]; 134*9712c20fSFrederick Mayle [me setWriter:fh]; 135*9712c20fSFrederick Mayle return me; 136*9712c20fSFrederick Mayle } 137*9712c20fSFrederick Mayle @catch (id e) { 138*9712c20fSFrederick Mayle // Ignored 139*9712c20fSFrederick Mayle } 140*9712c20fSFrederick Mayle return nil; 141*9712c20fSFrederick Mayle} 142*9712c20fSFrederick Mayle 143*9712c20fSFrederick Mayle+ (id)loggerWithWriter:(id<GTMLogWriter>)writer 144*9712c20fSFrederick Mayle formatter:(id<GTMLogFormatter>)formatter 145*9712c20fSFrederick Mayle filter:(id<GTMLogFilter>)filter { 146*9712c20fSFrederick Mayle return [[[self alloc] initWithWriter:writer 147*9712c20fSFrederick Mayle formatter:formatter 148*9712c20fSFrederick Mayle filter:filter] autorelease]; 149*9712c20fSFrederick Mayle} 150*9712c20fSFrederick Mayle 151*9712c20fSFrederick Mayle+ (id)logger { 152*9712c20fSFrederick Mayle return [[[self alloc] init] autorelease]; 153*9712c20fSFrederick Mayle} 154*9712c20fSFrederick Mayle 155*9712c20fSFrederick Mayle- (id)init { 156*9712c20fSFrederick Mayle return [self initWithWriter:nil formatter:nil filter:nil]; 157*9712c20fSFrederick Mayle} 158*9712c20fSFrederick Mayle 159*9712c20fSFrederick Mayle- (id)initWithWriter:(id<GTMLogWriter>)writer 160*9712c20fSFrederick Mayle formatter:(id<GTMLogFormatter>)formatter 161*9712c20fSFrederick Mayle filter:(id<GTMLogFilter>)filter { 162*9712c20fSFrederick Mayle if ((self = [super init])) { 163*9712c20fSFrederick Mayle [self setWriter:writer]; 164*9712c20fSFrederick Mayle [self setFormatter:formatter]; 165*9712c20fSFrederick Mayle [self setFilter:filter]; 166*9712c20fSFrederick Mayle } 167*9712c20fSFrederick Mayle return self; 168*9712c20fSFrederick Mayle} 169*9712c20fSFrederick Mayle 170*9712c20fSFrederick Mayle- (void)dealloc { 171*9712c20fSFrederick Mayle // Unlikely, but |writer_| may be an NSFileHandle, which can throw 172*9712c20fSFrederick Mayle @try { 173*9712c20fSFrederick Mayle [formatter_ release]; 174*9712c20fSFrederick Mayle [filter_ release]; 175*9712c20fSFrederick Mayle [writer_ release]; 176*9712c20fSFrederick Mayle } 177*9712c20fSFrederick Mayle @catch (id e) { 178*9712c20fSFrederick Mayle // Ignored 179*9712c20fSFrederick Mayle } 180*9712c20fSFrederick Mayle [super dealloc]; 181*9712c20fSFrederick Mayle} 182*9712c20fSFrederick Mayle 183*9712c20fSFrederick Mayle- (id<GTMLogWriter>)writer { 184*9712c20fSFrederick Mayle return [[writer_ retain] autorelease]; 185*9712c20fSFrederick Mayle} 186*9712c20fSFrederick Mayle 187*9712c20fSFrederick Mayle- (void)setWriter:(id<GTMLogWriter>)writer { 188*9712c20fSFrederick Mayle @synchronized(self) { 189*9712c20fSFrederick Mayle [writer_ autorelease]; 190*9712c20fSFrederick Mayle writer_ = nil; 191*9712c20fSFrederick Mayle if (writer == nil) { 192*9712c20fSFrederick Mayle // Try to use stdout, but don't trust NSFileHandle 193*9712c20fSFrederick Mayle @try { 194*9712c20fSFrederick Mayle writer_ = [[NSFileHandle fileHandleWithStandardOutput] retain]; 195*9712c20fSFrederick Mayle } 196*9712c20fSFrederick Mayle @catch (id e) { 197*9712c20fSFrederick Mayle // Leave |writer_| nil 198*9712c20fSFrederick Mayle } 199*9712c20fSFrederick Mayle } else { 200*9712c20fSFrederick Mayle writer_ = [writer retain]; 201*9712c20fSFrederick Mayle } 202*9712c20fSFrederick Mayle } 203*9712c20fSFrederick Mayle} 204*9712c20fSFrederick Mayle 205*9712c20fSFrederick Mayle- (id<GTMLogFormatter>)formatter { 206*9712c20fSFrederick Mayle return [[formatter_ retain] autorelease]; 207*9712c20fSFrederick Mayle} 208*9712c20fSFrederick Mayle 209*9712c20fSFrederick Mayle- (void)setFormatter:(id<GTMLogFormatter>)formatter { 210*9712c20fSFrederick Mayle @synchronized(self) { 211*9712c20fSFrederick Mayle [formatter_ autorelease]; 212*9712c20fSFrederick Mayle formatter_ = nil; 213*9712c20fSFrederick Mayle if (formatter == nil) { 214*9712c20fSFrederick Mayle @try { 215*9712c20fSFrederick Mayle formatter_ = [[GTMLogBasicFormatter alloc] init]; 216*9712c20fSFrederick Mayle } 217*9712c20fSFrederick Mayle @catch (id e) { 218*9712c20fSFrederick Mayle // Leave |formatter_| nil 219*9712c20fSFrederick Mayle } 220*9712c20fSFrederick Mayle } else { 221*9712c20fSFrederick Mayle formatter_ = [formatter retain]; 222*9712c20fSFrederick Mayle } 223*9712c20fSFrederick Mayle } 224*9712c20fSFrederick Mayle} 225*9712c20fSFrederick Mayle 226*9712c20fSFrederick Mayle- (id<GTMLogFilter>)filter { 227*9712c20fSFrederick Mayle return [[filter_ retain] autorelease]; 228*9712c20fSFrederick Mayle} 229*9712c20fSFrederick Mayle 230*9712c20fSFrederick Mayle- (void)setFilter:(id<GTMLogFilter>)filter { 231*9712c20fSFrederick Mayle @synchronized(self) { 232*9712c20fSFrederick Mayle [filter_ autorelease]; 233*9712c20fSFrederick Mayle filter_ = nil; 234*9712c20fSFrederick Mayle if (filter == nil) { 235*9712c20fSFrederick Mayle @try { 236*9712c20fSFrederick Mayle filter_ = [[GTMLogNoFilter alloc] init]; 237*9712c20fSFrederick Mayle } 238*9712c20fSFrederick Mayle @catch (id e) { 239*9712c20fSFrederick Mayle // Leave |filter_| nil 240*9712c20fSFrederick Mayle } 241*9712c20fSFrederick Mayle } else { 242*9712c20fSFrederick Mayle filter_ = [filter retain]; 243*9712c20fSFrederick Mayle } 244*9712c20fSFrederick Mayle } 245*9712c20fSFrederick Mayle} 246*9712c20fSFrederick Mayle 247*9712c20fSFrederick Mayle- (void)logDebug:(NSString *)fmt, ... { 248*9712c20fSFrederick Mayle va_list args; 249*9712c20fSFrederick Mayle va_start(args, fmt); 250*9712c20fSFrederick Mayle [self logInternalFunc:NULL format:fmt valist:args level:kGTMLoggerLevelDebug]; 251*9712c20fSFrederick Mayle va_end(args); 252*9712c20fSFrederick Mayle} 253*9712c20fSFrederick Mayle 254*9712c20fSFrederick Mayle- (void)logInfo:(NSString *)fmt, ... { 255*9712c20fSFrederick Mayle va_list args; 256*9712c20fSFrederick Mayle va_start(args, fmt); 257*9712c20fSFrederick Mayle [self logInternalFunc:NULL format:fmt valist:args level:kGTMLoggerLevelInfo]; 258*9712c20fSFrederick Mayle va_end(args); 259*9712c20fSFrederick Mayle} 260*9712c20fSFrederick Mayle 261*9712c20fSFrederick Mayle- (void)logError:(NSString *)fmt, ... { 262*9712c20fSFrederick Mayle va_list args; 263*9712c20fSFrederick Mayle va_start(args, fmt); 264*9712c20fSFrederick Mayle [self logInternalFunc:NULL format:fmt valist:args level:kGTMLoggerLevelError]; 265*9712c20fSFrederick Mayle va_end(args); 266*9712c20fSFrederick Mayle} 267*9712c20fSFrederick Mayle 268*9712c20fSFrederick Mayle- (void)logAssert:(NSString *)fmt, ... { 269*9712c20fSFrederick Mayle va_list args; 270*9712c20fSFrederick Mayle va_start(args, fmt); 271*9712c20fSFrederick Mayle [self logInternalFunc:NULL format:fmt valist:args level:kGTMLoggerLevelAssert]; 272*9712c20fSFrederick Mayle va_end(args); 273*9712c20fSFrederick Mayle} 274*9712c20fSFrederick Mayle 275*9712c20fSFrederick Mayle@end // GTMLogger 276*9712c20fSFrederick Mayle 277*9712c20fSFrederick Mayle@implementation GTMLogger (GTMLoggerMacroHelpers) 278*9712c20fSFrederick Mayle 279*9712c20fSFrederick Mayle- (void)logFuncDebug:(const char *)func msg:(NSString *)fmt, ... { 280*9712c20fSFrederick Mayle va_list args; 281*9712c20fSFrederick Mayle va_start(args, fmt); 282*9712c20fSFrederick Mayle [self logInternalFunc:func format:fmt valist:args level:kGTMLoggerLevelDebug]; 283*9712c20fSFrederick Mayle va_end(args); 284*9712c20fSFrederick Mayle} 285*9712c20fSFrederick Mayle 286*9712c20fSFrederick Mayle- (void)logFuncInfo:(const char *)func msg:(NSString *)fmt, ... { 287*9712c20fSFrederick Mayle va_list args; 288*9712c20fSFrederick Mayle va_start(args, fmt); 289*9712c20fSFrederick Mayle [self logInternalFunc:func format:fmt valist:args level:kGTMLoggerLevelInfo]; 290*9712c20fSFrederick Mayle va_end(args); 291*9712c20fSFrederick Mayle} 292*9712c20fSFrederick Mayle 293*9712c20fSFrederick Mayle- (void)logFuncError:(const char *)func msg:(NSString *)fmt, ... { 294*9712c20fSFrederick Mayle va_list args; 295*9712c20fSFrederick Mayle va_start(args, fmt); 296*9712c20fSFrederick Mayle [self logInternalFunc:func format:fmt valist:args level:kGTMLoggerLevelError]; 297*9712c20fSFrederick Mayle va_end(args); 298*9712c20fSFrederick Mayle} 299*9712c20fSFrederick Mayle 300*9712c20fSFrederick Mayle- (void)logFuncAssert:(const char *)func msg:(NSString *)fmt, ... { 301*9712c20fSFrederick Mayle va_list args; 302*9712c20fSFrederick Mayle va_start(args, fmt); 303*9712c20fSFrederick Mayle [self logInternalFunc:func format:fmt valist:args level:kGTMLoggerLevelAssert]; 304*9712c20fSFrederick Mayle va_end(args); 305*9712c20fSFrederick Mayle} 306*9712c20fSFrederick Mayle 307*9712c20fSFrederick Mayle@end // GTMLoggerMacroHelpers 308*9712c20fSFrederick Mayle 309*9712c20fSFrederick Mayle@implementation GTMLogger (PrivateMethods) 310*9712c20fSFrederick Mayle 311*9712c20fSFrederick Mayle- (void)logInternalFunc:(const char *)func 312*9712c20fSFrederick Mayle format:(NSString *)fmt 313*9712c20fSFrederick Mayle valist:(va_list)args 314*9712c20fSFrederick Mayle level:(GTMLoggerLevel)level { 315*9712c20fSFrederick Mayle // Primary point where logging happens, logging should never throw, catch 316*9712c20fSFrederick Mayle // everything. 317*9712c20fSFrederick Mayle @try { 318*9712c20fSFrederick Mayle NSString *fname = func ? [NSString stringWithUTF8String:func] : nil; 319*9712c20fSFrederick Mayle NSString *msg = [formatter_ stringForFunc:fname 320*9712c20fSFrederick Mayle withFormat:fmt 321*9712c20fSFrederick Mayle valist:args 322*9712c20fSFrederick Mayle level:level]; 323*9712c20fSFrederick Mayle if (msg && [filter_ filterAllowsMessage:msg level:level]) 324*9712c20fSFrederick Mayle [writer_ logMessage:msg level:level]; 325*9712c20fSFrederick Mayle } 326*9712c20fSFrederick Mayle @catch (id e) { 327*9712c20fSFrederick Mayle // Ignored 328*9712c20fSFrederick Mayle } 329*9712c20fSFrederick Mayle} 330*9712c20fSFrederick Mayle 331*9712c20fSFrederick Mayle@end // PrivateMethods 332*9712c20fSFrederick Mayle 333*9712c20fSFrederick Mayle 334*9712c20fSFrederick Mayle@implementation NSFileHandle (GTMFileHandleLogWriter) 335*9712c20fSFrederick Mayle 336*9712c20fSFrederick Mayle+ (id)fileHandleForLoggingAtPath:(NSString *)path mode:(mode_t)mode { 337*9712c20fSFrederick Mayle int fd = -1; 338*9712c20fSFrederick Mayle if (path) { 339*9712c20fSFrederick Mayle int flags = O_WRONLY | O_APPEND | O_CREAT; 340*9712c20fSFrederick Mayle fd = open([path fileSystemRepresentation], flags, mode); 341*9712c20fSFrederick Mayle } 342*9712c20fSFrederick Mayle if (fd == -1) return nil; 343*9712c20fSFrederick Mayle return [[[self alloc] initWithFileDescriptor:fd 344*9712c20fSFrederick Mayle closeOnDealloc:YES] autorelease]; 345*9712c20fSFrederick Mayle} 346*9712c20fSFrederick Mayle 347*9712c20fSFrederick Mayle- (void)logMessage:(NSString *)msg level:(GTMLoggerLevel)level { 348*9712c20fSFrederick Mayle @synchronized(self) { 349*9712c20fSFrederick Mayle // Closed pipes should not generate exceptions in our caller. Catch here 350*9712c20fSFrederick Mayle // as well [GTMLogger logInternalFunc:...] so that an exception in this 351*9712c20fSFrederick Mayle // writer does not prevent other writers from having a chance. 352*9712c20fSFrederick Mayle @try { 353*9712c20fSFrederick Mayle NSString *line = [NSString stringWithFormat:@"%@\n", msg]; 354*9712c20fSFrederick Mayle [self writeData:[line dataUsingEncoding:NSUTF8StringEncoding]]; 355*9712c20fSFrederick Mayle } 356*9712c20fSFrederick Mayle @catch (id e) { 357*9712c20fSFrederick Mayle // Ignored 358*9712c20fSFrederick Mayle } 359*9712c20fSFrederick Mayle } 360*9712c20fSFrederick Mayle} 361*9712c20fSFrederick Mayle 362*9712c20fSFrederick Mayle@end // GTMFileHandleLogWriter 363*9712c20fSFrederick Mayle 364*9712c20fSFrederick Mayle 365*9712c20fSFrederick Mayle@implementation NSArray (GTMArrayCompositeLogWriter) 366*9712c20fSFrederick Mayle 367*9712c20fSFrederick Mayle- (void)logMessage:(NSString *)msg level:(GTMLoggerLevel)level { 368*9712c20fSFrederick Mayle @synchronized(self) { 369*9712c20fSFrederick Mayle id<GTMLogWriter> child = nil; 370*9712c20fSFrederick Mayle GTM_FOREACH_OBJECT(child, self) { 371*9712c20fSFrederick Mayle if ([child conformsToProtocol:@protocol(GTMLogWriter)]) 372*9712c20fSFrederick Mayle [child logMessage:msg level:level]; 373*9712c20fSFrederick Mayle } 374*9712c20fSFrederick Mayle } 375*9712c20fSFrederick Mayle} 376*9712c20fSFrederick Mayle 377*9712c20fSFrederick Mayle@end // GTMArrayCompositeLogWriter 378*9712c20fSFrederick Mayle 379*9712c20fSFrederick Mayle 380*9712c20fSFrederick Mayle@implementation GTMLogger (GTMLoggerLogWriter) 381*9712c20fSFrederick Mayle 382*9712c20fSFrederick Mayle- (void)logMessage:(NSString *)msg level:(GTMLoggerLevel)level { 383*9712c20fSFrederick Mayle switch (level) { 384*9712c20fSFrederick Mayle case kGTMLoggerLevelDebug: 385*9712c20fSFrederick Mayle [self logDebug:@"%@", msg]; 386*9712c20fSFrederick Mayle break; 387*9712c20fSFrederick Mayle case kGTMLoggerLevelInfo: 388*9712c20fSFrederick Mayle [self logInfo:@"%@", msg]; 389*9712c20fSFrederick Mayle break; 390*9712c20fSFrederick Mayle case kGTMLoggerLevelError: 391*9712c20fSFrederick Mayle [self logError:@"%@", msg]; 392*9712c20fSFrederick Mayle break; 393*9712c20fSFrederick Mayle case kGTMLoggerLevelAssert: 394*9712c20fSFrederick Mayle [self logAssert:@"%@", msg]; 395*9712c20fSFrederick Mayle break; 396*9712c20fSFrederick Mayle default: 397*9712c20fSFrederick Mayle // Ignore the message. 398*9712c20fSFrederick Mayle break; 399*9712c20fSFrederick Mayle } 400*9712c20fSFrederick Mayle} 401*9712c20fSFrederick Mayle 402*9712c20fSFrederick Mayle@end // GTMLoggerLogWriter 403*9712c20fSFrederick Mayle 404*9712c20fSFrederick Mayle 405*9712c20fSFrederick Mayle@implementation GTMLogBasicFormatter 406*9712c20fSFrederick Mayle 407*9712c20fSFrederick Mayle- (NSString *)prettyNameForFunc:(NSString *)func { 408*9712c20fSFrederick Mayle NSString *name = [func stringByTrimmingCharactersInSet: 409*9712c20fSFrederick Mayle [NSCharacterSet whitespaceAndNewlineCharacterSet]]; 410*9712c20fSFrederick Mayle NSString *function = @"(unknown)"; 411*9712c20fSFrederick Mayle if ([name length]) { 412*9712c20fSFrederick Mayle if (// Objective C __func__ and __PRETTY_FUNCTION__ 413*9712c20fSFrederick Mayle [name hasPrefix:@"-["] || [name hasPrefix:@"+["] || 414*9712c20fSFrederick Mayle // C++ __PRETTY_FUNCTION__ and other preadorned formats 415*9712c20fSFrederick Mayle [name hasSuffix:@")"]) { 416*9712c20fSFrederick Mayle function = name; 417*9712c20fSFrederick Mayle } else { 418*9712c20fSFrederick Mayle // Assume C99 __func__ 419*9712c20fSFrederick Mayle function = [NSString stringWithFormat:@"%@()", name]; 420*9712c20fSFrederick Mayle } 421*9712c20fSFrederick Mayle } 422*9712c20fSFrederick Mayle return function; 423*9712c20fSFrederick Mayle} 424*9712c20fSFrederick Mayle 425*9712c20fSFrederick Mayle- (NSString *)stringForFunc:(NSString *)func 426*9712c20fSFrederick Mayle withFormat:(NSString *)fmt 427*9712c20fSFrederick Mayle valist:(va_list)args 428*9712c20fSFrederick Mayle level:(GTMLoggerLevel)level { 429*9712c20fSFrederick Mayle // Performance note: We may want to do a quick check here to see if |fmt| 430*9712c20fSFrederick Mayle // contains a '%', and if not, simply return 'fmt'. 431*9712c20fSFrederick Mayle if (!(fmt && args)) return nil; 432*9712c20fSFrederick Mayle return [[[NSString alloc] initWithFormat:fmt arguments:args] autorelease]; 433*9712c20fSFrederick Mayle} 434*9712c20fSFrederick Mayle 435*9712c20fSFrederick Mayle@end // GTMLogBasicFormatter 436*9712c20fSFrederick Mayle 437*9712c20fSFrederick Mayle 438*9712c20fSFrederick Mayle@implementation GTMLogStandardFormatter 439*9712c20fSFrederick Mayle 440*9712c20fSFrederick Mayle- (id)init { 441*9712c20fSFrederick Mayle if ((self = [super init])) { 442*9712c20fSFrederick Mayle dateFormatter_ = [[NSDateFormatter alloc] init]; 443*9712c20fSFrederick Mayle [dateFormatter_ setFormatterBehavior:NSDateFormatterBehavior10_4]; 444*9712c20fSFrederick Mayle [dateFormatter_ setDateFormat:@"yyyy-MM-dd HH:mm:ss.SSS"]; 445*9712c20fSFrederick Mayle pname_ = [[[NSProcessInfo processInfo] processName] copy]; 446*9712c20fSFrederick Mayle pid_ = [[NSProcessInfo processInfo] processIdentifier]; 447*9712c20fSFrederick Mayle if (!(dateFormatter_ && pname_)) { 448*9712c20fSFrederick Mayle [self release]; 449*9712c20fSFrederick Mayle return nil; 450*9712c20fSFrederick Mayle } 451*9712c20fSFrederick Mayle } 452*9712c20fSFrederick Mayle return self; 453*9712c20fSFrederick Mayle} 454*9712c20fSFrederick Mayle 455*9712c20fSFrederick Mayle- (void)dealloc { 456*9712c20fSFrederick Mayle [dateFormatter_ release]; 457*9712c20fSFrederick Mayle [pname_ release]; 458*9712c20fSFrederick Mayle [super dealloc]; 459*9712c20fSFrederick Mayle} 460*9712c20fSFrederick Mayle 461*9712c20fSFrederick Mayle- (NSString *)stringForFunc:(NSString *)func 462*9712c20fSFrederick Mayle withFormat:(NSString *)fmt 463*9712c20fSFrederick Mayle valist:(va_list)args 464*9712c20fSFrederick Mayle level:(GTMLoggerLevel)level { 465*9712c20fSFrederick Mayle NSString *tstamp = nil; 466*9712c20fSFrederick Mayle @synchronized (dateFormatter_) { 467*9712c20fSFrederick Mayle tstamp = [dateFormatter_ stringFromDate:[NSDate date]]; 468*9712c20fSFrederick Mayle } 469*9712c20fSFrederick Mayle return [NSString stringWithFormat:@"%@ %@[%d/%p] [lvl=%d] %@ %@", 470*9712c20fSFrederick Mayle tstamp, pname_, pid_, pthread_self(), 471*9712c20fSFrederick Mayle level, [self prettyNameForFunc:func], 472*9712c20fSFrederick Mayle // |super| has guard for nil |fmt| and |args| 473*9712c20fSFrederick Mayle [super stringForFunc:func withFormat:fmt valist:args level:level]]; 474*9712c20fSFrederick Mayle} 475*9712c20fSFrederick Mayle 476*9712c20fSFrederick Mayle@end // GTMLogStandardFormatter 477*9712c20fSFrederick Mayle 478*9712c20fSFrederick Mayle 479*9712c20fSFrederick Mayle@implementation GTMLogLevelFilter 480*9712c20fSFrederick Mayle 481*9712c20fSFrederick Mayle// Check the environment and the user preferences for the GTMVerboseLogging key 482*9712c20fSFrederick Mayle// to see if verbose logging has been enabled. The environment variable will 483*9712c20fSFrederick Mayle// override the defaults setting, so check the environment first. 484*9712c20fSFrederick Mayle// COV_NF_START 485*9712c20fSFrederick Maylestatic BOOL IsVerboseLoggingEnabled(void) { 486*9712c20fSFrederick Mayle static NSString *const kVerboseLoggingKey = @"GTMVerboseLogging"; 487*9712c20fSFrederick Mayle NSString *value = [[[NSProcessInfo processInfo] environment] 488*9712c20fSFrederick Mayle objectForKey:kVerboseLoggingKey]; 489*9712c20fSFrederick Mayle if (value) { 490*9712c20fSFrederick Mayle // Emulate [NSString boolValue] for pre-10.5 491*9712c20fSFrederick Mayle value = [value stringByTrimmingCharactersInSet: 492*9712c20fSFrederick Mayle [NSCharacterSet whitespaceAndNewlineCharacterSet]]; 493*9712c20fSFrederick Mayle if ([[value uppercaseString] hasPrefix:@"Y"] || 494*9712c20fSFrederick Mayle [[value uppercaseString] hasPrefix:@"T"] || 495*9712c20fSFrederick Mayle [value intValue]) { 496*9712c20fSFrederick Mayle return YES; 497*9712c20fSFrederick Mayle } else { 498*9712c20fSFrederick Mayle return NO; 499*9712c20fSFrederick Mayle } 500*9712c20fSFrederick Mayle } 501*9712c20fSFrederick Mayle return [[NSUserDefaults standardUserDefaults] boolForKey:kVerboseLoggingKey]; 502*9712c20fSFrederick Mayle} 503*9712c20fSFrederick Mayle// COV_NF_END 504*9712c20fSFrederick Mayle 505*9712c20fSFrederick Mayle// In DEBUG builds, log everything. If we're not in a debug build we'll assume 506*9712c20fSFrederick Mayle// that we're in a Release build. 507*9712c20fSFrederick Mayle- (BOOL)filterAllowsMessage:(NSString *)msg level:(GTMLoggerLevel)level { 508*9712c20fSFrederick Mayle#if defined(DEBUG) && DEBUG 509*9712c20fSFrederick Mayle return YES; 510*9712c20fSFrederick Mayle#endif 511*9712c20fSFrederick Mayle 512*9712c20fSFrederick Mayle BOOL allow = YES; 513*9712c20fSFrederick Mayle 514*9712c20fSFrederick Mayle switch (level) { 515*9712c20fSFrederick Mayle case kGTMLoggerLevelDebug: 516*9712c20fSFrederick Mayle allow = NO; 517*9712c20fSFrederick Mayle break; 518*9712c20fSFrederick Mayle case kGTMLoggerLevelInfo: 519*9712c20fSFrederick Mayle allow = IsVerboseLoggingEnabled(); 520*9712c20fSFrederick Mayle break; 521*9712c20fSFrederick Mayle case kGTMLoggerLevelError: 522*9712c20fSFrederick Mayle allow = YES; 523*9712c20fSFrederick Mayle break; 524*9712c20fSFrederick Mayle case kGTMLoggerLevelAssert: 525*9712c20fSFrederick Mayle allow = YES; 526*9712c20fSFrederick Mayle break; 527*9712c20fSFrederick Mayle default: 528*9712c20fSFrederick Mayle allow = YES; 529*9712c20fSFrederick Mayle break; 530*9712c20fSFrederick Mayle } 531*9712c20fSFrederick Mayle 532*9712c20fSFrederick Mayle return allow; 533*9712c20fSFrederick Mayle} 534*9712c20fSFrederick Mayle 535*9712c20fSFrederick Mayle@end // GTMLogLevelFilter 536*9712c20fSFrederick Mayle 537*9712c20fSFrederick Mayle 538*9712c20fSFrederick Mayle@implementation GTMLogNoFilter 539*9712c20fSFrederick Mayle 540*9712c20fSFrederick Mayle- (BOOL)filterAllowsMessage:(NSString *)msg level:(GTMLoggerLevel)level { 541*9712c20fSFrederick Mayle return YES; // Allow everything through 542*9712c20fSFrederick Mayle} 543*9712c20fSFrederick Mayle 544*9712c20fSFrederick Mayle@end // GTMLogNoFilter 545*9712c20fSFrederick Mayle 546*9712c20fSFrederick Mayle 547*9712c20fSFrederick Mayle@implementation GTMLogAllowedLevelFilter 548*9712c20fSFrederick Mayle 549*9712c20fSFrederick Mayle// Private designated initializer 550*9712c20fSFrederick Mayle- (id)initWithAllowedLevels:(NSIndexSet *)levels { 551*9712c20fSFrederick Mayle self = [super init]; 552*9712c20fSFrederick Mayle if (self != nil) { 553*9712c20fSFrederick Mayle allowedLevels_ = [levels retain]; 554*9712c20fSFrederick Mayle // Cap min/max level 555*9712c20fSFrederick Mayle if (!allowedLevels_ || 556*9712c20fSFrederick Mayle // NSIndexSet is unsigned so only check the high bound, but need to 557*9712c20fSFrederick Mayle // check both first and last index because NSIndexSet appears to allow 558*9712c20fSFrederick Mayle // wraparound. 559*9712c20fSFrederick Mayle ([allowedLevels_ firstIndex] > kGTMLoggerLevelAssert) || 560*9712c20fSFrederick Mayle ([allowedLevels_ lastIndex] > kGTMLoggerLevelAssert)) { 561*9712c20fSFrederick Mayle [self release]; 562*9712c20fSFrederick Mayle return nil; 563*9712c20fSFrederick Mayle } 564*9712c20fSFrederick Mayle } 565*9712c20fSFrederick Mayle return self; 566*9712c20fSFrederick Mayle} 567*9712c20fSFrederick Mayle 568*9712c20fSFrederick Mayle- (id)init { 569*9712c20fSFrederick Mayle // Allow all levels in default init 570*9712c20fSFrederick Mayle return [self initWithAllowedLevels:[NSIndexSet indexSetWithIndexesInRange: 571*9712c20fSFrederick Mayle NSMakeRange(kGTMLoggerLevelUnknown, 572*9712c20fSFrederick Mayle (kGTMLoggerLevelAssert - kGTMLoggerLevelUnknown + 1))]]; 573*9712c20fSFrederick Mayle} 574*9712c20fSFrederick Mayle 575*9712c20fSFrederick Mayle- (void)dealloc { 576*9712c20fSFrederick Mayle [allowedLevels_ release]; 577*9712c20fSFrederick Mayle [super dealloc]; 578*9712c20fSFrederick Mayle} 579*9712c20fSFrederick Mayle 580*9712c20fSFrederick Mayle- (BOOL)filterAllowsMessage:(NSString *)msg level:(GTMLoggerLevel)level { 581*9712c20fSFrederick Mayle return [allowedLevels_ containsIndex:level]; 582*9712c20fSFrederick Mayle} 583*9712c20fSFrederick Mayle 584*9712c20fSFrederick Mayle@end // GTMLogAllowedLevelFilter 585*9712c20fSFrederick Mayle 586*9712c20fSFrederick Mayle 587*9712c20fSFrederick Mayle@implementation GTMLogMininumLevelFilter 588*9712c20fSFrederick Mayle 589*9712c20fSFrederick Mayle- (id)initWithMinimumLevel:(GTMLoggerLevel)level { 590*9712c20fSFrederick Mayle return [super initWithAllowedLevels:[NSIndexSet indexSetWithIndexesInRange: 591*9712c20fSFrederick Mayle NSMakeRange(level, 592*9712c20fSFrederick Mayle (kGTMLoggerLevelAssert - level + 1))]]; 593*9712c20fSFrederick Mayle} 594*9712c20fSFrederick Mayle 595*9712c20fSFrederick Mayle@end // GTMLogMininumLevelFilter 596*9712c20fSFrederick Mayle 597*9712c20fSFrederick Mayle 598*9712c20fSFrederick Mayle@implementation GTMLogMaximumLevelFilter 599*9712c20fSFrederick Mayle 600*9712c20fSFrederick Mayle- (id)initWithMaximumLevel:(GTMLoggerLevel)level { 601*9712c20fSFrederick Mayle return [super initWithAllowedLevels:[NSIndexSet indexSetWithIndexesInRange: 602*9712c20fSFrederick Mayle NSMakeRange(kGTMLoggerLevelUnknown, level + 1)]]; 603*9712c20fSFrederick Mayle} 604*9712c20fSFrederick Mayle 605*9712c20fSFrederick Mayle@end // GTMLogMaximumLevelFilter 606*9712c20fSFrederick Mayle 607*9712c20fSFrederick Mayle#if !defined(__clang__) && (__GNUC__*10+__GNUC_MINOR__ >= 42) 608*9712c20fSFrederick Mayle// See comment at top of file. 609*9712c20fSFrederick Mayle#pragma GCC diagnostic error "-Wmissing-format-attribute" 610*9712c20fSFrederick Mayle#endif // !__clang__ 611*9712c20fSFrederick Mayle 612