2019-10-16 20:03:47 +03:00
|
|
|
/*
|
2018-09-12 01:27:47 +03:00
|
|
|
* Copyright (c) Facebook, Inc. and its affiliates.
|
2015-03-23 23:28:42 +03:00
|
|
|
*
|
2018-02-17 05:24:55 +03:00
|
|
|
* This source code is licensed under the MIT license found in the
|
|
|
|
* LICENSE file in the root directory of this source tree.
|
2015-03-23 23:28:42 +03:00
|
|
|
*/
|
2015-02-20 07:10:52 +03:00
|
|
|
|
|
|
|
#import "RCTLog.h"
|
|
|
|
|
2016-11-14 15:49:19 +03:00
|
|
|
#include <cxxabi.h>
|
2015-07-13 19:38:53 +03:00
|
|
|
|
2019-11-05 02:58:49 +03:00
|
|
|
#import <objc/message.h>
|
2020-01-29 02:18:22 +03:00
|
|
|
#import <os/log.h>
|
2019-11-05 02:58:49 +03:00
|
|
|
|
|
|
|
#import "RCTRedBoxSetEnabled.h"
|
2015-04-07 17:36:26 +03:00
|
|
|
#import "RCTAssert.h"
|
2015-12-15 16:39:30 +03:00
|
|
|
#import "RCTBridge+Private.h"
|
2017-01-13 14:37:15 +03:00
|
|
|
#import "RCTBridge.h"
|
2015-04-21 15:26:51 +03:00
|
|
|
#import "RCTDefines.h"
|
2016-03-01 20:44:05 +03:00
|
|
|
#import "RCTUtils.h"
|
2015-02-20 07:10:52 +03:00
|
|
|
|
2015-07-29 15:54:59 +03:00
|
|
|
static NSString *const RCTLogFunctionStack = @"RCTLogFunctionStack";
|
2015-04-07 17:36:26 +03:00
|
|
|
|
|
|
|
const char *RCTLogLevels[] = {
|
2015-11-05 23:20:08 +03:00
|
|
|
"trace",
|
2015-04-07 17:36:26 +03:00
|
|
|
"info",
|
|
|
|
"warn",
|
|
|
|
"error",
|
2015-11-05 23:20:08 +03:00
|
|
|
"fatal",
|
2015-04-07 17:36:26 +03:00
|
|
|
};
|
|
|
|
|
2020-01-29 02:18:22 +03:00
|
|
|
/* os log will discard debug and info messages if they are not needed */
|
2016-11-14 15:49:19 +03:00
|
|
|
static const RCTLogLevel RCTDefaultLogThreshold = (RCTLogLevel)(RCTLogLevelInfo - 1);
|
2015-09-28 14:20:20 +03:00
|
|
|
|
2015-04-07 17:36:26 +03:00
|
|
|
static RCTLogFunction RCTCurrentLogFunction;
|
2015-09-28 14:20:20 +03:00
|
|
|
static RCTLogLevel RCTCurrentLogThreshold = RCTDefaultLogThreshold;
|
2015-04-07 17:36:26 +03:00
|
|
|
|
2015-09-13 21:08:44 +03:00
|
|
|
RCTLogLevel RCTGetLogThreshold()
|
2015-04-07 17:36:26 +03:00
|
|
|
{
|
2015-09-13 21:08:44 +03:00
|
|
|
return RCTCurrentLogThreshold;
|
2015-04-07 17:36:26 +03:00
|
|
|
}
|
|
|
|
|
2015-09-28 14:20:20 +03:00
|
|
|
void RCTSetLogThreshold(RCTLogLevel threshold) {
|
|
|
|
RCTCurrentLogThreshold = threshold;
|
|
|
|
}
|
|
|
|
|
2020-01-29 02:18:22 +03:00
|
|
|
static os_log_type_t RCTLogTypeForLogLevel(RCTLogLevel logLevel)
|
|
|
|
{
|
|
|
|
if (logLevel < RCTLogLevelInfo) {
|
|
|
|
return OS_LOG_TYPE_DEBUG;
|
|
|
|
} else if (logLevel <= RCTLogLevelWarning) {
|
|
|
|
return OS_LOG_TYPE_INFO;
|
|
|
|
} else {
|
|
|
|
return OS_LOG_TYPE_ERROR;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static os_log_t RCTLogForLogSource(RCTLogSource source)
|
|
|
|
{
|
|
|
|
switch (source) {
|
|
|
|
case RCTLogSourceNative: {
|
|
|
|
static os_log_t nativeLog;
|
|
|
|
static dispatch_once_t onceToken;
|
|
|
|
dispatch_once(&onceToken, ^{
|
|
|
|
nativeLog = os_log_create("com.facebook.react.log", "native");
|
|
|
|
});
|
|
|
|
return nativeLog;
|
|
|
|
}
|
|
|
|
case RCTLogSourceJavaScript: {
|
|
|
|
static os_log_t javaScriptLog;
|
|
|
|
static dispatch_once_t onceToken;
|
|
|
|
dispatch_once(&onceToken, ^{
|
|
|
|
javaScriptLog = os_log_create("com.facebook.react.log", "javascript");
|
|
|
|
});
|
|
|
|
return javaScriptLog;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-04-07 17:36:26 +03:00
|
|
|
RCTLogFunction RCTDefaultLogFunction = ^(
|
|
|
|
RCTLogLevel level,
|
2020-01-29 02:18:22 +03:00
|
|
|
RCTLogSource source,
|
|
|
|
__unused NSString *fileName,
|
|
|
|
__unused NSNumber *lineNumber,
|
2015-04-07 17:36:26 +03:00
|
|
|
NSString *message
|
2015-08-20 02:50:39 +03:00
|
|
|
)
|
|
|
|
{
|
2020-01-29 02:18:22 +03:00
|
|
|
os_log_with_type(RCTLogForLogSource(source), RCTLogTypeForLogLevel(level), "%{public}s", message.UTF8String);
|
2015-03-02 02:33:55 +03:00
|
|
|
};
|
2015-02-20 07:10:52 +03:00
|
|
|
|
2015-04-07 17:36:26 +03:00
|
|
|
void RCTSetLogFunction(RCTLogFunction logFunction)
|
|
|
|
{
|
|
|
|
RCTCurrentLogFunction = logFunction;
|
|
|
|
}
|
2015-03-02 02:33:55 +03:00
|
|
|
|
2015-04-07 17:36:26 +03:00
|
|
|
RCTLogFunction RCTGetLogFunction()
|
|
|
|
{
|
2015-09-13 21:08:44 +03:00
|
|
|
if (!RCTCurrentLogFunction) {
|
|
|
|
RCTCurrentLogFunction = RCTDefaultLogFunction;
|
|
|
|
}
|
2015-04-07 17:36:26 +03:00
|
|
|
return RCTCurrentLogFunction;
|
2015-02-20 07:10:52 +03:00
|
|
|
}
|
|
|
|
|
2015-04-07 17:36:26 +03:00
|
|
|
void RCTAddLogFunction(RCTLogFunction logFunction)
|
2015-02-20 07:10:52 +03:00
|
|
|
{
|
2015-09-13 21:08:44 +03:00
|
|
|
RCTLogFunction existing = RCTGetLogFunction();
|
2015-04-07 17:36:26 +03:00
|
|
|
if (existing) {
|
2015-11-11 17:42:27 +03:00
|
|
|
RCTSetLogFunction(^(RCTLogLevel level, RCTLogSource source, NSString *fileName, NSNumber *lineNumber, NSString *message) {
|
|
|
|
existing(level, source, fileName, lineNumber, message);
|
|
|
|
logFunction(level, source, fileName, lineNumber, message);
|
2015-09-13 21:08:44 +03:00
|
|
|
});
|
2015-04-07 17:36:26 +03:00
|
|
|
} else {
|
2015-09-13 21:08:44 +03:00
|
|
|
RCTSetLogFunction(logFunction);
|
2015-02-20 07:10:52 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-07-29 15:54:59 +03:00
|
|
|
/**
|
|
|
|
* returns the topmost stacked log function for the current thread, which
|
|
|
|
* may not be the same as the current value of RCTCurrentLogFunction.
|
|
|
|
*/
|
|
|
|
static RCTLogFunction RCTGetLocalLogFunction()
|
|
|
|
{
|
|
|
|
NSMutableDictionary *threadDictionary = [NSThread currentThread].threadDictionary;
|
2015-11-04 01:45:46 +03:00
|
|
|
NSArray<RCTLogFunction> *functionStack = threadDictionary[RCTLogFunctionStack];
|
2015-08-24 13:14:33 +03:00
|
|
|
RCTLogFunction logFunction = functionStack.lastObject;
|
2015-07-29 15:54:59 +03:00
|
|
|
if (logFunction) {
|
|
|
|
return logFunction;
|
|
|
|
}
|
2015-09-13 21:08:44 +03:00
|
|
|
return RCTGetLogFunction();
|
2015-07-29 15:54:59 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
void RCTPerformBlockWithLogFunction(void (^block)(void), RCTLogFunction logFunction)
|
2015-02-20 07:10:52 +03:00
|
|
|
{
|
2015-04-07 17:36:26 +03:00
|
|
|
NSMutableDictionary *threadDictionary = [NSThread currentThread].threadDictionary;
|
2015-11-04 01:45:46 +03:00
|
|
|
NSMutableArray<RCTLogFunction> *functionStack = threadDictionary[RCTLogFunctionStack];
|
2015-07-29 15:54:59 +03:00
|
|
|
if (!functionStack) {
|
2015-08-17 17:35:34 +03:00
|
|
|
functionStack = [NSMutableArray new];
|
2015-07-29 15:54:59 +03:00
|
|
|
threadDictionary[RCTLogFunctionStack] = functionStack;
|
2015-04-07 17:36:26 +03:00
|
|
|
}
|
2015-07-29 15:54:59 +03:00
|
|
|
[functionStack addObject:logFunction];
|
2015-04-07 17:36:26 +03:00
|
|
|
block();
|
2015-07-29 15:54:59 +03:00
|
|
|
[functionStack removeLastObject];
|
|
|
|
}
|
|
|
|
|
|
|
|
void RCTPerformBlockWithLogPrefix(void (^block)(void), NSString *prefix)
|
|
|
|
{
|
|
|
|
RCTLogFunction logFunction = RCTGetLocalLogFunction();
|
|
|
|
if (logFunction) {
|
2015-11-11 17:42:27 +03:00
|
|
|
RCTPerformBlockWithLogFunction(block, ^(RCTLogLevel level, RCTLogSource source,
|
|
|
|
NSString *fileName, NSNumber *lineNumber,
|
|
|
|
NSString *message) {
|
|
|
|
logFunction(level, source, fileName, lineNumber, [prefix stringByAppendingString:message]);
|
2015-07-29 15:54:59 +03:00
|
|
|
});
|
|
|
|
}
|
2015-04-07 17:36:26 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
NSString *RCTFormatLog(
|
|
|
|
NSDate *timestamp,
|
|
|
|
RCTLogLevel level,
|
|
|
|
NSString *fileName,
|
|
|
|
NSNumber *lineNumber,
|
|
|
|
NSString *message
|
2015-08-20 02:50:39 +03:00
|
|
|
)
|
|
|
|
{
|
2015-08-17 17:35:34 +03:00
|
|
|
NSMutableString *log = [NSMutableString new];
|
2015-04-07 17:36:26 +03:00
|
|
|
if (timestamp) {
|
|
|
|
static NSDateFormatter *formatter;
|
|
|
|
static dispatch_once_t onceToken;
|
|
|
|
dispatch_once(&onceToken, ^{
|
2015-08-17 17:35:34 +03:00
|
|
|
formatter = [NSDateFormatter new];
|
2015-04-07 17:36:26 +03:00
|
|
|
formatter.dateFormat = formatter.dateFormat = @"yyyy-MM-dd HH:mm:ss.SSS ";
|
|
|
|
});
|
|
|
|
[log appendString:[formatter stringFromDate:timestamp]];
|
|
|
|
}
|
|
|
|
if (level) {
|
2016-01-20 21:27:34 +03:00
|
|
|
[log appendFormat:@"[%s]", RCTLogLevels[level]];
|
2015-04-07 17:36:26 +03:00
|
|
|
}
|
2015-05-25 15:19:53 +03:00
|
|
|
|
|
|
|
[log appendFormat:@"[tid:%@]", RCTCurrentThreadName()];
|
|
|
|
|
2015-04-07 17:36:26 +03:00
|
|
|
if (fileName) {
|
2015-08-24 13:14:33 +03:00
|
|
|
fileName = fileName.lastPathComponent;
|
2015-04-07 17:36:26 +03:00
|
|
|
if (lineNumber) {
|
|
|
|
[log appendFormat:@"[%@:%@]", fileName, lineNumber];
|
2015-02-20 07:10:52 +03:00
|
|
|
} else {
|
2015-04-07 17:36:26 +03:00
|
|
|
[log appendFormat:@"[%@]", fileName];
|
2015-02-20 07:10:52 +03:00
|
|
|
}
|
|
|
|
}
|
2015-04-07 17:36:26 +03:00
|
|
|
if (message) {
|
|
|
|
[log appendString:@" "];
|
|
|
|
[log appendString:message];
|
|
|
|
}
|
|
|
|
return log;
|
2015-02-20 07:10:52 +03:00
|
|
|
}
|
|
|
|
|
2019-10-07 21:42:14 +03:00
|
|
|
NSString *RCTFormatLogLevel(RCTLogLevel level)
|
|
|
|
{
|
|
|
|
NSDictionary *levelsToString = @{@(RCTLogLevelTrace) : @"trace",
|
|
|
|
@(RCTLogLevelInfo) : @"info",
|
|
|
|
@(RCTLogLevelWarning) : @"warning",
|
|
|
|
@(RCTLogLevelFatal) : @"fatal",
|
|
|
|
@(RCTLogLevelError) : @"error"};
|
2019-11-05 02:58:49 +03:00
|
|
|
|
2019-10-07 21:42:14 +03:00
|
|
|
return levelsToString[@(level)];
|
|
|
|
}
|
|
|
|
|
|
|
|
NSString *RCTFormatLogSource(RCTLogSource source)
|
|
|
|
{
|
|
|
|
NSDictionary *sourcesToString = @{@(RCTLogSourceNative) : @"native",
|
|
|
|
@(RCTLogSourceJavaScript) : @"js"};
|
2019-11-05 02:58:49 +03:00
|
|
|
|
2019-10-07 21:42:14 +03:00
|
|
|
return sourcesToString[@(source)];
|
|
|
|
}
|
|
|
|
|
2016-11-14 15:49:19 +03:00
|
|
|
static NSRegularExpression *nativeStackFrameRegex()
|
|
|
|
{
|
|
|
|
static dispatch_once_t onceToken;
|
|
|
|
static NSRegularExpression *_regex;
|
|
|
|
dispatch_once(&onceToken, ^{
|
|
|
|
NSError *regexError;
|
|
|
|
_regex = [NSRegularExpression regularExpressionWithPattern:@"0x[0-9a-f]+ (.*) \\+ (\\d+)$" options:0 error:®exError];
|
|
|
|
if (regexError) {
|
|
|
|
RCTLogError(@"Failed to build regex: %@", [regexError localizedDescription]);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
return _regex;
|
|
|
|
}
|
|
|
|
|
2015-11-11 17:42:27 +03:00
|
|
|
void _RCTLogNativeInternal(RCTLogLevel level, const char *fileName, int lineNumber, NSString *format, ...)
|
2015-08-20 02:50:39 +03:00
|
|
|
{
|
2015-07-29 15:54:59 +03:00
|
|
|
RCTLogFunction logFunction = RCTGetLocalLogFunction();
|
|
|
|
BOOL log = RCT_DEBUG || (logFunction != nil);
|
2015-09-13 21:08:44 +03:00
|
|
|
if (log && level >= RCTGetLogThreshold()) {
|
2015-04-07 17:36:26 +03:00
|
|
|
// Get message
|
|
|
|
va_list args;
|
|
|
|
va_start(args, format);
|
2015-04-12 01:08:00 +03:00
|
|
|
NSString *message = [[NSString alloc] initWithFormat:format arguments:args];
|
2015-04-07 17:36:26 +03:00
|
|
|
va_end(args);
|
|
|
|
|
|
|
|
// Call log function
|
2015-07-29 15:54:59 +03:00
|
|
|
if (logFunction) {
|
2015-11-11 17:42:27 +03:00
|
|
|
logFunction(level, RCTLogSourceNative, fileName ? @(fileName) : nil, lineNumber > 0 ? @(lineNumber) : nil, message);
|
2015-07-29 15:54:59 +03:00
|
|
|
}
|
2015-08-20 02:50:39 +03:00
|
|
|
|
2019-10-03 00:40:57 +03:00
|
|
|
// Log to red box if one is configured.
|
2019-10-08 17:36:35 +03:00
|
|
|
if (RCTSharedApplication() && RCTRedBoxGetEnabled() && level >= RCTLOG_REDBOX_LEVEL) {
|
2018-05-15 06:34:35 +03:00
|
|
|
NSArray<NSString *> *stackSymbols = [NSThread callStackSymbols];
|
|
|
|
NSMutableArray<NSDictionary *> *stack =
|
|
|
|
[NSMutableArray arrayWithCapacity:(stackSymbols.count - 1)];
|
|
|
|
[stackSymbols enumerateObjectsUsingBlock:^(NSString *frameSymbols, NSUInteger idx, __unused BOOL *stop) {
|
|
|
|
if (idx == 0) {
|
|
|
|
// don't include the current frame
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
NSRange range = NSMakeRange(0, frameSymbols.length);
|
|
|
|
NSTextCheckingResult *match = [nativeStackFrameRegex() firstMatchInString:frameSymbols options:0 range:range];
|
|
|
|
if (!match) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
NSString *methodName = [frameSymbols substringWithRange:[match rangeAtIndex:1]];
|
|
|
|
char *demangledName = abi::__cxa_demangle([methodName UTF8String], NULL, NULL, NULL);
|
|
|
|
if (demangledName) {
|
|
|
|
methodName = @(demangledName);
|
|
|
|
free(demangledName);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (idx == 1 && fileName) {
|
|
|
|
NSString *file = [@(fileName) componentsSeparatedByString:@"/"].lastObject;
|
|
|
|
[stack addObject:@{@"methodName": methodName, @"file": file, @"lineNumber": @(lineNumber)}];
|
|
|
|
} else {
|
|
|
|
[stack addObject:@{@"methodName": methodName}];
|
|
|
|
}
|
|
|
|
}];
|
2016-11-14 15:49:19 +03:00
|
|
|
|
2015-08-26 19:28:14 +03:00
|
|
|
dispatch_async(dispatch_get_main_queue(), ^{
|
|
|
|
// red box is thread safe, but by deferring to main queue we avoid a startup
|
|
|
|
// race condition that causes the module to be accessed before it has loaded
|
2019-11-05 02:58:49 +03:00
|
|
|
id redbox = [[RCTBridge currentBridge] moduleForName:@"RedBox" lazilyLoadIfNecessary:YES];
|
|
|
|
if (redbox) {
|
|
|
|
void (*showErrorMessage)(id, SEL, NSString *, NSMutableArray<NSDictionary *> *) = (__typeof__(showErrorMessage))objc_msgSend;
|
|
|
|
SEL showErrorMessageSEL = NSSelectorFromString(@"showErrorMessage:withStack:");
|
|
|
|
|
|
|
|
if ([redbox respondsToSelector:showErrorMessageSEL]) {
|
|
|
|
showErrorMessage(redbox, showErrorMessageSEL, message, stack);
|
|
|
|
}
|
|
|
|
}
|
2015-08-26 19:28:14 +03:00
|
|
|
});
|
2015-08-20 02:50:39 +03:00
|
|
|
}
|
2019-11-05 02:58:49 +03:00
|
|
|
|
2019-10-03 00:40:57 +03:00
|
|
|
#if RCT_DEBUG
|
2016-03-01 20:44:05 +03:00
|
|
|
if (!RCTRunningInTestEnvironment()) {
|
|
|
|
// Log to JS executor
|
|
|
|
[[RCTBridge currentBridge] logMessage:message level:level ? @(RCTLogLevels[level]) : @"info"];
|
|
|
|
}
|
2015-08-20 02:50:39 +03:00
|
|
|
#endif
|
2015-12-08 14:29:08 +03:00
|
|
|
|
2015-02-20 07:10:52 +03:00
|
|
|
}
|
2015-04-07 17:36:26 +03:00
|
|
|
}
|
2015-11-11 17:42:27 +03:00
|
|
|
|
|
|
|
void _RCTLogJavaScriptInternal(RCTLogLevel level, NSString *message)
|
|
|
|
{
|
|
|
|
RCTLogFunction logFunction = RCTGetLocalLogFunction();
|
|
|
|
BOOL log = RCT_DEBUG || (logFunction != nil);
|
|
|
|
if (log && level >= RCTGetLogThreshold()) {
|
|
|
|
if (logFunction) {
|
|
|
|
logFunction(level, RCTLogSourceJavaScript, nil, nil, message);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|