Masalah Un-symbolicated Stack Trace di Production
Saat aplikasi React Native berjalan di environment production Android, penelusuran akar masalah (root cause analysis) dari crash log sering terhambat oleh dua lapisan transformasi kode: optimasi bytecode Hermes pada layer JavaScript dan obfuskasi ProGuard/R8 pada layer native Android.
Ketika crash terjadi pada runtime JavaScript, Sentry hanya menerima referensi alamat memori bytecode (misalnya bytecode offset 0x0001a2b4) atau satu baris bundle terkompresi (index.android.bundle:1:24510) alih-alih file sumber TypeScript asli beserta nomor barisnya. Di sisi lain, pengecualian native Android menampilkan stack trace dengan nama kelas dan method terpotong, seperti a.b.c.d(SourceFile:1). Tanpa artefak pemetaan (mapping files), proses deobfuskasi dan simbolikasi log menjadi mustahil dilakukan.
Arsitektur Source Map Hermes: Komposisi Dua Tahap
Hermes tidak mengeksekusi plain JavaScript secara langsung di runtime. Metro bundler terlebih dahulu mengompilasi kode TypeScript/ES6 menjadi JavaScript bundle tunggal, kemudian Hermes Compiler (hermesc) mengompilasi JavaScript bundle tersebut menjadi Hermes Bytecode (HBC). Proses ini menghasilkan dua map terpisah:
- Packager Source Map: Memetakan kode sumber TypeScript/JavaScript asli ke plain JavaScript bundle Metro.
- Hermes Compiler Source Map: Memetakan plain JavaScript bundle Metro ke bytecode Hermes.
Agar Sentry dapat melacak error dari bytecode Hermes kembali ke file TypeScript orisinal, kedua file tersebut harus digabungkan menggunakan script bawaan React Native: compose-source-maps.js.
# Alur komposisi source map manual/headless
node node_modules/react-native/scripts/compose-source-maps.js \
/tmp/index.android.bundle.packager.map \
/tmp/index.android.bundle.hbc.map \
-o /tmp/index.android.bundle.mapKonfigurasi Pipeline GitHub Actions
Berikut adalah workflow GitHub Actions yang mengompilasi aplikasi Android release, menghasilkan Hermes source maps komposit, dan mengunggah artefak tersebut bersama file ProGuard mapping.txt ke Sentry.
name: Android Release & Sentry Upload
on:
push:
tags:
- 'v*'
jobs:
build-android:
runs-on: ubuntu-latest
env:
SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }}
SENTRY_ORG: ${{ secrets.SENTRY_ORG }}
SENTRY_PROJECT: ${{ secrets.SENTRY_PROJECT }}
steps:
- name: Checkout Repository
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: 20
cache: 'yarn'
- name: Setup Java JDK
uses: actions/setup-java@v4
with:
distribution: 'zulu'
java-version: 17
- name: Install Dependencies
run: yarn install --frozen-lockfile
- name: Install Sentry CLI
run: curl -sL https://sentry.io/get-cli/ | bash
- name: Set Release Version Name
run: |
VERSION_NAME=$(node -p "require('./package.json').version")
echo "RELEASE_VERSION=my.app.package@${VERSION_NAME}" >> $GITHUB_ENV
echo "DIST_VERSION=${{ github.run_number }}" >> $GITHUB_ENV
- name: Build Android Bundle (AAB) & Mapping
working-directory: android
run: ./gradlew bundleRelease --no-daemon
- name: Upload ProGuard / R8 Mapping to Sentry
run: |
sentry-cli upload-proguard \
--org "$SENTRY_ORG" \
--project "$SENTRY_PROJECT" \
--app-id "my.app.package" \
--version "$RELEASE_VERSION" \
android/app/build/outputs/mapping/release/mapping.txt
- name: Bundle JS and Hermes Source Maps
run: |
mkdir -p build/sentry
# 1. Generate Metro JS Bundle & Packager Map
npx react-native bundle \
--platform android \
--dev false \
--entry-file index.js \
--bundle-output build/sentry/index.android.bundle \
--sourcemap-output build/sentry/index.android.bundle.packager.map
# 2. Compile ke Hermes Bytecode dan generate Hermes Map
HERMESC_BIN="node_modules/react-native/sdks/hermesc/linux64-bin/hermesc"
$HERMESC_BIN -emit-binary \
-out build/sentry/index.android.bundle.hbc \
build/sentry/index.android.bundle \
-O -output-source-map
# 3. Gabungkan kedua Source Map
node node_modules/react-native/scripts/compose-source-maps.js \
build/sentry/index.android.bundle.packager.map \
build/sentry/index.android.bundle.hbc.map \
-o build/sentry/index.android.bundle.map
- name: Create Sentry Release & Upload Source Maps
run: |
sentry-cli releases new "$RELEASE_VERSION"
sentry-cli releases set-commits "$RELEASE_VERSION" --auto
sentry-cli sourcemaps upload \
--org "$SENTRY_ORG" \
--project "$SENTRY_PROJECT" \
--release "$RELEASE_VERSION" \
--dist "$DIST_VERSION" \
--strip-prefix "$PWD" \
build/sentry/index.android.bundle build/sentry/index.android.bundle.map
sentry-cli releases finalize "$RELEASE_VERSION"Opsi Alternatif: Sentry Android Gradle Plugin
Penggunaan script manual pada pipeline di atas memberikan kontrol penuh dan dependensi minimal terhadap plugin native. Namun, ada opsi yang lebih ringkas: menggunakan @sentry/react-native yang terintegrasi dengan Gradle plugin.
Alternatif Ringkas: Aktifkan
sentry-android-gradle-plugindiandroid/app/build.gradle. Plugin ini secara otomatis mengaitkan taskbundleReleaseJsAndAssetsdanminifyReleaseWithR8untuk mengunggah ProGuard dan Hermes map langsung selama proses kompilasi Gradle.
Gunakan pendekatan skrip manual jika Anda menggunakan custom build pipeline, monorepo dengan isolated build container, atau memisahkan tahapan kompilasi native dan JavaScript bundle untuk efisiensi caching CI.
Validasi Simbolikasi Crash Log
Untuk memastikan setup berhasil sebelum merilis ke publik, lakukan pengujian crash sintetis di sisi TypeScript dan Java/Kotlin:
// Test Crash JavaScript (App.tsx)
import * as Sentry from '@sentry/react-native';
export function CrashButton() {
return (
<Button
title="Trigger JS Crash"
onPress={() => {
throw new Error('Test Hermes Symbolication Error');
}}
/>
);
}Periksa hasil event di dashboard Sentry:
- Buka menu Issues > pilih event error yang ditrigger.
- Periksa tab Tags: pastikan
releasedandistsesuai dengan variabel CI yang dikirimkan. - Periksa stack trace: jika simbolikasi berhasil, Sentry menampilkan referensi file
App.tsx:8dengan cuplikan baris kode asli, bukanindex.android.bundleatau representasi bytecode mentah. - Buka Project Settings > Source Maps untuk memverifikasi bahwa file
index.android.bundle.maptelah terindeks dengan status valid. - Buka Project Settings > ProGuard untuk memastikan UUID mapping file ProGuard terdaftar.
Komentar
0 komentar
Masuk ke akun kamu untuk ikut berkomentar.
Belum ada komentar
Jadilah yang pertama ikut berdiskusi!