Skip to content

Commit

Permalink
Merge pull request #20359 from wordpress-mobile/rnmobile/add/log-exce…
Browse files Browse the repository at this point in the history
…ption-to-crash-logging

[Gutenberg] Add error boundary components and exception logging
  • Loading branch information
fluiddot authored Mar 12, 2024
2 parents 5adbf32 + eadc097 commit aeb40f8
Show file tree
Hide file tree
Showing 10 changed files with 121 additions and 4 deletions.
1 change: 1 addition & 0 deletions Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ gem 'nokogiri'

### Fastlane Plugins

gem 'fastlane-plugin-sentry'
gem 'fastlane-plugin-wpmreleasetoolkit', '~> 9.2'
# gem 'fastlane-plugin-wpmreleasetoolkit', path: '../../release-toolkit'
# gem 'fastlane-plugin-wpmreleasetoolkit', git: 'https://github.com/wordpress-mobile/release-toolkit', branch: ''
Expand Down
3 changes: 3 additions & 0 deletions Gemfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -174,6 +174,8 @@ GEM
xcodeproj (>= 1.13.0, < 2.0.0)
xcpretty (~> 0.3.0)
xcpretty-travis-formatter (>= 0.0.3)
fastlane-plugin-sentry (1.19.0)
os (~> 1.1, >= 1.1.4)
fastlane-plugin-wpmreleasetoolkit (9.4.0)
activesupport (>= 6.1.7.1)
buildkit (~> 1.5)
Expand Down Expand Up @@ -355,6 +357,7 @@ PLATFORMS
DEPENDENCIES
danger-dangermattic (~> 1.0)
fastlane (~> 2)
fastlane-plugin-sentry
fastlane-plugin-wpmreleasetoolkit (~> 9.2)
nokogiri
rmagick (~> 4.1)
Expand Down
20 changes: 20 additions & 0 deletions WordPress/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -713,3 +713,23 @@ if (project.hasProperty("debugStoreFile")) {
}
}
}

// Copy React Native JavaScript bundle and source map so they can be upload it to the Crash logging
// service during the build process.
android {
applicationVariants.all { variant ->
variant.mergeAssetsProvider.configure {
doLast {
// Copy bundle and source map files
copy {
from(outputDir)
into("${buildDir}/react-native-bundle-source-map")
include("*.bundle", "*.bundle.map")
}

// Delete source maps
delete(fileTree(dir: outputDir, includes: ['**/*.bundle.map']))
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,8 @@
import androidx.viewpager.widget.ViewPager;

import com.automattic.android.tracks.crashlogging.CrashLogging;
import com.automattic.android.tracks.crashlogging.JsException;
import com.automattic.android.tracks.crashlogging.JsExceptionCallback;
import com.google.android.material.appbar.AppBarLayout;
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
import com.google.android.material.snackbar.Snackbar;
Expand Down Expand Up @@ -3933,4 +3935,8 @@ public LiveData<DialogVisibility> getSavingInProgressDialogVisibility() {
@Nullable private SavedInstanceDatabase getDB() {
return SavedInstanceDatabase.Companion.getDatabase(WordPress.getContext());
}

@Override public void onLogJsException(JsException exception, JsExceptionCallback onExceptionSend) {
mCrashLogging.sendJavaScriptReport(exception, onExceptionSend);
}
}
4 changes: 2 additions & 2 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,8 @@ ext {
// libs
automatticAboutVersion = '1.4.0'
automatticRestVersion = '1.0.8'
automatticTracksVersion = '3.4.0'
gutenbergMobileVersion = 'v1.115.0-alpha3'
automatticTracksVersion = '3.5.0'
gutenbergMobileVersion = 'v1.115.0-alpha5'
wordPressAztecVersion = 'v2.0'
wordPressFluxCVersion = 'trunk-b9ecc708dde74d6cc95aeab42e56fb8067640039'
wordPressLoginVersion = '1.14.1'
Expand Down
35 changes: 35 additions & 0 deletions fastlane/lanes/build.rb
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@
build_bundle(app: app, version_name: version_name, build_code: current_build_code, flavor: 'Vanilla', buildType: 'Release')

upload_build_to_play_store(app: app, version_name: version_name, track: 'production')
upload_gutenberg_sourcemaps(app: app, release_version: version_name)

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

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

create_gh_release(app: app, version_name: version_name, prerelease: true) if options[:create_release]
end
Expand Down Expand Up @@ -217,6 +219,7 @@
)

upload_prototype_build(product: 'WordPress', version_name: version_name)
upload_gutenberg_sourcemaps(app: 'Wordpress', release_version: version_name)
end

#####################################################################################
Expand All @@ -240,6 +243,7 @@
)

upload_prototype_build(product: 'Jetpack', version_name: version_name)
upload_gutenberg_sourcemaps(app: 'Jetpack', release_version: version_name)
end

#####################################################################################
Expand Down Expand Up @@ -359,4 +363,35 @@ def generate_prototype_build_number
"#{branch}-#{commit}"
end
end

# Uploads the React Native JavaScript bundle and source map files.
# These files are provided by the Gutenberg Mobile library.
#
# @param [String] app App name, e.g. 'WordPress' or 'Jetpack'.
# @param [String] release_version Release version name to attach the files to in Sentry.
#
def upload_gutenberg_sourcemaps(app:, release_version:)
# Load Sentry properties
sentry_path = File.join(PROJECT_ROOT_FOLDER, 'WordPress', 'src', app.downcase, 'sentry.properties')
sentry_properties = JavaProperties.load(sentry_path)
sentry_token = sentry_properties[:'auth.token']
project_slug = sentry_properties[:'defaults.project']
org_slug = sentry_properties[:'defaults.org']

# Bundle and source map files are copied to a specific folder as part of the build process.
bundle_source_map_path = File.join(PROJECT_ROOT_FOLDER, 'WordPress', 'build', 'react-native-bundle-source-map')

sentry_upload_sourcemap(
auth_token: sentry_token,
org_slug: org_slug,
project_slug: project_slug,
version: release_version,
dist: current_build_code,
# When the React native bundle is generated, the source map file references include the local machine path;
# With the `rewrite` and `strip_common_prefix` options, Sentry automatically strips this part.
rewrite: true,
strip_common_prefix: true,
sourcemap: bundle_source_map_path
)
end
end
3 changes: 3 additions & 0 deletions libs/editor/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ repositories {
includeGroup "org.wordpress.aztec"
includeGroup "org.wordpress.gutenberg-mobile"
includeGroupByRegex "org.wordpress.react-native-libraries.*"
includeGroup "com.automattic"
includeGroup "com.automattic.tracks"
}
}
maven {
Expand Down Expand Up @@ -104,6 +106,7 @@ dependencies {
implementation "com.google.android.material:material:$googleMaterialVersion"
implementation "com.android.volley:volley:$androidVolleyVersion"
implementation "com.google.code.gson:gson:$googleGsonVersion"
implementation "com.automattic:Automattic-Tracks-Android:$automatticTracksVersion"

lintChecks "org.wordpress:lint:$wordPressLintVersion"
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@
import androidx.lifecycle.LiveData;

import com.android.volley.toolbox.ImageLoader;
import com.automattic.android.tracks.crashlogging.JsException;
import com.automattic.android.tracks.crashlogging.JsExceptionCallback;

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

void onBackHandlerButton();

void onLogJsException(JsException jsException, JsExceptionCallback onSendJsException);
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
import org.wordpress.mobile.WPAndroidGlue.WPAndroidGlueCode.OnGutenbergDidRequestUnsupportedBlockFallbackListener;
import org.wordpress.mobile.WPAndroidGlue.WPAndroidGlueCode.OnGutenbergDidSendButtonPressedActionListener;
import org.wordpress.mobile.WPAndroidGlue.WPAndroidGlueCode.OnImageFullscreenPreviewListener;
import org.wordpress.mobile.WPAndroidGlue.WPAndroidGlueCode.OnLogExceptionListener;
import org.wordpress.mobile.WPAndroidGlue.WPAndroidGlueCode.OnReattachMediaUploadQueryListener;
import org.wordpress.mobile.WPAndroidGlue.WPAndroidGlueCode.OnFocalPointPickerTooltipShownEventListener;
import org.wordpress.mobile.WPAndroidGlue.WPAndroidGlueCode.OnMediaEditorListener;
Expand Down Expand Up @@ -95,6 +96,7 @@ public void attachToContainer(ViewGroup viewGroup, OnMediaLibraryButtonListener
OnToggleRedoButtonListener onToggleRedoButtonListener,
OnConnectionStatusEventListener onConnectionStatusEventListener,
OnBackHandlerEventListener onBackHandlerEventListener,
OnLogExceptionListener onLogExceptionListener,
boolean isDarkMode) {
mWPAndroidGlueCode.attachToContainer(
viewGroup,
Expand All @@ -120,6 +122,7 @@ public void attachToContainer(ViewGroup viewGroup, OnMediaLibraryButtonListener
onToggleRedoButtonListener,
onConnectionStatusEventListener,
onBackHandlerEventListener,
onLogExceptionListener,
isDarkMode);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,9 @@
import androidx.lifecycle.LiveData;

import com.android.volley.toolbox.ImageLoader;
import com.automattic.android.tracks.crashlogging.JsException;
import com.automattic.android.tracks.crashlogging.JsExceptionCallback;
import com.automattic.android.tracks.crashlogging.JsExceptionStackTraceElement;
import com.facebook.react.bridge.ReadableMap;
import com.facebook.react.bridge.WritableNativeMap;
import com.google.android.material.dialog.MaterialAlertDialogBuilder;
Expand All @@ -47,10 +50,10 @@
import org.wordpress.android.editor.EditorThemeUpdateListener;
import org.wordpress.android.editor.LiveTextWatcher;
import org.wordpress.android.editor.R;
import org.wordpress.android.editor.savedinstance.SavedInstanceDatabase;
import org.wordpress.android.editor.WPGutenbergWebViewActivity;
import org.wordpress.android.editor.gutenberg.GutenbergDialogFragment.GutenbergDialogNegativeClickInterface;
import org.wordpress.android.editor.gutenberg.GutenbergDialogFragment.GutenbergDialogPositiveClickInterface;
import org.wordpress.android.editor.savedinstance.SavedInstanceDatabase;
import org.wordpress.android.util.AppLog;
import org.wordpress.android.util.AppLog.T;
import org.wordpress.android.util.DateTimeUtils;
Expand All @@ -62,14 +65,16 @@
import org.wordpress.android.util.helpers.MediaFile;
import org.wordpress.android.util.helpers.MediaGallery;
import org.wordpress.aztec.IHistoryListener;
import org.wordpress.mobile.ReactNativeGutenbergBridge.GutenbergBridgeJS2Parent.LogExceptionCallback;
import org.wordpress.mobile.ReactNativeGutenbergBridge.GutenbergEmbedWebViewActivity;
import org.wordpress.mobile.WPAndroidGlue.GutenbergJsException;
import org.wordpress.mobile.WPAndroidGlue.Media;
import org.wordpress.mobile.WPAndroidGlue.MediaOption;
import org.wordpress.mobile.WPAndroidGlue.RequestExecutor;
import org.wordpress.mobile.WPAndroidGlue.ShowSuggestionsUtil;
import org.wordpress.mobile.WPAndroidGlue.UnsupportedBlock;
import org.wordpress.mobile.WPAndroidGlue.WPAndroidGlueCode.OnBlockTypeImpressionsEventListener;
import org.wordpress.mobile.WPAndroidGlue.WPAndroidGlueCode.OnBackHandlerEventListener;
import org.wordpress.mobile.WPAndroidGlue.WPAndroidGlueCode.OnBlockTypeImpressionsEventListener;
import org.wordpress.mobile.WPAndroidGlue.WPAndroidGlueCode.OnConnectionStatusEventListener;
import org.wordpress.mobile.WPAndroidGlue.WPAndroidGlueCode.OnContentInfoReceivedListener;
import org.wordpress.mobile.WPAndroidGlue.WPAndroidGlueCode.OnCustomerSupportOptionsListener;
Expand All @@ -80,6 +85,7 @@
import org.wordpress.mobile.WPAndroidGlue.WPAndroidGlueCode.OnGutenbergDidRequestPreviewListener;
import org.wordpress.mobile.WPAndroidGlue.WPAndroidGlueCode.OnGutenbergDidRequestUnsupportedBlockFallbackListener;
import org.wordpress.mobile.WPAndroidGlue.WPAndroidGlueCode.OnGutenbergDidSendButtonPressedActionListener;
import org.wordpress.mobile.WPAndroidGlue.WPAndroidGlueCode.OnLogExceptionListener;
import org.wordpress.mobile.WPAndroidGlue.WPAndroidGlueCode.OnMediaLibraryButtonListener;
import org.wordpress.mobile.WPAndroidGlue.WPAndroidGlueCode.OnReattachMediaUploadQueryListener;
import org.wordpress.mobile.WPAndroidGlue.WPAndroidGlueCode.OnSetFeaturedImageListener;
Expand All @@ -88,9 +94,11 @@
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;

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

Expand Down Expand Up @@ -517,6 +525,40 @@ public void onGotoCustomerSupportOptions() {
}
},

new OnLogExceptionListener() {
@Override public void onLogException(GutenbergJsException exception,
LogExceptionCallback logExceptionCallback) {
List<JsExceptionStackTraceElement> stackTraceElements = exception.getStackTrace().stream().map(
stackTrace -> {
return new JsExceptionStackTraceElement(
stackTrace.getFileName(),
stackTrace.getLineNumber(),
stackTrace.getColNumber(),
stackTrace.getFunction()
);
}).collect(Collectors.toList());

JsException jsException = new JsException(
exception.getType(),
exception.getMessage(),
stackTraceElements,
exception.getContext(),
exception.getTags(),
exception.isHandled(),
exception.getHandledBy()
);

JsExceptionCallback callback = new JsExceptionCallback() {
@Override
public void onReportSent(boolean success) {
logExceptionCallback.onLogException(success);
}
};

mEditorFragmentListener.onLogJsException(jsException, callback);
}
},

GutenbergUtils.isDarkMode(getActivity()));

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

0 comments on commit aeb40f8

Please sign in to comment.