motion-logger 0.1.3 → 0.1.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.gitignore +2 -0
- data/README.md +3 -1
- data/Rakefile +2 -4
- data/lib/logger/version.rb +1 -1
- data/motion-logger.gemspec +3 -3
- metadata +28 -51
- data/vendor/Podfile.lock +0 -5
- data/vendor/Pods/CocoaLumberjack/.gitignore +0 -24
- data/vendor/Pods/CocoaLumberjack/.hgignore +0 -6
- data/vendor/Pods/CocoaLumberjack/CocoaLumberjack.podspec +0 -18
- data/vendor/Pods/CocoaLumberjack/LICENSE.txt +0 -18
- data/vendor/Pods/CocoaLumberjack/Lumberjack/DDASLLogger.h +0 -41
- data/vendor/Pods/CocoaLumberjack/Lumberjack/DDASLLogger.m +0 -99
- data/vendor/Pods/CocoaLumberjack/Lumberjack/DDAbstractDatabaseLogger.h +0 -102
- data/vendor/Pods/CocoaLumberjack/Lumberjack/DDAbstractDatabaseLogger.m +0 -618
- data/vendor/Pods/CocoaLumberjack/Lumberjack/DDFileLogger.h +0 -334
- data/vendor/Pods/CocoaLumberjack/Lumberjack/DDFileLogger.m +0 -1346
- data/vendor/Pods/CocoaLumberjack/Lumberjack/DDLog.h +0 -498
- data/vendor/Pods/CocoaLumberjack/Lumberjack/DDLog.m +0 -979
- data/vendor/Pods/CocoaLumberjack/Lumberjack/DDTTYLogger.h +0 -49
- data/vendor/Pods/CocoaLumberjack/Lumberjack/DDTTYLogger.m +0 -186
- data/vendor/Pods/CocoaLumberjack/README.markdown +0 -37
- data/vendor/Pods/Headers/CocoaLumberjack/DDASLLogger.h +0 -41
- data/vendor/Pods/Headers/CocoaLumberjack/DDAbstractDatabaseLogger.h +0 -102
- data/vendor/Pods/Headers/CocoaLumberjack/DDFileLogger.h +0 -334
- data/vendor/Pods/Headers/CocoaLumberjack/DDLog.h +0 -498
- data/vendor/Pods/Headers/CocoaLumberjack/DDTTYLogger.h +0 -49
- data/vendor/Pods/Pods-Acknowledgements.markdown +0 -24
- data/vendor/Pods/Pods-Acknowledgements.plist +0 -54
- data/vendor/Pods/Pods-prefix.pch +0 -3
- data/vendor/Pods/Pods-resources.sh +0 -19
- data/vendor/Pods/Pods.bridgesupport +0 -462
- data/vendor/Pods/Pods.xcconfig +0 -4
- 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
|