From 52b3105f652eca72892f200923e1687f1d995486 Mon Sep 17 00:00:00 2001 From: Rick Hanlon Date: Wed, 1 Apr 2020 16:42:50 -0700 Subject: [PATCH] Implement RCTWarn equivalent on Android Summary: ## Overview This diff is an RFC to port a logging feature from iOS to Android. Changelog: [Internal] ## Motivation On iOS we have the following log functions and behaviors available for logging native warnings and errors: - **Warnings** (`RCTLogWarn`) - Log level 'warn' to console - Display warning in LogBox - **Errors** (`RCTLogError`) - Log level 'error' to console - Display a native RedBox (needs converted to show a LogBox if available) - **Logs** - We also have `RCTLog`, `RCTTrace`, `RCTAdvice`, `RCTInfo`, which just log to the console. In Java, we have: - **Warnings** - **None**, added in this diff - **Errors** (`DevSupportManager.showNewJavaError`) - Log level 'error' to console with `FLog.e` - Display a native RedBox (needs converted to show a LogBox if available - **Logs** - `ReactSoftException` (crashes the app??) - `ReactNoCrashSoftException` (only logs??) - Others? ## Details This diff adds a method to pair with `RCTLogWarn`, `DevSupportManager.showNewJavaWarning`, which will log to the console and show a LogBox warning if LogBox is available. ## Concerns I have a few concerns/questions about the state of logging on Android: - Should/can we move all of the logging to it's own class, like how RCTLog works? - Why does some logging happen on DevSupportManager and some in other classes? - If we moved it all to it's own class, how could we access the reactContext to call the RCTLog JS module Reviewed By: JoshuaGross Differential Revision: D20056394 fbshipit-source-id: 32d57e300685e46da8039fc77cb22b4084acf81a --- .../java/com/facebook/react/devsupport/BUCK | 1 + .../react/devsupport/LogBoxModule.java | 10 +- .../main/java/com/facebook/react/util/BUCK | 2 + .../java/com/facebook/react/util/RCTLog.java | 26 ++++ .../java/com/facebook/react/util/RNLog.java | 125 ++++++++++++++++++ 5 files changed, 157 insertions(+), 7 deletions(-) create mode 100644 ReactAndroid/src/main/java/com/facebook/react/util/RCTLog.java create mode 100644 ReactAndroid/src/main/java/com/facebook/react/util/RNLog.java diff --git a/ReactAndroid/src/main/java/com/facebook/react/devsupport/BUCK b/ReactAndroid/src/main/java/com/facebook/react/devsupport/BUCK index ba1e76099c..a5c503d2f7 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/devsupport/BUCK +++ b/ReactAndroid/src/main/java/com/facebook/react/devsupport/BUCK @@ -26,6 +26,7 @@ rn_android_library( react_native_target("java/com/facebook/debug/tags:tags"), react_native_target("java/com/facebook/react/bridge:bridge"), react_native_target("java/com/facebook/react/common:common"), + react_native_target("java/com/facebook/react/util:util"), react_native_target("java/com/facebook/react/common/network:network"), react_native_target("java/com/facebook/react/devsupport:interfaces"), react_native_target("java/com/facebook/react/module/annotations:annotations"), diff --git a/ReactAndroid/src/main/java/com/facebook/react/devsupport/LogBoxModule.java b/ReactAndroid/src/main/java/com/facebook/react/devsupport/LogBoxModule.java index 2c4e4eae53..201ee96f95 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/devsupport/LogBoxModule.java +++ b/ReactAndroid/src/main/java/com/facebook/react/devsupport/LogBoxModule.java @@ -11,13 +11,12 @@ import android.app.Activity; import android.view.View; import android.view.ViewGroup; import androidx.annotation.Nullable; -import com.facebook.common.logging.FLog; import com.facebook.fbreact.specs.NativeLogBoxSpec; import com.facebook.react.bridge.ReactApplicationContext; import com.facebook.react.bridge.UiThreadUtil; -import com.facebook.react.common.ReactConstants; import com.facebook.react.devsupport.interfaces.DevSupportManager; import com.facebook.react.module.annotations.ReactModule; +import com.facebook.react.util.RNLog; @ReactModule(name = LogBoxModule.NAME) public class LogBoxModule extends NativeLogBoxSpec { @@ -40,9 +39,7 @@ public class LogBoxModule extends NativeLogBoxSpec { if (mReactRootView == null && mDevSupportManager != null) { mReactRootView = mDevSupportManager.createRootView("LogBox"); if (mReactRootView == null) { - FLog.e( - ReactConstants.TAG, - "Unable to launch logbox because react was unable to create the root view"); + RNLog.e("Unable to launch logbox because react was unable to create the root view"); } } } @@ -64,8 +61,7 @@ public class LogBoxModule extends NativeLogBoxSpec { if (mLogBoxDialog == null && mReactRootView != null) { Activity context = getCurrentActivity(); if (context == null || context.isFinishing()) { - FLog.e( - ReactConstants.TAG, + RNLog.e( "Unable to launch logbox because react activity " + "is not available, here is the error that logbox would've displayed: "); return; diff --git a/ReactAndroid/src/main/java/com/facebook/react/util/BUCK b/ReactAndroid/src/main/java/com/facebook/react/util/BUCK index 93288f7267..b78a3fd8e3 100644 --- a/ReactAndroid/src/main/java/com/facebook/react/util/BUCK +++ b/ReactAndroid/src/main/java/com/facebook/react/util/BUCK @@ -7,7 +7,9 @@ rn_android_library( "PUBLIC", ], deps = [ + react_native_dep("libraries/fbcore/src/main/java/com/facebook/common/logging:logging"), react_native_dep("third-party/java/jsr-305:jsr-305"), + react_native_target("java/com/facebook/react/common:common"), react_native_target("java/com/facebook/react/bridge:bridge"), ], ) diff --git a/ReactAndroid/src/main/java/com/facebook/react/util/RCTLog.java b/ReactAndroid/src/main/java/com/facebook/react/util/RCTLog.java new file mode 100644 index 0000000000..511190ebac --- /dev/null +++ b/ReactAndroid/src/main/java/com/facebook/react/util/RCTLog.java @@ -0,0 +1,26 @@ +/* + * 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. + */ + +package com.facebook.react.util; + +import com.facebook.react.bridge.JavaScriptModule; + +/** + * JS module interface for RCTLog + * + *

The RCTLog module allows for showing native logs in JavaScript. + */ +public interface RCTLog extends JavaScriptModule { + + /** + * Send a log to JavaScript. + * + * @param level The level of the log. + * @param message The message to log. + */ + void logIfNoNativeHook(String level, String message); +} diff --git a/ReactAndroid/src/main/java/com/facebook/react/util/RNLog.java b/ReactAndroid/src/main/java/com/facebook/react/util/RNLog.java new file mode 100644 index 0000000000..6355251cb5 --- /dev/null +++ b/ReactAndroid/src/main/java/com/facebook/react/util/RNLog.java @@ -0,0 +1,125 @@ +/* + * 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. + */ + +package com.facebook.react.util; + +import android.util.Log; +import com.facebook.common.logging.FLog; +import com.facebook.react.bridge.ReactContext; +import com.facebook.react.common.ReactConstants; + +/** Logging wrapper for FLog with LogBox support. */ +public class RNLog { + public static final int MINIMUM_LEVEL_FOR_UI = Log.WARN; + + public static final int LOG = Log.VERBOSE; + + public static final int TRACE = Log.DEBUG; + + public static final int ADVICE = Log.INFO; + + public static final int WARN = Log.WARN; + + public static final int ERROR = Log.ERROR; + + /** + * Log a log level message tagged as React Native to the console. + * + * @param message The message to log. + */ + public static void l(String message) { + FLog.i(ReactConstants.TAG, message); + } + + /** + * Log a trace level message tagged as React Native to the console. + * + * @param message The message to log. + */ + public static void t(String message) { + FLog.i(ReactConstants.TAG, message); + } + + /** + * Log a warning level message tagged as React Native to the console. This warning will not be + * shown in LogBox. + * + * @param message The message to log. + */ + public static void a(String message) { + FLog.w(ReactConstants.TAG, "(ADVICE)" + message); + } + + /** + * Log a warning level message tagged as React Native to the console and display in the app. + * + * @param context The React context of the application use to display the warning. + * @param message The message to log. + */ + public static void w(ReactContext context, String message) { + RNLog.logInternal(context, message, WARN); + FLog.w(ReactConstants.TAG, message); + } + + /** + * Log an error level message tagged as React Native to the console and display in the app. + * + * @param context The React context of the application use to display the error. + * @param message The message to log. + */ + public static void e(ReactContext context, String message) { + RNLog.logInternal(context, message, ERROR); + FLog.e(ReactConstants.TAG, message); + } + + /** + * Log an error level message tagged as React Native to the console. This error will not be shown + * in LogBox. + * + * @param message The message to log. + */ + public static void e(String message) { + FLog.e(ReactConstants.TAG, message); + } + + private static void logInternal(ReactContext context, String message, int level) { + if (level >= MINIMUM_LEVEL_FOR_UI) { + if (context != null && message != null) { + context.getJSModule(RCTLog.class).logIfNoNativeHook(levelToString(level), message); + } + } + } + + private static String levelToString(int level) { + switch (level) { + case LOG: + { + return "log"; + } + case TRACE: + { + return "log"; + } + case ADVICE: + { + return "warn"; + } + case WARN: + { + return "warn"; + } + case ERROR: + { + return "error"; + } + default: + { + return "none"; + } + } + } +}