feat(next/swc): setup native next-swc crash reporter with platform supports (#38221)
This is second attempt to https://github.com/vercel/next.js/pull/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)
This commit is contained in:
parent
0299f14a7e
commit
46dde0dd4a
10 changed files with 947 additions and 12 deletions
12
.github/workflows/build_test_deploy.yml
vendored
12
.github/workflows/build_test_deploy.yml
vendored
|
@ -1261,7 +1261,7 @@ jobs:
|
|||
rustup default "${RUST_TOOLCHAIN}" &&
|
||||
rustup target add x86_64-unknown-linux-musl &&
|
||||
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 &&
|
||||
turbo run build-native --cache-dir=".turbo" -- --release --target x86_64-unknown-linux-musl &&
|
||||
turbo run build-native --cache-dir=".turbo" -- --release --target x86_64-unknown-linux-musl --cargo-flags=--no-default-features --features sentry_rustls &&
|
||||
strip packages/next-swc/native/next-swc.*.node
|
||||
- host: macos-latest
|
||||
target: 'aarch64-apple-darwin'
|
||||
|
@ -1293,7 +1293,7 @@ jobs:
|
|||
sudo apt-get install gcc-arm-linux-gnueabihf g++-arm-linux-gnueabihf -y
|
||||
build: |
|
||||
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
|
||||
turbo run build-native-no-plugin --cache-dir=".turbo" -- --release --target armv7-unknown-linux-gnueabihf
|
||||
turbo run build-native-no-plugin --cache-dir=".turbo" -- --release --target armv7-unknown-linux-gnueabihf --cargo-flags=--no-default-features --features sentry_rustls
|
||||
arm-linux-gnueabihf-strip packages/next-swc/native/next-swc.*.node
|
||||
- host: ubuntu-latest
|
||||
target: aarch64-linux-android
|
||||
|
@ -1303,7 +1303,7 @@ jobs:
|
|||
export CXX="/usr/local/lib/android/sdk/ndk/21.4.7075529/toolchains/llvm/prebuilt/linux-x86_64/bin/aarch64-linux-android24-clang++"
|
||||
export PATH="/usr/local/lib/android/sdk/ndk/21.4.7075529/toolchains/llvm/prebuilt/linux-x86_64/bin:${PATH}"
|
||||
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
|
||||
turbo run build-native --cache-dir=".turbo" -- --release --target aarch64-linux-android
|
||||
turbo run build-native --cache-dir=".turbo" -- --release --target aarch64-linux-android --cargo-flags=--no-default-features --features sentry_rustls
|
||||
/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
|
||||
- host: ubuntu-latest
|
||||
target: armv7-linux-androideabi
|
||||
|
@ -1313,7 +1313,7 @@ jobs:
|
|||
export CXX="/usr/local/lib/android/sdk/ndk/21.4.7075529/toolchains/llvm/prebuilt/linux-x86_64/bin/armv7a-linux-androideabi24-clang++"
|
||||
export PATH="/usr/local/lib/android/sdk/ndk/21.4.7075529/toolchains/llvm/prebuilt/linux-x86_64/bin:${PATH}"
|
||||
npm i -g "@napi-rs/cli@${NAPI_CLI_VERSION}" "turbo@${TURBO_VERSION}" "pnpm@${PNPM_VERSION}"
|
||||
turbo run build-native-no-plugin --cache-dir=".turbo" -- --release --target armv7-linux-androideabi
|
||||
turbo run build-native-no-plugin --cache-dir=".turbo" -- --release --target armv7-linux-androideabi --cargo-flags=--no-default-features --features sentry_rustls
|
||||
/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
|
||||
- host: ubuntu-latest
|
||||
target: 'aarch64-unknown-linux-musl'
|
||||
|
@ -1324,13 +1324,13 @@ jobs:
|
|||
rustup toolchain install "${RUST_TOOLCHAIN}" &&
|
||||
rustup default "${RUST_TOOLCHAIN}" &&
|
||||
rustup target add aarch64-unknown-linux-musl &&
|
||||
turbo run build-native --cache-dir=".turbo" -- --release --target aarch64-unknown-linux-musl &&
|
||||
turbo run build-native --cache-dir=".turbo" -- --release --target aarch64-unknown-linux-musl --cargo-flags=--no-default-features --features sentry_rustls &&
|
||||
llvm-strip -x packages/next-swc/native/next-swc.*.node
|
||||
- host: windows-latest
|
||||
target: 'aarch64-pc-windows-msvc'
|
||||
build: |
|
||||
npm i -g "@napi-rs/cli@${NAPI_CLI_VERSION}" "turbo@${TURBO_VERSION}" "pnpm@${PNPM_VERSION}"
|
||||
turbo run build-native-no-plugin --cache-dir=".turbo" -- --release --target aarch64-pc-windows-msvc
|
||||
turbo run build-native-no-plugin --cache-dir=".turbo" -- --release --target aarch64-pc-windows-msvc --cargo-flags=--no-default-features
|
||||
if: ${{ needs.build.outputs.isRelease == 'true' }}
|
||||
needs: build
|
||||
name: stable - ${{ matrix.settings.target }} - node@16
|
||||
|
|
791
packages/next-swc/Cargo.lock
generated
791
packages/next-swc/Cargo.lock
generated
File diff suppressed because it is too large
Load diff
|
@ -8,6 +8,7 @@ publish = false
|
|||
crate-type = ["cdylib", "rlib"]
|
||||
|
||||
[features]
|
||||
default = ["sentry_native_tls"]
|
||||
# Instead of enabling all the plugin-related features by default, make it explicitly specified
|
||||
# when build (i.e napi --build --features plugin), same for the wasm as well.
|
||||
# this is due to some of transitive dependencies have features cannot be enabled at the same time
|
||||
|
@ -19,6 +20,8 @@ plugin = [
|
|||
"wasmer-wasi/default",
|
||||
"next-swc/plugin"
|
||||
]
|
||||
sentry_native_tls = ["_sentry_native_tls"]
|
||||
sentry_rustls = ["_sentry_rustls"]
|
||||
|
||||
[dependencies]
|
||||
anyhow = "1.0"
|
||||
|
@ -44,6 +47,20 @@ tracing-subscriber = "0.3.9"
|
|||
tracing-chrome = "0.5.0"
|
||||
wasmer = { version = "2.3.0", optional = true, default-features = false }
|
||||
wasmer-wasi = { version = "2.3.0", optional = true, default-features = false }
|
||||
# There are few build targets we can't use native-tls which default features rely on,
|
||||
# allow to specify alternative (rustls) instead via features.
|
||||
# Note to opt in rustls default-features should be disabled
|
||||
# (--no-default-features --features sentry_rustls)
|
||||
_sentry_native_tls = { package = "sentry", version = "0.27.0", optional = true }
|
||||
_sentry_rustls = { package = "sentry", version = "0.27.0", default-features = false, features = [
|
||||
"backtrace",
|
||||
"contexts",
|
||||
"panic",
|
||||
"rustls",
|
||||
"reqwest"
|
||||
], optional = true }
|
||||
|
||||
[build-dependencies]
|
||||
napi-build = "1"
|
||||
serde = "1"
|
||||
serde_json = "1"
|
|
@ -8,6 +8,8 @@ use std::{
|
|||
extern crate napi_build;
|
||||
|
||||
fn main() {
|
||||
// Emit current platform's target-triple into a text file to create static const
|
||||
// in util.rs
|
||||
let out_dir = env::var("OUT_DIR").expect("Outdir should exist");
|
||||
let dest_path = Path::new(&out_dir).join("triple.txt");
|
||||
let mut f =
|
||||
|
@ -19,5 +21,22 @@ fn main() {
|
|||
)
|
||||
.expect("Failed to write target triple text");
|
||||
|
||||
// Emit current package.json's version field into a text file to create static
|
||||
// const in util.rs This is being used to set correct release version for
|
||||
// the sentry's crash reporter.
|
||||
let pkg_file =
|
||||
File::open(Path::new("../../package.json")).expect("Should able to open package.json");
|
||||
let json: serde_json::Value = serde_json::from_reader(pkg_file).unwrap();
|
||||
let pkg_version_dest_path = Path::new(&out_dir).join("package.txt");
|
||||
let mut package_version_writer = BufWriter::new(
|
||||
File::create(&pkg_version_dest_path).expect("Failed to create package version text"),
|
||||
);
|
||||
write!(
|
||||
package_version_writer,
|
||||
"{}",
|
||||
json["version"].as_str().unwrap()
|
||||
)
|
||||
.expect("Failed to write target triple text");
|
||||
|
||||
napi_build::setup();
|
||||
}
|
||||
|
|
|
@ -79,6 +79,9 @@ fn init(mut exports: JsObject) -> napi::Result<()> {
|
|||
)?;
|
||||
exports.create_named_method("teardownTraceSubscriber", util::teardown_trace_subscriber)?;
|
||||
|
||||
exports.create_named_method("initCrashReporter", util::init_crash_reporter)?;
|
||||
exports.create_named_method("teardownCrashReporter", util::teardown_crash_reporter)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
|
|
@ -29,11 +29,13 @@ DEALINGS IN THE SOFTWARE.
|
|||
use anyhow::{anyhow, Context, Error};
|
||||
use napi::{CallContext, Env, JsBuffer, JsExternal, JsString, JsUndefined, JsUnknown, Status};
|
||||
use serde::de::DeserializeOwned;
|
||||
use std::{any::type_name, cell::RefCell, convert::TryFrom, path::PathBuf};
|
||||
use std::{any::type_name, cell::RefCell, convert::TryFrom, env, path::PathBuf};
|
||||
use tracing_chrome::{ChromeLayerBuilder, FlushGuard};
|
||||
use tracing_subscriber::{filter, prelude::*, util::SubscriberInitExt, Layer};
|
||||
|
||||
static TARGET_TRIPLE: &str = include_str!(concat!(env!("OUT_DIR"), "/triple.txt"));
|
||||
static PACKAGE_VERSION: &str = include_str!(concat!(env!("OUT_DIR"), "/package.txt"));
|
||||
|
||||
#[contextless_function]
|
||||
pub fn get_target_triple(env: Env) -> napi::ContextlessResult<JsString> {
|
||||
env.create_string(TARGET_TRIPLE).map(Some)
|
||||
|
@ -144,3 +146,72 @@ pub fn teardown_trace_subscriber(cx: CallContext) -> napi::Result<JsUndefined> {
|
|||
}
|
||||
cx.env.get_undefined()
|
||||
}
|
||||
|
||||
/// Initialize crash reporter to collect unexpected native next-swc crashes.
|
||||
#[js_function(1)]
|
||||
pub fn init_crash_reporter(cx: CallContext) -> napi::Result<JsExternal> {
|
||||
// Attempts to follow https://nextjs.org/telemetry's debug behavior.
|
||||
// However, this is techinically not identical to the behavior of the telemetry
|
||||
// itself as sentry's debug option does not provides full payuload output.
|
||||
let debug = env::var("NEXT_TELEMETRY_DEBUG").map_or_else(|_| false, |v| v == "1");
|
||||
|
||||
#[cfg(not(all(target_os = "windows", target_arch = "aarch64")))]
|
||||
let guard = {
|
||||
#[cfg(feature = "sentry_native_tls")]
|
||||
use _sentry_native_tls::{init, types::Dsn, ClientOptions};
|
||||
#[cfg(feature = "sentry_rustls")]
|
||||
use _sentry_rustls::{init, types::Dsn, ClientOptions};
|
||||
use std::{borrow::Cow, str::FromStr};
|
||||
|
||||
let dsn = if debug {
|
||||
None
|
||||
} else {
|
||||
Dsn::from_str(
|
||||
"https://7619e5990e3045cda747e50e6ed087a7@o205439.ingest.sentry.io/6528434",
|
||||
)
|
||||
.ok()
|
||||
};
|
||||
|
||||
Some(init(ClientOptions {
|
||||
release: Some(Cow::Borrowed(PACKAGE_VERSION)),
|
||||
dsn,
|
||||
debug,
|
||||
// server_name includes device host name, which _can_ be considered as PII depends on
|
||||
// the machine name.
|
||||
server_name: Some(Cow::Borrowed("[REDACTED]")),
|
||||
..Default::default()
|
||||
}))
|
||||
};
|
||||
|
||||
// aarch64_msvc neither compiles native-tls nor rustls for sentry transport
|
||||
#[cfg(all(target_os = "windows", target_arch = "aarch64"))]
|
||||
let guard: Option<usize> = None;
|
||||
|
||||
let guard_cell = RefCell::new(guard);
|
||||
cx.env.create_external(guard_cell, None)
|
||||
}
|
||||
|
||||
/// Trying to drop crash reporter guard if exists. This is the way to hold
|
||||
/// guards to not to be dropped immediately after crash reporter is initialized
|
||||
/// in napi context.
|
||||
#[js_function(1)]
|
||||
pub fn teardown_crash_reporter(cx: CallContext) -> napi::Result<JsUndefined> {
|
||||
#[cfg(not(all(target_os = "windows", target_arch = "aarch64")))]
|
||||
{
|
||||
#[cfg(feature = "sentry_native_tls")]
|
||||
use _sentry_native_tls::ClientInitGuard;
|
||||
#[cfg(feature = "sentry_rustls")]
|
||||
use _sentry_rustls::ClientInitGuard;
|
||||
|
||||
let guard_external = cx.get::<JsExternal>(0)?;
|
||||
let guard_cell = &*cx
|
||||
.env
|
||||
.get_value_external::<RefCell<Option<ClientInitGuard>>>(&guard_external)?;
|
||||
|
||||
if let Some(guard) = guard_cell.take() {
|
||||
drop(guard);
|
||||
}
|
||||
}
|
||||
|
||||
cx.env.get_undefined()
|
||||
}
|
||||
|
|
|
@ -104,7 +104,11 @@ import { TelemetryPlugin } from './webpack/plugins/telemetry-plugin'
|
|||
import { MiddlewareManifest } from './webpack/plugins/middleware-plugin'
|
||||
import { recursiveCopy } from '../lib/recursive-copy'
|
||||
import { recursiveReadDir } from '../lib/recursive-readdir'
|
||||
import { lockfilePatchPromise, teardownTraceSubscriber } from './swc'
|
||||
import {
|
||||
lockfilePatchPromise,
|
||||
teardownTraceSubscriber,
|
||||
teardownCrashReporter,
|
||||
} from './swc'
|
||||
import { injectedClientEntries } from './webpack/plugins/client-entry-plugin'
|
||||
import { getNamedRouteRegex } from '../shared/lib/router/utils/route-regex'
|
||||
import { flatReaddir } from '../lib/flat-readdir'
|
||||
|
@ -2315,6 +2319,7 @@ export default async function build(
|
|||
// Ensure all traces are flushed before finishing the command
|
||||
await flushAllTraces()
|
||||
teardownTraceSubscriber()
|
||||
teardownCrashReporter()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import createStore from 'next/dist/compiled/unistore'
|
||||
import stripAnsi from 'next/dist/compiled/strip-ansi'
|
||||
import { flushAllTraces } from '../../trace'
|
||||
import { teardownTraceSubscriber } from '../swc'
|
||||
import { teardownCrashReporter, teardownTraceSubscriber } from '../swc'
|
||||
import * as Log from './log'
|
||||
|
||||
export type OutputState =
|
||||
|
@ -92,6 +92,7 @@ store.subscribe((state) => {
|
|||
// Ensure traces are flushed after each compile in development mode
|
||||
flushAllTraces()
|
||||
teardownTraceSubscriber()
|
||||
teardownCrashReporter()
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -119,6 +120,7 @@ store.subscribe((state) => {
|
|||
// Ensure traces are flushed after each compile in development mode
|
||||
flushAllTraces()
|
||||
teardownTraceSubscriber()
|
||||
teardownCrashReporter()
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -135,4 +137,5 @@ store.subscribe((state) => {
|
|||
// Ensure traces are flushed after each compile in development mode
|
||||
flushAllTraces()
|
||||
teardownTraceSubscriber()
|
||||
teardownCrashReporter()
|
||||
})
|
||||
|
|
1
packages/next/build/swc/index.d.ts
vendored
1
packages/next/build/swc/index.d.ts
vendored
|
@ -8,4 +8,5 @@ export function parse(src: string, options: any): any
|
|||
export const lockfilePatchPromise: { cur?: Promise<void> }
|
||||
export function initCustomTraceSubscriber(traceFileName?: string): void
|
||||
export function teardownTraceSubscriber(): void
|
||||
export function teardownCrashReporter(): void
|
||||
export function loadBindings(): Promise<void>
|
||||
|
|
|
@ -8,6 +8,7 @@ import { eventSwcLoadFailure } from '../../telemetry/events/swc-load-failure'
|
|||
import { patchIncorrectLockfile } from '../../lib/patch-incorrect-lockfile'
|
||||
import { downloadWasmSwc } from '../../lib/download-wasm-swc'
|
||||
import { version as nextVersion } from 'next/package.json'
|
||||
import { Telemetry } from '../../telemetry/storage'
|
||||
|
||||
const ArchName = arch()
|
||||
const PlatformName = platform()
|
||||
|
@ -18,6 +19,7 @@ let wasmBindings
|
|||
let downloadWasmPromise
|
||||
let pendingBindings
|
||||
let swcTraceFlushGuard
|
||||
let swcCrashReporterFlushGuard
|
||||
export const lockfilePatchPromise = {}
|
||||
|
||||
export async function loadBindings() {
|
||||
|
@ -215,6 +217,17 @@ function loadNative() {
|
|||
}
|
||||
|
||||
if (bindings) {
|
||||
// Initialize crash reporter, as earliest as possible from any point of import.
|
||||
// The first-time import to next-swc is not predicatble in the import tree of next.js, which makes
|
||||
// we can't rely on explicit manual initialization as similar to trace reporter.
|
||||
if (!swcCrashReporterFlushGuard) {
|
||||
// Crash reports in next-swc should be treated in the same way we treat telemetry to opt out.
|
||||
let telemetry = new Telemetry({ distDir: process.cwd() })
|
||||
if (telemetry.isEnabled) {
|
||||
swcCrashReporterFlushGuard = bindings.initCrashReporter?.()
|
||||
}
|
||||
}
|
||||
|
||||
nativeBindings = {
|
||||
isWasm: false,
|
||||
transform(src, options) {
|
||||
|
@ -278,6 +291,7 @@ function loadNative() {
|
|||
getTargetTriple: bindings.getTargetTriple,
|
||||
initCustomTraceSubscriber: bindings.initCustomTraceSubscriber,
|
||||
teardownTraceSubscriber: bindings.teardownTraceSubscriber,
|
||||
teardownCrashReporter: bindings.teardownCrashReporter,
|
||||
}
|
||||
return nativeBindings
|
||||
}
|
||||
|
@ -377,3 +391,20 @@ export const teardownTraceSubscriber = (() => {
|
|||
}
|
||||
}
|
||||
})()
|
||||
|
||||
export const teardownCrashReporter = (() => {
|
||||
let flushed = false
|
||||
return () => {
|
||||
if (!flushed) {
|
||||
flushed = true
|
||||
try {
|
||||
let bindings = loadNative()
|
||||
if (swcCrashReporterFlushGuard) {
|
||||
bindings.teardownCrashReporter(swcCrashReporterFlushGuard)
|
||||
}
|
||||
} catch (e) {
|
||||
// Suppress exceptions, this fn allows to fail to load native bindings
|
||||
}
|
||||
}
|
||||
}
|
||||
})()
|
||||
|
|
Loading…
Reference in a new issue