| // |
| // GTMLogger.h |
| // |
| // Copyright 2007-2008 Google Inc. |
| // |
| // 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 |
| // License for the specific language governing permissions and limitations under |
| // the License. |
| // |
| |
| // Key Abstractions |
| // ---------------- |
| // |
| // 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). |
| // |
| // * log formatter (GTMLogFormatter) - Given a format string and arguments as |
| // a va_list, returns a single formatted NSString. A "formatted string" could |
| // be a string with the date prepended, a string with values in a CSV format, |
| // or even a string of XML. |
| // |
| // * log filter (GTMLogFilter) - Given a formatted log message as an NSString |
| // and the level at which the message is to be logged, this class will decide |
| // whether the given message should be logged or not. This is a flexible way |
| // to filter out messages logged at a certain level, messages that contain |
| // certain text, or filter nothing out at all. This gives the caller the |
| // 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, |
| // 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. |
| |
| #import <Foundation/Foundation.h> |
| #import "GTMDefines.h" |
| |
| // Predeclaration of used protocols that are declared later in this file. |
| @protocol GTMLogWriter, GTMLogFormatter, GTMLogFilter; |
| |
| // GTMLogger |
| // |
| // GTMLogger is the primary user-facing class for an object-oriented logging |
| // system. It is built on the concept of log formatters (GTMLogFormatter), log |
| // writers (GTMLogWriter), and log filters (GTMLogFilter). When a message is |
| // 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, |
| // filters, and formatters can be combined, stacked, and customized in arbitrary |
| // 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 |
| // release builds. Using the standard log settings, a log message will look like |
| // 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 |
| // it convenient for all code in a process to use the same GTMLogger instance. |
| // The shared GTMLogger instance can also be configured in an arbitrary, and |
| // these configuration changes will affect all code that logs through the shared |
| // instance. |
| |
| // |
| // Log Levels |
| // ---------- |
| // GTMLogger has 3 different log levels: Debug, Info, and Error. GTMLogger |
| // doesn't take any special action based on the log level; it simply forwards |
| // this information on to formatters, filters, and writers, each of which may |
| // optionally take action based on the level. Since log level filtering is |
| // performed at runtime, log messages are typically not filtered out at compile |
| // time. The exception to this rule is that calls to the GTMLoggerDebug() macro |
| // *ARE* filtered out of non-DEBUG builds. This is to be backwards compatible |
| // 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: |
| // |
| // GTMLoggerDebug(...) |
| // GTMLoggerInfo(...) |
| // GTMLoggerError(...) |
| // |
| // Again, a notable feature of these macros is that GTMLogDebug() calls *will be |
| // compiled out of non-DEBUG builds*. |
| // |
| // Standard Loggers |
| // ---------------- |
| // GTMLogger has the concept of "standard loggers". A standard logger is simply |
| // a logger that is pre-configured with some standard/common writer, formatter, |
| // and filter combination. Standard loggers are created using the creation |
| // methods beginning with "standard". The alternative to a standard logger is a |
| // regular logger, which will send messages to stdout, with no special |
| // formatting, and no filtering. |
| // |
| // How do I use GTMLogger? |
| // ---------------------- |
| // The typical way you will want to use GTMLogger is to simply use the |
| // GTMLogger*() macros for logging from code. That way we can easily make |
| // changes to the GTMLogger class and simply update the macros accordingly. Only |
| // your application startup code (perhaps, somewhere in main()) should use the |
| // 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. |
| // |
| // Examples |
| // -------- |
| // The following show some common GTMLogger use cases. |
| // |
| // 1. You want to log something as simply as possible. Also, this call will only |
| // appear in debug builds. In non-DEBUG builds it will be completely removed. |
| // |
| // GTMLoggerDebug(@"foo = %@", foo); |
| // |
| // 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 |
| // that file handle as the logger's writer. |
| // |
| // NSFileHandle *f = [NSFileHandle fileHandleForWritingAtPath:@"/tmp/f.log" |
| // create:YES]; |
| // [[GTMLogger sharedLogger] setWriter:f]; |
| // GTMLoggerError(@"hi"); // This will be sent to /tmp/f.log |
| // |
| // 4. Create a new GTMLogger that will log to a file. This example differs from |
| // the previous one because here we create a new GTMLogger that is different |
| // from the shared logger. |
| // |
| // GTMLogger *logger = [GTMLogger standardLoggerWithPath:@"/tmp/temp.log"]; |
| // [logger logInfo:@"hi temp log file"]; |
| // |
| // 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 |
| // set as the log writer, it forwards all logging messages to all of its |
| // contained GTMLogWriters. |
| // |
| // // Create array of GTMLogWriters |
| // 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 |
| // |
| // For futher details on log writers, formatters, and filters, see the |
| // documentation below. |
| // |
| // NOTE: GTMLogger is application level logging. By default it does nothing |
| // with _GTMDevLog/_GTMDevAssert (see GTMDefines.h). An application can choose |
| // to bridge _GTMDevLog/_GTMDevAssert to GTMLogger by providing macro |
| // definitions in its prefix header (see GTMDefines.h for how one would do |
| // that). |
| // |
| @interface GTMLogger : NSObject { |
| @private |
| id<GTMLogWriter> writer_; |
| id<GTMLogFormatter> formatter_; |
| id<GTMLogFilter> filter_; |
| } |
| |
| // |
| // Accessors for the shared logger instance |
| // |
| |
| // Returns a shared/global standard GTMLogger instance. Callers should typically |
| // use this method to get a GTMLogger instance, unless they explicitly want |
| // their own instance to configure for their own needs. This is the only method |
| // that returns a shared instance; all the rest return new GTMLogger instances. |
| + (id)sharedLogger; |
| |
| // Sets the shared logger instance to |logger|. Future calls to +sharedLogger |
| // will return |logger| instead. |
| + (void)setSharedLogger:(GTMLogger *)logger; |
| |
| // |
| // Creation methods |
| // |
| |
| // Returns a new autoreleased GTMLogger instance that will log to stdout, using |
| // the GTMLogStandardFormatter, and the GTMLogLevelFilter filter. |
| + (id)standardLogger; |
| |
| // Same as +standardLogger, but logs to stderr. |
| + (id)standardLoggerWithStderr; |
| |
| // Same as +standardLogger but levels >= kGTMLoggerLevelError are routed to |
| // stderr, everything else goes to stdout. |
| + (id)standardLoggerWithStdoutAndStderr; |
| |
| // Returns a new standard GTMLogger instance with a log writer that will |
| // write to the file at |path|, and will use the GTMLogStandardFormatter and |
| // GTMLogLevelFilter classes. If |path| does not exist, it will be created. |
| + (id)standardLoggerWithPath:(NSString *)path; |
| |
| // Returns an autoreleased GTMLogger instance that will use the specified |
| // |writer|, |formatter|, and |filter|. |
| + (id)loggerWithWriter:(id<GTMLogWriter>)writer |
| formatter:(id<GTMLogFormatter>)formatter |
| filter:(id<GTMLogFilter>)filter; |
| |
| // Returns an autoreleased GTMLogger instance that logs to stdout, with the |
| // basic formatter, and no filter. The returned logger differs from the logger |
| // returned by +standardLogger because this one does not do any filtering and |
| // does not do any special log formatting; this is the difference between a |
| // "regular" logger and a "standard" logger. |
| + (id)logger; |
| |
| // Designated initializer. This method returns a GTMLogger initialized with the |
| // specified |writer|, |formatter|, and |filter|. See the setter methods below |
| // for what values will be used if nil is passed for a parameter. |
| - (id)initWithWriter:(id<GTMLogWriter>)writer |
| formatter:(id<GTMLogFormatter>)formatter |
| filter:(id<GTMLogFilter>)filter; |
| |
| // |
| // Logging methods |
| // |
| |
| // Logs a message at the debug level (kGTMLoggerLevelDebug). |
| - (void)logDebug:(NSString *)fmt, ... NS_FORMAT_FUNCTION(1, 2); |
| // Logs a message at the info level (kGTMLoggerLevelInfo). |
| - (void)logInfo:(NSString *)fmt, ... NS_FORMAT_FUNCTION(1, 2); |
| // Logs a message at the error level (kGTMLoggerLevelError). |
| - (void)logError:(NSString *)fmt, ... NS_FORMAT_FUNCTION(1, 2); |
| // Logs a message at the assert level (kGTMLoggerLevelAssert). |
| - (void)logAssert:(NSString *)fmt, ... NS_FORMAT_FUNCTION(1, 2); |
| |
| |
| // |
| // Accessors |
| // |
| |
| // 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; |
| |
| // Accessor methods for the log formatter. If the log formatter is set to nil, |
| // GTMLogBasicFormatter is used. This formatter will format log messages in a |
| // plain printf style. |
| - (id<GTMLogFormatter>)formatter; |
| - (void)setFormatter:(id<GTMLogFormatter>)formatter; |
| |
| // Accessor methods for the log filter. If the log filter is set to nil, |
| // GTMLogNoFilter is used, which allows all log messages through. |
| - (id<GTMLogFilter>)filter; |
| - (void)setFilter:(id<GTMLogFilter>)filter; |
| |
| @end // GTMLogger |
| |
| |
| // 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, ... |
| NS_FORMAT_FUNCTION(2, 3); |
| - (void)logFuncInfo:(const char *)func msg:(NSString *)fmt, ... |
| NS_FORMAT_FUNCTION(2, 3); |
| - (void)logFuncError:(const char *)func msg:(NSString *)fmt, ... |
| NS_FORMAT_FUNCTION(2, 3); |
| - (void)logFuncAssert:(const char *)func msg:(NSString *)fmt, ... |
| NS_FORMAT_FUNCTION(2, 3); |
| @end // GTMLoggerMacroHelpers |
| |
| |
| // The convenience macros are only defined if they haven't already been defined. |
| #ifndef GTMLoggerInfo |
| |
| // Convenience macros that log to the shared GTMLogger instance. These macros |
| // are how users should typically log to GTMLogger. Notice that GTMLoggerDebug() |
| // calls will be compiled out of non-Debug builds. |
| #define GTMLoggerDebug(...) \ |
| [[GTMLogger sharedLogger] logFuncDebug:__func__ msg:__VA_ARGS__] |
| #define GTMLoggerInfo(...) \ |
| [[GTMLogger sharedLogger] logFuncInfo:__func__ msg:__VA_ARGS__] |
| #define GTMLoggerError(...) \ |
| [[GTMLogger sharedLogger] logFuncError:__func__ msg:__VA_ARGS__] |
| #define GTMLoggerAssert(...) \ |
| [[GTMLogger sharedLogger] logFuncAssert:__func__ msg:__VA_ARGS__] |
| |
| // If we're not in a debug build, remove the GTMLoggerDebug statements. This |
| // makes calls to GTMLoggerDebug "compile out" of Release builds |
| #ifndef DEBUG |
| #undef GTMLoggerDebug |
| #define GTMLoggerDebug(...) do {} while(0) |
| #endif |
| |
| #endif // !defined(GTMLoggerInfo) |
| |
| // Log levels. |
| typedef enum { |
| kGTMLoggerLevelUnknown, |
| kGTMLoggerLevelDebug, |
| kGTMLoggerLevelInfo, |
| kGTMLoggerLevelError, |
| kGTMLoggerLevelAssert, |
| } GTMLoggerLevel; |
| |
| |
| // |
| // Log Writers |
| // |
| |
| // Protocol to be implemented by a GTMLogWriter instance. |
| @protocol GTMLogWriter <NSObject> |
| // Writes the given log message to where the log writer is configured to write. |
| - (void)logMessage:(NSString *)msg level:(GTMLoggerLevel)level; |
| @end // GTMLogWriter |
| |
| |
| // Simple category on NSFileHandle that makes NSFileHandles valid log writers. |
| // This is convenient because something like, say, +fileHandleWithStandardError |
| // 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| |
| // if it didn't previously exist. |
| + (id)fileHandleForLoggingAtPath:(NSString *)path mode:(mode_t)mode; |
| @end // NSFileHandle |
| |
| |
| // This category makes NSArray a GTMLogWriter that can be composed of other |
| // GTMLogWriters. This is the classic Composite GoF design pattern. When the |
| // GTMLogWriter -logMessage:level: message is sent to the array, the array |
| // forwards the message to all of its elements that implement the GTMLogWriter |
| // protocol. |
| // |
| // 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 |
| // GTMLogger instance. |
| @interface NSArray (GTMArrayCompositeLogWriter) <GTMLogWriter> |
| @end // GTMArrayCompositeLogWriter |
| |
| |
| // This category adapts the GTMLogger interface so that it can be used as a log |
| // writer; it's an "adapter" in the GoF Adapter pattern sense. |
| // |
| // 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 |
| // filter. |
| @interface GTMLogger (GTMLoggerLogWriter) <GTMLogWriter> |
| @end // GTMLoggerLogWriter |
| |
| |
| // |
| // Log Formatters |
| // |
| |
| // Protocol to be implemented by a GTMLogFormatter instance. |
| @protocol GTMLogFormatter <NSObject> |
| // Returns a formatted string using the format specified in |fmt| and the va |
| // args specified in |args|. |
| - (NSString *)stringForFunc:(NSString *)func |
| withFormat:(NSString *)fmt |
| valist:(va_list)args |
| level:(GTMLoggerLevel)level NS_FORMAT_FUNCTION(2, 0); |
| @end // GTMLogFormatter |
| |
| |
| // 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 |
| |
| |
| // A log formatter that formats the log string like the basic formatter, but |
| // also prepends a timestamp and some basic process info to the message, as |
| // shown in the following sample output. |
| // 2007-12-30 10:29:24.177 myapp[4588/0xa07d0f60] [lvl=1] log mesage here |
| @interface GTMLogStandardFormatter : GTMLogBasicFormatter { |
| @private |
| NSDateFormatter *dateFormatter_; // yyyy-MM-dd HH:mm:ss.SSS |
| NSString *pname_; |
| pid_t pid_; |
| } |
| @end // GTMLogStandardFormatter |
| |
| |
| // |
| // Log Filters |
| // |
| |
| // Protocol to be implemented by a GTMLogFilter instance. |
| @protocol GTMLogFilter <NSObject> |
| // Returns YES if |msg| at |level| should be logged; NO otherwise. |
| - (BOOL)filterAllowsMessage:(NSString *)msg level:(GTMLoggerLevel)level; |
| @end // GTMLogFilter |
| |
| |
| // A log filter that filters messages at the kGTMLoggerLevelDebug level out of |
| // non-debug builds. Messages at the kGTMLoggerLevelInfo level are also filtered |
| // out of non-debug builds unless GTMVerboseLogging is set in the environment or |
| // the processes's defaults. Messages at the kGTMLoggerLevelError level are |
| // never filtered. |
| @interface GTMLogLevelFilter : NSObject <GTMLogFilter> { |
| @private |
| BOOL verboseLoggingEnabled_; |
| NSUserDefaults *userDefaults_; |
| } |
| @end // GTMLogLevelFilter |
| |
| // A simple log filter that does NOT filter anything out; |
| // -filterAllowsMessage:level will always return YES. This can be a convenient |
| // way to enable debug-level logging in release builds (if you so desire). |
| @interface GTMLogNoFilter : NSObject <GTMLogFilter> |
| @end // GTMLogNoFilter |
| |
| |
| // Base class for custom level filters. Not for direct use, use the minimum |
| // or maximum level subclasses below. |
| @interface GTMLogAllowedLevelFilter : NSObject <GTMLogFilter> { |
| @private |
| NSIndexSet *allowedLevels_; |
| } |
| @end |
| |
| // A log filter that allows you to set a minimum log level. Messages below this |
| // level will be filtered. |
| @interface GTMLogMininumLevelFilter : GTMLogAllowedLevelFilter |
| |
| // Designated initializer, logs at levels < |level| will be filtered. |
| - (id)initWithMinimumLevel:(GTMLoggerLevel)level; |
| |
| @end |
| |
| // A log filter that allows you to set a maximum log level. Messages whose level |
| // exceeds this level will be filtered. This is really only useful if you have |
| // a composite GTMLogger that is sending the other messages elsewhere. |
| @interface GTMLogMaximumLevelFilter : GTMLogAllowedLevelFilter |
| |
| // Designated initializer, logs at levels > |level| will be filtered. |
| - (id)initWithMaximumLevel:(GTMLoggerLevel)level; |
| |
| @end |
| |
| |
| // For subclasses only |
| @interface GTMLogger (PrivateMethods) |
| |
| - (void)logInternalFunc:(const char *)func |
| format:(NSString *)fmt |
| valist:(va_list)args |
| level:(GTMLoggerLevel)level NS_FORMAT_FUNCTION(2, 0); |
| |
| @end |
| |