OLD | NEW |
| (Empty) |
1 // | |
2 // GTMLogger.m | |
3 // | |
4 // Copyright 2007-2008 Google Inc. | |
5 // | |
6 // Licensed under the Apache License, Version 2.0 (the "License"); you may not | |
7 // use this file except in compliance with the License. You may obtain a copy | |
8 // of the License at | |
9 // | |
10 // http://www.apache.org/licenses/LICENSE-2.0 | |
11 // | |
12 // Unless required by applicable law or agreed to in writing, software | |
13 // distributed under the License is distributed on an "AS IS" BASIS, WITHOUT | |
14 // WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the | |
15 // License for the specific language governing permissions and limitations unde
r | |
16 // the License. | |
17 // | |
18 | |
19 #import "GTMLogger.h" | |
20 #import "GTMGarbageCollection.h" | |
21 #import <fcntl.h> | |
22 #import <unistd.h> | |
23 #import <stdlib.h> | |
24 #import <pthread.h> | |
25 | |
26 | |
27 // Define a trivial assertion macro to avoid dependencies | |
28 #ifdef DEBUG | |
29 #define GTMLOGGER_ASSERT(expr) assert(expr) | |
30 #else | |
31 #define GTMLOGGER_ASSERT(expr) | |
32 #endif | |
33 | |
34 | |
35 @interface GTMLogger (PrivateMethods) | |
36 | |
37 - (void)logInternalFunc:(const char *)func | |
38 format:(NSString *)fmt | |
39 valist:(va_list)args | |
40 level:(GTMLoggerLevel)level; | |
41 | |
42 @end | |
43 | |
44 | |
45 // Reference to the shared GTMLogger instance. This is not a singleton, it's | |
46 // just an easy reference to one shared instance. | |
47 static GTMLogger *gSharedLogger = nil; | |
48 | |
49 | |
50 @implementation GTMLogger | |
51 | |
52 // Returns a pointer to the shared logger instance. If none exists, a standard | |
53 // logger is created and returned. | |
54 + (id)sharedLogger { | |
55 @synchronized(self) { | |
56 if (gSharedLogger == nil) { | |
57 gSharedLogger = [[self standardLogger] retain]; | |
58 } | |
59 GTMLOGGER_ASSERT(gSharedLogger != nil); | |
60 } | |
61 return [[gSharedLogger retain] autorelease]; | |
62 } | |
63 | |
64 + (void)setSharedLogger:(GTMLogger *)logger { | |
65 @synchronized(self) { | |
66 [gSharedLogger autorelease]; | |
67 gSharedLogger = [logger retain]; | |
68 } | |
69 } | |
70 | |
71 + (id)standardLogger { | |
72 id<GTMLogWriter> writer = [NSFileHandle fileHandleWithStandardOutput]; | |
73 id<GTMLogFormatter> fr = [[[GTMLogStandardFormatter alloc] init] autorelease]; | |
74 id<GTMLogFilter> filter = [[[GTMLogLevelFilter alloc] init] autorelease]; | |
75 return [self loggerWithWriter:writer formatter:fr filter:filter]; | |
76 } | |
77 | |
78 + (id)standardLoggerWithStderr { | |
79 id me = [self standardLogger]; | |
80 [me setWriter:[NSFileHandle fileHandleWithStandardError]]; | |
81 return me; | |
82 } | |
83 | |
84 + (id)standardLoggerWithPath:(NSString *)path { | |
85 NSFileHandle *fh = [NSFileHandle fileHandleForLoggingAtPath:path mode:0644]; | |
86 if (fh == nil) return nil; | |
87 id me = [self standardLogger]; | |
88 [me setWriter:fh]; | |
89 return me; | |
90 } | |
91 | |
92 + (id)loggerWithWriter:(id<GTMLogWriter>)writer | |
93 formatter:(id<GTMLogFormatter>)formatter | |
94 filter:(id<GTMLogFilter>)filter { | |
95 return [[[self alloc] initWithWriter:writer | |
96 formatter:formatter | |
97 filter:filter] autorelease]; | |
98 } | |
99 | |
100 + (id)logger { | |
101 return [[[self alloc] init] autorelease]; | |
102 } | |
103 | |
104 - (id)init { | |
105 return [self initWithWriter:nil formatter:nil filter:nil]; | |
106 } | |
107 | |
108 - (id)initWithWriter:(id<GTMLogWriter>)writer | |
109 formatter:(id<GTMLogFormatter>)formatter | |
110 filter:(id<GTMLogFilter>)filter { | |
111 if ((self = [super init])) { | |
112 [self setWriter:writer]; | |
113 [self setFormatter:formatter]; | |
114 [self setFilter:filter]; | |
115 GTMLOGGER_ASSERT(formatter_ != nil); | |
116 GTMLOGGER_ASSERT(filter_ != nil); | |
117 GTMLOGGER_ASSERT(writer_ != nil); | |
118 } | |
119 return self; | |
120 } | |
121 | |
122 - (void)dealloc { | |
123 GTMLOGGER_ASSERT(writer_ != nil); | |
124 GTMLOGGER_ASSERT(formatter_ != nil); | |
125 GTMLOGGER_ASSERT(filter_ != nil); | |
126 [writer_ release]; | |
127 [formatter_ release]; | |
128 [filter_ release]; | |
129 [super dealloc]; | |
130 } | |
131 | |
132 - (id<GTMLogWriter>)writer { | |
133 GTMLOGGER_ASSERT(writer_ != nil); | |
134 return [[writer_ retain] autorelease]; | |
135 } | |
136 | |
137 - (void)setWriter:(id<GTMLogWriter>)writer { | |
138 @synchronized(self) { | |
139 [writer_ autorelease]; | |
140 if (writer == nil) | |
141 writer_ = [[NSFileHandle fileHandleWithStandardOutput] retain]; | |
142 else | |
143 writer_ = [writer retain]; | |
144 } | |
145 GTMLOGGER_ASSERT(writer_ != nil); | |
146 } | |
147 | |
148 - (id<GTMLogFormatter>)formatter { | |
149 GTMLOGGER_ASSERT(formatter_ != nil); | |
150 return [[formatter_ retain] autorelease]; | |
151 } | |
152 | |
153 - (void)setFormatter:(id<GTMLogFormatter>)formatter { | |
154 @synchronized(self) { | |
155 [formatter_ autorelease]; | |
156 if (formatter == nil) | |
157 formatter_ = [[GTMLogBasicFormatter alloc] init]; | |
158 else | |
159 formatter_ = [formatter retain]; | |
160 } | |
161 GTMLOGGER_ASSERT(formatter_ != nil); | |
162 } | |
163 | |
164 - (id<GTMLogFilter>)filter { | |
165 GTMLOGGER_ASSERT(filter_ != nil); | |
166 return [[filter_ retain] autorelease]; | |
167 } | |
168 | |
169 - (void)setFilter:(id<GTMLogFilter>)filter { | |
170 @synchronized(self) { | |
171 [filter_ autorelease]; | |
172 if (filter == nil) | |
173 filter_ = [[GTMLogNoFilter alloc] init]; | |
174 else | |
175 filter_ = [filter retain]; | |
176 } | |
177 GTMLOGGER_ASSERT(filter_ != nil); | |
178 } | |
179 | |
180 - (void)logDebug:(NSString *)fmt, ... { | |
181 va_list args; | |
182 va_start(args, fmt); | |
183 [self logInternalFunc:NULL format:fmt valist:args level:kGTMLoggerLevelDebug]; | |
184 va_end(args); | |
185 } | |
186 | |
187 - (void)logInfo:(NSString *)fmt, ... { | |
188 va_list args; | |
189 va_start(args, fmt); | |
190 [self logInternalFunc:NULL format:fmt valist:args level:kGTMLoggerLevelInfo]; | |
191 va_end(args); | |
192 } | |
193 | |
194 - (void)logError:(NSString *)fmt, ... { | |
195 va_list args; | |
196 va_start(args, fmt); | |
197 [self logInternalFunc:NULL format:fmt valist:args level:kGTMLoggerLevelError]; | |
198 va_end(args); | |
199 } | |
200 | |
201 - (void)logAssert:(NSString *)fmt, ... { | |
202 va_list args; | |
203 va_start(args, fmt); | |
204 [self logInternalFunc:NULL format:fmt valist:args level:kGTMLoggerLevelAssert]
; | |
205 va_end(args); | |
206 } | |
207 | |
208 @end // GTMLogger | |
209 | |
210 | |
211 @implementation GTMLogger (GTMLoggerMacroHelpers) | |
212 | |
213 - (void)logFuncDebug:(const char *)func msg:(NSString *)fmt, ... { | |
214 va_list args; | |
215 va_start(args, fmt); | |
216 [self logInternalFunc:func format:fmt valist:args level:kGTMLoggerLevelDebug]; | |
217 va_end(args); | |
218 } | |
219 | |
220 - (void)logFuncInfo:(const char *)func msg:(NSString *)fmt, ... { | |
221 va_list args; | |
222 va_start(args, fmt); | |
223 [self logInternalFunc:func format:fmt valist:args level:kGTMLoggerLevelInfo]; | |
224 va_end(args); | |
225 } | |
226 | |
227 - (void)logFuncError:(const char *)func msg:(NSString *)fmt, ... { | |
228 va_list args; | |
229 va_start(args, fmt); | |
230 [self logInternalFunc:func format:fmt valist:args level:kGTMLoggerLevelError]; | |
231 va_end(args); | |
232 } | |
233 | |
234 - (void)logFuncAssert:(const char *)func msg:(NSString *)fmt, ... { | |
235 va_list args; | |
236 va_start(args, fmt); | |
237 [self logInternalFunc:func format:fmt valist:args level:kGTMLoggerLevelAssert]
; | |
238 va_end(args); | |
239 } | |
240 | |
241 @end // GTMLoggerMacroHelpers | |
242 | |
243 | |
244 @implementation GTMLogger (PrivateMethods) | |
245 | |
246 - (void)logInternalFunc:(const char *)func | |
247 format:(NSString *)fmt | |
248 valist:(va_list)args | |
249 level:(GTMLoggerLevel)level { | |
250 GTMLOGGER_ASSERT(formatter_ != nil); | |
251 GTMLOGGER_ASSERT(filter_ != nil); | |
252 GTMLOGGER_ASSERT(writer_ != nil); | |
253 | |
254 NSString *fname = func ? [NSString stringWithUTF8String:func] : nil; | |
255 NSString *msg = [formatter_ stringForFunc:fname | |
256 withFormat:fmt | |
257 valist:args | |
258 level:level]; | |
259 if (msg && [filter_ filterAllowsMessage:msg level:level]) | |
260 [writer_ logMessage:msg level:level]; | |
261 } | |
262 | |
263 @end // PrivateMethods | |
264 | |
265 | |
266 @implementation NSFileHandle (GTMFileHandleLogWriter) | |
267 | |
268 + (id)fileHandleForLoggingAtPath:(NSString *)path mode:(mode_t)mode { | |
269 int fd = -1; | |
270 if (path) { | |
271 int flags = O_WRONLY | O_APPEND | O_CREAT; | |
272 fd = open([path fileSystemRepresentation], flags, mode); | |
273 } | |
274 if (fd == -1) return nil; | |
275 return [[[self alloc] initWithFileDescriptor:fd | |
276 closeOnDealloc:YES] autorelease]; | |
277 } | |
278 | |
279 - (void)logMessage:(NSString *)msg level:(GTMLoggerLevel)level { | |
280 @synchronized(self) { | |
281 NSString *line = [NSString stringWithFormat:@"%@\n", msg]; | |
282 [self writeData:[line dataUsingEncoding:NSUTF8StringEncoding]]; | |
283 } | |
284 } | |
285 | |
286 @end // GTMFileHandleLogWriter | |
287 | |
288 | |
289 @implementation NSArray (GTMArrayCompositeLogWriter) | |
290 | |
291 - (void)logMessage:(NSString *)msg level:(GTMLoggerLevel)level { | |
292 @synchronized(self) { | |
293 id<GTMLogWriter> child = nil; | |
294 GTM_FOREACH_OBJECT(child, self) { | |
295 if ([child conformsToProtocol:@protocol(GTMLogWriter)]) | |
296 [child logMessage:msg level:level]; | |
297 } | |
298 } | |
299 } | |
300 | |
301 @end // GTMArrayCompositeLogWriter | |
302 | |
303 | |
304 @implementation GTMLogger (GTMLoggerLogWriter) | |
305 | |
306 - (void)logMessage:(NSString *)msg level:(GTMLoggerLevel)level { | |
307 switch (level) { | |
308 case kGTMLoggerLevelDebug: | |
309 [self logDebug:@"%@", msg]; | |
310 break; | |
311 case kGTMLoggerLevelInfo: | |
312 [self logInfo:@"%@", msg]; | |
313 break; | |
314 case kGTMLoggerLevelError: | |
315 [self logError:@"%@", msg]; | |
316 break; | |
317 case kGTMLoggerLevelAssert: | |
318 [self logAssert:@"%@", msg]; | |
319 break; | |
320 default: | |
321 // Ignore the message. | |
322 break; | |
323 } | |
324 } | |
325 | |
326 @end // GTMLoggerLogWriter | |
327 | |
328 | |
329 @implementation GTMLogBasicFormatter | |
330 | |
331 - (NSString *)stringForFunc:(NSString *)func | |
332 withFormat:(NSString *)fmt | |
333 valist:(va_list)args | |
334 level:(GTMLoggerLevel)level { | |
335 // Performance note: since we always have to create a new NSString from the | |
336 // returned CFStringRef, we may want to do a quick check here to see if |fmt| | |
337 // contains a '%', and if not, simply return 'fmt'. | |
338 CFStringRef cfmsg = NULL; | |
339 cfmsg = CFStringCreateWithFormatAndArguments(kCFAllocatorDefault, | |
340 NULL, // format options | |
341 (CFStringRef)fmt, | |
342 args); | |
343 return GTMCFAutorelease(cfmsg); | |
344 } | |
345 | |
346 @end // GTMLogBasicFormatter | |
347 | |
348 | |
349 @implementation GTMLogStandardFormatter | |
350 | |
351 - (id)init { | |
352 if ((self = [super init])) { | |
353 dateFormatter_ = [[NSDateFormatter alloc] init]; | |
354 [dateFormatter_ setFormatterBehavior:NSDateFormatterBehavior10_4]; | |
355 [dateFormatter_ setDateFormat:@"yyyy-MM-dd HH:mm:ss.SSS"]; | |
356 pname_ = [[[NSProcessInfo processInfo] processName] copy]; | |
357 pid_ = [[NSProcessInfo processInfo] processIdentifier]; | |
358 } | |
359 return self; | |
360 } | |
361 | |
362 - (void)dealloc { | |
363 [dateFormatter_ release]; | |
364 [pname_ release]; | |
365 [super dealloc]; | |
366 } | |
367 | |
368 - (NSString *)stringForFunc:(NSString *)func | |
369 withFormat:(NSString *)fmt | |
370 valist:(va_list)args | |
371 level:(GTMLoggerLevel)level { | |
372 GTMLOGGER_ASSERT(dateFormatter_ != nil); | |
373 NSString *tstamp = nil; | |
374 @synchronized (dateFormatter_) { | |
375 tstamp = [dateFormatter_ stringFromDate:[NSDate date]]; | |
376 } | |
377 return [NSString stringWithFormat:@"%@ %@[%d/%p] [lvl=%d] %@ %@", | |
378 tstamp, pname_, pid_, pthread_self(), | |
379 level, (func ? func : @"(no func)"), | |
380 [super stringForFunc:func withFormat:fmt valist:args level:level]]; | |
381 } | |
382 | |
383 @end // GTMLogStandardFormatter | |
384 | |
385 | |
386 @implementation GTMLogLevelFilter | |
387 | |
388 // Check the environment and the user preferences for the GTMVerboseLogging key | |
389 // to see if verbose logging has been enabled. The environment variable will | |
390 // override the defaults setting, so check the environment first. | |
391 // COV_NF_START | |
392 static BOOL IsVerboseLoggingEnabled(void) { | |
393 static NSString *const kVerboseLoggingKey = @"GTMVerboseLogging"; | |
394 static char *env = NULL; | |
395 if (env == NULL) | |
396 env = getenv([kVerboseLoggingKey UTF8String]); | |
397 | |
398 if (env && env[0]) { | |
399 return (strtol(env, NULL, 10) != 0); | |
400 } | |
401 | |
402 return [[NSUserDefaults standardUserDefaults] boolForKey:kVerboseLoggingKey]; | |
403 } | |
404 // COV_NF_END | |
405 | |
406 // In DEBUG builds, log everything. If we're not in a debug build we'll assume | |
407 // that we're in a Release build. | |
408 - (BOOL)filterAllowsMessage:(NSString *)msg level:(GTMLoggerLevel)level { | |
409 #if DEBUG | |
410 return YES; | |
411 #endif | |
412 | |
413 BOOL allow = YES; | |
414 | |
415 switch (level) { | |
416 case kGTMLoggerLevelDebug: | |
417 allow = NO; | |
418 break; | |
419 case kGTMLoggerLevelInfo: | |
420 allow = (IsVerboseLoggingEnabled() == YES); | |
421 break; | |
422 case kGTMLoggerLevelError: | |
423 allow = YES; | |
424 break; | |
425 case kGTMLoggerLevelAssert: | |
426 allow = YES; | |
427 break; | |
428 default: | |
429 allow = YES; | |
430 break; | |
431 } | |
432 | |
433 return allow; | |
434 } | |
435 | |
436 @end // GTMLogLevelFilter | |
437 | |
438 | |
439 @implementation GTMLogNoFilter | |
440 | |
441 - (BOOL)filterAllowsMessage:(NSString *)msg level:(GTMLoggerLevel)level { | |
442 return YES; // Allow everything through | |
443 } | |
444 | |
445 @end // GTMLogNoFilter | |
OLD | NEW |