Grafana Faro Web SDK

repository·main·Indexed 22 days ago

https://github.com/grafana/faro-web-sdk

A toolkit for instrumenting frontend JavaScript applications to collect and forward telemetry—including logs, traces, and metrics—to observability backends such as Grafana Cloud, Loki, or Tempo. It supports various frameworks including React, Next.js, Angular, Vue, Svelte, and Vanilla JS. Key features include automatic tracking of JavaScript errors, Core Web Vitals, and distributed tracing for API calls, as well as experimental support for session replay and OpenTelemetry HTTP transport.

Tokens
63.7K
Snippets
184
Records
281
Agent score
76%

What's inside Grafana Faro Web SDK

  1. Overview of Grafana Faro Web SDK

    main

    Grafana Faro Web SDK is used to instrument frontend JavaScript applications to collect telemetry. This telemetry can be forwarded to:

    • Grafana Alloy (with faro receiver integration enabled)
    • Grafana Cloud instance
    • A custom receiver

    Once collected, data can be sent to Loki (for logs) or Tempo (for traces).

  2. Use @grafana/faro-react for React applications

    main

    The @grafana/faro-react package is a distribution of the Faro Web SDK specifically designed for React projects. It simplifies integration by providing specialized React-aware features that automatically capture telemetry related to the React component lifecycle and routing.

    Key features include:

    • React Router Integration (v4–v7): Automatically sends events for all route changes, including support for the data router API.
    • Error Boundary: Provides enhanced stack traces for component errors and allows configuration for pushError behavior.
    • Component Profiler: Captures performance metrics such as component re-renders and unmounting/mounting times.
  3. Use the View meta for UI tracking

    main

    The view meta allows you to associate signals with specific sections of your UI that change without a full route change (e.g., an auth view containing both sign-in and sign-up components).

    Unlike other dynamic metas, view does not change automatically. You can set it during initialization or update it programmatically to track movement between different UI states.

    /* Example View meta */
    {
      "name": "auth"
    }
  4. Configure Instrumentations and Tracing

    main

    You can customize what the SDK captures by providing an array of instrumentations to initializeFaro.

    Available Instrumentations:

    • console: Captures warn, info, and error messages from the global console object.
    • errors: Captures unhandled top-level exceptions.
    • web-vitals: Captures performance metrics from the web vitals API.
    • session: Sends session start events.
    • view: Sends view changed events.

    To enable console capture, use getWebInstrumentations({ captureConsole: true }). For OpenTelemetry (OTel) tracing, include TracingInstrumentation from @grafana/faro-web-tracing in the instrumentations array.

    import { ConsoleInstrumentation, getWebInstrumentations, initializeFaro } from '@grafana/faro-web-sdk';
    import { TracingInstrumentation } from '@grafana/faro-web-tracing';
    
    const faro = initializeFaro({
      url: 'https://agent.myapp/collect',
      apiKey: 'secret',
      instrumentations: [...getWebInstrumentations({ captureConsole: true }), new TracingInstrumentation()],
      app: {
        name: 'frontend',
        version: '1.0.0',
      },
    });
  5. Understand the Unpatched Console in Faro

    main

    The 'unpatched console' is the original, unmodified console object captured at the very beginning of the Faro initialization process. Faro uses this unpatched instance for several critical purposes:

    1. Internal Logging: It serves as the foundation for the internal Faro logger to ensure that logs are captured even if the application's global console is later modified.
    2. Custom Logger Support: It provides a reference point for applications that use custom logging frameworks, allowing Faro to interact with the original console if needed.
    3. Component Availability: Once captured during initialization, this unpatched console is made available to all Faro components, including internal loggers and various instrumentations.

    By capturing the console before any other logic runs, Faro ensures its own telemetry and error reporting remain reliable even if the host application overrides console.log, console.error, etc.

  6. Configure the App meta

    main

    The app meta ties signals to a specific application. It is a static meta that must be defined by the end-user during initialization and should not change during a session.

    When using the faro-web-tracing package, the following mapping occurs for resource attributes:

    • name, namespace, and version are attached as service.name|namespace|version.
    • environment becomes deployment.environment.
    /* Example properties for App meta */
    {
      "name": "my-app",
      "version": "1.0.0",
      "namespace": "my-namespace",
      "release": "commit-hash-or-build-id",
      "environment": "production",
      "bundleId": "com.example.app", // mobile
      "installationId": "unique-id" // mobile
    }
  7. What are Instrumentations in Faro

    main

    In the Faro architecture, Instrumentations are the data collectors. Their primary responsibility is to gather data from various sources, such as browser APIs or other available interfaces.

    Note that the @grafana/faro-core library does not include any instrumentations by default. They are typically provided in one of two ways:

    1. Through wrapper packages like @grafana/faro-web-sdk.
    2. By the user implementing custom instrumentations.
  8. Enable advanced Faro features

    main

    Faro supports several advanced configuration options to enhance observability:

    • Route tracking: For projects with a supported router, integrate with the router to provide route patterns (e.g., /users/:id) instead of raw URLs.
    • User identity: Tag errors and sessions with information about the currently logged-in user.
    • Error boundary: (React only) Catch and report component crashes.
    • Cookie consent gate: Delay tracking until the user has accepted cookies.
    • Cross-origin tracing: Trace headers for API calls made to other domains.
    • Session settings: Configure session persistence and inactivity timeouts.
    • Disable console capture: Prevent Faro from capturing console.log calls.
    • Disable distributed tracing: Turn off tracing capabilities.
  9. Explore Faro SDK Packages

    main

    The SDK is modular. Depending on your requirements, you can combine different packages:

    • @grafana/faro-core: The main package providing core functionality and architecture.
    • @grafana/faro-web-sdk: Provides instrumentations, metas, and transports specifically for web applications.
    • @grafana/faro-web-tracing: Provides implementation for tracing web applications.
    • @grafana/faro-react: Enables easier integration for projects built with React.
  10. Customize log bodies with otlpTransform

    main

    By default, OtlpHttpTransport only adds the body property to signals that have a body value. This excludes Measurement and Exception signals, which can cause issues with some Otel Collector components.

    To resolve this, use the otlpTransform property in OtlpHttpTransportOptions to provide custom body string generators: createErrorLogBody and createMeasurementLogBody. Each function receives a TransportItem representing the log being transformed, allowing you to build a custom string from the payload.

    initializeFaro({
      // ...
      transports: [
        new OtlpHttpTransport({
          apiKey: env.faro.apiKey,
          logsURL: 'https://example.com/v1/logs',
          tracesURL: 'https://example.com/v1/traces',
    
          // customize logs transformation
          otlpTransform: {
            // create custom body string for measurement logs
            createMeasurementLogBody(item) {
              const { payload } = item;
              const [measurementName, measurementValue] = Object.entries(payload.values).flat();
              const body = `faro.signal.measurement: type=${payload.type} name=${measurementName} value=${measurementValue}`;
              return body;
            },
            // create custom body string for error logs
            createErrorLogBody(item) {
              const { payload } = item;
              const body = `faro.signal.error: type=${payload.type} message=${payload.value}`;
              return body;
            },
          },
        }),
      ],
    });