Permalink
Cannot retrieve contributors at this time
// | |
// 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 | |