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