123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512 |
- // Software License Agreement (BSD License)
- //
- // Copyright (c) 2010-2016, Deusty, LLC
- // All rights reserved.
- //
- // Redistribution and use of this software in source and binary forms,
- // with or without modification, are permitted provided that the following conditions are met:
- //
- // * Redistributions of source code must retain the above copyright notice,
- // this list of conditions and the following disclaimer.
- //
- // * Neither the name of Deusty nor the names of its contributors may be used
- // to endorse or promote products derived from this software without specific
- // prior written permission of Deusty, LLC.
-
- // Disable legacy macros
- #ifndef AWSDD_LEGACY_MACROS
- #define AWSDD_LEGACY_MACROS 0
- #endif
-
- #import "AWSDDLog.h"
-
- @class AWSDDLogFileInfo;
-
- /**
- * This class provides a logger to write log statements to a file.
- **/
-
-
- // Default configuration and safety/sanity values.
- //
- // maximumFileSize -> kAWSDDDefaultLogMaxFileSize
- // rollingFrequency -> kAWSDDDefaultLogRollingFrequency
- // maximumNumberOfLogFiles -> kAWSDDDefaultLogMaxNumLogFiles
- // logFilesDiskQuota -> kAWSDDDefaultLogFilesDiskQuota
- //
- // You should carefully consider the proper configuration values for your application.
-
- extern unsigned long long const kAWSDDDefaultLogMaxFileSize;
- extern NSTimeInterval const kAWSDDDefaultLogRollingFrequency;
- extern NSUInteger const kAWSDDDefaultLogMaxNumLogFiles;
- extern unsigned long long const kAWSDDDefaultLogFilesDiskQuota;
-
-
- ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
- #pragma mark -
- ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
-
- /**
- * The LogFileManager protocol is designed to allow you to control all aspects of your log files.
- *
- * The primary purpose of this is to allow you to do something with the log files after they have been rolled.
- * Perhaps you want to compress them to save disk space.
- * Perhaps you want to upload them to an FTP server.
- * Perhaps you want to run some analytics on the file.
- *
- * A default LogFileManager is, of course, provided.
- * The default LogFileManager simply deletes old log files according to the maximumNumberOfLogFiles property.
- *
- * This protocol provides various methods to fetch the list of log files.
- *
- * There are two variants: sorted and unsorted.
- * If sorting is not necessary, the unsorted variant is obviously faster.
- * The sorted variant will return an array sorted by when the log files were created,
- * with the most recently created log file at index 0, and the oldest log file at the end of the array.
- *
- * You can fetch only the log file paths (full path including name), log file names (name only),
- * or an array of `AWSDDLogFileInfo` objects.
- * The `AWSDDLogFileInfo` class is documented below, and provides a handy wrapper that
- * gives you easy access to various file attributes such as the creation date or the file size.
- */
- @protocol AWSDDLogFileManager <NSObject>
- @required
-
- // Public properties
-
- /**
- * The maximum number of archived log files to keep on disk.
- * For example, if this property is set to 3,
- * then the LogFileManager will only keep 3 archived log files (plus the current active log file) on disk.
- * Once the active log file is rolled/archived, then the oldest of the existing 3 rolled/archived log files is deleted.
- *
- * You may optionally disable this option by setting it to zero.
- **/
- @property (readwrite, assign, atomic) NSUInteger maximumNumberOfLogFiles;
-
- /**
- * The maximum space that logs can take. On rolling logfile all old logfiles that exceed logFilesDiskQuota will
- * be deleted.
- *
- * You may optionally disable this option by setting it to zero.
- **/
- @property (readwrite, assign, atomic) unsigned long long logFilesDiskQuota;
-
- // Public methods
-
- /**
- * Returns the logs directory (path)
- */
- @property (nonatomic, readonly, copy) NSString *logsDirectory;
-
- /**
- * Returns an array of `NSString` objects,
- * each of which is the filePath to an existing log file on disk.
- **/
- @property (nonatomic, readonly, strong) NSArray<NSString *> *unsortedLogFilePaths;
-
- /**
- * Returns an array of `NSString` objects,
- * each of which is the fileName of an existing log file on disk.
- **/
- @property (nonatomic, readonly, strong) NSArray<NSString *> *unsortedLogFileNames;
-
- /**
- * Returns an array of `AWSDDLogFileInfo` objects,
- * each representing an existing log file on disk,
- * and containing important information about the log file such as it's modification date and size.
- **/
- @property (nonatomic, readonly, strong) NSArray<AWSDDLogFileInfo *> *unsortedLogFileInfos;
-
- /**
- * Just like the `unsortedLogFilePaths` method, but sorts the array.
- * The items in the array are sorted by creation date.
- * The first item in the array will be the most recently created log file.
- **/
- @property (nonatomic, readonly, strong) NSArray<NSString *> *sortedLogFilePaths;
-
- /**
- * Just like the `unsortedLogFileNames` method, but sorts the array.
- * The items in the array are sorted by creation date.
- * The first item in the array will be the most recently created log file.
- **/
- @property (nonatomic, readonly, strong) NSArray<NSString *> *sortedLogFileNames;
-
- /**
- * Just like the `unsortedLogFileInfos` method, but sorts the array.
- * The items in the array are sorted by creation date.
- * The first item in the array will be the most recently created log file.
- **/
- @property (nonatomic, readonly, strong) NSArray<AWSDDLogFileInfo *> *sortedLogFileInfos;
-
- // Private methods (only to be used by AWSDDFileLogger)
-
- /**
- * Generates a new unique log file path, and creates the corresponding log file.
- **/
- - (NSString *)createNewLogFile;
-
- @optional
-
- // Notifications from AWSDDFileLogger
-
- /**
- * Called when a log file was archieved
- */
- - (void)didArchiveLogFile:(NSString *)logFilePath NS_SWIFT_NAME(didArchiveLogFile(atPath:));
-
- /**
- * Called when the roll action was executed and the log was archieved
- */
- - (void)didRollAndArchiveLogFile:(NSString *)logFilePath NS_SWIFT_NAME(didRollAndArchiveLogFile(atPath:));
-
- @end
-
- ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
- #pragma mark -
- ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
-
- /**
- * Default log file manager.
- *
- * All log files are placed inside the logsDirectory.
- * If a specific logsDirectory isn't specified, the default directory is used.
- * On Mac, this is in `~/Library/Logs/<Application Name>`.
- * On iPhone, this is in `~/Library/Caches/Logs`.
- *
- * Log files are named `"<bundle identifier> <date> <time>.log"`
- * Example: `com.organization.myapp 2013-12-03 17-14.log`
- *
- * Archived log files are automatically deleted according to the `maximumNumberOfLogFiles` property.
- **/
- @interface AWSDDLogFileManagerDefault : NSObject <AWSDDLogFileManager>
-
- /**
- * Default initializer
- */
- - (instancetype)init;
-
- /**
- * Designated initialized, requires the logs directory
- */
- - (instancetype)initWithLogsDirectory:(NSString *)logsDirectory NS_DESIGNATED_INITIALIZER;
-
- #if TARGET_OS_IPHONE
- /*
- * Calling this constructor you can override the default "automagically" chosen NSFileProtection level.
- * Useful if you are writing a command line utility / CydiaSubstrate addon for iOS that has no NSBundle
- * or like SpringBoard no BackgroundModes key in the NSBundle:
- * iPhone:~ root# cycript -p SpringBoard
- * cy# [NSBundle mainBundle]
- * #"NSBundle </System/Library/CoreServices/SpringBoard.app> (loaded)"
- * cy# [[NSBundle mainBundle] objectForInfoDictionaryKey:@"UIBackgroundModes"];
- * null
- * cy#
- **/
- - (instancetype)initWithLogsDirectory:(NSString *)logsDirectory defaultFileProtectionLevel:(NSString *)fileProtectionLevel;
- #endif
-
- /*
- * Methods to override.
- *
- * Log files are named `"<bundle identifier> <date> <time>.log"`
- * Example: `com.organization.myapp 2013-12-03 17-14.log`
- *
- * If you wish to change default filename, you can override following two methods.
- * - `newLogFileName` method would be called on new logfile creation.
- * - `isLogFile:` method would be called to filter logfiles from all other files in logsDirectory.
- * You have to parse given filename and return YES if it is logFile.
- *
- * **NOTE**
- * `newLogFileName` returns filename. If appropriate file already exists, number would be added
- * to filename before extension. You have to handle this case in isLogFile: method.
- *
- * Example:
- * - newLogFileName returns `"com.organization.myapp 2013-12-03.log"`,
- * file `"com.organization.myapp 2013-12-03.log"` would be created.
- * - after some time `"com.organization.myapp 2013-12-03.log"` is archived
- * - newLogFileName again returns `"com.organization.myapp 2013-12-03.log"`,
- * file `"com.organization.myapp 2013-12-03 2.log"` would be created.
- * - after some time `"com.organization.myapp 2013-12-03 1.log"` is archived
- * - newLogFileName again returns `"com.organization.myapp 2013-12-03.log"`,
- * file `"com.organization.myapp 2013-12-03 3.log"` would be created.
- **/
-
- /**
- * Generates log file name with default format `"<bundle identifier> <date> <time>.log"`
- * Example: `MobileSafari 2013-12-03 17-14.log`
- *
- * You can change it by overriding `newLogFileName` and `isLogFile:` methods.
- **/
- @property (readonly, copy) NSString *newLogFileName;
-
- /**
- * Default log file name is `"<bundle identifier> <date> <time>.log"`.
- * Example: `MobileSafari 2013-12-03 17-14.log`
- *
- * You can change it by overriding `newLogFileName` and `isLogFile:` methods.
- **/
- - (BOOL)isLogFile:(NSString *)fileName NS_SWIFT_NAME(isLogFile(withName:));
-
- /* Inherited from AWSDDLogFileManager protocol:
-
- @property (readwrite, assign, atomic) NSUInteger maximumNumberOfLogFiles;
- @property (readwrite, assign, atomic) NSUInteger logFilesDiskQuota;
-
- - (NSString *)logsDirectory;
-
- - (NSArray *)unsortedLogFilePaths;
- - (NSArray *)unsortedLogFileNames;
- - (NSArray *)unsortedLogFileInfos;
-
- - (NSArray *)sortedLogFilePaths;
- - (NSArray *)sortedLogFileNames;
- - (NSArray *)sortedLogFileInfos;
-
- */
-
- @end
-
- ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
- #pragma mark -
- ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
-
- /**
- * Most users will want file log messages to be prepended with the date and time.
- * Rather than forcing the majority of users to write their own formatter,
- * we will supply a logical default formatter.
- * Users can easily replace this formatter with their own by invoking the `setLogFormatter:` method.
- * It can also be removed by calling `setLogFormatter:`, and passing a nil parameter.
- *
- * In addition to the convenience of having a logical default formatter,
- * it will also provide a template that makes it easy for developers to copy and change.
- **/
- @interface AWSDDLogFileFormatterDefault : NSObject <AWSDDLogFormatter>
-
- /**
- * Default initializer
- */
- - (instancetype)init;
-
- /**
- * Designated initializer, requires a date formatter
- */
- - (instancetype)initWithDateFormatter:(NSDateFormatter *)dateFormatter NS_DESIGNATED_INITIALIZER;
-
- @end
-
- ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
- #pragma mark -
- ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
-
- /**
- * The standard implementation for a file logger
- */
- @interface AWSDDFileLogger : AWSDDAbstractLogger <AWSDDLogger> {
- AWSDDLogFileInfo *_currentLogFileInfo;
- }
-
- /**
- * Default initializer
- */
- - (instancetype)init;
-
- /**
- * Designated initializer, requires a `AWSDDLogFileManager` instance
- */
- - (instancetype)initWithLogFileManager:(id <AWSDDLogFileManager>)logFileManager NS_DESIGNATED_INITIALIZER;
-
- /**
- * Called when the logger is about to write message. Call super before your implementation.
- */
- - (void)willLogMessage NS_REQUIRES_SUPER;
-
- /**
- * Called when the logger wrote message. Call super after your implementation.
- */
- - (void)didLogMessage NS_REQUIRES_SUPER;
-
- /**
- * Called when the logger checks archive or not current log file.
- * Override this method to exdend standart behavior. By default returns NO.
- */
- - (BOOL)shouldArchiveRecentLogFileInfo:(AWSDDLogFileInfo *)recentLogFileInfo;
-
- /**
- * Log File Rolling:
- *
- * `maximumFileSize`:
- * The approximate maximum size (in bytes) to allow log files to grow.
- * If a log file is larger than this value after a log statement is appended,
- * then the log file is rolled.
- *
- * `rollingFrequency`
- * How often to roll the log file.
- * The frequency is given as an `NSTimeInterval`, which is a double that specifies the interval in seconds.
- * Once the log file gets to be this old, it is rolled.
- *
- * `doNotReuseLogFiles`
- * When set, will always create a new log file at application launch.
- *
- * Both the `maximumFileSize` and the `rollingFrequency` are used to manage rolling.
- * Whichever occurs first will cause the log file to be rolled.
- *
- * For example:
- * The `rollingFrequency` is 24 hours,
- * but the log file surpasses the `maximumFileSize` after only 20 hours.
- * The log file will be rolled at that 20 hour mark.
- * A new log file will be created, and the 24 hour timer will be restarted.
- *
- * You may optionally disable rolling due to filesize by setting `maximumFileSize` to zero.
- * If you do so, rolling is based solely on `rollingFrequency`.
- *
- * You may optionally disable rolling due to time by setting `rollingFrequency` to zero (or any non-positive number).
- * If you do so, rolling is based solely on `maximumFileSize`.
- *
- * If you disable both `maximumFileSize` and `rollingFrequency`, then the log file won't ever be rolled.
- * This is strongly discouraged.
- **/
- @property (readwrite, assign) unsigned long long maximumFileSize;
-
- /**
- * See description for `maximumFileSize`
- */
- @property (readwrite, assign) NSTimeInterval rollingFrequency;
-
- /**
- * See description for `maximumFileSize`
- */
- @property (readwrite, assign, atomic) BOOL doNotReuseLogFiles;
-
- /**
- * The AWSDDLogFileManager instance can be used to retrieve the list of log files,
- * and configure the maximum number of archived log files to keep.
- *
- * @see AWSDDLogFileManager.maximumNumberOfLogFiles
- **/
- @property (strong, nonatomic, readonly) id <AWSDDLogFileManager> logFileManager;
-
- /**
- * When using a custom formatter you can set the `logMessage` method not to append
- * `\n` character after each output. This allows for some greater flexibility with
- * custom formatters. Default value is YES.
- **/
- @property (nonatomic, readwrite, assign) BOOL automaticallyAppendNewlineForCustomFormatters;
-
- /**
- * You can optionally force the current log file to be rolled with this method.
- * CompletionBlock will be called on main queue.
- */
- - (void)rollLogFileWithCompletionBlock:(void (^)(void))completionBlock NS_SWIFT_NAME(rollLogFile(withCompletion:));
-
- /**
- * Method is deprecated.
- * @deprecated Use `rollLogFileWithCompletionBlock:` method instead.
- */
- - (void)rollLogFile __attribute((deprecated));
-
- // Inherited from AWSDDAbstractLogger
-
- // - (id <AWSDDLogFormatter>)logFormatter;
- // - (void)setLogFormatter:(id <AWSDDLogFormatter>)formatter;
-
- /**
- * Returns the log file that should be used.
- * If there is an existing log file that is suitable,
- * within the constraints of `maximumFileSize` and `rollingFrequency`, then it is returned.
- *
- * Otherwise a new file is created and returned.
- **/
- @property (nonatomic, readonly, strong) AWSDDLogFileInfo *currentLogFileInfo;
-
- @end
-
- ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
- #pragma mark -
- ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
-
- /**
- * `AWSDDLogFileInfo` is a simple class that provides access to various file attributes.
- * It provides good performance as it only fetches the information if requested,
- * and it caches the information to prevent duplicate fetches.
- *
- * It was designed to provide quick snapshots of the current state of log files,
- * and to help sort log files in an array.
- *
- * This class does not monitor the files, or update it's cached attribute values if the file changes on disk.
- * This is not what the class was designed for.
- *
- * If you absolutely must get updated values,
- * you can invoke the reset method which will clear the cache.
- **/
- @interface AWSDDLogFileInfo : NSObject
-
- @property (strong, nonatomic, readonly) NSString *filePath;
- @property (strong, nonatomic, readonly) NSString *fileName;
-
- #if FOUNDATION_SWIFT_SDK_EPOCH_AT_LEAST(8)
- @property (strong, nonatomic, readonly) NSDictionary<NSFileAttributeKey, id> *fileAttributes;
- #else
- @property (strong, nonatomic, readonly) NSDictionary<NSString *, id> *fileAttributes;
- #endif
-
- @property (strong, nonatomic, readonly) NSDate *creationDate;
- @property (strong, nonatomic, readonly) NSDate *modificationDate;
-
- @property (nonatomic, readonly) unsigned long long fileSize;
-
- @property (nonatomic, readonly) NSTimeInterval age;
-
- @property (nonatomic, readwrite) BOOL isArchived;
-
- + (instancetype)logFileWithPath:(NSString *)filePath NS_SWIFT_UNAVAILABLE("Use init(filePath:)");
-
- - (instancetype)init NS_UNAVAILABLE;
- - (instancetype)initWithFilePath:(NSString *)filePath NS_DESIGNATED_INITIALIZER;
-
- - (void)reset;
- - (void)renameFile:(NSString *)newFileName NS_SWIFT_NAME(renameFile(to:));
-
- #if TARGET_IPHONE_SIMULATOR
-
- // So here's the situation.
- // Extended attributes are perfect for what we're trying to do here (marking files as archived).
- // This is exactly what extended attributes were designed for.
- //
- // But Apple screws us over on the simulator.
- // Everytime you build-and-go, they copy the application into a new folder on the hard drive,
- // and as part of the process they strip extended attributes from our log files.
- // Normally, a copy of a file preserves extended attributes.
- // So obviously Apple has gone to great lengths to piss us off.
- //
- // Thus we use a slightly different tactic for marking log files as archived in the simulator.
- // That way it "just works" and there's no confusion when testing.
- //
- // The difference in method names is indicative of the difference in functionality.
- // On the simulator we add an attribute by appending a filename extension.
- //
- // For example:
- // "mylog.txt" -> "mylog.archived.txt"
- // "mylog" -> "mylog.archived"
-
- - (BOOL)hasExtensionAttributeWithName:(NSString *)attrName;
-
- - (void)addExtensionAttributeWithName:(NSString *)attrName;
- - (void)removeExtensionAttributeWithName:(NSString *)attrName;
-
- #else /* if TARGET_IPHONE_SIMULATOR */
-
- // Normal use of extended attributes used everywhere else,
- // such as on Macs and on iPhone devices.
-
- - (BOOL)hasExtendedAttributeWithName:(NSString *)attrName;
-
- - (void)addExtendedAttributeWithName:(NSString *)attrName;
- - (void)removeExtendedAttributeWithName:(NSString *)attrName;
-
- #endif /* if TARGET_IPHONE_SIMULATOR */
-
- - (NSComparisonResult)reverseCompareByCreationDate:(AWSDDLogFileInfo *)another;
- - (NSComparisonResult)reverseCompareByModificationDate:(AWSDDLogFileInfo *)another;
-
- @end
|