DDFileLogger.h 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391
  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. // Disable legacy macros
  16. #ifndef DD_LEGACY_MACROS
  17. #define DD_LEGACY_MACROS 0
  18. #endif
  19. #import "DDLog.h"
  20. @class DDLogFileInfo;
  21. /**
  22. * This class provides a logger to write log statements to a file.
  23. **/
  24. // Default configuration and safety/sanity values.
  25. //
  26. // maximumFileSize -> kDDDefaultLogMaxFileSize
  27. // rollingFrequency -> kDDDefaultLogRollingFrequency
  28. // maximumNumberOfLogFiles -> kDDDefaultLogMaxNumLogFiles
  29. // logFilesDiskQuota -> kDDDefaultLogFilesDiskQuota
  30. //
  31. // You should carefully consider the proper configuration values for your application.
  32. extern unsigned long long const kDDDefaultLogMaxFileSize;
  33. extern NSTimeInterval const kDDDefaultLogRollingFrequency;
  34. extern NSUInteger const kDDDefaultLogMaxNumLogFiles;
  35. extern unsigned long long const kDDDefaultLogFilesDiskQuota;
  36. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  37. #pragma mark -
  38. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  39. // The LogFileManager protocol is designed to allow you to control all aspects of your log files.
  40. //
  41. // The primary purpose of this is to allow you to do something with the log files after they have been rolled.
  42. // Perhaps you want to compress them to save disk space.
  43. // Perhaps you want to upload them to an FTP server.
  44. // Perhaps you want to run some analytics on the file.
  45. //
  46. // A default LogFileManager is, of course, provided.
  47. // The default LogFileManager simply deletes old log files according to the maximumNumberOfLogFiles property.
  48. //
  49. // This protocol provides various methods to fetch the list of log files.
  50. //
  51. // There are two variants: sorted and unsorted.
  52. // If sorting is not necessary, the unsorted variant is obviously faster.
  53. // The sorted variant will return an array sorted by when the log files were created,
  54. // with the most recently created log file at index 0, and the oldest log file at the end of the array.
  55. //
  56. // You can fetch only the log file paths (full path including name), log file names (name only),
  57. // or an array of DDLogFileInfo objects.
  58. // The DDLogFileInfo class is documented below, and provides a handy wrapper that
  59. // gives you easy access to various file attributes such as the creation date or the file size.
  60. @protocol DDLogFileManager <NSObject>
  61. @required
  62. // Public properties
  63. /**
  64. * The maximum number of archived log files to keep on disk.
  65. * For example, if this property is set to 3,
  66. * then the LogFileManager will only keep 3 archived log files (plus the current active log file) on disk.
  67. * Once the active log file is rolled/archived, then the oldest of the existing 3 rolled/archived log files is deleted.
  68. *
  69. * You may optionally disable this option by setting it to zero.
  70. **/
  71. @property (readwrite, assign, atomic) NSUInteger maximumNumberOfLogFiles;
  72. /**
  73. * The maximum space that logs can take. On rolling logfile all old logfiles that exceed logFilesDiskQuota will
  74. * be deleted.
  75. *
  76. * You may optionally disable this option by setting it to zero.
  77. **/
  78. @property (readwrite, assign, atomic) unsigned long long logFilesDiskQuota;
  79. // Public methods
  80. - (NSString *)logsDirectory;
  81. - (NSArray *)unsortedLogFilePaths;
  82. - (NSArray *)unsortedLogFileNames;
  83. - (NSArray *)unsortedLogFileInfos;
  84. - (NSArray *)sortedLogFilePaths;
  85. - (NSArray *)sortedLogFileNames;
  86. - (NSArray *)sortedLogFileInfos;
  87. // Private methods (only to be used by DDFileLogger)
  88. - (NSString *)createNewLogFile;
  89. @optional
  90. // Notifications from DDFileLogger
  91. - (void)didArchiveLogFile:(NSString *)logFilePath;
  92. - (void)didRollAndArchiveLogFile:(NSString *)logFilePath;
  93. @end
  94. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  95. #pragma mark -
  96. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  97. /**
  98. * Default log file manager.
  99. *
  100. * All log files are placed inside the logsDirectory.
  101. * If a specific logsDirectory isn't specified, the default directory is used.
  102. * On Mac, this is in ~/Library/Logs/<Application Name>.
  103. * On iPhone, this is in ~/Library/Caches/Logs.
  104. *
  105. * Log files are named "<bundle identifier> <date> <time>.log"
  106. * Example: com.organization.myapp 2013-12-03 17-14.log
  107. *
  108. * Archived log files are automatically deleted according to the maximumNumberOfLogFiles property.
  109. **/
  110. @interface DDLogFileManagerDefault : NSObject <DDLogFileManager>
  111. - (instancetype)init;
  112. - (instancetype)initWithLogsDirectory:(NSString *)logsDirectory NS_DESIGNATED_INITIALIZER;
  113. #if TARGET_OS_IPHONE
  114. /*
  115. * Calling this constructor you can override the default "automagically" chosen NSFileProtection level.
  116. * Useful if you are writing a command line utility / CydiaSubstrate addon for iOS that has no NSBundle
  117. * or like SpringBoard no BackgroundModes key in the NSBundle:
  118. * iPhone:~ root# cycript -p SpringBoard
  119. * cy# [NSBundle mainBundle]
  120. * #"NSBundle </System/Library/CoreServices/SpringBoard.app> (loaded)"
  121. * cy# [[NSBundle mainBundle] objectForInfoDictionaryKey:@"UIBackgroundModes"];
  122. * null
  123. * cy#
  124. **/
  125. - (instancetype)initWithLogsDirectory:(NSString *)logsDirectory defaultFileProtectionLevel:(NSString *)fileProtectionLevel;
  126. #endif
  127. /*
  128. * Methods to override.
  129. *
  130. * Log files are named "<bundle identifier> <date> <time>.log"
  131. * Example: com.organization.myapp 2013-12-03 17-14.log
  132. *
  133. * If you wish to change default filename, you can override following two methods.
  134. * - newLogFileName method would be called on new logfile creation.
  135. * - isLogFile: method would be called to filter logfiles from all other files in logsDirectory.
  136. * You have to parse given filename and return YES if it is logFile.
  137. *
  138. * **NOTE**
  139. * newLogFileName returns filename. If appropriate file already exists, number would be added
  140. * to filename before extension. You have to handle this case in isLogFile: method.
  141. *
  142. * Example:
  143. * - newLogFileName returns "com.organization.myapp 2013-12-03.log",
  144. * file "com.organization.myapp 2013-12-03.log" would be created.
  145. * - after some time "com.organization.myapp 2013-12-03.log" is archived
  146. * - newLogFileName again returns "com.organization.myapp 2013-12-03.log",
  147. * file "com.organization.myapp 2013-12-03 2.log" would be created.
  148. * - after some time "com.organization.myapp 2013-12-03 1.log" is archived
  149. * - newLogFileName again returns "com.organization.myapp 2013-12-03.log",
  150. * file "com.organization.myapp 2013-12-03 3.log" would be created.
  151. **/
  152. @property (readonly, copy) NSString *newLogFileName;
  153. - (BOOL)isLogFile:(NSString *)fileName;
  154. /* Inherited from DDLogFileManager protocol:
  155. @property (readwrite, assign, atomic) NSUInteger maximumNumberOfLogFiles;
  156. @property (readwrite, assign, atomic) NSUInteger logFilesDiskQuota;
  157. - (NSString *)logsDirectory;
  158. - (NSArray *)unsortedLogFilePaths;
  159. - (NSArray *)unsortedLogFileNames;
  160. - (NSArray *)unsortedLogFileInfos;
  161. - (NSArray *)sortedLogFilePaths;
  162. - (NSArray *)sortedLogFileNames;
  163. - (NSArray *)sortedLogFileInfos;
  164. */
  165. @end
  166. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  167. #pragma mark -
  168. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  169. /**
  170. * Most users will want file log messages to be prepended with the date and time.
  171. * Rather than forcing the majority of users to write their own formatter,
  172. * we will supply a logical default formatter.
  173. * Users can easily replace this formatter with their own by invoking the setLogFormatter method.
  174. * It can also be removed by calling setLogFormatter, and passing a nil parameter.
  175. *
  176. * In addition to the convenience of having a logical default formatter,
  177. * it will also provide a template that makes it easy for developers to copy and change.
  178. **/
  179. @interface DDLogFileFormatterDefault : NSObject <DDLogFormatter>
  180. - (instancetype)init;
  181. - (instancetype)initWithDateFormatter:(NSDateFormatter *)dateFormatter NS_DESIGNATED_INITIALIZER;
  182. @end
  183. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  184. #pragma mark -
  185. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  186. @interface DDFileLogger : DDAbstractLogger <DDLogger>
  187. - (instancetype)init;
  188. - (instancetype)initWithLogFileManager:(id <DDLogFileManager>)logFileManager NS_DESIGNATED_INITIALIZER;
  189. /**
  190. * Log File Rolling:
  191. *
  192. * maximumFileSize:
  193. * The approximate maximum size to allow log files to grow.
  194. * If a log file is larger than this value after a log statement is appended,
  195. * then the log file is rolled.
  196. *
  197. * rollingFrequency
  198. * How often to roll the log file.
  199. * The frequency is given as an NSTimeInterval, which is a double that specifies the interval in seconds.
  200. * Once the log file gets to be this old, it is rolled.
  201. *
  202. * Both the maximumFileSize and the rollingFrequency are used to manage rolling.
  203. * Whichever occurs first will cause the log file to be rolled.
  204. *
  205. * For example:
  206. * The rollingFrequency is 24 hours,
  207. * but the log file surpasses the maximumFileSize after only 20 hours.
  208. * The log file will be rolled at that 20 hour mark.
  209. * A new log file will be created, and the 24 hour timer will be restarted.
  210. *
  211. * You may optionally disable rolling due to filesize by setting maximumFileSize to zero.
  212. * If you do so, rolling is based solely on rollingFrequency.
  213. *
  214. * You may optionally disable rolling due to time by setting rollingFrequency to zero (or any non-positive number).
  215. * If you do so, rolling is based solely on maximumFileSize.
  216. *
  217. * If you disable both maximumFileSize and rollingFrequency, then the log file won't ever be rolled.
  218. * This is strongly discouraged.
  219. **/
  220. @property (readwrite, assign) unsigned long long maximumFileSize;
  221. @property (readwrite, assign) NSTimeInterval rollingFrequency;
  222. @property (readwrite, assign, atomic) BOOL doNotReuseLogFiles;
  223. /**
  224. * The DDLogFileManager instance can be used to retrieve the list of log files,
  225. * and configure the maximum number of archived log files to keep.
  226. *
  227. * @see DDLogFileManager.maximumNumberOfLogFiles
  228. **/
  229. @property (strong, nonatomic, readonly) id <DDLogFileManager> logFileManager;
  230. /**
  231. * When using a custom formatter you can set the logMessage method not to append
  232. * '\n' character after each output. This allows for some greater flexibility with
  233. * custom formatters. Default value is YES.
  234. **/
  235. @property (nonatomic, readwrite, assign) BOOL automaticallyAppendNewlineForCustomFormatters;
  236. // You can optionally force the current log file to be rolled with this method.
  237. // CompletionBlock will be called on main queue.
  238. - (void)rollLogFileWithCompletionBlock:(void (^)())completionBlock;
  239. // Method is deprecated. Use rollLogFileWithCompletionBlock: method instead.
  240. - (void)rollLogFile __attribute((deprecated));
  241. // Inherited from DDAbstractLogger
  242. // - (id <DDLogFormatter>)logFormatter;
  243. // - (void)setLogFormatter:(id <DDLogFormatter>)formatter;
  244. /**
  245. * Returns the log file that should be used.
  246. * If there is an existing log file that is suitable,
  247. * within the constraints of maximumFileSize and rollingFrequency, then it is returned.
  248. *
  249. * Otherwise a new file is created and returned.
  250. **/
  251. - (DDLogFileInfo *)currentLogFileInfo;
  252. @end
  253. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  254. #pragma mark -
  255. ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
  256. /**
  257. * DDLogFileInfo is a simple class that provides access to various file attributes.
  258. * It provides good performance as it only fetches the information if requested,
  259. * and it caches the information to prevent duplicate fetches.
  260. *
  261. * It was designed to provide quick snapshots of the current state of log files,
  262. * and to help sort log files in an array.
  263. *
  264. * This class does not monitor the files, or update it's cached attribute values if the file changes on disk.
  265. * This is not what the class was designed for.
  266. *
  267. * If you absolutely must get updated values,
  268. * you can invoke the reset method which will clear the cache.
  269. **/
  270. @interface DDLogFileInfo : NSObject
  271. @property (strong, nonatomic, readonly) NSString *filePath;
  272. @property (strong, nonatomic, readonly) NSString *fileName;
  273. @property (strong, nonatomic, readonly) NSDictionary *fileAttributes;
  274. @property (strong, nonatomic, readonly) NSDate *creationDate;
  275. @property (strong, nonatomic, readonly) NSDate *modificationDate;
  276. @property (nonatomic, readonly) unsigned long long fileSize;
  277. @property (nonatomic, readonly) NSTimeInterval age;
  278. @property (nonatomic, readwrite) BOOL isArchived;
  279. + (instancetype)logFileWithPath:(NSString *)filePath;
  280. - (instancetype)initWithFilePath:(NSString *)filePath;
  281. - (void)reset;
  282. - (void)renameFile:(NSString *)newFileName;
  283. #if TARGET_IPHONE_SIMULATOR
  284. // So here's the situation.
  285. // Extended attributes are perfect for what we're trying to do here (marking files as archived).
  286. // This is exactly what extended attributes were designed for.
  287. //
  288. // But Apple screws us over on the simulator.
  289. // Everytime you build-and-go, they copy the application into a new folder on the hard drive,
  290. // and as part of the process they strip extended attributes from our log files.
  291. // Normally, a copy of a file preserves extended attributes.
  292. // So obviously Apple has gone to great lengths to piss us off.
  293. //
  294. // Thus we use a slightly different tactic for marking log files as archived in the simulator.
  295. // That way it "just works" and there's no confusion when testing.
  296. //
  297. // The difference in method names is indicative of the difference in functionality.
  298. // On the simulator we add an attribute by appending a filename extension.
  299. //
  300. // For example:
  301. // "mylog.txt" -> "mylog.archived.txt"
  302. // "mylog" -> "mylog.archived"
  303. - (BOOL)hasExtensionAttributeWithName:(NSString *)attrName;
  304. - (void)addExtensionAttributeWithName:(NSString *)attrName;
  305. - (void)removeExtensionAttributeWithName:(NSString *)attrName;
  306. #else /* if TARGET_IPHONE_SIMULATOR */
  307. // Normal use of extended attributes used everywhere else,
  308. // such as on Macs and on iPhone devices.
  309. - (BOOL)hasExtendedAttributeWithName:(NSString *)attrName;
  310. - (void)addExtendedAttributeWithName:(NSString *)attrName;
  311. - (void)removeExtendedAttributeWithName:(NSString *)attrName;
  312. #endif /* if TARGET_IPHONE_SIMULATOR */
  313. - (NSComparisonResult)reverseCompareByCreationDate:(DDLogFileInfo *)another;
  314. - (NSComparisonResult)reverseCompareByModificationDate:(DDLogFileInfo *)another;
  315. @end