Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(vue): Rework tracing and add support for Vue 3 #3804

Merged
merged 9 commits into from
Jul 16, 2021
Merged

Conversation

kamilogorek
Copy link
Contributor

@kamilogorek kamilogorek commented Jul 14, 2021

The examples below show how to use Error Tracking and Performance Tracing features. In case you don't want to use Performance Tracing, integrations, tracesSampleRate, hooks, and trackComponents can be omited.

Vue 2

import Vue from "vue";
import App from "./App.vue";
import router from "./router";
import store from "./store";

import * as Sentry from "@sentry/vue";
import { Integrations } from "@sentry/tracing";

Sentry.init({
  // `app` and `Vue` options are interchangeable and are only for UX purpose, as in Vue 2 you are passing constructor, where in Vue 3 it's an instance
  Vue: Vue,
  dsn: "__PUBLIC__DSN",
  integrations: [
    new Integrations.BrowserTracing({
      routingInstrumentation: Sentry.vueRouterInstrumentation(router),
      tracingOrigins: ["localhost", "https://myapp.io/api"]
    })
  ],
  tracesSampleRate: 1.0,
  logErrors: true,
  trackComponents: true,
  // or specify specific names
  // trackComponents: ["App", "RwvHeader", "RwvFooter"],
  hooks: ["activate", "create", "destroy", "mount", "update"],
  // or use default hooks ['activate', 'mount', 'update']
});

// ...

new Vue({
  router,
  store,
  render: h => h(App)
}).$mount("#app");

Vue 3

import * as Vue from "vue";
import App from "./App.vue";
import { router } from "./router";

import * as Sentry from "@sentry/vue";
import { Integrations } from "@sentry/tracing";

const app = Vue.createApp(App);

Sentry.init({
  // `app` and `Vue` options are interchangeable and are only for UX purpose, as in Vue 2 you are passing constructor, where in Vue 3 it's an instance
  app: app,
  dsn: "__PUBLIC_DSN__",
  integrations: [
    new Integrations.BrowserTracing({
      routingInstrumentation: Sentry.vueRouterInstrumentation(router),
      // list of URLs to capture for tracing
      tracingOrigins: ["localhost", "https://myapp.io/api"],
    }),
  ],
  tracesSampleRate: 1.0,
  logErrors: true,
  trackComponents: true,
  // or specify specific names
  // trackComponents: ["App", "AppNavigation", "AppFooter"],
  hooks: ["activate", "create", "destroy", "mount", "update"],
  // or use default hooks ['activate', 'mount', 'update']
});

// ...

app.use(router);
app.mount("#app");

Vue 3 - multiple apps

const appOne = Vue.createApp(App);
const appTwo = Vue.createApp(App);
const appThree = Vue.createApp(App);

Sentry.init({
  app: [appOne, appTwo, appThree],
});

Vue 3 - adding app after initialization

import * as Sentry from '@sentry/vue';

// ...
const app = createApp(App);

Sentry.init({
  app: app
  // ...
});

const miscApp = createApp(MiscApp);
miscApp.mixin(Sentry.createTracingMixins({ trackComponents: true }));
Sentry.attachErrorHandler(miscApp, { logErrors: true });

@github-actions
Copy link
Contributor

github-actions bot commented Jul 14, 2021

size-limit report

Path Size
@sentry/browser - CDN Bundle (gzipped) 21.46 KB (0%)
@sentry/browser - Webpack 22.47 KB (0%)
@sentry/react - Webpack 22.5 KB (0%)
@sentry/browser + @sentry/tracing - CDN Bundle (gzipped) 28.96 KB (0%)

@Kocal
Copy link

Kocal commented Jul 14, 2021

Thanks for this incredible PR, I will try it when I have free time!

Just a question, I see you inject app (an instance of Vue app) when initializing Sentry.
Does it mean we need to initialize Sentry N times if we have N Vue app instances on the page?

@kamilogorek
Copy link
Contributor Author

kamilogorek commented Jul 14, 2021

I didn't know that multiple Vue apps it's actually a thing, but here you go :)

const appOne = Vue.createApp(App);
const appTwo = Vue.createApp(App);
const appThree = Vue.createApp(App);

Sentry.init({
  app: [appOne, appTwo, appThree],
});

@Kocal
Copy link

Kocal commented Jul 14, 2021

Nice, thanks!

I (we) have another advanced usecase with multiples Vue instances on a single page.

On server-rendered websites, sometimes we create small components (and Vue instances) to add some interactivity on the page (e.g.: dropdowns, inputs with characters counters, modals...), but thoses instances are often created after Sentry has been initialized (since we initialize Sentry the ASAP).

It was not a problem with Vue 2, but Vue 3 it is. 😕

Do you think it would be possible to listen on every createApp(), or having a method Sentry.pushVueApp()?

Thanks!

@kamilogorek
Copy link
Contributor Author

import * as Sentry from '@sentry/vue';

// ...
const app = createApp(App);

Sentry.init({
  app: app
  // ...
});

const miscApp = createApp(MiscApp);
miscApp.mixin(Sentry.createTracingMixins({ trackComponents: true }));
Sentry.attachErrorHandler(miscApp, { logErrors: true });

Although not a common use case I believe, this should do the job.

@Kocal
Copy link

Kocal commented Jul 14, 2021

That's awesome! Even if it's more verbose than a single Sentry.init(), people can still write their own Sentry "wrapper" for their other Vue instances.

Thanks for this! ❤️

packages/vue/src/components.ts Outdated Show resolved Hide resolved
packages/vue/src/components.ts Show resolved Hide resolved
packages/vue/src/components.ts Show resolved Hide resolved
packages/vue/src/components.ts Outdated Show resolved Hide resolved
packages/vue/src/router.ts Outdated Show resolved Hide resolved
packages/vue/src/tracing.ts Outdated Show resolved Hide resolved
packages/vue/src/errorhandler.ts Outdated Show resolved Hide resolved
@kamilogorek
Copy link
Contributor Author

@AbhiPrasad updated

Copy link
Member

@AbhiPrasad AbhiPrasad left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wonder if we can ping someone in that Vue 3 GH issue or through the Vue community discord to get some more 👀 on it.

@kamilogorek kamilogorek merged commit 14ccb55 into master Jul 16, 2021
@kamilogorek kamilogorek deleted the vue3 branch July 16, 2021 10:06
@vaaski
Copy link

vaaski commented Jul 18, 2021

When using typescript, the types for the app property seem to be wrong.

image

image

I am using @sentry/vue@6.10.0-beta and @sentry/tracing@6.10.0-beta with vue@3.1.4

@tjk
Copy link

tjk commented Jul 18, 2021

@vaaski app.use returns app so you can chain it, but app.mount returns something different (vnode.component.proxy).

@vaaski
Copy link

vaaski commented Jul 19, 2021

You're right, my bad. Works like a charm now!

@cawa-93
Copy link
Contributor

cawa-93 commented Jul 22, 2021

How use it with '@sentry/integrations' ?

import { Vue as VueIntegration} from '@sentry/integrations';

  Sentry.init({
    dsn: import.meta.env.VITE_SENTRY_DSN,
    integrations: [
      new VueIntegration({ ??? }),
    ],
  });

I tried:

new VueIntegration({ app }),

but I got TS error:

Object literal may only specify known properties, and 'app' does not exist in type 'Partial<Omit<IntegrationOptions, "tracingOptions"> & { tracingOptions: Partial<TracingOptions>; }>'.

@kamilogorek
Copy link
Contributor Author

You don't. Vue integration predates a standalone Vue sdk. It has been replaced by it now.

@mac-cain13
Copy link

mac-cain13 commented Jul 22, 2021

Seems this change introduces a type error, the Route type defined in this PR has a name: string, but in the vue-router I'm using the name on a Route is optional.

Therefore TypeScript complains that I can't pass my router into Sentry.vueRouterInstrumentation:

Argument of type 'import("[redacted]/node_modules/vue-router/types/router").VueRouter' is not assignable to parameter of type 'VueRouter'.
  Types of property 'beforeEach' are incompatible.
    Type '(guard: NavigationGuard<Vue>) => Function' is not assignable to type '(fn: (to: Route, from: Route, next: () => void) => void) => void'.
      Types of parameters 'guard' and 'fn' are incompatible.
        Types of parameters 'to' and 'to' are incompatible.
          Type 'import("[redacted]/node_modules/vue-router/types/router").Route' is not assignable to type 'Route'.
            Property 'name' is optional in type 'Route' but required in type 'Route'.
    32 |     integrations: [
    33 |       new Integrations.BrowserTracing({
  > 34 |         routingInstrumentation: Sentry.vueRouterInstrumentation(router),
       |                                                                 ^
    35 |       }),
    36 |     ],

Is this expected and should I adjust my code or is this a newly introduced bug? Please let me know if I should open a separate issue for this.

@kamilogorek
Copy link
Contributor Author

@mac-cain13 fix is coming

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

7 participants