Adding Soft Error Logging to FallbackJSBundleLoader

Reviewed By: michalgr

Differential Revision: D4405355

fbshipit-source-id: 7730a1afd74ace7fdad91b22f9254e83d16d1050
This commit is contained in:
Ashok Menon 2017-01-13 03:52:13 -08:00 committed by Facebook Github Bot
parent c3892fa871
commit 87c6bcb65d
6 changed files with 214 additions and 2 deletions

View File

@ -32,4 +32,3 @@ android_library(
'PUBLIC', 'PUBLIC',
], ],
) )

View File

@ -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);
} }
} }
} }

View File

@ -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',
],
)

View File

@ -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);
}
}

View File

@ -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'

View File

@ -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) {