GTMLogger.h 20.5 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504
//
//  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 imlemented by a GTMLogFilter instance.
@protocol GTMLogFilter <NSObject>
// Returns YES if |msg| at |level| should be filtered out; 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>
@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