Adding Soft Error Logging to FallbackJSBundleLoader
Reviewed By: michalgr Differential Revision: D4405355 fbshipit-source-id: 7730a1afd74ace7fdad91b22f9254e83d16d1050
This commit is contained in:
parent
c3892fa871
commit
87c6bcb65d
|
@ -32,4 +32,3 @@ android_library(
|
||||||
'PUBLIC',
|
'PUBLIC',
|
||||||
],
|
],
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -14,6 +14,8 @@ import java.util.List;
|
||||||
import java.util.ListIterator;
|
import java.util.ListIterator;
|
||||||
import java.util.Stack;
|
import java.util.Stack;
|
||||||
|
|
||||||
|
import com.facebook.common.logging.FLog;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* FallbackJSBundleLoader
|
* FallbackJSBundleLoader
|
||||||
*
|
*
|
||||||
|
@ -24,6 +26,7 @@ import java.util.Stack;
|
||||||
public final class FallbackJSBundleLoader extends JSBundleLoader {
|
public final class FallbackJSBundleLoader extends JSBundleLoader {
|
||||||
|
|
||||||
/* package */ static final String RECOVERABLE = "facebook::react::Recoverable";
|
/* package */ static final String RECOVERABLE = "facebook::react::Recoverable";
|
||||||
|
/* package */ static final String TAG = "FallbackJSBundleLoader";
|
||||||
|
|
||||||
// Loaders to delegate to, with the preferred one at the top.
|
// Loaders to delegate to, with the preferred one at the top.
|
||||||
private Stack<JSBundleLoader> mLoaders;
|
private Stack<JSBundleLoader> mLoaders;
|
||||||
|
@ -60,7 +63,7 @@ public final class FallbackJSBundleLoader extends JSBundleLoader {
|
||||||
|
|
||||||
mLoaders.pop();
|
mLoaders.pop();
|
||||||
mRecoveredErrors.add(e);
|
mRecoveredErrors.add(e);
|
||||||
// TODO (t14839302): Report a soft error for each swallowed exception.
|
FLog.wtf(TAG, "Falling back from recoverable error", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,16 @@
|
||||||
|
android_library(
|
||||||
|
name = 'logging',
|
||||||
|
srcs = glob(['**/*.java']),
|
||||||
|
|
||||||
|
exported_deps = [
|
||||||
|
react_native_dep('libraries/fbcore/src/main/java/com/facebook/common/logging:logging'),
|
||||||
|
],
|
||||||
|
|
||||||
|
deps = [
|
||||||
|
react_native_dep('third-party/java/jsr-305:jsr-305'),
|
||||||
|
],
|
||||||
|
|
||||||
|
visibility = [
|
||||||
|
'PUBLIC',
|
||||||
|
],
|
||||||
|
)
|
|
@ -0,0 +1,151 @@
|
||||||
|
/**
|
||||||
|
* Copyright (c) 2015-present, Facebook, Inc.
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* This source code is licensed under the BSD-style license found in the
|
||||||
|
* LICENSE file in the root directory of this source tree. An additional grant
|
||||||
|
* of patent rights can be found in the PATENTS file in the same directory.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.facebook.common.logging;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Collections;
|
||||||
|
|
||||||
|
import javax.annotation.Nullable;
|
||||||
|
|
||||||
|
public final class FakeLoggingDelegate implements LoggingDelegate {
|
||||||
|
|
||||||
|
public static final class LogLine {
|
||||||
|
public final int priority;
|
||||||
|
public final String tag;
|
||||||
|
public final String msg;
|
||||||
|
public final @Nullable Throwable tr;
|
||||||
|
|
||||||
|
private LogLine(
|
||||||
|
int priority,
|
||||||
|
String tag,
|
||||||
|
String msg,
|
||||||
|
@Nullable Throwable tr) {
|
||||||
|
|
||||||
|
this.priority = priority;
|
||||||
|
this.tag = tag;
|
||||||
|
this.msg = msg;
|
||||||
|
this.tr = tr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static final int ASSERT = FLog.ASSERT;
|
||||||
|
public static final int DEBUG = FLog.DEBUG;
|
||||||
|
public static final int ERROR = FLog.ERROR;
|
||||||
|
public static final int INFO = FLog.INFO;
|
||||||
|
public static final int VERBOSE = FLog.VERBOSE;
|
||||||
|
public static final int WARN = FLog.WARN;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* There is no log level for Terrible Failures (we emit them at the Error
|
||||||
|
* Log-level), but to test that WTF errors are being logged, we are making up
|
||||||
|
* a new log level here, guaranteed to be larger than any of the other log
|
||||||
|
* levels.
|
||||||
|
*/
|
||||||
|
public static final int WTF =
|
||||||
|
1 + Collections.max(Arrays.asList(ASSERT, DEBUG, ERROR, INFO, VERBOSE, WARN));
|
||||||
|
|
||||||
|
private int mMinLogLevel = FLog.VERBOSE;
|
||||||
|
private final ArrayList<LogLine> mLogs = new ArrayList<>();
|
||||||
|
|
||||||
|
/** Test Harness */
|
||||||
|
|
||||||
|
private static boolean matchLogQuery(
|
||||||
|
int priority,
|
||||||
|
String tag,
|
||||||
|
@Nullable String throwMsg,
|
||||||
|
LogLine line) {
|
||||||
|
return priority == line.priority
|
||||||
|
&& tag.equals(line.tag)
|
||||||
|
&& (throwMsg == null || throwMsg.equals(line.tr.getMessage()));
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean logContains(int priority, String tag, String throwMsg) {
|
||||||
|
for (FakeLoggingDelegate.LogLine line : mLogs) {
|
||||||
|
if (matchLogQuery(priority, tag, throwMsg, line)) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** LoggingDelegate API */
|
||||||
|
|
||||||
|
public int getMinimumLoggingLevel() {
|
||||||
|
return mMinLogLevel;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setMinimumLoggingLevel(int level) {
|
||||||
|
mMinLogLevel = level;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isLoggable(int level) {
|
||||||
|
return level >= mMinLogLevel;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void logImpl(int priority, String tag, String msg, Throwable tr) {
|
||||||
|
if (isLoggable(priority)) {
|
||||||
|
mLogs.add(new LogLine(priority, tag, msg, tr));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void log(int priority, String tag, String msg) {
|
||||||
|
logImpl(priority, tag, msg, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void d(String tag, String msg, Throwable tr) {
|
||||||
|
logImpl(DEBUG, tag, msg, tr);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void d(String tag, String msg) {
|
||||||
|
logImpl(DEBUG, tag, msg, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void e(String tag, String msg, Throwable tr) {
|
||||||
|
logImpl(ERROR, tag, msg, tr);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void e(String tag, String msg) {
|
||||||
|
logImpl(ERROR, tag, msg, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void i(String tag, String msg, Throwable tr) {
|
||||||
|
logImpl(INFO, tag, msg, tr);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void i(String tag, String msg) {
|
||||||
|
logImpl(INFO, tag, msg, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void v(String tag, String msg, Throwable tr) {
|
||||||
|
logImpl(VERBOSE, tag, msg, tr);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void v(String tag, String msg) {
|
||||||
|
logImpl(VERBOSE, tag, msg, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void w(String tag, String msg, Throwable tr) {
|
||||||
|
logImpl(WARN, tag, msg, tr);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void w(String tag, String msg) {
|
||||||
|
logImpl(WARN, tag, msg, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void wtf(String tag, String msg, Throwable tr) {
|
||||||
|
logImpl(WTF, tag, msg, tr);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void wtf(String tag, String msg) {
|
||||||
|
logImpl(WTF, tag, msg, null);
|
||||||
|
}
|
||||||
|
}
|
|
@ -11,6 +11,7 @@ rn_robolectric_test(
|
||||||
react_native_dep('third-party/java/mockito:mockito'),
|
react_native_dep('third-party/java/mockito:mockito'),
|
||||||
react_native_dep('third-party/java/robolectric3/robolectric:robolectric'),
|
react_native_dep('third-party/java/robolectric3/robolectric:robolectric'),
|
||||||
react_native_target('java/com/facebook/react/cxxbridge:bridge'),
|
react_native_target('java/com/facebook/react/cxxbridge:bridge'),
|
||||||
|
react_native_tests_target('java/com/facebook/common/logging:logging'),
|
||||||
],
|
],
|
||||||
visibility = [
|
visibility = [
|
||||||
'PUBLIC'
|
'PUBLIC'
|
||||||
|
|
|
@ -15,6 +15,10 @@ import java.util.Arrays;
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import com.facebook.common.logging.FLog;
|
||||||
|
import com.facebook.common.logging.FakeLoggingDelegate;
|
||||||
|
import com.facebook.common.logging.LoggingDelegate;
|
||||||
|
|
||||||
import static org.fest.assertions.api.Assertions.assertThat;
|
import static org.fest.assertions.api.Assertions.assertThat;
|
||||||
import static org.fest.assertions.api.Assertions.fail;
|
import static org.fest.assertions.api.Assertions.fail;
|
||||||
|
|
||||||
|
@ -34,6 +38,14 @@ public class FallbackJSBundleLoaderTest {
|
||||||
UNRECOVERABLE = prefix.replace(first, (char) (first + 1));
|
UNRECOVERABLE = prefix.replace(first, (char) (first + 1));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private FakeLoggingDelegate mLoggingDelegate;
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void setup() {
|
||||||
|
mLoggingDelegate = new FakeLoggingDelegate();
|
||||||
|
FLog.setLoggingDelegate(mLoggingDelegate);
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
public void firstLoaderSucceeds() {
|
public void firstLoaderSucceeds() {
|
||||||
JSBundleLoader delegates[] = new JSBundleLoader[] {
|
JSBundleLoader delegates[] = new JSBundleLoader[] {
|
||||||
|
@ -48,6 +60,12 @@ public class FallbackJSBundleLoaderTest {
|
||||||
|
|
||||||
verify(delegates[0], times(1)).loadScript(null);
|
verify(delegates[0], times(1)).loadScript(null);
|
||||||
verify(delegates[1], never()).loadScript(null);
|
verify(delegates[1], never()).loadScript(null);
|
||||||
|
|
||||||
|
assertThat(mLoggingDelegate.logContains(
|
||||||
|
FakeLoggingDelegate.WTF,
|
||||||
|
FallbackJSBundleLoader.TAG,
|
||||||
|
null))
|
||||||
|
.isFalse();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -66,6 +84,12 @@ public class FallbackJSBundleLoaderTest {
|
||||||
verify(delegates[0], times(1)).loadScript(null);
|
verify(delegates[0], times(1)).loadScript(null);
|
||||||
verify(delegates[1], times(1)).loadScript(null);
|
verify(delegates[1], times(1)).loadScript(null);
|
||||||
verify(delegates[2], never()).loadScript(null);
|
verify(delegates[2], never()).loadScript(null);
|
||||||
|
|
||||||
|
assertThat(mLoggingDelegate.logContains(
|
||||||
|
FakeLoggingDelegate.WTF,
|
||||||
|
FallbackJSBundleLoader.TAG,
|
||||||
|
recoverableMsg("error1")))
|
||||||
|
.isTrue();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -98,6 +122,18 @@ public class FallbackJSBundleLoaderTest {
|
||||||
|
|
||||||
verify(delegates[0], times(1)).loadScript(null);
|
verify(delegates[0], times(1)).loadScript(null);
|
||||||
verify(delegates[1], times(1)).loadScript(null);
|
verify(delegates[1], times(1)).loadScript(null);
|
||||||
|
|
||||||
|
assertThat(mLoggingDelegate.logContains(
|
||||||
|
FakeLoggingDelegate.WTF,
|
||||||
|
FallbackJSBundleLoader.TAG,
|
||||||
|
recoverableMsg("error1")))
|
||||||
|
.isTrue();
|
||||||
|
|
||||||
|
assertThat(mLoggingDelegate.logContains(
|
||||||
|
FakeLoggingDelegate.WTF,
|
||||||
|
FallbackJSBundleLoader.TAG,
|
||||||
|
recoverableMsg("error2")))
|
||||||
|
.isTrue();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -119,6 +155,12 @@ public class FallbackJSBundleLoaderTest {
|
||||||
|
|
||||||
verify(delegates[0], times(1)).loadScript(null);
|
verify(delegates[0], times(1)).loadScript(null);
|
||||||
verify(delegates[1], never()).loadScript(null);
|
verify(delegates[1], never()).loadScript(null);
|
||||||
|
|
||||||
|
assertThat(mLoggingDelegate.logContains(
|
||||||
|
FakeLoggingDelegate.WTF,
|
||||||
|
FallbackJSBundleLoader.TAG,
|
||||||
|
null))
|
||||||
|
.isFalse();
|
||||||
}
|
}
|
||||||
|
|
||||||
private static JSBundleLoader successfulLoader(String url) {
|
private static JSBundleLoader successfulLoader(String url) {
|
||||||
|
|
Loading…
Reference in New Issue