motion-logger 0.1.3 → 0.1.4

Sign up to get free protection for your applications and to get access to all the features.
Files changed (35) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +2 -0
  3. data/README.md +3 -1
  4. data/Rakefile +2 -4
  5. data/lib/logger/version.rb +1 -1
  6. data/motion-logger.gemspec +3 -3
  7. metadata +28 -51
  8. data/vendor/Podfile.lock +0 -5
  9. data/vendor/Pods/CocoaLumberjack/.gitignore +0 -24
  10. data/vendor/Pods/CocoaLumberjack/.hgignore +0 -6
  11. data/vendor/Pods/CocoaLumberjack/CocoaLumberjack.podspec +0 -18
  12. data/vendor/Pods/CocoaLumberjack/LICENSE.txt +0 -18
  13. data/vendor/Pods/CocoaLumberjack/Lumberjack/DDASLLogger.h +0 -41
  14. data/vendor/Pods/CocoaLumberjack/Lumberjack/DDASLLogger.m +0 -99
  15. data/vendor/Pods/CocoaLumberjack/Lumberjack/DDAbstractDatabaseLogger.h +0 -102
  16. data/vendor/Pods/CocoaLumberjack/Lumberjack/DDAbstractDatabaseLogger.m +0 -618
  17. data/vendor/Pods/CocoaLumberjack/Lumberjack/DDFileLogger.h +0 -334
  18. data/vendor/Pods/CocoaLumberjack/Lumberjack/DDFileLogger.m +0 -1346
  19. data/vendor/Pods/CocoaLumberjack/Lumberjack/DDLog.h +0 -498
  20. data/vendor/Pods/CocoaLumberjack/Lumberjack/DDLog.m +0 -979
  21. data/vendor/Pods/CocoaLumberjack/Lumberjack/DDTTYLogger.h +0 -49
  22. data/vendor/Pods/CocoaLumberjack/Lumberjack/DDTTYLogger.m +0 -186
  23. data/vendor/Pods/CocoaLumberjack/README.markdown +0 -37
  24. data/vendor/Pods/Headers/CocoaLumberjack/DDASLLogger.h +0 -41
  25. data/vendor/Pods/Headers/CocoaLumberjack/DDAbstractDatabaseLogger.h +0 -102
  26. data/vendor/Pods/Headers/CocoaLumberjack/DDFileLogger.h +0 -334
  27. data/vendor/Pods/Headers/CocoaLumberjack/DDLog.h +0 -498
  28. data/vendor/Pods/Headers/CocoaLumberjack/DDTTYLogger.h +0 -49
  29. data/vendor/Pods/Pods-Acknowledgements.markdown +0 -24
  30. data/vendor/Pods/Pods-Acknowledgements.plist +0 -54
  31. data/vendor/Pods/Pods-prefix.pch +0 -3
  32. data/vendor/Pods/Pods-resources.sh +0 -19
  33. data/vendor/Pods/Pods.bridgesupport +0 -462
  34. data/vendor/Pods/Pods.xcconfig +0 -4
  35. data/vendor/Pods/PodsDummy_Pods.m +0 -4
@@ -1,334 +0,0 @@
1
- #import <Foundation/Foundation.h>
2
- #import "DDLog.h"
3
-
4
- @class DDLogFileInfo;
5
-
6
- /**
7
- * Welcome to Cocoa Lumberjack!
8
- *
9
- * The project page has a wealth of documentation if you have any questions.
10
- * https://github.com/robbiehanson/CocoaLumberjack
11
- *
12
- * If you're new to the project you may wish to read the "Getting Started" wiki.
13
- * https://github.com/robbiehanson/CocoaLumberjack/wiki/GettingStarted
14
- *
15
- *
16
- * This class provides a logger to write log statements to a file.
17
- **/
18
-
19
-
20
- // Default configuration and safety/sanity values.
21
- //
22
- // maximumFileSize -> DEFAULT_LOG_MAX_FILE_SIZE
23
- // rollingFrequency -> DEFAULT_LOG_ROLLING_FREQUENCY
24
- // maximumNumberOfLogFiles -> DEFAULT_LOG_MAX_NUM_LOG_FILES
25
- //
26
- // You should carefully consider the proper configuration values for your application.
27
-
28
- #define DEFAULT_LOG_MAX_FILE_SIZE (1024 * 1024) // 1 MB
29
- #define DEFAULT_LOG_ROLLING_FREQUENCY (60 * 60 * 24) // 24 Hours
30
- #define DEFAULT_LOG_MAX_NUM_LOG_FILES (5) // 5 Files
31
-
32
-
33
- ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
34
- #pragma mark -
35
- ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
36
-
37
- // The LogFileManager protocol is designed to allow you to control all aspects of your log files.
38
- //
39
- // The primary purpose of this is to allow you to do something with the log files after they have been rolled.
40
- // Perhaps you want to compress them to save disk space.
41
- // Perhaps you want to upload them to an FTP server.
42
- // Perhaps you want to run some analytics on the file.
43
- //
44
- // A default LogFileManager is, of course, provided.
45
- // The default LogFileManager simply deletes old log files according to the maximumNumberOfLogFiles property.
46
- //
47
- // This protocol provides various methods to fetch the list of log files.
48
- //
49
- // There are two variants: sorted and unsorted.
50
- // If sorting is not necessary, the unsorted variant is obviously faster.
51
- // The sorted variant will return an array sorted by when the log files were created,
52
- // with the most recently created log file at index 0, and the oldest log file at the end of the array.
53
- //
54
- // You can fetch only the log file paths (full path including name), log file names (name only),
55
- // or an array of DDLogFileInfo objects.
56
- // The DDLogFileInfo class is documented below, and provides a handy wrapper that
57
- // gives you easy access to various file attributes such as the creation date or the file size.
58
-
59
- @protocol DDLogFileManager <NSObject>
60
- @required
61
-
62
- // Public properties
63
-
64
- /**
65
- * The maximum number of archived log files to keep on disk.
66
- * For example, if this property is set to 3,
67
- * then the LogFileManager will only keep 3 archived log files (plus the current active log file) on disk.
68
- * Once the active log file is rolled/archived, then the oldest of the existing 3 rolled/archived log files is deleted.
69
- *
70
- * You may optionally disable deleting old/rolled/archived log files by setting this property to zero.
71
- **/
72
- @property (readwrite, assign) NSUInteger maximumNumberOfLogFiles;
73
-
74
- // Public methods
75
-
76
- - (NSString *)logsDirectory;
77
-
78
- - (NSArray *)unsortedLogFilePaths;
79
- - (NSArray *)unsortedLogFileNames;
80
- - (NSArray *)unsortedLogFileInfos;
81
-
82
- - (NSArray *)sortedLogFilePaths;
83
- - (NSArray *)sortedLogFileNames;
84
- - (NSArray *)sortedLogFileInfos;
85
-
86
- // Private methods (only to be used by DDFileLogger)
87
-
88
- - (NSString *)createNewLogFile;
89
-
90
- @optional
91
-
92
- // Notifications from DDFileLogger
93
-
94
- - (void)didArchiveLogFile:(NSString *)logFilePath;
95
- - (void)didRollAndArchiveLogFile:(NSString *)logFilePath;
96
-
97
- @end
98
-
99
- ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
100
- #pragma mark -
101
- ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
102
-
103
- /**
104
- * Default log file manager.
105
- *
106
- * All log files are placed inside the logsDirectory.
107
- * If a specific logsDirectory isn't specified, the default directory is used.
108
- * On Mac, this is in ~/Library/Logs/<Application Name>.
109
- * On iPhone, this is in ~/Library/Caches/Logs.
110
- *
111
- * Log files are named "log-<uuid>.txt",
112
- * where uuid is a 6 character hexadecimal consisting of the set [0123456789ABCDEF].
113
- *
114
- * Archived log files are automatically deleted according to the maximumNumberOfLogFiles property.
115
- **/
116
- @interface DDLogFileManagerDefault : NSObject <DDLogFileManager>
117
- {
118
- NSUInteger maximumNumberOfLogFiles;
119
- NSString *_logsDirectory;
120
- }
121
-
122
- - (id)init;
123
- - (id)initWithLogsDirectory:(NSString *)logsDirectory;
124
-
125
- /* Inherited from DDLogFileManager protocol:
126
-
127
- @property (readwrite, assign) NSUInteger maximumNumberOfLogFiles;
128
-
129
- - (NSString *)logsDirectory;
130
-
131
- - (NSArray *)unsortedLogFilePaths;
132
- - (NSArray *)unsortedLogFileNames;
133
- - (NSArray *)unsortedLogFileInfos;
134
-
135
- - (NSArray *)sortedLogFilePaths;
136
- - (NSArray *)sortedLogFileNames;
137
- - (NSArray *)sortedLogFileInfos;
138
-
139
- */
140
-
141
- @end
142
-
143
- ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
144
- #pragma mark -
145
- ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
146
-
147
- /**
148
- * Most users will want file log messages to be prepended with the date and time.
149
- * Rather than forcing the majority of users to write their own formatter,
150
- * we will supply a logical default formatter.
151
- * Users can easily replace this formatter with their own by invoking the setLogFormatter method.
152
- * It can also be removed by calling setLogFormatter, and passing a nil parameter.
153
- *
154
- * In addition to the convenience of having a logical default formatter,
155
- * it will also provide a template that makes it easy for developers to copy and change.
156
- **/
157
- @interface DDLogFileFormatterDefault : NSObject <DDLogFormatter>
158
- {
159
- NSDateFormatter *dateFormatter;
160
- }
161
-
162
- - (id)init;
163
- - (id)initWithDateFormatter:(NSDateFormatter *)dateFormatter;
164
-
165
- @end
166
-
167
- ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
168
- #pragma mark -
169
- ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
170
-
171
- @interface DDFileLogger : DDAbstractLogger <DDLogger>
172
- {
173
- __strong id <DDLogFileManager> logFileManager;
174
-
175
- DDLogFileInfo *currentLogFileInfo;
176
- NSFileHandle *currentLogFileHandle;
177
-
178
- dispatch_source_t rollingTimer;
179
-
180
- unsigned long long maximumFileSize;
181
- NSTimeInterval rollingFrequency;
182
- }
183
-
184
- - (id)init;
185
- - (id)initWithLogFileManager:(id <DDLogFileManager>)logFileManager;
186
-
187
- /**
188
- * Log File Rolling:
189
- *
190
- * maximumFileSize:
191
- * The approximate maximum size to allow log files to grow.
192
- * If a log file is larger than this value after a log statement is appended,
193
- * then the log file is rolled.
194
- *
195
- * rollingFrequency
196
- * How often to roll the log file.
197
- * The frequency is given as an NSTimeInterval, which is a double that specifies the interval in seconds.
198
- * Once the log file gets to be this old, it is rolled.
199
- *
200
- * Both the maximumFileSize and the rollingFrequency are used to manage rolling.
201
- * Whichever occurs first will cause the log file to be rolled.
202
- *
203
- * For example:
204
- * The rollingFrequency is 24 hours,
205
- * but the log file surpasses the maximumFileSize after only 20 hours.
206
- * The log file will be rolled at that 20 hour mark.
207
- * A new log file will be created, and the 24 hour timer will be restarted.
208
- *
209
- * You may optionally disable rolling due to filesize by setting maximumFileSize to zero.
210
- * If you do so, rolling is based solely on rollingFrequency.
211
- *
212
- * You may optionally disable rolling due to time by setting rollingFrequency to zero (or any non-positive number).
213
- * If you do so, rolling is based solely on maximumFileSize.
214
- *
215
- * If you disable both maximumFileSize and rollingFrequency, then the log file won't ever be rolled.
216
- * This is strongly discouraged.
217
- **/
218
- @property (readwrite, assign) unsigned long long maximumFileSize;
219
- @property (readwrite, assign) NSTimeInterval rollingFrequency;
220
-
221
- /**
222
- * The DDLogFileManager instance can be used to retrieve the list of log files,
223
- * and configure the maximum number of archived log files to keep.
224
- *
225
- * @see DDLogFileManager.maximumNumberOfLogFiles
226
- **/
227
- @property (strong, nonatomic, readonly) id <DDLogFileManager> logFileManager;
228
-
229
-
230
- // You can optionally force the current log file to be rolled with this method.
231
-
232
- - (void)rollLogFile;
233
-
234
- // Inherited from DDAbstractLogger
235
-
236
- // - (id <DDLogFormatter>)logFormatter;
237
- // - (void)setLogFormatter:(id <DDLogFormatter>)formatter;
238
-
239
- @end
240
-
241
- ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
242
- #pragma mark -
243
- ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
244
-
245
- /**
246
- * DDLogFileInfo is a simple class that provides access to various file attributes.
247
- * It provides good performance as it only fetches the information if requested,
248
- * and it caches the information to prevent duplicate fetches.
249
- *
250
- * It was designed to provide quick snapshots of the current state of log files,
251
- * and to help sort log files in an array.
252
- *
253
- * This class does not monitor the files, or update it's cached attribute values if the file changes on disk.
254
- * This is not what the class was designed for.
255
- *
256
- * If you absolutely must get updated values,
257
- * you can invoke the reset method which will clear the cache.
258
- **/
259
- @interface DDLogFileInfo : NSObject
260
- {
261
- __strong NSString *filePath;
262
- __strong NSString *fileName;
263
-
264
- __strong NSDictionary *fileAttributes;
265
-
266
- __strong NSDate *creationDate;
267
- __strong NSDate *modificationDate;
268
-
269
- unsigned long long fileSize;
270
- }
271
-
272
- @property (strong, nonatomic, readonly) NSString *filePath;
273
- @property (strong, nonatomic, readonly) NSString *fileName;
274
-
275
- @property (strong, nonatomic, readonly) NSDictionary *fileAttributes;
276
-
277
- @property (strong, nonatomic, readonly) NSDate *creationDate;
278
- @property (strong, nonatomic, readonly) NSDate *modificationDate;
279
-
280
- @property (nonatomic, readonly) unsigned long long fileSize;
281
-
282
- @property (nonatomic, readonly) NSTimeInterval age;
283
-
284
- @property (nonatomic, readwrite) BOOL isArchived;
285
-
286
- + (id)logFileWithPath:(NSString *)filePath;
287
-
288
- - (id)initWithFilePath:(NSString *)filePath;
289
-
290
- - (void)reset;
291
- - (void)renameFile:(NSString *)newFileName;
292
-
293
- #if TARGET_IPHONE_SIMULATOR
294
-
295
- // So here's the situation.
296
- // Extended attributes are perfect for what we're trying to do here (marking files as archived).
297
- // This is exactly what extended attributes were designed for.
298
- //
299
- // But Apple screws us over on the simulator.
300
- // Everytime you build-and-go, they copy the application into a new folder on the hard drive,
301
- // and as part of the process they strip extended attributes from our log files.
302
- // Normally, a copy of a file preserves extended attributes.
303
- // So obviously Apple has gone to great lengths to piss us off.
304
- //
305
- // Thus we use a slightly different tactic for marking log files as archived in the simulator.
306
- // That way it "just works" and there's no confusion when testing.
307
- //
308
- // The difference in method names is indicative of the difference in functionality.
309
- // On the simulator we add an attribute by appending a filename extension.
310
- //
311
- // For example:
312
- // log-ABC123.txt -> log-ABC123.archived.txt
313
-
314
- - (BOOL)hasExtensionAttributeWithName:(NSString *)attrName;
315
-
316
- - (void)addExtensionAttributeWithName:(NSString *)attrName;
317
- - (void)removeExtensionAttributeWithName:(NSString *)attrName;
318
-
319
- #else
320
-
321
- // Normal use of extended attributes used everywhere else,
322
- // such as on Macs and on iPhone devices.
323
-
324
- - (BOOL)hasExtendedAttributeWithName:(NSString *)attrName;
325
-
326
- - (void)addExtendedAttributeWithName:(NSString *)attrName;
327
- - (void)removeExtendedAttributeWithName:(NSString *)attrName;
328
-
329
- #endif
330
-
331
- - (NSComparisonResult)reverseCompareByCreationDate:(DDLogFileInfo *)another;
332
- - (NSComparisonResult)reverseCompareByModificationDate:(DDLogFileInfo *)another;
333
-
334
- @end
@@ -1,1346 +0,0 @@
1
- #import "DDFileLogger.h"
2
-
3
- #import <unistd.h>
4
- #import <sys/attr.h>
5
- #import <sys/xattr.h>
6
- #import <libkern/OSAtomic.h>
7
-
8
- /**
9
- * Welcome to Cocoa Lumberjack!
10
- *
11
- * The project page has a wealth of documentation if you have any questions.
12
- * https://github.com/robbiehanson/CocoaLumberjack
13
- *
14
- * If you're new to the project you may wish to read the "Getting Started" wiki.
15
- * https://github.com/robbiehanson/CocoaLumberjack/wiki/GettingStarted
16
- **/
17
-
18
- #if ! __has_feature(objc_arc)
19
- #warning This file must be compiled with ARC. Use -fobjc-arc flag (or convert project to ARC).
20
- #endif
21
-
22
- // We probably shouldn't be using DDLog() statements within the DDLog implementation.
23
- // But we still want to leave our log statements for any future debugging,
24
- // and to allow other developers to trace the implementation (which is a great learning tool).
25
- //
26
- // So we use primitive logging macros around NSLog.
27
- // We maintain the NS prefix on the macros to be explicit about the fact that we're using NSLog.
28
-
29
- #define LOG_LEVEL 2
30
-
31
- #define NSLogError(frmt, ...) do{ if(LOG_LEVEL >= 1) NSLog((frmt), ##__VA_ARGS__); } while(0)
32
- #define NSLogWarn(frmt, ...) do{ if(LOG_LEVEL >= 2) NSLog((frmt), ##__VA_ARGS__); } while(0)
33
- #define NSLogInfo(frmt, ...) do{ if(LOG_LEVEL >= 3) NSLog((frmt), ##__VA_ARGS__); } while(0)
34
- #define NSLogVerbose(frmt, ...) do{ if(LOG_LEVEL >= 4) NSLog((frmt), ##__VA_ARGS__); } while(0)
35
-
36
- @interface DDLogFileManagerDefault (PrivateAPI)
37
-
38
- - (void)deleteOldLogFiles;
39
- - (NSString *)defaultLogsDirectory;
40
-
41
- @end
42
-
43
- @interface DDFileLogger (PrivateAPI)
44
-
45
- - (void)rollLogFileNow;
46
- - (void)maybeRollLogFileDueToAge;
47
- - (void)maybeRollLogFileDueToSize;
48
-
49
- @end
50
-
51
- ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
52
- #pragma mark -
53
- ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
54
-
55
- @implementation DDLogFileManagerDefault
56
-
57
- @synthesize maximumNumberOfLogFiles;
58
-
59
- - (id)init
60
- {
61
- return [self initWithLogsDirectory:nil];
62
- }
63
-
64
- - (id)initWithLogsDirectory:(NSString *)aLogsDirectory
65
- {
66
- if ((self = [super init]))
67
- {
68
- maximumNumberOfLogFiles = DEFAULT_LOG_MAX_NUM_LOG_FILES;
69
-
70
- if (aLogsDirectory)
71
- _logsDirectory = [aLogsDirectory copy];
72
- else
73
- _logsDirectory = [[self defaultLogsDirectory] copy];
74
-
75
- NSKeyValueObservingOptions kvoOptions = NSKeyValueObservingOptionOld | NSKeyValueObservingOptionNew;
76
-
77
- [self addObserver:self forKeyPath:@"maximumNumberOfLogFiles" options:kvoOptions context:nil];
78
-
79
- NSLogVerbose(@"DDFileLogManagerDefault: logsDirectory:\n%@", [self logsDirectory]);
80
- NSLogVerbose(@"DDFileLogManagerDefault: sortedLogFileNames:\n%@", [self sortedLogFileNames]);
81
- }
82
- return self;
83
- }
84
-
85
- ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
86
- #pragma mark Configuration
87
- ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
88
-
89
- - (void)observeValueForKeyPath:(NSString *)keyPath
90
- ofObject:(id)object
91
- change:(NSDictionary *)change
92
- context:(void *)context
93
- {
94
- NSNumber *old = [change objectForKey:NSKeyValueChangeOldKey];
95
- NSNumber *new = [change objectForKey:NSKeyValueChangeNewKey];
96
-
97
- if ([old isEqual:new])
98
- {
99
- // No change in value - don't bother with any processing.
100
- return;
101
- }
102
-
103
- if ([keyPath isEqualToString:@"maximumNumberOfLogFiles"])
104
- {
105
- NSLogInfo(@"DDFileLogManagerDefault: Responding to configuration change: maximumNumberOfLogFiles");
106
-
107
- dispatch_async([DDLog loggingQueue], ^{ @autoreleasepool {
108
-
109
- [self deleteOldLogFiles];
110
- }});
111
- }
112
- }
113
-
114
- ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
115
- #pragma mark File Deleting
116
- ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
117
-
118
- /**
119
- * Deletes archived log files that exceed the maximumNumberOfLogFiles configuration value.
120
- **/
121
- - (void)deleteOldLogFiles
122
- {
123
- NSLogVerbose(@"DDLogFileManagerDefault: deleteOldLogFiles");
124
-
125
- NSUInteger maxNumLogFiles = self.maximumNumberOfLogFiles;
126
- if (maxNumLogFiles == 0)
127
- {
128
- // Unlimited - don't delete any log files
129
- return;
130
- }
131
-
132
- NSArray *sortedLogFileInfos = [self sortedLogFileInfos];
133
-
134
- // Do we consider the first file?
135
- // We are only supposed to be deleting archived files.
136
- // In most cases, the first file is likely the log file that is currently being written to.
137
- // So in most cases, we do not want to consider this file for deletion.
138
-
139
- NSUInteger count = [sortedLogFileInfos count];
140
- BOOL excludeFirstFile = NO;
141
-
142
- if (count > 0)
143
- {
144
- DDLogFileInfo *logFileInfo = [sortedLogFileInfos objectAtIndex:0];
145
-
146
- if (!logFileInfo.isArchived)
147
- {
148
- excludeFirstFile = YES;
149
- }
150
- }
151
-
152
- NSArray *sortedArchivedLogFileInfos;
153
- if (excludeFirstFile)
154
- {
155
- count--;
156
- sortedArchivedLogFileInfos = [sortedLogFileInfos subarrayWithRange:NSMakeRange(1, count)];
157
- }
158
- else
159
- {
160
- sortedArchivedLogFileInfos = sortedLogFileInfos;
161
- }
162
-
163
- NSUInteger i;
164
- for (i = maxNumLogFiles; i < count; i++)
165
- {
166
- DDLogFileInfo *logFileInfo = [sortedArchivedLogFileInfos objectAtIndex:i];
167
-
168
- NSLogInfo(@"DDLogFileManagerDefault: Deleting file: %@", logFileInfo.fileName);
169
-
170
- [[NSFileManager defaultManager] removeItemAtPath:logFileInfo.filePath error:nil];
171
- }
172
- }
173
-
174
- ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
175
- #pragma mark Log Files
176
- ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
177
-
178
- /**
179
- * Returns the path to the default logs directory.
180
- * If the logs directory doesn't exist, this method automatically creates it.
181
- **/
182
- - (NSString *)defaultLogsDirectory
183
- {
184
- #if TARGET_OS_IPHONE
185
- NSArray *paths = NSSearchPathForDirectoriesInDomains(NSCachesDirectory, NSUserDomainMask, YES);
186
- NSString *baseDir = ([paths count] > 0) ? [paths objectAtIndex:0] : nil;
187
- NSString *logsDirectory = [baseDir stringByAppendingPathComponent:@"Logs"];
188
-
189
- #else
190
- NSString *appName = [[NSProcessInfo processInfo] processName];
191
- NSArray *paths = NSSearchPathForDirectoriesInDomains(NSLibraryDirectory, NSUserDomainMask, YES);
192
- NSString *basePath = ([paths count] > 0) ? [paths objectAtIndex:0] : NSTemporaryDirectory();
193
- NSString *logsDirectory = [[basePath stringByAppendingPathComponent:@"Logs"] stringByAppendingPathComponent:appName];
194
-
195
- #endif
196
-
197
- return logsDirectory;
198
- }
199
-
200
- - (NSString *)logsDirectory
201
- {
202
- // We could do this check once, during initalization, and not bother again.
203
- // But this way the code continues to work if the directory gets deleted while the code is running.
204
-
205
- if (![[NSFileManager defaultManager] fileExistsAtPath:_logsDirectory])
206
- {
207
- NSError *err = nil;
208
- if (![[NSFileManager defaultManager] createDirectoryAtPath:_logsDirectory
209
- withIntermediateDirectories:YES attributes:nil error:&err])
210
- {
211
- NSLogError(@"DDFileLogManagerDefault: Error creating logsDirectory: %@", err);
212
- }
213
- }
214
-
215
- return _logsDirectory;
216
- }
217
-
218
- - (BOOL)isLogFile:(NSString *)fileName
219
- {
220
- // A log file has a name like "log-<uuid>.txt", where <uuid> is a HEX-string of 6 characters.
221
- //
222
- // For example: log-DFFE99.txt
223
-
224
- BOOL hasProperPrefix = [fileName hasPrefix:@"log-"];
225
-
226
- BOOL hasProperLength = [fileName length] >= 10;
227
-
228
-
229
- if (hasProperPrefix && hasProperLength)
230
- {
231
- NSCharacterSet *hexSet = [NSCharacterSet characterSetWithCharactersInString:@"0123456789ABCDEF"];
232
-
233
- NSString *hex = [fileName substringWithRange:NSMakeRange(4, 6)];
234
- NSString *nohex = [hex stringByTrimmingCharactersInSet:hexSet];
235
-
236
- if ([nohex length] == 0)
237
- {
238
- return YES;
239
- }
240
- }
241
-
242
- return NO;
243
- }
244
-
245
- /**
246
- * Returns an array of NSString objects,
247
- * each of which is the filePath to an existing log file on disk.
248
- **/
249
- - (NSArray *)unsortedLogFilePaths
250
- {
251
- NSString *logsDirectory = [self logsDirectory];
252
- NSArray *fileNames = [[NSFileManager defaultManager] contentsOfDirectoryAtPath:logsDirectory error:nil];
253
-
254
- NSMutableArray *unsortedLogFilePaths = [NSMutableArray arrayWithCapacity:[fileNames count]];
255
-
256
- for (NSString *fileName in fileNames)
257
- {
258
- // Filter out any files that aren't log files. (Just for extra safety)
259
-
260
- if ([self isLogFile:fileName])
261
- {
262
- NSString *filePath = [logsDirectory stringByAppendingPathComponent:fileName];
263
-
264
- [unsortedLogFilePaths addObject:filePath];
265
- }
266
- }
267
-
268
- return unsortedLogFilePaths;
269
- }
270
-
271
- /**
272
- * Returns an array of NSString objects,
273
- * each of which is the fileName of an existing log file on disk.
274
- **/
275
- - (NSArray *)unsortedLogFileNames
276
- {
277
- NSArray *unsortedLogFilePaths = [self unsortedLogFilePaths];
278
-
279
- NSMutableArray *unsortedLogFileNames = [NSMutableArray arrayWithCapacity:[unsortedLogFilePaths count]];
280
-
281
- for (NSString *filePath in unsortedLogFilePaths)
282
- {
283
- [unsortedLogFileNames addObject:[filePath lastPathComponent]];
284
- }
285
-
286
- return unsortedLogFileNames;
287
- }
288
-
289
- /**
290
- * Returns an array of DDLogFileInfo objects,
291
- * each representing an existing log file on disk,
292
- * and containing important information about the log file such as it's modification date and size.
293
- **/
294
- - (NSArray *)unsortedLogFileInfos
295
- {
296
- NSArray *unsortedLogFilePaths = [self unsortedLogFilePaths];
297
-
298
- NSMutableArray *unsortedLogFileInfos = [NSMutableArray arrayWithCapacity:[unsortedLogFilePaths count]];
299
-
300
- for (NSString *filePath in unsortedLogFilePaths)
301
- {
302
- DDLogFileInfo *logFileInfo = [[DDLogFileInfo alloc] initWithFilePath:filePath];
303
-
304
- [unsortedLogFileInfos addObject:logFileInfo];
305
- }
306
-
307
- return unsortedLogFileInfos;
308
- }
309
-
310
- /**
311
- * Just like the unsortedLogFilePaths method, but sorts the array.
312
- * The items in the array are sorted by modification date.
313
- * The first item in the array will be the most recently modified log file.
314
- **/
315
- - (NSArray *)sortedLogFilePaths
316
- {
317
- NSArray *sortedLogFileInfos = [self sortedLogFileInfos];
318
-
319
- NSMutableArray *sortedLogFilePaths = [NSMutableArray arrayWithCapacity:[sortedLogFileInfos count]];
320
-
321
- for (DDLogFileInfo *logFileInfo in sortedLogFileInfos)
322
- {
323
- [sortedLogFilePaths addObject:[logFileInfo filePath]];
324
- }
325
-
326
- return sortedLogFilePaths;
327
- }
328
-
329
- /**
330
- * Just like the unsortedLogFileNames method, but sorts the array.
331
- * The items in the array are sorted by modification date.
332
- * The first item in the array will be the most recently modified log file.
333
- **/
334
- - (NSArray *)sortedLogFileNames
335
- {
336
- NSArray *sortedLogFileInfos = [self sortedLogFileInfos];
337
-
338
- NSMutableArray *sortedLogFileNames = [NSMutableArray arrayWithCapacity:[sortedLogFileInfos count]];
339
-
340
- for (DDLogFileInfo *logFileInfo in sortedLogFileInfos)
341
- {
342
- [sortedLogFileNames addObject:[logFileInfo fileName]];
343
- }
344
-
345
- return sortedLogFileNames;
346
- }
347
-
348
- /**
349
- * Just like the unsortedLogFileInfos method, but sorts the array.
350
- * The items in the array are sorted by modification date.
351
- * The first item in the array will be the most recently modified log file.
352
- **/
353
- - (NSArray *)sortedLogFileInfos
354
- {
355
- return [[self unsortedLogFileInfos] sortedArrayUsingSelector:@selector(reverseCompareByCreationDate:)];
356
- }
357
-
358
- ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
359
- #pragma mark Creation
360
- ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
361
-
362
- /**
363
- * Generates a short UUID suitable for use in the log file's name.
364
- * The result will have six characters, all in the hexadecimal set [0123456789ABCDEF].
365
- **/
366
- - (NSString *)generateShortUUID
367
- {
368
- CFUUIDRef uuid = CFUUIDCreate(NULL);
369
-
370
- CFStringRef fullStr = CFUUIDCreateString(NULL, uuid);
371
- NSString *result = (__bridge_transfer NSString *)CFStringCreateWithSubstring(NULL, fullStr, CFRangeMake(0, 6));
372
-
373
- CFRelease(fullStr);
374
- CFRelease(uuid);
375
-
376
- return result;
377
- }
378
-
379
- /**
380
- * Generates a new unique log file path, and creates the corresponding log file.
381
- **/
382
- - (NSString *)createNewLogFile
383
- {
384
- // Generate a random log file name, and create the file (if there isn't a collision)
385
-
386
- NSString *logsDirectory = [self logsDirectory];
387
- do
388
- {
389
- NSString *fileName = [NSString stringWithFormat:@"log-%@.txt", [self generateShortUUID]];
390
-
391
- NSString *filePath = [logsDirectory stringByAppendingPathComponent:fileName];
392
-
393
- if (![[NSFileManager defaultManager] fileExistsAtPath:filePath])
394
- {
395
- NSLogVerbose(@"DDLogFileManagerDefault: Creating new log file: %@", fileName);
396
-
397
- [[NSFileManager defaultManager] createFileAtPath:filePath contents:nil attributes:nil];
398
-
399
- // Since we just created a new log file, we may need to delete some old log files
400
- [self deleteOldLogFiles];
401
-
402
- return filePath;
403
- }
404
-
405
- } while(YES);
406
- }
407
-
408
- @end
409
-
410
- ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
411
- #pragma mark -
412
- ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
413
-
414
- @implementation DDLogFileFormatterDefault
415
-
416
- - (id)init
417
- {
418
- return [self initWithDateFormatter:nil];
419
- }
420
-
421
- - (id)initWithDateFormatter:(NSDateFormatter *)aDateFormatter
422
- {
423
- if ((self = [super init]))
424
- {
425
- if (aDateFormatter)
426
- {
427
- dateFormatter = aDateFormatter;
428
- }
429
- else
430
- {
431
- dateFormatter = [[NSDateFormatter alloc] init];
432
- [dateFormatter setFormatterBehavior:NSDateFormatterBehavior10_4]; // 10.4+ style
433
- [dateFormatter setDateFormat:@"yyyy/MM/dd HH:mm:ss:SSS"];
434
- }
435
- }
436
- return self;
437
- }
438
-
439
- - (NSString *)formatLogMessage:(DDLogMessage *)logMessage
440
- {
441
- NSString *dateAndTime = [dateFormatter stringFromDate:(logMessage->timestamp)];
442
-
443
- return [NSString stringWithFormat:@"%@ %@", dateAndTime, logMessage->logMsg];
444
- }
445
-
446
- @end
447
-
448
- ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
449
- #pragma mark -
450
- ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
451
-
452
- @implementation DDFileLogger
453
-
454
- - (id)init
455
- {
456
- DDLogFileManagerDefault *defaultLogFileManager = [[DDLogFileManagerDefault alloc] init];
457
-
458
- return [self initWithLogFileManager:defaultLogFileManager];
459
- }
460
-
461
- - (id)initWithLogFileManager:(id <DDLogFileManager>)aLogFileManager
462
- {
463
- if ((self = [super init]))
464
- {
465
- maximumFileSize = DEFAULT_LOG_MAX_FILE_SIZE;
466
- rollingFrequency = DEFAULT_LOG_ROLLING_FREQUENCY;
467
-
468
- logFileManager = aLogFileManager;
469
-
470
- formatter = [[DDLogFileFormatterDefault alloc] init];
471
- }
472
- return self;
473
- }
474
-
475
- - (void)dealloc
476
- {
477
- [currentLogFileHandle synchronizeFile];
478
- [currentLogFileHandle closeFile];
479
-
480
- if (rollingTimer)
481
- {
482
- dispatch_source_cancel(rollingTimer);
483
- dispatch_release(rollingTimer);
484
- rollingTimer = NULL;
485
- }
486
- }
487
-
488
- ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
489
- #pragma mark Properties
490
- ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
491
-
492
- @synthesize logFileManager;
493
-
494
- - (unsigned long long)maximumFileSize
495
- {
496
- // The design of this method is taken from the DDAbstractLogger implementation.
497
- // For extensive documentation please refer to the DDAbstractLogger implementation.
498
-
499
- // Note: The internal implementation should access the maximumFileSize variable directly,
500
- // but if we forget to do this, then this method should at least work properly.
501
-
502
- dispatch_queue_t currentQueue = dispatch_get_current_queue();
503
- if (currentQueue == loggerQueue)
504
- {
505
- return maximumFileSize;
506
- }
507
- else
508
- {
509
- dispatch_queue_t loggingQueue = [DDLog loggingQueue];
510
- NSAssert(currentQueue != loggingQueue, @"Core architecture requirement failure");
511
-
512
- __block unsigned long long result;
513
-
514
- dispatch_sync(loggingQueue, ^{
515
- dispatch_sync(loggerQueue, ^{
516
- result = maximumFileSize;
517
- });
518
- });
519
-
520
- return result;
521
- }
522
- }
523
-
524
- - (void)setMaximumFileSize:(unsigned long long)newMaximumFileSize
525
- {
526
- // The design of this method is taken from the DDAbstractLogger implementation.
527
- // For documentation please refer to the DDAbstractLogger implementation.
528
-
529
- dispatch_block_t block = ^{ @autoreleasepool {
530
-
531
- maximumFileSize = newMaximumFileSize;
532
- [self maybeRollLogFileDueToSize];
533
-
534
- }};
535
-
536
- dispatch_queue_t currentQueue = dispatch_get_current_queue();
537
- if (currentQueue == loggerQueue)
538
- {
539
- block();
540
- }
541
- else
542
- {
543
- dispatch_queue_t loggingQueue = [DDLog loggingQueue];
544
- NSAssert(currentQueue != loggingQueue, @"Core architecture requirement failure");
545
-
546
- dispatch_async(loggingQueue, ^{
547
- dispatch_async(loggerQueue, block);
548
- });
549
- }
550
- }
551
-
552
- - (NSTimeInterval)rollingFrequency
553
- {
554
- // The design of this method is taken from the DDAbstractLogger implementation.
555
- // For documentation please refer to the DDAbstractLogger implementation.
556
-
557
- // Note: The internal implementation should access the rollingFrequency variable directly,
558
- // but if we forget to do this, then this method should at least work properly.
559
-
560
- dispatch_queue_t currentQueue = dispatch_get_current_queue();
561
- if (currentQueue == loggerQueue)
562
- {
563
- return rollingFrequency;
564
- }
565
- else
566
- {
567
- dispatch_queue_t loggingQueue = [DDLog loggingQueue];
568
- NSAssert(currentQueue != loggingQueue, @"Core architecture requirement failure");
569
-
570
- __block NSTimeInterval result;
571
-
572
- dispatch_sync(loggingQueue, ^{
573
- dispatch_sync(loggerQueue, ^{
574
- result = rollingFrequency;
575
- });
576
- });
577
-
578
- return result;
579
- }
580
- }
581
-
582
- - (void)setRollingFrequency:(NSTimeInterval)newRollingFrequency
583
- {
584
- // The design of this method is taken from the DDAbstractLogger implementation.
585
- // For documentation please refer to the DDAbstractLogger implementation.
586
-
587
- dispatch_block_t block = ^{ @autoreleasepool {
588
-
589
- rollingFrequency = newRollingFrequency;
590
- [self maybeRollLogFileDueToAge];
591
-
592
- }};
593
-
594
- dispatch_queue_t currentQueue = dispatch_get_current_queue();
595
- if (currentQueue == loggerQueue)
596
- {
597
- block();
598
- }
599
- else
600
- {
601
- dispatch_queue_t loggingQueue = [DDLog loggingQueue];
602
- NSAssert(currentQueue != loggingQueue, @"Core architecture requirement failure");
603
-
604
- dispatch_async(loggingQueue, ^{
605
- dispatch_async(loggerQueue, block);
606
- });
607
- }
608
- }
609
-
610
- ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
611
- #pragma mark File Rolling
612
- ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
613
-
614
- - (void)scheduleTimerToRollLogFileDueToAge
615
- {
616
- if (rollingTimer)
617
- {
618
- dispatch_source_cancel(rollingTimer);
619
- dispatch_release(rollingTimer);
620
- rollingTimer = NULL;
621
- }
622
-
623
- if (currentLogFileInfo == nil || rollingFrequency <= 0.0)
624
- {
625
- return;
626
- }
627
-
628
- NSDate *logFileCreationDate = [currentLogFileInfo creationDate];
629
-
630
- NSTimeInterval ti = [logFileCreationDate timeIntervalSinceReferenceDate];
631
- ti += rollingFrequency;
632
-
633
- NSDate *logFileRollingDate = [NSDate dateWithTimeIntervalSinceReferenceDate:ti];
634
-
635
- NSLogVerbose(@"DDFileLogger: scheduleTimerToRollLogFileDueToAge");
636
-
637
- NSLogVerbose(@"DDFileLogger: logFileCreationDate: %@", logFileCreationDate);
638
- NSLogVerbose(@"DDFileLogger: logFileRollingDate : %@", logFileRollingDate);
639
-
640
- rollingTimer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, loggerQueue);
641
-
642
- dispatch_source_set_event_handler(rollingTimer, ^{ @autoreleasepool {
643
-
644
- [self maybeRollLogFileDueToAge];
645
-
646
- }});
647
-
648
- uint64_t delay = [logFileRollingDate timeIntervalSinceNow] * NSEC_PER_SEC;
649
- dispatch_time_t fireTime = dispatch_time(DISPATCH_TIME_NOW, delay);
650
-
651
- dispatch_source_set_timer(rollingTimer, fireTime, DISPATCH_TIME_FOREVER, 1.0);
652
- dispatch_resume(rollingTimer);
653
- }
654
-
655
- - (void)rollLogFile
656
- {
657
- // This method is public.
658
- // We need to execute the rolling on our logging thread/queue.
659
- //
660
- // The design of this method is taken from the DDAbstractLogger implementation.
661
- // For documentation please refer to the DDAbstractLogger implementation.
662
-
663
- dispatch_block_t block = ^{ @autoreleasepool {
664
-
665
- [self rollLogFileNow];
666
- }};
667
-
668
- dispatch_queue_t currentQueue = dispatch_get_current_queue();
669
- if (currentQueue == loggerQueue)
670
- {
671
- block();
672
- }
673
- else
674
- {
675
- dispatch_queue_t loggingQueue = [DDLog loggingQueue];
676
- NSAssert(currentQueue != loggingQueue, @"Core architecture requirement failure");
677
-
678
- dispatch_async(loggingQueue, ^{
679
- dispatch_async(loggerQueue, block);
680
- });
681
- }
682
- }
683
-
684
- - (void)rollLogFileNow
685
- {
686
- NSLogVerbose(@"DDFileLogger: rollLogFileNow");
687
-
688
-
689
- if (currentLogFileHandle == nil) return;
690
-
691
- [currentLogFileHandle synchronizeFile];
692
- [currentLogFileHandle closeFile];
693
- currentLogFileHandle = nil;
694
-
695
- currentLogFileInfo.isArchived = YES;
696
-
697
- if ([logFileManager respondsToSelector:@selector(didRollAndArchiveLogFile:)])
698
- {
699
- [logFileManager didRollAndArchiveLogFile:(currentLogFileInfo.filePath)];
700
- }
701
-
702
- currentLogFileInfo = nil;
703
-
704
- if (rollingTimer)
705
- {
706
- dispatch_source_cancel(rollingTimer);
707
- dispatch_release(rollingTimer);
708
- rollingTimer = NULL;
709
- }
710
- }
711
-
712
- - (void)maybeRollLogFileDueToAge
713
- {
714
- if (rollingFrequency > 0.0 && currentLogFileInfo.age >= rollingFrequency)
715
- {
716
- NSLogVerbose(@"DDFileLogger: Rolling log file due to age...");
717
-
718
- [self rollLogFileNow];
719
- }
720
- else
721
- {
722
- [self scheduleTimerToRollLogFileDueToAge];
723
- }
724
- }
725
-
726
- - (void)maybeRollLogFileDueToSize
727
- {
728
- // This method is called from logMessage.
729
- // Keep it FAST.
730
-
731
- // Note: Use direct access to maximumFileSize variable.
732
- // We specifically wrote our own getter/setter method to allow us to do this (for performance reasons).
733
-
734
- if (maximumFileSize > 0)
735
- {
736
- unsigned long long fileSize = [currentLogFileHandle offsetInFile];
737
-
738
- if (fileSize >= maximumFileSize)
739
- {
740
- NSLogVerbose(@"DDFileLogger: Rolling log file due to size (%qu)...", fileSize);
741
-
742
- [self rollLogFileNow];
743
- }
744
- }
745
- }
746
-
747
- ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
748
- #pragma mark File Logging
749
- ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
750
-
751
- /**
752
- * Returns the log file that should be used.
753
- * If there is an existing log file that is suitable,
754
- * within the constraints of maximumFileSize and rollingFrequency, then it is returned.
755
- *
756
- * Otherwise a new file is created and returned.
757
- **/
758
- - (DDLogFileInfo *)currentLogFileInfo
759
- {
760
- if (currentLogFileInfo == nil)
761
- {
762
- NSArray *sortedLogFileInfos = [logFileManager sortedLogFileInfos];
763
-
764
- if ([sortedLogFileInfos count] > 0)
765
- {
766
- DDLogFileInfo *mostRecentLogFileInfo = [sortedLogFileInfos objectAtIndex:0];
767
-
768
- BOOL useExistingLogFile = YES;
769
- BOOL shouldArchiveMostRecent = NO;
770
-
771
- if (mostRecentLogFileInfo.isArchived)
772
- {
773
- useExistingLogFile = NO;
774
- shouldArchiveMostRecent = NO;
775
- }
776
- else if (maximumFileSize > 0 && mostRecentLogFileInfo.fileSize >= maximumFileSize)
777
- {
778
- useExistingLogFile = NO;
779
- shouldArchiveMostRecent = YES;
780
- }
781
- else if (rollingFrequency > 0.0 && mostRecentLogFileInfo.age >= rollingFrequency)
782
- {
783
- useExistingLogFile = NO;
784
- shouldArchiveMostRecent = YES;
785
- }
786
-
787
- if (useExistingLogFile)
788
- {
789
- NSLogVerbose(@"DDFileLogger: Resuming logging with file %@", mostRecentLogFileInfo.fileName);
790
-
791
- currentLogFileInfo = mostRecentLogFileInfo;
792
- }
793
- else
794
- {
795
- if (shouldArchiveMostRecent)
796
- {
797
- mostRecentLogFileInfo.isArchived = YES;
798
-
799
- if ([logFileManager respondsToSelector:@selector(didArchiveLogFile:)])
800
- {
801
- [logFileManager didArchiveLogFile:(mostRecentLogFileInfo.filePath)];
802
- }
803
- }
804
- }
805
- }
806
-
807
- if (currentLogFileInfo == nil)
808
- {
809
- NSString *currentLogFilePath = [logFileManager createNewLogFile];
810
-
811
- currentLogFileInfo = [[DDLogFileInfo alloc] initWithFilePath:currentLogFilePath];
812
- }
813
- }
814
-
815
- return currentLogFileInfo;
816
- }
817
-
818
- - (NSFileHandle *)currentLogFileHandle
819
- {
820
- if (currentLogFileHandle == nil)
821
- {
822
- NSString *logFilePath = [[self currentLogFileInfo] filePath];
823
-
824
- currentLogFileHandle = [NSFileHandle fileHandleForWritingAtPath:logFilePath];
825
- [currentLogFileHandle seekToEndOfFile];
826
-
827
- if (currentLogFileHandle)
828
- {
829
- [self scheduleTimerToRollLogFileDueToAge];
830
- }
831
- }
832
-
833
- return currentLogFileHandle;
834
- }
835
-
836
- ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
837
- #pragma mark DDLogger Protocol
838
- ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
839
-
840
- - (void)logMessage:(DDLogMessage *)logMessage
841
- {
842
- NSString *logMsg = logMessage->logMsg;
843
-
844
- if (formatter)
845
- {
846
- logMsg = [formatter formatLogMessage:logMessage];
847
- }
848
-
849
- if (logMsg)
850
- {
851
- if (![logMsg hasSuffix:@"\n"])
852
- {
853
- logMsg = [logMsg stringByAppendingString:@"\n"];
854
- }
855
-
856
- NSData *logData = [logMsg dataUsingEncoding:NSUTF8StringEncoding];
857
-
858
- [[self currentLogFileHandle] writeData:logData];
859
-
860
- [self maybeRollLogFileDueToSize];
861
- }
862
- }
863
-
864
- - (void)willRemoveLogger
865
- {
866
- // If you override me be sure to invoke [super willRemoveLogger];
867
-
868
- [self rollLogFileNow];
869
- }
870
-
871
- - (NSString *)loggerName
872
- {
873
- return @"cocoa.lumberjack.fileLogger";
874
- }
875
-
876
- @end
877
-
878
- ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
879
- #pragma mark -
880
- ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
881
-
882
- #if TARGET_IPHONE_SIMULATOR
883
- #define XATTR_ARCHIVED_NAME @"archived"
884
- #else
885
- #define XATTR_ARCHIVED_NAME @"lumberjack.log.archived"
886
- #endif
887
-
888
- @implementation DDLogFileInfo
889
-
890
- @synthesize filePath;
891
-
892
- @dynamic fileName;
893
- @dynamic fileAttributes;
894
- @dynamic creationDate;
895
- @dynamic modificationDate;
896
- @dynamic fileSize;
897
- @dynamic age;
898
-
899
- @dynamic isArchived;
900
-
901
-
902
- #pragma mark Lifecycle
903
-
904
- + (id)logFileWithPath:(NSString *)aFilePath
905
- {
906
- return [[DDLogFileInfo alloc] initWithFilePath:aFilePath];
907
- }
908
-
909
- - (id)initWithFilePath:(NSString *)aFilePath
910
- {
911
- if ((self = [super init]))
912
- {
913
- filePath = [aFilePath copy];
914
- }
915
- return self;
916
- }
917
-
918
- ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
919
- #pragma mark Standard Info
920
- ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
921
-
922
- - (NSDictionary *)fileAttributes
923
- {
924
- if (fileAttributes == nil)
925
- {
926
- fileAttributes = [[NSFileManager defaultManager] attributesOfItemAtPath:filePath error:nil];
927
- }
928
- return fileAttributes;
929
- }
930
-
931
- - (NSString *)fileName
932
- {
933
- if (fileName == nil)
934
- {
935
- fileName = [filePath lastPathComponent];
936
- }
937
- return fileName;
938
- }
939
-
940
- - (NSDate *)modificationDate
941
- {
942
- if (modificationDate == nil)
943
- {
944
- modificationDate = [[self fileAttributes] objectForKey:NSFileModificationDate];
945
- }
946
-
947
- return modificationDate;
948
- }
949
-
950
- - (NSDate *)creationDate
951
- {
952
- if (creationDate == nil)
953
- {
954
-
955
- #if TARGET_OS_IPHONE
956
-
957
- const char *path = [filePath UTF8String];
958
-
959
- struct attrlist attrList;
960
- memset(&attrList, 0, sizeof(attrList));
961
- attrList.bitmapcount = ATTR_BIT_MAP_COUNT;
962
- attrList.commonattr = ATTR_CMN_CRTIME;
963
-
964
- struct {
965
- u_int32_t attrBufferSizeInBytes;
966
- struct timespec crtime;
967
- } attrBuffer;
968
-
969
- int result = getattrlist(path, &attrList, &attrBuffer, sizeof(attrBuffer), 0);
970
- if (result == 0)
971
- {
972
- double seconds = (double)(attrBuffer.crtime.tv_sec);
973
- double nanos = (double)(attrBuffer.crtime.tv_nsec);
974
-
975
- NSTimeInterval ti = seconds + (nanos / 1000000000.0);
976
-
977
- creationDate = [NSDate dateWithTimeIntervalSince1970:ti];
978
- }
979
- else
980
- {
981
- NSLogError(@"DDLogFileInfo: creationDate(%@): getattrlist result = %i", self.fileName, result);
982
- }
983
-
984
- #else
985
-
986
- creationDate = [[self fileAttributes] objectForKey:NSFileCreationDate];
987
-
988
- #endif
989
-
990
- }
991
- return creationDate;
992
- }
993
-
994
- - (unsigned long long)fileSize
995
- {
996
- if (fileSize == 0)
997
- {
998
- fileSize = [[[self fileAttributes] objectForKey:NSFileSize] unsignedLongLongValue];
999
- }
1000
-
1001
- return fileSize;
1002
- }
1003
-
1004
- - (NSTimeInterval)age
1005
- {
1006
- return [[self creationDate] timeIntervalSinceNow] * -1.0;
1007
- }
1008
-
1009
- - (NSString *)description
1010
- {
1011
- return [[NSDictionary dictionaryWithObjectsAndKeys:
1012
- self.filePath, @"filePath",
1013
- self.fileName, @"fileName",
1014
- self.fileAttributes, @"fileAttributes",
1015
- self.creationDate, @"creationDate",
1016
- self.modificationDate, @"modificationDate",
1017
- [NSNumber numberWithUnsignedLongLong:self.fileSize], @"fileSize",
1018
- [NSNumber numberWithDouble:self.age], @"age",
1019
- [NSNumber numberWithBool:self.isArchived], @"isArchived",
1020
- nil] description];
1021
- }
1022
-
1023
- ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
1024
- #pragma mark Archiving
1025
- ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
1026
-
1027
- - (BOOL)isArchived
1028
- {
1029
-
1030
- #if TARGET_IPHONE_SIMULATOR
1031
-
1032
- // Extended attributes don't work properly on the simulator.
1033
- // So we have to use a less attractive alternative.
1034
- // See full explanation in the header file.
1035
-
1036
- return [self hasExtensionAttributeWithName:XATTR_ARCHIVED_NAME];
1037
-
1038
- #else
1039
-
1040
- return [self hasExtendedAttributeWithName:XATTR_ARCHIVED_NAME];
1041
-
1042
- #endif
1043
- }
1044
-
1045
- - (void)setIsArchived:(BOOL)flag
1046
- {
1047
-
1048
- #if TARGET_IPHONE_SIMULATOR
1049
-
1050
- // Extended attributes don't work properly on the simulator.
1051
- // So we have to use a less attractive alternative.
1052
- // See full explanation in the header file.
1053
-
1054
- if (flag)
1055
- [self addExtensionAttributeWithName:XATTR_ARCHIVED_NAME];
1056
- else
1057
- [self removeExtensionAttributeWithName:XATTR_ARCHIVED_NAME];
1058
-
1059
- #else
1060
-
1061
- if (flag)
1062
- [self addExtendedAttributeWithName:XATTR_ARCHIVED_NAME];
1063
- else
1064
- [self removeExtendedAttributeWithName:XATTR_ARCHIVED_NAME];
1065
-
1066
- #endif
1067
- }
1068
-
1069
- ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
1070
- #pragma mark Changes
1071
- ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
1072
-
1073
- - (void)reset
1074
- {
1075
- fileName = nil;
1076
- fileAttributes = nil;
1077
- creationDate = nil;
1078
- modificationDate = nil;
1079
- }
1080
-
1081
- - (void)renameFile:(NSString *)newFileName
1082
- {
1083
- // This method is only used on the iPhone simulator, where normal extended attributes are broken.
1084
- // See full explanation in the header file.
1085
-
1086
- if (![newFileName isEqualToString:[self fileName]])
1087
- {
1088
- NSString *fileDir = [filePath stringByDeletingLastPathComponent];
1089
-
1090
- NSString *newFilePath = [fileDir stringByAppendingPathComponent:newFileName];
1091
-
1092
- NSLogVerbose(@"DDLogFileInfo: Renaming file: '%@' -> '%@'", self.fileName, newFileName);
1093
-
1094
- NSError *error = nil;
1095
- if (![[NSFileManager defaultManager] moveItemAtPath:filePath toPath:newFilePath error:&error])
1096
- {
1097
- NSLogError(@"DDLogFileInfo: Error renaming file (%@): %@", self.fileName, error);
1098
- }
1099
-
1100
- filePath = newFilePath;
1101
- [self reset];
1102
- }
1103
- }
1104
-
1105
- ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
1106
- #pragma mark Attribute Management
1107
- ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
1108
-
1109
- #if TARGET_IPHONE_SIMULATOR
1110
-
1111
- // Extended attributes don't work properly on the simulator.
1112
- // So we have to use a less attractive alternative.
1113
- // See full explanation in the header file.
1114
-
1115
- - (BOOL)hasExtensionAttributeWithName:(NSString *)attrName
1116
- {
1117
- // This method is only used on the iPhone simulator, where normal extended attributes are broken.
1118
- // See full explanation in the header file.
1119
-
1120
- // Split the file name into components.
1121
- //
1122
- // log-ABC123.archived.uploaded.txt
1123
- //
1124
- // 0. log-ABC123
1125
- // 1. archived
1126
- // 2. uploaded
1127
- // 3. txt
1128
- //
1129
- // So we want to search for the attrName in the components (ignoring the first and last array indexes).
1130
-
1131
- NSArray *components = [[self fileName] componentsSeparatedByString:@"."];
1132
-
1133
- // Watch out for file names without an extension
1134
-
1135
- NSUInteger count = [components count];
1136
- NSUInteger max = (count >= 2) ? count-1 : count;
1137
-
1138
- NSUInteger i;
1139
- for (i = 1; i < max; i++)
1140
- {
1141
- NSString *attr = [components objectAtIndex:i];
1142
-
1143
- if ([attrName isEqualToString:attr])
1144
- {
1145
- return YES;
1146
- }
1147
- }
1148
-
1149
- return NO;
1150
- }
1151
-
1152
- - (void)addExtensionAttributeWithName:(NSString *)attrName
1153
- {
1154
- // This method is only used on the iPhone simulator, where normal extended attributes are broken.
1155
- // See full explanation in the header file.
1156
-
1157
- if ([attrName length] == 0) return;
1158
-
1159
- // Example:
1160
- // attrName = "archived"
1161
- //
1162
- // "log-ABC123.txt" -> "log-ABC123.archived.txt"
1163
-
1164
- NSArray *components = [[self fileName] componentsSeparatedByString:@"."];
1165
-
1166
- NSUInteger count = [components count];
1167
-
1168
- NSUInteger estimatedNewLength = [[self fileName] length] + [attrName length] + 1;
1169
- NSMutableString *newFileName = [NSMutableString stringWithCapacity:estimatedNewLength];
1170
-
1171
- if (count > 0)
1172
- {
1173
- [newFileName appendString:[components objectAtIndex:0]];
1174
- }
1175
-
1176
- NSString *lastExt = @"";
1177
-
1178
- NSUInteger i;
1179
- for (i = 1; i < count; i++)
1180
- {
1181
- NSString *attr = [components objectAtIndex:i];
1182
- if ([attr length] == 0)
1183
- {
1184
- continue;
1185
- }
1186
-
1187
- if ([attrName isEqualToString:attr])
1188
- {
1189
- // Extension attribute already exists in file name
1190
- return;
1191
- }
1192
-
1193
- if ([lastExt length] > 0)
1194
- {
1195
- [newFileName appendFormat:@".%@", lastExt];
1196
- }
1197
-
1198
- lastExt = attr;
1199
- }
1200
-
1201
- [newFileName appendFormat:@".%@", attrName];
1202
-
1203
- if ([lastExt length] > 0)
1204
- {
1205
- [newFileName appendFormat:@".%@", lastExt];
1206
- }
1207
-
1208
- [self renameFile:newFileName];
1209
- }
1210
-
1211
- - (void)removeExtensionAttributeWithName:(NSString *)attrName
1212
- {
1213
- // This method is only used on the iPhone simulator, where normal extended attributes are broken.
1214
- // See full explanation in the header file.
1215
-
1216
- if ([attrName length] == 0) return;
1217
-
1218
- // Example:
1219
- // attrName = "archived"
1220
- //
1221
- // "log-ABC123.txt" -> "log-ABC123.archived.txt"
1222
-
1223
- NSArray *components = [[self fileName] componentsSeparatedByString:@"."];
1224
-
1225
- NSUInteger count = [components count];
1226
-
1227
- NSUInteger estimatedNewLength = [[self fileName] length];
1228
- NSMutableString *newFileName = [NSMutableString stringWithCapacity:estimatedNewLength];
1229
-
1230
- if (count > 0)
1231
- {
1232
- [newFileName appendString:[components objectAtIndex:0]];
1233
- }
1234
-
1235
- BOOL found = NO;
1236
-
1237
- NSUInteger i;
1238
- for (i = 1; i < count; i++)
1239
- {
1240
- NSString *attr = [components objectAtIndex:i];
1241
-
1242
- if ([attrName isEqualToString:attr])
1243
- {
1244
- found = YES;
1245
- }
1246
- else
1247
- {
1248
- [newFileName appendFormat:@".%@", attr];
1249
- }
1250
- }
1251
-
1252
- if (found)
1253
- {
1254
- [self renameFile:newFileName];
1255
- }
1256
- }
1257
-
1258
- #else
1259
-
1260
- - (BOOL)hasExtendedAttributeWithName:(NSString *)attrName
1261
- {
1262
- const char *path = [filePath UTF8String];
1263
- const char *name = [attrName UTF8String];
1264
-
1265
- ssize_t result = getxattr(path, name, NULL, 0, 0, 0);
1266
-
1267
- return (result >= 0);
1268
- }
1269
-
1270
- - (void)addExtendedAttributeWithName:(NSString *)attrName
1271
- {
1272
- const char *path = [filePath UTF8String];
1273
- const char *name = [attrName UTF8String];
1274
-
1275
- int result = setxattr(path, name, NULL, 0, 0, 0);
1276
-
1277
- if (result < 0)
1278
- {
1279
- NSLogError(@"DDLogFileInfo: setxattr(%@, %@): error = %i", attrName, self.fileName, result);
1280
- }
1281
- }
1282
-
1283
- - (void)removeExtendedAttributeWithName:(NSString *)attrName
1284
- {
1285
- const char *path = [filePath UTF8String];
1286
- const char *name = [attrName UTF8String];
1287
-
1288
- int result = removexattr(path, name, 0);
1289
-
1290
- if (result < 0 && errno != ENOATTR)
1291
- {
1292
- NSLogError(@"DDLogFileInfo: removexattr(%@, %@): error = %i", attrName, self.fileName, result);
1293
- }
1294
- }
1295
-
1296
- #endif
1297
-
1298
- ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
1299
- #pragma mark Comparisons
1300
- ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
1301
-
1302
- - (BOOL)isEqual:(id)object
1303
- {
1304
- if ([object isKindOfClass:[self class]])
1305
- {
1306
- DDLogFileInfo *another = (DDLogFileInfo *)object;
1307
-
1308
- return [filePath isEqualToString:[another filePath]];
1309
- }
1310
-
1311
- return NO;
1312
- }
1313
-
1314
- - (NSComparisonResult)reverseCompareByCreationDate:(DDLogFileInfo *)another
1315
- {
1316
- NSDate *us = [self creationDate];
1317
- NSDate *them = [another creationDate];
1318
-
1319
- NSComparisonResult result = [us compare:them];
1320
-
1321
- if (result == NSOrderedAscending)
1322
- return NSOrderedDescending;
1323
-
1324
- if (result == NSOrderedDescending)
1325
- return NSOrderedAscending;
1326
-
1327
- return NSOrderedSame;
1328
- }
1329
-
1330
- - (NSComparisonResult)reverseCompareByModificationDate:(DDLogFileInfo *)another
1331
- {
1332
- NSDate *us = [self modificationDate];
1333
- NSDate *them = [another modificationDate];
1334
-
1335
- NSComparisonResult result = [us compare:them];
1336
-
1337
- if (result == NSOrderedAscending)
1338
- return NSOrderedDescending;
1339
-
1340
- if (result == NSOrderedDescending)
1341
- return NSOrderedAscending;
1342
-
1343
- return NSOrderedSame;
1344
- }
1345
-
1346
- @end