DDFileLogger.h 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335
  1. #import <Foundation/Foundation.h>
  2. #import "DDLog.h"
  3. @class DDLogFileInfo;
  4. /**
  5. * Welcome to Cocoa Lumberjack!
  6. *
  7. * The project page has a wealth of documentation if you have any questions.
  8. * https://github.com/robbiehanson/CocoaLumberjack
  9. *
  10. * If you're new to the project you may wish to read the "Getting Started" wiki.
  11. * https://github.com/robbiehanson/CocoaLumberjack/wiki/GettingStarted
  12. *
  13. *
  14. * This class provides a logger to write log statements to a file.
  15. **/
  16. // Default configuration and safety/sanity values.
  17. //
  18. // maximumFileSize -> DEFAULT_LOG_MAX_FILE_SIZE
  19. // rollingFrequency -> DEFAULT_LOG_ROLLING_FREQUENCY
  20. // maximumNumberOfLogFiles -> DEFAULT_LOG_MAX_NUM_LOG_FILES
  21. //
  22. // You should carefully consider the proper configuration values for your application.
  23. #define DEFAULT_LOG_MAX_FILE_SIZE (1024 * 1024) // 1 MB
  24. #define DEFAULT_LOG_ROLLING_FREQUENCY (60 * 60 * 24) // 24 Hours
  25. #define DEFAULT_LOG_MAX_NUM_LOG_FILES (5) // 5 Files
  26. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  27. #pragma mark -
  28. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  29. // The LogFileManager protocol is designed to allow you to control all aspects of your log files.
  30. //
  31. // The primary purpose of this is to allow you to do something with the log files after they have been rolled.
  32. // Perhaps you want to compress them to save disk space.
  33. // Perhaps you want to upload them to an FTP server.
  34. // Perhaps you want to run some analytics on the file.
  35. //
  36. // A default LogFileManager is, of course, provided.
  37. // The default LogFileManager simply deletes old log files according to the maximumNumberOfLogFiles property.
  38. //
  39. // This protocol provides various methods to fetch the list of log files.
  40. //
  41. // There are two variants: sorted and unsorted.
  42. // If sorting is not necessary, the unsorted variant is obviously faster.
  43. // The sorted variant will return an array sorted by when the log files were created,
  44. // with the most recently created log file at index 0, and the oldest log file at the end of the array.
  45. //
  46. // You can fetch only the log file paths (full path including name), log file names (name only),
  47. // or an array of DDLogFileInfo objects.
  48. // The DDLogFileInfo class is documented below, and provides a handy wrapper that
  49. // gives you easy access to various file attributes such as the creation date or the file size.
  50. @protocol DDLogFileManager <NSObject>
  51. @required
  52. // Public properties
  53. /**
  54. * The maximum number of archived log files to keep on disk.
  55. * For example, if this property is set to 3,
  56. * then the LogFileManager will only keep 3 archived log files (plus the current active log file) on disk.
  57. * Once the active log file is rolled/archived, then the oldest of the existing 3 rolled/archived log files is deleted.
  58. *
  59. * You may optionally disable deleting old/rolled/archived log files by setting this property to zero.
  60. **/
  61. @property (readwrite, assign) NSUInteger maximumNumberOfLogFiles;
  62. // Public methods
  63. - (NSString *)logsDirectory;
  64. - (NSArray *)unsortedLogFilePaths;
  65. - (NSArray *)unsortedLogFileNames;
  66. - (NSArray *)unsortedLogFileInfos;
  67. - (NSArray *)sortedLogFilePaths;
  68. - (NSArray *)sortedLogFileNames;
  69. - (NSArray *)sortedLogFileInfos;
  70. // Private methods (only to be used by DDFileLogger)
  71. - (NSString *)createNewLogFile;
  72. @optional
  73. // Notifications from DDFileLogger
  74. - (void)didArchiveLogFile:(NSString *)logFilePath;
  75. - (void)didRollAndArchiveLogFile:(NSString *)logFilePath;
  76. @end
  77. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  78. #pragma mark -
  79. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  80. /**
  81. * Default log file manager.
  82. *
  83. * All log files are placed inside the logsDirectory.
  84. * If a specific logsDirectory isn't specified, the default directory is used.
  85. * On Mac, this is in ~/Library/Logs/<Application Name>.
  86. * On iPhone, this is in ~/Library/Caches/Logs.
  87. *
  88. * Log files are named "log-<uuid>.txt",
  89. * where uuid is a 6 character hexadecimal consisting of the set [0123456789ABCDEF].
  90. *
  91. * Archived log files are automatically deleted according to the maximumNumberOfLogFiles property.
  92. **/
  93. @interface DDLogFileManagerDefault : NSObject <DDLogFileManager>
  94. {
  95. NSUInteger maximumNumberOfLogFiles;
  96. NSString *_logsDirectory;
  97. }
  98. - (id)init;
  99. - (id)initWithLogsDirectory:(NSString *)logsDirectory;
  100. /* Inherited from DDLogFileManager protocol:
  101. @property (readwrite, assign) NSUInteger maximumNumberOfLogFiles;
  102. - (NSString *)logsDirectory;
  103. - (NSArray *)unsortedLogFilePaths;
  104. - (NSArray *)unsortedLogFileNames;
  105. - (NSArray *)unsortedLogFileInfos;
  106. - (NSArray *)sortedLogFilePaths;
  107. - (NSArray *)sortedLogFileNames;
  108. - (NSArray *)sortedLogFileInfos;
  109. */
  110. @end
  111. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  112. #pragma mark -
  113. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  114. /**
  115. * Most users will want file log messages to be prepended with the date and time.
  116. * Rather than forcing the majority of users to write their own formatter,
  117. * we will supply a logical default formatter.
  118. * Users can easily replace this formatter with their own by invoking the setLogFormatter method.
  119. * It can also be removed by calling setLogFormatter, and passing a nil parameter.
  120. *
  121. * In addition to the convenience of having a logical default formatter,
  122. * it will also provide a template that makes it easy for developers to copy and change.
  123. **/
  124. @interface DDLogFileFormatterDefault : NSObject <DDLogFormatter>
  125. {
  126. NSDateFormatter *dateFormatter;
  127. }
  128. - (id)init;
  129. - (id)initWithDateFormatter:(NSDateFormatter *)dateFormatter;
  130. @end
  131. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  132. #pragma mark -
  133. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  134. @interface DDFileLogger : DDAbstractLogger <DDLogger>
  135. {
  136. __strong id <DDLogFileManager> logFileManager;
  137. DDLogFileInfo *currentLogFileInfo;
  138. NSFileHandle *currentLogFileHandle;
  139. dispatch_source_t rollingTimer;
  140. unsigned long long maximumFileSize;
  141. NSTimeInterval rollingFrequency;
  142. }
  143. - (id)init;
  144. - (id)initWithLogFileManager:(id <DDLogFileManager>)logFileManager;
  145. /**
  146. * Log File Rolling:
  147. *
  148. * maximumFileSize:
  149. * The approximate maximum size to allow log files to grow.
  150. * If a log file is larger than this value after a log statement is appended,
  151. * then the log file is rolled.
  152. *
  153. * rollingFrequency
  154. * How often to roll the log file.
  155. * The frequency is given as an NSTimeInterval, which is a double that specifies the interval in seconds.
  156. * Once the log file gets to be this old, it is rolled.
  157. *
  158. * Both the maximumFileSize and the rollingFrequency are used to manage rolling.
  159. * Whichever occurs first will cause the log file to be rolled.
  160. *
  161. * For example:
  162. * The rollingFrequency is 24 hours,
  163. * but the log file surpasses the maximumFileSize after only 20 hours.
  164. * The log file will be rolled at that 20 hour mark.
  165. * A new log file will be created, and the 24 hour timer will be restarted.
  166. *
  167. * You may optionally disable rolling due to filesize by setting maximumFileSize to zero.
  168. * If you do so, rolling is based solely on rollingFrequency.
  169. *
  170. * You may optionally disable rolling due to time by setting rollingFrequency to zero (or any non-positive number).
  171. * If you do so, rolling is based solely on maximumFileSize.
  172. *
  173. * If you disable both maximumFileSize and rollingFrequency, then the log file won't ever be rolled.
  174. * This is strongly discouraged.
  175. **/
  176. @property (readwrite, assign) unsigned long long maximumFileSize;
  177. @property (readwrite, assign) NSTimeInterval rollingFrequency;
  178. /**
  179. * The DDLogFileManager instance can be used to retrieve the list of log files,
  180. * and configure the maximum number of archived log files to keep.
  181. *
  182. * @see DDLogFileManager.maximumNumberOfLogFiles
  183. **/
  184. @property (strong, nonatomic, readonly) id <DDLogFileManager> logFileManager;
  185. // You can optionally force the current log file to be rolled with this method.
  186. - (void)rollLogFile;
  187. // Inherited from DDAbstractLogger
  188. // - (id <DDLogFormatter>)logFormatter;
  189. // - (void)setLogFormatter:(id <DDLogFormatter>)formatter;
  190. @end
  191. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  192. #pragma mark -
  193. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  194. /**
  195. * DDLogFileInfo is a simple class that provides access to various file attributes.
  196. * It provides good performance as it only fetches the information if requested,
  197. * and it caches the information to prevent duplicate fetches.
  198. *
  199. * It was designed to provide quick snapshots of the current state of log files,
  200. * and to help sort log files in an array.
  201. *
  202. * This class does not monitor the files, or update it's cached attribute values if the file changes on disk.
  203. * This is not what the class was designed for.
  204. *
  205. * If you absolutely must get updated values,
  206. * you can invoke the reset method which will clear the cache.
  207. **/
  208. @interface DDLogFileInfo : NSObject
  209. {
  210. __strong NSString *filePath;
  211. __strong NSString *fileName;
  212. __strong NSDictionary *fileAttributes;
  213. __strong NSDate *creationDate;
  214. __strong NSDate *modificationDate;
  215. unsigned long long fileSize;
  216. }
  217. @property (strong, nonatomic, readonly) NSString *filePath;
  218. @property (strong, nonatomic, readonly) NSString *fileName;
  219. @property (strong, nonatomic, readonly) NSDictionary *fileAttributes;
  220. @property (strong, nonatomic, readonly) NSDate *creationDate;
  221. @property (strong, nonatomic, readonly) NSDate *modificationDate;
  222. @property (nonatomic, readonly) unsigned long long fileSize;
  223. @property (nonatomic, readonly) NSTimeInterval age;
  224. @property (nonatomic, readwrite) BOOL isArchived;
  225. + (id)logFileWithPath:(NSString *)filePath;
  226. - (id)initWithFilePath:(NSString *)filePath;
  227. - (void)reset;
  228. - (void)renameFile:(NSString *)newFileName;
  229. #if TARGET_IPHONE_SIMULATOR
  230. // So here's the situation.
  231. // Extended attributes are perfect for what we're trying to do here (marking files as archived).
  232. // This is exactly what extended attributes were designed for.
  233. //
  234. // But Apple screws us over on the simulator.
  235. // Everytime you build-and-go, they copy the application into a new folder on the hard drive,
  236. // and as part of the process they strip extended attributes from our log files.
  237. // Normally, a copy of a file preserves extended attributes.
  238. // So obviously Apple has gone to great lengths to piss us off.
  239. //
  240. // Thus we use a slightly different tactic for marking log files as archived in the simulator.
  241. // That way it "just works" and there's no confusion when testing.
  242. //
  243. // The difference in method names is indicative of the difference in functionality.
  244. // On the simulator we add an attribute by appending a filename extension.
  245. //
  246. // For example:
  247. // log-ABC123.txt -> log-ABC123.archived.txt
  248. - (BOOL)hasExtensionAttributeWithName:(NSString *)attrName;
  249. - (void)addExtensionAttributeWithName:(NSString *)attrName;
  250. - (void)removeExtensionAttributeWithName:(NSString *)attrName;
  251. #else
  252. // Normal use of extended attributes used everywhere else,
  253. // such as on Macs and on iPhone devices.
  254. - (BOOL)hasExtendedAttributeWithName:(NSString *)attrName;
  255. - (void)addExtendedAttributeWithName:(NSString *)attrName;
  256. - (void)removeExtendedAttributeWithName:(NSString *)attrName;
  257. #endif
  258. - (NSComparisonResult)reverseCompareByCreationDate:(DDLogFileInfo *)another;
  259. - (NSComparisonResult)reverseCompareByModificationDate:(DDLogFileInfo *)another;
  260. @end