You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

508 lines
21 KiB

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