aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGravatar gtm.daemon <gtm.daemon@7dc7ac4e-7543-0410-b95c-c1676fc8e2a3>2011-04-28 20:30:21 +0000
committerGravatar gtm.daemon <gtm.daemon@7dc7ac4e-7543-0410-b95c-c1676fc8e2a3>2011-04-28 20:30:21 +0000
commit784f85ae9a475667b7afb726d0653701c196ed48 (patch)
treecc258f8f11881c38875e3edc8bc8ef699ef24857
parente27ef9a94628853e1ab7dd9cf7ac883d96596dda (diff)
[Author: aharper]
- Fix ASL logging to reference the correct formatter (and then replace with a new formatter). - Format ASL messages to include information that would normally be present in the standard formatter. - Allow ASL writers to pass a facility string. - Pretty up the function names in all logging, better matching NSAssert() names (as one example). - Don't throw if a logging filehandle is closed (SIGPIPE). Random exceptions from logging is bad, m'kay? R=thomasvl,dmaclach APPROVED=thomasvl
-rw-r--r--Foundation/GTMLogger+ASL.h47
-rw-r--r--Foundation/GTMLogger+ASL.m66
-rw-r--r--Foundation/GTMLogger+ASLTest.m79
-rw-r--r--Foundation/GTMLogger.h54
-rw-r--r--Foundation/GTMLogger.m62
-rw-r--r--Foundation/GTMLoggerTest.m46
6 files changed, 253 insertions, 101 deletions
diff --git a/Foundation/GTMLogger+ASL.h b/Foundation/GTMLogger+ASL.h
index 689a0d9..87a513d 100644
--- a/Foundation/GTMLogger+ASL.h
+++ b/Foundation/GTMLogger+ASL.h
@@ -6,9 +6,9 @@
// Licensed under the Apache License, Version 2.0 (the "License"); you may not
// use this file except in compliance with the License. You may obtain a copy
// of the License at
-//
+//
// http://www.apache.org/licenses/LICENSE-2.0
-//
+//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
@@ -23,14 +23,14 @@
// GTMLogger (GTMLoggerASLAdditions)
//
-// Adds a convenience creation method that allows you to get a standard
+// Adds a convenience creation method that allows you to get a standard
// GTMLogger object that is configured to write to ASL (Apple System Log) using
// the GTMLogASLWriter (declared below).
//
@interface GTMLogger (GTMLoggerASLAdditions)
// Returns a new autoreleased GTMLogger instance that will log to ASL, using
-// the GTMLogStandardFormatter, and the GTMLogLevelFilter filter.
+// the GTMLogASLFormatter, and the GTMLogLevelFilter filter.
+ (id)standardLoggerWithASL;
@end
@@ -39,10 +39,10 @@
@class GTMLoggerASLClient;
// GTMLogASLWriter
-//
-// A GTMLogWriter implementation that will send log messages to ASL (Apple
-// System Log facility). To use with GTMLogger simply set the "writer" for a
-// GTMLogger to be an instance of this class. The following example sets the
+//
+// A GTMLogWriter implementation that will send log messages to ASL (Apple
+// System Log facility). To use with GTMLogger simply set the "writer" for a
+// GTMLogger to be an instance of this class. The following example sets the
// shared system logger to lot to ASL.
//
// [[GTMLogger sharedLogger] setWriter:[GTMLogASLWriter aslWriter]];
@@ -53,34 +53,51 @@
@interface GTMLogASLWriter : NSObject <GTMLogWriter> {
@private
__weak Class aslClientClass_;
+ NSString *facility_;
}
-// Returns an autoreleased GTMLogASLWriter instance that uses an instance of
-// GTMLoggerASLClient.
+// Returns an autoreleased GTMLogASLWriter instance that uses an instance of
+// GTMLoggerASLClient and the default ASL facility.
+ (id)aslWriter;
+// Returns an autoreleased GTMLogASLWriter instance that uses an instance of
+// GTMLoggerASLClient and the supplied facility. See asl_open(3) for a
+// discusssion of ASL facility strings.
++ (id)aslWriterWithFacility:(NSString *)facility;
+
// Designated initializer. Uses instances of the specified |clientClass| to talk
-// to the ASL system. This method is typically only useful for testing. Users
+// to the ASL system. All logs from this method will use |facility| as the ASL
+// log facility. This method is typically only useful for testing. Users
// should generally NOT use this method to get an instance. Instead, simply use
-// the +aslWriter method to obtain an instance.
-- (id)initWithClientClass:(Class)clientClass;
+// the +aslWriter or +aslWriterWithFacility: methods to obtain an instance.
+- (id)initWithClientClass:(Class)clientClass facility:(NSString *)facility;
@end // GTMLogASLWriter
+// An ASL-specific log formatter that replicates the same fields as
+// GTMLogStandardFormatter except for those (date, process name) that ASL
+// records independently.
+@interface GTMLogASLFormatter : GTMLogBasicFormatter
+@end // GTMLogASLFormatter
+
+
// Helper class used by GTMLogASLWriter to create an ASL client and write to the
// ASL log. This class is need to make management/cleanup of the aslclient work
// in a multithreaded environment. You'll need one of these GTMLoggerASLClient
-// per thread (this is automatically handled by GTMLogASLWriter).
+// per thread (this is automatically handled by GTMLogASLWriter).
//
// This class should rarely (if EVER) be used directly. It's designed to be used
-// internally by GTMLogASLWriter, and by some unit tests. It should not be
+// internally by GTMLogASLWriter, and by some unit tests. It should not be
// used externally.
@interface GTMLoggerASLClient : NSObject {
@private
aslclient client_;
}
+// Designated initializer, |facility| is supplied to asl_open().
+- (id)initWithFacility:(NSString *)facility;
+
// Sends the given string to ASL at the specified ASL log |level|.
- (void)log:(NSString *)msg level:(int)level;
diff --git a/Foundation/GTMLogger+ASL.m b/Foundation/GTMLogger+ASL.m
index 90ea7e5..435cec3 100644
--- a/Foundation/GTMLogger+ASL.m
+++ b/Foundation/GTMLogger+ASL.m
@@ -6,9 +6,9 @@
// Licensed under the Apache License, Version 2.0 (the "License"); you may not
// use this file except in compliance with the License. You may obtain a copy
// of the License at
-//
+//
// http://www.apache.org/licenses/LICENSE-2.0
-//
+//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
@@ -25,7 +25,7 @@
+ (id)standardLoggerWithASL {
id me = [self standardLogger];
[me setWriter:[[[GTMLogASLWriter alloc] init] autorelease]];
- [me setFormatter:[[[GTMLogBasicFormatter alloc] init] autorelease]];
+ [me setFormatter:[[[GTMLogASLFormatter alloc] init] autorelease]];
return me;
}
@@ -35,37 +35,53 @@
@implementation GTMLogASLWriter
+ (id)aslWriter {
- return [[[self alloc] init] autorelease];
+ return [[[self alloc] initWithClientClass:nil facility:nil] autorelease];
+}
+
++ (id)aslWriterWithFacility:(NSString *)facility {
+ return [[[self alloc] initWithClientClass:nil facility:facility] autorelease];
}
- (id)init {
- return [self initWithClientClass:nil];
+ return [self initWithClientClass:nil facility:nil];
}
-- (id)initWithClientClass:(Class)clientClass {
+- (id)initWithClientClass:(Class)clientClass facility:(NSString *)facility {
if ((self = [super init])) {
aslClientClass_ = clientClass;
if (aslClientClass_ == nil) {
aslClientClass_ = [GTMLoggerASLClient class];
}
+ facility_ = [facility copy];
}
return self;
}
+- (void)dealloc {
+ [facility_ release];
+ [super dealloc];
+}
+
- (void)logMessage:(NSString *)msg level:(GTMLoggerLevel)level {
- static NSString *const kASLClientKey = @"GTMLoggerASLClientKey";
-
+ // Because |facility_| is an argument to asl_open() we must store a separate
+ // one for each facility in thread-local storage.
+ static NSString *const kASLClientKey = @"GTMLoggerASLClient";
+ NSString *key = kASLClientKey;
+ if (facility_) {
+ key = [NSString stringWithFormat:@"GTMLoggerASLClient-%@", facility_];
+ }
+
// Lookup the ASL client in the thread-local storage dictionary
NSMutableDictionary *tls = [[NSThread currentThread] threadDictionary];
- GTMLoggerASLClient *client = [tls objectForKey:kASLClientKey];
-
+ GTMLoggerASLClient *client = [tls objectForKey:key];
+
// If the ASL client wasn't found (e.g., the first call from this thread),
// then create it and store it in the thread-local storage dictionary
if (client == nil) {
- client = [[[aslClientClass_ alloc] init] autorelease];
- [tls setObject:client forKey:kASLClientKey];
+ client = [[[aslClientClass_ alloc] initWithFacility:facility_] autorelease];
+ [tls setObject:client forKey:key];
}
-
+
// Map the GTMLoggerLevel level to an ASL level.
int aslLevel = ASL_LEVEL_INFO;
switch (level) {
@@ -81,18 +97,36 @@
aslLevel = ASL_LEVEL_ALERT;
break;
}
-
+
[client log:msg level:aslLevel];
}
@end // GTMLogASLWriter
+@implementation GTMLogASLFormatter
+
+- (NSString *)stringForFunc:(NSString *)func
+ withFormat:(NSString *)fmt
+ valist:(va_list)args
+ level:(GTMLoggerLevel)level {
+ return [NSString stringWithFormat:@"%@ %@",
+ [self prettyNameForFunc:func],
+ [super stringForFunc:func withFormat:fmt valist:args level:level]];
+}
+
+@end // GTMLogASLFormatter
+
+
@implementation GTMLoggerASLClient
- (id)init {
+ return [self initWithFacility:nil];
+}
+
+- (id)initWithFacility:(NSString *)facility {
if ((self = [super init])) {
- client_ = asl_open(NULL, NULL, 0);
+ client_ = asl_open(NULL, [facility UTF8String], 0);
if (client_ == nil) {
// COV_NF_START - no real way to test this
[self release];
@@ -115,7 +149,7 @@
}
#endif
-// We don't test this one line because we don't want to pollute actual system
+// We don't test this one line because we don't want to pollute actual system
// logs with test messages.
// COV_NF_START
- (void)log:(NSString *)msg level:(int)level {
diff --git a/Foundation/GTMLogger+ASLTest.m b/Foundation/GTMLogger+ASLTest.m
index cd32484..2756bfc 100644
--- a/Foundation/GTMLogger+ASLTest.m
+++ b/Foundation/GTMLogger+ASLTest.m
@@ -6,9 +6,9 @@
// Licensed under the Apache License, Version 2.0 (the "License"); you may not
// use this file except in compliance with the License. You may obtain a copy
// of the License at
-//
+//
// http://www.apache.org/licenses/LICENSE-2.0
-//
+//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
@@ -19,15 +19,31 @@
#import "GTMLogger+ASL.h"
#import "GTMSenTestCase.h"
-@interface DummyASLClient : GTMLoggerASLClient
+@interface DummyASLClient : GTMLoggerASLClient {
+ @private
+ NSString *facility_;
+}
@end
static NSMutableArray *gDummyLog; // weak
@implementation DummyASLClient
+- (id)initWithFacility:(NSString *)facility {
+ if ((self = [super initWithFacility:facility])) {
+ facility_ = [facility copy];
+ }
+ return self;
+}
+
+- (void)dealloc {
+ [facility_ release];
+ [super dealloc];
+}
+
- (void)log:(NSString *)msg level:(int)level {
- NSString *line = [msg stringByAppendingFormat:@"@%d", level];
+ NSString *line = [NSString stringWithFormat:@"%@-%@-%d",
+ (facility_ ? facility_ : @""), msg, level];
[gDummyLog addObject:line];
}
@@ -42,7 +58,7 @@ static NSMutableArray *gDummyLog; // weak
- (void)testCreation {
GTMLogger *aslLogger = [GTMLogger standardLoggerWithASL];
STAssertNotNil(aslLogger, nil);
-
+
GTMLogASLWriter *writer = [GTMLogASLWriter aslWriter];
STAssertNotNil(writer, nil);
}
@@ -50,12 +66,13 @@ static NSMutableArray *gDummyLog; // weak
- (void)testLogWriter {
gDummyLog = [[[NSMutableArray alloc] init] autorelease];
GTMLogASLWriter *writer = [[[GTMLogASLWriter alloc]
- initWithClientClass:[DummyASLClient class]]
+ initWithClientClass:[DummyASLClient class]
+ facility:nil]
autorelease];
-
+
STAssertNotNil(writer, nil);
- STAssertTrue([gDummyLog count] == 0, nil);
+ STAssertEquals([gDummyLog count], (NSUInteger)0, nil);
// Log some messages
[writer logMessage:@"unknown" level:kGTMLoggerLevelUnknown];
@@ -63,21 +80,45 @@ static NSMutableArray *gDummyLog; // weak
[writer logMessage:@"info" level:kGTMLoggerLevelInfo];
[writer logMessage:@"error" level:kGTMLoggerLevelError];
[writer logMessage:@"assert" level:kGTMLoggerLevelAssert];
-
- // Inspect the logged message to make sure they were logged correctly. The
- // dummy writer will save the messages w/ @level concatenated. The "level"
+
+ // Inspect the logged message to make sure they were logged correctly. The
+ // dummy writer will save the messages w/ @level concatenated. The "level"
// will be the ASL level, not the GTMLogger level. GTMLogASLWriter will log
- // all
+ // all
NSArray *expected = [NSArray arrayWithObjects:
- @"unknown@5",
- @"debug@5",
- @"info@5",
- @"error@3",
- @"assert@1",
+ @"-unknown-5",
+ @"-debug-5",
+ @"-info-5",
+ @"-error-3",
+ @"-assert-1",
nil];
-
+
STAssertEqualObjects(gDummyLog, expected, nil);
-
+ [gDummyLog removeAllObjects];
+
+ // Same test with facility
+ writer = [[[GTMLogASLWriter alloc]
+ initWithClientClass:[DummyASLClient class]
+ facility:@"testfac"] autorelease];
+
+
+ STAssertNotNil(writer, nil);
+ STAssertEquals([gDummyLog count], (NSUInteger)0, nil);
+
+ [writer logMessage:@"unknown" level:kGTMLoggerLevelUnknown];
+ [writer logMessage:@"debug" level:kGTMLoggerLevelDebug];
+ [writer logMessage:@"info" level:kGTMLoggerLevelInfo];
+ [writer logMessage:@"error" level:kGTMLoggerLevelError];
+ [writer logMessage:@"assert" level:kGTMLoggerLevelAssert];
+ expected = [NSArray arrayWithObjects:
+ @"testfac-unknown-5",
+ @"testfac-debug-5",
+ @"testfac-info-5",
+ @"testfac-error-3",
+ @"testfac-assert-1",
+ nil];
+ STAssertEqualObjects(gDummyLog, expected, nil);
+
gDummyLog = nil;
}
diff --git a/Foundation/GTMLogger.h b/Foundation/GTMLogger.h
index 6223999..9fd2362 100644
--- a/Foundation/GTMLogger.h
+++ b/Foundation/GTMLogger.h
@@ -6,9 +6,9 @@
// Licensed under the Apache License, Version 2.0 (the "License"); you may not
// use this file except in compliance with the License. You may obtain a copy
// of the License at
-//
+//
// http://www.apache.org/licenses/LICENSE-2.0
-//
+//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
@@ -18,15 +18,15 @@
// Key Abstractions
// ----------------
-//
-// This file declares multiple classes and protocols that are used by the
+//
+// This file declares multiple classes and protocols that are used by the
// GTMLogger logging system. The 4 main abstractions used in this file are the
// following:
//
// * logger (GTMLogger) - The main logging class that users interact with. It
// has methods for logging at different levels and uses a log writer, a log
// formatter, and a log filter to get the job done.
-//
+//
// * log writer (GTMLogWriter) - Writes a given string to some log file, where
// a "log file" can be a physical file on disk, a POST over HTTP to some URL,
// or even some in-memory structure (e.g., a ring buffer).
@@ -44,7 +44,7 @@
// flexibility to dynamically enable debug logging in Release builds.
//
// This file also declares some classes to handle the common log writer, log
-// formatter, and log filter cases. Callers can also create their own writers,
+// formatter, and log filter cases. Callers can also create their own writers,
// formatters, and filters and they can even build them on top of the ones
// declared here. Keep in mind that your custom writer/formatter/filter may be
// called from multiple threads, so it must be thread-safe.
@@ -63,7 +63,7 @@
// sent to a GTMLogger to log a message, the message is formatted using the log
// formatter, then the log filter is consulted to see if the message should be
// logged, and if so, the message is sent to the log writer to be written out.
-//
+//
// GTMLogger is intended to be a flexible and thread-safe logging solution. Its
// flexibility comes from the fact that GTMLogger instances can be customized
// with user defined formatters, filters, and writers. And these writers,
@@ -71,7 +71,7 @@
// ways to suit the needs at hand. For example, multiple writers can be used at
// the same time, and a GTMLogger instance can even be used as another
// GTMLogger's writer. This allows for arbitrarily deep logging trees.
-//
+//
// A standard GTMLogger uses a writer that sends messages to standard out, a
// formatter that smacks a timestamp and a few other bits of interesting
// information on the message, and a filter that filters out debug messages from
@@ -79,13 +79,13 @@
// the following:
//
// 2007-12-30 10:29:24.177 myapp[4588/0xa07d0f60] [lvl=1] foo=<Foo: 0x123>
-//
+//
// The output contains the date and time of the log message, the name of the
// process followed by its process ID/thread ID, the log level at which the
// message was logged (in the previous example the level was 1:
// kGTMLoggerLevelDebug), and finally, the user-specified log message itself (in
// this case, the log message was @"foo=%@", foo).
-//
+//
// Multiple instances of GTMLogger can be created, each configured their own
// way. Though GTMLogger is not a singleton (in the GoF sense), it does provide
// access to a shared (i.e., globally accessible) GTMLogger instance. This makes
@@ -107,10 +107,10 @@
// with behavior that many developers are currently used to. Note that this
// means that GTMLoggerDebug(@"hi") will be compiled out of Release builds, but
// [[GTMLogger sharedLogger] logDebug:@"hi"] will NOT be compiled out.
-//
+//
// Standard loggers are created with the GTMLogLevelFilter log filter, which
// filters out certain log messages based on log level, and some other settings.
-//
+//
// In addition to the -logDebug:, -logInfo:, and -logError: methods defined on
// GTMLogger itself, there are also C macros that make usage of the shared
// GTMLogger instance very convenient. These macros are:
@@ -140,7 +140,7 @@
// GTMLogger class directly in order to configure the shared logger, which all
// of the code using the macros will be using. Again, this is just the typical
// situation.
-//
+//
// To be complete, there are cases where you may want to use GTMLogger directly,
// or even create separate GTMLogger instances for some reason. That's fine,
// too.
@@ -154,14 +154,14 @@
//
// GTMLoggerDebug(@"foo = %@", foo);
//
-// 2. The previous example is similar to the following. The major difference is
+// 2. The previous example is similar to the following. The major difference is
// that the previous call (example 1) will be compiled out of Release builds
// but this statement will not be compiled out.
//
// [[GTMLogger sharedLogger] logDebug:@"foo = %@", foo];
//
// 3. Send all logging output from the shared logger to a file. We do this by
-// creating an NSFileHandle for writing associated with a file, and setting
+// creating an NSFileHandle for writing associated with a file, and setting
// that file handle as the logger's writer.
//
// NSFileHandle *f = [NSFileHandle fileHandleForWritingAtPath:@"/tmp/f.log"
@@ -179,12 +179,12 @@
// 5. Create a logger that writes to stdout and does NOT do any formatting to
// the log message. This might be useful, for example, when writing a help
// screen for a command-line tool to standard output.
-//
+//
// GTMLogger *logger = [GTMLogger logger];
// [logger logInfo:@"%@ version 0.1 usage", progName];
//
-// 6. Send log output to stdout AND to a log file. The trick here is that
-// NSArrays function as composite log writers, which means when an array is
+// 6. Send log output to stdout AND to a log file. The trick here is that
+// NSArrays function as composite log writers, which means when an array is
// set as the log writer, it forwards all logging messages to all of its
// contained GTMLogWriters.
//
@@ -192,7 +192,7 @@
// NSArray *writers = [NSArray arrayWithObjects:
// [NSFileHandle fileHandleForWritingAtPath:@"/tmp/f.log" create:YES],
// [NSFileHandle fileHandleWithStandardOutput], nil];
-//
+//
// GTMLogger *logger = [GTMLogger standardLogger];
// [logger setWriter:writers];
// [logger logInfo:@"hi"]; // Output goes to stdout and /tmp/f.log
@@ -281,7 +281,7 @@
// Accessors
//
-// Accessor methods for the log writer. If the log writer is set to nil,
+// Accessor methods for the log writer. If the log writer is set to nil,
// [NSFileHandle fileHandleWithStandardOutput] is used.
- (id<GTMLogWriter>)writer;
- (void)setWriter:(id<GTMLogWriter>)writer;
@@ -300,7 +300,7 @@
@end // GTMLogger
-// Helper functions that are used by the convenience GTMLogger*() macros that
+// Helper functions that are used by the convenience GTMLogger*() macros that
// enable the logging of function names.
@interface GTMLogger (GTMLoggerMacroHelpers)
- (void)logFuncDebug:(const char *)func msg:(NSString *)fmt, ...
@@ -359,7 +359,7 @@ typedef enum {
// now becomes a valid log writer. Log messages are written to the file handle
// with a newline appended.
@interface NSFileHandle (GTMFileHandleLogWriter) <GTMLogWriter>
-// Opens the file at |path| in append mode, and creates the file with |mode|
+// Opens the file at |path| in append mode, and creates the file with |mode|
// if it didn't previously exist.
+ (id)fileHandleForLoggingAtPath:(NSString *)path mode:(mode_t)mode;
@end // NSFileHandle
@@ -373,7 +373,7 @@ typedef enum {
//
// This is useful in situations where you would like to send log output to
// multiple log writers at the same time. Simply create an NSArray of the log
-// writers you wish to use, then set the array as the "writer" for your
+// writers you wish to use, then set the array as the "writer" for your
// GTMLogger instance.
@interface NSArray (GTMArrayCompositeLogWriter) <GTMLogWriter>
@end // GTMArrayCompositeLogWriter
@@ -384,7 +384,7 @@ typedef enum {
//
// This is useful when you want to configure a logger to log to a specific
// writer with a specific formatter and/or filter. But you want to also compose
-// that with a different log writer that may have its own formatter and/or
+// that with a different log writer that may have its own formatter and/or
// filter.
@interface GTMLogger (GTMLoggerLogWriter) <GTMLogWriter>
@end // GTMLoggerLogWriter
@@ -405,10 +405,14 @@ typedef enum {
@end // GTMLogFormatter
-// A basic log formatter that formats a string the same way that NSLog (or
+// A basic log formatter that formats a string the same way that NSLog (or
// printf) would. It does not do anything fancy, nor does it add any data of its
// own.
@interface GTMLogBasicFormatter : NSObject <GTMLogFormatter>
+
+// Helper method for prettying C99 __func__ and GCC __PRETTY_FUNCTION__
+- (NSString *)prettyNameForFunc:(NSString *)func;
+
@end // GTMLogBasicFormatter
diff --git a/Foundation/GTMLogger.m b/Foundation/GTMLogger.m
index e40defc..ec51107 100644
--- a/Foundation/GTMLogger.m
+++ b/Foundation/GTMLogger.m
@@ -6,9 +6,9 @@
// Licensed under the Apache License, Version 2.0 (the "License"); you may not
// use this file except in compliance with the License. You may obtain a copy
// of the License at
-//
+//
// http://www.apache.org/licenses/LICENSE-2.0
-//
+//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
@@ -36,20 +36,20 @@
- (void)logInternalFunc:(const char *)func
format:(NSString *)fmt
- valist:(va_list)args
+ valist:(va_list)args
level:(GTMLoggerLevel)level NS_FORMAT_FUNCTION(2, 0);
@end
-// Reference to the shared GTMLogger instance. This is not a singleton, it's
+// Reference to the shared GTMLogger instance. This is not a singleton, it's
// just an easy reference to one shared instance.
static GTMLogger *gSharedLogger = nil;
@implementation GTMLogger
-// Returns a pointer to the shared logger instance. If none exists, a standard
+// Returns a pointer to the shared logger instance. If none exists, a standard
// logger is created and returned.
+ (id)sharedLogger {
@synchronized(self) {
@@ -245,16 +245,16 @@ static GTMLogger *gSharedLogger = nil;
- (void)logInternalFunc:(const char *)func
format:(NSString *)fmt
- valist:(va_list)args
+ valist:(va_list)args
level:(GTMLoggerLevel)level {
GTMLOGGER_ASSERT(formatter_ != nil);
GTMLOGGER_ASSERT(filter_ != nil);
GTMLOGGER_ASSERT(writer_ != nil);
-
+
NSString *fname = func ? [NSString stringWithUTF8String:func] : nil;
NSString *msg = [formatter_ stringForFunc:fname
withFormat:fmt
- valist:args
+ valist:args
level:level];
if (msg && [filter_ filterAllowsMessage:msg level:level])
[writer_ logMessage:msg level:level];
@@ -279,7 +279,13 @@ static GTMLogger *gSharedLogger = nil;
- (void)logMessage:(NSString *)msg level:(GTMLoggerLevel)level {
@synchronized(self) {
NSString *line = [NSString stringWithFormat:@"%@\n", msg];
- [self writeData:[line dataUsingEncoding:NSUTF8StringEncoding]];
+ // Closed pipes should not generate exceptions in our caller
+ @try {
+ [self writeData:[line dataUsingEncoding:NSUTF8StringEncoding]];
+ }
+ @catch (NSException *e) {
+ // Ignored
+ }
}
}
@@ -306,18 +312,18 @@ static GTMLogger *gSharedLogger = nil;
- (void)logMessage:(NSString *)msg level:(GTMLoggerLevel)level {
switch (level) {
case kGTMLoggerLevelDebug:
- [self logDebug:@"%@", msg];
+ [self logDebug:@"%@", msg];
break;
case kGTMLoggerLevelInfo:
[self logInfo:@"%@", msg];
break;
- case kGTMLoggerLevelError:
+ case kGTMLoggerLevelError:
[self logError:@"%@", msg];
break;
case kGTMLoggerLevelAssert:
[self logAssert:@"%@", msg];
break;
- default:
+ default:
// Ignore the message.
break;
}
@@ -328,12 +334,30 @@ static GTMLogger *gSharedLogger = nil;
@implementation GTMLogBasicFormatter
+- (NSString *)prettyNameForFunc:(NSString *)func {
+ NSString *name = [func stringByTrimmingCharactersInSet:
+ [NSCharacterSet whitespaceAndNewlineCharacterSet]];
+ NSString *function = @"(unknown)";
+ if ([name length]) {
+ if (// Objective C __func__ and __PRETTY_FUNCTION__
+ [name hasPrefix:@"-["] || [name hasPrefix:@"+["] ||
+ // C++ __PRETTY_FUNCTION__ and other preadorned formats
+ [name hasSuffix:@")"]) {
+ function = name;
+ } else {
+ // Assume C99 __func__
+ function = [NSString stringWithFormat:@"%@()", name];
+ }
+ }
+ return function;
+}
+
- (NSString *)stringForFunc:(NSString *)func
withFormat:(NSString *)fmt
- valist:(va_list)args
+ valist:(va_list)args
level:(GTMLoggerLevel)level {
// Performance note: We may want to do a quick check here to see if |fmt|
- // contains a '%', and if not, simply return 'fmt'.
+ // contains a '%', and if not, simply return 'fmt'.
return [[[NSString alloc] initWithFormat:fmt arguments:args] autorelease];
}
@@ -361,7 +385,7 @@ static GTMLogger *gSharedLogger = nil;
- (NSString *)stringForFunc:(NSString *)func
withFormat:(NSString *)fmt
- valist:(va_list)args
+ valist:(va_list)args
level:(GTMLoggerLevel)level {
GTMLOGGER_ASSERT(dateFormatter_ != nil);
NSString *tstamp = nil;
@@ -370,7 +394,7 @@ static GTMLogger *gSharedLogger = nil;
}
return [NSString stringWithFormat:@"%@ %@[%d/%p] [lvl=%d] %@ %@",
tstamp, pname_, pid_, pthread_self(),
- level, (func ? func : @"(no func)"),
+ level, [self prettyNameForFunc:func],
[super stringForFunc:func withFormat:fmt valist:args level:level]];
}
@@ -388,7 +412,7 @@ static BOOL IsVerboseLoggingEnabled(void) {
static char *env = NULL;
if (env == NULL)
env = getenv([kVerboseLoggingKey UTF8String]);
-
+
if (env && env[0]) {
return (strtol(env, NULL, 10) != 0);
}
@@ -403,9 +427,9 @@ static BOOL IsVerboseLoggingEnabled(void) {
#if DEBUG
return YES;
#endif
-
+
BOOL allow = YES;
-
+
switch (level) {
case kGTMLoggerLevelDebug:
allow = NO;
diff --git a/Foundation/GTMLoggerTest.m b/Foundation/GTMLoggerTest.m
index 940014f..0dc7689 100644
--- a/Foundation/GTMLoggerTest.m
+++ b/Foundation/GTMLoggerTest.m
@@ -348,6 +348,38 @@
return msg;
}
+- (void)testFunctionPrettifier {
+ GTMLogBasicFormatter *fmtr = [[[GTMLogBasicFormatter alloc] init]
+ autorelease];
+ STAssertNotNil(fmtr, nil);
+
+ // Nil, empty and whitespace
+ STAssertEqualObjects([fmtr prettyNameForFunc:nil], @"(unknown)", nil);
+ STAssertEqualObjects([fmtr prettyNameForFunc:@""], @"(unknown)", nil);
+ STAssertEqualObjects([fmtr prettyNameForFunc:@" \n\t"], @"(unknown)", nil);
+
+ // C99 __func__
+ STAssertEqualObjects([fmtr prettyNameForFunc:@"main"], @"main()", nil);
+ STAssertEqualObjects([fmtr prettyNameForFunc:@"main"], @"main()", nil);
+ STAssertEqualObjects([fmtr prettyNameForFunc:@" main "], @"main()", nil);
+
+ // GCC Obj-C __func__ and __PRETTY_FUNCTION__
+ STAssertEqualObjects([fmtr prettyNameForFunc:@"+[Foo bar]"], @"+[Foo bar]",
+ nil);
+ STAssertEqualObjects([fmtr prettyNameForFunc:@" +[Foo bar] "], @"+[Foo bar]",
+ nil);
+ STAssertEqualObjects([fmtr prettyNameForFunc:@"-[Foo baz]"], @"-[Foo baz]",
+ nil);
+ STAssertEqualObjects([fmtr prettyNameForFunc:@" -[Foo baz] "], @"-[Foo baz]",
+ nil);
+
+ // GCC C++ __PRETTY_FUNCTION__
+ STAssertEqualObjects([fmtr prettyNameForFunc:@"void a::sub(int)"],
+ @"void a::sub(int)", nil);
+ STAssertEqualObjects([fmtr prettyNameForFunc:@" void a::sub(int) "],
+ @"void a::sub(int)", nil);
+}
+
- (void)testBasicFormatter {
id<GTMLogFormatter> fmtr = [[[GTMLogBasicFormatter alloc] init] autorelease];
STAssertNotNil(fmtr, nil);
@@ -388,16 +420,16 @@
if ([GTMSystemVersion isSnowLeopardOrGreater]) {
// E.g. 2009-10-26 22:26:25.086 otest-i386[53200/0xa0438500] [lvl=1] (no func) test
kFormatBasePattern =
- @"[0-9]{4}-[0-9]{2}-[0-9]{2} [0-9]{2}:[0-9]{2}:[0-9]{2}\\.[0-9]{3} ((otest-i386)|(otest-x86_64)|(otest-ppc))\\[[0-9]+/0x[0-9a-f]+\\] \\[lvl=[0-3]\\] \\(no func\\) ";
+ @"[0-9]{4}-[0-9]{2}-[0-9]{2} [0-9]{2}:[0-9]{2}:[0-9]{2}\\.[0-9]{3} ((otest-i386)|(otest-x86_64)|(otest-ppc))\\[[0-9]+/0x[0-9a-f]+\\] \\[lvl=[0-3]\\] \\(unknown\\) ";
} else {
// E.g. 2008-01-04 09:16:26.906 otest[5567/0xa07d0f60] [lvl=1] (no func) test
kFormatBasePattern =
- @"[0-9]{4}-[0-9]{2}-[0-9]{2} [0-9]{2}:[0-9]{2}:[0-9]{2}\\.[0-9]{3} (otest)\\[[0-9]+/0x[0-9a-f]+\\] \\[lvl=[0-3]\\] \\(no func\\) ";
+ @"[0-9]{4}-[0-9]{2}-[0-9]{2} [0-9]{2}:[0-9]{2}:[0-9]{2}\\.[0-9]{3} (otest)\\[[0-9]+/0x[0-9a-f]+\\] \\[lvl=[0-3]\\] \\(unknown\\) ";
}
#else // GTM_MACOS_SDK
// E.g. 2008-01-04 09:16:26.906 otest[5567/0xa07d0f60] [lvl=1] (no func) test
kFormatBasePattern =
- @"[0-9]{4}-[0-9]{2}-[0-9]{2} [0-9]{2}:[0-9]{2}:[0-9]{2}\\.[0-9]{3} (GTMiPhoneTest)\\[[0-9]+/0x[0-9a-f]+\\] \\[lvl=[0-3]\\] \\(no func\\) ";
+ @"[0-9]{4}-[0-9]{2}-[0-9]{2} [0-9]{2}:[0-9]{2}:[0-9]{2}\\.[0-9]{3} (GTMiPhoneTest)\\[[0-9]+/0x[0-9a-f]+\\] \\[lvl=[0-3]\\] \\(unknown\\) ";
#endif // GTM_MACOS_SDK
NSString *msg = nil;
@@ -405,27 +437,27 @@
msg = [self stringFromFormatter:fmtr
level:kGTMLoggerLevelDebug
format:@"test"];
- STAssertTrue([msg gtm_matchesPattern:[kFormatBasePattern stringByAppendingString:@"test"]],
+ STAssertTrue([msg gtm_matchesPattern:[kFormatBasePattern stringByAppendingString:@"test"]],
@"msg: %@", msg);
msg = [self stringFromFormatter:fmtr
level:kGTMLoggerLevelError
format:@"test %d", 1];
- STAssertTrue([msg gtm_matchesPattern:[kFormatBasePattern stringByAppendingString:@"test 1"]],
+ STAssertTrue([msg gtm_matchesPattern:[kFormatBasePattern stringByAppendingString:@"test 1"]],
@"msg: %@", msg);
msg = [self stringFromFormatter:fmtr
level:kGTMLoggerLevelInfo
format:@"test %@", @"hi"];
- STAssertTrue([msg gtm_matchesPattern:[kFormatBasePattern stringByAppendingString:@"test hi"]],
+ STAssertTrue([msg gtm_matchesPattern:[kFormatBasePattern stringByAppendingString:@"test hi"]],
@"msg: %@", msg);
msg = [self stringFromFormatter:fmtr
level:kGTMLoggerLevelUnknown
format:@"test"];
- STAssertTrue([msg gtm_matchesPattern:[kFormatBasePattern stringByAppendingString:@"test"]],
+ STAssertTrue([msg gtm_matchesPattern:[kFormatBasePattern stringByAppendingString:@"test"]],
@"msg: %@", msg);
}