-
Notifications
You must be signed in to change notification settings - Fork 3
/
MCCLumberJack.m
301 lines (231 loc) · 9.38 KB
/
MCCLumberJack.m
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
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
//
// MCCLumberJack.m
// MailCommon
//
// Created by Scott Little on 29/5/14.
// Copyright (c) 2014 Little Known Software. All rights reserved.
//
#import "MCCLumberJack.h"
#import "DDFileLogger.h"
#import "DDTTYLogger.h"
#import "MCCFeatureFormatter.h"
#import "MCCBugFormatter.h"
#import "MCCBundleFileManager.h"
#import "MCCFixedMailFileManager.h"
#ifdef DEBUG
#define MCC_LOG_VERBOSE
#endif
#ifdef BUG_LOGGING
#define MCC_LOG_VERBOSE
#endif
#ifdef MCC_LOG_VERBOSE
int MCC_PREFIXED_NAME(DDDebugLevel) = ((int)LOG_LEVEL_VERBOSE);
#else
int MCC_PREFIXED_NAME(DDDebugLevel) = ((int)LOG_LEVEL_INFO);
#endif
#ifdef MCC_BFM_FIXED_MAIL
#define MyFileManager FixedMailFileManager
#else
#define MyFileManager BundleFileManager
#endif
int MCC_PREFIXED_NAME(DDLogFeatures) = 0;
int MCC_PREFIXED_NAME(DDLogBugs) = 0;
@interface MCC_PREFIXED_NAME(LumberJack) ()
@property (strong) MCC_PREFIXED_NAME(FeatureFormatter) *featureFormatter;
@property (strong) MCC_PREFIXED_NAME(BugFormatter) *bugFormatter;
@end
@implementation MCC_PREFIXED_NAME(LumberJack)
#pragma mark - Helper Creation
+ (void)addStandardLoggersWithFeatureDict:(NSDictionary *)featureDict {
[self addStandardLoggersWithFeatureDict:featureDict forBundleId:nil];
}
+ (void)addStandardLoggersWithFeatureDict:(NSDictionary *)featureDict forBundleId:(NSString *)aBundleId {
// Set up the logging
MCC_PREFIXED_NAME(MyFileManager) *bundleFileManager = [[MCC_PREFIXED_NAME(MyFileManager) alloc] initWithBundleId:aBundleId];
DDFileLogger *fileLogger = [[DDFileLogger alloc] initWithLogFileManager:bundleFileManager];
MCC_PREFIXED_NAME(LumberJack) *jack = [self defaultInstance];
jack.featureFormatter.featureMappings = featureDict;
[fileLogger setLogFormatter:jack.featureFormatter];
[DDLog addLogger:fileLogger withLogLevel:INT32_MAX];
#ifdef DEBUG
// Will log everything to Xcode console
[DDLog addLogger:[DDTTYLogger sharedInstance] withLogLevel:INT32_MAX];
#endif
}
+ (void)addBugLoggerWithDict:(NSDictionary *)bugDict forBundleId:(NSString *)aBundleId {
NSString *bundleId = [@"bugs." stringByAppendingString:aBundleId];
// Set up the logging
MCC_PREFIXED_NAME(BundleFileManager) *bundleFileManager = [[MCC_PREFIXED_NAME(BundleFileManager) alloc] initWithBundleId:bundleId];
DDFileLogger *fileLogger = [[DDFileLogger alloc] initWithLogFileManager:bundleFileManager];
MCC_PREFIXED_NAME(LumberJack) *jack = [self defaultInstance];
jack.bugFormatter = [[MCC_PREFIXED_NAME(BugFormatter) alloc] init];
jack.bugFormatter.featureMappings = bugDict;
[fileLogger setLogFormatter:jack.bugFormatter];
[DDLog addLogger:fileLogger withLogLevel:INT32_MAX];
}
+ (instancetype)defaultInstance {
static MCC_PREFIXED_NAME(LumberJack) *defaultLumberJack = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
defaultLumberJack = [[self alloc] init];
defaultLumberJack.featureFormatter = [[MCC_PREFIXED_NAME(FeatureFormatter) alloc] init];
defaultLumberJack.bugFormatter = nil;
});
return defaultLumberJack;
}
#pragma mark - Level Settings
+ (int)debugLevel {
return MCC_PREFIXED_NAME(DDDebugLevel);
}
+ (void)setDebugLevel:(int)newLevel {
MCC_PREFIXED_NAME(DDDebugLevel) = newLevel;
}
#pragma mark - Feature Settings
+ (void)addLogFeature:(int)newFeature {
MCC_PREFIXED_NAME(DDLogFeatures) = (MCC_PREFIXED_NAME(DDLogFeatures) | newFeature);
}
+ (void)resetLogFeature {
MCC_PREFIXED_NAME(DDLogFeatures) = 0;
}
+ (void)addLogBug:(int)newBug {
MCC_PREFIXED_NAME(DDLogBugs) = (MCC_PREFIXED_NAME(DDLogBugs) | newBug);
}
+ (void)resetLogBug {
MCC_PREFIXED_NAME(DDLogBugs) = 0;
}
@end
@interface DDLog (MCCLumberJackInternal)
+ (void)queueLogMessage:(DDLogMessage *)logMessage asynchronously:(BOOL)asyncFlag;
@end
@implementation DDLog (MCCLumberJack)
+ (void)secureLog:(BOOL)asynchronous level:(int)level flag:(int)flag context:(int)context file:(const char *)file function:(const char *)function line:(int)line tag:(id)tag format:(NSString *)format, ... {
va_list args;
if (format) {
va_start(args, format);
NSString *logMsg = nil;
if (context & MCCSecureFormattingContext) {
NSString *preMarkedFormat = [self preMarkedSecureFormat:format];
preMarkedFormat = [[NSString alloc] initWithFormat:preMarkedFormat arguments:args];
logMsg = [[NSString alloc] initWithString:[self replaceMarkedFormat:preMarkedFormat]];
}
else {
logMsg = [[NSString alloc] initWithFormat:format arguments:args];
}
DDLogMessage *logMessage = [[DDLogMessage alloc] initWithLogMsg:logMsg
level:level
flag:flag
context:context
file:file
function:function
line:line
tag:tag
options:0];
[self queueLogMessage:logMessage asynchronously:asynchronous];
va_end(args);
}
}
+ (NSString *)preMarkedSecureFormat:(NSString *)format {
// the text to pre mark the secured output with
NSString *addBefore = @"<[*";
NSString *addAfter = @"*]>";
// the string format set, including the %
NSCharacterSet *stringFormatSet = [NSCharacterSet characterSetWithCharactersInString:@"@dDiuUxXoOfeEgGcCsSphq"];
// scan the string for an asterisk & percent (*%)
// if the next is '%' ignore
// then scan for one of the following:
// @ d D i u U x X o O f e E g G c C s S p h q
// if found delete from the % to the char inclusive
// unless one of the last two then add another character to delete
NSScanner *myScan = [NSScanner scannerWithString:format];
NSMutableString *newFormat = [NSMutableString string];
NSString *holder = nil;
// ensure that it doesn't skip any whitespace
[myScan setCharactersToBeSkipped:nil];
// If the format string starts with a '%', set a flag
BOOL startsWithPercent = [format hasPrefix:@"*%"];
// look for those '%'s
while ([myScan scanUpToString:@"*%" intoString:&holder] || startsWithPercent) {
// Immediately switch off that flag
startsWithPercent = NO;
// add holder to the newFormat
if (holder) {
[newFormat appendString:holder];
}
// if we are the end, leave
if ([myScan isAtEnd]) {
break;
}
// Advance the scanner position 1 to skip the asterisk
[myScan setScanLocation:([myScan scanLocation] + 1)];
// scan for the potentials
if ([myScan scanUpToCharactersFromSet:stringFormatSet
intoString:&holder]) {
// if current position is '%', reappend '%%' and continue
if ([format characterAtIndex:[myScan scanLocation]] == '%') {
[newFormat appendString:@"*%%"];
[myScan setScanLocation:([myScan scanLocation] + 1)];
continue;
}
[newFormat appendString:addBefore];
[newFormat appendString:holder];
// and if the last character is either 'h' or 'q',
// advance the pointer one more position to skip that
unichar lastChar = [format characterAtIndex:[myScan scanLocation]];
if ((lastChar == 'h') || (lastChar == 'q')) {
[myScan setScanLocation:([myScan scanLocation] + 1)];
[newFormat appendString:[NSString stringWithCharacters:&lastChar length:1]];
}
// always advance the scan position past the last matched character
lastChar = [format characterAtIndex:[myScan scanLocation]];
[newFormat appendString:[NSString stringWithCharacters:&lastChar length:1]];
[myScan setScanLocation:([myScan scanLocation] + 1)];
// stick the replace string into the outgoing string
[newFormat appendString:addAfter];
}
else {
// bad formatting, give warning and reset the format completely to ensure security
NSLog(@"Bad format during Secure Scan Reformat: original format is:%@", format);
return @"Bad format for Secure Logging";
}
}
// return the string
return [NSString stringWithString:newFormat];
}
+ (NSString *)replaceMarkedFormat:(NSString *)format {
// Pre Marked bookends
NSString *markerBefore = @"<[*";
NSString *markerAfter = @"*]>";
// the text to replace the hidden output with
NSString *replaceWith = @"<****>";
NSScanner *myScan = [NSScanner scannerWithString:format];
NSMutableString *newFormat = [NSMutableString string];
NSString *holder = nil;
// ensure that it doesn't skip any whitespace
[myScan setCharactersToBeSkipped:nil];
// look for the beginning marker
while ([myScan scanUpToString:markerBefore intoString:&holder]) {
// add holder to the newFormat
if (holder) {
[newFormat appendString:holder];
}
// if we are the end, leave
if ([myScan isAtEnd]) {
break;
}
// scan for the potentials
if ([myScan scanUpToString:markerAfter intoString:&holder]) {
// always advance the scan position past the matched string
[myScan setScanLocation:([myScan scanLocation] + [markerAfter length])];
// stick the replace string into the outgoing string
[newFormat appendString:replaceWith];
}
else {
// bad formatting, give warning and reset the format completely to ensure security
NSLog(@"Bad format during Secure Scan Reformat: original format is:%@", format);
return @"Bad format for Secure Logging";
}
}
// return the string
return [NSString stringWithString:newFormat];
}
@end