react-native-macos/React/Base/RCTLog.mm

312 строки
9.5 KiB
Plaintext
Исходник Обычный вид История

/*
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
#import "RCTLog.h"
#include <cxxabi.h>
#import <objc/message.h>
Use Apple unified logging API (os_log) (#27892) Summary: As discussed in https://github.com/facebook/react-native/issues/27863, the following changes were made to modernize the internal default logging function: - `RCTDefaultLogThreshold` is now set to `RCTLogLevelTrace` in both release and debug builds—the Apple logging system will discard uncollected log entires, while allowing for collection when needed - `RCTLogLevel` is translated to the appropriate log type - The log subsystem is "com.facebook.react.log" - `RCTLogSource` translates to the appropriate category ("native"/"javascript") - Log the provided message using `os_log_with_type` Closes https://github.com/facebook/react-native/issues/27863 ## Changelog [iOS] [Changed] - Use Apple unified logging API (os_log) Pull Request resolved: https://github.com/facebook/react-native/pull/27892 Test Plan: ## From Original PR Ran a test app in the iOS simulator, and verified that logs are correctly displayed in Console.app as well as using the following command: ```sh /usr/bin/xcrun simctl spawn booted log stream --level debug --style compact --predicate 'process=="ReactNativeTesterApp" && subsystem=="com.facebook.react.log"' ``` ## Peter's Test Plan 1. Apply P125583473 2. Verify log output in Xcode P125583504 3. Apply this diff 4. Verify log output in Xcode P125583597 These appear unchanged, after digging into why, I realized that FB doesn't even use the default log function, we inject a custom one [here](https://fburl.com/diffusion/887a1axs). So this PR shouldn't affect us at all. :) Differential Revision: D19605414 Pulled By: PeteTheHeat fbshipit-source-id: 1d70fb702c337a759905d4a65a951a31353ce775
2020-01-29 02:18:22 +03:00
#import <os/log.h>
#import "RCTRedBoxSetEnabled.h"
#import "RCTAssert.h"
#import "RCTBridge+Private.h"
#import "RCTBridge.h"
2015-04-21 15:26:51 +03:00
#import "RCTDefines.h"
#import "RCTUtils.h"
static NSString *const RCTLogFunctionStack = @"RCTLogFunctionStack";
const char *RCTLogLevels[] = {
"trace",
"info",
"warn",
"error",
"fatal",
};
Use Apple unified logging API (os_log) (#27892) Summary: As discussed in https://github.com/facebook/react-native/issues/27863, the following changes were made to modernize the internal default logging function: - `RCTDefaultLogThreshold` is now set to `RCTLogLevelTrace` in both release and debug builds—the Apple logging system will discard uncollected log entires, while allowing for collection when needed - `RCTLogLevel` is translated to the appropriate log type - The log subsystem is "com.facebook.react.log" - `RCTLogSource` translates to the appropriate category ("native"/"javascript") - Log the provided message using `os_log_with_type` Closes https://github.com/facebook/react-native/issues/27863 ## Changelog [iOS] [Changed] - Use Apple unified logging API (os_log) Pull Request resolved: https://github.com/facebook/react-native/pull/27892 Test Plan: ## From Original PR Ran a test app in the iOS simulator, and verified that logs are correctly displayed in Console.app as well as using the following command: ```sh /usr/bin/xcrun simctl spawn booted log stream --level debug --style compact --predicate 'process=="ReactNativeTesterApp" && subsystem=="com.facebook.react.log"' ``` ## Peter's Test Plan 1. Apply P125583473 2. Verify log output in Xcode P125583504 3. Apply this diff 4. Verify log output in Xcode P125583597 These appear unchanged, after digging into why, I realized that FB doesn't even use the default log function, we inject a custom one [here](https://fburl.com/diffusion/887a1axs). So this PR shouldn't affect us at all. :) Differential Revision: D19605414 Pulled By: PeteTheHeat fbshipit-source-id: 1d70fb702c337a759905d4a65a951a31353ce775
2020-01-29 02:18:22 +03:00
/* os log will discard debug and info messages if they are not needed */
static const RCTLogLevel RCTDefaultLogThreshold = (RCTLogLevel)(RCTLogLevelInfo - 1);
static RCTLogFunction RCTCurrentLogFunction;
static RCTLogLevel RCTCurrentLogThreshold = RCTDefaultLogThreshold;
RCTLogLevel RCTGetLogThreshold()
{
return RCTCurrentLogThreshold;
}
void RCTSetLogThreshold(RCTLogLevel threshold) {
RCTCurrentLogThreshold = threshold;
}
Use Apple unified logging API (os_log) (#27892) Summary: As discussed in https://github.com/facebook/react-native/issues/27863, the following changes were made to modernize the internal default logging function: - `RCTDefaultLogThreshold` is now set to `RCTLogLevelTrace` in both release and debug builds—the Apple logging system will discard uncollected log entires, while allowing for collection when needed - `RCTLogLevel` is translated to the appropriate log type - The log subsystem is "com.facebook.react.log" - `RCTLogSource` translates to the appropriate category ("native"/"javascript") - Log the provided message using `os_log_with_type` Closes https://github.com/facebook/react-native/issues/27863 ## Changelog [iOS] [Changed] - Use Apple unified logging API (os_log) Pull Request resolved: https://github.com/facebook/react-native/pull/27892 Test Plan: ## From Original PR Ran a test app in the iOS simulator, and verified that logs are correctly displayed in Console.app as well as using the following command: ```sh /usr/bin/xcrun simctl spawn booted log stream --level debug --style compact --predicate 'process=="ReactNativeTesterApp" && subsystem=="com.facebook.react.log"' ``` ## Peter's Test Plan 1. Apply P125583473 2. Verify log output in Xcode P125583504 3. Apply this diff 4. Verify log output in Xcode P125583597 These appear unchanged, after digging into why, I realized that FB doesn't even use the default log function, we inject a custom one [here](https://fburl.com/diffusion/887a1axs). So this PR shouldn't affect us at all. :) Differential Revision: D19605414 Pulled By: PeteTheHeat fbshipit-source-id: 1d70fb702c337a759905d4a65a951a31353ce775
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;
}
}
}
RCTLogFunction RCTDefaultLogFunction = ^(
RCTLogLevel level,
Use Apple unified logging API (os_log) (#27892) Summary: As discussed in https://github.com/facebook/react-native/issues/27863, the following changes were made to modernize the internal default logging function: - `RCTDefaultLogThreshold` is now set to `RCTLogLevelTrace` in both release and debug builds—the Apple logging system will discard uncollected log entires, while allowing for collection when needed - `RCTLogLevel` is translated to the appropriate log type - The log subsystem is "com.facebook.react.log" - `RCTLogSource` translates to the appropriate category ("native"/"javascript") - Log the provided message using `os_log_with_type` Closes https://github.com/facebook/react-native/issues/27863 ## Changelog [iOS] [Changed] - Use Apple unified logging API (os_log) Pull Request resolved: https://github.com/facebook/react-native/pull/27892 Test Plan: ## From Original PR Ran a test app in the iOS simulator, and verified that logs are correctly displayed in Console.app as well as using the following command: ```sh /usr/bin/xcrun simctl spawn booted log stream --level debug --style compact --predicate 'process=="ReactNativeTesterApp" && subsystem=="com.facebook.react.log"' ``` ## Peter's Test Plan 1. Apply P125583473 2. Verify log output in Xcode P125583504 3. Apply this diff 4. Verify log output in Xcode P125583597 These appear unchanged, after digging into why, I realized that FB doesn't even use the default log function, we inject a custom one [here](https://fburl.com/diffusion/887a1axs). So this PR shouldn't affect us at all. :) Differential Revision: D19605414 Pulled By: PeteTheHeat fbshipit-source-id: 1d70fb702c337a759905d4a65a951a31353ce775
2020-01-29 02:18:22 +03:00
RCTLogSource source,
__unused NSString *fileName,
__unused NSNumber *lineNumber,
NSString *message
)
{
Use Apple unified logging API (os_log) (#27892) Summary: As discussed in https://github.com/facebook/react-native/issues/27863, the following changes were made to modernize the internal default logging function: - `RCTDefaultLogThreshold` is now set to `RCTLogLevelTrace` in both release and debug builds—the Apple logging system will discard uncollected log entires, while allowing for collection when needed - `RCTLogLevel` is translated to the appropriate log type - The log subsystem is "com.facebook.react.log" - `RCTLogSource` translates to the appropriate category ("native"/"javascript") - Log the provided message using `os_log_with_type` Closes https://github.com/facebook/react-native/issues/27863 ## Changelog [iOS] [Changed] - Use Apple unified logging API (os_log) Pull Request resolved: https://github.com/facebook/react-native/pull/27892 Test Plan: ## From Original PR Ran a test app in the iOS simulator, and verified that logs are correctly displayed in Console.app as well as using the following command: ```sh /usr/bin/xcrun simctl spawn booted log stream --level debug --style compact --predicate 'process=="ReactNativeTesterApp" && subsystem=="com.facebook.react.log"' ``` ## Peter's Test Plan 1. Apply P125583473 2. Verify log output in Xcode P125583504 3. Apply this diff 4. Verify log output in Xcode P125583597 These appear unchanged, after digging into why, I realized that FB doesn't even use the default log function, we inject a custom one [here](https://fburl.com/diffusion/887a1axs). So this PR shouldn't affect us at all. :) Differential Revision: D19605414 Pulled By: PeteTheHeat fbshipit-source-id: 1d70fb702c337a759905d4a65a951a31353ce775
2020-01-29 02:18:22 +03:00
os_log_with_type(RCTLogForLogSource(source), RCTLogTypeForLogLevel(level), "%{public}s", message.UTF8String);
};
void RCTSetLogFunction(RCTLogFunction logFunction)
{
RCTCurrentLogFunction = logFunction;
}
RCTLogFunction RCTGetLogFunction()
{
if (!RCTCurrentLogFunction) {
RCTCurrentLogFunction = RCTDefaultLogFunction;
}
return RCTCurrentLogFunction;
}
void RCTAddLogFunction(RCTLogFunction logFunction)
{
RCTLogFunction existing = RCTGetLogFunction();
if (existing) {
RCTSetLogFunction(^(RCTLogLevel level, RCTLogSource source, NSString *fileName, NSNumber *lineNumber, NSString *message) {
existing(level, source, fileName, lineNumber, message);
logFunction(level, source, fileName, lineNumber, message);
});
} else {
RCTSetLogFunction(logFunction);
}
}
/**
* 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;
NSArray<RCTLogFunction> *functionStack = threadDictionary[RCTLogFunctionStack];
RCTLogFunction logFunction = functionStack.lastObject;
if (logFunction) {
return logFunction;
}
return RCTGetLogFunction();
}
void RCTPerformBlockWithLogFunction(void (^block)(void), RCTLogFunction logFunction)
{
NSMutableDictionary *threadDictionary = [NSThread currentThread].threadDictionary;
NSMutableArray<RCTLogFunction> *functionStack = threadDictionary[RCTLogFunctionStack];
if (!functionStack) {
functionStack = [NSMutableArray new];
threadDictionary[RCTLogFunctionStack] = functionStack;
}
[functionStack addObject:logFunction];
block();
[functionStack removeLastObject];
}
void RCTPerformBlockWithLogPrefix(void (^block)(void), NSString *prefix)
{
RCTLogFunction logFunction = RCTGetLocalLogFunction();
if (logFunction) {
RCTPerformBlockWithLogFunction(block, ^(RCTLogLevel level, RCTLogSource source,
NSString *fileName, NSNumber *lineNumber,
NSString *message) {
logFunction(level, source, fileName, lineNumber, [prefix stringByAppendingString:message]);
});
}
}
NSString *RCTFormatLog(
NSDate *timestamp,
RCTLogLevel level,
NSString *fileName,
NSNumber *lineNumber,
NSString *message
)
{
NSMutableString *log = [NSMutableString new];
if (timestamp) {
static NSDateFormatter *formatter;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
formatter = [NSDateFormatter new];
formatter.dateFormat = formatter.dateFormat = @"yyyy-MM-dd HH:mm:ss.SSS ";
});
[log appendString:[formatter stringFromDate:timestamp]];
}
if (level) {
[log appendFormat:@"[%s]", RCTLogLevels[level]];
}
[log appendFormat:@"[tid:%@]", RCTCurrentThreadName()];
if (fileName) {
fileName = fileName.lastPathComponent;
if (lineNumber) {
[log appendFormat:@"[%@:%@]", fileName, lineNumber];
} else {
[log appendFormat:@"[%@]", fileName];
}
}
if (message) {
[log appendString:@" "];
[log appendString:message];
}
return log;
}
NSString *RCTFormatLogLevel(RCTLogLevel level)
{
NSDictionary *levelsToString = @{@(RCTLogLevelTrace) : @"trace",
@(RCTLogLevelInfo) : @"info",
@(RCTLogLevelWarning) : @"warning",
@(RCTLogLevelFatal) : @"fatal",
@(RCTLogLevelError) : @"error"};
return levelsToString[@(level)];
}
NSString *RCTFormatLogSource(RCTLogSource source)
{
NSDictionary *sourcesToString = @{@(RCTLogSourceNative) : @"native",
@(RCTLogSourceJavaScript) : @"js"};
return sourcesToString[@(source)];
}
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:&regexError];
if (regexError) {
RCTLogError(@"Failed to build regex: %@", [regexError localizedDescription]);
}
});
return _regex;
}
void _RCTLogNativeInternal(RCTLogLevel level, const char *fileName, int lineNumber, NSString *format, ...)
{
RCTLogFunction logFunction = RCTGetLocalLogFunction();
BOOL log = RCT_DEBUG || (logFunction != nil);
if (log && level >= RCTGetLogThreshold()) {
// 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];
va_end(args);
// Call log function
if (logFunction) {
logFunction(level, RCTLogSourceNative, fileName ? @(fileName) : nil, lineNumber > 0 ? @(lineNumber) : nil, message);
}
// Log to red box if one is configured.
if (RCTSharedApplication() && RCTRedBoxGetEnabled() && level >= RCTLOG_REDBOX_LEVEL) {
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}];
}
}];
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
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);
}
}
});
}
#if RCT_DEBUG
if (!RCTRunningInTestEnvironment()) {
// Log to JS executor
[[RCTBridge currentBridge] logMessage:message level:level ? @(RCTLogLevels[level]) : @"info"];
}
#endif
}
}
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);
}
}
}