优化RTMP播放器重连和稳定性

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
caleb
2026-04-16 14:42:54 +08:00
parent f78ca66860
commit 72bfbb5cdf
81 changed files with 4869 additions and 5461 deletions

View File

@@ -1,6 +1,6 @@
// Software License Agreement (BSD License)
//
// Copyright (c) 2010-2021, Deusty, LLC
// Copyright (c) 2010-2026, Deusty, LLC
// All rights reserved.
//
// Redistribution and use of this software in source and binary forms,
@@ -29,7 +29,7 @@
@implementation CLIColor
+ (instancetype)colorWithCalibratedRed:(CGFloat)red green:(CGFloat)green blue:(CGFloat)blue alpha:(CGFloat)alpha {
CLIColor *color = [CLIColor new];
__auto_type color = [CLIColor new];
color->_red = red;
color->_green = green;
color->_blue = blue;

View File

@@ -1,6 +1,6 @@
// Software License Agreement (BSD License)
//
// Copyright (c) 2010-2021, Deusty, LLC
// Copyright (c) 2010-2026, Deusty, LLC
// All rights reserved.
//
// Redistribution and use of this software in source and binary forms,
@@ -29,8 +29,8 @@
#define DD_LEGACY_MACROS 0
#endif
static BOOL _cancel = YES;
static DDLogLevel _captureLevel = DDLogLevelVerbose;
static __auto_type _cancel = YES;
static __auto_type _captureLevel = DDLogLevelVerbose;
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated-implementations"
@@ -73,7 +73,7 @@ static DDLogLevel _captureLevel = DDLogLevelVerbose;
asl_set_query(query, kDDASLKeyDDLog, kDDASLDDLogValue, ASL_QUERY_OP_NOT_EQUAL);
#if !TARGET_OS_IPHONE || (defined(TARGET_SIMULATOR) && TARGET_SIMULATOR)
int processId = [[NSProcessInfo processInfo] processIdentifier];
__auto_type processId = [[NSProcessInfo processInfo] processIdentifier];
char pid[16];
snprintf(pid, sizeof(pid), "%d", processId);
asl_set_query(query, ASL_KEY_PID, pid, ASL_QUERY_OP_EQUAL | ASL_QUERY_OP_NUMERIC);
@@ -81,14 +81,14 @@ static DDLogLevel _captureLevel = DDLogLevelVerbose;
}
+ (void)aslMessageReceived:(aslmsg)msg {
const char* messageCString = asl_get( msg, ASL_KEY_MSG );
if ( messageCString == NULL )
__auto_type messageCString = asl_get(msg, ASL_KEY_MSG);
if (messageCString == NULL)
return;
DDLogFlag flag;
BOOL async;
const char* levelCString = asl_get(msg, ASL_KEY_LEVEL);
__auto_type levelCString = asl_get(msg, ASL_KEY_LEVEL);
switch (levelCString? atoi(levelCString) : 0) {
// By default all NSLog's with a ASL_LEVEL_WARNING level
case ASL_LEVEL_EMERG :
@@ -109,25 +109,25 @@ static DDLogLevel _captureLevel = DDLogLevelVerbose;
// NSString * sender = [NSString stringWithCString:asl_get(msg, ASL_KEY_SENDER) encoding:NSUTF8StringEncoding];
NSString *message = @(messageCString);
const char* secondsCString = asl_get( msg, ASL_KEY_TIME );
const char* nanoCString = asl_get( msg, ASL_KEY_TIME_NSEC );
NSTimeInterval seconds = secondsCString ? strtod(secondsCString, NULL) : [NSDate timeIntervalSinceReferenceDate] - NSTimeIntervalSince1970;
double nanoSeconds = nanoCString? strtod(nanoCString, NULL) : 0;
NSTimeInterval totalSeconds = seconds + (nanoSeconds / 1e9);
__auto_type secondsCString = asl_get(msg, ASL_KEY_TIME);
__auto_type nanoCString = asl_get(msg, ASL_KEY_TIME_NSEC);
__auto_type seconds = secondsCString ? strtod(secondsCString, NULL) : [NSDate timeIntervalSinceReferenceDate] - NSTimeIntervalSince1970;
__auto_type nanoSeconds = nanoCString? strtod(nanoCString, NULL) : 0;
__auto_type totalSeconds = seconds + (nanoSeconds / 1e9);
NSDate *timeStamp = [NSDate dateWithTimeIntervalSince1970:totalSeconds];
__auto_type timeStamp = [NSDate dateWithTimeIntervalSince1970:totalSeconds];
__auto_type logMessage = [[DDLogMessage alloc] initWithMessage:message
level:_captureLevel
flag:flag
context:0
file:@"DDASLLogCapture"
function:nil
line:0
tag:nil
options:DDLogMessageDontCopyMessage
timestamp:timeStamp];
DDLogMessage *logMessage = [[DDLogMessage alloc] initWithMessage:message
level:_captureLevel
flag:flag
context:0
file:@"DDASLLogCapture"
function:nil
line:0
tag:nil
options:0
timestamp:timeStamp];
[DDLog log:async message:logMessage];
}
@@ -144,7 +144,7 @@ static DDLogLevel _captureLevel = DDLogLevelVerbose;
.tv_sec = 0
};
gettimeofday(&timeval, NULL);
unsigned long long startTime = (unsigned long long)timeval.tv_sec;
__auto_type startTime = (unsigned long long)timeval.tv_sec;
__block unsigned long long lastSeenID = 0;
/*
@@ -163,7 +163,7 @@ static DDLogLevel _captureLevel = DDLogLevelVerbose;
// At least one message has been posted; build a search query.
@autoreleasepool
{
aslmsg query = asl_new(ASL_TYPE_QUERY);
__auto_type query = asl_new(ASL_TYPE_QUERY);
char stringValue[64];
if (lastSeenID > 0) {
@@ -178,8 +178,8 @@ static DDLogLevel _captureLevel = DDLogLevelVerbose;
// Iterate over new messages.
aslmsg msg;
aslresponse response = asl_search(NULL, query);
__auto_type response = asl_search(NULL, query);
while ((msg = asl_next(response)))
{
[self aslMessageReceived:msg];

View File

@@ -1,6 +1,6 @@
// Software License Agreement (BSD License)
//
// Copyright (c) 2010-2021, Deusty, LLC
// Copyright (c) 2010-2026, Deusty, LLC
// All rights reserved.
//
// Redistribution and use of this software in source and binary forms,
@@ -80,10 +80,10 @@ static DDASLLogger *sharedInstance;
return;
}
NSString * message = _logFormatter ? [_logFormatter formatLogMessage:logMessage] : logMessage->_message;
__auto_type message = _logFormatter ? [_logFormatter formatLogMessage:logMessage] : logMessage->_message;
if (message) {
const char *msg = [message UTF8String];
__auto_type msg = [message UTF8String];
size_t aslLogLevel;
switch (logMessage->_flag) {
@@ -100,11 +100,11 @@ static DDASLLogger *sharedInstance;
static char const *const level_strings[] = { "0", "1", "2", "3", "4", "5", "6", "7" };
// NSLog uses the current euid to set the ASL_KEY_READ_UID.
uid_t const readUID = geteuid();
const __auto_type readUID = geteuid();
char readUIDString[16];
#ifndef NS_BLOCK_ASSERTIONS
size_t l = (size_t)snprintf(readUIDString, sizeof(readUIDString), "%d", readUID);
__auto_type l = (size_t)snprintf(readUIDString, sizeof(readUIDString), "%d", readUID);
#else
snprintf(readUIDString, sizeof(readUIDString), "%d", readUID);
#endif
@@ -114,7 +114,7 @@ static DDASLLogger *sharedInstance;
NSAssert(aslLogLevel < (sizeof(level_strings) / sizeof(level_strings[0])),
@"Unhandled ASL log level.");
aslmsg m = asl_new(ASL_TYPE_MSG);
__auto_type m = asl_new(ASL_TYPE_MSG);
if (m != NULL) {
if (asl_set(m, ASL_KEY_LEVEL, level_strings[aslLogLevel]) == 0 &&
asl_set(m, ASL_KEY_MSG, msg) == 0 &&

View File

@@ -1,6 +1,6 @@
// Software License Agreement (BSD License)
//
// Copyright (c) 2010-2021, Deusty, LLC
// Copyright (c) 2010-2026, Deusty, LLC
// All rights reserved.
//
// Redistribution and use of this software in source and binary forms,
@@ -114,21 +114,15 @@
dispatch_source_cancel(_saveTimer);
// Must activate a timer before releasing it (or it will crash)
if (@available(macOS 10.12, iOS 10.0, tvOS 10.0, watchOS 3.0, *)) {
if (_saveTimerSuspended < 0) {
dispatch_activate(_saveTimer);
} else if (_saveTimerSuspended > 0) {
dispatch_resume(_saveTimer);
}
} else {
if (_saveTimerSuspended != 0) {
dispatch_resume(_saveTimer);
}
if (_saveTimerSuspended < 0) {
dispatch_activate(_saveTimer);
} else if (_saveTimerSuspended > 0) {
dispatch_resume(_saveTimer);
}
#if !OS_OBJECT_USE_OBJC
#if !OS_OBJECT_USE_OBJC
dispatch_release(_saveTimer);
#endif
#endif
_saveTimer = NULL;
_saveTimerSuspended = 0;
}
@@ -136,24 +130,17 @@
- (void)updateAndResumeSaveTimer {
if ((_saveTimer != NULL) && (_saveInterval > 0.0) && (_unsavedTime > 0)) {
uint64_t interval = (uint64_t)(_saveInterval * (NSTimeInterval) NSEC_PER_SEC);
dispatch_time_t startTime = dispatch_time(_unsavedTime, (int64_t)interval);
__auto_type interval = (uint64_t)(_saveInterval * (NSTimeInterval) NSEC_PER_SEC);
__auto_type startTime = dispatch_time(_unsavedTime, (int64_t)interval);
dispatch_source_set_timer(_saveTimer, startTime, interval, 1ull * NSEC_PER_SEC);
if (@available(macOS 10.12, iOS 10.0, tvOS 10.0, watchOS 3.0, *)) {
if (_saveTimerSuspended < 0) {
dispatch_activate(_saveTimer);
_saveTimerSuspended = 0;
} else if (_saveTimerSuspended > 0) {
dispatch_resume(_saveTimer);
_saveTimerSuspended = 0;
}
} else {
if (_saveTimerSuspended != 0) {
dispatch_resume(_saveTimer);
_saveTimerSuspended = 0;
}
if (_saveTimerSuspended < 0) {
dispatch_activate(_saveTimer);
_saveTimerSuspended = 0;
} else if (_saveTimerSuspended > 0) {
dispatch_resume(_saveTimer);
_saveTimerSuspended = 0;
}
}
}
@@ -163,8 +150,8 @@
_saveTimer = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, self.loggerQueue);
dispatch_source_set_event_handler(_saveTimer, ^{ @autoreleasepool {
[self performSaveAndSuspendSaveTimer];
} });
[self performSaveAndSuspendSaveTimer];
} });
_saveTimerSuspended = -1;
}
@@ -173,16 +160,16 @@
- (void)destroyDeleteTimer {
if (_deleteTimer != NULL) {
dispatch_source_cancel(_deleteTimer);
#if !OS_OBJECT_USE_OBJC
#if !OS_OBJECT_USE_OBJC
dispatch_release(_deleteTimer);
#endif
#endif
_deleteTimer = NULL;
}
}
- (void)updateDeleteTimer {
if ((_deleteTimer != NULL) && (_deleteInterval > 0.0) && (_maxAge > 0.0)) {
int64_t interval = (int64_t)(_deleteInterval * (NSTimeInterval) NSEC_PER_SEC);
__auto_type interval = (int64_t)(_deleteInterval * (NSTimeInterval) NSEC_PER_SEC);
dispatch_time_t startTime;
if (_lastDeleteTime > 0) {
@@ -201,17 +188,14 @@
if (_deleteTimer != NULL) {
dispatch_source_set_event_handler(_deleteTimer, ^{ @autoreleasepool {
[self performDelete];
} });
[self performDelete];
} });
[self updateDeleteTimer];
// We are sure that -updateDeleteTimer did call dispatch_source_set_timer()
// since it has the same guards on _deleteInterval and _maxAge
if (@available(macOS 10.12, iOS 10.0, tvOS 10.0, watchOS 3.0, *))
dispatch_activate(_deleteTimer);
else
dispatch_resume(_deleteTimer);
dispatch_activate(_deleteTimer);
}
}
}
@@ -231,14 +215,10 @@
// This is the intended result. Fix it by accessing the ivar directly.
// Great strides have been take to ensure this is safe to do. Plus it's MUCH faster.
NSAssert(![self isOnGlobalLoggingQueue], @"Core architecture requirement failure");
NSAssert(![self isOnInternalLoggerQueue], @"MUST access ivar directly, NOT via self.* syntax.");
dispatch_queue_t globalLoggingQueue = [DDLog loggingQueue];
DDAbstractLoggerAssertLockedPropertyAccess();
__block NSUInteger result;
dispatch_sync(globalLoggingQueue, ^{
dispatch_sync(DDLog.loggingQueue, ^{
dispatch_sync(self.loggerQueue, ^{
result = self->_saveThreshold;
});
@@ -271,10 +251,8 @@
if ([self isOnInternalLoggerQueue]) {
block();
} else {
dispatch_queue_t globalLoggingQueue = [DDLog loggingQueue];
NSAssert(![self isOnGlobalLoggingQueue], @"Core architecture requirement failure");
dispatch_async(globalLoggingQueue, ^{
DDAbstractLoggerAssertNotOnGlobalLoggingQueue();
dispatch_async(DDLog.loggingQueue, ^{
dispatch_async(self.loggerQueue, block);
});
}
@@ -291,14 +269,10 @@
// This is the intended result. Fix it by accessing the ivar directly.
// Great strides have been take to ensure this is safe to do. Plus it's MUCH faster.
NSAssert(![self isOnGlobalLoggingQueue], @"Core architecture requirement failure");
NSAssert(![self isOnInternalLoggerQueue], @"MUST access ivar directly, NOT via self.* syntax.");
dispatch_queue_t globalLoggingQueue = [DDLog loggingQueue];
DDAbstractLoggerAssertLockedPropertyAccess();
__block NSTimeInterval result;
dispatch_sync(globalLoggingQueue, ^{
dispatch_sync(DDLog.loggingQueue, ^{
dispatch_sync(self.loggerQueue, ^{
result = self->_saveInterval;
});
@@ -308,7 +282,7 @@
}
- (void)setSaveInterval:(NSTimeInterval)interval {
dispatch_block_t block = ^{
__auto_type block = ^{
@autoreleasepool {
// C99 recommended floating point comparison macro
// Read: isLessThanOrGreaterThan(floatA, floatB)
@@ -362,10 +336,8 @@
if ([self isOnInternalLoggerQueue]) {
block();
} else {
dispatch_queue_t globalLoggingQueue = [DDLog loggingQueue];
NSAssert(![self isOnGlobalLoggingQueue], @"Core architecture requirement failure");
dispatch_async(globalLoggingQueue, ^{
DDAbstractLoggerAssertNotOnGlobalLoggingQueue();
dispatch_async(DDLog.loggingQueue, ^{
dispatch_async(self.loggerQueue, block);
});
}
@@ -382,14 +354,11 @@
// This is the intended result. Fix it by accessing the ivar directly.
// Great strides have been take to ensure this is safe to do. Plus it's MUCH faster.
NSAssert(![self isOnGlobalLoggingQueue], @"Core architecture requirement failure");
NSAssert(![self isOnInternalLoggerQueue], @"MUST access ivar directly, NOT via self.* syntax.");
dispatch_queue_t globalLoggingQueue = [DDLog loggingQueue];
DDAbstractLoggerAssertLockedPropertyAccess();
__block NSTimeInterval result;
dispatch_sync(globalLoggingQueue, ^{
dispatch_sync(DDLog.loggingQueue, ^{
dispatch_sync(self.loggerQueue, ^{
result = self->_maxAge;
});
@@ -399,14 +368,14 @@
}
- (void)setMaxAge:(NSTimeInterval)interval {
dispatch_block_t block = ^{
__auto_type block = ^{
@autoreleasepool {
// C99 recommended floating point comparison macro
// Read: isLessThanOrGreaterThan(floatA, floatB)
if (/* maxAge != interval */ islessgreater(self->_maxAge, interval)) {
NSTimeInterval oldMaxAge = self->_maxAge;
NSTimeInterval newMaxAge = interval;
__auto_type oldMaxAge = self->_maxAge;
__auto_type newMaxAge = interval;
self->_maxAge = interval;
@@ -424,7 +393,7 @@
// 4. If the maxAge was decreased,
// then we should do an immediate delete.
BOOL shouldDeleteNow = NO;
__auto_type shouldDeleteNow = NO;
if (oldMaxAge > 0.0) {
if (newMaxAge <= 0.0) {
@@ -459,10 +428,8 @@
if ([self isOnInternalLoggerQueue]) {
block();
} else {
dispatch_queue_t globalLoggingQueue = [DDLog loggingQueue];
NSAssert(![self isOnGlobalLoggingQueue], @"Core architecture requirement failure");
dispatch_async(globalLoggingQueue, ^{
DDAbstractLoggerAssertNotOnGlobalLoggingQueue();
dispatch_async(DDLog.loggingQueue, ^{
dispatch_async(self.loggerQueue, block);
});
}
@@ -479,14 +446,11 @@
// This is the intended result. Fix it by accessing the ivar directly.
// Great strides have been take to ensure this is safe to do. Plus it's MUCH faster.
NSAssert(![self isOnGlobalLoggingQueue], @"Core architecture requirement failure");
NSAssert(![self isOnInternalLoggerQueue], @"MUST access ivar directly, NOT via self.* syntax.");
dispatch_queue_t globalLoggingQueue = [DDLog loggingQueue];
DDAbstractLoggerAssertLockedPropertyAccess();
__block NSTimeInterval result;
dispatch_sync(globalLoggingQueue, ^{
dispatch_sync(DDLog.loggingQueue, ^{
dispatch_sync(self.loggerQueue, ^{
result = self->_deleteInterval;
});
@@ -496,7 +460,7 @@
}
- (void)setDeleteInterval:(NSTimeInterval)interval {
dispatch_block_t block = ^{
__auto_type block = ^{
@autoreleasepool {
// C99 recommended floating point comparison macro
// Read: isLessThanOrGreaterThan(floatA, floatB)
@@ -549,10 +513,9 @@
if ([self isOnInternalLoggerQueue]) {
block();
} else {
dispatch_queue_t globalLoggingQueue = [DDLog loggingQueue];
NSAssert(![self isOnGlobalLoggingQueue], @"Core architecture requirement failure");
DDAbstractLoggerAssertNotOnGlobalLoggingQueue();
dispatch_async(globalLoggingQueue, ^{
dispatch_async(DDLog.loggingQueue, ^{
dispatch_async(self.loggerQueue, block);
});
}
@@ -569,14 +532,11 @@
// This is the intended result. Fix it by accessing the ivar directly.
// Great strides have been take to ensure this is safe to do. Plus it's MUCH faster.
NSAssert(![self isOnGlobalLoggingQueue], @"Core architecture requirement failure");
NSAssert(![self isOnInternalLoggerQueue], @"MUST access ivar directly, NOT via self.* syntax.");
dispatch_queue_t globalLoggingQueue = [DDLog loggingQueue];
DDAbstractLoggerAssertLockedPropertyAccess();
__block BOOL result;
dispatch_sync(globalLoggingQueue, ^{
dispatch_sync(DDLog.loggingQueue, ^{
dispatch_sync(self.loggerQueue, ^{
result = self->_deleteOnEverySave;
});
@@ -586,7 +546,7 @@
}
- (void)setDeleteOnEverySave:(BOOL)flag {
dispatch_block_t block = ^{
__auto_type block = ^{
self->_deleteOnEverySave = flag;
};
@@ -596,10 +556,8 @@
if ([self isOnInternalLoggerQueue]) {
block();
} else {
dispatch_queue_t globalLoggingQueue = [DDLog loggingQueue];
NSAssert(![self isOnGlobalLoggingQueue], @"Core architecture requirement failure");
dispatch_async(globalLoggingQueue, ^{
DDAbstractLoggerAssertNotOnGlobalLoggingQueue();
dispatch_async(DDLog.loggingQueue, ^{
dispatch_async(self.loggerQueue, block);
});
}
@@ -610,7 +568,7 @@
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
- (void)savePendingLogEntries {
dispatch_block_t block = ^{
__auto_type block = ^{
@autoreleasepool {
[self performSaveAndSuspendSaveTimer];
}
@@ -624,7 +582,7 @@
}
- (void)deleteOldLogEntries {
dispatch_block_t block = ^{
__auto_type block = ^{
@autoreleasepool {
[self performDelete];
}
@@ -643,24 +601,20 @@
- (void)didAddLogger {
// If you override me be sure to invoke [super didAddLogger];
[self createSuspendedSaveTimer];
[self createAndStartDeleteTimer];
}
- (void)willRemoveLogger {
// If you override me be sure to invoke [super willRemoveLogger];
[self performSaveAndSuspendSaveTimer];
[self destroySaveTimer];
[self destroyDeleteTimer];
}
- (void)logMessage:(DDLogMessage *)logMessage {
if ([self db_log:logMessage]) {
BOOL firstUnsavedEntry = (++_unsavedCount == 1);
__auto_type firstUnsavedEntry = (++_unsavedCount == 1);
if ((_unsavedCount >= _saveThreshold) && (_saveThreshold > 0)) {
[self performSaveAndSuspendSaveTimer];
@@ -676,7 +630,6 @@
//
// It is called automatically when the application quits,
// or if the developer invokes DDLog's flushLog method prior to crashing or something.
[self performSaveAndSuspendSaveTimer];
}

View File

@@ -1,6 +1,6 @@
// Software License Agreement (BSD License)
//
// Copyright (c) 2010-2021, Deusty, LLC
// Copyright (c) 2010-2026, Deusty, LLC
// All rights reserved.
//
// Redistribution and use of this software in source and binary forms,

File diff suppressed because it is too large Load Diff

View File

@@ -1,6 +1,6 @@
// Software License Agreement (BSD License)
//
// Copyright (c) 2010-2021, Deusty, LLC
// Copyright (c) 2010-2026, Deusty, LLC
// All rights reserved.
//
// Redistribution and use of this software in source and binary forms,
@@ -48,6 +48,11 @@
#define NSLogDebug(frmt, ...) do{ if(DD_DEBUG) NSLog((frmt), ##__VA_ARGS__); } while(0)
#define DDLogAssertOnGlobalLoggingQueue() \
NSAssert(dispatch_get_specific(GlobalLoggingQueueIdentityKey), @"This method must be called on the logging thread/queue!")
#define DDLogAssertNotOnGlobalLoggingQueue() \
NSAssert(!dispatch_get_specific(GlobalLoggingQueueIdentityKey), @"This method must not be called on the logging thread/queue!")
// The "global logging queue" refers to [DDLog loggingQueue].
// It is the queue that all log statements go through.
//
@@ -107,7 +112,7 @@ static NSUInteger _numProcessors;
* @return The singleton `DDLog`.
*/
+ (instancetype)sharedInstance {
static id sharedInstance = nil;
static DDLog *sharedInstance = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
@@ -159,7 +164,7 @@ static NSUInteger _numProcessors;
self._loggers = [[NSMutableArray alloc] initWithCapacity:4];
#if TARGET_OS_IOS
NSString *notificationName = UIApplicationWillTerminateNotification;
__auto_type notificationName = UIApplicationWillTerminateNotification;
#else
NSString *notificationName = nil;
@@ -317,7 +322,7 @@ static NSUInteger _numProcessors;
// Now assume we have another separate thread that attempts to issue log message G.
// It should block until log messages A and B have been unqueued.
dispatch_block_t logBlock = ^{
__auto_type logBlock = ^{
// We're now sure we won't overflow the queue.
// It is time to queue our log message.
@autoreleasepool {
@@ -349,21 +354,16 @@ static NSUInteger _numProcessors;
if (format) {
va_start(args, format);
NSString *message = [[NSString alloc] initWithFormat:format arguments:args];
va_end(args);
va_start(args, format);
[self log:asynchronous
message:message
level:level
flag:flag
context:context
file:file
function:function
line:line
tag:tag];
tag:tag
format:format
args:args];
va_end(args);
}
@@ -383,21 +383,16 @@ static NSUInteger _numProcessors;
if (format) {
va_start(args, format);
NSString *message = [[NSString alloc] initWithFormat:format arguments:args];
va_end(args);
va_start(args, format);
[self log:asynchronous
message:message
level:level
flag:flag
context:context
file:file
function:function
line:line
tag:tag];
tag:tag
format:format
args:args];
va_end(args);
}
@@ -427,54 +422,26 @@ static NSUInteger _numProcessors;
format:(NSString *)format
args:(va_list)args {
if (format) {
NSString *message = [[NSString alloc] initWithFormat:format arguments:args];
[self log:asynchronous
message:message
level:level
flag:flag
context:context
file:file
function:function
line:line
tag:tag];
// Null checks are handled by -initWithMessage:
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wnullable-to-nonnull-conversion"
__auto_type logMessage = [[DDLogMessage alloc] initWithFormat:format
args:args
level:level
flag:flag
context:context
file:@(file)
function:@(function)
line:line
tag:tag
options:(DDLogMessageOptions)0
timestamp:nil];
#pragma clang diagnostic pop
[self queueLogMessage:logMessage asynchronously:asynchronous];
}
}
+ (void)log:(BOOL)asynchronous
message:(NSString *)message
level:(DDLogLevel)level
flag:(DDLogFlag)flag
context:(NSInteger)context
file:(const char *)file
function:(const char *)function
line:(NSUInteger)line
tag:(id)tag {
[self.sharedInstance log:asynchronous message:message level:level flag:flag context:context file:file function:function line:line tag:tag];
}
- (void)log:(BOOL)asynchronous
message:(NSString *)message
level:(DDLogLevel)level
flag:(DDLogFlag)flag
context:(NSInteger)context
file:(const char *)file
function:(const char *)function
line:(NSUInteger)line
tag:(id)tag {
DDLogMessage *logMessage = [[DDLogMessage alloc] initWithMessage:message
level:level
flag:flag
context:context
file:[NSString stringWithFormat:@"%s", file]
function:[NSString stringWithFormat:@"%s", function]
line:line
tag:tag
options:(DDLogMessageOptions)0
timestamp:nil];
[self queueLogMessage:logMessage asynchronously:asynchronous];
}
+ (void)log:(BOOL)asynchronous message:(DDLogMessage *)logMessage {
[self.sharedInstance log:asynchronous message:logMessage];
}
@@ -488,12 +455,12 @@ static NSUInteger _numProcessors;
}
- (void)flushLog {
NSAssert(!dispatch_get_specific(GlobalLoggingQueueIdentityKey),
@"This method shouldn't be run on the logging thread/queue that make flush fast enough");
dispatch_sync(_loggingQueue, ^{ @autoreleasepool {
[self lt_flush];
} });
DDLogAssertNotOnGlobalLoggingQueue();
dispatch_sync(_loggingQueue, ^{
@autoreleasepool {
[self lt_flush];
}
});
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
@@ -501,69 +468,65 @@ static NSUInteger _numProcessors;
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
+ (BOOL)isRegisteredClass:(Class)class {
SEL getterSel = @selector(ddLogLevel);
SEL setterSel = @selector(ddSetLogLevel:);
#if TARGET_OS_IPHONE && !TARGET_OS_SIMULATOR
__auto_type getterSel = @selector(ddLogLevel);
__auto_type setterSel = @selector(ddSetLogLevel:);
// Issue #6 (GoogleCode) - Crashes on iOS 4.2.1 and iPhone 4
//
// Crash caused by class_getClassMethod(2).
//
// "It's a bug with UIAccessibilitySafeCategory__NSObject so it didn't pop up until
// users had VoiceOver enabled [...]. I was able to work around it by searching the
// result of class_copyMethodList() instead of calling class_getClassMethod()"
//
// Issue #24 (GitHub) - Crashing in in ARC+Simulator
// The method +[DDLog isRegisteredClass] will crash a project when using it with ARC + Simulator.
// For running in the Simulator, it needs to execute the non-iOS code. Unless we're running on iOS 17+.
BOOL result = NO;
#if TARGET_OS_IPHONE
#if TARGET_OS_SIMULATOR
if (@available(iOS 17, tvOS 17, *)) {
#endif
__auto_type result = NO;
unsigned int methodCount, i;
__auto_type methodList = class_copyMethodList(object_getClass(class), &methodCount);
unsigned int methodCount, i;
Method *methodList = class_copyMethodList(object_getClass(class), &methodCount);
if (methodList != NULL) {
__auto_type getterFound = NO;
__auto_type setterFound = NO;
if (methodList != NULL) {
BOOL getterFound = NO;
BOOL setterFound = NO;
for (i = 0; i < methodCount; ++i) {
__auto_type currentSel = method_getName(methodList[i]);
for (i = 0; i < methodCount; ++i) {
SEL currentSel = method_getName(methodList[i]);
if (currentSel == getterSel) {
getterFound = YES;
} else if (currentSel == setterSel) {
setterFound = YES;
}
if (currentSel == getterSel) {
getterFound = YES;
} else if (currentSel == setterSel) {
setterFound = YES;
if (getterFound && setterFound) {
result = YES;
break;
}
}
if (getterFound && setterFound) {
result = YES;
break;
}
free(methodList);
}
free(methodList);
return result;
#if TARGET_OS_SIMULATOR
} else {
#endif /* TARGET_OS_SIMULATOR */
#endif /* TARGET_OS_IPHONE */
#if !TARGET_OS_IPHONE || TARGET_OS_SIMULATOR
__auto_type getter = class_getClassMethod(class, getterSel);
__auto_type setter = class_getClassMethod(class, setterSel);
return (getter != NULL) && (setter != NULL);
#endif /* !TARGET_OS_IPHONE || TARGET_OS_SIMULATOR */
#if TARGET_OS_IPHONE && TARGET_OS_SIMULATOR
}
return result;
#else /* if TARGET_OS_IPHONE && !TARGET_OS_SIMULATOR */
// Issue #24 (GitHub) - Crashing in in ARC+Simulator
//
// The method +[DDLog isRegisteredClass] will crash a project when using it with ARC + Simulator.
// For running in the Simulator, it needs to execute the non-iOS code.
Method getter = class_getClassMethod(class, getterSel);
Method setter = class_getClassMethod(class, setterSel);
if ((getter != NULL) && (setter != NULL)) {
return YES;
}
return NO;
#endif /* if TARGET_OS_IPHONE && !TARGET_OS_SIMULATOR */
#endif /* TARGET_OS_IPHONE && TARGET_OS_SIMULATOR */
}
+ (NSArray *)registeredClasses {
// We're going to get the list of all registered classes.
// The Objective-C runtime library automatically registers all the classes defined in your source code.
//
@@ -579,23 +542,19 @@ static NSUInteger _numProcessors;
Class *classes = NULL;
while (numClasses == 0) {
numClasses = (NSUInteger)MAX(objc_getClassList(NULL, 0), 0);
// numClasses now tells us how many classes we have (but it might change)
// So we can allocate our buffer, and get pointers to all the class definitions.
NSUInteger bufferSize = numClasses;
__auto_type bufferSize = numClasses;
classes = numClasses ? (Class *)calloc(bufferSize, sizeof(Class)) : NULL;
if (classes == NULL) {
return @[]; //no memory or classes?
return @[]; // no memory or classes?
}
numClasses = (NSUInteger)MAX(objc_getClassList(classes, (int)bufferSize),0);
if (numClasses > bufferSize || numClasses == 0) {
//apparently more classes added between calls (or a problem); try again
// apparently more classes added between calls (or a problem); try again
free(classes);
classes = NULL;
numClasses = 0;
@@ -603,10 +562,9 @@ static NSUInteger _numProcessors;
}
// We can now loop through the classes, and test each one to see if it is a DDLogging class.
NSMutableArray *result = [NSMutableArray arrayWithCapacity:numClasses];
__auto_type result = [NSMutableArray arrayWithCapacity:numClasses];
for (NSUInteger i = 0; i < numClasses; i++) {
// Cannot use `__auto_type` here, since this will lead to crashes when deallocating!
Class class = classes[i];
if ([self isRegisteredClass:class]) {
@@ -620,8 +578,8 @@ static NSUInteger _numProcessors;
}
+ (NSArray *)registeredClassNames {
NSArray *registeredClasses = [self registeredClasses];
NSMutableArray *result = [NSMutableArray arrayWithCapacity:[registeredClasses count]];
__auto_type registeredClasses = [self registeredClasses];
__auto_type result = [NSMutableArray arrayWithCapacity:[registeredClasses count]];
for (Class class in registeredClasses) {
[result addObject:NSStringFromClass(class)];
@@ -637,9 +595,9 @@ static NSUInteger _numProcessors;
}
+ (DDLogLevel)levelForClassWithName:(NSString *)aClassName {
Class aClass = NSClassFromString(aClassName);
return [self levelForClass:aClass];
Class clazz = NSClassFromString(aClassName);
if (clazz == nil) return (DDLogLevel)-1;
return [self levelForClass:clazz];
}
+ (void)setLevel:(DDLogLevel)level forClass:(Class)aClass {
@@ -649,8 +607,9 @@ static NSUInteger _numProcessors;
}
+ (void)setLevel:(DDLogLevel)level forClassWithName:(NSString *)aClassName {
Class aClass = NSClassFromString(aClassName);
[self setLevel:level forClass:aClass];
Class clazz = NSClassFromString(aClassName);
if (clazz == nil) return;
[self setLevel:level forClass:clazz];
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
@@ -662,15 +621,13 @@ static NSUInteger _numProcessors;
// Need to create loggerQueue if loggerNode doesn't provide one.
for (DDLoggerNode *node in self._loggers) {
if (node->_logger == logger
&& node->_level == level) {
if (node->_logger == logger && node->_level == level) {
// Exactly same logger already added, exit
return;
}
}
NSAssert(dispatch_get_specific(GlobalLoggingQueueIdentityKey),
@"This method should only be run on the logging thread/queue");
DDLogAssertOnGlobalLoggingQueue();
dispatch_queue_t loggerQueue = NULL;
if ([logger respondsToSelector:@selector(loggerQueue)]) {
@@ -690,7 +647,7 @@ static NSUInteger _numProcessors;
loggerQueue = dispatch_queue_create(loggerQueueName, NULL);
}
DDLoggerNode *loggerNode = [DDLoggerNode nodeWithLogger:logger loggerQueue:loggerQueue level:level];
__auto_type loggerNode = [DDLoggerNode nodeWithLogger:logger loggerQueue:loggerQueue level:level];
[self._loggers addObject:loggerNode];
if ([logger respondsToSelector:@selector(didAddLoggerInQueue:)]) {
@@ -707,8 +664,7 @@ static NSUInteger _numProcessors;
- (void)lt_removeLogger:(id <DDLogger>)logger {
// Find associated loggerNode in list of added loggers
NSAssert(dispatch_get_specific(GlobalLoggingQueueIdentityKey),
@"This method should only be run on the logging thread/queue");
DDLogAssertOnGlobalLoggingQueue();
DDLoggerNode *loggerNode = nil;
@@ -736,8 +692,7 @@ static NSUInteger _numProcessors;
}
- (void)lt_removeAllLoggers {
NSAssert(dispatch_get_specific(GlobalLoggingQueueIdentityKey),
@"This method should only be run on the logging thread/queue");
DDLogAssertOnGlobalLoggingQueue();
// Notify all loggers
for (DDLoggerNode *loggerNode in self._loggers) {
@@ -749,17 +704,16 @@ static NSUInteger _numProcessors;
}
// Remove all loggers from array
[self._loggers removeAllObjects];
}
- (NSArray *)lt_allLoggers {
NSAssert(dispatch_get_specific(GlobalLoggingQueueIdentityKey),
@"This method should only be run on the logging thread/queue");
DDLogAssertOnGlobalLoggingQueue();
NSMutableArray *theLoggers = [NSMutableArray new];
__auto_type loggerNodes = self._loggers;
__auto_type theLoggers = [NSMutableArray arrayWithCapacity:loggerNodes.count];
for (DDLoggerNode *loggerNode in self._loggers) {
for (DDLoggerNode *loggerNode in loggerNodes) {
[theLoggers addObject:loggerNode->_logger];
}
@@ -767,12 +721,13 @@ static NSUInteger _numProcessors;
}
- (NSArray *)lt_allLoggersWithLevel {
NSAssert(dispatch_get_specific(GlobalLoggingQueueIdentityKey),
@"This method should only be run on the logging thread/queue");
DDLogAssertOnGlobalLoggingQueue();
NSMutableArray *theLoggersWithLevel = [NSMutableArray new];
for (DDLoggerNode *loggerNode in self._loggers) {
__auto_type loggerNodes = self._loggers;
__auto_type theLoggersWithLevel = [NSMutableArray arrayWithCapacity:loggerNodes.count];
for (DDLoggerNode *loggerNode in loggerNodes) {
[theLoggersWithLevel addObject:[DDLoggerInformation informationWithLogger:loggerNode->_logger
andLevel:loggerNode->_level]];
}
@@ -781,10 +736,9 @@ static NSUInteger _numProcessors;
}
- (void)lt_log:(DDLogMessage *)logMessage {
// Execute the given log message on each of our loggers.
DDLogAssertOnGlobalLoggingQueue();
NSAssert(dispatch_get_specific(GlobalLoggingQueueIdentityKey),
@"This method should only be run on the logging thread/queue");
// Execute the given log message on each of our loggers.
if (_numProcessors > 1) {
// Execute each logger concurrently, each within its own queue.
@@ -797,7 +751,7 @@ static NSUInteger _numProcessors;
for (DDLoggerNode *loggerNode in self._loggers) {
// skip the loggers that shouldn't write this message based on the log level
if (!(logMessage->_flag & loggerNode->_level)) {
if (!(logMessage->_flag & (DDLogFlag)(loggerNode->_level))) {
continue;
}
@@ -813,7 +767,7 @@ static NSUInteger _numProcessors;
for (DDLoggerNode *loggerNode in self._loggers) {
// skip the loggers that shouldn't write this message based on the log level
if (!(logMessage->_flag & loggerNode->_level)) {
if (!(logMessage->_flag & (DDLogFlag)(loggerNode->_level))) {
continue;
}
@@ -846,8 +800,7 @@ static NSUInteger _numProcessors;
// Now we need to propagate the flush request to any loggers that implement the flush method.
// This is designed for loggers that buffer IO.
NSAssert(dispatch_get_specific(GlobalLoggingQueueIdentityKey),
@"This method should only be run on the logging thread/queue");
DDLogAssertOnGlobalLoggingQueue();
for (DDLoggerNode *loggerNode in self._loggers) {
if ([loggerNode->_logger respondsToSelector:@selector(flush)]) {
@@ -872,8 +825,7 @@ NSString * __nullable DDExtractFileNameWithoutExtension(const char *filePath, BO
char *lastSlash = NULL;
char *lastDot = NULL;
char *p = (char *)filePath;
__auto_type p = (char *)filePath;
while (*p != '\0') {
if (*p == '/') {
lastSlash = p;
@@ -939,9 +891,9 @@ NSString * __nullable DDExtractFileNameWithoutExtension(const char *filePath, BO
if (loggerQueue) {
_loggerQueue = loggerQueue;
#if !OS_OBJECT_USE_OBJC
#if !OS_OBJECT_USE_OBJC
dispatch_retain(loggerQueue);
#endif
#endif
}
_level = level;
@@ -954,11 +906,11 @@ NSString * __nullable DDExtractFileNameWithoutExtension(const char *filePath, BO
}
- (void)dealloc {
#if !OS_OBJECT_USE_OBJC
#if !OS_OBJECT_USE_OBJC
if (_loggerQueue) {
dispatch_release(_loggerQueue);
}
#endif
#endif
}
@end
@@ -974,27 +926,33 @@ NSString * __nullable DDExtractFileNameWithoutExtension(const char *filePath, BO
return self;
}
- (instancetype)initWithMessage:(NSString *)message
level:(DDLogLevel)level
flag:(DDLogFlag)flag
context:(NSInteger)context
file:(NSString *)file
function:(NSString *)function
line:(NSUInteger)line
tag:(id)tag
options:(DDLogMessageOptions)options
timestamp:(NSDate *)timestamp {
if ((self = [super init])) {
BOOL copyMessage = (options & DDLogMessageDontCopyMessage) == 0;
_message = copyMessage ? [message copy] : message;
_level = level;
_flag = flag;
_context = context;
- (instancetype)initWithFormat:(NSString *)messageFormat
formatted:(NSString *)message
level:(DDLogLevel)level
flag:(DDLogFlag)flag
context:(NSInteger)context
file:(NSString *)file
function:(NSString *)function
line:(NSUInteger)line
tag:(id)tag
options:(DDLogMessageOptions)options
timestamp:(NSDate *)timestamp {
NSParameterAssert(messageFormat);
NSParameterAssert(message);
NSParameterAssert(file);
BOOL copyFile = (options & DDLogMessageCopyFile) != 0;
if ((self = [super init])) {
__auto_type copyMessage = (options & DDLogMessageDontCopyMessage) == 0;
_messageFormat = copyMessage ? [messageFormat copy] : messageFormat;
_message = copyMessage ? [message copy] : message;
_level = level;
_flag = flag;
_context = context;
__auto_type copyFile = (options & DDLogMessageCopyFile) != 0;
_file = copyFile ? [file copy] : file;
BOOL copyFunction = (options & DDLogMessageCopyFunction) != 0;
__auto_type copyFunction = (options & DDLogMessageCopyFunction) != 0;
_function = copyFunction ? [function copy] : function;
_line = line;
@@ -1006,48 +964,111 @@ NSString * __nullable DDExtractFileNameWithoutExtension(const char *filePath, BO
#pragma clang diagnostic pop
#endif
_options = options;
_timestamp = timestamp ?: [NSDate new];
_timestamp = timestamp ?: [NSDate date];
__uint64_t tid;
if (pthread_threadid_np(NULL, &tid) == 0) {
_threadID = [[NSString alloc] initWithFormat:@"%llu", tid];
} else {
_threadID = @"missing threadId";
_threadID = @"N/A";
}
_threadName = NSThread.currentThread.name;
// Get the file name without extension
_fileName = [_file lastPathComponent];
NSUInteger dotLocation = [_fileName rangeOfString:@"." options:NSBackwardsSearch].location;
if (dotLocation != NSNotFound)
{
__auto_type dotLocation = [_fileName rangeOfString:@"." options:NSBackwardsSearch].location;
if (dotLocation != NSNotFound) {
_fileName = [_fileName substringToIndex:dotLocation];
}
// Try to get the current queue's label
_queueLabel = [[NSString alloc] initWithFormat:@"%s", dispatch_queue_get_label(DISPATCH_CURRENT_QUEUE_LABEL)];
_queueLabel = @(dispatch_queue_get_label(DISPATCH_CURRENT_QUEUE_LABEL));
_qos = (NSUInteger) qos_class_self();
}
return self;
}
- (instancetype)initWithFormat:(NSString *)messageFormat
args:(va_list)messageArgs
level:(DDLogLevel)level
flag:(DDLogFlag)flag
context:(NSInteger)context
file:(NSString *)file
function:(NSString *)function
line:(NSUInteger)line
tag:(id)tag
options:(DDLogMessageOptions)options
timestamp:(NSDate *)timestamp {
__auto_type copyMessage = (options & DDLogMessageDontCopyMessage) == 0;
NSString *format = copyMessage ? [messageFormat copy] : messageFormat;
self = [self initWithFormat:format
formatted:[[NSString alloc] initWithFormat:format arguments:messageArgs]
level:level
flag:flag
context:context
file:file
function:function
line:line
tag:tag
options:options | DDLogMessageDontCopyMessage // we already did the copying if needed.
timestamp:timestamp];
return self;
}
- (instancetype)initWithMessage:(NSString *)message
level:(DDLogLevel)level
flag:(DDLogFlag)flag
context:(NSInteger)context
file:(NSString *)file
function:(NSString *)function
line:(NSUInteger)line
tag:(id)tag
options:(DDLogMessageOptions)options
timestamp:(NSDate *)timestamp {
self = [self initWithFormat:message
formatted:message
level:level
flag:flag
context:context
file:file
function:function
line:line
tag:tag
options:options
timestamp:timestamp];
return self;
}
NS_INLINE BOOL _nullable_strings_equal(NSString* _Nullable lhs, NSString* _Nullable rhs)
{
if (lhs == nil) {
if (rhs == nil) {
return YES;
}
} else if (rhs != nil && [lhs isEqualToString:(NSString* _Nonnull)rhs]) {
return YES;
}
return NO;
}
- (BOOL)isEqual:(id)other {
// Subclasses of NSObject should not call [super isEqual:] here.
// See https://stackoverflow.com/questions/36593038/confused-about-the-default-isequal-and-hash-implements
if (other == self) {
return YES;
} else if (![super isEqual:other] || ![other isKindOfClass:[self class]]) {
} else if (!other || ![other isKindOfClass:[DDLogMessage class]]) {
return NO;
} else {
__auto_type otherMsg = (DDLogMessage *)other;
return [otherMsg->_message isEqualToString:_message]
&& [otherMsg->_messageFormat isEqualToString:_messageFormat]
&& otherMsg->_level == _level
&& otherMsg->_flag == _flag
&& otherMsg->_context == _context
&& [otherMsg->_file isEqualToString:_file]
&& [otherMsg->_fileName isEqualToString:_fileName]
&& [otherMsg->_function isEqualToString:_function]
&& _nullable_strings_equal(otherMsg->_function, _function)
&& otherMsg->_line == _line
&& (([otherMsg->_representedObject respondsToSelector:@selector(isEqual:)] && [otherMsg->_representedObject isEqual:_representedObject]) || otherMsg->_representedObject == _representedObject)
&& otherMsg->_options == _options
&& [otherMsg->_timestamp isEqualToDate:_timestamp]
&& [otherMsg->_threadID isEqualToString:_threadID] // If the thread ID is the same, the name will likely be the same as well.
&& [otherMsg->_queueLabel isEqualToString:_queueLabel]
@@ -1056,17 +1077,17 @@ NSString * __nullable DDExtractFileNameWithoutExtension(const char *filePath, BO
}
- (NSUInteger)hash {
return [super hash]
^ _message.hash
// Subclasses of NSObject should not call [super hash] here.
// See https://stackoverflow.com/questions/36593038/confused-about-the-default-isequal-and-hash-implements
return _message.hash
^ _messageFormat.hash
^ _level
^ _flag
^ _context
^ _file.hash
^ _fileName.hash
^ _function.hash
^ _line
^ ([_representedObject respondsToSelector:@selector(hash)] ? [_representedObject hash] : 0)
^ _options
^ ([_representedObject respondsToSelector:@selector(hash)] ? [_representedObject hash] : (NSUInteger)_representedObject)
^ _timestamp.hash
^ _threadID.hash
^ _queueLabel.hash
@@ -1076,6 +1097,7 @@ NSString * __nullable DDExtractFileNameWithoutExtension(const char *filePath, BO
- (id)copyWithZone:(NSZone * __attribute__((unused)))zone {
DDLogMessage *newMessage = [DDLogMessage new];
newMessage->_messageFormat = _messageFormat;
newMessage->_message = _message;
newMessage->_level = _level;
newMessage->_flag = _flag;
@@ -1139,8 +1161,8 @@ NSString * __nullable DDExtractFileNameWithoutExtension(const char *filePath, BO
//
// This is used primarily for thread-safety assertions (via the isOnInternalLoggerQueue method below).
void *key = (__bridge void *)self;
void *nonNullValue = (__bridge void *)self;
__auto_type key = (__bridge void *)self;
__auto_type nonNullValue = (__bridge void *)self;
dispatch_queue_set_specific(_loggerQueue, key, nonNullValue, NULL);
}
@@ -1149,13 +1171,11 @@ NSString * __nullable DDExtractFileNameWithoutExtension(const char *filePath, BO
}
- (void)dealloc {
#if !OS_OBJECT_USE_OBJC
#if !OS_OBJECT_USE_OBJC
if (_loggerQueue) {
dispatch_release(_loggerQueue);
}
#endif
#endif
}
- (void)logMessage:(DDLogMessage * __attribute__((unused)))logMessage {
@@ -1212,14 +1232,10 @@ NSString * __nullable DDExtractFileNameWithoutExtension(const char *filePath, BO
// This is the intended result. Fix it by accessing the ivar directly.
// Great strides have been take to ensure this is safe to do. Plus it's MUCH faster.
NSAssert(![self isOnGlobalLoggingQueue], @"Core architecture requirement failure");
NSAssert(![self isOnInternalLoggerQueue], @"MUST access ivar directly, NOT via self.* syntax.");
dispatch_queue_t globalLoggingQueue = [DDLog loggingQueue];
DDAbstractLoggerAssertLockedPropertyAccess();
__block id <DDLogFormatter> result;
dispatch_sync(globalLoggingQueue, ^{
dispatch_sync(DDLog.loggingQueue, ^{
dispatch_sync(self->_loggerQueue, ^{
result = self->_logFormatter;
});
@@ -1231,10 +1247,9 @@ NSString * __nullable DDExtractFileNameWithoutExtension(const char *filePath, BO
- (void)setLogFormatter:(id <DDLogFormatter>)logFormatter {
// The design of this method is documented extensively in the logFormatter message (above in code).
NSAssert(![self isOnGlobalLoggingQueue], @"Core architecture requirement failure");
NSAssert(![self isOnInternalLoggerQueue], @"MUST access ivar directly, NOT via self.* syntax.");
DDAbstractLoggerAssertLockedPropertyAccess();
dispatch_block_t block = ^{
__auto_type block = ^{
@autoreleasepool {
if (self->_logFormatter != logFormatter) {
if ([self->_logFormatter respondsToSelector:@selector(willRemoveFromLogger:)]) {
@@ -1270,9 +1285,7 @@ NSString * __nullable DDExtractFileNameWithoutExtension(const char *filePath, BO
}
- (BOOL)isOnInternalLoggerQueue {
void *key = (__bridge void *)self;
return (dispatch_get_specific(key) != NULL);
return dispatch_get_specific((__bridge void *)self) != NULL;
}
@end

View File

@@ -1,6 +1,6 @@
// Software License Agreement (BSD License)
//
// Copyright (c) 2010-2021, Deusty, LLC
// Copyright (c) 2010-2026, Deusty, LLC
// All rights reserved.
//
// Redistribution and use of this software in source and binary forms,

View File

@@ -1,6 +1,6 @@
// Software License Agreement (BSD License)
//
// Copyright (c) 2010-2021, Deusty, LLC
// Copyright (c) 2010-2026, Deusty, LLC
// All rights reserved.
//
// Redistribution and use of this software in source and binary forms,
@@ -13,18 +13,51 @@
// to endorse or promote products derived from this software without specific
// prior written permission of Deusty, LLC.
#import <TargetConditionals.h>
#import <os/log.h>
#import <CocoaLumberjack/DDOSLogger.h>
@interface DDOSLogger () {
NSString *_subsystem;
NSString *_category;
@implementation DDOSLogLevelMapperDefault
- (instancetype)init {
self = [super init];
return self;
}
@property (copy, nonatomic, readonly, nullable) NSString *subsystem;
@property (copy, nonatomic, readonly, nullable) NSString *category;
@property (strong, nonatomic, readwrite, nonnull) os_log_t logger;
- (os_log_type_t)osLogTypeForLogFlag:(DDLogFlag)logFlag {
switch (logFlag) {
case DDLogFlagError:
case DDLogFlagWarning:
return OS_LOG_TYPE_ERROR;
case DDLogFlagInfo:
return OS_LOG_TYPE_INFO;
case DDLogFlagDebug:
case DDLogFlagVerbose:
return OS_LOG_TYPE_DEBUG;
default:
return OS_LOG_TYPE_DEFAULT;
}
}
@end
#if TARGET_OS_SIMULATOR
@implementation DDOSLogLevelMapperSimulatorConsoleAppWorkaround
- (os_log_type_t)osLogTypeForLogFlag:(DDLogFlag)logFlag {
__auto_type defaultMapping = [super osLogTypeForLogFlag:logFlag];
return (defaultMapping == OS_LOG_TYPE_DEBUG) ? OS_LOG_TYPE_DEFAULT : defaultMapping;
}
@end
#endif
@interface DDOSLogger ()
@property (nonatomic, copy, readonly, nullable) NSString *subsystem;
@property (nonatomic, copy, readonly, nullable) NSString *category;
@property (nonatomic, strong, readonly, nonnull) os_log_t logger;
@end
@@ -32,29 +65,14 @@
@synthesize subsystem = _subsystem;
@synthesize category = _category;
@synthesize logLevelMapper = _logLevelMapper;
@synthesize logger = _logger;
#pragma mark - Initialization
/**
* Assertion
* Swift: (String, String)?
*/
- (instancetype)initWithSubsystem:(NSString *)subsystem category:(NSString *)category {
NSAssert((subsystem == nil) == (category == nil), @"Either both subsystem and category or neither should be nil.");
if (self = [super init]) {
_subsystem = [subsystem copy];
_category = [category copy];
}
return self;
}
#pragma mark - Shared Instance
API_AVAILABLE(macos(10.12), ios(10.0), watchos(3.0), tvos(10.0))
static DDOSLogger *sharedInstance;
- (instancetype)init {
return [self initWithSubsystem:nil category:nil];
}
+ (instancetype)sharedInstance {
static dispatch_once_t DDOSLoggerOnceToken;
@@ -65,53 +83,75 @@ static DDOSLogger *sharedInstance;
return sharedInstance;
}
#pragma mark - os_log
- (os_log_t)getLogger {
if (self.subsystem == nil || self.category == nil) {
return OS_LOG_DEFAULT;
#pragma mark - Initialization
- (instancetype)initWithSubsystem:(NSString *)subsystem
category:(NSString *)category
logLevelMapper:(id<DDOSLogLevelMapper>)logLevelMapper {
NSAssert((subsystem == nil) == (category == nil),
@"Either both subsystem and category or neither should be nil.");
NSParameterAssert(logLevelMapper);
if (self = [super init]) {
_subsystem = [subsystem copy];
_category = [category copy];
_logLevelMapper = logLevelMapper;
}
return os_log_create(self.subsystem.UTF8String, self.category.UTF8String);
return self;
}
- (instancetype)initWithSubsystem:(NSString *)subsystem category:(NSString *)category {
return [self initWithSubsystem:subsystem
category:category
logLevelMapper:[[DDOSLogLevelMapperDefault alloc] init]];
}
- (instancetype)init {
return [self initWithSubsystem:nil category:nil];
}
- (instancetype)initWithLogLevelMapper:(id<DDOSLogLevelMapper>)logLevelMapper {
return [self initWithSubsystem:nil category:nil logLevelMapper:logLevelMapper];
}
#pragma mark - Mapper
- (id<DDOSLogLevelMapper>)logLevelMapper {
if (_logLevelMapper == nil) {
_logLevelMapper = [[DDOSLogLevelMapperDefault alloc] init];
}
return _logLevelMapper;
}
#pragma mark - os_log
- (os_log_t)logger {
if (_logger == nil) {
_logger = [self getLogger];
if (self.subsystem == nil || self.category == nil) {
_logger = OS_LOG_DEFAULT;
} else {
_logger = os_log_create(self.subsystem.UTF8String, self.category.UTF8String);
}
}
return _logger;
}
#pragma mark - DDLogger
- (DDLoggerName)loggerName {
return DDLoggerNameOS;
}
- (void)logMessage:(DDLogMessage *)logMessage {
// Skip captured log messages
#if !TARGET_OS_WATCH // See DDASLLogCapture.m -> Was never supported on watchOS.
// Skip captured log messages.
if ([logMessage->_fileName isEqualToString:@"DDASLLogCapture"]) {
return;
}
#endif
if (@available(iOS 10.0, macOS 10.12, tvOS 10.0, watchOS 3.0, *)) {
NSString * message = _logFormatter ? [_logFormatter formatLogMessage:logMessage] : logMessage->_message;
__auto_type message = _logFormatter ? [_logFormatter formatLogMessage:logMessage] : logMessage->_message;
if (message != nil) {
const char *msg = [message UTF8String];
__auto_type logger = [self logger];
switch (logMessage->_flag) {
case DDLogFlagError :
os_log_error(logger, "%{public}s", msg);
break;
case DDLogFlagWarning:
case DDLogFlagInfo :
os_log_info(logger, "%{public}s", msg);
break;
case DDLogFlagDebug :
case DDLogFlagVerbose:
default :
os_log_debug(logger, "%{public}s", msg);
break;
}
__auto_type logType = [self.logLevelMapper osLogTypeForLogFlag:logMessage->_flag];
os_log_with_type(self.logger, logType, "%{public}s", message.UTF8String);
}
}
}

View File

@@ -1,6 +1,6 @@
// Software License Agreement (BSD License)
//
// Copyright (c) 2010-2021, Deusty, LLC
// Copyright (c) 2010-2026, Deusty, LLC
// All rights reserved.
//
// Redistribution and use of this software in source and binary forms,
@@ -79,23 +79,18 @@
#define MAP_TO_TERMINAL_APP_COLORS 1
typedef struct {
uint8_t r;
uint8_t g;
uint8_t b;
uint8_t r;
uint8_t g;
uint8_t b;
} DDRGBColor;
@interface DDTTYLoggerColorProfile : NSObject {
@public
@public
DDLogFlag mask;
NSInteger context;
uint8_t fg_r;
uint8_t fg_g;
uint8_t fg_b;
uint8_t bg_r;
uint8_t bg_g;
uint8_t bg_b;
DDRGBColor fg;
DDRGBColor bg;
NSUInteger fgCodeIndex;
NSString *fgCodeRaw;
@@ -117,19 +112,15 @@ typedef struct {
@end
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
#pragma mark -
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
@interface DDTTYLogger () {
NSString *_appName;
char *_app;
size_t _appLen;
NSString *_processID;
char *_pid;
size_t _pidLen;
BOOL _colorsEnabled;
NSMutableArray *_colorProfilesArray;
NSMutableDictionary *_colorProfilesDict;
@@ -137,6 +128,7 @@ typedef struct {
@end
#pragma mark -
@implementation DDTTYLogger
@@ -144,30 +136,30 @@ static BOOL isaColorTTY;
static BOOL isaColor256TTY;
static BOOL isaXcodeColorTTY;
static NSArray *codes_fg = nil;
static NSArray *codes_bg = nil;
static NSArray *codesFg = nil;
static NSArray *codesBg = nil;
static NSArray *colors = nil;
static DDTTYLogger *sharedInstance;
/**
* Initializes the colors array, as well as the codes_fg and codes_bg arrays, for 16 color mode.
* Initializes the colors array, as well as the `codesFg` and `codesBg` arrays, for 16 color mode.
*
* This method is used when the application is running from within a shell that only supports 16 color mode.
* This method is not invoked if the application is running within Xcode, or via normal UI app launch.
**/
+ (void)initialize_colors_16 {
if (codes_fg || codes_bg || colors) {
+ (void)initializeColors16 {
if (codesFg || codesBg || colors) {
return;
}
NSMutableArray *m_colors = [NSMutableArray arrayWithCapacity:16];
__auto_type mColors = [NSMutableArray arrayWithCapacity:16];
// In a standard shell only 16 colors are supported.
//
// More information about ansi escape codes can be found online.
// http://en.wikipedia.org/wiki/ANSI_escape_code
codes_fg = @[
codesFg = @[
@"30m", // normal - black
@"31m", // normal - red
@"32m", // normal - green
@@ -186,7 +178,7 @@ static DDTTYLogger *sharedInstance;
@"1;37m", // bright - white
];
codes_bg = @[
codesBg = @[
@"40m", // normal - black
@"41m", // normal - red
@"42m", // normal - green
@@ -205,13 +197,12 @@ static DDTTYLogger *sharedInstance;
@"1;47m", // bright - white
];
#if MAP_TO_TERMINAL_APP_COLORS
// Standard Terminal.app colors:
//
// These are the default colors used by Apple's Terminal.app.
DDRGBColor rgbColors[] = {
const DDRGBColor rgbColors[] = {
{ 0, 0, 0}, // normal - black
{194, 54, 33}, // normal - red
{ 37, 188, 36}, // normal - green
@@ -235,8 +226,7 @@ static DDTTYLogger *sharedInstance;
// Standard xterm colors:
//
// These are the default colors used by most xterm shells.
DDRGBColor rgbColors[] = {
const DDRGBColor rgbColors[] = {
{ 0, 0, 0}, // normal - black
{205, 0, 0}, // normal - red
{ 0, 205, 0}, // normal - green
@@ -257,30 +247,30 @@ static DDTTYLogger *sharedInstance;
#endif /* if MAP_TO_TERMINAL_APP_COLORS */
for (size_t i = 0; i < sizeof(rgbColors) / sizeof(rgbColors[0]); ++i) {
[m_colors addObject:DDMakeColor(rgbColors[i].r, rgbColors[i].g, rgbColors[i].b)];
[mColors addObject:DDMakeColor(rgbColors[i].r, rgbColors[i].g, rgbColors[i].b)];
}
colors = [m_colors copy];
colors = [mColors copy];
NSAssert([codes_fg count] == [codes_bg count], @"Invalid colors/codes array(s)");
NSAssert([codes_fg count] == [colors count], @"Invalid colors/codes array(s)");
NSAssert([codesFg count] == [codesBg count], @"Invalid colors/codes array(s)");
NSAssert([codesFg count] == [colors count], @"Invalid colors/codes array(s)");
}
/**
* Initializes the colors array, as well as the codes_fg and codes_bg arrays, for 256 color mode.
* Initializes the colors array, as well as the `codesFg` and `codesBg` arrays, for 256 color mode.
*
* This method is used when the application is running from within a shell that supports 256 color mode.
* This method is not invoked if the application is running within Xcode, or via normal UI app launch.
**/
+ (void)initialize_colors_256 {
if (codes_fg || codes_bg || colors) {
+ (void)initializeColors256 {
if (codesFg || codesBg || colors) {
return;
}
NSMutableArray *m_codes_fg = [NSMutableArray arrayWithCapacity:(256 - 16)];
NSMutableArray *m_codes_bg = [NSMutableArray arrayWithCapacity:(256 - 16)];
NSMutableArray *m_colors = [NSMutableArray arrayWithCapacity:(256 - 16)];
__auto_type mCodesFg = [NSMutableArray arrayWithCapacity:(256 - 16)];
__auto_type mCodesBg = [NSMutableArray arrayWithCapacity:(256 - 16)];
__auto_type mColors = [NSMutableArray arrayWithCapacity:(256 - 16)];
#if MAP_TO_TERMINAL_APP_COLORS
#if MAP_TO_TERMINAL_APP_COLORS
// Standard Terminal.app colors:
//
@@ -307,7 +297,7 @@ static DDTTYLogger *sharedInstance;
// http://en.wikipedia.org/wiki/ANSI_escape_code
// Colors
DDRGBColor rgbColors[] = {
const DDRGBColor rgbColors[] = {
{ 47, 49, 49},
{ 60, 42, 144},
{ 66, 44, 183},
@@ -592,21 +582,19 @@ static DDTTYLogger *sharedInstance;
};
for (size_t i = 0; i < sizeof(rgbColors) / sizeof(rgbColors[0]); ++i) {
[m_colors addObject:DDMakeColor(rgbColors[i].r, rgbColors[i].g, rgbColors[i].b)];
[mColors addObject:DDMakeColor(rgbColors[i].r, rgbColors[i].g, rgbColors[i].b)];
}
// Color codes
int index = 16;
while (index < 256) {
[m_codes_fg addObject:[NSString stringWithFormat:@"38;5;%dm", index]];
[m_codes_bg addObject:[NSString stringWithFormat:@"48;5;%dm", index]];
[mCodesFg addObject:[NSString stringWithFormat:@"38;5;%dm", index]];
[mCodesBg addObject:[NSString stringWithFormat:@"48;5;%dm", index]];
index++;
}
#else /* if MAP_TO_TERMINAL_APP_COLORS */
#else /* if MAP_TO_TERMINAL_APP_COLORS */
// Standard xterm colors:
//
@@ -652,9 +640,9 @@ static DDTTYLogger *sharedInstance;
for (bi = 0; bi < 6; bi++) {
b = (bi == 0) ? 0 : 95 + (40 * (bi - 1));
[m_codes_fg addObject:[NSString stringWithFormat:@"38;5;%dm", index]];
[m_codes_bg addObject:[NSString stringWithFormat:@"48;5;%dm", index]];
[m_colors addObject:DDMakeColor(r, g, b)];
[mCodesFg addObject:[NSString stringWithFormat:@"38;5;%dm", index]];
[mCodesBg addObject:[NSString stringWithFormat:@"48;5;%dm", index]];
[mColors addObject:DDMakeColor(r, g, b)];
index++;
}
@@ -668,9 +656,9 @@ static DDTTYLogger *sharedInstance;
b = 8;
while (index < 256) {
[m_codes_fg addObject:[NSString stringWithFormat:@"38;5;%dm", index]];
[m_codes_bg addObject:[NSString stringWithFormat:@"48;5;%dm", index]];
[m_colors addObject:DDMakeColor(r, g, b)];
[mCodesFg addObject:[NSString stringWithFormat:@"38;5;%dm", index]];
[mCodesBg addObject:[NSString stringWithFormat:@"48;5;%dm", index]];
[mColor s addObject:DDMakeColor(r, g, b)];
r += 10;
g += 10;
@@ -679,22 +667,21 @@ static DDTTYLogger *sharedInstance;
index++;
}
#endif /* if MAP_TO_TERMINAL_APP_COLORS */
#endif /* if MAP_TO_TERMINAL_APP_COLORS */
codes_fg = [m_codes_fg copy];
codes_bg = [m_codes_bg copy];
colors = [m_colors copy];
codesFg = [mCodesFg copy];
codesBg = [mCodesBg copy];
colors = [mColors copy];
NSAssert([codes_fg count] == [codes_bg count], @"Invalid colors/codes array(s)");
NSAssert([codes_fg count] == [colors count], @"Invalid colors/codes array(s)");
NSAssert([codesFg count] == [codesBg count], @"Invalid colors/codes array(s)");
NSAssert([codesFg count] == [colors count], @"Invalid colors/codes array(s)");
}
+ (void)getRed:(CGFloat *)rPtr green:(CGFloat *)gPtr blue:(CGFloat *)bPtr fromColor:(DDColor *)color {
#if TARGET_OS_IPHONE
#if TARGET_OS_IPHONE
// iOS
BOOL done = NO;
__auto_type done = NO;
if ([color respondsToSelector:@selector(getRed:green:blue:alpha:)]) {
done = [color getRed:rPtr green:gPtr blue:bPtr alpha:NULL];
@@ -704,44 +691,49 @@ static DDTTYLogger *sharedInstance;
// The method getRed:green:blue:alpha: was only available starting iOS 5.
// So in iOS 4 and earlier, we have to jump through hoops.
CGColorSpaceRef rgbColorSpace = CGColorSpaceCreateDeviceRGB();
__auto_type rgbColorSpace = CGColorSpaceCreateDeviceRGB();
unsigned char pixel[4];
CGContextRef context = CGBitmapContextCreate(&pixel, 1, 1, 8, 4, rgbColorSpace, (CGBitmapInfo)(kCGBitmapAlphaInfoMask & kCGImageAlphaNoneSkipLast));
__auto_type context = CGBitmapContextCreate(&pixel, 1, 1, 8, 4, rgbColorSpace, (CGBitmapInfo)(((CGImageAlphaInfo)kCGBitmapAlphaInfoMask) & kCGImageAlphaNoneSkipLast));
CGContextSetFillColorWithColor(context, [color CGColor]);
CGContextFillRect(context, CGRectMake(0, 0, 1, 1));
if (rPtr) {
*rPtr = pixel[0] / 255.0f;
*rPtr = pixel[0] / 255.0;
}
if (gPtr) {
*gPtr = pixel[1] / 255.0f;
*gPtr = pixel[1] / 255.0;
}
if (bPtr) {
*bPtr = pixel[2] / 255.0f;
*bPtr = pixel[2] / 255.0;
}
CGContextRelease(context);
CGColorSpaceRelease(rgbColorSpace);
}
#elif defined(DD_CLI) || !__has_include(<AppKit/NSColor.h>)
#elif defined(DD_CLI) || !__has_include(<AppKit/NSColor.h>)
// OS X without AppKit
[color getRed:rPtr green:gPtr blue:bPtr alpha:NULL];
#else /* if TARGET_OS_IPHONE */
#else /* if TARGET_OS_IPHONE */
// OS X with AppKit
NSColor *safeColor = [color colorUsingColorSpaceName:NSCalibratedRGBColorSpace];
NSColor *safeColor;
if (@available(macOS 10.14,*)) {
safeColor = [color colorUsingColorSpace:NSColorSpace.deviceRGBColorSpace];
} else {
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
safeColor = [color colorUsingColorSpaceName:NSCalibratedRGBColorSpace];
#pragma clang diagnostic pop
}
[safeColor getRed:rPtr green:gPtr blue:bPtr alpha:NULL];
#endif /* if TARGET_OS_IPHONE */
#endif /* if TARGET_OS_IPHONE */
}
/**
@@ -750,7 +742,7 @@ static DDTTYLogger *sharedInstance;
*
* This method loops through the known supported color set, and calculates the closest color.
* The array index of that color, within the colors array, is then returned.
* This array index may also be used as the index within the codes_fg and codes_bg arrays.
* This array index may also be used as the index within the `codesFg` and `codesBg` arrays.
**/
+ (NSUInteger)codeIndexForColor:(DDColor *)inColor {
CGFloat inR, inG, inB;
@@ -758,7 +750,7 @@ static DDTTYLogger *sharedInstance;
[self getRed:&inR green:&inG blue:&inB fromColor:inColor];
NSUInteger bestIndex = 0;
CGFloat lowestDistance = 100.0f;
CGFloat lowestDistance = 100.0;
NSUInteger i = 0;
@@ -768,14 +760,14 @@ static DDTTYLogger *sharedInstance;
CGFloat r, g, b;
[self getRed:&r green:&g blue:&b fromColor:color];
#if CGFLOAT_IS_DOUBLE
CGFloat distance = sqrt(pow(r - inR, 2.0) + pow(g - inG, 2.0) + pow(b - inB, 2.0));
#else
CGFloat distance = sqrtf(powf(r - inR, 2.0f) + powf(g - inG, 2.0f) + powf(b - inB, 2.0f));
#endif
#if CGFLOAT_IS_DOUBLE
__auto_type distance = sqrt(pow(r - inR, 2.0) + pow(g - inG, 2.0) + pow(b - inB, 2.0));
#else
__auto_type distance = sqrtf(powf(r - inR, 2.0f) + powf(g - inG, 2.0f) + powf(b - inB, 2.0f));
#endif
NSLogVerbose(@"DDTTYLogger: %3lu : %.3f,%.3f,%.3f & %.3f,%.3f,%.3f = %.6f",
(unsigned long)i, inR, inG, inB, r, g, b, distance);
(unsigned long)i, (double)inR, (double)inG, (double)inB, (double)r, (double)g, (double)b, (double)distance);
if (distance < lowestDistance) {
bestIndex = i;
@@ -799,10 +791,10 @@ static DDTTYLogger *sharedInstance;
//
// PS - Please read the header file before diving into the source code.
char *xcode_colors = getenv("XcodeColors");
char *term = getenv("TERM");
__auto_type xcodeColors = getenv("XcodeColors");
__auto_type term = getenv("TERM");
if (xcode_colors && (strcmp(xcode_colors, "YES") == 0)) {
if (xcodeColors && (strcmp(xcodeColors, "YES") == 0)) {
isaXcodeColorTTY = YES;
} else if (term) {
if (strcasestr(term, "color") != NULL) {
@@ -810,9 +802,9 @@ static DDTTYLogger *sharedInstance;
isaColor256TTY = (strcasestr(term, "256") != NULL);
if (isaColor256TTY) {
[self initialize_colors_256];
[self initializeColors256];
} else {
[self initialize_colors_16];
[self initializeColors16];
}
}
}
@@ -840,9 +832,7 @@ static DDTTYLogger *sharedInstance;
if ((self = [super init])) {
// Initialize 'app' variable (char *)
_appName = [[NSProcessInfo processInfo] processName];
_appLen = [_appName lengthOfBytesUsingEncoding:NSUTF8StringEncoding];
if (_appLen == 0) {
@@ -851,14 +841,12 @@ static DDTTYLogger *sharedInstance;
}
_app = (char *)calloc(_appLen + 1, sizeof(char));
if (_app == NULL) {
return nil;
}
BOOL processedAppName = [_appName getCString:_app maxLength:(_appLen + 1) encoding:NSUTF8StringEncoding];
if (NO == processedAppName) {
if (!processedAppName) {
free(_app);
return nil;
}
@@ -876,8 +864,7 @@ static DDTTYLogger *sharedInstance;
}
BOOL processedID = [_processID getCString:_pid maxLength:(_pidLen + 1) encoding:NSUTF8StringEncoding];
if (NO == processedID) {
if (!processedID) {
free(_app);
free(_pid);
return nil;
@@ -915,14 +902,9 @@ static DDTTYLogger *sharedInstance;
// This is the intended result. Fix it by accessing the ivar directly.
// Great strides have been take to ensure this is safe to do. Plus it's MUCH faster.
NSAssert(![self isOnGlobalLoggingQueue], @"Core architecture requirement failure");
NSAssert(![self isOnInternalLoggerQueue], @"MUST access ivar directly, NOT via self.* syntax.");
dispatch_queue_t globalLoggingQueue = [DDLog loggingQueue];
DDAbstractLoggerAssertLockedPropertyAccess();
__block BOOL result;
dispatch_sync(globalLoggingQueue, ^{
dispatch_sync(DDLog.loggingQueue, ^{
dispatch_sync(self.loggerQueue, ^{
result = self->_colorsEnabled;
});
@@ -932,7 +914,7 @@ static DDTTYLogger *sharedInstance;
}
- (void)setColorsEnabled:(BOOL)newColorsEnabled {
dispatch_block_t block = ^{
__auto_type block = ^{
@autoreleasepool {
self->_colorsEnabled = newColorsEnabled;
@@ -952,12 +934,8 @@ static DDTTYLogger *sharedInstance;
// This is the intended result. Fix it by accessing the ivar directly.
// Great strides have been take to ensure this is safe to do. Plus it's MUCH faster.
NSAssert(![self isOnGlobalLoggingQueue], @"Core architecture requirement failure");
NSAssert(![self isOnInternalLoggerQueue], @"MUST access ivar directly, NOT via self.* syntax.");
dispatch_queue_t globalLoggingQueue = [DDLog loggingQueue];
dispatch_async(globalLoggingQueue, ^{
DDAbstractLoggerAssertLockedPropertyAccess();
dispatch_async(DDLog.loggingQueue, ^{
dispatch_async(self.loggerQueue, block);
});
}
@@ -969,11 +947,11 @@ static DDTTYLogger *sharedInstance;
- (void)setForegroundColor:(DDColor *)txtColor backgroundColor:(DDColor *)bgColor forFlag:(DDLogFlag)mask context:(NSInteger)ctxt {
dispatch_block_t block = ^{
@autoreleasepool {
DDTTYLoggerColorProfile *newColorProfile =
[[DDTTYLoggerColorProfile alloc] initWithForegroundColor:txtColor
backgroundColor:bgColor
flag:mask
context:ctxt];
DDTTYLoggerColorProfile *newColorProfile = [[DDTTYLoggerColorProfile alloc] initWithForegroundColor:txtColor
backgroundColor:bgColor
flag:mask
context:ctxt];
if (!newColorProfile) return;
NSLogInfo(@"DDTTYLogger: newColorProfile: %@", newColorProfile);
@@ -1001,25 +979,22 @@ static DDTTYLogger *sharedInstance;
if ([self isOnInternalLoggerQueue]) {
block();
} else {
dispatch_queue_t globalLoggingQueue = [DDLog loggingQueue];
NSAssert(![self isOnGlobalLoggingQueue], @"Core architecture requirement failure");
dispatch_async(globalLoggingQueue, ^{
DDAbstractLoggerAssertNotOnGlobalLoggingQueue();
dispatch_async(DDLog.loggingQueue, ^{
dispatch_async(self.loggerQueue, block);
});
}
}
- (void)setForegroundColor:(DDColor *)txtColor backgroundColor:(DDColor *)bgColor forTag:(id <NSCopying>)tag {
NSAssert([(id < NSObject >) tag conformsToProtocol: @protocol(NSCopying)], @"Invalid tag");
NSAssert([(id <NSObject>)tag conformsToProtocol: @protocol(NSCopying)], @"Invalid tag");
dispatch_block_t block = ^{
__auto_type block = ^{
@autoreleasepool {
DDTTYLoggerColorProfile *newColorProfile =
[[DDTTYLoggerColorProfile alloc] initWithForegroundColor:txtColor
backgroundColor:bgColor
flag:(DDLogFlag)0
context:0];
__auto_type newColorProfile = [[DDTTYLoggerColorProfile alloc] initWithForegroundColor:txtColor
backgroundColor:bgColor
flag:(DDLogFlag)0
context:0];
NSLogInfo(@"DDTTYLogger: newColorProfile: %@", newColorProfile);
@@ -1033,10 +1008,8 @@ static DDTTYLogger *sharedInstance;
if ([self isOnInternalLoggerQueue]) {
block();
} else {
dispatch_queue_t globalLoggingQueue = [DDLog loggingQueue];
NSAssert(![self isOnGlobalLoggingQueue], @"Core architecture requirement failure");
dispatch_async(globalLoggingQueue, ^{
DDAbstractLoggerAssertNotOnGlobalLoggingQueue();
dispatch_async(DDLog.loggingQueue, ^{
dispatch_async(self.loggerQueue, block);
});
}
@@ -1047,7 +1020,7 @@ static DDTTYLogger *sharedInstance;
}
- (void)clearColorsForFlag:(DDLogFlag)mask context:(NSInteger)context {
dispatch_block_t block = ^{
__auto_type block = ^{
@autoreleasepool {
NSUInteger i = 0;
@@ -1071,19 +1044,17 @@ static DDTTYLogger *sharedInstance;
if ([self isOnInternalLoggerQueue]) {
block();
} else {
dispatch_queue_t globalLoggingQueue = [DDLog loggingQueue];
NSAssert(![self isOnGlobalLoggingQueue], @"Core architecture requirement failure");
dispatch_async(globalLoggingQueue, ^{
DDAbstractLoggerAssertNotOnGlobalLoggingQueue();
dispatch_async(DDLog.loggingQueue, ^{
dispatch_async(self.loggerQueue, block);
});
}
}
- (void)clearColorsForTag:(id <NSCopying>)tag {
NSAssert([(id < NSObject >) tag conformsToProtocol: @protocol(NSCopying)], @"Invalid tag");
NSAssert([(id <NSObject>) tag conformsToProtocol: @protocol(NSCopying)], @"Invalid tag");
dispatch_block_t block = ^{
__auto_type block = ^{
@autoreleasepool {
[self->_colorProfilesDict removeObjectForKey:tag];
}
@@ -1095,17 +1066,15 @@ static DDTTYLogger *sharedInstance;
if ([self isOnInternalLoggerQueue]) {
block();
} else {
dispatch_queue_t globalLoggingQueue = [DDLog loggingQueue];
NSAssert(![self isOnGlobalLoggingQueue], @"Core architecture requirement failure");
dispatch_async(globalLoggingQueue, ^{
DDAbstractLoggerAssertNotOnGlobalLoggingQueue();
dispatch_async(DDLog.loggingQueue, ^{
dispatch_async(self.loggerQueue, block);
});
}
}
- (void)clearColorsForAllFlags {
dispatch_block_t block = ^{
__auto_type block = ^{
@autoreleasepool {
[self->_colorProfilesArray removeAllObjects];
}
@@ -1117,17 +1086,15 @@ static DDTTYLogger *sharedInstance;
if ([self isOnInternalLoggerQueue]) {
block();
} else {
dispatch_queue_t globalLoggingQueue = [DDLog loggingQueue];
NSAssert(![self isOnGlobalLoggingQueue], @"Core architecture requirement failure");
dispatch_async(globalLoggingQueue, ^{
DDAbstractLoggerAssertNotOnGlobalLoggingQueue();
dispatch_async(DDLog.loggingQueue, ^{
dispatch_async(self.loggerQueue, block);
});
}
}
- (void)clearColorsForAllTags {
dispatch_block_t block = ^{
__auto_type block = ^{
@autoreleasepool {
[self->_colorProfilesDict removeAllObjects];
}
@@ -1139,17 +1106,15 @@ static DDTTYLogger *sharedInstance;
if ([self isOnInternalLoggerQueue]) {
block();
} else {
dispatch_queue_t globalLoggingQueue = [DDLog loggingQueue];
NSAssert(![self isOnGlobalLoggingQueue], @"Core architecture requirement failure");
dispatch_async(globalLoggingQueue, ^{
DDAbstractLoggerAssertNotOnGlobalLoggingQueue();
dispatch_async(DDLog.loggingQueue, ^{
dispatch_async(self.loggerQueue, block);
});
}
}
- (void)clearAllColors {
dispatch_block_t block = ^{
__auto_type block = ^{
@autoreleasepool {
[self->_colorProfilesArray removeAllObjects];
[self->_colorProfilesDict removeAllObjects];
@@ -1162,18 +1127,16 @@ static DDTTYLogger *sharedInstance;
if ([self isOnInternalLoggerQueue]) {
block();
} else {
dispatch_queue_t globalLoggingQueue = [DDLog loggingQueue];
NSAssert(![self isOnGlobalLoggingQueue], @"Core architecture requirement failure");
dispatch_async(globalLoggingQueue, ^{
DDAbstractLoggerAssertNotOnGlobalLoggingQueue();
dispatch_async(DDLog.loggingQueue, ^{
dispatch_async(self.loggerQueue, block);
});
}
}
- (void)logMessage:(DDLogMessage *)logMessage {
NSString *logMsg = logMessage->_message;
BOOL isFormatted = NO;
__auto_type logMsg = logMessage->_message;
__auto_type isFormatted = NO;
if (_logFormatter) {
logMsg = [_logFormatter formatLogMessage:logMessage];
@@ -1217,8 +1180,8 @@ static DDTTYLogger *sharedInstance;
// We use the stack instead of the heap for speed if possible.
// But we're extra cautious to avoid a stack overflow.
NSUInteger msgLen = [logMsg lengthOfBytesUsingEncoding:NSUTF8StringEncoding];
const BOOL useStack = msgLen < (1024 * 4);
__auto_type msgLen = [logMsg lengthOfBytesUsingEncoding:NSUTF8StringEncoding];
const __auto_type useStack = msgLen < (1024 * 4);
char *msg;
if (useStack) {
@@ -1242,8 +1205,12 @@ static DDTTYLogger *sharedInstance;
if (isFormatted) {
// The log message has already been formatted.
const int iovec_len = (_automaticallyAppendNewlineForCustomFormatters) ? 5 : 4;
struct iovec v[iovec_len];
// Needs to be a define, because otherwise the compiler warns "Variable length array folded to constant array as an extension"
#define DD_TTYLOGGER_MAX_IOVEC_LEN 5
size_t iovecLen = _automaticallyAppendNewlineForCustomFormatters ? 5 : 4;
struct iovec v[DD_TTYLOGGER_MAX_IOVEC_LEN] = { 0 };
if (colorProfile) {
v[0].iov_base = colorProfile->fgCode;
@@ -1252,28 +1219,20 @@ static DDTTYLogger *sharedInstance;
v[1].iov_base = colorProfile->bgCode;
v[1].iov_len = colorProfile->bgCodeLen;
v[iovec_len - 1].iov_base = colorProfile->resetCode;
v[iovec_len - 1].iov_len = colorProfile->resetCodeLen;
} else {
v[0].iov_base = "";
v[0].iov_len = 0;
v[1].iov_base = "";
v[1].iov_len = 0;
v[iovec_len - 1].iov_base = "";
v[iovec_len - 1].iov_len = 0;
v[DD_TTYLOGGER_MAX_IOVEC_LEN - 1].iov_base = colorProfile->resetCode;
v[DD_TTYLOGGER_MAX_IOVEC_LEN - 1].iov_len = colorProfile->resetCodeLen;
}
v[2].iov_base = msg;
v[2].iov_len = msgLen;
v[2].iov_len = (msgLen > SIZE_MAX - 1) ? SIZE_MAX - 1 : msgLen;
if (iovec_len == 5) {
if (_automaticallyAppendNewlineForCustomFormatters && (v[2].iov_len == 0 || msg[v[2].iov_len - 1] != '\n')) {
v[3].iov_base = "\n";
v[3].iov_len = (msg[msgLen] == '\n') ? 0 : 1;
v[3].iov_len = 1;
iovecLen = 5;
}
writev(STDERR_FILENO, v, iovec_len);
writev(STDERR_FILENO, v, (int)iovecLen);
} else {
// The log message is unformatted, so apply standard NSLog style formatting.
@@ -1284,13 +1243,15 @@ static DDTTYLogger *sharedInstance;
// Calculate timestamp.
// The technique below is faster than using NSDateFormatter.
if (logMessage->_timestamp) {
NSTimeInterval epoch = [logMessage->_timestamp timeIntervalSince1970];
__auto_type epoch = [logMessage->_timestamp timeIntervalSince1970];
double integral;
__auto_type fract = modf(epoch, &integral);
struct tm tm;
time_t time = (time_t)epoch;
__auto_type time = (time_t)integral;
(void)localtime_r(&time, &tm);
int milliseconds = (int)((epoch - floor(epoch)) * 1000.0);
__auto_type milliseconds = (long)(fract * 1000.0);
len = snprintf(ts, 24, "%04d-%02d-%02d %02d:%02d:%02d:%03d", // yyyy-MM-dd HH:mm:ss:SSS
len = snprintf(ts, 24, "%04d-%02d-%02d %02d:%02d:%02d:%03ld", // yyyy-MM-dd HH:mm:ss:SSS
tm.tm_year + 1900,
tm.tm_mon + 1,
tm.tm_mday,
@@ -1312,7 +1273,7 @@ static DDTTYLogger *sharedInstance;
char tid[9];
len = snprintf(tid, 9, "%s", [logMessage->_threadID cStringUsingEncoding:NSUTF8StringEncoding]);
size_t tidLen = (NSUInteger)MAX(MIN(9 - 1, len), 0);
__auto_type tidLen = (NSUInteger)MAX(MIN(9 - 1, len), 0);
// Here is our format: "%s %s[%i:%s] %s", timestamp, appName, processID, threadID, logMsg
@@ -1379,7 +1340,7 @@ static DDTTYLogger *sharedInstance;
@end
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
#pragma mark -
@implementation DDTTYLoggerColorProfile
@@ -1393,31 +1354,31 @@ static DDTTYLogger *sharedInstance;
if (fgColor) {
[DDTTYLogger getRed:&r green:&g blue:&b fromColor:fgColor];
fg_r = (uint8_t)(r * 255.0f);
fg_g = (uint8_t)(g * 255.0f);
fg_b = (uint8_t)(b * 255.0f);
fg.r = (uint8_t)(r * (CGFloat)255.0);
fg.g = (uint8_t)(g * (CGFloat)255.0);
fg.b = (uint8_t)(b * (CGFloat)255.0);
}
if (bgColor) {
[DDTTYLogger getRed:&r green:&g blue:&b fromColor:bgColor];
bg_r = (uint8_t)(r * 255.0f);
bg_g = (uint8_t)(g * 255.0f);
bg_b = (uint8_t)(b * 255.0f);
bg.r = (uint8_t)(r * (CGFloat)255.0);
bg.g = (uint8_t)(g * (CGFloat)255.0);
bg.b = (uint8_t)(b * (CGFloat)255.0);
}
if (fgColor && isaColorTTY) {
// Map foreground color to closest available shell color
fgCodeIndex = [DDTTYLogger codeIndexForColor:fgColor];
fgCodeRaw = codes_fg[fgCodeIndex];
fgCodeRaw = codesFg[fgCodeIndex];
NSString *escapeSeq = @"\033[";
const __auto_type escapeSeq = @"\033[";
NSUInteger len1 = [escapeSeq lengthOfBytesUsingEncoding:NSUTF8StringEncoding];
NSUInteger len2 = [fgCodeRaw lengthOfBytesUsingEncoding:NSUTF8StringEncoding];
__auto_type len1 = [escapeSeq lengthOfBytesUsingEncoding:NSUTF8StringEncoding];
__auto_type len2 = [fgCodeRaw lengthOfBytesUsingEncoding:NSUTF8StringEncoding];
BOOL escapeSeqEnc = [escapeSeq getCString:(fgCode) maxLength:(len1 + 1) encoding:NSUTF8StringEncoding];
BOOL escapeSeqEnc = [escapeSeq getCString:(fgCode) maxLength:(len1 + 1) encoding:NSUTF8StringEncoding];
BOOL fgCodeRawEsc = [fgCodeRaw getCString:(fgCode + len1) maxLength:(len2 + 1) encoding:NSUTF8StringEncoding];
if (!escapeSeqEnc || !fgCodeRawEsc) {
@@ -1427,14 +1388,11 @@ static DDTTYLogger *sharedInstance;
fgCodeLen = len1 + len2;
} else if (fgColor && isaXcodeColorTTY) {
// Convert foreground color to color code sequence
const char *escapeSeq = XCODE_COLORS_ESCAPE_SEQ;
int result = snprintf(fgCode, 24, "%sfg%u,%u,%u;", escapeSeq, fg_r, fg_g, fg_b);
__auto_type result = snprintf(fgCode, 24, "%sfg%u,%u,%u;", escapeSeq, fg.r, fg.g, fg.b);
fgCodeLen = (NSUInteger)MAX(MIN(result, (24 - 1)), 0);
} else {
// No foreground color or no color support
fgCode[0] = '\0';
fgCodeLen = 0;
}
@@ -1443,14 +1401,14 @@ static DDTTYLogger *sharedInstance;
// Map background color to closest available shell color
bgCodeIndex = [DDTTYLogger codeIndexForColor:bgColor];
bgCodeRaw = codes_bg[bgCodeIndex];
bgCodeRaw = codesBg[bgCodeIndex];
NSString *escapeSeq = @"\033[";
const __auto_type escapeSeq = @"\033[";
NSUInteger len1 = [escapeSeq lengthOfBytesUsingEncoding:NSUTF8StringEncoding];
NSUInteger len2 = [bgCodeRaw lengthOfBytesUsingEncoding:NSUTF8StringEncoding];
__auto_type len1 = [escapeSeq lengthOfBytesUsingEncoding:NSUTF8StringEncoding];
__auto_type len2 = [bgCodeRaw lengthOfBytesUsingEncoding:NSUTF8StringEncoding];
BOOL escapeSeqEnc = [escapeSeq getCString:(bgCode) maxLength:(len1 + 1) encoding:NSUTF8StringEncoding];
BOOL escapeSeqEnc = [escapeSeq getCString:(bgCode) maxLength:(len1 + 1) encoding:NSUTF8StringEncoding];
BOOL bgCodeRawEsc = [bgCodeRaw getCString:(bgCode + len1) maxLength:(len2 + 1) encoding:NSUTF8StringEncoding];
if (!escapeSeqEnc || !bgCodeRawEsc) {
@@ -1460,14 +1418,11 @@ static DDTTYLogger *sharedInstance;
bgCodeLen = len1 + len2;
} else if (bgColor && isaXcodeColorTTY) {
// Convert background color to color code sequence
const char *escapeSeq = XCODE_COLORS_ESCAPE_SEQ;
int result = snprintf(bgCode, 24, "%sbg%u,%u,%u;", escapeSeq, bg_r, bg_g, bg_b);
__auto_type result = snprintf(bgCode, 24, "%sbg%u,%u,%u;", escapeSeq, bg.r, bg.g, bg.b);
bgCodeLen = (NSUInteger)MAX(MIN(result, (24 - 1)), 0);
} else {
// No background color or no color support
bgCode[0] = '\0';
bgCodeLen = 0;
}
@@ -1488,7 +1443,7 @@ static DDTTYLogger *sharedInstance;
- (NSString *)description {
return [NSString stringWithFormat:
@"<DDTTYLoggerColorProfile: %p mask:%i ctxt:%ld fg:%u,%u,%u bg:%u,%u,%u fgCode:%@ bgCode:%@>",
self, (int)mask, (long)context, fg_r, fg_g, fg_b, bg_r, bg_g, bg_b, fgCodeRaw, bgCodeRaw];
self, (int)mask, (long)context, fg.r, fg.g, fg.b, bg.r, bg.g, bg.b, fgCodeRaw, bgCodeRaw];
}
@end

View File

@@ -1,6 +1,6 @@
// Software License Agreement (BSD License)
//
// Copyright (c) 2010-2021, Deusty, LLC
// Copyright (c) 2010-2026, Deusty, LLC
// All rights reserved.
//
// Redistribution and use of this software in source and binary forms,

View File

@@ -1,6 +1,6 @@
// Software License Agreement (BSD License)
//
// Copyright (c) 2010-2021, Deusty, LLC
// Copyright (c) 2010-2026, Deusty, LLC
// All rights reserved.
//
// Redistribution and use of this software in source and binary forms,
@@ -171,7 +171,7 @@
}
- (BOOL)isInSet:(NSInteger)loggingContext {
BOOL result = NO;
__auto_type result = NO;
pthread_mutex_lock(&_mutex);
{

View File

@@ -1,6 +1,6 @@
// Software License Agreement (BSD License)
//
// Copyright (c) 2010-2021, Deusty, LLC
// Copyright (c) 2010-2026, Deusty, LLC
// All rights reserved.
//
// Redistribution and use of this software in source and binary forms,
@@ -134,99 +134,70 @@ static DDQualityOfServiceName _qos_name(NSUInteger qos) {
- (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;
__auto_type useQueueLabel = NO;
if (logMessage->_queueLabel) {
useQueueLabel = YES;
// 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 = @[
const NSArray<NSString *> *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",
@"com.apple.root.default-qos.overcommit"
@"com.apple.root.default-qos.overcommit",
];
for (NSString * name in names) {
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;
// Get the name of the queue, thread, or machID (whichever we are to use).
NSString *queueThreadLabel;
if (useQueueLabel || [logMessage->_threadName length] > 0) {
__auto_type fullLabel = useQueueLabel ? logMessage->_queueLabel : logMessage->_threadName;
NSString *abrvLabel;
if (useQueueLabel) {
fullLabel = logMessage->_queueLabel;
} else {
fullLabel = logMessage->_threadName;
}
pthread_mutex_lock(&_mutex);
{
abrvLabel = _replacements[fullLabel];
}
pthread_mutex_unlock(&_mutex);
if (abrvLabel) {
queueThreadLabel = abrvLabel;
} else {
queueThreadLabel = fullLabel;
}
queueThreadLabel = abrvLabel ?: 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)) {
__auto_type minQueueLength = self.minQueueLength;
__auto_type maxQueueLength = self.maxQueueLength;
__auto_type labelLength = [queueThreadLabel length];
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];
return [queueThreadLabel stringByPaddingToLength:minQueueLength
withString:@" "
startingAtIndex:0];
} else {
// Exact
return queueThreadLabel;
}
}
- (NSString *)formatLogMessage:(DDLogMessage *)logMessage {
NSString *timestamp = [self stringFromDate:(logMessage->_timestamp)];
NSString *queueThreadLabel = [self queueThreadLabelForLogMessage:logMessage];
__auto_type timestamp = [self stringFromDate:logMessage->_timestamp];
__auto_type queueThreadLabel = [self queueThreadLabelForLogMessage:logMessage];
return [NSString stringWithFormat:@"%@ [%@ (QOS:%@)] %@", timestamp, queueThreadLabel, _qos_name(logMessage->_qos), logMessage->_message];
}

View File

@@ -1,6 +1,6 @@
// Software License Agreement (BSD License)
//
// Copyright (c) 2010-2021, Deusty, LLC
// Copyright (c) 2010-2026, Deusty, LLC
// All rights reserved.
//
// Redistribution and use of this software in source and binary forms,
@@ -26,11 +26,11 @@ static const NSUInteger kDDMaxBufferSize = 1048576; // ~1 mB, f_iosize on iphone
// f_bsize == "default", and f_iosize == "max"
static inline NSUInteger p_DDGetDefaultBufferSizeBytesMax(const BOOL max) {
struct statfs *mountedFileSystems = NULL;
int count = getmntinfo(&mountedFileSystems, 0);
__auto_type count = getmntinfo(&mountedFileSystems, 0);
for (int i = 0; i < count; i++) {
struct statfs mounted = mountedFileSystems[i];
const char *name = mounted.f_mntonname;
__auto_type mounted = mountedFileSystems[i];
__auto_type name = mounted.f_mntonname;
// We can use 2 as max here, since any length > 1 will fail the if-statement.
if (strnlen(name, 2) == 1 && *name == '/') {
@@ -41,7 +41,7 @@ static inline NSUInteger p_DDGetDefaultBufferSizeBytesMax(const BOOL max) {
return max ? kDDMaxBufferSize : kDDDefaultBufferSize;
}
static NSUInteger DDGetMaxBufferSizeBytes() {
static NSUInteger DDGetMaxBufferSizeBytes(void) {
static NSUInteger maxBufferSize = 0;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
@@ -50,7 +50,7 @@ static NSUInteger DDGetMaxBufferSizeBytes() {
return maxBufferSize;
}
static NSUInteger DDGetDefaultBufferSizeBytes() {
static NSUInteger DDGetDefaultBufferSizeBytes(void) {
static NSUInteger defaultBufferSize = 0;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
@@ -80,7 +80,7 @@ static NSUInteger DDGetDefaultBufferSizeBytes() {
}
- (void)dealloc {
dispatch_block_t block = ^{
__auto_type block = ^{
[self lt_sendBufferedDataToFileLogger];
self.fileLogger = nil;
};
@@ -111,18 +111,18 @@ static NSUInteger DDGetDefaultBufferSizeBytes() {
- (void)logMessage:(DDLogMessage *)logMessage {
// Don't need to check for isOnInternalLoggerQueue, -lt_dataForMessage: will do it for us.
NSData *data = [_fileLogger lt_dataForMessage:logMessage];
__auto_type data = [_fileLogger lt_dataForMessage:logMessage];
if (data.length == 0) {
return;
}
[data enumerateByteRangesUsingBlock:^(const void * __nonnull bytes, NSRange byteRange, BOOL * __nonnull __unused stop) {
NSUInteger bytesLength = byteRange.length;
__auto_type bytesLength = byteRange.length;
#ifdef NS_BLOCK_ASSERTIONS
__unused
#endif
NSInteger written = [_buffer write:bytes maxLength:bytesLength];
__auto_type written = [_buffer write:bytes maxLength:bytesLength];
NSAssert(written > 0 && (NSUInteger)written == bytesLength, @"Failed to write to memory buffer.");
_currentBufferSizeBytes += bytesLength;
@@ -137,7 +137,7 @@ static NSUInteger DDGetDefaultBufferSizeBytes() {
// This method is public.
// We need to execute the rolling on our logging thread/queue.
dispatch_block_t block = ^{
__auto_type block = ^{
@autoreleasepool {
[self lt_sendBufferedDataToFileLogger];
[self.fileLogger flush];
@@ -150,10 +150,8 @@ static NSUInteger DDGetDefaultBufferSizeBytes() {
if ([self.fileLogger isOnInternalLoggerQueue]) {
block();
} else {
dispatch_queue_t globalLoggingQueue = [DDLog loggingQueue];
NSAssert(![self.fileLogger isOnGlobalLoggingQueue], @"Core architecture requirement failure");
dispatch_sync(globalLoggingQueue, ^{
dispatch_sync(DDLog.loggingQueue, ^{
dispatch_sync(self.fileLogger.loggerQueue, block);
});
}

View File

@@ -1,6 +1,6 @@
// Software License Agreement (BSD License)
//
// Copyright (c) 2010-2021, Deusty, LLC
// Copyright (c) 2010-2026, Deusty, LLC
// All rights reserved.
//
// Redistribution and use of this software in source and binary forms,
@@ -45,11 +45,11 @@
#pragma mark Processing
- (NSString *)formatLogMessage:(DDLogMessage *)logMessage {
__block NSString *line = logMessage->_message;
__block __auto_type line = logMessage->_message;
dispatch_sync(_queue, ^{
for (id<DDLogFormatter> formatter in self->_formatters) {
DDLogMessage *message = [self logMessageForLine:line originalMessage:logMessage];
__auto_type message = [self logMessageForLine:line originalMessage:logMessage];
line = [formatter formatLogMessage:message];
if (!line) {
@@ -63,7 +63,6 @@
- (DDLogMessage *)logMessageForLine:(NSString *)line originalMessage:(DDLogMessage *)message {
DDLogMessage *newMessage = [message copy];
newMessage->_message = line;
return newMessage;
}

View File

@@ -1,6 +1,6 @@
// Software License Agreement (BSD License)
//
// Copyright (c) 2010-2021, Deusty, LLC
// Copyright (c) 2010-2026, Deusty, LLC
// All rights reserved.
//
// Redistribution and use of this software in source and binary forms,

View File

@@ -1,6 +1,6 @@
// Software License Agreement (BSD License)
//
// Copyright (c) 2010-2021, Deusty, LLC
// Copyright (c) 2010-2026, Deusty, LLC
// All rights reserved.
//
// Redistribution and use of this software in source and binary forms,

View File

@@ -1,6 +1,6 @@
// Software License Agreement (BSD License)
//
// Copyright (c) 2010-2021, Deusty, LLC
// Copyright (c) 2010-2026, Deusty, LLC
// All rights reserved.
//
// Redistribution and use of this software in source and binary forms,

View File

@@ -1,6 +1,6 @@
// Software License Agreement (BSD License)
//
// Copyright (c) 2010-2021, Deusty, LLC
// Copyright (c) 2010-2026, Deusty, LLC
// All rights reserved.
//
// Redistribution and use of this software in source and binary forms,

View File

@@ -1,6 +1,6 @@
// Software License Agreement (BSD License)
//
// Copyright (c) 2010-2021, Deusty, LLC
// Copyright (c) 2010-2026, Deusty, LLC
// All rights reserved.
//
// Redistribution and use of this software in source and binary forms,

View File

@@ -1,6 +1,6 @@
// Software License Agreement (BSD License)
//
// Copyright (c) 2010-2021, Deusty, LLC
// Copyright (c) 2010-2026, Deusty, LLC
// All rights reserved.
//
// Redistribution and use of this software in source and binary forms,

View File

@@ -1,6 +1,6 @@
// Software License Agreement (BSD License)
//
// Copyright (c) 2010-2021, Deusty, LLC
// Copyright (c) 2010-2026, Deusty, LLC
// All rights reserved.
//
// Redistribution and use of this software in source and binary forms,
@@ -22,7 +22,7 @@
DDLogError(@"%@", description); \
NSAssert(NO, @"%@", description); \
}
#define DDAssertCondition(condition) DDAssert(condition, @"Condition not satisfied: %s", #condition)
#define DDAssertCondition(condition) DDAssert(condition, @"Condition not satisfied: %@", @(#condition))
/**
* Analog to `DDAssertionFailure` from DDAssert.swift for use in Objective C

View File

@@ -1,6 +1,6 @@
// Software License Agreement (BSD License)
//
// Copyright (c) 2010-2021, Deusty, LLC
// Copyright (c) 2010-2026, Deusty, LLC
// All rights reserved.
//
// Redistribution and use of this software in source and binary forms,

View File

@@ -1,6 +1,6 @@
// Software License Agreement (BSD License)
//
// Copyright (c) 2010-2021, Deusty, LLC
// Copyright (c) 2010-2026, Deusty, LLC
// All rights reserved.
//
// Redistribution and use of this software in source and binary forms,

View File

@@ -1,6 +1,6 @@
// Software License Agreement (BSD License)
//
// Copyright (c) 2010-2021, Deusty, LLC
// Copyright (c) 2010-2026, Deusty, LLC
// All rights reserved.
//
// Redistribution and use of this software in source and binary forms,

View File

@@ -1,6 +1,6 @@
// Software License Agreement (BSD License)
//
// Copyright (c) 2010-2021, Deusty, LLC
// Copyright (c) 2010-2026, Deusty, LLC
// All rights reserved.
//
// Redistribution and use of this software in source and binary forms,

View File

@@ -1,6 +1,6 @@
// Software License Agreement (BSD License)
//
// Copyright (c) 2010-2021, Deusty, LLC
// Copyright (c) 2010-2026, Deusty, LLC
// All rights reserved.
//
// Redistribution and use of this software in source and binary forms,
@@ -48,6 +48,41 @@ extern unsigned long long const kDDDefaultLogFilesDiskQuota;
#pragma mark -
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
/// The serializer is responsible for turning a log message into binary for writing into a file.
/// It allows storing log messages in a non-text format.
/// The serialier should not be used for filtering or formatting messages!
/// Also, it must be fast!
@protocol DDFileLogMessageSerializer <NSObject>
@required
/// Returns the binary representation of the message.
/// - Parameter message: The formatted log message to serialize.
//
/// Returns the binary representation of the message.
/// - Parameters:
/// - string: The string to serialize. Usually, this is the formatted message, but it can also be e.g. a log file header.
/// - message: The message which represents the `string`. This is null, if `string` is e.g. a log file header.
/// - Note: The `message` parameter should not be used for formatting! It should simply be used to extract the necessary metadata for serializing.
- (NSData *)dataForString:(NSString *)string
originatingFromMessage:(nullable DDLogMessage *)message NS_SWIFT_NAME(dataForString(_:originatingFrom:));
@end
/// The (default) plain text message serializer.
@interface DDFileLogPlainTextMessageSerializer : NSObject <DDFileLogMessageSerializer>
- (instancetype)init;
@end
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
#pragma mark -
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
@class DDFileLogger;
/**
* The LogFileManager protocol is designed to allow you to control all aspects of your log files.
*
@@ -152,30 +187,49 @@ extern unsigned long long const kDDDefaultLogFilesDiskQuota;
@optional
// Private methods (only to be used by DDFileLogger)
/**
* Creates a new log file ignoring any errors. Deprecated in favor of `-createNewLogFileWithError:`.
* Will only be called if `-createNewLogFileWithError:` is not implemented.
**/
- (nullable NSString *)createNewLogFile __attribute__((deprecated("Use -createNewLogFileWithError:"))) NS_SWIFT_UNAVAILABLE("Use -createNewLogFileWithError:");
/// The log message serializer.
@property (nonatomic, readonly, strong) id<DDFileLogMessageSerializer> logMessageSerializer;
// Notifications from DDFileLogger
/// The file manager to use. Defaults to `[NSFileManager defaultManager]`.
@property (nonatomic, readonly, strong) NSFileManager *fileManager;
/// Whether the log file should be locked by the file logger before writing to it (and unlocked after).
/// - Parameter logFilePath: The path to the log file for which to decide locking.
/// - Remark: Logging from multiple processes (e.g. an app extensions) to the same log file without file locking will result in interleaved and possibly even overwritten log messages.
/// Without locking, the resulting logfile could be described as "best effort" and might become corrupted.
/// The downside of locking is that if the process holds the file lock when it gets suspended by the system, the system will kill the process.
/// This could happen by inproper handling of app suspension (e.g. not properly calling `+[DDLog flushLog]` and waiting for its completion before suspension).
/// Regardless of locking, you should always call `+[DDLog flushLog]` before your app gets suspended or terminated to make sure every log message makes it to your disk.
- (BOOL)shouldLockLogFile:(NSString *)logFilePath;
/// Manually perform a cleanup of the log files managed by this manager.
/// This can be called from any queue!
- (BOOL)cleanupLogFilesWithError:(NSError **)error;
// MARK: Private methods (only to be used by DDFileLogger)
// MARK: Notifications from DDFileLogger
/// Called when the log file manager was added to a file logger.
/// This should be used to make the manager "active" - like starting internal timers etc.
/// Executed on global queue with default priority.
/// - Parameter fileLogger: The file logger this manager was added to.
/// - Important: The manager **must not** keep a strong reference to `fileLogger` or a retain cycle will be created!
- (void)didAddToFileLogger:(DDFileLogger *)fileLogger;
/// Called when a log file was archived. Executed on global queue with default priority.
/// @param logFilePath The path to the log file that was archived.
/// @param wasRolled Whether or not the archiving happend after rolling the log file.
- (void)didArchiveLogFile:(NSString *)logFilePath wasRolled:(BOOL)wasRolled NS_SWIFT_NAME(didArchiveLogFile(atPath:wasRolled:));
// Deprecated APIs
/**
* Called when a log file was archived. Executed on global queue with default priority.
*/
// MARK: Deprecated APIs
/// Creates a new log file ignoring any errors. Deprecated in favor of `-createNewLogFileWithError:`.
/// Will only be called if `-createNewLogFileWithError:` is not implemented.
- (nullable NSString *)createNewLogFile __attribute__((deprecated("Use -createNewLogFileWithError:"))) NS_SWIFT_UNAVAILABLE("Use -createNewLogFileWithError:");
/// Called when a log file was archived. Executed on global queue with default priority.
- (void)didArchiveLogFile:(NSString *)logFilePath NS_SWIFT_NAME(didArchiveLogFile(atPath:)) __attribute__((deprecated("Use -didArchiveLogFile:wasRolled:")));
/**
* Called when the roll action was executed and the log was archived.
* Executed on global queue with default priority.
*/
/// Called when the roll action was executed and the log was archived. Executed on global queue with default priority.
- (void)didRollAndArchiveLogFile:(NSString *)logFilePath NS_SWIFT_NAME(didRollAndArchiveLogFile(atPath:)) __attribute__((deprecated("Use -didArchiveLogFile:wasRolled:")));
@end
@@ -199,11 +253,6 @@ extern unsigned long long const kDDDefaultLogFilesDiskQuota;
**/
@interface DDLogFileManagerDefault : NSObject <DDLogFileManager>
/**
* Default initializer
*/
- (instancetype)init;
/**
* If logDirectory is not specified, then a folder called "Logs" is created in the app's cache directory.
* While running on the simulator, the "Logs" folder is located in the library temporary directory.
@@ -226,6 +275,9 @@ extern unsigned long long const kDDDefaultLogFilesDiskQuota;
defaultFileProtectionLevel:(NSFileProtectionType)fileProtectionLevel;
#endif
/// Convenience initializer.
- (instancetype)init;
/*
* Methods to override.
*
@@ -276,6 +328,12 @@ extern unsigned long long const kDDDefaultLogFilesDiskQuota;
**/
@property (readonly, copy, nullable) NSString *logFileHeader;
/// The log message serializer.
@property (nonatomic, strong) id<DDFileLogMessageSerializer> logMessageSerializer;
/// The file manager to use. Defaults to `[NSFileManager defaultManager]`.
@property (nonatomic, strong) NSFileManager *fileManager;
/* Inherited from DDLogFileManager protocol:
@property (readwrite, assign, atomic) NSUInteger maximumNumberOfLogFiles;
@@ -310,16 +368,12 @@ extern unsigned long long const kDDDefaultLogFilesDiskQuota;
**/
@interface DDLogFileFormatterDefault : NSObject <DDLogFormatter>
/**
* Default initializer
*/
- (instancetype)init;
/**
* Designated initializer, requires a date formatter
*/
/// Designated initializer, requires a date formatter
- (instancetype)initWithDateFormatter:(nullable NSDateFormatter *)dateFormatter NS_DESIGNATED_INITIALIZER;
/// Convenience initializer
- (instancetype)init;
@end
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
@@ -507,12 +561,18 @@ extern unsigned long long const kDDDefaultLogFilesDiskQuota;
@property (nonatomic, readonly) NSTimeInterval age;
@property (nonatomic, readonly) BOOL isSymlink;
@property (nonatomic, readwrite) BOOL isArchived;
+ (nullable instancetype)logFileWithPath:(nullable NSString *)filePath NS_SWIFT_UNAVAILABLE("Use init(filePath:)");
+ (nullable instancetype)logFileWithPath:(nullable NSString *)filePath
__attribute__((deprecated("Check file path for nil and pass it to the initializer instead")))
NS_SWIFT_UNAVAILABLE("Use init(filePath:)");
- (instancetype)init NS_UNAVAILABLE;
- (instancetype)initWithFilePath:(NSString *)filePath NS_DESIGNATED_INITIALIZER;
// TODO: This should really become the designated initializer.
- (instancetype)initWithFilePath:(NSString *)filePath fileManager:(NSFileManager *)fileManager;
- (void)reset;
- (void)renameFile:(NSString *)newFileName NS_SWIFT_NAME(renameFile(to:));

View File

@@ -1,6 +1,6 @@
// Software License Agreement (BSD License)
//
// Copyright (c) 2010-2021, Deusty, LLC
// Copyright (c) 2010-2026, Deusty, LLC
// All rights reserved.
//
// Redistribution and use of this software in source and binary forms,

View File

@@ -1,6 +1,6 @@
// Software License Agreement (BSD License)
//
// Copyright (c) 2010-2021, Deusty, LLC
// Copyright (c) 2010-2026, Deusty, LLC
// All rights reserved.
//
// Redistribution and use of this software in source and binary forms,
@@ -207,6 +207,20 @@ FOUNDATION_EXTERN NSString * __nullable DDExtractFileNameWithoutExtension(const
**/
#define THIS_METHOD NSStringFromSelector(_cmd)
/**
* Makes a declaration "Sendable" in Swift (if supported by the compiler).
*/
#ifndef DD_SENDABLE
#ifdef __has_attribute
#if __has_attribute(swift_attr)
#define DD_SENDABLE __attribute__((swift_attr("@Sendable")))
#endif
#endif
#endif
#ifndef DD_SENDABLE
#define DD_SENDABLE
#endif
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
#pragma mark -
@@ -216,6 +230,7 @@ FOUNDATION_EXTERN NSString * __nullable DDExtractFileNameWithoutExtension(const
* The main class, exposes all logging mechanisms, loggers, ...
* For most of the users, this class is hidden behind the logging functions like `DDLogInfo`
*/
DD_SENDABLE
@interface DDLog : NSObject
/**
@@ -770,11 +785,13 @@ typedef NS_OPTIONS(NSInteger, DDLogMessageOptions){
* The `DDLogMessage` class encapsulates information about the log message.
* If you write custom loggers or formatters, you will be dealing with objects of this class.
**/
DD_SENDABLE
@interface DDLogMessage : NSObject <NSCopying>
{
// Direct accessors to be used only for performance
@public
NSString *_message;
NSString *_messageFormat;
DDLogLevel _level;
DDLogFlag _flag;
NSInteger _context;
@@ -782,9 +799,9 @@ typedef NS_OPTIONS(NSInteger, DDLogMessageOptions){
NSString *_fileName;
NSString *_function;
NSUInteger _line;
#if DD_LEGACY_MESSAGE_TAG
id _tag __attribute__((deprecated("Use _representedObject instead", "_representedObject")));;
#endif
#if DD_LEGACY_MESSAGE_TAG
id _tag __attribute__((deprecated("Use _representedObject instead", "_representedObject")));
#endif
id _representedObject;
DDLogMessageOptions _options;
NSDate * _timestamp;
@@ -813,6 +830,64 @@ typedef NS_OPTIONS(NSInteger, DDLogMessageOptions){
* so it makes sense to optimize and skip the unnecessary allocations.
* However, if you need them to be copied you may use the options parameter to specify this.
*
* @param messageFormat the message format
* @param message the formatted message
* @param level the log level
* @param flag the log flag
* @param context the context (if any is defined)
* @param file the current file
* @param function the current function
* @param line the current code line
* @param tag potential tag
* @param options a bitmask which supports DDLogMessageCopyFile and DDLogMessageCopyFunction.
* @param timestamp the log timestamp
*
* @return a new instance of a log message model object
*/
- (instancetype)initWithFormat:(NSString *)messageFormat
formatted:(NSString *)message
level:(DDLogLevel)level
flag:(DDLogFlag)flag
context:(NSInteger)context
file:(NSString *)file
function:(nullable NSString *)function
line:(NSUInteger)line
tag:(nullable id)tag
options:(DDLogMessageOptions)options
timestamp:(nullable NSDate *)timestamp NS_DESIGNATED_INITIALIZER;
/**
* Convenience initializer taking a `va_list` as arguments to create the formatted message.
*
* @param messageFormat the message format
* @param messageArgs the message arguments.
* @param level the log level
* @param flag the log flag
* @param context the context (if any is defined)
* @param file the current file
* @param function the current function
* @param line the current code line
* @param tag potential tag
* @param options a bitmask which supports DDLogMessageCopyFile and DDLogMessageCopyFunction.
* @param timestamp the log timestamp
*
* @return a new instance of a log message model object
*/
- (instancetype)initWithFormat:(NSString *)messageFormat
args:(va_list)messageArgs
level:(DDLogLevel)level
flag:(DDLogFlag)flag
context:(NSInteger)context
file:(NSString *)file
function:(nullable NSString *)function
line:(NSUInteger)line
tag:(nullable id)tag
options:(DDLogMessageOptions)options
timestamp:(nullable NSDate *)timestamp;
/**
* Deprecated initialier. See initWithFormat:args:formatted:level:flag:context:file:function:line:tag:options:timestamp:.
*
* @param message the message
* @param level the log level
* @param flag the log flag
@@ -835,16 +910,21 @@ typedef NS_OPTIONS(NSInteger, DDLogMessageOptions){
line:(NSUInteger)line
tag:(nullable id)tag
options:(DDLogMessageOptions)options
timestamp:(nullable NSDate *)timestamp NS_DESIGNATED_INITIALIZER;
timestamp:(nullable NSDate *)timestamp
__attribute__((deprecated("Use initializer taking unformatted message and args instead", "initWithFormat:formatted:level:flag:context:file:function:line:tag:options:timestamp:")));
/**
* Read-only properties
**/
/**
* The log message
* The log message.
*/
@property (readonly, nonatomic) NSString *message;
/**
* The message format. When the deprecated initializer is used, this might be the same as `message`.
*/
@property (readonly, nonatomic) NSString *messageFormat;
@property (readonly, nonatomic) DDLogLevel level;
@property (readonly, nonatomic) DDLogFlag flag;
@property (readonly, nonatomic) NSInteger context;
@@ -889,7 +969,7 @@ typedef NS_OPTIONS(NSInteger, DDLogMessageOptions){
{
// Direct accessors to be used only for performance
@public
id <DDLogFormatter> _logFormatter;
_Nullable id <DDLogFormatter> _logFormatter;
dispatch_queue_t _loggerQueue;
}
@@ -901,7 +981,7 @@ typedef NS_OPTIONS(NSInteger, DDLogMessageOptions){
/**
* Return YES if the current logger uses a global queue for logging
*/
@property (nonatomic, readonly, getter=isOnGlobalLoggingQueue) BOOL onGlobalLoggingQueue;
@property (nonatomic, readonly, getter=isOnGlobalLoggingQueue) BOOL onGlobalLoggingQueue;
/**
* Return YES if the current logger uses the internal designated queue for logging
@@ -910,10 +990,27 @@ typedef NS_OPTIONS(NSInteger, DDLogMessageOptions){
@end
#define _DDAbstractLoggerSelectorMessage(msg) [NSStringFromSelector(_cmd) stringByAppendingString:@" " msg]
// Note: we do not wrap these in any do {...} while 0 construct, because NSAssert does that for us.
#define DDAbstractLoggerAssertOnGlobalLoggingQueue() \
NSAssert([self isOnGlobalLoggingQueue], _DDAbstractLoggerSelectorMessage("must only be called on the global logging queue!"))
#define DDAbstractLoggerAssertOnInternalLoggerQueue() \
NSAssert([self isOnInternalLoggerQueue], _DDAbstractLoggerSelectorMessage("must only be called on the internal logger queue!"))
#define DDAbstractLoggerAssertNotOnGlobalLoggingQueue() \
NSAssert(![self isOnGlobalLoggingQueue], _DDAbstractLoggerSelectorMessage("must not be called on the global logging queue!"))
#define DDAbstractLoggerAssertNotOnInternalLoggerQueue() \
NSAssert(![self isOnGlobalLoggingQueue], _DDAbstractLoggerSelectorMessage("must not be called on the internal logger queue!"))
#define DDAbstractLoggerAssertLockedPropertyAccess() \
DDAbstractLoggerAssertNotOnGlobalLoggingQueue(); \
NSAssert(![self isOnInternalLoggerQueue], @"MUST access ivar directly, NOT via self.* syntax.")
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
#pragma mark -
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
DD_SENDABLE
@interface DDLoggerInformation : NSObject
@property (nonatomic, readonly) id <DDLogger> logger;

View File

@@ -1,6 +1,6 @@
// Software License Agreement (BSD License)
//
// Copyright (c) 2010-2021, Deusty, LLC
// Copyright (c) 2010-2026, Deusty, LLC
// All rights reserved.
//
// Redistribution and use of this software in source and binary forms,
@@ -80,10 +80,10 @@
* We also define shorthand versions for asynchronous and synchronous logging.
**/
#define LOG_MAYBE(async, lvl, flg, ctx, tag, fnct, frmt, ...) \
do { if((lvl & flg) != 0) LOG_MACRO(async, lvl, flg, ctx, tag, fnct, frmt, ##__VA_ARGS__); } while(0)
do { if(((NSUInteger)lvl & (NSUInteger)flg) != 0) LOG_MACRO(async, lvl, flg, ctx, tag, fnct, frmt, ##__VA_ARGS__); } while(0)
#define LOG_MAYBE_TO_DDLOG(ddlog, async, lvl, flg, ctx, tag, fnct, frmt, ...) \
do { if((lvl & flg) != 0) LOG_MACRO_TO_DDLOG(ddlog, async, lvl, flg, ctx, tag, fnct, frmt, ##__VA_ARGS__); } while(0)
do { if(((NSUInteger)lvl & (NSUInteger)flg) != 0) LOG_MACRO_TO_DDLOG(ddlog, async, lvl, flg, ctx, tag, fnct, frmt, ##__VA_ARGS__); } while(0)
/**
* Ready to use log macros with no context or tag.

View File

@@ -1,6 +1,6 @@
// Software License Agreement (BSD License)
//
// Copyright (c) 2010-2021, Deusty, LLC
// Copyright (c) 2010-2026, Deusty, LLC
// All rights reserved.
//
// Redistribution and use of this software in source and binary forms,

View File

@@ -1,6 +1,6 @@
// Software License Agreement (BSD License)
//
// Copyright (c) 2010-2021, Deusty, LLC
// Copyright (c) 2010-2026, Deusty, LLC
// All rights reserved.
//
// Redistribution and use of this software in source and binary forms,

View File

@@ -1,6 +1,6 @@
// Software License Agreement (BSD License)
//
// Copyright (c) 2010-2021, Deusty, LLC
// Copyright (c) 2010-2026, Deusty, LLC
// All rights reserved.
//
// Redistribution and use of this software in source and binary forms,
@@ -13,7 +13,9 @@
// to endorse or promote products derived from this software without specific
// prior written permission of Deusty, LLC.
#import <TargetConditionals.h>
#import <Foundation/Foundation.h>
#import <os/log.h>
// Disable legacy macros
#ifndef DD_LEGACY_MACROS
@@ -24,31 +26,79 @@
NS_ASSUME_NONNULL_BEGIN
/**
* This class provides a logger for the Apple os_log facility.
**/
/// Describes a type that maps CocoaLumberjack log levels to os\_log levels (`os_log_type_t`).
API_AVAILABLE(macos(10.12), ios(10.0), watchos(3.0), tvos(10.0))
DD_SENDABLE
@protocol DDOSLogLevelMapper <NSObject>
/// Maps the given `DDLogFlag` to a `os_log_type_t`.
/// - Parameter logFlag: `DDLogFlag` for which to return the os log type.
- (os_log_type_t)osLogTypeForLogFlag:(DDLogFlag)logFlag;
@end
/// The default os\_log level mapper.
/// Uses the following mapping:
/// - `DDLogFlagError` -> `OS_LOG_TYPE_ERROR`
/// - `DDLogFlagWarning` -> `OS_LOG_TYPE_ERROR`
/// - `DDLogFlagInfo` -> `OS_LOG_TYPE_INFO`
/// - `DDLogFlagDebug` -> `OS_LOG_TYPE_DEBUG`
/// - `DDLogFlagVerbose` -> `OS_LOG_TYPE_DEBUG`
API_AVAILABLE(macos(10.12), ios(10.0), watchos(3.0), tvos(10.0))
DD_SENDABLE
@interface DDOSLogLevelMapperDefault : NSObject <DDOSLogLevelMapper>
- (instancetype)init NS_DESIGNATED_INITIALIZER;
@end
#if TARGET_OS_SIMULATOR
/// An os\_log level mapper that works around the fact that `OS_LOG_TYPE_DEBUG` messages logged in the simulator do not show up in the Console.app.
/// Performs the same mapping as ``DDOSLogLevelMapperDefault``, except that `OS_LOG_TYPE_DEBUG` is raised to `OS_LOG_TYPE_DEFAULT`.
/// See [this thread](https://developer.apple.com/forums/thread/82736?answerId=761544022#761544022) in the Apple Developer Forums.
API_AVAILABLE(macos(10.12), ios(10.0), watchos(3.0), tvos(10.0))
DD_SENDABLE
@interface DDOSLogLevelMapperSimulatorConsoleAppWorkaround : DDOSLogLevelMapperDefault
@end
#endif
/// This class provides a logger for the Apple os\_log facility.
API_AVAILABLE(macos(10.12), ios(10.0), watchos(3.0), tvos(10.0))
DD_SENDABLE
@interface DDOSLogger : DDAbstractLogger <DDLogger>
/**
* Singleton method
*
* @return the shared instance with OS_LOG_DEFAULT.
*/
/// The shared instance using `OS_LOG_DEFAULT`.
@property (nonatomic, class, readonly, strong) DDOSLogger *sharedInstance;
/**
Designated initializer
@param subsystem Desired subsystem in log. E.g. "org.example"
@param category Desired category in log. E.g. "Point of interests."
@return New instance of DDOSLogger.
@discussion This method requires either both or no parameter to be set. Much like `(String, String)?` in Swift.
If both parameters are nil, this method will return a logger configured with `OS_LOG_DEFAULT`.
If both parameters are non-nil, it will return a logger configured with `os_log_create(subsystem, category)`
*/
- (instancetype)initWithSubsystem:(nullable NSString *)subsystem category:(nullable NSString *)category NS_DESIGNATED_INITIALIZER;
/// The log level mapper, that maps ``DDLogFlag``s to ``os_log_type_t`` for this logger.
@property (nonatomic, strong, readonly) id<DDOSLogLevelMapper> logLevelMapper;
/// An initializer that in addition to subsystem and category also allows providing the log level mapper.
/// @param subsystem Desired subsystem in log. E.g. "org.example"
/// @param category Desired category in log. E.g. "Point of interests."
/// @param logLevelMapper The log level mapper to use.
/// @discussion This method requires either both or no parameter to be set. Much like `(String, String)?` in Swift.
/// If both parameters are nil, this method will return a logger configured with `OS_LOG_DEFAULT`.
/// If both parameters are non-nil, it will return a logger configured with `os_log_create(subsystem, category)`
- (instancetype)initWithSubsystem:(nullable NSString *)subsystem
category:(nullable NSString *)category
logLevelMapper:(id<DDOSLogLevelMapper>)logLevelMapper NS_DESIGNATED_INITIALIZER;
/// The designated initializer, using `DDOSLogLevelMapperDefault`.
/// @param subsystem Desired subsystem in log. E.g. "org.example"
/// @param category Desired category in log. E.g. "Point of interests."
/// @discussion This method requires either both or no parameter to be set. Much like `(String, String)?` in Swift.
/// If both parameters are nil, this method will return a logger configured with `OS_LOG_DEFAULT`.
/// If both parameters are non-nil, it will return a logger configured with `os_log_create(subsystem, category)`.
- (instancetype)initWithSubsystem:(nullable NSString *)subsystem
category:(nullable NSString *)category;
/// Creates an instance that uses `OS_LOG_DEFAULT`.
/// @param logLevelMapper The log level mapper to use.
- (instancetype)initWithLogLevelMapper:(id<DDOSLogLevelMapper>)logLevelMapper;
/// Creates an instance that uses `OS_LOG_DEFAULT` and `DDOSLogLevelMapperDefault`.
- (instancetype)init;
@end

View File

@@ -1,6 +1,6 @@
// Software License Agreement (BSD License)
//
// Copyright (c) 2010-2021, Deusty, LLC
// Copyright (c) 2010-2026, Deusty, LLC
// All rights reserved.
//
// Redistribution and use of this software in source and binary forms,
@@ -28,17 +28,17 @@
// iOS or tvOS or watchOS
#import <UIKit/UIColor.h>
typedef UIColor DDColor;
static inline DDColor* _Nonnull DDMakeColor(CGFloat r, CGFloat g, CGFloat b) {return [DDColor colorWithRed:(r/255.0f) green:(g/255.0f) blue:(b/255.0f) alpha:1.0f];}
static inline DDColor* _Nonnull DDMakeColor(CGFloat r, CGFloat g, CGFloat b) {return [DDColor colorWithRed:(r/(CGFloat)255.0) green:(g/(CGFloat)255.0) blue:(b/(CGFloat)255.0) alpha:1.0];}
#elif defined(DD_CLI) || !__has_include(<AppKit/NSColor.h>)
// OS X CLI
#import <CocoaLumberjack/CLIColor.h>
typedef CLIColor DDColor;
static inline DDColor* _Nonnull DDMakeColor(CGFloat r, CGFloat g, CGFloat b) {return [DDColor colorWithCalibratedRed:(r/255.0f) green:(g/255.0f) blue:(b/255.0f) alpha:1.0f];}
static inline DDColor* _Nonnull DDMakeColor(CGFloat r, CGFloat g, CGFloat b) {return [DDColor colorWithCalibratedRed:(r/255.0) green:(g/255.0) blue:(b/255.0) alpha:1.0];}
#else
// OS X with AppKit
#import <AppKit/NSColor.h>
typedef NSColor DDColor;
static inline DDColor * _Nonnull DDMakeColor(CGFloat r, CGFloat g, CGFloat b) {return [DDColor colorWithCalibratedRed:(r/255.0f) green:(g/255.0f) blue:(b/255.0f) alpha:1.0f];}
static inline DDColor * _Nonnull DDMakeColor(CGFloat r, CGFloat g, CGFloat b) {return [DDColor colorWithCalibratedRed:(r/255.0) green:(g/255.0) blue:(b/255.0) alpha:1.0];}
#endif
#pragma clang diagnostic pop

View File

@@ -1,6 +1,6 @@
// Software License Agreement (BSD License)
//
// Copyright (c) 2010-2021, Deusty, LLC
// Copyright (c) 2010-2026, Deusty, LLC
// All rights reserved.
//
// Redistribution and use of this software in source and binary forms,
@@ -13,71 +13,164 @@
// to endorse or promote products derived from this software without specific
// prior written permission of Deusty, LLC.
@_exported import CocoaLumberjack
@_exported public import CocoaLumberjack
#if SWIFT_PACKAGE
import CocoaLumberjackSwiftSupport
public import CocoaLumberjackSwiftSupport
#endif
extension DDLogFlag {
public static func from(_ logLevel: DDLogLevel) -> DDLogFlag {
return DDLogFlag(rawValue: logLevel.rawValue)
}
public init(_ logLevel: DDLogLevel) {
self = DDLogFlag(rawValue: logLevel.rawValue)
}
/// Returns the log level, or the lowest equivalent.
public func toLogLevel() -> DDLogLevel {
if let ourValid = DDLogLevel(rawValue: rawValue) {
return ourValid
} else {
if contains(.verbose) {
return .verbose
} else if contains(.debug) {
return .debug
} else if contains(.info) {
return .info
} else if contains(.warning) {
return .warning
} else if contains(.error) {
return .error
} else {
return .off
}
}
}
}
/// The log level that can dynamically limit log messages (vs. the static DDDefaultLogLevel). This log level will only be checked, if the message passes the `DDDefaultLogLevel`.
public var dynamicLogLevel = DDLogLevel.all
/// Resets the `dynamicLogLevel` to `.all`.
/// - SeeAlso: `dynamicLogLevel`
@inlinable
public func resetDynamicLogLevel() {
dynamicLogLevel = .all
}
@available(*, deprecated, message: "Please use dynamicLogLevel", renamed: "dynamicLogLevel")
public var defaultDebugLevel: DDLogLevel {
get {
return dynamicLogLevel
public func _DDLogMessage(_ messageFormat: @autoclosure () -> DDLogMessageFormat,
level: DDLogLevel,
flag: DDLogFlag,
context: Int,
file: StaticString,
function: StaticString,
line: UInt,
tag: Any?,
asynchronous: Bool?,
ddlog: DDLog) {
// The `dynamicLogLevel` will always be checked here (instead of being passed in).
// We cannot "mix" it with the `DDDefaultLogLevel`, because otherwise the compiler won't strip strings that are not logged.
#if compiler(>=6.2)
if unsafe level.rawValue & flag.rawValue != 0 && dynamicLogLevel.rawValue & flag.rawValue != 0 {
let logMessage = DDLogMessage(messageFormat(),
level: level,
flag: flag,
context: context,
file: file,
function: function,
line: line,
tag: tag)
unsafe ddlog.log(asynchronous: asynchronous ?? asyncLoggingEnabled, message: logMessage)
}
set {
dynamicLogLevel = newValue
#else
if level.rawValue & flag.rawValue != 0 && dynamicLogLevel.rawValue & flag.rawValue != 0 {
let logMessage = DDLogMessage(messageFormat(),
level: level,
flag: flag,
context: context,
file: file,
function: function,
line: line,
tag: tag)
ddlog.log(asynchronous: asynchronous ?? asyncLoggingEnabled, message: logMessage)
}
#endif
}
@available(*, deprecated, message: "Please use resetDynamicLogLevel", renamed: "resetDynamicLogLevel")
public func resetDefaultDebugLevel() {
resetDynamicLogLevel()
}
/// If `true`, all logs (except errors) are logged asynchronously by default.
public var asyncLoggingEnabled = true
@inlinable
public func DDLogDebug(_ message: @autoclosure () -> DDLogMessageFormat,
level: DDLogLevel = DDDefaultLogLevel,
context: Int = 0,
file: StaticString = #file,
function: StaticString = #function,
line: UInt = #line,
tag: Any? = nil,
asynchronous: Bool? = nil,
ddlog: DDLog = .sharedInstance) {
_DDLogMessage(message(),
level: level,
flag: .debug,
context: context,
file: file,
function: function,
line: line,
tag: tag,
asynchronous: asynchronous,
ddlog: ddlog)
}
@inlinable
public func DDLogInfo(_ message: @autoclosure () -> DDLogMessageFormat,
level: DDLogLevel = DDDefaultLogLevel,
context: Int = 0,
file: StaticString = #file,
function: StaticString = #function,
line: UInt = #line,
tag: Any? = nil,
asynchronous: Bool? = nil,
ddlog: DDLog = .sharedInstance) {
_DDLogMessage(message(),
level: level,
flag: .info,
context: context,
file: file,
function: function,
line: line,
tag: tag,
asynchronous: asynchronous,
ddlog: ddlog)
}
@inlinable
public func DDLogWarn(_ message: @autoclosure () -> DDLogMessageFormat,
level: DDLogLevel = DDDefaultLogLevel,
context: Int = 0,
file: StaticString = #file,
function: StaticString = #function,
line: UInt = #line,
tag: Any? = nil,
asynchronous: Bool? = nil,
ddlog: DDLog = .sharedInstance) {
_DDLogMessage(message(),
level: level,
flag: .warning,
context: context,
file: file,
function: function,
line: line,
tag: tag,
asynchronous: asynchronous,
ddlog: ddlog)
}
@inlinable
public func DDLogVerbose(_ message: @autoclosure () -> DDLogMessageFormat,
level: DDLogLevel = DDDefaultLogLevel,
context: Int = 0,
file: StaticString = #file,
function: StaticString = #function,
line: UInt = #line,
tag: Any? = nil,
asynchronous: Bool? = nil,
ddlog: DDLog = .sharedInstance) {
_DDLogMessage(message(),
level: level,
flag: .verbose,
context: context,
file: file,
function: function,
line: line,
tag: tag,
asynchronous: asynchronous,
ddlog: ddlog)
}
@inlinable
public func DDLogError(_ message: @autoclosure () -> DDLogMessageFormat,
level: DDLogLevel = DDDefaultLogLevel,
context: Int = 0,
file: StaticString = #file,
function: StaticString = #function,
line: UInt = #line,
tag: Any? = nil,
asynchronous: Bool? = nil,
ddlog: DDLog = .sharedInstance) {
_DDLogMessage(message(),
level: level,
flag: .error,
context: context,
file: file,
function: function,
line: line,
tag: tag,
asynchronous: asynchronous ?? false,
ddlog: ddlog)
}
@available(*, deprecated, message: "Use an interpolated DDLogMessageFormat instead")
@inlinable
@_disfavoredOverload
public func _DDLogMessage(_ message: @autoclosure () -> Any,
level: DDLogLevel,
flag: DDLogFlag,
@@ -86,27 +179,25 @@ public func _DDLogMessage(_ message: @autoclosure () -> Any,
function: StaticString,
line: UInt,
tag: Any?,
asynchronous: Bool,
asynchronous: Bool?,
ddlog: DDLog) {
// The `dynamicLogLevel` will always be checked here (instead of being passed in).
// We cannot "mix" it with the `DDDefaultLogLevel`, because otherwise the compiler won't strip strings that are not logged.
if level.rawValue & flag.rawValue != 0 && dynamicLogLevel.rawValue & flag.rawValue != 0 {
// Tell the DDLogMessage constructor to copy the C strings that get passed to it.
let logMessage = DDLogMessage(message: String(describing: message()),
level: level,
flag: flag,
context: context,
file: String(describing: file),
function: String(describing: function),
line: line,
tag: tag,
options: [.copyFile, .copyFunction],
timestamp: nil)
ddlog.log(asynchronous: asynchronous, message: logMessage)
}
// This will lead to `messageFormat` and `message` being equal on DDLogMessage,
// which is what the legacy initializer of DDLogMessage does as well.
_DDLogMessage(.init(_formattedMessage: String(describing: message())),
level: level,
flag: flag,
context: context,
file: file,
function: function,
line: line,
tag: tag,
asynchronous: asynchronous,
ddlog: ddlog)
}
@available(*, deprecated, message: "Use an interpolated DDLogMessageFormat instead")
@inlinable
@_disfavoredOverload
public func DDLogDebug(_ message: @autoclosure () -> Any,
level: DDLogLevel = DDDefaultLogLevel,
context: Int = 0,
@@ -114,12 +205,23 @@ public func DDLogDebug(_ message: @autoclosure () -> Any,
function: StaticString = #function,
line: UInt = #line,
tag: Any? = nil,
asynchronous async: Bool = asyncLoggingEnabled,
asynchronous async: Bool? = nil,
ddlog: DDLog = .sharedInstance) {
_DDLogMessage(message(), level: level, flag: .debug, context: context, file: file, function: function, line: line, tag: tag, asynchronous: async, ddlog: ddlog)
_DDLogMessage(message(),
level: level,
flag: .debug,
context: context,
file: file,
function: function,
line: line,
tag: tag,
asynchronous: async,
ddlog: ddlog)
}
@available(*, deprecated, message: "Use an interpolated DDLogMessageFormat instead")
@inlinable
@_disfavoredOverload
public func DDLogInfo(_ message: @autoclosure () -> Any,
level: DDLogLevel = DDDefaultLogLevel,
context: Int = 0,
@@ -127,12 +229,23 @@ public func DDLogInfo(_ message: @autoclosure () -> Any,
function: StaticString = #function,
line: UInt = #line,
tag: Any? = nil,
asynchronous async: Bool = asyncLoggingEnabled,
asynchronous async: Bool? = nil,
ddlog: DDLog = .sharedInstance) {
_DDLogMessage(message(), level: level, flag: .info, context: context, file: file, function: function, line: line, tag: tag, asynchronous: async, ddlog: ddlog)
_DDLogMessage(message(),
level: level,
flag: .info,
context: context,
file: file,
function: function,
line: line,
tag: tag,
asynchronous: async,
ddlog: ddlog)
}
@available(*, deprecated, message: "Use an interpolated DDLogMessageFormat instead")
@inlinable
@_disfavoredOverload
public func DDLogWarn(_ message: @autoclosure () -> Any,
level: DDLogLevel = DDDefaultLogLevel,
context: Int = 0,
@@ -140,12 +253,23 @@ public func DDLogWarn(_ message: @autoclosure () -> Any,
function: StaticString = #function,
line: UInt = #line,
tag: Any? = nil,
asynchronous async: Bool = asyncLoggingEnabled,
asynchronous async: Bool? = nil,
ddlog: DDLog = .sharedInstance) {
_DDLogMessage(message(), level: level, flag: .warning, context: context, file: file, function: function, line: line, tag: tag, asynchronous: async, ddlog: ddlog)
_DDLogMessage(message(),
level: level,
flag: .warning,
context: context,
file: file,
function: function,
line: line,
tag: tag,
asynchronous: async,
ddlog: ddlog)
}
@available(*, deprecated, message: "Use an interpolated DDLogMessageFormat instead")
@inlinable
@_disfavoredOverload
public func DDLogVerbose(_ message: @autoclosure () -> Any,
level: DDLogLevel = DDDefaultLogLevel,
context: Int = 0,
@@ -153,12 +277,23 @@ public func DDLogVerbose(_ message: @autoclosure () -> Any,
function: StaticString = #function,
line: UInt = #line,
tag: Any? = nil,
asynchronous async: Bool = asyncLoggingEnabled,
asynchronous async: Bool? = nil,
ddlog: DDLog = .sharedInstance) {
_DDLogMessage(message(), level: level, flag: .verbose, context: context, file: file, function: function, line: line, tag: tag, asynchronous: async, ddlog: ddlog)
_DDLogMessage(message(),
level: level,
flag: .verbose,
context: context,
file: file,
function: function,
line: line,
tag: tag,
asynchronous: async,
ddlog: ddlog)
}
@available(*, deprecated, message: "Use an interpolated DDLogMessageFormat instead")
@inlinable
@_disfavoredOverload
public func DDLogError(_ message: @autoclosure () -> Any,
level: DDLogLevel = DDDefaultLogLevel,
context: Int = 0,
@@ -166,28 +301,16 @@ public func DDLogError(_ message: @autoclosure () -> Any,
function: StaticString = #function,
line: UInt = #line,
tag: Any? = nil,
asynchronous async: Bool = false,
asynchronous async: Bool? = nil,
ddlog: DDLog = .sharedInstance) {
_DDLogMessage(message(), level: level, flag: .error, context: context, file: file, function: function, line: line, tag: tag, asynchronous: async, ddlog: ddlog)
}
/// Returns a String of the current filename, without full path or extension.
///
/// Analogous to the C preprocessor macro `THIS_FILE`.
public func currentFileName(_ fileName: StaticString = #file) -> String {
var str = String(describing: fileName)
if let idx = str.range(of: "/", options: .backwards)?.upperBound {
str = String(str[idx...])
}
if let idx = str.range(of: ".", options: .backwards)?.lowerBound {
str = String(str[..<idx])
}
return str
}
// swiftlint:disable identifier_name
// swiftlint doesn't like func names that begin with a capital letter - deprecated
@available(*, deprecated, message: "Please use currentFileName", renamed: "currentFileName")
public func CurrentFileName(_ fileName: StaticString = #file) -> String {
return currentFileName(fileName)
_DDLogMessage(message(),
level: level,
flag: .error,
context: context,
file: file,
function: function,
line: line,
tag: tag,
asynchronous: async ?? false,
ddlog: ddlog)
}

View File

@@ -1,6 +1,6 @@
// Software License Agreement (BSD License)
//
// Copyright (c) 2010-2021, Deusty, LLC
// Copyright (c) 2010-2026, Deusty, LLC
// All rights reserved.
//
// Redistribution and use of this software in source and binary forms,
@@ -14,8 +14,8 @@
// prior written permission of Deusty, LLC.
#if SWIFT_PACKAGE
import CocoaLumberjack
import CocoaLumberjackSwiftSupport
public import CocoaLumberjack
public import CocoaLumberjackSwiftSupport
#endif
/**
@@ -29,9 +29,91 @@ import CocoaLumberjackSwiftSupport
* The default is an empty string.
*/
@inlinable
public func DDAssert(_ condition: @autoclosure () -> Bool, _ message: @autoclosure () -> String = "", level: DDLogLevel = DDDefaultLogLevel, context: Int = 0, file: StaticString = #file, function: StaticString = #function, line: UInt = #line, tag: Any? = nil, asynchronous async: Bool = false, ddlog: DDLog = DDLog.sharedInstance) {
public func DDAssert(_ condition: @autoclosure () -> Bool,
_ message: @autoclosure () -> DDLogMessageFormat = "",
level: DDLogLevel = DDDefaultLogLevel,
context: Int = 0,
file: StaticString = #file,
function: StaticString = #function,
line: UInt = #line,
tag: Any? = nil,
asynchronous async: Bool? = nil,
ddlog: DDLog = DDLog.sharedInstance) {
if !condition() {
DDLogError(message(), level: level, context: context, file: file, function: function, line: line, tag: tag, asynchronous: async, ddlog: ddlog)
DDLogError(message(),
level: level,
context: context,
file: file,
function: function,
line: line,
tag: tag,
asynchronous: async,
ddlog: ddlog)
Swift.assertionFailure(message().formatted, file: file, line: line)
}
}
/**
* Replacement for Swift's `assertionFailure` function that will output a log message even
* when assertions are disabled.
*
* - Parameters:
* - message: A string to log (using `DDLogError`). The default is an empty string.
*/
@inlinable
public func DDAssertionFailure(_ message: @autoclosure () -> DDLogMessageFormat = "",
level: DDLogLevel = DDDefaultLogLevel,
context: Int = 0,
file: StaticString = #file,
function: StaticString = #function,
line: UInt = #line,
tag: Any? = nil,
asynchronous async: Bool? = nil,
ddlog: DDLog = DDLog.sharedInstance) {
DDLogError(message(),
level: level,
context: context,
file: file,
function: function,
line: line,
tag: tag,
asynchronous: async,
ddlog: ddlog)
Swift.assertionFailure(message().formatted, file: file, line: line)
}
/**
* Replacement for Swift's `assert` function that will output a log message even when assertions
* are disabled.
*
* - Parameters:
* - condition: The condition to test. Unlike `Swift.assert`, `condition` is always evaluated,
* even when assertions are disabled.
* - message: A string to log (using `DDLogError`) if `condition` evaluates to `false`.
* The default is an empty string.
*/
@inlinable
@available(*, deprecated, message: "Use an interpolated message.")
public func DDAssert(_ condition: @autoclosure () -> Bool,
_ message: @autoclosure () -> String = "",
level: DDLogLevel = DDDefaultLogLevel,
context: Int = 0,
file: StaticString = #file,
function: StaticString = #function,
line: UInt = #line,
tag: Any? = nil,
asynchronous async: Bool? = nil,
ddlog: DDLog = DDLog.sharedInstance) {
if !condition() {
DDLogError(message(),
level: level,
context: context,
file: file,
function: function,
line: line,
tag: tag,
asynchronous: async,
ddlog: ddlog)
Swift.assertionFailure(message(), file: file, line: line)
}
}
@@ -44,7 +126,24 @@ public func DDAssert(_ condition: @autoclosure () -> Bool, _ message: @autoclosu
* - message: A string to log (using `DDLogError`). The default is an empty string.
*/
@inlinable
public func DDAssertionFailure(_ message: @autoclosure () -> String = "", level: DDLogLevel = DDDefaultLogLevel, context: Int = 0, file: StaticString = #file, function: StaticString = #function, line: UInt = #line, tag: Any? = nil, asynchronous async: Bool = false, ddlog: DDLog = DDLog.sharedInstance) {
DDLogError(message(), level: level, context: context, file: file, function: function, line: line, tag: tag, asynchronous: async, ddlog: ddlog)
@available(*, deprecated, message: "Use an interpolated message.")
public func DDAssertionFailure(_ message: @autoclosure () -> String = "",
level: DDLogLevel = DDDefaultLogLevel,
context: Int = 0,
file: StaticString = #file,
function: StaticString = #function,
line: UInt = #line,
tag: Any? = nil,
asynchronous async: Bool? = nil,
ddlog: DDLog = DDLog.sharedInstance) {
DDLogError(message(),
level: level,
context: context,
file: file,
function: function,
line: line,
tag: tag,
asynchronous: async,
ddlog: ddlog)
Swift.assertionFailure(message(), file: file, line: line)
}

View File

@@ -1,6 +1,6 @@
// Software License Agreement (BSD License)
//
// Copyright (c) 2010-2021, Deusty, LLC
// Copyright (c) 2010-2026, Deusty, LLC
// All rights reserved.
//
// Redistribution and use of this software in source and binary forms,
@@ -15,15 +15,76 @@
#if arch(arm64) || arch(x86_64)
#if canImport(Combine)
import Combine
public import Combine
#if SWIFT_PACKAGE
import CocoaLumberjack
import CocoaLumberjackSwiftSupport
public import CocoaLumberjack
#endif
@available(OSX 10.15, iOS 13.0, tvOS 13.0, watchOS 6.0, *)
@available(macOS 10.15, iOS 13.0, tvOS 13.0, watchOS 6.0, *)
extension DDLog {
// MARK: - Subscription
private final class Subscription<S: Subscriber>: NSObject, DDLogger, Combine.Subscription
where S.Input == DDLogMessage
{ // swiftlint:disable:this opening_brace
private var subscriber: S?
private weak var log: DDLog?
/// Not used but ``DDLogger`` requires it.
/// The preferred way to achieve this is to use the `map` Combine operator of the publisher.
/// Example:
/// ```
/// DDLog.sharedInstance.messagePublisher()
/// .map { message in /* format message */ }
/// .sink(receiveValue: { formattedMessage in /* process formattedMessage */ })
/// ```
var logFormatter: (any DDLogFormatter)?
init(log: DDLog, with logLevel: DDLogLevel, subscriber: S) {
self.subscriber = subscriber
self.log = log
super.init()
log.add(self, with: logLevel)
}
func request(_ demand: Subscribers.Demand) {
// The log messages are endless until canceled, so we won't do any demand management.
// Combine operators can be used to deal with it as needed.
}
func cancel() {
log?.remove(self)
subscriber = nil
}
func log(message logMessage: DDLogMessage) {
_ = subscriber?.receive(logMessage)
}
}
// MARK: - Publisher
@available(macOS 10.15, iOS 13.0, tvOS 13.0, watchOS 6.0, *)
public struct MessagePublisher: Combine.Publisher {
public typealias Output = DDLogMessage
public typealias Failure = Never
private let log: DDLog
private let logLevel: DDLogLevel
public init(log: DDLog, with logLevel: DDLogLevel) {
self.log = log
self.logLevel = logLevel
}
public func receive<S>(subscriber: S)
where S: Subscriber, S.Failure == Failure, S.Input == Output
{ // swiftlint:disable:this opening_brace
let subscription = Subscription(log: log, with: logLevel, subscriber: subscriber)
subscriber.receive(subscription: subscription)
}
}
/**
* Creates a message publisher.
*
@@ -40,73 +101,15 @@ extension DDLog {
* - Returns: A MessagePublisher that emits LogMessages filtered by the specified logLevel
**/
public func messagePublisher(with logLevel: DDLogLevel = .all) -> MessagePublisher {
return MessagePublisher(log: self, with: logLevel)
}
// MARK: - MessagePublisher
@available(OSX 10.15, iOS 13.0, tvOS 13.0, watchOS 6.0, *)
public struct MessagePublisher: Combine.Publisher {
public typealias Output = DDLogMessage
public typealias Failure = Never
private let log: DDLog
private let logLevel: DDLogLevel
public init(log: DDLog, with logLevel: DDLogLevel) {
self.log = log
self.logLevel = logLevel
}
public func receive<S>(subscriber: S) where S: Subscriber, S.Failure == Failure, S.Input == Output {
let subscription = Subscription(log: log, with: logLevel, subscriber: subscriber)
subscriber.receive(subscription: subscription)
}
}
// MARK: - Subscription
private final class Subscription<S: Subscriber>: NSObject, DDLogger, Combine.Subscription where S.Input == DDLogMessage {
private var subscriber: S?
private weak var log: DDLog?
//Not used but DDLogger requires it. The preferred way to achieve this is to use the `map()` Combine operator of the publisher.
//ie:
// DDLog.sharedInstance.messagePublisher()
// .map { [format log message] }
// .sink(receiveValue: { [process log message] })
//
var logFormatter: DDLogFormatter?
init(log: DDLog, with logLevel: DDLogLevel, subscriber: S) {
self.subscriber = subscriber
self.log = log
super.init()
log.add(self, with: logLevel)
}
func request(_ demand: Subscribers.Demand) {
//The log messages are endless until canceled, so we won't do any demand management.
//Combine operators can be used to deal with it as needed.
}
func cancel() {
log?.remove(self)
subscriber = nil
}
func log(message logMessage: DDLogMessage) {
_ = subscriber?.receive(logMessage)
}
MessagePublisher(log: self, with: logLevel)
}
}
@available(OSX 10.15, iOS 13.0, tvOS 13.0, watchOS 6.0, *)
@available(macOS 10.15, iOS 13.0, tvOS 13.0, watchOS 6.0, *)
extension Publisher where Output == DDLogMessage {
public func formatted(with formatter: DDLogFormatter) -> Publishers.CompactMap<Self, String> {
return compactMap { formatter.format(message: $0) }
public func formatted(with formatter: any DDLogFormatter) -> Publishers.CompactMap<Self, String> {
compactMap { formatter.format(message: $0) }
}
}
#endif
#endif

View File

@@ -1,6 +1,6 @@
// Software License Agreement (BSD License)
//
// Copyright (c) 2010-2021, Deusty, LLC
// Copyright (c) 2010-2026, Deusty, LLC
// All rights reserved.
//
// Redistribution and use of this software in source and binary forms,