1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
|
//
// SDLLogTargetFile.m
// SmartDeviceLink-iOS
//
// Created by Joel Fischer on 2/28/17.
// Copyright © 2017 smartdevicelink. All rights reserved.
//
#import "SDLLogTargetFile.h"
#import "SDLLogModel.h"
NS_ASSUME_NONNULL_BEGIN
@interface SDLLogTargetFile ()
@property (assign, nonatomic) NSUInteger maxFiles;
@property (strong, nonatomic, nullable) NSFileHandle *logFileHandle;
@end
@implementation SDLLogTargetFile
- (instancetype)init {
self = [super init];
if (!self) { return nil; }
_maxFiles = 3;
return self;
}
+ (id<SDLLogTarget>)logger {
return [[self alloc] init];
}
- (BOOL)setupLogger {
self.logFileHandle = [self sdl_createFile];
if (self.logFileHandle == nil) {
return NO;
}
[self.logFileHandle seekToEndOfFile];
return YES;
}
- (void)logWithLog:(SDLLogModel *)log formattedLog:(NSString *)stringLog {
[self.logFileHandle writeData:[stringLog dataUsingEncoding:NSUTF8StringEncoding]];
}
- (void)teardownLogger {
[self.logFileHandle synchronizeFile];
[self.logFileHandle closeFile];
}
- (void)dealloc {
[_logFileHandle synchronizeFile];
[_logFileHandle closeFile];
}
#pragma mark - File handling
+ (NSString *)sdl_logDirectory {
NSString *documentsDirectory = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES).firstObject;
return [documentsDirectory stringByAppendingPathComponent:@"/smartdevicelink/log/"];
}
#pragma mark File creation
- (NSFileHandle *)sdl_createFile {
NSString *newFilePath = [[self.class sdl_logDirectory] stringByAppendingPathComponent:[self.class sdl_newFileName]];
NSFileManager *fileManager = [NSFileManager defaultManager];
if (![fileManager fileExistsAtPath:[self.class sdl_logDirectory]]) {
[fileManager createDirectoryAtPath:[self.class sdl_logDirectory] withIntermediateDirectories:YES attributes:nil error:nil];
}
if ([fileManager fileExistsAtPath:newFilePath]) {
[fileManager removeItemAtPath:newFilePath error:nil];
}
[fileManager createFileAtPath:newFilePath contents:nil attributes:nil];
[self.class sdl_cleanupLogFilesWithMaxFiles:self.maxFiles];
return [NSFileHandle fileHandleForWritingAtPath:newFilePath];
}
+ (NSString *)sdl_newFileName {
// Grab the name of the app if available, or just use 'smartdevicelink' if it's not
NSString *appName = [NSBundle mainBundle].infoDictionary[@"CFBundleDisplayName"];
if (appName == nil) {
appName = @"smartdevicelink";
}
NSDateFormatter *fileDateFormatter = [[NSDateFormatter alloc] init];
fileDateFormatter.dateFormat = @"yyyy-MM-dd-HH:mm:ss";
return [NSString stringWithFormat:@"%@-%@.log", [fileDateFormatter stringFromDate:[NSDate date]], appName];
}
#pragma mark File cleanup
+ (void)sdl_cleanupLogFilesWithMaxFiles:(NSUInteger)maxFiles {
NSArray<NSString *> *sortedLogFilePaths = [self sdl_sortedLogFilePaths];
// If we have more files now than the max, remove the oldest ones
NSInteger filesToRemove = (NSInteger)sortedLogFilePaths.count - (NSInteger)maxFiles;
for (NSInteger i = 0; i < filesToRemove; i++) {
NSString *path = [[self sdl_logDirectory] stringByAppendingPathComponent:sortedLogFilePaths[(NSUInteger)i]];
[[NSFileManager defaultManager] removeItemAtPath:path error:nil];
}
}
/**
Return the sorted file paths in order from oldest to newest (based on the file names).
@return An array of file paths from oldest to newest
*/
+ (NSArray<NSString *> *)sdl_sortedLogFilePaths {
NSArray *logFiles = [[NSFileManager defaultManager] contentsOfDirectoryAtPath:[self sdl_logDirectory] error:nil];
return [logFiles sortedArrayUsingSelector:@selector(localizedCaseInsensitiveCompare:)];
}
#pragma mark - NSObject
- (NSUInteger)hash {
return NSStringFromClass(self.class).hash;
}
// For the target classes, we're going to assume that if they're the same class, they're the same. The reason for this is so that NSSet, for instance, will only allow one of each target type in a set.
- (BOOL)isEqual:(id)object {
return [object isMemberOfClass:self.class];
}
@end
NS_ASSUME_NONNULL_END
|