Refactored RCTLog and added facility to prepend extra data to the log message
This commit is contained in:
parent
5b3e935332
commit
80cd687e95
|
@ -1,5 +1,9 @@
|
|||
{
|
||||
"images" : [
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "1x"
|
||||
},
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"scale" : "2x",
|
||||
|
|
|
@ -71,7 +71,7 @@
|
|||
|
||||
UIView *view = viewRegistry[reactTag];
|
||||
if (!view) {
|
||||
RCTLogWarn(@"React tag %@ is not registered with the view registry", reactTag);
|
||||
RCTLogWarn(@"React tag #%@ is not registered with the view registry", reactTag);
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
@ -119,7 +119,9 @@ typedef void (^WSMessageCallback)(NSError *error, NSDictionary *reply);
|
|||
|
||||
[_jsQueue addOperationWithBlock:^{
|
||||
if (!self.valid) {
|
||||
NSError *error = [NSError errorWithDomain:@"WS" code:1 userInfo:@{NSLocalizedDescriptionKey:@"socket closed"}];
|
||||
NSError *error = [NSError errorWithDomain:@"WS" code:1 userInfo:@{
|
||||
NSLocalizedDescriptionKey: @"socket closed"
|
||||
}];
|
||||
callback(error, nil);
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -28,6 +28,11 @@ typedef NSArray *(^RCTBridgeModuleProviderBlock)(void);
|
|||
|
||||
extern NSString *const RCTReloadBridge;
|
||||
|
||||
/**
|
||||
* This function returns the module name for a given class.
|
||||
*/
|
||||
extern NSString *RCTBridgeModuleNameForClass(Class bridgeModuleClass);
|
||||
|
||||
/**
|
||||
* Async batched bridge used to communicate with the JavaScript application.
|
||||
*/
|
||||
|
@ -81,14 +86,6 @@ extern NSString *const RCTReloadBridge;
|
|||
*/
|
||||
@property (nonatomic, readonly) dispatch_queue_t shadowQueue;
|
||||
|
||||
/**
|
||||
* Global logging function that will print to both xcode and JS debugger consoles.
|
||||
*
|
||||
* NOTE: Use via RCTLog* macros defined in RCTLog.h
|
||||
* TODO (#5906496): should log function be exposed here, or could it be a module?
|
||||
*/
|
||||
+ (void)log:(NSArray *)objects level:(NSString *)level;
|
||||
|
||||
@property (nonatomic, copy, readonly) NSDictionary *launchOptions;
|
||||
|
||||
|
||||
|
|
|
@ -22,6 +22,7 @@
|
|||
#import "RCTJavaScriptLoader.h"
|
||||
#import "RCTKeyCommands.h"
|
||||
#import "RCTLog.h"
|
||||
#import "RCTRedBox.h"
|
||||
#import "RCTRootView.h"
|
||||
#import "RCTSparseArray.h"
|
||||
#import "RCTUtils.h"
|
||||
|
@ -41,10 +42,7 @@ typedef NS_ENUM(NSUInteger, RCTBridgeFields) {
|
|||
|
||||
NSString *const RCTReloadBridge = @"RCTReloadBridge";
|
||||
|
||||
/**
|
||||
* This function returns the module name for a given class.
|
||||
*/
|
||||
static NSString *RCTModuleNameForClass(Class cls)
|
||||
NSString *RCTBridgeModuleNameForClass(Class cls)
|
||||
{
|
||||
return [cls respondsToSelector:@selector(moduleName)] ? [cls moduleName] : NSStringFromClass(cls);
|
||||
}
|
||||
|
@ -92,7 +90,7 @@ static NSArray *RCTBridgeModuleClassesByModuleID(void)
|
|||
[(NSMutableArray *)modules addObject:cls];
|
||||
|
||||
// Add module name
|
||||
NSString *moduleName = RCTModuleNameForClass(cls);
|
||||
NSString *moduleName = RCTBridgeModuleNameForClass(cls);
|
||||
[(NSMutableArray *)RCTModuleNamesByID addObject:moduleName];
|
||||
}
|
||||
});
|
||||
|
@ -187,7 +185,7 @@ static Class _globalExecutorClass;
|
|||
RCT_ARG_BLOCK( \
|
||||
if (json && ![json isKindOfClass:[_class class]]) { \
|
||||
RCTLogError(@"Argument %tu (%@) of %@.%@ should be of type %@", index, \
|
||||
json, RCTModuleNameForClass(_moduleClass), _JSMethodName, [_class class]); \
|
||||
json, RCTBridgeModuleNameForClass(_moduleClass), _JSMethodName, [_class class]); \
|
||||
return; \
|
||||
} \
|
||||
_logic \
|
||||
|
@ -203,7 +201,7 @@ static Class _globalExecutorClass;
|
|||
RCT_ARG_BLOCK( \
|
||||
if (json && ![json respondsToSelector:@selector(_selector)]) { \
|
||||
RCTLogError(@"Argument %tu (%@) of %@.%@ does not respond to selector: %@", \
|
||||
index, json, RCTModuleNameForClass(_moduleClass), _JSMethodName, @#_selector); \
|
||||
index, json, RCTBridgeModuleNameForClass(_moduleClass), _JSMethodName, @#_selector); \
|
||||
return; \
|
||||
} \
|
||||
_type value = [json _selector]; \
|
||||
|
@ -231,7 +229,7 @@ static Class _globalExecutorClass;
|
|||
RCT_ARG_BLOCK(
|
||||
if (json && ![json isKindOfClass:[NSNumber class]]) {
|
||||
RCTLogError(@"Argument %tu (%@) of %@.%@ should be a number", index,
|
||||
json, RCTModuleNameForClass(_moduleClass), _JSMethodName);
|
||||
json, RCTBridgeModuleNameForClass(_moduleClass), _JSMethodName);
|
||||
return;
|
||||
}
|
||||
// Marked as autoreleasing, because NSInvocation doesn't retain arguments
|
||||
|
@ -268,7 +266,7 @@ static Class _globalExecutorClass;
|
|||
// Safety check
|
||||
if (arguments.count != _argumentBlocks.count) {
|
||||
RCTLogError(@"%@.%@ was called with %zd arguments, but expects %zd",
|
||||
RCTModuleNameForClass(_moduleClass), _JSMethodName,
|
||||
RCTBridgeModuleNameForClass(_moduleClass), _JSMethodName,
|
||||
arguments.count, _argumentBlocks.count);
|
||||
return;
|
||||
}
|
||||
|
@ -544,7 +542,7 @@ static id<RCTJavaScriptExecutor> _latestJSExecutor;
|
|||
// Register passed-in module instances
|
||||
NSMutableDictionary *preregisteredModules = [[NSMutableDictionary alloc] init];
|
||||
for (id<RCTBridgeModule> module in _moduleProvider ? _moduleProvider() : nil) {
|
||||
preregisteredModules[RCTModuleNameForClass([module class])] = module;
|
||||
preregisteredModules[RCTBridgeModuleNameForClass([module class])] = module;
|
||||
}
|
||||
|
||||
// Instantiate modules
|
||||
|
@ -895,27 +893,18 @@ static id<RCTJavaScriptExecutor> _latestJSExecutor;
|
|||
return (_latestJSExecutor != nil && [_latestJSExecutor isValid]);
|
||||
}
|
||||
|
||||
+ (void)log:(NSArray *)objects level:(NSString *)level
|
||||
+ (void)logMessage:(NSString *)message level:(NSString *)level
|
||||
{
|
||||
if (!_latestJSExecutor || ![_latestJSExecutor isValid]) {
|
||||
RCTLogError(@"ERROR: No valid JS executor to log %@.", objects);
|
||||
RCTLogError(@"ERROR: No valid JS executor to log '%@'.", message);
|
||||
return;
|
||||
}
|
||||
NSMutableArray *args = [NSMutableArray arrayWithObject:level];
|
||||
|
||||
// TODO (#5906496): Find out and document why we skip the first object
|
||||
for (id ob in [objects subarrayWithRange:(NSRange){1, [objects count] - 1}]) {
|
||||
if ([NSJSONSerialization isValidJSONObject:@[ob]]) {
|
||||
[args addObject:ob];
|
||||
} else {
|
||||
[args addObject:[ob description]];
|
||||
}
|
||||
}
|
||||
|
||||
// Note: the js executor could get invalidated while we're trying to call this...need to watch out for that.
|
||||
// Note: the js executor could get invalidated while we're trying to call
|
||||
// this...need to watch out for that.
|
||||
[_latestJSExecutor executeJSCall:@"RCTLog"
|
||||
method:@"logIfNoNativeHook"
|
||||
arguments:args
|
||||
arguments:@[level, message]
|
||||
callback:^(id json, NSError *error) {}];
|
||||
}
|
||||
|
||||
|
|
|
@ -145,7 +145,7 @@ RCT_CUSTOM_CONVERTER(type, name, [json getter])
|
|||
} \
|
||||
@catch (__unused NSException *e) { \
|
||||
RCTLogError(@"JSON value '%@' of type '%@' cannot be converted to '%s'", \
|
||||
json, [json class], #type); \
|
||||
json, [json classForCoder], #type); \
|
||||
json = nil; \
|
||||
return code; \
|
||||
} \
|
||||
|
@ -181,7 +181,8 @@ RCT_CUSTOM_CONVERTER(type, type, [[self NSNumber:json] getter])
|
|||
return default; \
|
||||
} \
|
||||
if (![json isKindOfClass:[NSString class]]) { \
|
||||
RCTLogError(@"Expected NSNumber or NSString for %s, received %@: %@", #type, [json class], json); \
|
||||
RCTLogError(@"Expected NSNumber or NSString for %s, received %@: %@", \
|
||||
#type, [json classForCoder], json); \
|
||||
} \
|
||||
id value = mapping[json]; \
|
||||
if(!value && [json description].length > 0) { \
|
||||
|
|
|
@ -45,7 +45,7 @@ RCT_CONVERTER(NSString *, NSString, description)
|
|||
}
|
||||
return number;
|
||||
} else if (json && json != [NSNull null]) {
|
||||
RCTLogError(@"JSON value '%@' of class %@ could not be interpreted as a number", json, [json class]);
|
||||
RCTLogError(@"JSON value '%@' of class %@ could not be interpreted as a number", json, [json classForCoder]);
|
||||
}
|
||||
return nil;
|
||||
}
|
||||
|
@ -53,7 +53,7 @@ RCT_CONVERTER(NSString *, NSString, description)
|
|||
+ (NSURL *)NSURL:(id)json
|
||||
{
|
||||
if (![json isKindOfClass:[NSString class]]) {
|
||||
RCTLogError(@"Expected NSString for NSURL, received %@: %@", [json class], json);
|
||||
RCTLogError(@"Expected NSString for NSURL, received %@: %@", [json classForCoder], json);
|
||||
return nil;
|
||||
}
|
||||
|
||||
|
@ -98,7 +98,7 @@ RCT_CONVERTER(NSString *, NSString, description)
|
|||
}
|
||||
return date;
|
||||
} else if (json && json != [NSNull null]) {
|
||||
RCTLogError(@"JSON value '%@' of class %@ could not be interpreted as a date", json, [json class]);
|
||||
RCTLogError(@"JSON value '%@' of class %@ could not be interpreted as a date", json, [json classForCoder]);
|
||||
}
|
||||
return nil;
|
||||
}
|
||||
|
@ -234,7 +234,8 @@ RCT_ENUM_CONVERTER(UIBarStyle, (@{
|
|||
((CGFloat *)&result)[i] = [self CGFloat:json[fields[i]]]; \
|
||||
} \
|
||||
} else if (json && json != [NSNull null]) { \
|
||||
RCTLogError(@"Expected NSArray or NSDictionary for %s, received %@: %@", #type, [json class], json); \
|
||||
RCTLogError(@"Expected NSArray or NSDictionary for %s, received %@: %@", \
|
||||
#type, [json classForCoder], json); \
|
||||
} \
|
||||
return result; \
|
||||
} \
|
||||
|
@ -511,8 +512,8 @@ RCT_CGSTRUCT_CONVERTER(CGAffineTransform, (@[
|
|||
|
||||
} else if (json && ![json isKindOfClass:[NSNull class]]) {
|
||||
|
||||
RCTLogError(@"Expected NSArray, NSDictionary or NSString for UIColor, \
|
||||
received %@: %@", [json class], json);
|
||||
RCTLogError(@"Expected NSArray, NSDictionary or NSString for UIColor, received %@: %@",
|
||||
[json classForCoder], json);
|
||||
}
|
||||
|
||||
// Default color
|
||||
|
@ -538,7 +539,7 @@ RCT_CGSTRUCT_CONVERTER(CGAffineTransform, (@[
|
|||
// image itself) so as to reduce overhead on subsequent checks of the same input
|
||||
|
||||
if (![json isKindOfClass:[NSString class]]) {
|
||||
RCTLogError(@"Expected NSString for UIImage, received %@: %@", [json class], json);
|
||||
RCTLogError(@"Expected NSString for UIImage, received %@: %@", [json classForCoder], json);
|
||||
return nil;
|
||||
}
|
||||
|
||||
|
|
|
@ -7,58 +7,138 @@
|
|||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*/
|
||||
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
#import "RCTAssert.h"
|
||||
#import "RCTRedBox.h"
|
||||
|
||||
#define RCTLOG_INFO 1
|
||||
#define RCTLOG_WARN 2
|
||||
#define RCTLOG_ERROR 3
|
||||
#define RCTLOG_MUSTFIX 4
|
||||
|
||||
// If set to e.g. `RCTLOG_ERROR`, will assert after logging the first error.
|
||||
#if DEBUG
|
||||
#define RCTLOG_FATAL_LEVEL RCTLOG_MUSTFIX
|
||||
#define RCTLOG_REDBOX_LEVEL RCTLOG_ERROR
|
||||
#else
|
||||
#define RCTLOG_FATAL_LEVEL (RCTLOG_MUSTFIX + 1)
|
||||
#define RCTLOG_REDBOX_LEVEL (RCTLOG_MUSTFIX + 1)
|
||||
#endif
|
||||
|
||||
// If defined, only log messages that match this regex will fatal
|
||||
#define RCTLOG_FATAL_REGEX nil
|
||||
|
||||
extern __unsafe_unretained NSString *RCTLogLevels[];
|
||||
|
||||
#define _RCTLog(_level, ...) do { \
|
||||
NSString *__RCTLog__levelStr = RCTLogLevels[_level - 1]; \
|
||||
NSString *__RCTLog__msg = RCTLogObjects(RCTLogFormat(__FILE__, __LINE__, __PRETTY_FUNCTION__, __VA_ARGS__), __RCTLog__levelStr); \
|
||||
if (_level >= RCTLOG_FATAL_LEVEL) { \
|
||||
BOOL __RCTLog__fail = YES; \
|
||||
if (RCTLOG_FATAL_REGEX) { \
|
||||
NSRegularExpression *__RCTLog__regex = [NSRegularExpression regularExpressionWithPattern:RCTLOG_FATAL_REGEX options:0 error:NULL]; \
|
||||
__RCTLog__fail = [__RCTLog__regex numberOfMatchesInString:__RCTLog__msg options:0 range:NSMakeRange(0, [__RCTLog__msg length])] > 0; \
|
||||
} \
|
||||
RCTCAssert(!__RCTLog__fail, @"RCTLOG_FATAL_LEVEL %@: %@", __RCTLog__levelStr, __RCTLog__msg); \
|
||||
} \
|
||||
if (_level >= RCTLOG_REDBOX_LEVEL) { \
|
||||
[[RCTRedBox sharedInstance] showErrorMessage:__RCTLog__msg]; \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#define RCTLog(...) _RCTLog(RCTLOG_INFO, __VA_ARGS__)
|
||||
#define RCTLogInfo(...) _RCTLog(RCTLOG_INFO, __VA_ARGS__)
|
||||
#define RCTLogWarn(...) _RCTLog(RCTLOG_WARN, __VA_ARGS__)
|
||||
#define RCTLogError(...) _RCTLog(RCTLOG_ERROR, __VA_ARGS__)
|
||||
#define RCTLogMustFix(...) _RCTLog(RCTLOG_MUSTFIX, __VA_ARGS__)
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
NSString *RCTLogObjects(NSArray *objects, NSString *level);
|
||||
NSArray *RCTLogFormat(const char *file, int lineNumber, const char *funcName, NSString *format, ...) NS_FORMAT_FUNCTION(4,5);
|
||||
/**
|
||||
* Thresholds for logs to raise an assertion, or display redbox, respectively.
|
||||
* You can override these values when debugging in order to tweak the default
|
||||
* logging behavior.
|
||||
*/
|
||||
#define RCTLOG_FATAL_LEVEL RCTLogLevelMustFix
|
||||
#define RCTLOG_REDBOX_LEVEL RCTLogLevelError
|
||||
|
||||
void RCTInjectLogFunction(void (^logFunction)(NSString *msg));
|
||||
/**
|
||||
* A regular expression that can be used to selectively limit the throwing of
|
||||
* a exception to specific log contents.
|
||||
*/
|
||||
#define RCTLOG_FATAL_REGEX nil
|
||||
|
||||
/**
|
||||
* An enum representing the severity of the log message.
|
||||
*/
|
||||
typedef NS_ENUM(NSInteger, RCTLogLevel) {
|
||||
RCTLogLevelInfo = 1,
|
||||
RCTLogLevelWarning = 2,
|
||||
RCTLogLevelError = 3,
|
||||
RCTLogLevelMustFix = 4
|
||||
};
|
||||
|
||||
/**
|
||||
* A block signature to be used for custom logging functions. In most cases you
|
||||
* will want to pass these arguments to the RCTFormatLog function in order to
|
||||
* generate a string, or use the RCTSimpleLogFunction() constructor to register
|
||||
* a simple function that does not use all of the arguments.
|
||||
*/
|
||||
typedef void (^RCTLogFunction)(
|
||||
RCTLogLevel level,
|
||||
NSString *fileName,
|
||||
NSNumber *lineNumber,
|
||||
NSString *message
|
||||
);
|
||||
|
||||
/**
|
||||
* A method to generate a string from a collection of log data. To omit any
|
||||
* particular data from the log, just pass nil or zero for the argument.
|
||||
*/
|
||||
NSString *RCTFormatLog(
|
||||
NSDate *timestamp,
|
||||
NSThread *thread,
|
||||
RCTLogLevel level,
|
||||
NSString *fileName,
|
||||
NSNumber *lineNumber,
|
||||
NSString *message
|
||||
);
|
||||
|
||||
/**
|
||||
* A method to generate a log function from a block with a much simpler
|
||||
* template. The message passed to the simpler block is equivalent to the
|
||||
* output of the RCTFormatLog() function.
|
||||
*/
|
||||
RCTLogFunction RCTSimpleLogFunction(void (^logFunction)(RCTLogLevel level, NSString *message));
|
||||
|
||||
/**
|
||||
* The default logging function used by RCTLogXX.
|
||||
*/
|
||||
extern RCTLogFunction RCTDefaultLogFunction;
|
||||
|
||||
/**
|
||||
* These methods get and set the current logging threshold. This is the level
|
||||
* below which logs will be ignored. Default is RCTLogLevelInfo for debug and
|
||||
* RCTLogLevelError for production.
|
||||
*/
|
||||
void RCTSetLogThreshold(RCTLogLevel threshold);
|
||||
RCTLogLevel RCTGetLogThreshold(void);
|
||||
|
||||
/**
|
||||
* These methods get and set the current logging function called by the RCTLogXX
|
||||
* macros. You can use these to replace the standard behavior with custom log
|
||||
* functionality.
|
||||
*/
|
||||
void RCTSetLogFunction(RCTLogFunction logFunction);
|
||||
RCTLogFunction RCTGetLogFunction(void);
|
||||
|
||||
/**
|
||||
* This appends additional code to the existing log function, without replacing
|
||||
* the existing functionality. Useful if you just want to forward logs to an
|
||||
* extra service without changing the default behavior.
|
||||
*/
|
||||
void RCTAddLogFunction(RCTLogFunction logFunction);
|
||||
|
||||
/**
|
||||
* This method adds a conditional prefix to any messages logged within the scope
|
||||
* of the passed block. This is useful for adding additional context to log
|
||||
* messages. The block will be performed synchronously on the current thread.
|
||||
*/
|
||||
void RCTPerformBlockWithLogPrefix(void (^block)(void), NSString *prefix);
|
||||
|
||||
/**
|
||||
* Private logging functions - ignore these.
|
||||
*/
|
||||
void _RCTLogFormat(RCTLogLevel, const char *, int, NSString *, ...) NS_FORMAT_FUNCTION(4,5);
|
||||
#define _RCTLog(lvl, ...) do { \
|
||||
NSString *msg = [NSString stringWithFormat:__VA_ARGS__]; \
|
||||
if (lvl >= RCTLOG_FATAL_LEVEL) { \
|
||||
BOOL fail = YES; \
|
||||
if (RCTLOG_FATAL_REGEX) { \
|
||||
if ([msg rangeOfString:RCTLOG_FATAL_REGEX options:NSRegularExpressionSearch].length) { \
|
||||
fail = NO; \
|
||||
} \
|
||||
} \
|
||||
RCTCAssert(!fail, @"FATAL ERROR: %@", msg); \
|
||||
}\
|
||||
_RCTLogFormat(lvl, __FILE__, __LINE__, __VA_ARGS__); \
|
||||
} while (0)
|
||||
|
||||
/**
|
||||
* Legacy injection function - don't use this.
|
||||
*/
|
||||
void RCTInjectLogFunction(void (^)(NSString *msg));
|
||||
|
||||
/**
|
||||
* Logging macros. Use these to log information, warnings and errors in your
|
||||
* own code.
|
||||
*/
|
||||
#define RCTLog(...) _RCTLog(RCTLogLevelInfo, __VA_ARGS__)
|
||||
#define RCTLogInfo(...) _RCTLog(RCTLogLevelInfo, __VA_ARGS__)
|
||||
#define RCTLogWarn(...) _RCTLog(RCTLogLevelWarning, __VA_ARGS__)
|
||||
#define RCTLogError(...) _RCTLog(RCTLogLevelError, __VA_ARGS__)
|
||||
#define RCTLogMustFix(...) _RCTLog(RCTLogLevelMustFix, __VA_ARGS__)
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
|
|
|
@ -9,85 +9,218 @@
|
|||
|
||||
#import "RCTLog.h"
|
||||
|
||||
#import "RCTAssert.h"
|
||||
#import "RCTBridge.h"
|
||||
#import "RCTRedBox.h"
|
||||
|
||||
__unsafe_unretained NSString *RCTLogLevels[] = {
|
||||
@"info",
|
||||
@"warn",
|
||||
@"error",
|
||||
@"mustfix"
|
||||
@interface RCTBridge (Logging)
|
||||
|
||||
+ (void)logMessage:(NSString *)message level:(NSString *)level;
|
||||
|
||||
@end
|
||||
|
||||
static NSString *const RCTLogPrefixStack = @"RCTLogPrefixStack";
|
||||
|
||||
const char *RCTLogLevels[] = {
|
||||
"info",
|
||||
"warn",
|
||||
"error",
|
||||
"mustfix"
|
||||
};
|
||||
|
||||
static void (^RCTInjectedLogFunction)(NSString *msg);
|
||||
static RCTLogFunction RCTCurrentLogFunction;
|
||||
static RCTLogLevel RCTCurrentLogThreshold;
|
||||
|
||||
void RCTInjectLogFunction(void (^logFunction)(NSString *msg)) {
|
||||
RCTInjectedLogFunction = logFunction;
|
||||
}
|
||||
|
||||
static inline NSString *_RCTLogPreamble(const char *file, int lineNumber, const char *funcName)
|
||||
void RCTLogSetup(void) __attribute__((constructor));
|
||||
void RCTLogSetup()
|
||||
{
|
||||
NSString *threadName = [[NSThread currentThread] name];
|
||||
NSString *fileName=[[NSString stringWithUTF8String:file] lastPathComponent];
|
||||
if (!threadName || threadName.length <= 0) {
|
||||
threadName = [NSString stringWithFormat:@"%p", [NSThread currentThread]];
|
||||
}
|
||||
return [NSString stringWithFormat:@"[RCTLog][tid:%@][%@:%d]>", threadName, fileName, lineNumber];
|
||||
}
|
||||
RCTCurrentLogFunction = RCTDefaultLogFunction;
|
||||
|
||||
// TODO (#5906496): Does this need to be tied to RCTBridge?
|
||||
NSString *RCTLogObjects(NSArray *objects, NSString *level)
|
||||
{
|
||||
NSString *str = objects[0];
|
||||
#if TARGET_IPHONE_SIMULATOR
|
||||
if ([RCTBridge hasValidJSExecutor]) {
|
||||
fprintf(stderr, "%s\n", [str UTF8String]); // don't print timestamps and other junk
|
||||
[RCTBridge log:objects level:level];
|
||||
} else
|
||||
#if DEBUG
|
||||
RCTCurrentLogThreshold = RCTLogLevelInfo - 1;
|
||||
#else
|
||||
RCTCurrentLogThreshold = RCTLogLevelError;
|
||||
#endif
|
||||
{
|
||||
// Print normal errors with timestamps when not in simulator.
|
||||
// Non errors are already compiled out above, so log as error here.
|
||||
if (RCTInjectedLogFunction) {
|
||||
RCTInjectedLogFunction(str);
|
||||
} else {
|
||||
NSLog(@">\n %@", str);
|
||||
}
|
||||
}
|
||||
return str;
|
||||
|
||||
}
|
||||
|
||||
// Returns array of objects. First arg is a simple string to print, remaining args
|
||||
// are objects to pass through to the debugger so they are inspectable in the console.
|
||||
NSArray *RCTLogFormat(const char *file, int lineNumber, const char *funcName, NSString *format, ...)
|
||||
RCTLogFunction RCTDefaultLogFunction = ^(
|
||||
RCTLogLevel level,
|
||||
NSString *fileName,
|
||||
NSNumber *lineNumber,
|
||||
NSString *message
|
||||
)
|
||||
{
|
||||
NSString *log = RCTFormatLog(
|
||||
[NSDate date], [NSThread currentThread], level, fileName, lineNumber, message
|
||||
);
|
||||
fprintf(stderr, "%s\n", log.UTF8String);
|
||||
};
|
||||
|
||||
void RCTSetLogFunction(RCTLogFunction logFunction)
|
||||
{
|
||||
RCTCurrentLogFunction = logFunction;
|
||||
}
|
||||
|
||||
RCTLogFunction RCTGetLogFunction()
|
||||
{
|
||||
return RCTCurrentLogFunction;
|
||||
}
|
||||
|
||||
void RCTAddLogFunction(RCTLogFunction logFunction)
|
||||
{
|
||||
RCTLogFunction existing = RCTCurrentLogFunction;
|
||||
if (existing) {
|
||||
RCTCurrentLogFunction = ^(RCTLogLevel level,
|
||||
NSString *fileName,
|
||||
NSNumber *lineNumber,
|
||||
NSString *message) {
|
||||
|
||||
existing(level, fileName, lineNumber, message);
|
||||
logFunction(level, fileName, lineNumber, message);
|
||||
};
|
||||
} else {
|
||||
RCTCurrentLogFunction = logFunction;
|
||||
}
|
||||
}
|
||||
|
||||
void RCTPerformBlockWithLogPrefix(void (^block)(void), NSString *prefix)
|
||||
{
|
||||
NSMutableDictionary *threadDictionary = [NSThread currentThread].threadDictionary;
|
||||
NSMutableArray *prefixStack = threadDictionary[RCTLogPrefixStack];
|
||||
if (!prefixStack) {
|
||||
prefixStack = [[NSMutableArray alloc] init];
|
||||
threadDictionary[RCTLogPrefixStack] = prefixStack;
|
||||
}
|
||||
[prefixStack addObject:prefix];
|
||||
block();
|
||||
[prefixStack removeLastObject];
|
||||
}
|
||||
|
||||
NSString *RCTFormatLog(
|
||||
NSDate *timestamp,
|
||||
NSThread *thread,
|
||||
RCTLogLevel level,
|
||||
NSString *fileName,
|
||||
NSNumber *lineNumber,
|
||||
NSString *message
|
||||
)
|
||||
{
|
||||
NSMutableString *log = [[NSMutableString alloc] init];
|
||||
if (timestamp) {
|
||||
static NSDateFormatter *formatter;
|
||||
static dispatch_once_t onceToken;
|
||||
dispatch_once(&onceToken, ^{
|
||||
formatter = [[NSDateFormatter alloc] init];
|
||||
formatter.dateFormat = formatter.dateFormat = @"yyyy-MM-dd HH:mm:ss.SSS ";
|
||||
});
|
||||
[log appendString:[formatter stringFromDate:timestamp]];
|
||||
}
|
||||
[log appendString:@"[react]"];
|
||||
if (level) {
|
||||
[log appendFormat:@"[%s]", RCTLogLevels[level - 1]];
|
||||
}
|
||||
if (thread) {
|
||||
NSString *threadName = thread.name;
|
||||
if (threadName.length == 0) {
|
||||
#if DEBUG
|
||||
#pragma clang diagnostic push
|
||||
#pragma clang diagnostic ignored "-Wdeprecated-declarations"
|
||||
threadName = @(dispatch_queue_get_label(dispatch_get_current_queue()));
|
||||
#pragma clang diagnostic pop
|
||||
#else
|
||||
threadName = [NSString stringWithFormat:@"%p", thread];
|
||||
#endif
|
||||
}
|
||||
[log appendFormat:@"[tid:%@]", threadName];
|
||||
}
|
||||
if (fileName) {
|
||||
fileName = [fileName lastPathComponent];
|
||||
if (lineNumber) {
|
||||
[log appendFormat:@"[%@:%@]", fileName, lineNumber];
|
||||
} else {
|
||||
[log appendFormat:@"[%@]", fileName];
|
||||
}
|
||||
}
|
||||
if (message) {
|
||||
[log appendString:@" "];
|
||||
[log appendString:message];
|
||||
}
|
||||
return log;
|
||||
}
|
||||
|
||||
RCTLogFunction RCTSimpleLogFunction(void (^logFunction)(RCTLogLevel level, NSString *message))
|
||||
{
|
||||
return ^(RCTLogLevel level,
|
||||
NSString *fileName,
|
||||
NSNumber *lineNumber,
|
||||
NSString *message) {
|
||||
|
||||
logFunction(level, RCTFormatLog(
|
||||
[NSDate date], [NSThread currentThread], level, fileName, lineNumber, message
|
||||
));
|
||||
};
|
||||
}
|
||||
|
||||
void _RCTLogFormat(RCTLogLevel level, const char *fileName, int lineNumber, NSString *format, ...)
|
||||
{
|
||||
if (RCTCurrentLogFunction && level >= RCTCurrentLogThreshold) {
|
||||
|
||||
// Get message
|
||||
va_list args;
|
||||
va_start(args, format);
|
||||
NSString *preamble = _RCTLogPreamble(file, lineNumber, funcName);
|
||||
__block NSString *message = [[NSString alloc] initWithFormat:format arguments:args];
|
||||
va_end(args);
|
||||
|
||||
// Pull out NSObjects so we can pass them through as inspectable objects to the js debugger
|
||||
NSArray *formatParts = [format componentsSeparatedByString:@"%"];
|
||||
NSMutableArray *objects = [NSMutableArray arrayWithObject:preamble];
|
||||
BOOL valid = YES;
|
||||
for (int i = 0; i < formatParts.count; i++) {
|
||||
if (i == 0) { // first part is always a string
|
||||
[objects addObject:formatParts[i]];
|
||||
} else {
|
||||
if (valid && [formatParts[i] length] && [formatParts[i] characterAtIndex:0] == '@') {
|
||||
id obj = va_arg(args, id);
|
||||
[objects addObject:obj ?: @"null"];
|
||||
[objects addObject:[formatParts[i] substringFromIndex:1]]; // remove formatting char
|
||||
} else {
|
||||
// We could determine the type (double, int?) of the va_arg by parsing the formatPart, but for now we just bail.
|
||||
valid = NO;
|
||||
[objects addObject:[NSString stringWithFormat:@"unknown object for %%%@", formatParts[i]]];
|
||||
// Add prefix
|
||||
NSMutableDictionary *threadDictionary = [NSThread currentThread].threadDictionary;
|
||||
NSArray *prefixStack = threadDictionary[RCTLogPrefixStack];
|
||||
NSString *prefix = [prefixStack lastObject];
|
||||
if (prefix) {
|
||||
message = [prefix stringByAppendingString:message];
|
||||
}
|
||||
|
||||
// Call log function
|
||||
RCTCurrentLogFunction(
|
||||
level, fileName ? @(fileName) : nil, (lineNumber >= 0) ? @(lineNumber) : nil, message
|
||||
);
|
||||
|
||||
#if DEBUG
|
||||
|
||||
// Log to red box
|
||||
if (level >= RCTLOG_REDBOX_LEVEL) {
|
||||
[[RCTRedBox sharedInstance] showErrorMessage:message];
|
||||
}
|
||||
|
||||
// Log to JS executor
|
||||
if ([RCTBridge hasValidJSExecutor]) {
|
||||
[RCTBridge logMessage:message level:level ? @(RCTLogLevels[level - 1]) : @"info"];
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
}
|
||||
va_end(args);
|
||||
va_start(args, format);
|
||||
NSString *strOut = [preamble stringByAppendingString:[[NSString alloc] initWithFormat:format arguments:args]];
|
||||
va_end(args);
|
||||
NSMutableArray *objectsOut = [NSMutableArray arrayWithObject:strOut];
|
||||
[objectsOut addObjectsFromArray:objects];
|
||||
return objectsOut;
|
||||
}
|
||||
|
||||
#pragma mark - Deprecated
|
||||
|
||||
void RCTInjectLogFunction(void (^logFunction)(NSString *msg))
|
||||
{
|
||||
RCTSetLogFunction(^(RCTLogLevel level,
|
||||
NSString *fileName,
|
||||
NSNumber *lineNumber,
|
||||
NSString *message) {
|
||||
|
||||
if (level > RCTLogLevelError) {
|
||||
|
||||
// Use custom log function
|
||||
NSString *loc = fileName ? [NSString stringWithFormat:@"[%@:%@] ", fileName, lineNumber] : @"";
|
||||
logFunction([loc stringByAppendingString:message]);
|
||||
|
||||
} else if (RCTDefaultLogFunction && level >= RCTCurrentLogThreshold) {
|
||||
|
||||
// Use default logger
|
||||
RCTDefaultLogFunction(level, fileName, lineNumber, message);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
|
|
@ -17,6 +17,9 @@ extern NSString *const RCTReloadViewsNotification;
|
|||
|
||||
@interface RCTRootView : UIView <RCTInvalidating>
|
||||
|
||||
/**
|
||||
* - Designated initializer -
|
||||
*/
|
||||
- (instancetype)initWithBridge:(RCTBridge *)bridge
|
||||
moduleName:(NSString *)moduleName NS_DESIGNATED_INITIALIZER;
|
||||
|
||||
|
@ -39,6 +42,10 @@ extern NSString *const RCTReloadViewsNotification;
|
|||
*/
|
||||
@property (nonatomic, copy, readonly) NSString *moduleName;
|
||||
|
||||
/**
|
||||
* The bridge used by the root view. Bridges can be shared between multiple
|
||||
* root views, so you can use this property to initialize another RCTRootView.
|
||||
*/
|
||||
@property (nonatomic, strong, readonly) RCTBridge *bridge;
|
||||
|
||||
/**
|
||||
|
|
|
@ -17,7 +17,6 @@
|
|||
#import "RCTEventDispatcher.h"
|
||||
#import "RCTKeyCommands.h"
|
||||
#import "RCTLog.h"
|
||||
#import "RCTRedBox.h"
|
||||
#import "RCTSourceCode.h"
|
||||
#import "RCTTouchHandler.h"
|
||||
#import "RCTUIManager.h"
|
||||
|
|
|
@ -38,23 +38,18 @@ static JSValueRef RCTNativeLoggingHook(JSContextRef context, JSObjectRef object,
|
|||
if (!string) {
|
||||
return JSValueMakeUndefined(context);
|
||||
}
|
||||
|
||||
NSString *str = (__bridge_transfer NSString *)JSStringCopyCFString(kCFAllocatorDefault, string);
|
||||
NSError *error = nil;
|
||||
NSString *message = (__bridge_transfer NSString *)JSStringCopyCFString(kCFAllocatorDefault, string);
|
||||
JSStringRelease(string);
|
||||
NSRegularExpression *regex = [NSRegularExpression regularExpressionWithPattern:
|
||||
@"( stack: )?([_a-z0-9]*)@?(http://|file:///)[a-z.0-9:/_-]+/([a-z0-9_]+).includeRequire.runModule.bundle(:[0-9]+:[0-9]+)"
|
||||
options:NSRegularExpressionCaseInsensitive
|
||||
error:&error];
|
||||
NSString *modifiedString = [regex stringByReplacingMatchesInString:str options:0 range:NSMakeRange(0, [str length]) withTemplate:@"[$4$5] \t$2"];
|
||||
error:NULL];
|
||||
message = [regex stringByReplacingMatchesInString:message
|
||||
options:0
|
||||
range:(NSRange){0, message.length}
|
||||
withTemplate:@"[$4$5] \t$2"];
|
||||
|
||||
modifiedString = [@"RCTJSLog> " stringByAppendingString:modifiedString];
|
||||
#if TARGET_IPHONE_SIMULATOR
|
||||
fprintf(stderr, "%s\n", [modifiedString UTF8String]); // don't print timestamps and other junk
|
||||
#else
|
||||
// Print normal errors with timestamps to files when not in simulator.
|
||||
RCTLogObjects(@[modifiedString], @"log");
|
||||
#endif
|
||||
JSStringRelease(string);
|
||||
_RCTLogFormat(0, NULL, -1, @"%@", message);
|
||||
}
|
||||
|
||||
return JSValueMakeUndefined(context);
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
|
||||
#import "RCTAlertManager.h"
|
||||
|
||||
#import "RCTAssert.h"
|
||||
#import "RCTLog.h"
|
||||
|
||||
@interface RCTAlertManager() <UIAlertViewDelegate>
|
||||
|
|
|
@ -197,6 +197,11 @@ static UIViewAnimationCurve UIViewAnimationCurveFromRCTAnimationType(RCTAnimatio
|
|||
|
||||
@synthesize bridge = _bridge;
|
||||
|
||||
/**
|
||||
* Declared in RCTBridge.
|
||||
*/
|
||||
extern NSString *RCTBridgeModuleNameForClass(Class cls);
|
||||
|
||||
/**
|
||||
* This function derives the view name automatically
|
||||
* from the module name.
|
||||
|
@ -334,7 +339,7 @@ static NSString *RCTViewNameForModuleName(NSString *moduleName)
|
|||
|
||||
dispatch_async(_bridge.shadowQueue, ^{
|
||||
RCTShadowView *rootShadowView = _shadowViewRegistry[reactTag];
|
||||
RCTAssert(rootShadowView != nil, @"Could not locate root view with tag %@", reactTag);
|
||||
RCTAssert(rootShadowView != nil, @"Could not locate root view with tag #%@", reactTag);
|
||||
rootShadowView.frame = frame;
|
||||
[rootShadowView updateLayout];
|
||||
|
||||
|
@ -672,7 +677,7 @@ static NSString *RCTViewNameForModuleName(NSString *moduleName)
|
|||
}
|
||||
}
|
||||
|
||||
static BOOL RCTCallPropertySetter(SEL setter, id value, id view, id defaultView, RCTViewManager *manager)
|
||||
static BOOL RCTCallPropertySetter(NSString *key, SEL setter, id value, id view, id defaultView, RCTViewManager *manager)
|
||||
{
|
||||
// TODO: cache respondsToSelector tests
|
||||
if ([manager respondsToSelector:setter]) {
|
||||
|
@ -681,7 +686,25 @@ static BOOL RCTCallPropertySetter(SEL setter, id value, id view, id defaultView,
|
|||
value = nil;
|
||||
}
|
||||
|
||||
void (^block)() = ^{
|
||||
((void (*)(id, SEL, id, id, id))objc_msgSend)(manager, setter, value, view, defaultView);
|
||||
};
|
||||
|
||||
#if DEBUG
|
||||
|
||||
NSString *viewName = RCTViewNameForModuleName(RCTBridgeModuleNameForClass([manager class]));
|
||||
NSString *logPrefix = [NSString stringWithFormat:
|
||||
@"Error setting property '%@' of %@ with tag #%@: ",
|
||||
key, viewName, [view reactTag]];
|
||||
|
||||
RCTPerformBlockWithLogPrefix(block, logPrefix);
|
||||
|
||||
#else
|
||||
|
||||
block();
|
||||
|
||||
#endif
|
||||
|
||||
return YES;
|
||||
}
|
||||
return NO;
|
||||
|
@ -693,7 +716,7 @@ static void RCTSetViewProps(NSDictionary *props, UIView *view,
|
|||
[props enumerateKeysAndObjectsUsingBlock:^(NSString *key, id obj, BOOL *stop) {
|
||||
|
||||
SEL setter = NSSelectorFromString([NSString stringWithFormat:@"set_%@:forView:withDefaultView:", key]);
|
||||
RCTCallPropertySetter(setter, obj, view, defaultView, manager);
|
||||
RCTCallPropertySetter(key, setter, obj, view, defaultView, manager);
|
||||
|
||||
}];
|
||||
}
|
||||
|
@ -704,7 +727,7 @@ static void RCTSetShadowViewProps(NSDictionary *props, RCTShadowView *shadowView
|
|||
[props enumerateKeysAndObjectsUsingBlock:^(NSString *key, id obj, BOOL *stop) {
|
||||
|
||||
SEL setter = NSSelectorFromString([NSString stringWithFormat:@"set_%@:forShadowView:withDefaultView:", key]);
|
||||
RCTCallPropertySetter(setter, obj, shadowView, defaultView, manager);
|
||||
RCTCallPropertySetter(key, setter, obj, shadowView, defaultView, manager);
|
||||
|
||||
}];
|
||||
|
||||
|
@ -875,7 +898,7 @@ static void RCTSetShadowViewProps(NSDictionary *props, RCTShadowView *shadowView
|
|||
[self addUIBlock:^(RCTUIManager *uiManager, RCTSparseArray *viewRegistry) {
|
||||
UIView *view = viewRegistry[reactTag];
|
||||
if (!view) {
|
||||
RCTLogError(@"measure cannot find view with tag %@", reactTag);
|
||||
RCTLogError(@"measure cannot find view with tag #%@", reactTag);
|
||||
return;
|
||||
}
|
||||
CGRect frame = view.frame;
|
||||
|
@ -1039,7 +1062,7 @@ static void RCTMeasureLayout(RCTShadowView *view,
|
|||
uiManager.mainScrollView = (id<RCTScrollableProtocol>)rkObject;
|
||||
((id<RCTScrollableProtocol>)rkObject).nativeMainScrollDelegate = uiManager.nativeMainScrollDelegate;
|
||||
} else {
|
||||
RCTCAssert(NO, @"Tag %@ does not conform to RCTScrollableProtocol", reactTag);
|
||||
RCTCAssert(NO, @"Tag #%@ does not conform to RCTScrollableProtocol", reactTag);
|
||||
}
|
||||
} else {
|
||||
uiManager.mainScrollView = nil;
|
||||
|
@ -1056,7 +1079,7 @@ static void RCTMeasureLayout(RCTShadowView *view,
|
|||
if ([view conformsToProtocol:@protocol(RCTScrollableProtocol)]) {
|
||||
[(id<RCTScrollableProtocol>)view scrollToOffset:CGPointMake([offsetX floatValue], [offsetY floatValue]) animated:YES];
|
||||
} else {
|
||||
RCTLogError(@"tried to scrollToOffset: on non-RCTScrollableProtocol view %@ with tag %@", view, reactTag);
|
||||
RCTLogError(@"tried to scrollToOffset: on non-RCTScrollableProtocol view %@ with tag #%@", view, reactTag);
|
||||
}
|
||||
}];
|
||||
}
|
||||
|
@ -1070,7 +1093,7 @@ static void RCTMeasureLayout(RCTShadowView *view,
|
|||
if ([view conformsToProtocol:@protocol(RCTScrollableProtocol)]) {
|
||||
[(id<RCTScrollableProtocol>)view scrollToOffset:CGPointMake([offsetX floatValue], [offsetY floatValue]) animated:NO];
|
||||
} else {
|
||||
RCTLogError(@"tried to scrollToOffset: on non-RCTScrollableProtocol view %@ with tag %@", view, reactTag);
|
||||
RCTLogError(@"tried to scrollToOffset: on non-RCTScrollableProtocol view %@ with tag #%@", view, reactTag);
|
||||
}
|
||||
}];
|
||||
}
|
||||
|
@ -1084,7 +1107,7 @@ static void RCTMeasureLayout(RCTShadowView *view,
|
|||
if ([view conformsToProtocol:@protocol(RCTScrollableProtocol)]) {
|
||||
[(id<RCTScrollableProtocol>)view zoomToRect:[RCTConvert CGRect:rectDict] animated:YES];
|
||||
} else {
|
||||
RCTLogError(@"tried to zoomToRect: on non-RCTScrollableProtocol view %@ with tag %@", view, reactTag);
|
||||
RCTLogError(@"tried to zoomToRect: on non-RCTScrollableProtocol view %@ with tag #%@", view, reactTag);
|
||||
}
|
||||
}];
|
||||
}
|
||||
|
|
|
@ -72,7 +72,7 @@ RCT_DEPRECATED_VIEW_PROPERTY(throttleScrollCallbackMS, scrollEventThrottle)
|
|||
|
||||
UIView *view = viewRegistry[reactTag];
|
||||
if (!view) {
|
||||
RCTLogError(@"Cannot find view with tag %@", reactTag);
|
||||
RCTLogError(@"Cannot find view with tag #%@", reactTag);
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue