Skip to content

Commit aeb40f8

Browse files
authored
Merge pull request #20359 from wordpress-mobile/rnmobile/add/log-exception-to-crash-logging
[Gutenberg] Add error boundary components and exception logging
2 parents 5adbf32 + eadc097 commit aeb40f8

File tree

10 files changed

+121
-4
lines changed

10 files changed

+121
-4
lines changed

Gemfile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ gem 'nokogiri'
88

99
### Fastlane Plugins
1010

11+
gem 'fastlane-plugin-sentry'
1112
gem 'fastlane-plugin-wpmreleasetoolkit', '~> 9.2'
1213
# gem 'fastlane-plugin-wpmreleasetoolkit', path: '../../release-toolkit'
1314
# gem 'fastlane-plugin-wpmreleasetoolkit', git: 'https://github.com/wordpress-mobile/release-toolkit', branch: ''

Gemfile.lock

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -174,6 +174,8 @@ GEM
174174
xcodeproj (>= 1.13.0, < 2.0.0)
175175
xcpretty (~> 0.3.0)
176176
xcpretty-travis-formatter (>= 0.0.3)
177+
fastlane-plugin-sentry (1.19.0)
178+
os (~> 1.1, >= 1.1.4)
177179
fastlane-plugin-wpmreleasetoolkit (9.4.0)
178180
activesupport (>= 6.1.7.1)
179181
buildkit (~> 1.5)
@@ -355,6 +357,7 @@ PLATFORMS
355357
DEPENDENCIES
356358
danger-dangermattic (~> 1.0)
357359
fastlane (~> 2)
360+
fastlane-plugin-sentry
358361
fastlane-plugin-wpmreleasetoolkit (~> 9.2)
359362
nokogiri
360363
rmagick (~> 4.1)

WordPress/build.gradle

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -713,3 +713,23 @@ if (project.hasProperty("debugStoreFile")) {
713713
}
714714
}
715715
}
716+
717+
// Copy React Native JavaScript bundle and source map so they can be upload it to the Crash logging
718+
// service during the build process.
719+
android {
720+
applicationVariants.all { variant ->
721+
variant.mergeAssetsProvider.configure {
722+
doLast {
723+
// Copy bundle and source map files
724+
copy {
725+
from(outputDir)
726+
into("${buildDir}/react-native-bundle-source-map")
727+
include("*.bundle", "*.bundle.map")
728+
}
729+
730+
// Delete source maps
731+
delete(fileTree(dir: outputDir, includes: ['**/*.bundle.map']))
732+
}
733+
}
734+
}
735+
}

WordPress/src/main/java/org/wordpress/android/ui/posts/EditPostActivity.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,8 @@
4545
import androidx.viewpager.widget.ViewPager;
4646

4747
import com.automattic.android.tracks.crashlogging.CrashLogging;
48+
import com.automattic.android.tracks.crashlogging.JsException;
49+
import com.automattic.android.tracks.crashlogging.JsExceptionCallback;
4850
import com.google.android.material.appbar.AppBarLayout;
4951
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
5052
import com.google.android.material.snackbar.Snackbar;
@@ -3933,4 +3935,8 @@ public LiveData<DialogVisibility> getSavingInProgressDialogVisibility() {
39333935
@Nullable private SavedInstanceDatabase getDB() {
39343936
return SavedInstanceDatabase.Companion.getDatabase(WordPress.getContext());
39353937
}
3938+
3939+
@Override public void onLogJsException(JsException exception, JsExceptionCallback onExceptionSend) {
3940+
mCrashLogging.sendJavaScriptReport(exception, onExceptionSend);
3941+
}
39363942
}

build.gradle

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,8 @@ ext {
2222
// libs
2323
automatticAboutVersion = '1.4.0'
2424
automatticRestVersion = '1.0.8'
25-
automatticTracksVersion = '3.4.0'
26-
gutenbergMobileVersion = 'v1.115.0-alpha3'
25+
automatticTracksVersion = '3.5.0'
26+
gutenbergMobileVersion = 'v1.115.0-alpha5'
2727
wordPressAztecVersion = 'v2.0'
2828
wordPressFluxCVersion = 'trunk-b9ecc708dde74d6cc95aeab42e56fb8067640039'
2929
wordPressLoginVersion = '1.14.1'

fastlane/lanes/build.rb

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@
3737
build_bundle(app: app, version_name: version_name, build_code: current_build_code, flavor: 'Vanilla', buildType: 'Release')
3838

3939
upload_build_to_play_store(app: app, version_name: version_name, track: 'production')
40+
upload_gutenberg_sourcemaps(app: app, release_version: version_name)
4041

4142
create_gh_release(app: app, version_name: version_name) if options[:create_release]
4243
end
@@ -105,6 +106,7 @@
105106
build_bundle(app: app, version_name: version_name, build_code: current_build_code, flavor: 'Vanilla', buildType: 'Release')
106107

107108
upload_build_to_play_store(app: app, version_name: version_name, track: 'beta') if options[:upload_to_play_store]
109+
upload_gutenberg_sourcemaps(app: app, release_version: version_name)
108110

109111
create_gh_release(app: app, version_name: version_name, prerelease: true) if options[:create_release]
110112
end
@@ -217,6 +219,7 @@
217219
)
218220

219221
upload_prototype_build(product: 'WordPress', version_name: version_name)
222+
upload_gutenberg_sourcemaps(app: 'Wordpress', release_version: version_name)
220223
end
221224

222225
#####################################################################################
@@ -240,6 +243,7 @@
240243
)
241244

242245
upload_prototype_build(product: 'Jetpack', version_name: version_name)
246+
upload_gutenberg_sourcemaps(app: 'Jetpack', release_version: version_name)
243247
end
244248

245249
#####################################################################################
@@ -359,4 +363,35 @@ def generate_prototype_build_number
359363
"#{branch}-#{commit}"
360364
end
361365
end
366+
367+
# Uploads the React Native JavaScript bundle and source map files.
368+
# These files are provided by the Gutenberg Mobile library.
369+
#
370+
# @param [String] app App name, e.g. 'WordPress' or 'Jetpack'.
371+
# @param [String] release_version Release version name to attach the files to in Sentry.
372+
#
373+
def upload_gutenberg_sourcemaps(app:, release_version:)
374+
# Load Sentry properties
375+
sentry_path = File.join(PROJECT_ROOT_FOLDER, 'WordPress', 'src', app.downcase, 'sentry.properties')
376+
sentry_properties = JavaProperties.load(sentry_path)
377+
sentry_token = sentry_properties[:'auth.token']
378+
project_slug = sentry_properties[:'defaults.project']
379+
org_slug = sentry_properties[:'defaults.org']
380+
381+
# Bundle and source map files are copied to a specific folder as part of the build process.
382+
bundle_source_map_path = File.join(PROJECT_ROOT_FOLDER, 'WordPress', 'build', 'react-native-bundle-source-map')
383+
384+
sentry_upload_sourcemap(
385+
auth_token: sentry_token,
386+
org_slug: org_slug,
387+
project_slug: project_slug,
388+
version: release_version,
389+
dist: current_build_code,
390+
# When the React native bundle is generated, the source map file references include the local machine path;
391+
# With the `rewrite` and `strip_common_prefix` options, Sentry automatically strips this part.
392+
rewrite: true,
393+
strip_common_prefix: true,
394+
sourcemap: bundle_source_map_path
395+
)
396+
end
362397
end

libs/editor/build.gradle

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@ repositories {
1313
includeGroup "org.wordpress.aztec"
1414
includeGroup "org.wordpress.gutenberg-mobile"
1515
includeGroupByRegex "org.wordpress.react-native-libraries.*"
16+
includeGroup "com.automattic"
17+
includeGroup "com.automattic.tracks"
1618
}
1719
}
1820
maven {
@@ -104,6 +106,7 @@ dependencies {
104106
implementation "com.google.android.material:material:$googleMaterialVersion"
105107
implementation "com.android.volley:volley:$androidVolleyVersion"
106108
implementation "com.google.code.gson:gson:$googleGsonVersion"
109+
implementation "com.automattic:Automattic-Tracks-Android:$automatticTracksVersion"
107110

108111
lintChecks "org.wordpress:lint:$wordPressLintVersion"
109112
}

libs/editor/src/main/java/org/wordpress/android/editor/EditorFragmentAbstract.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,8 @@
1414
import androidx.lifecycle.LiveData;
1515

1616
import com.android.volley.toolbox.ImageLoader;
17+
import com.automattic.android.tracks.crashlogging.JsException;
18+
import com.automattic.android.tracks.crashlogging.JsExceptionCallback;
1719

1820
import org.wordpress.android.editor.gutenberg.DialogVisibilityProvider;
1921
import org.wordpress.android.util.helpers.MediaFile;
@@ -232,6 +234,8 @@ public interface EditorFragmentListener extends DialogVisibilityProvider {
232234
void onToggleRedo(boolean isDisabled);
233235

234236
void onBackHandlerButton();
237+
238+
void onLogJsException(JsException jsException, JsExceptionCallback onSendJsException);
235239
}
236240

237241
/**

libs/editor/src/main/java/org/wordpress/android/editor/gutenberg/GutenbergContainerFragment.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@
3535
import org.wordpress.mobile.WPAndroidGlue.WPAndroidGlueCode.OnGutenbergDidRequestUnsupportedBlockFallbackListener;
3636
import org.wordpress.mobile.WPAndroidGlue.WPAndroidGlueCode.OnGutenbergDidSendButtonPressedActionListener;
3737
import org.wordpress.mobile.WPAndroidGlue.WPAndroidGlueCode.OnImageFullscreenPreviewListener;
38+
import org.wordpress.mobile.WPAndroidGlue.WPAndroidGlueCode.OnLogExceptionListener;
3839
import org.wordpress.mobile.WPAndroidGlue.WPAndroidGlueCode.OnReattachMediaUploadQueryListener;
3940
import org.wordpress.mobile.WPAndroidGlue.WPAndroidGlueCode.OnFocalPointPickerTooltipShownEventListener;
4041
import org.wordpress.mobile.WPAndroidGlue.WPAndroidGlueCode.OnMediaEditorListener;
@@ -95,6 +96,7 @@ public void attachToContainer(ViewGroup viewGroup, OnMediaLibraryButtonListener
9596
OnToggleRedoButtonListener onToggleRedoButtonListener,
9697
OnConnectionStatusEventListener onConnectionStatusEventListener,
9798
OnBackHandlerEventListener onBackHandlerEventListener,
99+
OnLogExceptionListener onLogExceptionListener,
98100
boolean isDarkMode) {
99101
mWPAndroidGlueCode.attachToContainer(
100102
viewGroup,
@@ -120,6 +122,7 @@ public void attachToContainer(ViewGroup viewGroup, OnMediaLibraryButtonListener
120122
onToggleRedoButtonListener,
121123
onConnectionStatusEventListener,
122124
onBackHandlerEventListener,
125+
onLogExceptionListener,
123126
isDarkMode);
124127
}
125128

libs/editor/src/main/java/org/wordpress/android/editor/gutenberg/GutenbergEditorFragment.java

Lines changed: 44 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,9 @@
3333
import androidx.lifecycle.LiveData;
3434

3535
import com.android.volley.toolbox.ImageLoader;
36+
import com.automattic.android.tracks.crashlogging.JsException;
37+
import com.automattic.android.tracks.crashlogging.JsExceptionCallback;
38+
import com.automattic.android.tracks.crashlogging.JsExceptionStackTraceElement;
3639
import com.facebook.react.bridge.ReadableMap;
3740
import com.facebook.react.bridge.WritableNativeMap;
3841
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
@@ -47,10 +50,10 @@
4750
import org.wordpress.android.editor.EditorThemeUpdateListener;
4851
import org.wordpress.android.editor.LiveTextWatcher;
4952
import org.wordpress.android.editor.R;
50-
import org.wordpress.android.editor.savedinstance.SavedInstanceDatabase;
5153
import org.wordpress.android.editor.WPGutenbergWebViewActivity;
5254
import org.wordpress.android.editor.gutenberg.GutenbergDialogFragment.GutenbergDialogNegativeClickInterface;
5355
import org.wordpress.android.editor.gutenberg.GutenbergDialogFragment.GutenbergDialogPositiveClickInterface;
56+
import org.wordpress.android.editor.savedinstance.SavedInstanceDatabase;
5457
import org.wordpress.android.util.AppLog;
5558
import org.wordpress.android.util.AppLog.T;
5659
import org.wordpress.android.util.DateTimeUtils;
@@ -62,14 +65,16 @@
6265
import org.wordpress.android.util.helpers.MediaFile;
6366
import org.wordpress.android.util.helpers.MediaGallery;
6467
import org.wordpress.aztec.IHistoryListener;
68+
import org.wordpress.mobile.ReactNativeGutenbergBridge.GutenbergBridgeJS2Parent.LogExceptionCallback;
6569
import org.wordpress.mobile.ReactNativeGutenbergBridge.GutenbergEmbedWebViewActivity;
70+
import org.wordpress.mobile.WPAndroidGlue.GutenbergJsException;
6671
import org.wordpress.mobile.WPAndroidGlue.Media;
6772
import org.wordpress.mobile.WPAndroidGlue.MediaOption;
6873
import org.wordpress.mobile.WPAndroidGlue.RequestExecutor;
6974
import org.wordpress.mobile.WPAndroidGlue.ShowSuggestionsUtil;
7075
import org.wordpress.mobile.WPAndroidGlue.UnsupportedBlock;
71-
import org.wordpress.mobile.WPAndroidGlue.WPAndroidGlueCode.OnBlockTypeImpressionsEventListener;
7276
import org.wordpress.mobile.WPAndroidGlue.WPAndroidGlueCode.OnBackHandlerEventListener;
77+
import org.wordpress.mobile.WPAndroidGlue.WPAndroidGlueCode.OnBlockTypeImpressionsEventListener;
7378
import org.wordpress.mobile.WPAndroidGlue.WPAndroidGlueCode.OnConnectionStatusEventListener;
7479
import org.wordpress.mobile.WPAndroidGlue.WPAndroidGlueCode.OnContentInfoReceivedListener;
7580
import org.wordpress.mobile.WPAndroidGlue.WPAndroidGlueCode.OnCustomerSupportOptionsListener;
@@ -80,6 +85,7 @@
8085
import org.wordpress.mobile.WPAndroidGlue.WPAndroidGlueCode.OnGutenbergDidRequestPreviewListener;
8186
import org.wordpress.mobile.WPAndroidGlue.WPAndroidGlueCode.OnGutenbergDidRequestUnsupportedBlockFallbackListener;
8287
import org.wordpress.mobile.WPAndroidGlue.WPAndroidGlueCode.OnGutenbergDidSendButtonPressedActionListener;
88+
import org.wordpress.mobile.WPAndroidGlue.WPAndroidGlueCode.OnLogExceptionListener;
8389
import org.wordpress.mobile.WPAndroidGlue.WPAndroidGlueCode.OnMediaLibraryButtonListener;
8490
import org.wordpress.mobile.WPAndroidGlue.WPAndroidGlueCode.OnReattachMediaUploadQueryListener;
8591
import org.wordpress.mobile.WPAndroidGlue.WPAndroidGlueCode.OnSetFeaturedImageListener;
@@ -88,9 +94,11 @@
8894
import java.util.Date;
8995
import java.util.HashMap;
9096
import java.util.HashSet;
97+
import java.util.List;
9198
import java.util.Map;
9299
import java.util.Set;
93100
import java.util.concurrent.ConcurrentHashMap;
101+
import java.util.stream.Collectors;
94102

95103
import static org.wordpress.mobile.WPAndroidGlue.Media.createRNMediaUsingMimeType;
96104

@@ -517,6 +525,40 @@ public void onGotoCustomerSupportOptions() {
517525
}
518526
},
519527

528+
new OnLogExceptionListener() {
529+
@Override public void onLogException(GutenbergJsException exception,
530+
LogExceptionCallback logExceptionCallback) {
531+
List<JsExceptionStackTraceElement> stackTraceElements = exception.getStackTrace().stream().map(
532+
stackTrace -> {
533+
return new JsExceptionStackTraceElement(
534+
stackTrace.getFileName(),
535+
stackTrace.getLineNumber(),
536+
stackTrace.getColNumber(),
537+
stackTrace.getFunction()
538+
);
539+
}).collect(Collectors.toList());
540+
541+
JsException jsException = new JsException(
542+
exception.getType(),
543+
exception.getMessage(),
544+
stackTraceElements,
545+
exception.getContext(),
546+
exception.getTags(),
547+
exception.isHandled(),
548+
exception.getHandledBy()
549+
);
550+
551+
JsExceptionCallback callback = new JsExceptionCallback() {
552+
@Override
553+
public void onReportSent(boolean success) {
554+
logExceptionCallback.onLogException(success);
555+
}
556+
};
557+
558+
mEditorFragmentListener.onLogJsException(jsException, callback);
559+
}
560+
},
561+
520562
GutenbergUtils.isDarkMode(getActivity()));
521563

522564
// request dependency injection. Do this after setting min/max dimensions

0 commit comments

Comments
 (0)