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

Trying to Connect Frontend Clicks to Traces. #675

Open
harshaj-toorak opened this issue Sep 12, 2024 · 3 comments
Open

Trying to Connect Frontend Clicks to Traces. #675

harshaj-toorak opened this issue Sep 12, 2024 · 3 comments
Labels
bug Report a bug

Comments

@harshaj-toorak
Copy link

harshaj-toorak commented Sep 12, 2024

`

Description

import React, { useEffect } from 'react';
import { LogLevel } from '@grafana/faro-web-sdk';
import { trace, context } from '@opentelemetry/api'; // Import OpenTelemetry APIs

const withClickTracking = (WrappedComponent) => {
return (props) => {
useEffect(() => {
const handleClick = (event) => {
const element = event?.target;
const elementType = element?.tagName?.toLowerCase();
const elementId = element?.id ? #${element?.id} : '';
const elementText = element?.innerText ? : ${element?.innerText} : '';

    // Retrieve the active span from the OpenTelemetry tracer
    const tracer = trace.getTracer('default');
    const activeSpan = tracer.startSpan('click-event', undefined, context.active());
    const traceId = activeSpan.spanContext().traceId;  // Get the trace ID

    const logMessage = `User clicked on ${elementId}${elementText}, traceId: ${traceId}`;
    const faro = window.faro;

    // Log the click event with Faro, including trace ID
    faro && (elementType === "span" || elementType === "button") && faro.api.pushLog([logMessage], {
      level: LogLevel.INFO,
      attributes: {
        elementType,
        elementId,
        elementText, // Pass the traceId to the log attributes
      },
      traceId:traceId,
      trace,
      activeSpan
    });

    // End the span after the event is handled
    activeSpan.end();
  };

  document.addEventListener('click', handleClick);

  return () => {
    document.removeEventListener('click', handleClick);
  };
}, []);

return <WrappedComponent {...props} />;

};
};

export default withClickTracking;
`

This is my Initialization for the faro instance

import { useEffect } from "react";
import {
ConsoleInstrumentation,
ErrorsInstrumentation,
FetchTransport,
initializeFaro,
getWebInstrumentations,
SessionInstrumentation,
WebVitalsInstrumentation,
ConsoleTransport
} from "@grafana/faro-web-sdk";
import { trace, context } from "@opentelemetry/api";
import { ZoneContextManager } from "@opentelemetry/context-zone";
import { W3CTraceContextPropagator } from "@opentelemetry/core";
import { registerInstrumentations } from "@opentelemetry/instrumentation";
import { UserInteractionInstrumentation } from "@opentelemetry/instrumentation-user-interaction";
import { DocumentLoadInstrumentation } from "@opentelemetry/instrumentation-document-load";
import { FetchInstrumentation } from "@opentelemetry/instrumentation-fetch";
import { XMLHttpRequestInstrumentation } from "@opentelemetry/instrumentation-xml-http-request";
import { Resource } from "@opentelemetry/resources";
import {
BatchSpanProcessor,
WebTracerProvider
} from "@opentelemetry/sdk-trace-web";
import {
SEMRESATTRS_SERVICE_NAME,
SEMRESATTRS_SERVICE_VERSION
} from "@opentelemetry/semantic-conventions";
import {
FaroSessionSpanProcessor,
FaroTraceExporter,
TracingInstrumentation
} from "@grafana/faro-web-tracing";
import {
createReactRouterV6Options,
ReactIntegration
} from "@grafana/faro-react";
import { getAPIURL } from "OP_WEB_COMMON_SERVICE/getAPIURL";
import {
createRoutesFromChildren,
matchRoutes,
Routes,
useLocation,
useNavigationType
} from "react-router-dom";

const useFaroInitializer = (url, apiKey, email, userId) => {
const apiURL = getAPIURL();

useEffect(() => {
if (!apiKey || !url || !email || !userId) return;

const instrumentationOptions = {
  propagateTraceHeaderCorsUrls: [
    new RegExp(`https://${apiURL.replace(/\./g, "\\.")}`)
  ]
};

const faro = initializeFaro({
  instrumentations: [
    ...getWebInstrumentations(),
    new TracingInstrumentation({
      instrumentations: [
        new FetchInstrumentation({
          url,
          propagateTraceHeaderCorsUrls: [
            new RegExp(`https://${apiURL.replace(/\./g, "\\.")}`)
          ]
        }),
        new XMLHttpRequestInstrumentation({
          url,
          propagateTraceHeaderCorsUrls: [
            new RegExp(`https://${apiURL.replace(/\./g, "\\.")}`)
          ]
        }),
        new UserInteractionInstrumentation({
          eventNames: ["click", "dblclick", "submit", "keypress"]
        })
      ]
    }),
    // new TracingInstrumentation({ instrumentationOptions }),
    new ErrorsInstrumentation(),
    new WebVitalsInstrumentation(),
    new ConsoleInstrumentation({ disabledLevels: [] }), // Capture console.log
    new SessionInstrumentation(),
    new ReactIntegration({
      router: createReactRouterV6Options({
        createRoutesFromChildren,
        matchRoutes,
        Routes,
        useLocation,
        useNavigationType
      })
    })
  ],
  transports: [
    new FetchTransport({
      url,
      apiKey
    }),
    new ConsoleTransport()
  ],
  app: {
    name: "frontend-dev",
    version: "1.0.0"
  }
});

const resource = Resource.default().merge(
  new Resource({
    [SEMRESATTRS_SERVICE_NAME]: "frontend-dev",
    [SEMRESATTRS_SERVICE_VERSION]: "1.0.0"
  })
);

const provider = new WebTracerProvider({ resource });

provider.addSpanProcessor(
  new FaroSessionSpanProcessor(
    new BatchSpanProcessor(new FaroTraceExporter({ ...faro }))
  )
);

provider.register({
  propagator: new W3CTraceContextPropagator(),
  contextManager: new ZoneContextManager()
});

const ignoreUrls = [url];

registerInstrumentations({
  instrumentations: [
    new DocumentLoadInstrumentation(),
    new FetchInstrumentation({ ignoreUrls }),
    new XMLHttpRequestInstrumentation({ ignoreUrls })
  ]
});

// register OTel with Faro
faro.api.initOTEL(trace, context);
console.log(context, "context");

faro.api.setUser({
  email,
  id: userId
});

// Optionally store the Faro instance globally for debugging
window.faroInstance = faro;

}, [url, apiKey, email, userId]);
};

export default useFaroInitializer;

I'm trying to connect the Frontend Logs to the backend so that it is clearly visible in the node graph that where the user clicked and what Backend calls were made but in that case it's not working. for only Backend api calls everyting looks fine.

Image

Expecting the Frontend calls like this as well.

Image

@harshaj-toorak harshaj-toorak added the bug Report a bug label Sep 12, 2024
@codecapitano
Copy link
Collaborator

@harshaj-toorak

api.pushLog(['test'], { spanContext }); attaches the trace context to the Loki log line.

Note: With Faro every Signal, besides Traces, is stored as a log in Loki.

Image

@harshaj-toorak
Copy link
Author

harshaj-toorak commented Sep 12, 2024

@codecapitano
After Adding this code i can see Tempo button over my loki logs but still what Backend calls are going that we are unable to see in the node graph.

const activeSpan = tracer.startSpan('click-event', undefined, context.active());
const traceId = activeSpan.spanContext().traceId; // Get the trace ID
const contextSpan = activeSpan.spanContext()

    const logMessage = `User clicked on ${elementId}${elementText}, traceId: ${traceId}`;
    const faro = window.faro;

    // Log the click event with Faro, including trace ID
    faro && (elementType === "span" || elementType === "button") && faro.api.pushLog([logMessage], contextSpan);

    // End the span after the event is handled
    activeSpan.end();

Image

@Raj-Ranveer
Copy link

I am facing facing very similar issue as this one do we have any solution for this @codecapitano | @harshaj-toorak

Thanks in advance..

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

No branches or pull requests

3 participants