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.
346typedef 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