Description
Description
Sentry documentation suggests to initialize Sentry "as early as possible", and the example does it after all application imports:
import Vue from "vue";
import Router from "vue-router";
import * as Sentry from "@sentry/vue";
import { Integrations } from "@sentry/tracing";
Vue.use(Router);
const router = new Router();
// Sentry is initialized after imports
Sentry.init(...)
This approach will cause Sentry to be initialized too late in certain (perhaps common) scenarios, and causes the sentry-header
not to be sent when using Api clients that read window.fetch
during its initialization.
Because this is unexpected behavior that is hard to diagnose, and the solution is also non-obvious, I would propose to add caution remarks in the Documentation, or to alter the documented code snippet.
Root Cause: Sentry should be initialized before imports that use window.fetch
Application imports will commonly end up at the top. Either by convention, or by force when using webpack. Vue JS does this.
A common scenario is importing an API module that initializes an API client by passing window.fetch
into the constructor.
// api.ts
export default const apiClient = new ApiClient('https://api.example.com', window.fetch);
// main.ts
import Vue from 'vue'
import { ApiModule} from '@/api' // this module reads `window.fetch` and won't add any sentry headers.
import * as Sentry from '@sentry/vue'
import { Integrations } from '@sentry/tracing'
var router = ...
// It looks like we're initializing Sentry as early as possible, but we're not.
Sentry.init(...)
Vue.use(AuthModule)
// ... rest of initialization
In this scenario, the initialization is too late.
Moving the logic before the import doesn't work
import Vue from 'vue'
// Trying to initialize before loading the ApiModule.
// Webpack (or babel?) will (secretly) move the block of code AFTER the imports!
Sentry.init(...)
import { ApiModule } from '@/api'
import * as Sentry from '@sentry/vue'
import { Integrations } from '@sentry/tracing'
Webpack will forcefully move the initialization logic after the import statements.
- Best case: This will happen while running
npm run serve
, and the source file is updated byeslint
. The change is obvious. - Worst case: This re-ordering happens during
npm run build
, and the order is only changed in thedist
output. Then it's completely mysterious why the init logic is out of order.
Solution
- Moving the sentry initialization logic to a separate file, and importing it at the top of the main application file.
- Unfortunately, because in the case of Vue,
Sentry
relies on the Router, and the main file also relies on the Router, the router definition has to be extracted in another file as well.
This works because the import order is maintained (at least in my project), whereas code statements are always moved after imports.
// main.ts
import "./sentry"
import Vue from "vue";
import { router } from "./router"
Vue.use(router);
// rest of vue initialization
// router.ts
import Vue from "vue";
export const router = new Router({
// ...
});
// sentry.ts
import * as Sentry from "@sentry/vue";
import { Integrations } from "@sentry/tracing";
import { router } from "./router"
Sentry.init(...)
The solution is not ideal because it makes integrating Sentry seem more complex than it is, and requires changing the structure of the application files.
Better solutions are welcome of course.
In any case, I hope that Sentry users who are trying to figure out why Sentry Headers are not being added to their fetch request, will find this issue and can solve it without opening yet another ticket 😉