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