getsentry / sentry-javascript

Official Sentry SDKs for JavaScript
https://sentry.io
MIT License
8.02k stars 1.59k forks source link

Distributed Tracing - Laravel InertiaJS Vue3 Sentry #11362

Closed RChutchev closed 6 months ago

RChutchev commented 8 months ago

Is there an existing issue for this?

How do you use Sentry?

Sentry Saas (sentry.io)

Which SDK are you using?

@sentry/vue

SDK Version

@sentry/vue@7.108.0 , @sentry/webpack-plugin@2.16.0

Framework Version

Laravel Framework 10.48.4

Link to Sentry event

No response

SDK Setup

/*

                    IMPORTANT!

     Some comments may be inappropriate for someone
   or may contain private project / corp information

                   DON'T FORGET
     TO ENABLE OPTION WHICH REMOVES ALL COMMENTS
         FOR PRODUCTION BEFORE PRODUCTION!

Last edit: RChutchev, 4/1/2024
*/
require("./bootstrap");
// Import modules...
import { createApp, h } from "vue";
import { createInertiaApp, router } from "@inertiajs/vue3";
import * as Sentry from "@sentry/vue";
import * as SentryIntegrations from "@sentry/integrations";

import Filters from "./filters";

createInertiaApp({
    id:'app',
    progress: {
        color: '#8d2618',
    },
    resolve: (name) => require(`./Pages/${name}`).default,
    setup({ el, App, props, plugin }) {

        const app = createApp({ render: () => h(App, props) })
            .use(plugin)
            .mixin({methods: {route}});
        Sentry.init({
            app,
            dsn: process.env.MIX_APP_SENTRY_DSN_PUBLIC,
            environment: process.env.MIX_APP_ENV,
            release: process.env.MIX_SENTRY_RELEASE,
            integrations: [
                // NOTE: Migrate to entry.browserTracingIntegration, 
                // but this will not work with routingInstrumentation 
                // and we'll unable to use inertiaRoutingInstrumentation function... 
                // temporally workaround to use old new Sentry.BrowserTracing
                // new Sentry.browserTracingIntegration(
                //    enableInp: true,
                // ),
                new Sentry.BrowserTracing({
                    // Helps to send pageload and navigation OPs to Sentry for InertiaJS
                    routingInstrumentation: inertiaRoutingInstrumentation,
                    enableInp: true,
                    tracePropagationTargets: [
                        "/http:\/\/subdomain\.domain\.local\/",
                        "/https:\/\/subdomain\.domain\.local\/",
                        "/https:\/\/subdomain\.domain\.net\/",
                        "/http:\/\/subdomain\.domain\.net\/",
                    ],
                    tracingOrigins: [
                        "localhost",
                        "subdomain.domain.local",
                        "subdomain.domain.net",
                    ]
                }),
                Sentry.replayIntegration({
                    maskAllText: false,
                    blockAllMedia: false,
                }),
                Sentry.replayCanvasIntegration(),
                SentryIntegrations.captureConsoleIntegration({
                    // Add 'warn', 'error', 'debug', 'assert' console output types to Sentry
                    // and additionally possible to add 'log' and 'info' console output types to Sentry if required
                    levels: ['log', 'info', 'warn', 'error', 'debug', 'assert'],
                }),
                SentryIntegrations.contextLinesIntegration({
                    frameContextLines: 50,
                }),
                SentryIntegrations.httpClientIntegration(),
            ],
            //Now not required to use beforeSend and modify event, but we're able to do that if req.
            //beforeSend: function(event, hint) {
            //    if (hint && hint.originalException instanceof Event) {
            //        event.extra.isTrusted = hint.originalException.isTrusted;
            //        event.extra.detail = hint.originalException.detail;
            //        event.extra.type = hint.originalException.type;
            //    }
            //    return event;
            //},
            sendDefaultPii: true,
            tracesSampleRate: process.env.MIX_SENTRY_TRACES_SAMPLE_RATE,
            replaysSessionSampleRate: 0.1,
            replaysOnErrorSampleRate: 1.0,
            autoSessionTracking: true,
            // Disable for Dev/Prod?
            telemetry: true,
            denyUrls:
                [
                    // All browser extensions
                    /extensions\//i,
                    /^safari-extension:\/\//i,
                    /^safari-web-extension:\/\//i,
                    /^moz-extension:\/\//i,
                    /^chrome:\/\//i,
                    /^chrome-extension:\/\//i,
                    /moz-extension/i,
                ],
            ignoreErrors:
                [
                    // Let's ignore some output as unnecessary for us now
                    '@webkit-masked-url',
                    'debugger eval',
                    'Not implemented',
                    //'Failed to fetch',
                ],
        });
        Sentry.attachErrorHandler(app, {
            logErrors: true,
        });
        app.config.globalProperties.$filters = Filters;
        app.mixin(
            Sentry.createTracingMixins({ 
                trackComponents: true 
            })
        );
        app.mount(el);

        /*

            Some code which not working or unnecessary now, starts here after this comment
                        Please, KEEP IT HERE and DON'T DELETE
                            or angry developer kill you (:

        */

        /*
        router.on('start', (event) => {

            //inertiaRoutingInstrumentation(Sentry.browserTracingIntegration);
            console.info(`Начинается переход к ${event.detail.visit.url}`);
            let sentryDataOnStart = props.initialPage.props.sentry || {};
            console.info(sentryDataOnStart);

            if (sentryDataOnStart.baggage) {
                event.detail.visit.headers['baggage'] = sentryDataOnStart.baggage;
            }
            if (sentryDataOnStart.sentryTrace) {
                event.detail.visit.headers['sentry-trace'] = sentryDataOnStart.sentryTrace;
            }
        });
        */

        /*
        router.on('navigate', (event) => {
            console.info(`Navigated to ${event.detail.page.url}`)
            let sentryData = event.detail.page.props || {};
            console.info(sentryData);

            if (sentryData.baggage) {
                // event.detail.visit.headers unavalible here let's try window.axios.defaults.headers.common
                // event.detail.visit.headers["X-Socket-ID"] = echo.socketId();
                window.axios.defaults.headers.common['baggage'] = sentryData.baggage;
            }
            if (sentryDataOnNavigate.sentryTrace) {
                window.axios.defaults.headers.common['sentry-trace'] = sentryDataOnNavigate.sentryTrace;
            }
        }); 
        */

        router.on('navigate', (event) => {
            console.info(`Navigate in router.on`);
            console.info(props.initialPage.props.sentry);
        });

        /*
        router.on('before', (event) => {
            // Let's try to set sentry headers from props on Before 
            console.info(`Navigated to ${event.detail.page.url}`)
            let sentryData = props.initialPage.props.sentry || {};
            console.log(sentryData);

            if (sentryData.baggage) {
                event.detail.visit.headers['baggage'] = sentryData.baggage;
            }
            if (sentryData.sentryTrace) {
                event.detail.visit.headers['sentry-trace'] = sentryData.sentryTrace;
            }

        })
        */
    },
});

// Uses https://inertiajs.com/events
function inertiaRoutingInstrumentation(
    customStartTransaction,
    startTransactionOnPageLoad = true,
    startTransactionOnLocationChange = true,
) {
    console.info('inertiaRoutingInstrumentation Started');

    let activeTransaction;
    let name;
    if (startTransactionOnPageLoad) {
        console.info('Start transaction on page load');
        name = '/'+route().current();

        activeTransaction = customStartTransaction({
            name,
            op: 'pageload',
            metadata: {
                source: 'route',
            },
        });
    }

    if (startTransactionOnLocationChange) {
        console.info('Start transaction on location change');
        console.info(propsForSentry);
        router.on('before', (_to, _from) => {
            if (activeTransaction) {
                activeTransaction.finish();
            }

            const newName = '/'+route().current();
            console.info('Old name: '+name+'. New name: '+newName)

            if (newName !== name) {
                console.info('Old name is not equal to new name!');
                activeTransaction = customStartTransaction({
                    name: newName,
                    op: 'navigation',
                    metadata: {
                        source: 'route',
                    },
                });
            }
        });

        router.on('finish', () => {
            console.info('Router on finish. Route: '+'/'+route().current())
            activeTransaction.setName('/'+route().current(), 'route');
        });
    }
    console.info('inertiaRoutingInstrumentation Finished');
}

Steps to Reproduce

  1. I've an application in production on Laravel 10 (Vue 3, InertiaJS - users portal, and Filament - admin portal) based on Github but of course, modified and working with new ver. of Laravel etc, but anyway code is based on the repo code and you can check it if it's required for understanding.
  2. Sentry integration added to Filament and works well, and added to InertiaJS, but InertiaJS works only with page-load. For all navigation events not working at all.
  3. I've created a new Sentry.BrowserTracing({*}); entity for integrations in Sentry.init, etc check in SDK Setup here.
  4. I've added config for webpack.mix.js which requires webpackConfig with devtool: "source-map" and sentryWebpackPlugin with ORG, PROJECT, and authToken configs. Everything transferred to Sentry successfully and works with Filament and Inertia fine.
  5. Opening a new page or full reload will cause an update tag for Sentry and send correct data to Sentry, but without page reload incorrect baggage and sentry-trace added on the page and no data will be sent to Sentry for Inertia navigation.
  6. Yes, i imported Laravel integration via use Sentry\Laravel\Integration;
  7. And added {!! Integration::sentryMeta() !!} in app.blade.php (main file for Inertia app) where application loading ( etc), and that's the reason why incorrect meta adds, but I didn't find another way to add bagger and sentry-trace to pages with Inertia.
  8. I am unable to find any good docs for Distributed Tracing with Vue and Inertia. Maybe I'm dumb, but it's going me crazy.
  9. I've also tried to add event.detail.visit.headers['baggage'] = sentryDataOnStart.baggage; and event.detail.visit.headers['sentry-trace'] = sentryDataOnStart.sentryTrace; to my js file from props.initialPage.props.sentry and added this code to the public function share in HandleInertiaRequests in the Middleware file.
    $data['sentry'] = [
    'baggage' => $baggage,
    'sentryTrace' => $sentryTrace,
    ]; 
  10. but still unable to set a proper header for the inertia app in setup for all outgoing requests b/c when I'm getting sentryData from event.detail.page.props it returns the same baggage and sentry-trace as for pageload.
  11. IDK how to set up Distributed Tracing and BrowserTracing properly with the Inertia app, do you have any idea how to fix that?

Expected Result

  1. I'm able to add BrowserTracing and Distributed Tracing Sentry future by reading Docs.
  2. Distributed Tracing and BrowserTracing for Laravel app with InertiaJS works and I'm getting correct data in Sentry for both navigation and page load events.
  3. Like on those screenshots for the Event when the page was reloaded and the event was recorded successfully and fully. Screenshot 2024-04-02 at 12 51 32 AM Screenshot 2024-04-02 at 12 52 02 AM

Actual Result

I'm getting this (no page view) Screenshot 2024-04-02 at 12 37 37 AM

Screenshot 2024-04-02 at 12 38 36 AM

or this (multiply events from different pages to one Event) Screenshot 2024-04-02 at 12 49 21 AM

RChutchev commented 8 months ago

That's the 'log' from the browser's console (new sentryTrace ID only after page reloaded event)

Screenshot 2024-04-02 at 2 38 49 AM

Lms24 commented 8 months ago

Hey @RChutchev thanks for writing in! I think we're talking about multiple issues here:

I'm getting this (no page view)

So in this case I assume that the page that the server rendered did not include any <meta> tags for Sentry that the Vue SDK would use to continue the trace started on the server. Just talked to @cleptric who maintains the Laravel SDK and you need to inject these tags manually (see link above).

or this (multiply events from different pages to one Event)

I can only guess here but I assume this happens for one of two reasons:

  1. The client application makes requests to these server endpoints and they have the same trace id. This is not ideal and we're working on reducing the "lifetime" of a traceId on the browser side. However, if these requests happen as long as your browser's pageload transaction is still active, I'd argue this is expected behaviour.
  2. The additional two http.server transactions are not part of this pageload and not triggered by a request from a client. Before investigating this further (suspected bleed of trace id) I'd like to rule out 1 though.

Let me know if this fixes your first and/or explains your second problem.

RChutchev commented 8 months ago

Hey @RChutchev thanks for writing in! I think we're talking about multiple issues here:

I'm getting this (no page view)

So in this case I assume that the page that the server rendered did not include any <meta> tags for Sentry that the Vue SDK would use to continue the trace started on the server. Just talked to @cleptric who maintains the Laravel SDK and you need to inject these tags manually (see link above).

or this (multiply events from different pages to one Event)

I can only guess here but I assume this happens for one of two reasons:

  1. The client application makes requests to these server endpoints and they have the same trace id. This is not ideal and we're working on reducing the "lifetime" of a traceId on the browser side. However, if these requests happen as long as your browser's pageload transaction is still active, I'd argue this is expected behaviour.
  2. The additional two http.server transactions are not part of this pageload and not triggered by a request from a client. Before investigating this further (suspected bleed of trace id) I'd like to rule out 1 though.

Let me know if this fixes your first and/or explains your second problem.

Hello @Lms24, thanks for your reply.

  1. The first link you provided isn't working Screenshot 2024-04-04 at 6 25 54 PM
  2. Yes. This transaction is triggered by a request from a client but that's user navigation on the app page.

Example user flow:

  1. User - Opened a main page new Sentry Tracing (Server Laravel + Client JS)
  2. User navigated to Login (with page reload) with Inertia new Sentry Tracing (Server Laravel + Client JS) (Note: if a user clicks "Create an account" it will cause a partial pagereload and an error will occur here)
  3. Successfully logged in and redirected to the main app page (with Inertia) in the client's area new Sentry Tracing (Server Laravel + Client JS)
  4. Navigated to the Metrics page (or any other page) (with Inertia and partial reload) in the client area Expected: new Sentry Tracing (Server Laravel + Client JS) Now: ERROR as shown on the second screenshot on my first comment (i mean multiply events from different pages to one Event)

Server Laravel - creates new IDs Screenshot 2024-04-04 at 7 07 49 PM

In my HandleInertiaRequests.php in class, HandleInertiaRequests extends Middleware i've public function share(Request $request) containing this code (log from this place in code shown on my screenshot):

$baggage = function_exists('Sentry\getBaggage') ? \Sentry\getBaggage() : null;
        $sentryTrace = function_exists('Sentry\getTraceparent') ? \Sentry\getTraceparent() : null;

        $data['sentry'] = [
            'baggage' => $baggage,
            'sentryTrace' => $sentryTrace,
        ];

        Log::debug('SentryTrace {id} was initiated.', ['id' => $sentryTrace]);

But this ID doesn't send to the frontend when the partial page reload occurs. I can't replace because the main page is already loaded and Inertia is already initialized (createInertiaApp executed and next time will be executed only after full page reload). I added {!! Integration::sentryMeta() !!} to app.blade.php which contain for all application's pages. Screenshot 2024-04-04 at 7 13 49 PM But it also contains @ inertia which creates Inertia App. All other page navigations will be partial page reloads.

Client JS data on page navigation doesn't send or send but is lost in a lot of events in Sentry.

Inertia JS app doesn't reload the page when the user navigate to another page. When the InertiaJS App is created, any Navigation event will be served without page reload, and only the main content will be reloaded (I mean partial reload). Only when the session is destroyed page be redirected and the app will be fully reloaded. Or manually, if the user reloads the page. In any other case user will get content without the main page reload, only vue JS 'view' will be rendered/reloaded. This is standard behavior for Inertia (Docs)

IDK how to track it into Sentry, do you have any advice for Sentry + Inertia integration or Sentry and JS applications with partial page reload?

Lms24 commented 8 months ago

Hey @RChutchev before we get to finding a solution here, I'd like to know if I understood your ask correctly. (Really sorry but right now, I'm not sure that I do understand it yet):

  1. Can you confirm that page load traces are correctly connected? So when a user first loads the page (or full-reloads it), the frontend pageload transaction is connected with the http.server transaction in your backend?
  2. As far as I understand your problem, it seems like navigation transactions (i.e. "partial" reloads) don't work correctly? I see you already created an inertiaRoutingInstrumentation which, as far as I can tell without knowledge about intertia, looks fine.

The general expectation for Sentry SDKs and how we handle pageload and navigation traces is as following (assuming tracesSampleRate > 0 and BrowserTracing or browserTracingIntegration is used):

  1. On browser SDK initialization, the SDK will start an op: pageload transaction. If it finds <meta> tags for sentry-trace and baggage, it'll continue the provided trace (e.g. the traceId). If there are none, the SDK assumes it is the "head of the trace", meaning it'll create a new traceId. The pageload transaction will live until there are no more activities (usually for a couple of seconds) and then finish itself and be sent to Sentry.
  2. For any subsquent navigation (so no full reload but client side routing), the SDK should create an op: navigation transaction. These navigation transactions are always the head of the trace, so they should always create their own, new traceId.

I can only recommend using debug: true in Sentry.init to log out transaction and span creations to check what's going on exactly.

getsantry[bot] commented 7 months ago

This issue has gone three weeks without activity. In another week, I will close it.

But! If you comment or otherwise update it, I will reset the clock, and if you remove the label Waiting for: Community, I will leave it alone ... forever!


"A weed is but an unloved flower." ― Ella Wheeler Wilcox 🥀

xyNNN commented 6 months ago

@RChutchev Do you have a working solution for it?

RChutchev commented 6 months ago

@RChutchev Do you have a working solution for it?

Hello, nope and I forget about this issue a little bit b/c was busy with another functionality.

IDK what's going on, maybe Sentry have not enough documentation for Distributed Tracing or Sentry is incompatible with Inertia (and/or Single Page Application), anyway Sentry only partially works with navigation event (that's real screenshot from production) Screenshot 2024-05-20 at 4 35 29 PM

And vice versa Sentry working good for full page reload (I mean pageload event) Screenshot 2024-05-20 at 4 41 16 PM Screenshot 2024-05-20 at 4 40 55 PM

Anyway idk how to fix this issue. Also, looks like new ver. 8, is incompatible with routingInstrumentation and inertiaRoutingInstrumentation function (we're using this function to set route name for activeTransaction. Code in my first message.

I think I'll delete all custom code later and enable integrations AS IS, b/c Sentry representative cant help me with that. I provided all code below, but received recommendation: "using debug: true in Sentry.init" and spend hours to debug.

RChutchev commented 6 months ago

@RChutchev Do you have a working solution for it?

See also this discussion if your project requires custom routing. Perhaps Mr. @AbhiPrasad will post an update for a function that will be compatible with Sentry ver 8.

AbhiPrasad commented 6 months ago

@RChutchev - we'll update the docs for better inertia js examples, it's on my todo!

xyNNN commented 6 months ago

Thanks @AbhiPrasad - Have you an ETA for this? Or how I can inform about the updated docs? Will you write a comment in this ticket? Thanks for your effort!

AbhiPrasad commented 6 months ago

we have a general tracking task here: https://github.com/getsentry/sentry-docs/issues/9943

we'll be creating more specific tasks in a little bit, just need to sync up with the team!

RChutchev commented 6 months ago

@RChutchev - we'll update the docs for better inertia js examples, it's on my todo!

Thanks, that's great! I hope it will be done soon.

xyNNN commented 6 months ago

Sorry for bothering again, but is there some ETA (1-2 weeks, up to 2 months, more than a half year) available for the integration guide of InertiaJS? Thanks @AbhiPrasad !

AbhiPrasad commented 6 months ago

A more formal guide will take atleast a couple months, the team is busy with some other projects. That does suck though - so here's a quick guide I just tested that you can try!

You can set up Sentry.init with Sentry.browserTracingIntegration based on https://github.com/getsentry/sentry-javascript/discussions/8528 to get distributed tracing/performance monitoring working. You can use tracePropagationTargets to set up what endpoints/services you want to do frontend -> backend distributed tracing.

Therefore your SDK init should look something like so:

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

Sentry.init({
  dsn: __SENTRY_DSN__,
  integrations: [
    Sentry.browserTracingIntegration({
      // disable automatic span creation
      instrumentNavigation: false,
      instrumentPageLoad: false,
    }),
    // optionally add session replay - https://docs.sentry.io/platforms/javascript/session-replay/
  ],
  // We recommend adjusting this value in production, or using tracesSampler for finer control
  tracesSampleRate: 1.0,
  // Set `tracePropagationTargets` to control for which URLs distributed tracing should be enabled
  tracePropagationTargets: ["localhost", /^https:\/\/yourserver\.io\/api/],
});

// We start the pageload span as early as possible!
let name = route().current();
let pageLoadSpan = Sentry.startBrowserTracingPageLoadSpan({ op: "pageload", name });

router.on("before", (route) => {
  const client = Sentry.getClient();

  const newName = route().current();
  if (newName !== name) {
    name = newName;
    // every time the route changes, we trigger a navigation
    Sentry.startBrowserTracingNavigationSpan(client, {
        op: "navigation",
        name,
        attributes: {
          [Sentry.SEMANTIC_ATTRIBUTE_SENTRY_SOURCE]: "route",
        },
      });
    }
  }
});

router.on('finish', () => {
  name = route().current();
  // always make sure we are using the correct route name
  const span = Sentry.getActiveSpan();
  const op = span && Sentry.spanToJSON(span).op;
  if (op === 'pageload' || op === 'navigation') {
    span.setName(name);
  }
});

To link your backend to your frontend pageload, you'll need to serialize meta tags into the HTML that the SDK can pick up on. Here's a guide: https://docs.sentry.io/platforms/php/guides/laravel/distributed-tracing/custom-instrumentation/#inject-tracing-information-into-rendered-html

<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8">
        <!-- Serialize span information from active span in PHP land to HTML -->
        <?= sprintf('<meta name="baggage" content="%s"/>', \Sentry\getBaggage()); ?>
        <?= sprintf('<meta name="sentry-trace" content="%s"/>', \Sentry\getTraceparent()); ?>
    </head>
    <body>
        <p>This is a website.</p>
    </body>
</html>

With that both set up, you should be good to go!

RChutchev commented 6 months ago

A more formal guide will take atleast a couple months, the team is busy with some other projects. That does suck though - so here's a quick guide I just tested that you can try!

You can set up Sentry.init with Sentry.browserTracingIntegration based on #8528 to get distributed tracing/performance monitoring working. You can use tracePropagationTargets to set up what endpoints/services you want to do frontend -> backend distributed tracing.

Therefore your SDK init should look something like so:

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

Sentry.init({
  dsn: __SENTRY_DSN__,
  integrations: [
    Sentry.browserTracingIntegration({
      // disable automatic span creation
      instrumentNavigation: false,
      instrumentPageLoad: false,
    }),
    // optionally add session replay - https://docs.sentry.io/platforms/javascript/session-replay/
  ],
  // We recommend adjusting this value in production, or using tracesSampler for finer control
  tracesSampleRate: 1.0,
  // Set `tracePropagationTargets` to control for which URLs distributed tracing should be enabled
  tracePropagationTargets: ["localhost", /^https:\/\/yourserver\.io\/api/],
});

// We start the pageload span as early as possible!
let name = route().current();
let pageLoadSpan = Sentry.startBrowserTracingPageLoadSpan({ op: "pageload", name });

router.on("before", (route) => {
  const client = Sentry.getClient();

  const newName = route().current();
  if (newName !== name) {
    name = newName;
    // every time the route changes, we trigger a navigation
    Sentry.startBrowserTracingNavigationSpan(client, {
        op: "navigation",
        name,
        attributes: {
          [Sentry.SEMANTIC_ATTRIBUTE_SENTRY_SOURCE]: "route",
        },
      });
    }
  }
});

router.on('finish', () => {
  name = route().current();
  // always make sure we are using the correct route name
  const span = Sentry.getActiveSpan();
  const op = span && Sentry.spanToJSON(span).op;
  if (op === 'pageload' || op === 'navigation) {
    span.setName(name);
  }
});

To link your backend to your frontend pageload, you'll need to serialize meta tags into the HTML that the SDK can pick up on. Here's a guide: https://docs.sentry.io/platforms/php/guides/laravel/distributed-tracing/custom-instrumentation/#inject-tracing-information-into-rendered-html

<!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8">
        <!-- Serialize span information from active span in PHP land to HTML -->
        <?= sprintf('<meta name="baggage" content="%s"/>', \Sentry\getBaggage()); ?>
        <?= sprintf('<meta name="sentry-trace" content="%s"/>', \Sentry\getTraceparent()); ?>
    </head>
    <body>
        <p>This is a website.</p>
    </body>
</html>

With that both set up, you should be good to go!

Hello, it looks like your solution has errors in syntaxis first of all. this part

router.on("before", (route) => {
  const client = Sentry.getClient();

  const newName = route().current();
  if (newName !== name) {
    name = newName;
    // every time the route changes, we trigger a navigation
    Sentry.startBrowserTracingNavigationSpan(client, {
        op: "navigation",
        name,
        attributes: {
          [Sentry.SEMANTIC_ATTRIBUTE_SENTRY_SOURCE]: "route",
        },
      });
    }
  }
});

should be

router.on("before", (route) => {
    const client = Sentry.getClient();

    const newName = route().current();
    if (newName !== name) {
        name = newName;
        // every time the route changes, we trigger a navigation
        Sentry.startBrowserTracingNavigationSpan(client, {
            op: "navigation",
            name,
            attributes: {
                [Sentry.SEMANTIC_ATTRIBUTE_SENTRY_SOURCE]: "route",
            },
        });
    }
});

this one too has a syntaxis error

router.on('finish', () => {
    name = route().current();
    // always make sure we are using the correct route name
    const span = Sentry.getActiveSpan();
    const op = span && Sentry.spanToJSON(span).op;
    if (op === 'pageload' || op === 'navigation) {
        span.setName(name);
}
});

should be

router.on('finish', () => {
    name = route().current();
    // always make sure we are using the correct route name
    const span = Sentry.getActiveSpan();
    const op = span && Sentry.spanToJSON(span).op;
    if (op === 'pageload' || op === 'navigation') {
        span.setName(name);
}
});
RChutchev commented 6 months ago

@AbhiPrasad, thanks for offering a solution, anyway it's working better but only partially working for me, 2 different problems, I'm getting errors in the console for

route().current();

but looks like that's happening because we're loading the non-Inertia page as an iframe. I'll think about how to fix that, but that's our application-specific problem, I think I should exclude this request from the Sentry workflow. Or maybe you know, how to exclude all iframes from Sentry recording? Screenshot 2024-06-06 at 7 26 16 AM Screenshot 2024-06-06 at 8 01 52 AM Screenshot 2024-06-06 at 7 27 12 AM

The second one is weird, with the new 8 ver, sometimes Sentry records some transactions as not connected to each other (for example if the user reloads the page a new TraceID will be created, and used but only the Backend event will be recorded with this ID, and this page load event will be recorded with a new TraceID and all next events will be recorded together with this ID, but root transaction will be missing), maybe that's the same problem - our application-specific but looks like something went wrong here. See an example Screenshot 2024-06-06 at 7 51 12 AM Screenshot 2024-06-06 at 7 51 22 AM after some navigation... Screenshot 2024-06-06 at 7 58 17 AM

For pure Laravel everything works good Screenshot 2024-06-06 at 7 39 46 AM

xyNNN commented 5 months ago

@RChutchev The problem with route() is that ne first parameter in the callback is called also route ... so this will be a conflict. Change the parameter so something else and this problem will solve. Currently integrating and will give feedback asap

xyNNN commented 5 months ago

For me the solution doesn't work ... the two transactions are linked in the trace, yes .. this works ... but no replay is recorded .... and I don't know why..

RChutchev commented 5 months ago

@RChutchev The problem with route() is that ne first parameter in the callback is called also route ... so this will be a conflict. Change the parameter so something else and this problem will solve. Currently integrating and will give feedback asap

Nope, I'm pretty sure that this error is because we're loading the page into an iframe but this page is a non-inertia page, a request to an ordinary Laravel page will cause this error. In a pure Laravel page no route().current is set, that's the reason why we're getting this error.

If you are interested you can check the production URL that's our corp service like Linktree. Currently, Sentry v.7 is in use, but I hope the new v.8 integration will be finished this month. In the user area (the faster way to register a Google OAuth) we have a preview (right panel) that loads a pure Laravel page (non-inertia). This request causes a problem in my case.

RChutchev commented 5 months ago

For me the solution doesn't work ... the two transactions are linked in the trace, yes .. this works ... but no replay is recorded .... and I don't know why..

Wow, can you show a screenshot of this issue, b/c I'm not sure that that's a problem. All transactions should be connected in distributed tracing, which looks like a normal behavior. For example, when the user opens /home page in distributed tracing we should be able to see backend rendering and frontend rendering (pageload) than if the user clicks on another link (e.g. /page1) we should see http.server and pageload for this page with same trace. @AbhiPrasad Am I right?

In addition, to record a trace in the Init Sentry.replayIntegration should be enabled,

Sentry.replayIntegration({
      maskAllText: false,
      blockAllMedia: false
}),

Did you set up something like this?

RChutchev commented 5 months ago

@xyNNN I'm sorry in my email notification I saw another of your comments here but now I don't see those, I was a little bit busy with offline activity. Also, sometimes I had one or two days of internet connection problems like the day before yesterday, that country-specific problem haha. Here temperature is above +35 degrees for the night and +45 for the day, sometimes that causes an outage for the provider's backbone equipment and a planned power outage for at least 2 hours per day. Active development for our corp project is finished, and now we're live, only critical fixes should be provided according to an agreement with my customer, upgrade Sentry integration to v.8 is not a critical and important fix, b/c v.7 still working well. I hope the new version of our corporate project will be agreed upon Dec. 2024 and active development will be continued in Jan 2025.

But I'm still online and I'm pretty sure that we find a solution to implement this integration for Senty v.8 and Inertia as soon as possible, when the integration is tested and works I'll update the production server outside the range of critical/scheduled updates

xyNNN commented 5 months ago

Thank you very much @RChutchev

Where are you currently? These temperatures are insane.... wish you all the best wish your power supply and stay hydrated and keep safe!

At first to clarify something. The provided solution from @AbhiPrasad is for Sentry v8, right? Because the syntax for v7 is not compatible in several ways.

RChutchev commented 2 months ago

Thank you very much @RChutchev

Where are you currently? These temperatures are insane.... wish you all the best wish your power supply and stay hydrated and keep safe!

At first to clarify something. The provided solution from @AbhiPrasad is for Sentry v8, right? Because the syntax for v7 is not compatible in several ways.

Hi, oh sorry I missed your message, I'm currently in Egypt, yep, that's hard, thanks. Yep, power supply and outages are real problems sometimes, but I got one Energizer power bank for 50K with QC 3, and one Anker for about 27.5K (250W), and now that's okay hahaha

RChutchev commented 2 months ago

Hi, @AbhiPrasad, are there any updates about Sentry-Inertia integration docs, or is this the only solution you offered us earlier? Or maybe @xyNNN, did you find any solution to stable integration for Sntry and Inertia?

xyNNN commented 1 month ago

@RChutchev Currently not. This topic is on hold until the team of @AbhiPrasad extend the documentation how to solve this issue with Inertia.

RChutchev commented 1 month ago

@xyNNN, okay, but honestly, I think it will never be extended/updated. Finally, it looks like I did it. After literally 12 h., of reading all available docs (migration guide, Node.JS, VueJS, pure JavaScript, etc), and got this work.
I'll check it out on production on the next planned release (today) and after some time (about 3-5 days) I'll text here about the results. In case I forget, if you are interested, tag me here.

xyNNN commented 1 month ago

@RChutchev I would be very interested in your solution! Thank you in advance!

RChutchev commented 1 month ago

@RChutchev I would be very interested in your solution! Thank you in advance!

Hi, finally we tested the code, and that's working, but I've some questions about Sentry Tracing, to be sure that Tracing works correctly with this code, I'll ask the Sentry representative in the next message, but now I'll provide my example.

/* Import modules... */
import { createApp, h } from "vue";
import { createInertiaApp, router } from "@inertiajs/vue3";
import * as Sentry from "@sentry/browser";

createInertiaApp({
    id: "app",
    resolve: (name) => require(`./Pages/${name}`).default,
    setup({ el, App, props, plugin }) {
        const app = createApp({ render: () => h(App, props) })
            .use(plugin)
            .mixin({ methods: { route } });

        const SentryClient = Sentry.init({
                app,
                dsn: ___APP_SENTRY_DSN_PUBLIC___,
                integrations: [
                    Sentry.browserTracingIntegration({
                        // disable automatic span creation
                        instrumentNavigation: false,
                        instrumentPageLoad: false,
                        enableInp: true,
                    }),
                    Sentry.replayIntegration({
                        maskAllText: false,
                        blockAllMedia: false
                    }),
                    Sentry.replayCanvasIntegration(),
                    Sentry.captureConsoleIntegration({
                        // Add 'warn', 'error', 'debug', 'assert' console output types to Sentry
                        // and additionally possible to add 'log' and 'info' console output types to Sentry if required
                        levels: ["warn", "error", "debug", "assert"]
                    }),
                    Sentry.contextLinesIntegration({
                        frameContextLines: 50
                    }),
                    Sentry.httpClientIntegration()
                ],

                sendDefaultPii: true,
                tracesSampleRate: ___SENTRY_TRACES_SAMPLE_RATE___,
                replaysSessionSampleRate: 0.1,
                replaysOnErrorSampleRate: 1.0,
                autoSessionTracking: true,
                telemetry: false,
                denyUrls: [
                    // All browser extensions
                    /extensions\//i,
                    /^safari-extension:\/\//i,
                    /^safari-web-extension:\/\//i,
                    /^moz-extension:\/\//i,
                    /^chrome:\/\//i,
                    /^chrome-extension:\/\//i,
                    /moz-extension/i
                ],
                ignoreErrors: [
                    // Let's ignore some output as unnecessary for us now
                    "@webkit-masked-url",
                    "debugger eval",
                    "Not implemented",
                    "top.GLOBALS"
                    //'Failed to fetch',
                ]
            });
            // We start the pageload span as early as possible!
            let name = '/'+route().current();
            console.info('Pageload: '+name)
            let pageLoadSpan = Sentry.startBrowserTracingPageLoadSpan(SentryClient,{ op: "pageload", name });

        // Let's mount el
        app.mount(el);

        router.on("start", (event) => {
            const client = Sentry.getClient();
            const newName = event.detail.visit.url.pathname;
            console.info('On start, Old: '+name+' | New: '+newName);
            if (newName && newName !== name) {
                /* Navigation to other page e.g. /home -> /new-page
                console.log('Location changed (navigation)'); */
                name = newName;
                Sentry.startBrowserTracingNavigationSpan(client, {
                    op: "navigation",
                    name,
                    attributes: {
                        [Sentry.SEMANTIC_ATTRIBUTE_SENTRY_SOURCE]: "route",
                    },
                });
            } else {
                /* Navigation to same page e.g. /home -> /home without page reload (INERTIA) */
                /* console.log('Location same (same-page navigation)'); */
                name = newName;
                Sentry.startBrowserTracingNavigationSpan(client, {
                    op: "navigation",
                    name,
                    attributes: {
                        [Sentry.SEMANTIC_ATTRIBUTE_SENTRY_SOURCE]: "route",
                    },
                });
            }
        })

        router.on('finish', () => {
            name = '/'+route().current();
            /* console.log('Finish - Current: '+name); */
            if (name){
                const span = Sentry.getActiveSpan();
                console.info('On finish (trace_id): '+span?._traceId+' | SpanID: '+span?._spanId);
                const op = span && Sentry.spanToJSON(span).op;
                /* console.log('On finish (OP): '+op) */
                if (op === 'pageload' || op === 'navigation') {
                    if (span){
                        span.updateName(name);
                    }
                }
            }
        });
    }
});

In this code, you should set _APP_SENTRY_DSN_PUBLIC__ and SENTRY_TRACES_SAMPLE_RATE___, for test hardcoded values can be used as well.

In your app.blade.php file add this code

    use function Sentry\getBaggage;
    use function Sentry\getTraceparent;

and this in your <head>

    <?= sprintf('<meta name="baggage" content="%s"/>', getBaggage()); ?>
    <?= sprintf('<meta name="sentry-trace" content="%s"/>', getTraceparent()); ?>
RChutchev commented 1 month ago

As a result, you will get something like this on the pagelaod event Image and on navigation event Image

Some values, requests, and queries were masked because my customer would not be so happy if I published real production screenshots, hahaha. Because some of the customer's websites are now under attack (small, about 1000 req/min) but anyway we wouldn't like to give any additional info about internal infrastructure.

RChutchev commented 1 month ago

Hi, @Lms24 could you please participate once again? b/c Mr. @AbhiPrasad has ignored us since 6 June'24.

It looks insane as a company representative to provide a demo JS code that contains at least syntax errors, and some errors in your own functions (span.updateName, for example, it was span.setName), but okay. Here's the link: https://github.com/getsentry/sentry-javascript/issues/11362#issuecomment-2148484699 to his comment.

This comment contains my real example related to this Sentry-Inertia SDK integration: https://github.com/getsentry/sentry-javascript/issues/11362#issuecomment-2400733402 based on AbhiPrasad's example.

Finally, now we have time to move to ver. 8, and looks like it's working, but I've some questions, about Sentry Tracing behavior with Inertia especially and I am not sure that behavior is absolutely correct.

1) Is that correct behavior to create a new transaction on the navigation event at all and in Inertia especially (/pageA -> /pageB)? For me it looks a little weird to crate different transactions on startBrowserTracingNavigationSpan b/c the navigation event doesn't fully reload the page only partially (https://inertiajs.com/partial-reloads), that's InertiaJS. But according to these PRs https://github.com/getsentry/sentry-javascript/issues/10634, https://github.com/getsentry/sentry-javascript/pull/10656 looks like that's correct behavior, okay, I just wanna be sure.

2) Is that correct behavior to create a new transaction on partial reload (yep, that's the same navigation event) but the user is still on the same page (/pageA -> /pageA, GET request /enable-something)? For example, in this case, a user on /page-A clicks a Button (e.g. Create2FaCodes) which starts an Inertia GET/POST/DELETE request to /do-something, which causes partial reload (e.g. Show2FaQR or Show2FaRecoveryCodes) but the user is still on /page-A, and only a part of the page will be changed (some parts are deleted or/and added). Now Sentry creates TraceID for /page-A (pageload) and adds an http.server and that looks like correct behavior. But when the user clicks the Button on Sentry a new TraceID will be created with navigation and http.server, but that looks a little bit weird. See my previous comment with screenshots.

For me, trying to fix something and integrate it without fully understanding how it should work is not as good an idea. Lms24, I will be so appreciative of your answers.

RChutchev commented 1 month ago

@xyNNN Also, I forgot to give additional comments about this part of the code, because, the "else" branch wasn't present in Mr. AbhiPrasad's code example. This part can be simplified for production use, all 'if, else' can be deleted and use only code inside as a router.on('start'... function code, but I keep it because that's same page navigation event (e.g. user clicks on the menu to the current page and it's cause a partial reload) and that's available in our project for now. Some users can delete the "else" branch if the same page navigation isn't in their project (e.g. clicking on the current menu option doesn't cause a partial reload).

if (newName && newName !== name) {
                /* Navigation to other page e.g. /home -> /new-page
                console.log('Location changed (navigation)'); */
                name = newName;
                Sentry.startBrowserTracingNavigationSpan(client, {
                    op: "navigation",
                    name,
                    attributes: {
                        [Sentry.SEMANTIC_ATTRIBUTE_SENTRY_SOURCE]: "route",
                    },
                });
            } else {
                /* Navigation to same page e.g. /home -> /home without page reload (INERTIA) */
                /* console.log('Location same (same-page navigation)'); */
                name = newName;
                Sentry.startBrowserTracingNavigationSpan(client, {
                    op: "navigation",
                    name,
                    attributes: {
                        [Sentry.SEMANTIC_ATTRIBUTE_SENTRY_SOURCE]: "route",
                    },
                });
            }
Lms24 commented 1 month ago

Hi everyone, hey @RChutchev

First of all, no-one is ignoring you on purpose and I'd politely ask you to refrain from insinuating that we're doing that. On the contrary, Abhi went above and beyond and provided you code as a starting point for a framework we don't even officially support (as in, no docs). Likewise, I don't share the sentiment that it's "insane" that we provide code with syntax errors. I'd rather argue it's deeply human (and a sign that we're in fact not chat bots :) ). Please understand that we (JS SDK team) deal with an incredible high load of issues (~100 per week as of late) to work through. Moreover, your issue was closed, so our internal alerting system about new replies isn't notifying us, leaving it up to our private GH notifications.


General update on Inertia: We still don't have docs. It's on our radar but we have to make hard priorization decisions every day. I'm sorry about the wait but at the same time can't give you an ETA.


Getting back to the issue, I believe the open questions for me to answer are here:

Is that correct behavior to create a new transaction on the navigation event at all and in Inertia especially (/pageA -> /pageB)?

I wrote this already in a post above somewhere but I do believe that a partial reload should be covered by a navigation transaction, if it behaves like a SPA "soft" navigation. This means that usually, the Browser's History state (i.e. route) changes and we want to track this, along with any child spans, with a navigation transaction.

Is that correct behavior to create a new transaction on partial reload (yep, that's the same navigation event) but the user is still on the same page (/pageA -> /pageA, GET request /enable-something)?

I'd argue this is up to you to decide :) Generally, our routing instrumentations do create a new navigation span because as you outlined, even a navigation from A to A can cause new resource loads, server request, etc and we want to trace these requests and connect them frontend to backend. Since you're writing your own routing instrumentation here, you can handle this as you please. There's no right or wrong here, just what the respective routing instrumentation author thinks should happen.

There's one thing that's currently a bit unexpected in the last screenshot you posted above: The pageload transaction and the navigation transaction are part of the same trace. Given that the pageload transaction happens after the navigation transaction, I guess this means that somehow startBrowserTracingPageloadSpan is called for a partial reload? Not sure. To be clear, this is not impacting behaviour negatively, it's just a bit unexpected for me to see. But if you're fine with it, it's totally okay to leave things as-is.

Hope this helps!

RChutchev commented 1 month ago

Hi, @Lms24!

All right, first of all, I want to say thank you for your professionalism and your speed of response.


1) All right, got it, that means we have gotten the right behavior as a result. 2) Okay, I asked about Sentry's point of view for this function (I mean how this should work out of the box), and I got it, thanks. 3) Yes, about the last screenshot, yes, if you mean 'preview' event, that's iFrame loading with a non-SPA page (Laravel blades + with JQuery) that's our project-specific, and totally fine if we've got this into one trace with a SPA pageload/navigation event.

The bottom line is that the variant I provided above works correctly, and has some settings specific to us, but @xyNNN, and anyone else, feel free to ask about integration using this code. I'll try to help in whatever I can.


PS: I do not want to debate with you, but a response time over 4 months is not a deeply human thing to do, and this is my opinion and I am allowed to have it. About syntaxis errors in code, yes, that's a deelpy human part of this, you're right, but means that he didn't check/test this before the post. In advance, I have no criticism of you, Mr. Lukas. If you (I mean Sentry staff, and you on behalf) cannot accept well-founded criticism towards staff, then politely forgive me. Moreover, I didn't close this issue, this issue was closed by Sentry staff, is am I responsible for that? FYI: I (like everyone non-staff here) have no right to reopen issues on GH in your repo.

After upgrading from version 7 to 8 (finally, GitHub Dependabot updated us to the new 8 version, because of security issues), our application was broken in all parts (admin and user areas) where Sentry was added. My client has requested twice to change Sentry to NewRelic (I discouraged the transition because of the lack of some features like User Feedback). I really hope that the next updates in the future will be nonbraking and bring us only new functions.

PPS: I apologize in advance for the possibly harsh manner of my comments, I do not feel well, I am on sick leave with a body temperature above 38 degrees Celsius, and a suspicion of new COVID bla-bla-bla, with limited access to the European level of medicine in the country where I am currently located. I don't wanna be rude, but everyone wants to get a reply at least once per week.

xyNNN commented 1 month ago

@RChutchev Thank you very much for your implementation, it works very well! Now the backend and frontend issues are connected each other. Thanks! How can I give you a tip for it? And get well soon!

RChutchev commented 1 month ago

@RChutchev Thank you very much for your implementation, it works very well! Now the backend and frontend issues are connected each other. Thanks! How can I give you a tip for it? And get well soon!

That sounds good! If you have any further questions, feel free to text me. Yes, please. Thanks.

xyNNN commented 1 month ago

@RChutchev Thank you very much for your implementation, it works very well! Now the backend and frontend issues are connected each other. Thanks! How can I give you a tip for it? And get well soon!

That sounds good! If you have any further questions, feel free to text me. Yes, please. Thanks.

Thanks mate! And what you mean with "Yes, please. Thanks" - the tip? And if yes, where I can give you to it?

The only issue is that now when I throwing an exception on backend site, this will generate in 2 Sentry issues which appears on the same trace. The one for the backend where the error are thrown. The other one from axios the http library which is the issue for the frontend that a request fails ... and there the replay is linked. But I don't want the issue for the frontend, because it's only a backend issue ... Do you know what I mean?

RChutchev commented 1 month ago

@RChutchev Thank you very much for your implementation, it works very well! Now the backend and frontend issues are connected each other. Thanks! How can I give you a tip for it? And get well soon!

That sounds good! If you have any further questions, feel free to text me. Yes, please. Thanks.

Thanks mate! And what you mean with "Yes, please. Thanks" - the tip? And if yes, where I can give you to it?

The only issue is that now when I throwing an exception on backend site, this will generate in 2 Sentry issues which appears on the same trace. The one for the backend where the error are thrown. The other one from axios the http library which is the issue for the frontend that a request fails ... and there the replay is linked. But I don't want the issue for the frontend, because it's only a backend issue ... Do you know what I mean?

Any time. Yep, exactly. I mean "Yes, please" - the tip, and thanks to "And get well soon!"

I'm not 100% sure I got it, can u please attach a screenshot for a better understanding? Because that should be something like this, Image Yes, I know that for the frontend but should be the same. One issue for one backend issue, if this issue does not cause fronted exception, etc. You should check the console in the newly created issue and SDK info (like Name), and in Event Highlights check the "level", is it the same? Please provide all information that you can and, maybe that's required to call Sentry staff again.

I have seen in my Sentry two different issues for the same error, but one was something like an "Alpine Expression Error: " and the other one was a real "SyntaxError: Unexpected token", did you mean something like this? The first one is a warning Image The next one is the error Image One issue includes another one... yes it looks like that's the same issue, but one has a warning level, and another one has an error level, looks like that's not so convenient, but that's correct behavior.

xyNNN commented 1 month ago

Thanks for your feedback, attached a screenshot that's describes my problem. On the right side you can see that on this trace are linked two errors. The second is the real issue, where the exception was thrown backend site. The first was is not a real issue, because it's only the http client on client side that getting the HTTP 500 response ... it's the same and not a specific frontend issue. But the replay is linked to the first error. I would like to have only one issue, with the real exception and the replay should be linked to this one. Is it now clear?Image

xyNNN commented 1 month ago

@RChutchev And where can I give you a tip? You haven't enabled sponsorship at GitHub. Do you have another way to get you a tip? Thanks in advance!

RChutchev commented 1 month ago

@RChutchev And where can I give you a tip? You haven't enabled sponsorship at GitHub. Do you have another way to get you a tip? Thanks in advance!

Oh, hahaha, there was some misunderstanding. I wrongly understood your first sentence. I understood you meant 'tip' like 'advice' in your first message. Okay, that's absolutely unnecessary, but if you really want to, I can enable that in GitHub, I so appreciate that.

That was a high body temperature, lol. Sorry for the misunderstanding (:

RChutchev commented 1 month ago

Thanks for your feedback, attached a screenshot that's describes my problem. On the right side you can see that on this trace are linked two errors. The second is the real issue, where the exception was thrown backend site. The first was is not a real issue, because it's only the http client on client side that getting the HTTP 500 response ... it's the same and not a specific frontend issue. But the replay is linked to the first error. I would like to have only one issue, with the real exception and the replay should be linked to this one. Is it now clear?Image

Hi, sorry I was a little bit busy. I'm back at my on-site work after a COVID-related illness.

Yes, I got it. That's a test exception from your backend (Laravel). I think you added this from the Sentry doc or some example when setting up a Sentry log coming from Laravel. Just try to search This is a test exception in your all Laravel code (with IDE, choose along all project files). It looks like that's coming from some PHP file (controller or provider, I think), I hope it's only one result will be found. Anyway, let me know about the results.

xyNNN commented 1 month ago

Thanks for your feedback, attached a screenshot that's describes my problem. On the right side you can see that on this trace are linked two errors. The second is the real issue, where the exception was thrown backend site. The first was is not a real issue, because it's only the http client on client side that getting the HTTP 500 response ... it's the same and not a specific frontend issue. But the replay is linked to the first error. I would like to have only one issue, with the real exception and the replay should be linked to this one. Is it now clear?Image

Hi, sorry I was a little bit busy. I'm back at my on-site work after a COVID-related illness.

Yes, I got it. That's a test exception from your backend (Laravel). I think you added this from the Sentry doc or some example when setting up a Sentry log coming from Laravel. Just try to search This is a test exception in your all Laravel code (with IDE, choose along all project files). It looks like that's coming from some PHP file (controller or provider, I think), I hope it's only one result will be found. Anyway, let me know about the results.

That was my test exception, which I added to check if the error tracking works in conjunction with replays. The problem is that for one issue (the exception in the backend), two errors are logged. The frontend error is actually completely irrelevant because it’s only logging the 500 error from the backend… but the replay is linked to it. Ideally, I would only expect the backend error, and the replay should be associated with that.

Does that make sense? ;)

xyNNN commented 1 month ago

@RChutchev And where can I give you a tip? You haven't enabled sponsorship at GitHub. Do you have another way to get you a tip? Thanks in advance!

Oh, hahaha, there was some misunderstanding. I wrongly understood your first sentence. I understood you meant 'tip' like 'advice' in your first message. Okay, that's absolutely unnecessary, but if you really want to, I can enable that in GitHub, I so appreciate that.

That was a high body temperature, lol. Sorry for the misunderstanding (:

Please enable it ;)

RChutchev commented 1 month ago

Thanks for your feedback, attached a screenshot that's describes my problem. On the right side you can see that on this trace are linked two errors. The second is the real issue, where the exception was thrown backend site. The first was is not a real issue, because it's only the http client on client side that getting the HTTP 500 response ... it's the same and not a specific frontend issue. But the replay is linked to the first error. I would like to have only one issue, with the real exception and the replay should be linked to this one. Is it now clear?Image

Hi, sorry I was a little bit busy. I'm back at my on-site work after a COVID-related illness. Yes, I got it. That's a test exception from your backend (Laravel). I think you added this from the Sentry doc or some example when setting up a Sentry log coming from Laravel. Just try to search This is a test exception in your all Laravel code (with IDE, choose along all project files). It looks like that's coming from some PHP file (controller or provider, I think), I hope it's only one result will be found. Anyway, let me know about the results.

That was my test exception, which I added to check if the error tracking works in conjunction with replays. The problem is that for one issue (the exception in the backend), two errors are logged. The frontend error is actually completely irrelevant because it’s only logging the 500 error from the backend… but the replay is linked to it. Ideally, I would only expect the backend error, and the replay should be associated with that.

Does that make sense? ;)

Okay, looks like yes, and it makes sense. Maybe that's two different errors, one reported by one part of the application and another one by another, maybe it is one error reported by different catch methods, is it better to contact you directly? Are you using WhatsApp or something like that?

RChutchev commented 1 month ago

@RChutchev And where can I give you a tip? You haven't enabled sponsorship at GitHub. Do you have another way to get you a tip? Thanks in advance!

Oh, hahaha, there was some misunderstanding. I wrongly understood your first sentence. I understood you meant 'tip' like 'advice' in your first message. Okay, that's absolutely unnecessary, but if you really want to, I can enable that in GitHub, I so appreciate that. That was a high body temperature, lol. Sorry for the misunderstanding (:

Please enable it ;)

Okay, thanks, I turned it on :)

xyNNN commented 1 month ago

@RChutchev Yes ... they are reported by two parts of the application, but when I disable it (the frontend error) the problem is that the replay is not linked anymore.. it's linked to the frontend issue and not the backend issue where the error occurs .... perhaps @AbhiPrasad @Lms24 have an idea?

Sure I've WhatsApp but I don't want to paste my number here in public :P

RChutchev commented 1 month ago

@RChutchev Yes ... they are reported by two parts of the application, but when I disable it (the frontend error) the problem is that the replay is not linked anymore.. it's linked to the frontend issue and not the backend issue where the error occurs .... perhaps @AbhiPrasad @Lms24 have an idea?

Sure I've WhatsApp but I don't want to paste my number here in public :P

Okay, no problem you can text me directly from my personal contact page.

RChutchev commented 1 month ago

@xyNNN, I received the notification, thank you so much for the tips ;) And, as more convenient for you, please add information about SDK for both reported issues from Sentry SDK, here or via WhatsApp directly. Image

Lms24 commented 1 month ago

Hi @xyNNN iiuc, you only catch the frontend error because you want the actual backend error linked to the replay, correct?

By the way, the frontend error looks like it's coming from SentryIntegrations.httpClientIntegration(). By default we don't catch http responses with erroneous codes.

As far as I can tell from the trace screenshot, the replay is started before the navigation span, right? When you start the navigation, you probably make some fetch requests to the backend where the server is running. You can try opening your devtools and check for the baggage http header in the request. If a replay was active at this time, it should contain a sentry-replayId key (or something along these lines). The receiving backend SDK should actually pick up this header and add a replay id to the error so that it can be linked.

Let's try to see if part 1 (baggage containing the replay id) works.

xyNNN commented 1 month ago

Thank you @Lms24 for your fast response. I checked the baggage header and can't find replay id. Also on the backend http request which results a exception the replayId is not present in the baggage header. My configuration is defined that a replay should exists when a error occurs, replay session rate is 0.1 by default (see configuration attached). And yes you're right the other error is thrown by the http client integration (Sentry.httpClientIntegration()). I've removed it and know I've only the correct backend error but without linked replay.

sendDefaultPii: true,
tracesSampleRate: 1.0,
replaysSessionSampleRate: 0.1,
replaysOnErrorSampleRate: 1.0,
autoSessionTracking: true,