Home | History | Annotate | Download | only in mac
      1 //
      2 //  GTMLogger.h
      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 under
     16 //  the License.
     17 //
     18 
     19 // Key Abstractions
     20 // ----------------
     21 //
     22 // This file declares multiple classes and protocols that are used by the
     23 // GTMLogger logging system. The 4 main abstractions used in this file are the
     24 // following:
     25 //
     26 //   * logger (GTMLogger) - The main logging class that users interact with. It
     27 //   has methods for logging at different levels and uses a log writer, a log
     28 //   formatter, and a log filter to get the job done.
     29 //
     30 //   * log writer (GTMLogWriter) - Writes a given string to some log file, where
     31 //   a "log file" can be a physical file on disk, a POST over HTTP to some URL,
     32 //   or even some in-memory structure (e.g., a ring buffer).
     33 //
     34 //   * log formatter (GTMLogFormatter) - Given a format string and arguments as
     35 //   a va_list, returns a single formatted NSString. A "formatted string" could
     36 //   be a string with the date prepended, a string with values in a CSV format,
     37 //   or even a string of XML.
     38 //
     39 //   * log filter (GTMLogFilter) - Given a formatted log message as an NSString
     40 //   and the level at which the message is to be logged, this class will decide
     41 //   whether the given message should be logged or not. This is a flexible way
     42 //   to filter out messages logged at a certain level, messages that contain
     43 //   certain text, or filter nothing out at all. This gives the caller the
     44 //   flexibility to dynamically enable debug logging in Release builds.
     45 //
     46 // This file also declares some classes to handle the common log writer, log
     47 // formatter, and log filter cases. Callers can also create their own writers,
     48 // formatters, and filters and they can even build them on top of the ones
     49 // declared here. Keep in mind that your custom writer/formatter/filter may be
     50 // called from multiple threads, so it must be thread-safe.
     51 
     52 #import <Foundation/Foundation.h>
     53 #import "GTMDefines.h"
     54 
     55 // Predeclaration of used protocols that are declared later in this file.
     56 @protocol GTMLogWriter, GTMLogFormatter, GTMLogFilter;
     57 
     58 // GTMLogger
     59 //
     60 // GTMLogger is the primary user-facing class for an object-oriented logging
     61 // system. It is built on the concept of log formatters (GTMLogFormatter), log
     62 // writers (GTMLogWriter), and log filters (GTMLogFilter). When a message is
     63 // sent to a GTMLogger to log a message, the message is formatted using the log
     64 // formatter, then the log filter is consulted to see if the message should be
     65 // logged, and if so, the message is sent to the log writer to be written out.
     66 //
     67 // GTMLogger is intended to be a flexible and thread-safe logging solution. Its
     68 // flexibility comes from the fact that GTMLogger instances can be customized
     69 // with user defined formatters, filters, and writers. And these writers,
     70 // filters, and formatters can be combined, stacked, and customized in arbitrary
     71 // ways to suit the needs at hand. For example, multiple writers can be used at
     72 // the same time, and a GTMLogger instance can even be used as another
     73 // GTMLogger's writer. This allows for arbitrarily deep logging trees.
     74 //
     75 // A standard GTMLogger uses a writer that sends messages to standard out, a
     76 // formatter that smacks a timestamp and a few other bits of interesting
     77 // information on the message, and a filter that filters out debug messages from
     78 // release builds. Using the standard log settings, a log message will look like
     79 // the following:
     80 //
     81 //   2007-12-30 10:29:24.177 myapp[4588/0xa07d0f60] [lvl=1] foo=<Foo: 0x123>
     82 //
     83 // The output contains the date and time of the log message, the name of the
     84 // process followed by its process ID/thread ID, the log level at which the
     85 // message was logged (in the previous example the level was 1:
     86 // kGTMLoggerLevelDebug), and finally, the user-specified log message itself (in
     87 // this case, the log message was @"foo=%@", foo).
     88 //
     89 // Multiple instances of GTMLogger can be created, each configured their own
     90 // way.  Though GTMLogger is not a singleton (in the GoF sense), it does provide
     91 // access to a shared (i.e., globally accessible) GTMLogger instance. This makes
     92 // it convenient for all code in a process to use the same GTMLogger instance.
     93 // The shared GTMLogger instance can also be configured in an arbitrary, and
     94 // these configuration changes will affect all code that logs through the shared
     95 // instance.
     96 
     97 //
     98 // Log Levels
     99 // ----------
    100 // GTMLogger has 3 different log levels: Debug, Info, and Error. GTMLogger
    101 // doesn't take any special action based on the log level; it simply forwards
    102 // this information on to formatters, filters, and writers, each of which may
    103 // optionally take action based on the level. Since log level filtering is
    104 // performed at runtime, log messages are typically not filtered out at compile
    105 // time.  The exception to this rule is that calls to the GTMLoggerDebug() macro
    106 // *ARE* filtered out of non-DEBUG builds. This is to be backwards compatible
    107 // with behavior that many developers are currently used to. Note that this
    108 // means that GTMLoggerDebug(@"hi") will be compiled out of Release builds, but
    109 // [[GTMLogger sharedLogger] logDebug:@"hi"] will NOT be compiled out.
    110 //
    111 // Standard loggers are created with the GTMLogLevelFilter log filter, which
    112 // filters out certain log messages based on log level, and some other settings.
    113 //
    114 // In addition to the -logDebug:, -logInfo:, and -logError: methods defined on
    115 // GTMLogger itself, there are also C macros that make usage of the shared
    116 // GTMLogger instance very convenient. These macros are:
    117 //
    118 //   GTMLoggerDebug(...)
    119 //   GTMLoggerInfo(...)
    120 //   GTMLoggerError(...)
    121 //
    122 // Again, a notable feature of these macros is that GTMLogDebug() calls *will be
    123 // compiled out of non-DEBUG builds*.
    124 //
    125 // Standard Loggers
    126 // ----------------
    127 // GTMLogger has the concept of "standard loggers". A standard logger is simply
    128 // a logger that is pre-configured with some standard/common writer, formatter,
    129 // and filter combination. Standard loggers are created using the creation
    130 // methods beginning with "standard". The alternative to a standard logger is a
    131 // regular logger, which will send messages to stdout, with no special
    132 // formatting, and no filtering.
    133 //
    134 // How do I use GTMLogger?
    135 // ----------------------
    136 // The typical way you will want to use GTMLogger is to simply use the
    137 // GTMLogger*() macros for logging from code. That way we can easily make
    138 // changes to the GTMLogger class and simply update the macros accordingly. Only
    139 // your application startup code (perhaps, somewhere in main()) should use the
    140 // GTMLogger class directly in order to configure the shared logger, which all
    141 // of the code using the macros will be using. Again, this is just the typical
    142 // situation.
    143 //
    144 // To be complete, there are cases where you may want to use GTMLogger directly,
    145 // or even create separate GTMLogger instances for some reason. That's fine,
    146 // too.
    147 //
    148 // Examples
    149 // --------
    150 // The following show some common GTMLogger use cases.
    151 //
    152 // 1. You want to log something as simply as possible. Also, this call will only
    153 //    appear in debug builds. In non-DEBUG builds it will be completely removed.
    154 //
    155 //      GTMLoggerDebug(@"foo = %@", foo);
    156 //
    157 // 2. The previous example is similar to the following. The major difference is
    158 //    that the previous call (example 1) will be compiled out of Release builds
    159 //    but this statement will not be compiled out.
    160 //
    161 //      [[GTMLogger sharedLogger] logDebug:@"foo = %@", foo];
    162 //
    163 // 3. Send all logging output from the shared logger to a file. We do this by
    164 //    creating an NSFileHandle for writing associated with a file, and setting
    165 //    that file handle as the logger's writer.
    166 //
    167 //      NSFileHandle *f = [NSFileHandle fileHandleForWritingAtPath:@"/tmp/f.log"
    168 //                                                          create:YES];
    169 //      [[GTMLogger sharedLogger] setWriter:f];
    170 //      GTMLoggerError(@"hi");  // This will be sent to /tmp/f.log
    171 //
    172 // 4. Create a new GTMLogger that will log to a file. This example differs from
    173 //    the previous one because here we create a new GTMLogger that is different
    174 //    from the shared logger.
    175 //
    176 //      GTMLogger *logger = [GTMLogger standardLoggerWithPath:@"/tmp/temp.log"];
    177 //      [logger logInfo:@"hi temp log file"];
    178 //
    179 // 5. Create a logger that writes to stdout and does NOT do any formatting to
    180 //    the log message. This might be useful, for example, when writing a help
    181 //    screen for a command-line tool to standard output.
    182 //
    183 //      GTMLogger *logger = [GTMLogger logger];
    184 //      [logger logInfo:@"%@ version 0.1 usage", progName];
    185 //
    186 // 6. Send log output to stdout AND to a log file. The trick here is that
    187 //    NSArrays function as composite log writers, which means when an array is
    188 //    set as the log writer, it forwards all logging messages to all of its
    189 //    contained GTMLogWriters.
    190 //
    191 //      // Create array of GTMLogWriters
    192 //      NSArray *writers = [NSArray arrayWithObjects:
    193 //          [NSFileHandle fileHandleForWritingAtPath:@"/tmp/f.log" create:YES],
    194 //          [NSFileHandle fileHandleWithStandardOutput], nil];
    195 //
    196 //      GTMLogger *logger = [GTMLogger standardLogger];
    197 //      [logger setWriter:writers];
    198 //      [logger logInfo:@"hi"];  // Output goes to stdout and /tmp/f.log
    199 //
    200 // For futher details on log writers, formatters, and filters, see the
    201 // documentation below.
    202 //
    203 // NOTE: GTMLogger is application level logging.  By default it does nothing
    204 // with _GTMDevLog/_GTMDevAssert (see GTMDefines.h).  An application can choose
    205 // to bridge _GTMDevLog/_GTMDevAssert to GTMLogger by providing macro
    206 // definitions in its prefix header (see GTMDefines.h for how one would do
    207 // that).
    208 //
    209 @interface GTMLogger : NSObject {
    210  @private
    211   id<GTMLogWriter> writer_;
    212   id<GTMLogFormatter> formatter_;
    213   id<GTMLogFilter> filter_;
    214 }
    215 
    216 //
    217 // Accessors for the shared logger instance
    218 //
    219 
    220 // Returns a shared/global standard GTMLogger instance. Callers should typically
    221 // use this method to get a GTMLogger instance, unless they explicitly want
    222 // their own instance to configure for their own needs. This is the only method
    223 // that returns a shared instance; all the rest return new GTMLogger instances.
    224 + (id)sharedLogger;
    225 
    226 // Sets the shared logger instance to |logger|. Future calls to +sharedLogger
    227 // will return |logger| instead.
    228 + (void)setSharedLogger:(GTMLogger *)logger;
    229 
    230 //
    231 // Creation methods
    232 //
    233 
    234 // Returns a new autoreleased GTMLogger instance that will log to stdout, using
    235 // the GTMLogStandardFormatter, and the GTMLogLevelFilter filter.
    236 + (id)standardLogger;
    237 
    238 // Same as +standardLogger, but logs to stderr.
    239 + (id)standardLoggerWithStderr;
    240 
    241 // Same as +standardLogger but levels >= kGTMLoggerLevelError are routed to
    242 // stderr, everything else goes to stdout.
    243 + (id)standardLoggerWithStdoutAndStderr;
    244 
    245 // Returns a new standard GTMLogger instance with a log writer that will
    246 // write to the file at |path|, and will use the GTMLogStandardFormatter and
    247 // GTMLogLevelFilter classes. If |path| does not exist, it will be created.
    248 + (id)standardLoggerWithPath:(NSString *)path;
    249 
    250 // Returns an autoreleased GTMLogger instance that will use the specified
    251 // |writer|, |formatter|, and |filter|.
    252 + (id)loggerWithWriter:(id<GTMLogWriter>)writer
    253              formatter:(id<GTMLogFormatter>)formatter
    254                 filter:(id<GTMLogFilter>)filter;
    255 
    256 // Returns an autoreleased GTMLogger instance that logs to stdout, with the
    257 // basic formatter, and no filter. The returned logger differs from the logger
    258 // returned by +standardLogger because this one does not do any filtering and
    259 // does not do any special log formatting; this is the difference between a
    260 // "regular" logger and a "standard" logger.
    261 + (id)logger;
    262 
    263 // Designated initializer. This method returns a GTMLogger initialized with the
    264 // specified |writer|, |formatter|, and |filter|. See the setter methods below
    265 // for what values will be used if nil is passed for a parameter.
    266 - (id)initWithWriter:(id<GTMLogWriter>)writer
    267            formatter:(id<GTMLogFormatter>)formatter
    268               filter:(id<GTMLogFilter>)filter;
    269 
    270 //
    271 // Logging  methods
    272 //
    273 
    274 // Logs a message at the debug level (kGTMLoggerLevelDebug).
    275 - (void)logDebug:(NSString *)fmt, ... NS_FORMAT_FUNCTION(1, 2);
    276 // Logs a message at the info level (kGTMLoggerLevelInfo).
    277 - (void)logInfo:(NSString *)fmt, ... NS_FORMAT_FUNCTION(1, 2);
    278 // Logs a message at the error level (kGTMLoggerLevelError).
    279 - (void)logError:(NSString *)fmt, ... NS_FORMAT_FUNCTION(1, 2);
    280 // Logs a message at the assert level (kGTMLoggerLevelAssert).
    281 - (void)logAssert:(NSString *)fmt, ... NS_FORMAT_FUNCTION(1, 2);
    282 
    283 
    284 //
    285 // Accessors
    286 //
    287 
    288 // Accessor methods for the log writer. If the log writer is set to nil,
    289 // [NSFileHandle fileHandleWithStandardOutput] is used.
    290 - (id<GTMLogWriter>)writer;
    291 - (void)setWriter:(id<GTMLogWriter>)writer;
    292 
    293 // Accessor methods for the log formatter. If the log formatter is set to nil,
    294 // GTMLogBasicFormatter is used. This formatter will format log messages in a
    295 // plain printf style.
    296 - (id<GTMLogFormatter>)formatter;
    297 - (void)setFormatter:(id<GTMLogFormatter>)formatter;
    298 
    299 // Accessor methods for the log filter. If the log filter is set to nil,
    300 // GTMLogNoFilter is used, which allows all log messages through.
    301 - (id<GTMLogFilter>)filter;
    302 - (void)setFilter:(id<GTMLogFilter>)filter;
    303 
    304 @end  // GTMLogger
    305 
    306 
    307 // Helper functions that are used by the convenience GTMLogger*() macros that
    308 // enable the logging of function names.
    309 @interface GTMLogger (GTMLoggerMacroHelpers)
    310 - (void)logFuncDebug:(const char *)func msg:(NSString *)fmt, ...
    311   NS_FORMAT_FUNCTION(2, 3);
    312 - (void)logFuncInfo:(const char *)func msg:(NSString *)fmt, ...
    313   NS_FORMAT_FUNCTION(2, 3);
    314 - (void)logFuncError:(const char *)func msg:(NSString *)fmt, ...
    315   NS_FORMAT_FUNCTION(2, 3);
    316 - (void)logFuncAssert:(const char *)func msg:(NSString *)fmt, ...
    317   NS_FORMAT_FUNCTION(2, 3);
    318 @end  // GTMLoggerMacroHelpers
    319 
    320 
    321 // The convenience macros are only defined if they haven't already been defined.
    322 #ifndef GTMLoggerInfo
    323 
    324 // Convenience macros that log to the shared GTMLogger instance. These macros
    325 // are how users should typically log to GTMLogger. Notice that GTMLoggerDebug()
    326 // calls will be compiled out of non-Debug builds.
    327 #define GTMLoggerDebug(...)  \
    328   [[GTMLogger sharedLogger] logFuncDebug:__func__ msg:__VA_ARGS__]
    329 #define GTMLoggerInfo(...)   \
    330   [[GTMLogger sharedLogger] logFuncInfo:__func__ msg:__VA_ARGS__]
    331 #define GTMLoggerError(...)  \
    332   [[GTMLogger sharedLogger] logFuncError:__func__ msg:__VA_ARGS__]
    333 #define GTMLoggerAssert(...) \
    334   [[GTMLogger sharedLogger] logFuncAssert:__func__ msg:__VA_ARGS__]
    335 
    336 // If we're not in a debug build, remove the GTMLoggerDebug statements. This
    337 // makes calls to GTMLoggerDebug "compile out" of Release builds
    338 #ifndef DEBUG
    339 #undef GTMLoggerDebug
    340 #define GTMLoggerDebug(...) do {} while(0)
    341 #endif
    342 
    343 #endif  // !defined(GTMLoggerInfo)
    344 
    345 // Log levels.
    346 typedef enum {
    347   kGTMLoggerLevelUnknown,
    348   kGTMLoggerLevelDebug,
    349   kGTMLoggerLevelInfo,
    350   kGTMLoggerLevelError,
    351   kGTMLoggerLevelAssert,
    352 } GTMLoggerLevel;
    353 
    354 
    355 //
    356 //   Log Writers
    357 //
    358 
    359 // Protocol to be implemented by a GTMLogWriter instance.
    360 @protocol GTMLogWriter <NSObject>
    361 // Writes the given log message to where the log writer is configured to write.
    362 - (void)logMessage:(NSString *)msg level:(GTMLoggerLevel)level;
    363 @end  // GTMLogWriter
    364 
    365 
    366 // Simple category on NSFileHandle that makes NSFileHandles valid log writers.
    367 // This is convenient because something like, say, +fileHandleWithStandardError
    368 // now becomes a valid log writer. Log messages are written to the file handle
    369 // with a newline appended.
    370 @interface NSFileHandle (GTMFileHandleLogWriter) <GTMLogWriter>
    371 // Opens the file at |path| in append mode, and creates the file with |mode|
    372 // if it didn't previously exist.
    373 + (id)fileHandleForLoggingAtPath:(NSString *)path mode:(mode_t)mode;
    374 @end  // NSFileHandle
    375 
    376 
    377 // This category makes NSArray a GTMLogWriter that can be composed of other
    378 // GTMLogWriters. This is the classic Composite GoF design pattern. When the
    379 // GTMLogWriter -logMessage:level: message is sent to the array, the array
    380 // forwards the message to all of its elements that implement the GTMLogWriter
    381 // protocol.
    382 //
    383 // This is useful in situations where you would like to send log output to
    384 // multiple log writers at the same time. Simply create an NSArray of the log
    385 // writers you wish to use, then set the array as the "writer" for your
    386 // GTMLogger instance.
    387 @interface NSArray (GTMArrayCompositeLogWriter) <GTMLogWriter>
    388 @end  // GTMArrayCompositeLogWriter
    389 
    390 
    391 // This category adapts the GTMLogger interface so that it can be used as a log
    392 // writer; it's an "adapter" in the GoF Adapter pattern sense.
    393 //
    394 // This is useful when you want to configure a logger to log to a specific
    395 // writer with a specific formatter and/or filter. But you want to also compose
    396 // that with a different log writer that may have its own formatter and/or
    397 // filter.
    398 @interface GTMLogger (GTMLoggerLogWriter) <GTMLogWriter>
    399 @end  // GTMLoggerLogWriter
    400 
    401 
    402 //
    403 //   Log Formatters
    404 //
    405 
    406 // Protocol to be implemented by a GTMLogFormatter instance.
    407 @protocol GTMLogFormatter <NSObject>
    408 // Returns a formatted string using the format specified in |fmt| and the va
    409 // args specified in |args|.
    410 - (NSString *)stringForFunc:(NSString *)func
    411                  withFormat:(NSString *)fmt
    412                      valist:(va_list)args
    413                       level:(GTMLoggerLevel)level NS_FORMAT_FUNCTION(2, 0);
    414 @end  // GTMLogFormatter
    415 
    416 
    417 // A basic log formatter that formats a string the same way that NSLog (or
    418 // printf) would. It does not do anything fancy, nor does it add any data of its
    419 // own.
    420 @interface GTMLogBasicFormatter : NSObject <GTMLogFormatter>
    421 
    422 // Helper method for prettying C99 __func__ and GCC __PRETTY_FUNCTION__
    423 - (NSString *)prettyNameForFunc:(NSString *)func;
    424 
    425 @end  // GTMLogBasicFormatter
    426 
    427 
    428 // A log formatter that formats the log string like the basic formatter, but
    429 // also prepends a timestamp and some basic process info to the message, as
    430 // shown in the following sample output.
    431 //   2007-12-30 10:29:24.177 myapp[4588/0xa07d0f60] [lvl=1] log mesage here
    432 @interface GTMLogStandardFormatter : GTMLogBasicFormatter {
    433  @private
    434   NSDateFormatter *dateFormatter_;  // yyyy-MM-dd HH:mm:ss.SSS
    435   NSString *pname_;
    436   pid_t pid_;
    437 }
    438 @end  // GTMLogStandardFormatter
    439 
    440 
    441 //
    442 //   Log Filters
    443 //
    444 
    445 // Protocol to be imlemented by a GTMLogFilter instance.
    446 @protocol GTMLogFilter <NSObject>
    447 // Returns YES if |msg| at |level| should be filtered out; NO otherwise.
    448 - (BOOL)filterAllowsMessage:(NSString *)msg level:(GTMLoggerLevel)level;
    449 @end  // GTMLogFilter
    450 
    451 
    452 // A log filter that filters messages at the kGTMLoggerLevelDebug level out of
    453 // non-debug builds. Messages at the kGTMLoggerLevelInfo level are also filtered
    454 // out of non-debug builds unless GTMVerboseLogging is set in the environment or
    455 // the processes's defaults. Messages at the kGTMLoggerLevelError level are
    456 // never filtered.
    457 @interface GTMLogLevelFilter : NSObject <GTMLogFilter>
    458 @end  // GTMLogLevelFilter
    459 
    460 // A simple log filter that does NOT filter anything out;
    461 // -filterAllowsMessage:level will always return YES. This can be a convenient
    462 // way to enable debug-level logging in release builds (if you so desire).
    463 @interface GTMLogNoFilter : NSObject <GTMLogFilter>
    464 @end  // GTMLogNoFilter
    465 
    466 
    467 // Base class for custom level filters. Not for direct use, use the minimum
    468 // or maximum level subclasses below.
    469 @interface GTMLogAllowedLevelFilter : NSObject <GTMLogFilter> {
    470  @private
    471   NSIndexSet *allowedLevels_;
    472 }
    473 @end
    474 
    475 // A log filter that allows you to set a minimum log level. Messages below this
    476 // level will be filtered.
    477 @interface GTMLogMininumLevelFilter : GTMLogAllowedLevelFilter
    478 
    479 // Designated initializer, logs at levels < |level| will be filtered.
    480 - (id)initWithMinimumLevel:(GTMLoggerLevel)level;
    481 
    482 @end
    483 
    484 // A log filter that allows you to set a maximum log level. Messages whose level
    485 // exceeds this level will be filtered. This is really only useful if you have
    486 // a composite GTMLogger that is sending the other messages elsewhere.
    487 @interface GTMLogMaximumLevelFilter : GTMLogAllowedLevelFilter
    488 
    489 // Designated initializer, logs at levels > |level| will be filtered.
    490 - (id)initWithMaximumLevel:(GTMLoggerLevel)level;
    491 
    492 @end
    493 
    494 
    495 // For subclasses only
    496 @interface GTMLogger (PrivateMethods)
    497 
    498 - (void)logInternalFunc:(const char *)func
    499                  format:(NSString *)fmt
    500                  valist:(va_list)args
    501                   level:(GTMLoggerLevel)level NS_FORMAT_FUNCTION(2, 0);
    502 
    503 @end
    504 
    505