Нет описания

DDLog.h 19KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547
  1. // Software License Agreement (BSD License)
  2. //
  3. // Copyright (c) 2010-2015, Deusty, LLC
  4. // All rights reserved.
  5. //
  6. // Redistribution and use of this software in source and binary forms,
  7. // with or without modification, are permitted provided that the following conditions are met:
  8. //
  9. // * Redistributions of source code must retain the above copyright notice,
  10. // this list of conditions and the following disclaimer.
  11. //
  12. // * Neither the name of Deusty nor the names of its contributors may be used
  13. // to endorse or promote products derived from this software without specific
  14. // prior written permission of Deusty, LLC.
  15. #import <Foundation/Foundation.h>
  16. // Enable 1.9.x legacy macros if imported directly
  17. #ifndef DD_LEGACY_MACROS
  18. #define DD_LEGACY_MACROS 1
  19. #endif
  20. #if DD_LEGACY_MACROS
  21. #import "DDLegacyMacros.h"
  22. #endif
  23. #if OS_OBJECT_USE_OBJC
  24. #define DISPATCH_QUEUE_REFERENCE_TYPE strong
  25. #else
  26. #define DISPATCH_QUEUE_REFERENCE_TYPE assign
  27. #endif
  28. @class DDLogMessage;
  29. @protocol DDLogger;
  30. @protocol DDLogFormatter;
  31. /**
  32. * Define the standard options.
  33. *
  34. * We default to only 4 levels because it makes it easier for beginners
  35. * to make the transition to a logging framework.
  36. *
  37. * More advanced users may choose to completely customize the levels (and level names) to suite their needs.
  38. * For more information on this see the "Custom Log Levels" page:
  39. * Documentation/CustomLogLevels.md
  40. *
  41. * Advanced users may also notice that we're using a bitmask.
  42. * This is to allow for custom fine grained logging:
  43. * Documentation/FineGrainedLogging.md
  44. *
  45. * -- Flags --
  46. *
  47. * Typically you will use the LOG_LEVELS (see below), but the flags may be used directly in certain situations.
  48. * For example, say you have a lot of warning log messages, and you wanted to disable them.
  49. * However, you still needed to see your error and info log messages.
  50. * You could accomplish that with the following:
  51. *
  52. * static const DDLogLevel ddLogLevel = DDLogFlagError | DDLogFlagInfo;
  53. *
  54. * When LOG_LEVEL_DEF is defined as ddLogLevel.
  55. *
  56. * Flags may also be consulted when writing custom log formatters,
  57. * as the DDLogMessage class captures the individual flag that caused the log message to fire.
  58. *
  59. * -- Levels --
  60. *
  61. * Log levels are simply the proper bitmask of the flags.
  62. *
  63. * -- Booleans --
  64. *
  65. * The booleans may be used when your logging code involves more than one line.
  66. * For example:
  67. *
  68. * if (LOG_VERBOSE) {
  69. * for (id sprocket in sprockets)
  70. * DDLogVerbose(@"sprocket: %@", [sprocket description])
  71. * }
  72. *
  73. * -- Async --
  74. *
  75. * Defines the default asynchronous options.
  76. * The default philosophy for asynchronous logging is very simple:
  77. *
  78. * Log messages with errors should be executed synchronously.
  79. * After all, an error just occurred. The application could be unstable.
  80. *
  81. * All other log messages, such as debug output, are executed asynchronously.
  82. * After all, if it wasn't an error, then it was just informational output,
  83. * or something the application was easily able to recover from.
  84. *
  85. * -- Changes --
  86. *
  87. * You are strongly discouraged from modifying this file.
  88. * If you do, you make it more difficult on yourself to merge future bug fixes and improvements from the project.
  89. * Instead, create your own MyLogging.h or ApplicationNameLogging.h or CompanyLogging.h
  90. *
  91. * For an example of customizing your logging experience, see the "Custom Log Levels" page:
  92. * Documentation/CustomLogLevels.md
  93. **/
  94. typedef NS_OPTIONS(NSUInteger, DDLogFlag) {
  95. DDLogFlagError = (1 << 0), // 0...00001
  96. DDLogFlagWarning = (1 << 1), // 0...00010
  97. DDLogFlagInfo = (1 << 2), // 0...00100
  98. DDLogFlagDebug = (1 << 3), // 0...01000
  99. DDLogFlagVerbose = (1 << 4) // 0...10000
  100. };
  101. typedef NS_ENUM(NSUInteger, DDLogLevel) {
  102. DDLogLevelOff = 0,
  103. DDLogLevelError = (DDLogFlagError), // 0...00001
  104. DDLogLevelWarning = (DDLogLevelError | DDLogFlagWarning), // 0...00011
  105. DDLogLevelInfo = (DDLogLevelWarning | DDLogFlagInfo), // 0...00111
  106. DDLogLevelDebug = (DDLogLevelInfo | DDLogFlagDebug), // 0...01111
  107. DDLogLevelVerbose = (DDLogLevelDebug | DDLogFlagVerbose), // 0...11111
  108. DDLogLevelAll = NSUIntegerMax // 1111....11111 (DDLogLevelVerbose plus any other flags)
  109. };
  110. /**
  111. * The THIS_FILE macro gives you an NSString of the file name.
  112. * For simplicity and clarity, the file name does not include the full path or file extension.
  113. *
  114. * For example: DDLogWarn(@"%@: Unable to find thingy", THIS_FILE) -> @"MyViewController: Unable to find thingy"
  115. **/
  116. NSString * DDExtractFileNameWithoutExtension(const char *filePath, BOOL copy);
  117. #define THIS_FILE (DDExtractFileNameWithoutExtension(__FILE__, NO))
  118. /**
  119. * The THIS_METHOD macro gives you the name of the current objective-c method.
  120. *
  121. * For example: DDLogWarn(@"%@ - Requires non-nil strings", THIS_METHOD) -> @"setMake:model: requires non-nil strings"
  122. *
  123. * Note: This does NOT work in straight C functions (non objective-c).
  124. * Instead you should use the predefined __FUNCTION__ macro.
  125. **/
  126. #define THIS_METHOD NSStringFromSelector(_cmd)
  127. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  128. #pragma mark -
  129. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  130. @interface DDLog : NSObject
  131. /**
  132. * Provides access to the underlying logging queue.
  133. * This may be helpful to Logger classes for things like thread synchronization.
  134. **/
  135. + (dispatch_queue_t)loggingQueue;
  136. /**
  137. * Logging Primitive.
  138. *
  139. * This method is used by the macros above.
  140. * It is suggested you stick with the macros as they're easier to use.
  141. **/
  142. + (void)log:(BOOL)synchronous
  143. level:(DDLogLevel)level
  144. flag:(DDLogFlag)flag
  145. context:(NSInteger)context
  146. file:(const char *)file
  147. function:(const char *)function
  148. line:(NSUInteger)line
  149. tag:(id)tag
  150. format:(NSString *)format, ... NS_FORMAT_FUNCTION(9,10);
  151. /**
  152. * Logging Primitive.
  153. *
  154. * This method can be used if you have a prepared va_list.
  155. **/
  156. + (void)log:(BOOL)asynchronous
  157. level:(DDLogLevel)level
  158. flag:(DDLogFlag)flag
  159. context:(NSInteger)context
  160. file:(const char *)file
  161. function:(const char *)function
  162. line:(NSUInteger)line
  163. tag:(id)tag
  164. format:(NSString *)format
  165. args:(va_list)argList;
  166. /**
  167. * Logging Primitive.
  168. **/
  169. + (void)log:(BOOL)asynchronous
  170. message:(NSString *)message
  171. level:(DDLogLevel)level
  172. flag:(DDLogFlag)flag
  173. context:(NSInteger)context
  174. file:(const char *)file
  175. function:(const char *)function
  176. line:(NSUInteger)line
  177. tag:(id)tag;
  178. /**
  179. * Logging Primitive.
  180. *
  181. * This method can be used if you manualy prepared DDLogMessage.
  182. **/
  183. + (void)log:(BOOL)asynchronous
  184. message:(DDLogMessage *)logMessage;
  185. /**
  186. * Since logging can be asynchronous, there may be times when you want to flush the logs.
  187. * The framework invokes this automatically when the application quits.
  188. **/
  189. + (void)flushLog;
  190. /**
  191. * Loggers
  192. *
  193. * In order for your log statements to go somewhere, you should create and add a logger.
  194. *
  195. * You can add multiple loggers in order to direct your log statements to multiple places.
  196. * And each logger can be configured separately.
  197. * So you could have, for example, verbose logging to the console, but a concise log file with only warnings & errors.
  198. **/
  199. /**
  200. * Adds the logger to the system.
  201. *
  202. * This is equivalent to invoking [DDLog addLogger:logger withLogLevel:DDLogLevelAll].
  203. **/
  204. + (void)addLogger:(id <DDLogger>)logger;
  205. /**
  206. * Adds the logger to the system.
  207. *
  208. * The level that you provide here is a preemptive filter (for performance).
  209. * That is, the level specified here will be used to filter out logMessages so that
  210. * the logger is never even invoked for the messages.
  211. *
  212. * More information:
  213. * When you issue a log statement, the logging framework iterates over each logger,
  214. * and checks to see if it should forward the logMessage to the logger.
  215. * This check is done using the level parameter passed to this method.
  216. *
  217. * For example:
  218. *
  219. * [DDLog addLogger:consoleLogger withLogLevel:DDLogLevelVerbose];
  220. * [DDLog addLogger:fileLogger withLogLevel:DDLogLevelWarning];
  221. *
  222. * DDLogError(@"oh no"); => gets forwarded to consoleLogger & fileLogger
  223. * DDLogInfo(@"hi"); => gets forwarded to consoleLogger only
  224. *
  225. * It is important to remember that Lumberjack uses a BITMASK.
  226. * Many developers & third party frameworks may define extra log levels & flags.
  227. * For example:
  228. *
  229. * #define SOME_FRAMEWORK_LOG_FLAG_TRACE (1 << 6) // 0...1000000
  230. *
  231. * So if you specify DDLogLevelVerbose to this method, you won't see the framework's trace messages.
  232. *
  233. * (SOME_FRAMEWORK_LOG_FLAG_TRACE & DDLogLevelVerbose) => (01000000 & 00011111) => NO
  234. *
  235. * Consider passing DDLogLevelAll to this method, which has all bits set.
  236. * You can also use the exclusive-or bitwise operator to get a bitmask that has all flags set,
  237. * except the ones you explicitly don't want. For example, if you wanted everything except verbose & debug:
  238. *
  239. * ((DDLogLevelAll ^ DDLogLevelVerbose) | DDLogLevelInfo)
  240. **/
  241. + (void)addLogger:(id <DDLogger>)logger withLevel:(DDLogLevel)level;
  242. + (void)removeLogger:(id <DDLogger>)logger;
  243. + (void)removeAllLoggers;
  244. + (NSArray *)allLoggers;
  245. /**
  246. * Registered Dynamic Logging
  247. *
  248. * These methods allow you to obtain a list of classes that are using registered dynamic logging,
  249. * and also provides methods to get and set their log level during run time.
  250. **/
  251. + (NSArray *)registeredClasses;
  252. + (NSArray *)registeredClassNames;
  253. + (DDLogLevel)levelForClass:(Class)aClass;
  254. + (DDLogLevel)levelForClassWithName:(NSString *)aClassName;
  255. + (void)setLevel:(DDLogLevel)level forClass:(Class)aClass;
  256. + (void)setLevel:(DDLogLevel)level forClassWithName:(NSString *)aClassName;
  257. @end
  258. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  259. #pragma mark -
  260. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  261. @protocol DDLogger <NSObject>
  262. - (void)logMessage:(DDLogMessage *)logMessage;
  263. /**
  264. * Formatters may optionally be added to any logger.
  265. *
  266. * If no formatter is set, the logger simply logs the message as it is given in logMessage,
  267. * or it may use its own built in formatting style.
  268. **/
  269. @property (nonatomic, strong) id <DDLogFormatter> logFormatter;
  270. @optional
  271. /**
  272. * Since logging is asynchronous, adding and removing loggers is also asynchronous.
  273. * In other words, the loggers are added and removed at appropriate times with regards to log messages.
  274. *
  275. * - Loggers will not receive log messages that were executed prior to when they were added.
  276. * - Loggers will not receive log messages that were executed after they were removed.
  277. *
  278. * These methods are executed in the logging thread/queue.
  279. * This is the same thread/queue that will execute every logMessage: invocation.
  280. * Loggers may use these methods for thread synchronization or other setup/teardown tasks.
  281. **/
  282. - (void)didAddLogger;
  283. - (void)willRemoveLogger;
  284. /**
  285. * Some loggers may buffer IO for optimization purposes.
  286. * For example, a database logger may only save occasionaly as the disk IO is slow.
  287. * In such loggers, this method should be implemented to flush any pending IO.
  288. *
  289. * This allows invocations of DDLog's flushLog method to be propogated to loggers that need it.
  290. *
  291. * Note that DDLog's flushLog method is invoked automatically when the application quits,
  292. * and it may be also invoked manually by the developer prior to application crashes, or other such reasons.
  293. **/
  294. - (void)flush;
  295. /**
  296. * Each logger is executed concurrently with respect to the other loggers.
  297. * Thus, a dedicated dispatch queue is used for each logger.
  298. * Logger implementations may optionally choose to provide their own dispatch queue.
  299. **/
  300. @property (nonatomic, DISPATCH_QUEUE_REFERENCE_TYPE, readonly) dispatch_queue_t loggerQueue;
  301. /**
  302. * If the logger implementation does not choose to provide its own queue,
  303. * one will automatically be created for it.
  304. * The created queue will receive its name from this method.
  305. * This may be helpful for debugging or profiling reasons.
  306. **/
  307. @property (nonatomic, readonly) NSString *loggerName;
  308. @end
  309. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  310. #pragma mark -
  311. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  312. @protocol DDLogFormatter <NSObject>
  313. @required
  314. /**
  315. * Formatters may optionally be added to any logger.
  316. * This allows for increased flexibility in the logging environment.
  317. * For example, log messages for log files may be formatted differently than log messages for the console.
  318. *
  319. * For more information about formatters, see the "Custom Formatters" page:
  320. * Documentation/CustomFormatters.md
  321. *
  322. * The formatter may also optionally filter the log message by returning nil,
  323. * in which case the logger will not log the message.
  324. **/
  325. - (NSString *)formatLogMessage:(DDLogMessage *)logMessage;
  326. @optional
  327. /**
  328. * A single formatter instance can be added to multiple loggers.
  329. * These methods provides hooks to notify the formatter of when it's added/removed.
  330. *
  331. * This is primarily for thread-safety.
  332. * If a formatter is explicitly not thread-safe, it may wish to throw an exception if added to multiple loggers.
  333. * Or if a formatter has potentially thread-unsafe code (e.g. NSDateFormatter),
  334. * it could possibly use these hooks to switch to thread-safe versions of the code.
  335. **/
  336. - (void)didAddToLogger:(id <DDLogger>)logger;
  337. - (void)willRemoveFromLogger:(id <DDLogger>)logger;
  338. @end
  339. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  340. #pragma mark -
  341. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  342. @protocol DDRegisteredDynamicLogging
  343. /**
  344. * Implement these methods to allow a file's log level to be managed from a central location.
  345. *
  346. * This is useful if you'd like to be able to change log levels for various parts
  347. * of your code from within the running application.
  348. *
  349. * Imagine pulling up the settings for your application,
  350. * and being able to configure the logging level on a per file basis.
  351. *
  352. * The implementation can be very straight-forward:
  353. *
  354. * + (int)ddLogLevel
  355. * {
  356. * return ddLogLevel;
  357. * }
  358. *
  359. * + (void)ddSetLogLevel:(DDLogLevel)level
  360. * {
  361. * ddLogLevel = level;
  362. * }
  363. **/
  364. + (DDLogLevel)ddLogLevel;
  365. + (void)ddSetLogLevel:(DDLogLevel)level;
  366. @end
  367. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  368. #pragma mark -
  369. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  370. #ifndef NS_DESIGNATED_INITIALIZER
  371. #define NS_DESIGNATED_INITIALIZER
  372. #endif
  373. /**
  374. * The DDLogMessage class encapsulates information about the log message.
  375. * If you write custom loggers or formatters, you will be dealing with objects of this class.
  376. **/
  377. typedef NS_OPTIONS(NSInteger, DDLogMessageOptions) {
  378. DDLogMessageCopyFile = 1 << 0,
  379. DDLogMessageCopyFunction = 1 << 1
  380. };
  381. @interface DDLogMessage : NSObject <NSCopying>
  382. {
  383. // Direct accessors to be used only for performance
  384. @public
  385. NSString *_message;
  386. DDLogLevel _level;
  387. DDLogFlag _flag;
  388. NSInteger _context;
  389. NSString *_file;
  390. NSString *_fileName;
  391. NSString *_function;
  392. NSUInteger _line;
  393. id _tag;
  394. DDLogMessageOptions _options;
  395. NSDate *_timestamp;
  396. NSString *_threadID;
  397. NSString *_threadName;
  398. NSString *_queueLabel;
  399. }
  400. /**
  401. * Standard init method for a log message object.
  402. * Used by the logging primitives. (And the macros use the logging primitives.)
  403. *
  404. * If you find need to manually create logMessage objects, there is one thing you should be aware of:
  405. *
  406. * If no flags are passed, the method expects the file and function parameters to be string literals.
  407. * That is, it expects the given strings to exist for the duration of the object's lifetime,
  408. * and it expects the given strings to be immutable.
  409. * In other words, it does not copy these strings, it simply points to them.
  410. * This is due to the fact that __FILE__ and __FUNCTION__ are usually used to specify these parameters,
  411. * so it makes sense to optimize and skip the unnecessary allocations.
  412. * However, if you need them to be copied you may use the options parameter to specify this.
  413. * Options is a bitmask which supports DDLogMessageCopyFile and DDLogMessageCopyFunction.
  414. **/
  415. - (instancetype)initWithMessage:(NSString *)message
  416. level:(DDLogLevel)level
  417. flag:(DDLogFlag)flag
  418. context:(NSInteger)context
  419. file:(NSString *)file
  420. function:(NSString *)function
  421. line:(NSUInteger)line
  422. tag:(id)tag
  423. options:(DDLogMessageOptions)options
  424. timestamp:(NSDate *)timestamp NS_DESIGNATED_INITIALIZER;
  425. /**
  426. * Read-only properties
  427. **/
  428. @property (readonly, nonatomic) NSString *message;
  429. @property (readonly, nonatomic) DDLogLevel level;
  430. @property (readonly, nonatomic) DDLogFlag flag;
  431. @property (readonly, nonatomic) NSInteger context;
  432. @property (readonly, nonatomic) NSString *file;
  433. @property (readonly, nonatomic) NSString *fileName;
  434. @property (readonly, nonatomic) NSString *function;
  435. @property (readonly, nonatomic) NSUInteger line;
  436. @property (readonly, nonatomic) id tag;
  437. @property (readonly, nonatomic) DDLogMessageOptions options;
  438. @property (readonly, nonatomic) NSDate *timestamp;
  439. @property (readonly, nonatomic) NSString *threadID; // ID as it appears in NSLog calculated from the machThreadID
  440. @property (readonly, nonatomic) NSString *threadName;
  441. @property (readonly, nonatomic) NSString *queueLabel;
  442. @end
  443. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  444. #pragma mark -
  445. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  446. /**
  447. * The DDLogger protocol specifies that an optional formatter can be added to a logger.
  448. * Most (but not all) loggers will want to support formatters.
  449. *
  450. * However, writting getters and setters in a thread safe manner,
  451. * while still maintaining maximum speed for the logging process, is a difficult task.
  452. *
  453. * To do it right, the implementation of the getter/setter has strict requiremenets:
  454. * - Must NOT require the logMessage method to acquire a lock.
  455. * - Must NOT require the logMessage method to access an atomic property (also a lock of sorts).
  456. *
  457. * To simplify things, an abstract logger is provided that implements the getter and setter.
  458. *
  459. * Logger implementations may simply extend this class,
  460. * and they can ACCESS THE FORMATTER VARIABLE DIRECTLY from within their logMessage method!
  461. **/
  462. @interface DDAbstractLogger : NSObject <DDLogger>
  463. {
  464. // Direct accessors to be used only for performance
  465. @public
  466. id <DDLogFormatter> _logFormatter;
  467. dispatch_queue_t _loggerQueue;
  468. }
  469. @property (nonatomic, strong) id <DDLogFormatter> logFormatter;
  470. @property (nonatomic, DISPATCH_QUEUE_REFERENCE_TYPE) dispatch_queue_t loggerQueue;
  471. // For thread-safety assertions
  472. @property (nonatomic, readonly, getter=isOnGlobalLoggingQueue) BOOL onGlobalLoggingQueue;
  473. @property (nonatomic, readonly, getter=isOnInternalLoggerQueue) BOOL onInternalLoggerQueue;
  474. @end