Skip to content

Commit 46dde0d

Browse files
authoredJul 7, 2022
feat(next/swc): setup native next-swc crash reporter with platform supports (#38221)
This is second attempt to #38076 . Most of changes are identical to previous PR. Main difference is introducing features `native-tls` and `rustls` for the sentry's downstream feature. Few platform targets we build (mostly where we cross compiles) fails to find native openssl for the specified target. For those, we falls back to rustls instead. The only exception is aarch64_windows, neither openssl nor rustls can be compiled straightforwardly, For those platform we bail out and do not init sentry at all. There are nearly 0 users on aarch64_windows anyway. We could try to located target's openssl binary, but the effort required seems not worth enough. Also PR changed `server_name` property to not to include real device hostname to avoid possible PII concerns. ## Bug - [ ] Related issues linked using `fixes #number` - [ ] Integration tests added - [ ] Errors have helpful link attached, see `contributing.md` ## Feature - [ ] Implements an existing feature request or RFC. Make sure the feature request has been accepted for implementation before opening a PR. - [ ] Related issues linked using `fixes #number` - [ ] Integration tests added - [ ] Documentation added - [ ] Telemetry added. In case of a feature if it's used or not. - [ ] Errors have helpful link attached, see `contributing.md` ## Documentation / Examples - [ ] Make sure the linting passes by running `pnpm lint` - [ ] The examples guidelines are followed from [our contributing doc](https://github.com/vercel/next.js/blob/canary/contributing.md#adding-examples)
1 parent 0299f14 commit 46dde0d

File tree

10 files changed

+947
-12
lines changed

10 files changed

+947
-12
lines changed
 

‎.github/workflows/build_test_deploy.yml

+6-6
Original file line numberDiff line numberDiff line change
@@ -1261,7 +1261,7 @@ jobs:
12611261
rustup default "${RUST_TOOLCHAIN}" &&
12621262
rustup target add x86_64-unknown-linux-musl &&
12631263
npm i -g "@napi-rs/cli@${NAPI_CLI_VERSION}" "turbo@${TURBO_VERSION}" && if [ ! -f $(dirname $(which yarn))/pnpm ]; then ln -s $(which yarn) $(dirname $(which yarn))/pnpm;fi &&
1264-
turbo run build-native --cache-dir=".turbo" -- --release --target x86_64-unknown-linux-musl &&
1264+
turbo run build-native --cache-dir=".turbo" -- --release --target x86_64-unknown-linux-musl --cargo-flags=--no-default-features --features sentry_rustls &&
12651265
strip packages/next-swc/native/next-swc.*.node
12661266
- host: macos-latest
12671267
target: 'aarch64-apple-darwin'
@@ -1293,7 +1293,7 @@ jobs:
12931293
sudo apt-get install gcc-arm-linux-gnueabihf g++-arm-linux-gnueabihf -y
12941294
build: |
12951295
npm i -g "@napi-rs/cli@${NAPI_CLI_VERSION}" "turbo@${TURBO_VERSION}" && if [ ! -f $(dirname $(which yarn))/pnpm ]; then ln -s $(which yarn) $(dirname $(which yarn))/pnpm;fi
1296-
turbo run build-native-no-plugin --cache-dir=".turbo" -- --release --target armv7-unknown-linux-gnueabihf
1296+
turbo run build-native-no-plugin --cache-dir=".turbo" -- --release --target armv7-unknown-linux-gnueabihf --cargo-flags=--no-default-features --features sentry_rustls
12971297
arm-linux-gnueabihf-strip packages/next-swc/native/next-swc.*.node
12981298
- host: ubuntu-latest
12991299
target: aarch64-linux-android
@@ -1303,7 +1303,7 @@ jobs:
13031303
export CXX="/usr/local/lib/android/sdk/ndk/21.4.7075529/toolchains/llvm/prebuilt/linux-x86_64/bin/aarch64-linux-android24-clang++"
13041304
export PATH="/usr/local/lib/android/sdk/ndk/21.4.7075529/toolchains/llvm/prebuilt/linux-x86_64/bin:${PATH}"
13051305
npm i -g "@napi-rs/cli@${NAPI_CLI_VERSION}" "turbo@${TURBO_VERSION}" && if [ ! -f $(dirname $(which yarn))/pnpm ]; then ln -s $(which yarn) $(dirname $(which yarn))/pnpm;fi
1306-
turbo run build-native --cache-dir=".turbo" -- --release --target aarch64-linux-android
1306+
turbo run build-native --cache-dir=".turbo" -- --release --target aarch64-linux-android --cargo-flags=--no-default-features --features sentry_rustls
13071307
/usr/local/lib/android/sdk/ndk/21.4.7075529/toolchains/llvm/prebuilt/linux-x86_64/bin/aarch64-linux-android-strip packages/next-swc/native/next-swc.*.node
13081308
- host: ubuntu-latest
13091309
target: armv7-linux-androideabi
@@ -1313,7 +1313,7 @@ jobs:
13131313
export CXX="/usr/local/lib/android/sdk/ndk/21.4.7075529/toolchains/llvm/prebuilt/linux-x86_64/bin/armv7a-linux-androideabi24-clang++"
13141314
export PATH="/usr/local/lib/android/sdk/ndk/21.4.7075529/toolchains/llvm/prebuilt/linux-x86_64/bin:${PATH}"
13151315
npm i -g "@napi-rs/cli@${NAPI_CLI_VERSION}" "turbo@${TURBO_VERSION}" "pnpm@${PNPM_VERSION}"
1316-
turbo run build-native-no-plugin --cache-dir=".turbo" -- --release --target armv7-linux-androideabi
1316+
turbo run build-native-no-plugin --cache-dir=".turbo" -- --release --target armv7-linux-androideabi --cargo-flags=--no-default-features --features sentry_rustls
13171317
/usr/local/lib/android/sdk/ndk/21.4.7075529/toolchains/llvm/prebuilt/linux-x86_64/bin/arm-linux-androideabi-strip packages/next-swc/native/next-swc.*.node
13181318
- host: ubuntu-latest
13191319
target: 'aarch64-unknown-linux-musl'
@@ -1324,13 +1324,13 @@ jobs:
13241324
rustup toolchain install "${RUST_TOOLCHAIN}" &&
13251325
rustup default "${RUST_TOOLCHAIN}" &&
13261326
rustup target add aarch64-unknown-linux-musl &&
1327-
turbo run build-native --cache-dir=".turbo" -- --release --target aarch64-unknown-linux-musl &&
1327+
turbo run build-native --cache-dir=".turbo" -- --release --target aarch64-unknown-linux-musl --cargo-flags=--no-default-features --features sentry_rustls &&
13281328
llvm-strip -x packages/next-swc/native/next-swc.*.node
13291329
- host: windows-latest
13301330
target: 'aarch64-pc-windows-msvc'
13311331
build: |
13321332
npm i -g "@napi-rs/cli@${NAPI_CLI_VERSION}" "turbo@${TURBO_VERSION}" "pnpm@${PNPM_VERSION}"
1333-
turbo run build-native-no-plugin --cache-dir=".turbo" -- --release --target aarch64-pc-windows-msvc
1333+
turbo run build-native-no-plugin --cache-dir=".turbo" -- --release --target aarch64-pc-windows-msvc --cargo-flags=--no-default-features
13341334
if: ${{ needs.build.outputs.isRelease == 'true' }}
13351335
needs: build
13361336
name: stable - ${{ matrix.settings.target }} - node@16

‎packages/next-swc/Cargo.lock

+788-3
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎packages/next-swc/crates/napi/Cargo.toml

+17
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ publish = false
88
crate-type = ["cdylib", "rlib"]
99

1010
[features]
11+
default = ["sentry_native_tls"]
1112
# Instead of enabling all the plugin-related features by default, make it explicitly specified
1213
# when build (i.e napi --build --features plugin), same for the wasm as well.
1314
# this is due to some of transitive dependencies have features cannot be enabled at the same time
@@ -19,6 +20,8 @@ plugin = [
1920
"wasmer-wasi/default",
2021
"next-swc/plugin"
2122
]
23+
sentry_native_tls = ["_sentry_native_tls"]
24+
sentry_rustls = ["_sentry_rustls"]
2225

2326
[dependencies]
2427
anyhow = "1.0"
@@ -44,6 +47,20 @@ tracing-subscriber = "0.3.9"
4447
tracing-chrome = "0.5.0"
4548
wasmer = { version = "2.3.0", optional = true, default-features = false }
4649
wasmer-wasi = { version = "2.3.0", optional = true, default-features = false }
50+
# There are few build targets we can't use native-tls which default features rely on,
51+
# allow to specify alternative (rustls) instead via features.
52+
# Note to opt in rustls default-features should be disabled
53+
# (--no-default-features --features sentry_rustls)
54+
_sentry_native_tls = { package = "sentry", version = "0.27.0", optional = true }
55+
_sentry_rustls = { package = "sentry", version = "0.27.0", default-features = false, features = [
56+
"backtrace",
57+
"contexts",
58+
"panic",
59+
"rustls",
60+
"reqwest"
61+
], optional = true }
4762

4863
[build-dependencies]
4964
napi-build = "1"
65+
serde = "1"
66+
serde_json = "1"

‎packages/next-swc/crates/napi/build.rs

+19
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@ use std::{
88
extern crate napi_build;
99

1010
fn main() {
11+
// Emit current platform's target-triple into a text file to create static const
12+
// in util.rs
1113
let out_dir = env::var("OUT_DIR").expect("Outdir should exist");
1214
let dest_path = Path::new(&out_dir).join("triple.txt");
1315
let mut f =
@@ -19,5 +21,22 @@ fn main() {
1921
)
2022
.expect("Failed to write target triple text");
2123

24+
// Emit current package.json's version field into a text file to create static
25+
// const in util.rs This is being used to set correct release version for
26+
// the sentry's crash reporter.
27+
let pkg_file =
28+
File::open(Path::new("../../package.json")).expect("Should able to open package.json");
29+
let json: serde_json::Value = serde_json::from_reader(pkg_file).unwrap();
30+
let pkg_version_dest_path = Path::new(&out_dir).join("package.txt");
31+
let mut package_version_writer = BufWriter::new(
32+
File::create(&pkg_version_dest_path).expect("Failed to create package version text"),
33+
);
34+
write!(
35+
package_version_writer,
36+
"{}",
37+
json["version"].as_str().unwrap()
38+
)
39+
.expect("Failed to write target triple text");
40+
2241
napi_build::setup();
2342
}

‎packages/next-swc/crates/napi/src/lib.rs

+3
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,9 @@ fn init(mut exports: JsObject) -> napi::Result<()> {
7979
)?;
8080
exports.create_named_method("teardownTraceSubscriber", util::teardown_trace_subscriber)?;
8181

82+
exports.create_named_method("initCrashReporter", util::init_crash_reporter)?;
83+
exports.create_named_method("teardownCrashReporter", util::teardown_crash_reporter)?;
84+
8285
Ok(())
8386
}
8487

‎packages/next-swc/crates/napi/src/util.rs

+72-1
Original file line numberDiff line numberDiff line change
@@ -29,11 +29,13 @@ DEALINGS IN THE SOFTWARE.
2929
use anyhow::{anyhow, Context, Error};
3030
use napi::{CallContext, Env, JsBuffer, JsExternal, JsString, JsUndefined, JsUnknown, Status};
3131
use serde::de::DeserializeOwned;
32-
use std::{any::type_name, cell::RefCell, convert::TryFrom, path::PathBuf};
32+
use std::{any::type_name, cell::RefCell, convert::TryFrom, env, path::PathBuf};
3333
use tracing_chrome::{ChromeLayerBuilder, FlushGuard};
3434
use tracing_subscriber::{filter, prelude::*, util::SubscriberInitExt, Layer};
3535

3636
static TARGET_TRIPLE: &str = include_str!(concat!(env!("OUT_DIR"), "/triple.txt"));
37+
static PACKAGE_VERSION: &str = include_str!(concat!(env!("OUT_DIR"), "/package.txt"));
38+
3739
#[contextless_function]
3840
pub fn get_target_triple(env: Env) -> napi::ContextlessResult<JsString> {
3941
env.create_string(TARGET_TRIPLE).map(Some)
@@ -144,3 +146,72 @@ pub fn teardown_trace_subscriber(cx: CallContext) -> napi::Result<JsUndefined> {
144146
}
145147
cx.env.get_undefined()
146148
}
149+
150+
/// Initialize crash reporter to collect unexpected native next-swc crashes.
151+
#[js_function(1)]
152+
pub fn init_crash_reporter(cx: CallContext) -> napi::Result<JsExternal> {
153+
// Attempts to follow https://nextjs.org/telemetry's debug behavior.
154+
// However, this is techinically not identical to the behavior of the telemetry
155+
// itself as sentry's debug option does not provides full payuload output.
156+
let debug = env::var("NEXT_TELEMETRY_DEBUG").map_or_else(|_| false, |v| v == "1");
157+
158+
#[cfg(not(all(target_os = "windows", target_arch = "aarch64")))]
159+
let guard = {
160+
#[cfg(feature = "sentry_native_tls")]
161+
use _sentry_native_tls::{init, types::Dsn, ClientOptions};
162+
#[cfg(feature = "sentry_rustls")]
163+
use _sentry_rustls::{init, types::Dsn, ClientOptions};
164+
use std::{borrow::Cow, str::FromStr};
165+
166+
let dsn = if debug {
167+
None
168+
} else {
169+
Dsn::from_str(
170+
"https://7619e5990e3045cda747e50e6ed087a7@o205439.ingest.sentry.io/6528434",
171+
)
172+
.ok()
173+
};
174+
175+
Some(init(ClientOptions {
176+
release: Some(Cow::Borrowed(PACKAGE_VERSION)),
177+
dsn,
178+
debug,
179+
// server_name includes device host name, which _can_ be considered as PII depends on
180+
// the machine name.
181+
server_name: Some(Cow::Borrowed("[REDACTED]")),
182+
..Default::default()
183+
}))
184+
};
185+
186+
// aarch64_msvc neither compiles native-tls nor rustls for sentry transport
187+
#[cfg(all(target_os = "windows", target_arch = "aarch64"))]
188+
let guard: Option<usize> = None;
189+
190+
let guard_cell = RefCell::new(guard);
191+
cx.env.create_external(guard_cell, None)
192+
}
193+
194+
/// Trying to drop crash reporter guard if exists. This is the way to hold
195+
/// guards to not to be dropped immediately after crash reporter is initialized
196+
/// in napi context.
197+
#[js_function(1)]
198+
pub fn teardown_crash_reporter(cx: CallContext) -> napi::Result<JsUndefined> {
199+
#[cfg(not(all(target_os = "windows", target_arch = "aarch64")))]
200+
{
201+
#[cfg(feature = "sentry_native_tls")]
202+
use _sentry_native_tls::ClientInitGuard;
203+
#[cfg(feature = "sentry_rustls")]
204+
use _sentry_rustls::ClientInitGuard;
205+
206+
let guard_external = cx.get::<JsExternal>(0)?;
207+
let guard_cell = &*cx
208+
.env
209+
.get_value_external::<RefCell<Option<ClientInitGuard>>>(&guard_external)?;
210+
211+
if let Some(guard) = guard_cell.take() {
212+
drop(guard);
213+
}
214+
}
215+
216+
cx.env.get_undefined()
217+
}

‎packages/next/build/index.ts

+6-1
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,11 @@ import { TelemetryPlugin } from './webpack/plugins/telemetry-plugin'
104104
import { MiddlewareManifest } from './webpack/plugins/middleware-plugin'
105105
import { recursiveCopy } from '../lib/recursive-copy'
106106
import { recursiveReadDir } from '../lib/recursive-readdir'
107-
import { lockfilePatchPromise, teardownTraceSubscriber } from './swc'
107+
import {
108+
lockfilePatchPromise,
109+
teardownTraceSubscriber,
110+
teardownCrashReporter,
111+
} from './swc'
108112
import { injectedClientEntries } from './webpack/plugins/client-entry-plugin'
109113
import { getNamedRouteRegex } from '../shared/lib/router/utils/route-regex'
110114
import { flatReaddir } from '../lib/flat-readdir'
@@ -2315,6 +2319,7 @@ export default async function build(
23152319
// Ensure all traces are flushed before finishing the command
23162320
await flushAllTraces()
23172321
teardownTraceSubscriber()
2322+
teardownCrashReporter()
23182323
}
23192324
}
23202325

‎packages/next/build/output/store.ts

+4-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import createStore from 'next/dist/compiled/unistore'
22
import stripAnsi from 'next/dist/compiled/strip-ansi'
33
import { flushAllTraces } from '../../trace'
4-
import { teardownTraceSubscriber } from '../swc'
4+
import { teardownCrashReporter, teardownTraceSubscriber } from '../swc'
55
import * as Log from './log'
66

77
export type OutputState =
@@ -92,6 +92,7 @@ store.subscribe((state) => {
9292
// Ensure traces are flushed after each compile in development mode
9393
flushAllTraces()
9494
teardownTraceSubscriber()
95+
teardownCrashReporter()
9596
return
9697
}
9798

@@ -119,6 +120,7 @@ store.subscribe((state) => {
119120
// Ensure traces are flushed after each compile in development mode
120121
flushAllTraces()
121122
teardownTraceSubscriber()
123+
teardownCrashReporter()
122124
return
123125
}
124126

@@ -135,4 +137,5 @@ store.subscribe((state) => {
135137
// Ensure traces are flushed after each compile in development mode
136138
flushAllTraces()
137139
teardownTraceSubscriber()
140+
teardownCrashReporter()
138141
})

‎packages/next/build/swc/index.d.ts

+1
Original file line numberDiff line numberDiff line change
@@ -8,4 +8,5 @@ export function parse(src: string, options: any): any
88
export const lockfilePatchPromise: { cur?: Promise<void> }
99
export function initCustomTraceSubscriber(traceFileName?: string): void
1010
export function teardownTraceSubscriber(): void
11+
export function teardownCrashReporter(): void
1112
export function loadBindings(): Promise<void>

‎packages/next/build/swc/index.js

+31
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import { eventSwcLoadFailure } from '../../telemetry/events/swc-load-failure'
88
import { patchIncorrectLockfile } from '../../lib/patch-incorrect-lockfile'
99
import { downloadWasmSwc } from '../../lib/download-wasm-swc'
1010
import { version as nextVersion } from 'next/package.json'
11+
import { Telemetry } from '../../telemetry/storage'
1112

1213
const ArchName = arch()
1314
const PlatformName = platform()
@@ -18,6 +19,7 @@ let wasmBindings
1819
let downloadWasmPromise
1920
let pendingBindings
2021
let swcTraceFlushGuard
22+
let swcCrashReporterFlushGuard
2123
export const lockfilePatchPromise = {}
2224

2325
export async function loadBindings() {
@@ -215,6 +217,17 @@ function loadNative() {
215217
}
216218

217219
if (bindings) {
220+
// Initialize crash reporter, as earliest as possible from any point of import.
221+
// The first-time import to next-swc is not predicatble in the import tree of next.js, which makes
222+
// we can't rely on explicit manual initialization as similar to trace reporter.
223+
if (!swcCrashReporterFlushGuard) {
224+
// Crash reports in next-swc should be treated in the same way we treat telemetry to opt out.
225+
let telemetry = new Telemetry({ distDir: process.cwd() })
226+
if (telemetry.isEnabled) {
227+
swcCrashReporterFlushGuard = bindings.initCrashReporter?.()
228+
}
229+
}
230+
218231
nativeBindings = {
219232
isWasm: false,
220233
transform(src, options) {
@@ -278,6 +291,7 @@ function loadNative() {
278291
getTargetTriple: bindings.getTargetTriple,
279292
initCustomTraceSubscriber: bindings.initCustomTraceSubscriber,
280293
teardownTraceSubscriber: bindings.teardownTraceSubscriber,
294+
teardownCrashReporter: bindings.teardownCrashReporter,
281295
}
282296
return nativeBindings
283297
}
@@ -377,3 +391,20 @@ export const teardownTraceSubscriber = (() => {
377391
}
378392
}
379393
})()
394+
395+
export const teardownCrashReporter = (() => {
396+
let flushed = false
397+
return () => {
398+
if (!flushed) {
399+
flushed = true
400+
try {
401+
let bindings = loadNative()
402+
if (swcCrashReporterFlushGuard) {
403+
bindings.teardownCrashReporter(swcCrashReporterFlushGuard)
404+
}
405+
} catch (e) {
406+
// Suppress exceptions, this fn allows to fail to load native bindings
407+
}
408+
}
409+
}
410+
})()

0 commit comments

Comments
 (0)
Please sign in to comment.