261 lines
7.8 KiB
Objective-C
261 lines
7.8 KiB
Objective-C
#import "DDDispatchQueueLogFormatter.h"
|
|
#import <libkern/OSAtomic.h>
|
|
|
|
/**
|
|
* Welcome to Cocoa Lumberjack!
|
|
*
|
|
* The project page has a wealth of documentation if you have any questions.
|
|
* https://github.com/CocoaLumberjack/CocoaLumberjack
|
|
*
|
|
* If you're new to the project you may wish to read the "Getting Started" wiki.
|
|
* https://github.com/CocoaLumberjack/CocoaLumberjack/wiki/GettingStarted
|
|
**/
|
|
|
|
#if ! __has_feature(objc_arc)
|
|
#error This file must be compiled with ARC. Use -fobjc-arc flag (or convert project to ARC).
|
|
#endif
|
|
|
|
|
|
@implementation DDDispatchQueueLogFormatter
|
|
{
|
|
int32_t atomicLoggerCount;
|
|
NSDateFormatter *threadUnsafeDateFormatter; // Use [self stringFromDate]
|
|
|
|
OSSpinLock lock;
|
|
|
|
NSUInteger _minQueueLength; // _prefix == Only access via atomic property
|
|
NSUInteger _maxQueueLength; // _prefix == Only access via atomic property
|
|
NSMutableDictionary *_replacements; // _prefix == Only access from within spinlock
|
|
}
|
|
|
|
- (id)init
|
|
{
|
|
if ((self = [super init]))
|
|
{
|
|
dateFormatString = @"yyyy-MM-dd HH:mm:ss:SSS";
|
|
|
|
atomicLoggerCount = 0;
|
|
threadUnsafeDateFormatter = nil;
|
|
|
|
_minQueueLength = 0;
|
|
_maxQueueLength = 0;
|
|
_replacements = [[NSMutableDictionary alloc] init];
|
|
|
|
// Set default replacements:
|
|
|
|
_replacements[@"com.apple.main-thread"] = @"main";
|
|
}
|
|
return self;
|
|
}
|
|
|
|
|
|
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
#pragma mark Configuration
|
|
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
@synthesize minQueueLength = _minQueueLength;
|
|
@synthesize maxQueueLength = _maxQueueLength;
|
|
|
|
- (NSString *)replacementStringForQueueLabel:(NSString *)longLabel
|
|
{
|
|
NSString *result = nil;
|
|
|
|
OSSpinLockLock(&lock);
|
|
{
|
|
result = _replacements[longLabel];
|
|
}
|
|
OSSpinLockUnlock(&lock);
|
|
|
|
return result;
|
|
}
|
|
|
|
- (void)setReplacementString:(NSString *)shortLabel forQueueLabel:(NSString *)longLabel
|
|
{
|
|
OSSpinLockLock(&lock);
|
|
{
|
|
if (shortLabel)
|
|
_replacements[longLabel] = shortLabel;
|
|
else
|
|
[_replacements removeObjectForKey:longLabel];
|
|
}
|
|
OSSpinLockUnlock(&lock);
|
|
}
|
|
|
|
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
#pragma mark DDLogFormatter
|
|
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
- (NSString *)stringFromDate:(NSDate *)date
|
|
{
|
|
int32_t loggerCount = OSAtomicAdd32(0, &atomicLoggerCount);
|
|
|
|
NSString *calendarIdentifier = nil;
|
|
#if defined(__IPHONE_8_0) || defined(__MAC_10_10)
|
|
calendarIdentifier = NSCalendarIdentifierGregorian;
|
|
#else
|
|
calendarIdentifier = NSGregorianCalendar;
|
|
#endif
|
|
|
|
if (loggerCount <= 1)
|
|
{
|
|
// Single-threaded mode.
|
|
|
|
if (threadUnsafeDateFormatter == nil)
|
|
{
|
|
threadUnsafeDateFormatter = [[NSDateFormatter alloc] init];
|
|
[threadUnsafeDateFormatter setFormatterBehavior:NSDateFormatterBehavior10_4];
|
|
[threadUnsafeDateFormatter setDateFormat:dateFormatString];
|
|
}
|
|
|
|
[threadUnsafeDateFormatter setCalendar:[[NSCalendar alloc] initWithCalendarIdentifier:calendarIdentifier]];
|
|
return [threadUnsafeDateFormatter stringFromDate:date];
|
|
}
|
|
else
|
|
{
|
|
// Multi-threaded mode.
|
|
// NSDateFormatter is NOT thread-safe.
|
|
|
|
NSString *key = @"DispatchQueueLogFormatter_NSDateFormatter";
|
|
|
|
NSMutableDictionary *threadDictionary = [[NSThread currentThread] threadDictionary];
|
|
NSDateFormatter *dateFormatter = threadDictionary[key];
|
|
|
|
if (dateFormatter == nil)
|
|
{
|
|
dateFormatter = [[NSDateFormatter alloc] init];
|
|
[dateFormatter setFormatterBehavior:NSDateFormatterBehavior10_4];
|
|
[dateFormatter setDateFormat:dateFormatString];
|
|
|
|
threadDictionary[key] = dateFormatter;
|
|
}
|
|
|
|
[dateFormatter setCalendar:[[NSCalendar alloc] initWithCalendarIdentifier:calendarIdentifier]];
|
|
return [dateFormatter stringFromDate:date];
|
|
}
|
|
}
|
|
|
|
- (NSString *)queueThreadLabelForLogMessage:(DDLogMessage *)logMessage
|
|
{
|
|
// As per the DDLogFormatter contract, this method is always invoked on the same thread/dispatch_queue
|
|
|
|
NSUInteger minQueueLength = self.minQueueLength;
|
|
NSUInteger maxQueueLength = self.maxQueueLength;
|
|
|
|
// Get the name of the queue, thread, or machID (whichever we are to use).
|
|
|
|
NSString *queueThreadLabel = nil;
|
|
|
|
BOOL useQueueLabel = YES;
|
|
BOOL useThreadName = NO;
|
|
|
|
if (logMessage->queueLabel)
|
|
{
|
|
// If you manually create a thread, it's dispatch_queue will have one of the thread names below.
|
|
// Since all such threads have the same name, we'd prefer to use the threadName or the machThreadID.
|
|
|
|
char *names[] = { "com.apple.root.low-priority",
|
|
"com.apple.root.default-priority",
|
|
"com.apple.root.high-priority",
|
|
"com.apple.root.low-overcommit-priority",
|
|
"com.apple.root.default-overcommit-priority",
|
|
"com.apple.root.high-overcommit-priority" };
|
|
|
|
int length = sizeof(names) / sizeof(char *);
|
|
|
|
int i;
|
|
for (i = 0; i < length; i++)
|
|
{
|
|
if (strcmp(logMessage->queueLabel, names[i]) == 0)
|
|
{
|
|
useQueueLabel = NO;
|
|
useThreadName = [logMessage->threadName length] > 0;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
useQueueLabel = NO;
|
|
useThreadName = [logMessage->threadName length] > 0;
|
|
}
|
|
|
|
if (useQueueLabel || useThreadName)
|
|
{
|
|
NSString *fullLabel;
|
|
NSString *abrvLabel;
|
|
|
|
if (useQueueLabel)
|
|
fullLabel = @(logMessage->queueLabel);
|
|
else
|
|
fullLabel = logMessage->threadName;
|
|
|
|
OSSpinLockLock(&lock);
|
|
{
|
|
abrvLabel = _replacements[fullLabel];
|
|
}
|
|
OSSpinLockUnlock(&lock);
|
|
|
|
if (abrvLabel)
|
|
queueThreadLabel = abrvLabel;
|
|
else
|
|
queueThreadLabel = fullLabel;
|
|
}
|
|
else
|
|
{
|
|
queueThreadLabel = [NSString stringWithFormat:@"%x", logMessage->machThreadID];
|
|
}
|
|
|
|
// Now use the thread label in the output
|
|
|
|
NSUInteger labelLength = [queueThreadLabel length];
|
|
|
|
// labelLength > maxQueueLength : truncate
|
|
// labelLength < minQueueLength : padding
|
|
// : exact
|
|
|
|
if ((maxQueueLength > 0) && (labelLength > maxQueueLength))
|
|
{
|
|
// Truncate
|
|
|
|
return [queueThreadLabel substringToIndex:maxQueueLength];
|
|
}
|
|
else if (labelLength < minQueueLength)
|
|
{
|
|
// Padding
|
|
|
|
NSUInteger numSpaces = minQueueLength - labelLength;
|
|
|
|
char spaces[numSpaces + 1];
|
|
memset(spaces, ' ', numSpaces);
|
|
spaces[numSpaces] = '\0';
|
|
|
|
return [NSString stringWithFormat:@"%@%s", queueThreadLabel, spaces];
|
|
}
|
|
else
|
|
{
|
|
// Exact
|
|
|
|
return queueThreadLabel;
|
|
}
|
|
}
|
|
|
|
- (NSString *)formatLogMessage:(DDLogMessage *)logMessage
|
|
{
|
|
NSString *timestamp = [self stringFromDate:(logMessage->timestamp)];
|
|
NSString *queueThreadLabel = [self queueThreadLabelForLogMessage:logMessage];
|
|
|
|
return [NSString stringWithFormat:@"%@ [%@] %@", timestamp, queueThreadLabel, logMessage->logMsg];
|
|
}
|
|
|
|
- (void)didAddToLogger:(id <DDLogger>)logger
|
|
{
|
|
OSAtomicIncrement32(&atomicLoggerCount);
|
|
}
|
|
|
|
- (void)willRemoveFromLogger:(id <DDLogger>)logger
|
|
{
|
|
OSAtomicDecrement32(&atomicLoggerCount);
|
|
}
|
|
|
|
@end
|