First Commit

This commit is contained in:
giuseppenuc committed 2016-02-24 15:45:24 +01:00
1 parent 1d4d9c2e6d
commit d281d765ea
124 files changed
+41873 -9

No files matched your search

@@ -0,0 +1,117 @@
// Software License Agreement (BSD License)
//
// Copyright (c) 2010-2015, Deusty, LLC
// All rights reserved.
//
// Redistribution and use of this software in source and binary forms,
// with or without modification, are permitted provided that the following conditions are met:
//
// * Redistributions of source code must retain the above copyright notice,
// this list of conditions and the following disclaimer.
//
// * Neither the name of Deusty nor the names of its contributors may be used
// to endorse or promote products derived from this software without specific
// prior written permission of Deusty, LLC.
#import <Foundation/Foundation.h>
// Disable legacy macros
#ifndef DD_LEGACY_MACROS
#define DD_LEGACY_MACROS 0
#endif
#import "DDLog.h"
/**
* This class provides a log formatter that filters log statements from a logging context not on the whitelist.
*
* A log formatter can be added to any logger to format and/or filter its output.
* You can learn more about log formatters here:
* Documentation/CustomFormatters.md
*
* You can learn more about logging context's here:
* Documentation/CustomContext.md
*
* But here's a quick overview / refresher:
*
* Every log statement has a logging context.
* These come from the underlying logging macros defined in DDLog.h.
* The default logging context is zero.
* You can define multiple logging context's for use in your application.
* For example, logically separate parts of your app each have a different logging context.
* Also 3rd party frameworks that make use of Lumberjack generally use their own dedicated logging context.
**/
@interface DDContextWhitelistFilterLogFormatter : NSObject <DDLogFormatter>
/**
* Designated default initializer
*/
- (instancetype)init NS_DESIGNATED_INITIALIZER;
/**
* Add a context to the whitelist
*
* @param loggingContext the context
*/
- (void)addToWhitelist:(NSUInteger)loggingContext;
/**
* Remove context from whitelist
*
* @param loggingContext the context
*/
- (void)removeFromWhitelist:(NSUInteger)loggingContext;
/**
* Return the whitelist
*/
@property (readonly, copy) NSArray *whitelist;
/**
* Check if a context is on the whitelist
*
* @param loggingContext the context
*/
- (BOOL)isOnWhitelist:(NSUInteger)loggingContext;
@end
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
#pragma mark -
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/**
* This class provides a log formatter that filters log statements from a logging context on the blacklist.
**/
@interface DDContextBlacklistFilterLogFormatter : NSObject <DDLogFormatter>
- (instancetype)init NS_DESIGNATED_INITIALIZER;
/**
* Add a context to the blacklist
*
* @param loggingContext the context
*/
- (void)addToBlacklist:(NSUInteger)loggingContext;
/**
* Remove context from blacklist
*
* @param loggingContext the context
*/
- (void)removeFromBlacklist:(NSUInteger)loggingContext;
/**
* Return the blacklist
*/
@property (readonly, copy) NSArray *blacklist;
/**
* Check if a context is on the blacklist
*
* @param loggingContext the context
*/
- (BOOL)isOnBlacklist:(NSUInteger)loggingContext;
@end
@@ -0,0 +1,191 @@
// Software License Agreement (BSD License)
//
// Copyright (c) 2010-2015, Deusty, LLC
// All rights reserved.
//
// Redistribution and use of this software in source and binary forms,
// with or without modification, are permitted provided that the following conditions are met:
//
// * Redistributions of source code must retain the above copyright notice,
// this list of conditions and the following disclaimer.
//
// * Neither the name of Deusty nor the names of its contributors may be used
// to endorse or promote products derived from this software without specific
// prior written permission of Deusty, LLC.
#import "DDContextFilterLogFormatter.h"
#import <libkern/OSAtomic.h>
#if !__has_feature(objc_arc)
#error This file must be compiled with ARC. Use -fobjc-arc flag (or convert project to ARC).
#endif
@interface DDLoggingContextSet : NSObject
- (void)addToSet:(NSUInteger)loggingContext;
- (void)removeFromSet:(NSUInteger)loggingContext;
@property (readonly, copy) NSArray *currentSet;
- (BOOL)isInSet:(NSUInteger)loggingContext;
@end
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
#pragma mark -
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
@interface DDContextWhitelistFilterLogFormatter () {
DDLoggingContextSet *_contextSet;
}
@end
@implementation DDContextWhitelistFilterLogFormatter
- (instancetype)init {
if ((self = [super init])) {
_contextSet = [[DDLoggingContextSet alloc] init];
}
return self;
}
- (void)addToWhitelist:(NSUInteger)loggingContext {
[_contextSet addToSet:loggingContext];
}
- (void)removeFromWhitelist:(NSUInteger)loggingContext {
[_contextSet removeFromSet:loggingContext];
}
- (NSArray *)whitelist {
return [_contextSet currentSet];
}
- (BOOL)isOnWhitelist:(NSUInteger)loggingContext {
return [_contextSet isInSet:loggingContext];
}
- (NSString *)formatLogMessage:(DDLogMessage *)logMessage {
if ([self isOnWhitelist:logMessage->_context]) {
return logMessage->_message;
} else {
return nil;
}
}
@end
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
#pragma mark -
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
@interface DDContextBlacklistFilterLogFormatter () {
DDLoggingContextSet *_contextSet;
}
@end
@implementation DDContextBlacklistFilterLogFormatter
- (instancetype)init {
if ((self = [super init])) {
_contextSet = [[DDLoggingContextSet alloc] init];
}
return self;
}
- (void)addToBlacklist:(NSUInteger)loggingContext {
[_contextSet addToSet:loggingContext];
}
- (void)removeFromBlacklist:(NSUInteger)loggingContext {
[_contextSet removeFromSet:loggingContext];
}
- (NSArray *)blacklist {
return [_contextSet currentSet];
}
- (BOOL)isOnBlacklist:(NSUInteger)loggingContext {
return [_contextSet isInSet:loggingContext];
}
- (NSString *)formatLogMessage:(DDLogMessage *)logMessage {
if ([self isOnBlacklist:logMessage->_context]) {
return nil;
} else {
return logMessage->_message;
}
}
@end
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
#pragma mark -
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
@interface DDLoggingContextSet () {
OSSpinLock _lock;
NSMutableSet *_set;
}
@end
@implementation DDLoggingContextSet
- (instancetype)init {
if ((self = [super init])) {
_set = [[NSMutableSet alloc] init];
}
return self;
}
- (void)addToSet:(NSUInteger)loggingContext {
OSSpinLockLock(&_lock);
{
[_set addObject:@(loggingContext)];
}
OSSpinLockUnlock(&_lock);
}
- (void)removeFromSet:(NSUInteger)loggingContext {
OSSpinLockLock(&_lock);
{
[_set removeObject:@(loggingContext)];
}
OSSpinLockUnlock(&_lock);
}
- (NSArray *)currentSet {
NSArray *result = nil;
OSSpinLockLock(&_lock);
{
result = [_set allObjects];
}
OSSpinLockUnlock(&_lock);
return result;
}
- (BOOL)isInSet:(NSUInteger)loggingContext {
BOOL result = NO;
OSSpinLockLock(&_lock);
{
result = [_set containsObject:@(loggingContext)];
}
OSSpinLockUnlock(&_lock);
return result;
}
@end
@@ -0,0 +1,178 @@
// Software License Agreement (BSD License)
//
// Copyright (c) 2010-2015, Deusty, LLC
// All rights reserved.
//
// Redistribution and use of this software in source and binary forms,
// with or without modification, are permitted provided that the following conditions are met:
//
// * Redistributions of source code must retain the above copyright notice,
// this list of conditions and the following disclaimer.
//
// * Neither the name of Deusty nor the names of its contributors may be used
// to endorse or promote products derived from this software without specific
// prior written permission of Deusty, LLC.
#import <Foundation/Foundation.h>
#import <libkern/OSAtomic.h>
// Disable legacy macros
#ifndef DD_LEGACY_MACROS
#define DD_LEGACY_MACROS 0
#endif
#import "DDLog.h"
/**
* Log formatter mode
*/
typedef NS_ENUM(NSUInteger, DDDispatchQueueLogFormatterMode){
/**
* This is the default option, means the formatter can be reused between multiple loggers and therefore is thread-safe.
* There is, of course, a performance cost for the thread-safety
*/
DDDispatchQueueLogFormatterModeShareble = 0,
/**
* If the formatter will only be used by a single logger, then the thread-safety can be removed
* @note: there is an assert checking if the formatter is added to multiple loggers and the mode is non-shareble
*/
DDDispatchQueueLogFormatterModeNonShareble,
};
/**
* This class provides a log formatter that prints the dispatch_queue label instead of the mach_thread_id.
*
* A log formatter can be added to any logger to format and/or filter its output.
* You can learn more about log formatters here:
* Documentation/CustomFormatters.md
*
* A typical `NSLog` (or `DDTTYLogger`) prints detailed info as `[<process_id>:<thread_id>]`.
* For example:
*
* `2011-10-17 20:21:45.435 AppName[19928:5207] Your log message here`
*
* Where:
* `- 19928 = process id`
* `- 5207 = thread id (mach_thread_id printed in hex)`
*
* When using grand central dispatch (GCD), this information is less useful.
* This is because a single serial dispatch queue may be run on any thread from an internally managed thread pool.
* For example:
*
* `2011-10-17 20:32:31.111 AppName[19954:4d07] Message from my_serial_dispatch_queue`
* `2011-10-17 20:32:31.112 AppName[19954:5207] Message from my_serial_dispatch_queue`
* `2011-10-17 20:32:31.113 AppName[19954:2c55] Message from my_serial_dispatch_queue`
*
* This formatter allows you to replace the standard `[box:info]` with the dispatch_queue name.
* For example:
*
* `2011-10-17 20:32:31.111 AppName[img-scaling] Message from my_serial_dispatch_queue`
* `2011-10-17 20:32:31.112 AppName[img-scaling] Message from my_serial_dispatch_queue`
* `2011-10-17 20:32:31.113 AppName[img-scaling] Message from my_serial_dispatch_queue`
*
* If the dispatch_queue doesn't have a set name, then it falls back to the thread name.
* If the current thread doesn't have a set name, then it falls back to the mach_thread_id in hex (like normal).
*
* Note: If manually creating your own background threads (via `NSThread/alloc/init` or `NSThread/detachNeThread`),
* you can use `[[NSThread currentThread] setName:(NSString *)]`.
**/
@interface DDDispatchQueueLogFormatter : NSObject <DDLogFormatter>
/**
* Standard init method.
* Configure using properties as desired.
**/
- (instancetype)init NS_DESIGNATED_INITIALIZER;
/**
* Initializer with ability to set the queue mode
*
* @param mode choose between DDDispatchQueueLogFormatterModeShareble and DDDispatchQueueLogFormatterModeNonShareble, depending if the formatter is shared between several loggers or not
*/
- (instancetype)initWithMode:(DDDispatchQueueLogFormatterMode)mode;
/**
* The minQueueLength restricts the minimum size of the [detail box].
* If the minQueueLength is set to 0, there is no restriction.
*
* For example, say a dispatch_queue has a label of "diskIO":
*
* If the minQueueLength is 0: [diskIO]
* If the minQueueLength is 4: [diskIO]
* If the minQueueLength is 5: [diskIO]
* If the minQueueLength is 6: [diskIO]
* If the minQueueLength is 7: [diskIO ]
* If the minQueueLength is 8: [diskIO ]
*
* The default minQueueLength is 0 (no minimum, so [detail box] won't be padded).
*
* If you want every [detail box] to have the exact same width,
* set both minQueueLength and maxQueueLength to the same value.
**/
@property (assign, atomic) NSUInteger minQueueLength;
/**
* The maxQueueLength restricts the number of characters that will be inside the [detail box].
* If the maxQueueLength is 0, there is no restriction.
*
* For example, say a dispatch_queue has a label of "diskIO":
*
* If the maxQueueLength is 0: [diskIO]
* If the maxQueueLength is 4: [disk]
* If the maxQueueLength is 5: [diskI]
* If the maxQueueLength is 6: [diskIO]
* If the maxQueueLength is 7: [diskIO]
* If the maxQueueLength is 8: [diskIO]
*
* The default maxQueueLength is 0 (no maximum, so [detail box] won't be truncated).
*
* If you want every [detail box] to have the exact same width,
* set both minQueueLength and maxQueueLength to the same value.
**/
@property (assign, atomic) NSUInteger maxQueueLength;
/**
* Sometimes queue labels have long names like "com.apple.main-queue",
* but you'd prefer something shorter like simply "main".
*
* This method allows you to set such preferred replacements.
* The above example is set by default.
*
* To remove/undo a previous replacement, invoke this method with nil for the 'shortLabel' parameter.
**/
- (NSString *)replacementStringForQueueLabel:(NSString *)longLabel;
/**
* See the `replacementStringForQueueLabel:` description
*/
- (void)setReplacementString:(NSString *)shortLabel forQueueLabel:(NSString *)longLabel;
@end
/**
* Category on `DDDispatchQueueLogFormatter` to make method declarations easier to extend/modify
**/
@interface DDDispatchQueueLogFormatter (OverridableMethods)
/**
* Date formatter default configuration
*/
- (void)configureDateFormatter:(NSDateFormatter *)dateFormatter;
/**
* Formatter method to transfrom from date to string
*/
- (NSString *)stringFromDate:(NSDate *)date;
/**
* Method to compute the queue thread label
*/
- (NSString *)queueThreadLabelForLogMessage:(DDLogMessage *)logMessage;
/**
* The actual method that formats a message (transforms a `DDLogMessage` model into a printable string)
*/
- (NSString *)formatLogMessage:(DDLogMessage *)logMessage;
@end
@@ -0,0 +1,277 @@
// Software License Agreement (BSD License)
//
// Copyright (c) 2010-2015, Deusty, LLC
// All rights reserved.
//
// Redistribution and use of this software in source and binary forms,
// with or without modification, are permitted provided that the following conditions are met:
//
// * Redistributions of source code must retain the above copyright notice,
// this list of conditions and the following disclaimer.
//
// * Neither the name of Deusty nor the names of its contributors may be used
// to endorse or promote products derived from this software without specific
// prior written permission of Deusty, LLC.
#import "DDDispatchQueueLogFormatter.h"
#import <libkern/OSAtomic.h>
#import <objc/runtime.h>
#if !__has_feature(objc_arc)
#error This file must be compiled with ARC. Use -fobjc-arc flag (or convert project to ARC).
#endif
@interface DDDispatchQueueLogFormatter () {
DDDispatchQueueLogFormatterMode _mode;
NSString *_dateFormatterKey;
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
}
@end
@implementation DDDispatchQueueLogFormatter
- (instancetype)init {
if ((self = [super init])) {
_mode = DDDispatchQueueLogFormatterModeShareble;
// We need to carefully pick the name for storing in thread dictionary to not
// use a formatter configured by subclass and avoid surprises.
Class cls = [self class];
Class superClass = class_getSuperclass(cls);
SEL configMethodName = @selector(configureDateFormatter:);
Method configMethod = class_getInstanceMethod(cls, configMethodName);
while (class_getInstanceMethod(superClass, configMethodName) == configMethod) {
cls = superClass;
superClass = class_getSuperclass(cls);
}
// now `cls` is the class that provides implementation for `configureDateFormatter:`
_dateFormatterKey = [NSString stringWithFormat:@"%s_NSDateFormatter", class_getName(cls)];
_atomicLoggerCount = 0;
_threadUnsafeDateFormatter = nil;
_minQueueLength = 0;
_maxQueueLength = 0;
_replacements = [[NSMutableDictionary alloc] init];
// Set default replacements:
_replacements[@"com.apple.main-thread"] = @"main";
}
return self;
}
- (instancetype)initWithMode:(DDDispatchQueueLogFormatterMode)mode {
if ((self = [self init])) {
_mode = mode;
}
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
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
- (NSDateFormatter *)createDateFormatter {
NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
[self configureDateFormatter:formatter];
return formatter;
}
- (void)configureDateFormatter:(NSDateFormatter *)dateFormatter {
[dateFormatter setFormatterBehavior:NSDateFormatterBehavior10_4];
[dateFormatter setDateFormat:@"yyyy-MM-dd HH:mm:ss:SSS"];
[dateFormatter setLocale:[NSLocale localeWithLocaleIdentifier:@"en_US_POSIX"]];
NSString *calendarIdentifier = nil;
#if defined(__IPHONE_8_0) || defined(__MAC_10_10)
calendarIdentifier = NSCalendarIdentifierGregorian;
#else
calendarIdentifier = NSGregorianCalendar;
#endif
[dateFormatter setCalendar:[[NSCalendar alloc] initWithCalendarIdentifier:calendarIdentifier]];
}
- (NSString *)stringFromDate:(NSDate *)date {
NSDateFormatter *dateFormatter = nil;
if (_mode == DDDispatchQueueLogFormatterModeNonShareble) {
// Single-threaded mode.
dateFormatter = _threadUnsafeDateFormatter;
if (dateFormatter == nil) {
dateFormatter = [self createDateFormatter];
_threadUnsafeDateFormatter = dateFormatter;
}
} else {
// Multi-threaded mode.
// NSDateFormatter is NOT thread-safe.
NSString *key = _dateFormatterKey;
NSMutableDictionary *threadDictionary = [[NSThread currentThread] threadDictionary];
dateFormatter = threadDictionary[key];
if (dateFormatter == nil) {
dateFormatter = [self createDateFormatter];
threadDictionary[key] = dateFormatter;
}
}
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.
NSArray *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"
];
for (NSString * name in names) {
if ([logMessage->_queueLabel isEqualToString:name]) {
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 = logMessage->_threadID;
}
// 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->_message];
}
- (void)didAddToLogger:(id <DDLogger> __attribute__((unused)))logger {
int32_t count = 0;
count = OSAtomicIncrement32(&_atomicLoggerCount);
NSAssert(count <= 1 || _mode == DDDispatchQueueLogFormatterModeShareble, @"Can't reuse formatter with multiple loggers in non-shareable mode.");
}
- (void)willRemoveFromLogger:(id <DDLogger> __attribute__((unused)))logger {
OSAtomicDecrement32(&_atomicLoggerCount);
}
@end
@@ -0,0 +1,56 @@
// Software License Agreement (BSD License)
//
// Copyright (c) 2010-2015, Deusty, LLC
// All rights reserved.
//
// Redistribution and use of this software in source and binary forms,
// with or without modification, are permitted provided that the following conditions are met:
//
// * Redistributions of source code must retain the above copyright notice,
// this list of conditions and the following disclaimer.
//
// * Neither the name of Deusty nor the names of its contributors may be used
// to endorse or promote products derived from this software without specific
// prior written permission of Deusty, LLC.
#import <Foundation/Foundation.h>
// Disable legacy macros
#ifndef DD_LEGACY_MACROS
#define DD_LEGACY_MACROS 0
#endif
#import "DDLog.h"
/**
* This formatter can be used to chain different formatters together.
* The log message will processed in the order of the formatters added.
**/
@interface DDMultiFormatter : NSObject <DDLogFormatter>
/**
* Array of chained formatters
*/
@property (readonly) NSArray *formatters;
/**
* Add a new formatter
*/
- (void)addFormatter:(id<DDLogFormatter>)formatter;
/**
* Remove a formatter
*/
- (void)removeFormatter:(id<DDLogFormatter>)formatter;
/**
* Remove all existing formatters
*/
- (void)removeAllFormatters;
/**
* Check if a certain formatter is used
*/
- (BOOL)isFormattingWithFormatter:(id<DDLogFormatter>)formatter;
@end
@@ -0,0 +1,144 @@
// Software License Agreement (BSD License)
//
// Copyright (c) 2010-2015, Deusty, LLC
// All rights reserved.
//
// Redistribution and use of this software in source and binary forms,
// with or without modification, are permitted provided that the following conditions are met:
//
// * Redistributions of source code must retain the above copyright notice,
// this list of conditions and the following disclaimer.
//
// * Neither the name of Deusty nor the names of its contributors may be used
// to endorse or promote products derived from this software without specific
// prior written permission of Deusty, LLC.
#import "DDMultiFormatter.h"
#if TARGET_OS_IOS
// Compiling for iOS
#if __IPHONE_OS_VERSION_MIN_REQUIRED >= 60000 // iOS 6.0 or later
#define NEEDS_DISPATCH_RETAIN_RELEASE 0
#else // iOS 5.X or earlier
#define NEEDS_DISPATCH_RETAIN_RELEASE 1
#endif
#elif TARGET_OS_WATCH || TARGET_OS_TV
// Compiling for watchOS, tvOS
#define NEEDS_DISPATCH_RETAIN_RELEASE 0
#else
// Compiling for Mac OS X
#if MAC_OS_X_VERSION_MIN_REQUIRED >= 1080 // Mac OS X 10.8 or later
#define NEEDS_DISPATCH_RETAIN_RELEASE 0
#else // Mac OS X 10.7 or earlier
#define NEEDS_DISPATCH_RETAIN_RELEASE 1
#endif
#endif
#if !__has_feature(objc_arc)
#error This file must be compiled with ARC. Use -fobjc-arc flag (or convert project to ARC).
#endif
@interface DDMultiFormatter () {
dispatch_queue_t _queue;
NSMutableArray *_formatters;
}
- (DDLogMessage *)logMessageForLine:(NSString *)line originalMessage:(DDLogMessage *)message;
@end
@implementation DDMultiFormatter
- (instancetype)init {
self = [super init];
if (self) {
#if MAC_OS_X_VERSION_MIN_REQUIRED >= 1070
_queue = dispatch_queue_create("cocoa.lumberjack.multiformatter", DISPATCH_QUEUE_CONCURRENT);
#else
_queue = dispatch_queue_create("cocoa.lumberjack.multiformatter", NULL);
#endif
_formatters = [NSMutableArray new];
}
return self;
}
#if NEEDS_DISPATCH_RETAIN_RELEASE
- (void)dealloc {
dispatch_release(_queue);
}
#endif
#pragma mark Processing
- (NSString *)formatLogMessage:(DDLogMessage *)logMessage {
__block NSString *line = logMessage->_message;
dispatch_sync(_queue, ^{
for (id<DDLogFormatter> formatter in _formatters) {
DDLogMessage *message = [self logMessageForLine:line originalMessage:logMessage];
line = [formatter formatLogMessage:message];
if (!line) {
break;
}
}
});
return line;
}
- (DDLogMessage *)logMessageForLine:(NSString *)line originalMessage:(DDLogMessage *)message {
DDLogMessage *newMessage = [message copy];
newMessage->_message = line;
return newMessage;
}
#pragma mark Formatters
- (NSArray *)formatters {
__block NSArray *formatters;
dispatch_sync(_queue, ^{
formatters = [_formatters copy];
});
return formatters;
}
- (void)addFormatter:(id<DDLogFormatter>)formatter {
dispatch_barrier_async(_queue, ^{
[_formatters addObject:formatter];
});
}
- (void)removeFormatter:(id<DDLogFormatter>)formatter {
dispatch_barrier_async(_queue, ^{
[_formatters removeObject:formatter];
});
}
- (void)removeAllFormatters {
dispatch_barrier_async(_queue, ^{
[_formatters removeAllObjects];
});
}
- (BOOL)isFormattingWithFormatter:(id<DDLogFormatter>)formatter {
__block BOOL hasFormatter;
dispatch_sync(_queue, ^{
hasFormatter = [_formatters containsObject:formatter];
});
return hasFormatter;
}
@end