Line data Source code
1 0 : /*
2 :
3 : OOLogging.m
4 :
5 :
6 : Copyright (C) 2007-2013 Jens Ayton and contributors
7 :
8 : Permission is hereby granted, free of charge, to any person obtaining a copy
9 : of this software and associated documentation files (the "Software"), to deal
10 : in the Software without restriction, including without limitation the rights
11 : to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
12 : copies of the Software, and to permit persons to whom the Software is
13 : furnished to do so, subject to the following conditions:
14 :
15 : The above copyright notice and this permission notice shall be included in all
16 : copies or substantial portions of the Software.
17 :
18 : THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19 : IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20 : FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21 : AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22 : LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23 : OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
24 : SOFTWARE.
25 :
26 : */
27 :
28 :
29 0 : #define OOLOG_POISON_NSLOG 0
30 :
31 : #import "OOLoggingExtended.h"
32 : #import "OOPListParsing.h"
33 : #import "OOFunctionAttributes.h"
34 : #import "ResourceManager.h"
35 : #import "OOCollectionExtractors.h"
36 : #import "NSThreadOOExtensions.h"
37 : #import "OOLogHeader.h"
38 : #import "OOLogOutputHandler.h"
39 :
40 : #undef NSLog // We need to be able to call the real NSLog.
41 :
42 :
43 0 : #define PER_THREAD_INDENTATION 1
44 :
45 :
46 : #if PER_THREAD_INDENTATION
47 : #if OOLITE_USE_TLS // Define to use __thread keyword where supported
48 : #define USE_INDENT_GLOBALS 1
49 : #define THREAD_LOCAL __thread
50 : #else
51 0 : #define USE_INDENT_GLOBALS 0
52 0 : static NSString * const kIndentLevelKey = @"org.aegidian.oolite.oolog.indentLevel";
53 0 : static NSString * const kIndentStackKey = @"org.aegidian.oolite.oolog.indentStack";
54 : #endif
55 : #else
56 : #define USE_INDENT_GLOBALS 1
57 : #define THREAD_LOCAL
58 : #endif
59 :
60 :
61 : // Control flags for OOLogInternal() - like message classes, but less cool.
62 0 : #define OOLOG_NOT_INITED 1
63 0 : #define OOLOG_SETTING_SET 0
64 0 : #define OOLOG_SETTING_RETRIEVE 0
65 0 : #define OOLOG_UNDEFINED_METACLASS 1
66 0 : #define OOLOG_BAD_SETTING 1
67 0 : #define OOLOG_BAD_DEFAULT_SETTING 1
68 0 : #define OOLOG_BAD_POP_INDENT 1
69 0 : #define OOLOG_EXCEPTION_IN_LOG 1
70 :
71 :
72 : // Used to track OOLogPushIndent()/OOLogPopIndent() state.
73 0 : typedef struct OOLogIndentStackElement OOLogIndentStackElement;
74 0 : struct OOLogIndentStackElement
75 : {
76 0 : OOLogIndentStackElement *link;
77 0 : unsigned indent;
78 : };
79 :
80 :
81 : // We could probably use less state variables.
82 0 : static BOOL sInited = NO;
83 0 : static NSLock *sLock = nil;
84 0 : static NSMutableDictionary *sExplicitSettings = nil;
85 0 : static NSMutableDictionary *sDerivedSettingsCache = nil;
86 : #if USE_INDENT_GLOBALS
87 : static THREAD_LOCAL unsigned sIndentLevel = 0;
88 : static THREAD_LOCAL OOLogIndentStackElement
89 : *sIndentStack = NULL;
90 : #endif
91 0 : static BOOL sShowFunction = NO;
92 0 : static BOOL sShowFileAndLine = NO;
93 0 : static BOOL sShowTime = NO; // This is defaulted to YES when loading settings, but after printing log header.
94 0 : static BOOL sShowClass = NO; // Ditto.
95 0 : static BOOL sDefaultDisplay = YES;
96 0 : static BOOL sOverrideInEffect = NO;
97 0 : static BOOL sOverrideValue = NO;
98 :
99 : // These specific values are used for true, false and inherit in the cache and explicitSettings dictionaries so we can use pointer comparison.
100 0 : static NSString * const kTrueToken = @"on";
101 0 : static NSString * const kFalseToken = @"off";
102 0 : static NSString * const kInheritToken = @"inherit";
103 :
104 :
105 : // To avoid recursion/self-dependencies, OOLog gets its own logging function.
106 0 : #define OOLogInternal(cond, format, ...) do { if ((cond)) { OOLogInternal_(OOLOG_FUNCTION_NAME, format, ## __VA_ARGS__); }} while (0)
107 : static void OOLogInternal_(const char *inFunction, NSString *inFormat, ...);
108 :
109 :
110 : // Functions used internally
111 : static void LoadExplicitSettings(void);
112 : static void LoadExplicitSettingsFromDictionary(NSDictionary *inDict);
113 : static id ResolveDisplaySetting(NSString *inMessageClass);
114 : static id ResolveMetaClassReference(NSString *inMetaClass, NSMutableSet *ioSeenMetaClasses);
115 :
116 : OOINLINE unsigned GetIndentLevel(void) PURE_FUNC;
117 : OOINLINE void SetIndentLevel(unsigned level);
118 :
119 :
120 : #ifndef OOLOG_NO_FILE_NAME
121 0 : static NSMapTable *sFileNamesCache = NULL;
122 : #endif
123 :
124 :
125 : // Given a boolean, return the appropriate value for the cache dictionary.
126 : OOINLINE id CacheValue(BOOL inValue) PURE_FUNC;
127 0 : OOINLINE id CacheValue(BOOL inValue)
128 : {
129 : return inValue ? kTrueToken : kFalseToken;
130 : }
131 :
132 :
133 : /* Inited()
134 : Test wether OOLoggingInit() has been called.
135 : */
136 0 : OOINLINE BOOL Inited(void)
137 : {
138 : if (EXPECT(sInited)) return YES;
139 : OOLogInternal(OOLOG_NOT_INITED, @"***** ERROR: OOLoggingInit() has not been called.");
140 : return NO;
141 : }
142 :
143 :
144 0 : BOOL OOLogWillDisplayMessagesInClass(NSString *inMessageClass)
145 : {
146 : id value = nil;
147 :
148 : if (!Inited()) return NO;
149 :
150 : if (sOverrideInEffect) return sOverrideValue;
151 :
152 : [sLock lock];
153 :
154 : // Look for cached value
155 : value = [sDerivedSettingsCache objectForKey:inMessageClass];
156 : if (EXPECT_NOT(value == nil))
157 : {
158 : @try
159 : {
160 : // No cached value.
161 : value = ResolveDisplaySetting(inMessageClass);
162 :
163 : if (value != nil)
164 : {
165 : if (EXPECT_NOT(sDerivedSettingsCache == nil)) sDerivedSettingsCache = [[NSMutableDictionary alloc] init];
166 : [sDerivedSettingsCache setObject:value forKey:inMessageClass];
167 : }
168 : }
169 : @catch (id exception)
170 : {
171 : [sLock unlock];
172 : @throw exception;
173 : }
174 : }
175 : [sLock unlock];
176 :
177 : OOLogInternal(OOLOG_SETTING_RETRIEVE, @"%@ is %s", inMessageClass, (value == kTrueToken) ? "on" : "off");
178 : return value == kTrueToken;
179 : }
180 :
181 :
182 0 : void OOLogSetDisplayMessagesInClass(NSString *inClass, BOOL inFlag)
183 : {
184 : id value = nil;
185 :
186 : if (!Inited()) return;
187 :
188 : [sLock lock];
189 : value = [sExplicitSettings objectForKey:inClass];
190 : if (value == nil || value != CacheValue(inFlag))
191 : {
192 : OOLogInternal(OOLOG_SETTING_SET, @"Setting %@ to %s", inClass, inFlag ? "ON" : "OFF");
193 :
194 : [sExplicitSettings setObject:CacheValue(inFlag) forKey:inClass];
195 :
196 : // Clear cache and let it be rebuilt as needed.
197 : DESTROY(sDerivedSettingsCache);
198 : }
199 : else
200 : {
201 : OOLogInternal(OOLOG_SETTING_SET, @"Keeping %@ %s", inClass, inFlag ? "ON" : "OFF");
202 : }
203 : [sLock unlock];
204 : }
205 :
206 :
207 0 : NSString *OOLogGetParentMessageClass(NSString *inClass)
208 : {
209 : NSRange range;
210 :
211 : if (inClass == nil) return nil;
212 :
213 : range = [inClass rangeOfString:@"." options:NSCaseInsensitiveSearch | NSLiteralSearch | NSBackwardsSearch]; // Only NSBackwardsSearch is important, others are optimizations
214 : if (range.location == NSNotFound) return nil;
215 :
216 : return [inClass substringToIndex:range.location];
217 : }
218 :
219 :
220 : #if !OOLOG_SHORT_CIRCUIT
221 :
222 : void OOLogIndentIf(NSString *inMessageClass)
223 : {
224 : if (OOLogWillDisplayMessagesInClass(inMessageClass)) OOLogIndent();
225 : }
226 :
227 :
228 : void OOLogOutdentIf(NSString *inMessageClass)
229 : {
230 : if (OOLogWillDisplayMessagesInClass(inMessageClass)) OOLogOutdent();
231 : }
232 :
233 : #endif
234 :
235 :
236 : #if USE_INDENT_GLOBALS
237 :
238 : #if OOLITE_USE_TLS
239 : #define INDENT_LOCK() do {} while (0)
240 : #define INDENT_UNLOCK() do {} while (0)
241 : #else
242 : #define INDENT_LOCK() [sLock lock]
243 : #define INDENT_UNLOCK() [sLock unlock]
244 : #endif
245 :
246 :
247 : OOINLINE unsigned GetIndentLevel(void)
248 : {
249 : return sIndentLevel;
250 : }
251 :
252 :
253 : OOINLINE void SetIndentLevel(unsigned value)
254 : {
255 : sIndentLevel = value;
256 : }
257 :
258 :
259 : void OOLogPushIndent(void)
260 : {
261 : OOLogIndentStackElement *elem = NULL;
262 :
263 : elem = malloc(sizeof *elem);
264 : if (elem != NULL)
265 : {
266 : INDENT_LOCK();
267 :
268 : elem->indent = sIndentLevel;
269 : elem->link = sIndentStack;
270 : sIndentStack = elem;
271 :
272 : INDENT_UNLOCK();
273 : }
274 : }
275 :
276 :
277 : void OOLogPopIndent(void)
278 : {
279 : INDENT_LOCK();
280 :
281 : OOLogIndentStackElement *elem = sIndentStack;
282 :
283 : if (elem != NULL)
284 : {
285 : sIndentStack = elem->link;
286 : sIndentLevel = elem->indent;
287 : free(elem);
288 : }
289 : else
290 : {
291 : OOLogInternal(OOLOG_BAD_POP_INDENT, @"OOLogPopIndent(): state stack underflow.");
292 : }
293 : INDENT_UNLOCK();
294 : }
295 :
296 : #else // !USE_INDENT_GLOBALS
297 :
298 0 : #define INDENT_LOCK() do {} while (0)
299 0 : #define INDENT_UNLOCK() do {} while (0)
300 :
301 :
302 0 : OOINLINE unsigned GetIndentLevel(void)
303 : {
304 : NSMutableDictionary *threadDict = [[NSThread currentThread] threadDictionary];
305 : return [[threadDict objectForKey:kIndentLevelKey] unsignedIntValue];
306 : }
307 :
308 :
309 0 : OOINLINE void SetIndentLevel(unsigned value)
310 : {
311 : NSMutableDictionary *threadDict = [[NSThread currentThread] threadDictionary];
312 : [threadDict setObject:[NSNumber numberWithUnsignedInt:value] forKey:kIndentLevelKey];
313 : }
314 :
315 :
316 0 : void OOLogPushIndent(void)
317 : {
318 : OOLogIndentStackElement *elem = NULL;
319 : NSMutableDictionary *threadDict = nil;
320 : NSValue *val = nil;
321 :
322 : elem = malloc(sizeof *elem);
323 : if (elem != NULL)
324 : {
325 : threadDict = [[NSThread currentThread] threadDictionary];
326 : val = [threadDict objectForKey:kIndentStackKey];
327 :
328 : elem->indent = [[threadDict objectForKey:kIndentLevelKey] intValue];
329 : elem->link = [val pointerValue];
330 :
331 : /*
332 : Clang static analyzer reports elem not released here. It is in fact
333 : released in OOLogPopIndent().
334 : */
335 : [threadDict setObject:[NSValue valueWithPointer:elem] forKey:kIndentStackKey];
336 : }
337 : }
338 :
339 :
340 0 : void OOLogPopIndent(void)
341 : {
342 : OOLogIndentStackElement *elem = NULL;
343 : NSMutableDictionary *threadDict = nil;
344 : NSValue *val = nil;
345 :
346 : threadDict = [[NSThread currentThread] threadDictionary];
347 : val = [threadDict objectForKey:kIndentStackKey];
348 :
349 : elem = [val pointerValue];
350 :
351 : if (elem != NULL)
352 : {
353 : [threadDict setObject:[NSNumber numberWithUnsignedInt:elem->indent] forKey:kIndentLevelKey];
354 : [threadDict setObject:[NSValue valueWithPointer:elem->link] forKey:kIndentStackKey];
355 : free(elem);
356 : }
357 : else
358 : {
359 : OOLogInternal(OOLOG_BAD_POP_INDENT, @"OOLogPopIndent(): state stack underflow.");
360 : }
361 : }
362 :
363 : #endif // USE_INDENT_GLOBALS
364 :
365 :
366 0 : void OOLogIndent(void)
367 : {
368 : INDENT_LOCK();
369 :
370 : SetIndentLevel(GetIndentLevel() + 1);
371 :
372 : INDENT_UNLOCK();
373 : }
374 :
375 :
376 0 : void OOLogOutdent(void)
377 : {
378 : INDENT_LOCK();
379 :
380 : unsigned indentLevel = GetIndentLevel();
381 : if (indentLevel != 0) SetIndentLevel(indentLevel - 1);
382 :
383 : INDENT_UNLOCK();
384 : }
385 :
386 :
387 0 : void OOLogWithPrefix(NSString *inMessageClass, const char *inFunction, const char *inFile, unsigned long inLine, NSString *inPrefix, NSString *inFormat, ...)
388 : {
389 : if (!OOLogWillDisplayMessagesInClass(inMessageClass)) return;
390 : va_list args;
391 : va_start(args, inFormat);
392 : OOLogWithFunctionFileAndLineAndArguments(inMessageClass, inFunction, inFile, inLine, [inPrefix stringByAppendingString:inFormat], args);
393 : va_end(args);
394 : }
395 :
396 :
397 0 : void OOLogWithFunctionFileAndLine(NSString *inMessageClass, const char *inFunction, const char *inFile, unsigned long inLine, NSString *inFormat, ...)
398 : {
399 : va_list args;
400 :
401 : va_start(args, inFormat);
402 : OOLogWithFunctionFileAndLineAndArguments(inMessageClass, inFunction, inFile, inLine, inFormat, args);
403 : va_end(args);
404 : }
405 :
406 :
407 0 : void OOLogWithFunctionFileAndLineAndArguments(NSString *inMessageClass, const char *inFunction, const char *inFile, unsigned long inLine, NSString *inFormat, va_list inArguments)
408 : {
409 : NSAutoreleasePool *pool = nil;
410 : NSString *formattedMessage = nil;
411 : unsigned indentLevel;
412 :
413 : if (inFormat == nil) return;
414 :
415 : #if !OOLOG_SHORT_CIRCUIT
416 : if (!OOLogWillDisplayMessagesInClass(inMessageClass)) return;
417 : #endif
418 :
419 : pool = [[NSAutoreleasePool alloc] init];
420 : @try
421 : {
422 : // Do argument substitution
423 : formattedMessage = [[[NSString alloc] initWithFormat:inFormat arguments:inArguments] autorelease];
424 :
425 : // Apply various prefix options
426 : #ifndef OOLOG_NO_FILE_NAME
427 : if (sShowFileAndLine && inFile != NULL)
428 : {
429 : if (sShowFunction)
430 : {
431 : formattedMessage = [NSString stringWithFormat:@"%s (%@:%lu): %@", inFunction, OOLogAbbreviatedFileName(inFile), inLine, formattedMessage];
432 : }
433 : else
434 : {
435 : formattedMessage = [NSString stringWithFormat:@"%@:%lu: %@", OOLogAbbreviatedFileName(inFile), inLine, formattedMessage];
436 : }
437 : }
438 : else
439 : #endif
440 : {
441 : if (sShowFunction)
442 : {
443 : formattedMessage = [NSString stringWithFormat:@"%s: %@", inFunction, formattedMessage];
444 : }
445 : }
446 :
447 : if (sShowClass)
448 : {
449 : if (sShowFunction || sShowFileAndLine)
450 : {
451 : formattedMessage = [NSString stringWithFormat:@"[%@] %@", inMessageClass, formattedMessage];
452 : }
453 : else
454 : {
455 : formattedMessage = [NSString stringWithFormat:@"[%@]: %@", inMessageClass, formattedMessage];
456 : }
457 : }
458 :
459 : if (sShowTime)
460 : {
461 : formattedMessage = [NSString stringWithFormat:@"%@ %@", [[NSDate date] descriptionWithCalendarFormat:@"%H:%M:%S.%F" timeZone:nil locale:nil], formattedMessage];
462 : }
463 :
464 : // Apply indentation
465 : indentLevel = GetIndentLevel();
466 : if (indentLevel != 0)
467 : {
468 0 : #define INDENT_FACTOR 2 /* Spaces per indent level */
469 0 : #define MAX_INDENT 64 /* Maximum number of indentation _spaces_ */
470 :
471 : unsigned indent;
472 : // String of 64 spaces (null-terminated)
473 : const char spaces[MAX_INDENT + 1] =
474 : " ";
475 : const char *indentString;
476 :
477 : indent = INDENT_FACTOR * indentLevel;
478 : if (MAX_INDENT < indent) indent = MAX_INDENT;
479 : indentString = &spaces[MAX_INDENT - indent];
480 :
481 : formattedMessage = [NSString stringWithFormat:@"%s%@", indentString, formattedMessage];
482 : }
483 :
484 : OOLogOutputHandlerPrint(formattedMessage);
485 : }
486 : @catch (NSException *exception)
487 : {
488 : OOLogInternal(OOLOG_EXCEPTION_IN_LOG, @"***** Exception thrown during logging: %@ : %@", [exception name], [exception reason]);
489 : }
490 :
491 : [pool release];
492 : }
493 :
494 :
495 0 : void OOLogGenericParameterErrorForFunction(const char *inFunction)
496 : {
497 : OOLog(kOOLogParameterError, @"***** %s: bad parameters. (This is an internal programming error, please report it.)", inFunction);
498 : }
499 :
500 :
501 0 : void OOLogGenericSubclassResponsibilityForFunction(const char *inFunction)
502 : {
503 : OOLog(kOOLogSubclassResponsibility, @"***** %s is a subclass responsibility. (This is an internal programming error, please report it.)", inFunction);
504 : }
505 :
506 :
507 0 : BOOL OOLogShowFunction(void)
508 : {
509 : return sShowFunction;
510 : }
511 :
512 :
513 0 : void OOLogSetShowFunction(BOOL flag)
514 : {
515 : flag = !!flag; // YES or NO, not 42.
516 :
517 : if (flag != sShowFunction)
518 : {
519 : sShowFunction = flag;
520 : [[NSUserDefaults standardUserDefaults] setBool:flag forKey:@"logging-show-function"];
521 : }
522 : }
523 :
524 :
525 0 : BOOL OOLogShowFileAndLine(void)
526 : {
527 : return sShowFileAndLine;
528 : }
529 :
530 :
531 0 : void OOLogSetShowFileAndLine(BOOL flag)
532 : {
533 : flag = !!flag; // YES or NO, not 42.
534 :
535 : if (flag != sShowFileAndLine)
536 : {
537 : sShowFileAndLine = flag;
538 : [[NSUserDefaults standardUserDefaults] setBool:flag forKey:@"logging-show-file-and-line"];
539 : }
540 : }
541 :
542 :
543 0 : BOOL OOLogShowTime(void)
544 : {
545 : return sShowTime;
546 : }
547 :
548 :
549 0 : void OOLogSetShowTime(BOOL flag)
550 : {
551 : flag = !!flag; // YES or NO, not 42.
552 :
553 : if (flag != sShowTime)
554 : {
555 : sShowTime = flag;
556 : [[NSUserDefaults standardUserDefaults] setBool:flag forKey:@"logging-show-time"];
557 : }
558 : }
559 :
560 :
561 0 : BOOL OOLogShowMessageClass(void)
562 : {
563 : return sShowClass;
564 : }
565 :
566 :
567 0 : void OOLogSetShowMessageClass(BOOL flag)
568 : {
569 : flag = !!flag; // YES or NO, not 42.
570 :
571 : if (flag != sShowClass)
572 : {
573 : sShowClass = flag;
574 : [[NSUserDefaults standardUserDefaults] setBool:flag forKey:@"logging-show-class"];
575 : }
576 : }
577 :
578 :
579 0 : void OOLogSetShowMessageClassTemporary(BOOL flag)
580 : {
581 : sShowClass = !!flag; // YES or NO, not 42.
582 : }
583 :
584 :
585 0 : void OOLoggingInit(void)
586 : {
587 : NSAutoreleasePool *pool = nil;
588 :
589 : if (sInited) return;
590 :
591 : pool = [[NSAutoreleasePool alloc] init];
592 :
593 : sLock = [[NSLock alloc] init];
594 : [sLock setName:@"OOLogging lock"];
595 : if (sLock == nil) exit(EXIT_FAILURE);
596 :
597 : #ifndef OOLOG_NO_FILE_NAME
598 : sFileNamesCache = NSCreateMapTable(NSNonOwnedPointerMapKeyCallBacks, NSObjectMapValueCallBacks, 100);
599 : #endif
600 :
601 : sInited = YES; // Must be before OOLogOutputHandlerInit().
602 : OOLogOutputHandlerInit();
603 :
604 : OOPrintLogHeader();
605 :
606 : LoadExplicitSettings();
607 :
608 : [pool release];
609 : }
610 :
611 :
612 0 : void OOLoggingTerminate(void)
613 : {
614 : if (!sInited) return;
615 :
616 : OOLogOutputHandlerClose();
617 :
618 : /* We do not set sInited to NO. Instead, the output handler is required
619 : to be able to handle working even after being closed. Under OS X, this
620 : is done by writing to stderr in this case; on other platforms, NSLog()
621 : is used and OOLogOutputHandlerClose() is a no-op.
622 : */
623 : }
624 :
625 :
626 0 : void OOLoggingReloadSettings(void)
627 : {
628 : LoadExplicitSettings();
629 : }
630 :
631 :
632 0 : void OOLogInsertMarker(void)
633 : {
634 : static unsigned lastMarkerID = 0;
635 : unsigned thisMarkerID;
636 : NSString *marker = nil;
637 :
638 : [sLock lock];
639 : thisMarkerID = ++lastMarkerID;
640 : [sLock unlock];
641 :
642 : marker = [NSString stringWithFormat:@"\n\n========== [Marker %u] ==========", thisMarkerID];
643 : OOLogOutputHandlerPrint(marker);
644 : }
645 :
646 0 : NSString * const kOOLogSubclassResponsibility = @"general.error.subclassResponsibility";
647 0 : NSString * const kOOLogParameterError = @"general.error.parameterError";
648 0 : NSString * const kOOLogDeprecatedMethod = @"general.error.deprecatedMethod";
649 0 : NSString * const kOOLogAllocationFailure = @"general.error.allocationFailure";
650 0 : NSString * const kOOLogInconsistentState = @"general.error.inconsistentState";
651 0 : NSString * const kOOLogException = @"exception";
652 0 : NSString * const kOOLogFileNotFound = @"files.notFound";
653 0 : NSString * const kOOLogFileNotLoaded = @"files.notLoaded";
654 0 : NSString * const kOOLogOpenGLError = @"rendering.opengl.error";
655 0 : NSString * const kOOLogUnconvertedNSLog = @"unclassified";
656 :
657 :
658 : /* OOLogInternal_()
659 : Implementation of OOLogInternal(), private logging function used by
660 : OOLogging so it doesn’t depend on itself (and risk recursiveness).
661 : */
662 0 : static void OOLogInternal_(const char *inFunction, NSString *inFormat, ...)
663 : {
664 : va_list args;
665 : NSString *formattedMessage = nil;
666 : NSAutoreleasePool *pool = nil;
667 :
668 : pool = [[NSAutoreleasePool alloc] init];
669 :
670 : @try
671 : {
672 : va_start(args, inFormat);
673 : formattedMessage = [[[NSString alloc] initWithFormat:inFormat arguments:args] autorelease];
674 : va_end(args);
675 :
676 : formattedMessage = [NSString stringWithFormat:@"OOLogging internal - %s: %@", inFunction, formattedMessage];
677 :
678 : OOLogOutputHandlerPrint(formattedMessage);
679 : }
680 : @catch (NSException *exception)
681 : {
682 : fprintf(stderr, "***** Exception in OOLogInternal_(): %s : %s", [[exception name] UTF8String], [[exception reason] UTF8String]);
683 : }
684 :
685 : [pool release];
686 : }
687 :
688 :
689 : /* LoadExplicitSettings()
690 : Read settings from logcontrol.plists, merge in settings from preferences.
691 :
692 : Log settings are loaded from the following locations, from lowest to
693 : highest priority:
694 : * logcontrol.plist inside OXPs, but only in hierarchies not defined
695 : by the built-in plist.
696 : * Built-in logcontrol.plist.
697 : * Loose logcontrol.plist files in AddOns folders.
698 : * Preferences (settable through the debug console).
699 :
700 : Because the settings are loaded very early (OOLoggingInit() time), before
701 : the state of strict mode has been set, OXP settings are always loaded.
702 : Since these generally shouldn't have any effect in strict mode, that's
703 : fine.
704 : */
705 0 : static void LoadExplicitSettings(void)
706 : {
707 : // Load display settings.
708 : NSUserDefaults *prefs = [NSUserDefaults standardUserDefaults];
709 : sShowFunction = [prefs oo_boolForKey:@"logging-show-function" defaultValue:NO];
710 : sShowFileAndLine = [prefs oo_boolForKey:@"logging-show-file-and-line" defaultValue:NO];
711 : sShowTime = [prefs oo_boolForKey:@"logging-show-time" defaultValue:YES];
712 : sShowClass = [prefs oo_boolForKey:@"logging-show-class" defaultValue:YES];
713 :
714 : NSDictionary *oldSettings = sExplicitSettings;
715 :
716 : /*
717 : HACK: we look up search paths while loading settings, which touches
718 : the cache, which logs stuff. To avoid spam like dataCache.retrieve.success,
719 : we first load the built-in logcontrol.plist only and use it while
720 : loading the full settings.
721 : */
722 : NSString *path = [[[ResourceManager builtInPath] stringByAppendingPathComponent:@"Config"]
723 : stringByAppendingPathComponent:@"logcontrol.plist"];
724 : sExplicitSettings = [NSMutableDictionary dictionary];
725 : LoadExplicitSettingsFromDictionary(OODictionaryFromFile(path));
726 :
727 : // Release previous sExplicitSettings.
728 : [oldSettings release];
729 :
730 : // Load new settings.
731 : NSDictionary *settings = [ResourceManager logControlDictionary];
732 :
733 : // Replace the old dictionary.
734 : sExplicitSettings = [[NSMutableDictionary alloc] init];
735 :
736 : // Populate.
737 : LoadExplicitSettingsFromDictionary(settings);
738 :
739 : // Get _default and _override values.
740 : id value = [sExplicitSettings objectForKey:@"_default"];
741 : if (value != nil && [value respondsToSelector:@selector(boolValue)])
742 : {
743 : if (value == kTrueToken) sDefaultDisplay = YES;
744 : else if (value == kFalseToken) sDefaultDisplay = NO;
745 : else OOLogInternal(OOLOG_BAD_DEFAULT_SETTING, @"_default may not be set to a metaclass, ignoring.");
746 :
747 : [sExplicitSettings removeObjectForKey:@"_default"];
748 : }
749 : value = [sExplicitSettings objectForKey:@"_override"];
750 : if (value != nil && [value respondsToSelector:@selector(boolValue)])
751 : {
752 : if (value == kTrueToken)
753 : {
754 : sOverrideInEffect = YES;
755 : sOverrideValue = YES;
756 : }
757 : else if (value == kFalseToken)
758 : {
759 : sOverrideInEffect = YES;
760 : sOverrideValue = NO;
761 : }
762 : else OOLogInternal(OOLOG_BAD_DEFAULT_SETTING, @"_override may not be set to a metaclass, ignoring.");
763 :
764 : [sExplicitSettings removeObjectForKey:@"_override"];
765 : }
766 :
767 : // Invalidate cache.
768 : DESTROY(sDerivedSettingsCache);
769 :
770 : OOLogInternal(OOLOG_SETTING_SET, @"Settings: %@", sExplicitSettings);
771 : }
772 :
773 :
774 : /* LoadExplicitSettingsFromDictionary()
775 : Helper for LoadExplicitSettings().
776 : */
777 0 : static void LoadExplicitSettingsFromDictionary(NSDictionary *inDict)
778 : {
779 : id key = nil;
780 : foreachkey (key, inDict)
781 : {
782 : id value = [inDict objectForKey:key];
783 :
784 : /* Supported values:
785 : "yes", "true" or "on" -> kTrueToken
786 : "no", "false" or "off" -> kFalseToken
787 : "inherit" or "inherited" -> nil
788 : NSNumber -> kTrueToken or kFalseToken
789 : "$metaclass" -> "$metaclass"
790 : */
791 : if ([value isKindOfClass:[NSString class]])
792 : {
793 : if (NSOrderedSame == [value caseInsensitiveCompare:@"yes"] ||
794 : NSOrderedSame == [value caseInsensitiveCompare:@"true"] ||
795 : NSOrderedSame == [value caseInsensitiveCompare:@"on"])
796 : {
797 : value = kTrueToken;
798 : }
799 : else if (NSOrderedSame == [value caseInsensitiveCompare:@"no"] ||
800 : NSOrderedSame == [value caseInsensitiveCompare:@"false"] ||
801 : NSOrderedSame == [value caseInsensitiveCompare:@"off"])
802 : {
803 : value = kFalseToken;
804 : }
805 : else if (NSOrderedSame == [value caseInsensitiveCompare:@"inherit"] ||
806 : NSOrderedSame == [value caseInsensitiveCompare:@"inherited"])
807 : {
808 : value = nil;
809 : [sExplicitSettings removeObjectForKey:key];
810 : }
811 : else if (![value hasPrefix:@"$"])
812 : {
813 : OOLogInternal(OOLOG_BAD_SETTING, @"Bad setting value \"%@\" (expected yes, no, inherit or $metaclass).", value);
814 : value = nil;
815 : }
816 : }
817 : else if ([value respondsToSelector:@selector(boolValue)])
818 : {
819 : value = CacheValue([value boolValue]);
820 : }
821 : else
822 : {
823 : OOLogInternal(OOLOG_BAD_SETTING, @"Bad setting value \"%@\" (expected yes, no, inherit or $metaclass).", value);
824 : value = nil;
825 : }
826 :
827 : if (value != nil)
828 : {
829 : [sExplicitSettings setObject:value forKey:key];
830 : }
831 : }
832 : }
833 :
834 :
835 : /* OOLogAbbreviatedFileName()
836 : Map full file paths provided by __FILE__ to more mananagable file names,
837 : with caching.
838 : */
839 0 : NSString *OOLogAbbreviatedFileName(const char *inName)
840 : {
841 : NSString *name = nil;
842 :
843 : if (EXPECT_NOT(inName == NULL)) return @"unspecified file";
844 :
845 : [sLock lock];
846 : name = NSMapGet(sFileNamesCache, inName);
847 : if (EXPECT_NOT(name == nil))
848 : {
849 : name = [[NSString stringWithUTF8String:inName] lastPathComponent];
850 : NSMapInsertKnownAbsent(sFileNamesCache, inName, name);
851 : }
852 : [sLock unlock];
853 :
854 : return name;
855 : }
856 :
857 :
858 : /* Look up setting for a message class in explicit settings, resolving
859 : inheritance and metaclasses.
860 : */
861 0 : static id ResolveDisplaySetting(NSString *inMessageClass)
862 : {
863 : id value = nil;
864 : NSMutableSet *seenMetaClasses = nil;
865 :
866 : if (inMessageClass == nil) return CacheValue(sDefaultDisplay);
867 :
868 : value = [sExplicitSettings objectForKey:inMessageClass];
869 :
870 : // Simple case: explicit setting for this value
871 : if (value == kTrueToken || value == kFalseToken) return value;
872 :
873 : // Simplish case: use inherited value
874 : if (value == nil || value == kInheritToken) return ResolveDisplaySetting(OOLogGetParentMessageClass(inMessageClass));
875 :
876 : // Less simple case: should be a metaclass.
877 : seenMetaClasses = [NSMutableSet set];
878 : return ResolveMetaClassReference(value, seenMetaClasses);
879 : }
880 :
881 :
882 : /* Resolve a metaclass reference, recursively if necessary. The
883 : ioSeenMetaClasses dictionary is used to avoid loops.
884 : */
885 0 : static id ResolveMetaClassReference(NSString *inMetaClass, NSMutableSet *ioSeenMetaClasses)
886 : {
887 : id value = nil;
888 :
889 : // All values should have been checked at load time, but what the hey.
890 : if (![inMetaClass isKindOfClass:[NSString class]] || ![inMetaClass hasPrefix:@"$"])
891 : {
892 : OOLogInternal(OOLOG_BAD_SETTING, @"Bad setting value \"%@\" (expected yes, no, inherit or $metaclass). Falling back to _default.", inMetaClass);
893 : return CacheValue(sDefaultDisplay);
894 : }
895 :
896 : [ioSeenMetaClasses addObject:inMetaClass];
897 :
898 : value = [sExplicitSettings objectForKey:inMetaClass];
899 :
900 : if (value == kTrueToken || value == kFalseToken) return value;
901 : if (value == nil)
902 : {
903 : OOLogInternal(OOLOG_UNDEFINED_METACLASS, @"Reference to undefined metaclass %@, falling back to _default.", inMetaClass);
904 : return CacheValue(sDefaultDisplay);
905 : }
906 :
907 : // If we get here, it should be a metaclass reference.
908 : return ResolveMetaClassReference(value, ioSeenMetaClasses);
909 : }
|