Menu
Grafana Cloud

Provided instrumentations

The Grafana Faro Web SDK powers the fully managed Grafana Cloud Frontend Observability solution for Real User Monitoring (RUM) in Grafana Cloud

Instrumentations are data collectors responsible for integrating with the platform, such as a browser API, to collect and parse data and use the Grafana Faro Web SDK core API to report it.

For example, an instrumentation replaces the browser’s console with a custom implementation that collects all the logs and reports them to the Grafana Faro Web SDK core API using the faro.api.pushLog() function.

By default, the core package of the Grafana Faro Web SDK does not include instrumentations. Instead, they are provided in specific packages for the platform you are using (except for browser tracing instrumentation - see below).

Console

Logs are critical for troubleshooting errors because they provide context and detail into what is happening in your application.

The console instrumentation replaces the browser’s console with a custom implementation that collects all logs, so whenever console.error (for example) is called, the message is sent as a log to the collector.

Console instrumentation helps you to:

  • Determine the context and the root cause of errors
  • Capture messages emitted by the application or the libraries it uses
  • Monitor your application and determine that it is working as expected

Because the console instrumentation can offer helpful context when you troubleshoot errors, we recommend that you keep this instrumentation enabled. However, enabling log collection for all log levels captures a lot of data that is sent to Grafana Cloud which can increase cost and result in verbose data.

By default, the following log levels are disabled and we recommend that you only enable them if required:

  • console.debug
  • console.trace
  • console.log

How to use the console instrumentation

The following console instrumentation is enabled by default. No additional configuration is required.

ts
initializeFaro({
  url: 'https://my-domain.my-tld/collect/{app-key}',
  app: {
    name: 'my-app',
  },
});

Note

If you overwrite the instrumentations array when you initialize the Grafana Faro Web SDK, you must manually include the console instrumentation.

To manually include the console instrumentation, use the following getWebInstrumentations helper function.

ts
initializeFaro({
  url: 'https://my-domain.my-tld/collect/{app-key}',
  app: {
    name: 'my-app',
  },
  instrumentations: [
    ...getWebInstrumentations({
      // Optional, if you want to disable the console instrumentation
      captureConsole: false,

      // Optional, if you want to collect all levels
      captureConsoleDisabledLevels: [],

      // Optional, if you want to disable only some levels
      captureConsoleDisabledLevels: [LogLevel.DEBUG, LogLevel.TRACE],
    }),
  ],
});

Alternatively, if you want to fine-tune which instrumentations are enabled, you can use the ConsoleInstrumentation class.

ts
initializeFaro({
  url: 'https://my-domain.my-tld/collect/{app-key}',
  app: {
    name: 'my-app',
  },
  instrumentations: [
    new ConsoleInstrumentation({
      // Optional, if you want to collect all levels
      disabledLevels: [],

      // Optional, if you want to disable only some levels
      disabledLevels: [LogLevel.DEBUG, LogLevel.TRACE],
    }),
  ],
});

Error

Applications can sometimes produce errors, but because they run in a browser, developers are unaware that they are occurring. In addition, users do not always report errors, which adds to the complexity of troubleshooting them.

The error instrumentation subscribes to the browser’s window.onerror and onunhandledrejection events to collect them, extract the stacktrace if available, and report them to the Grafana Faro Web SDK core API using the faro.api.pushError() function.

Tracking errors helps you to:

  • Analyze anomalies that occur in your application (crashes, incorrect behaviour etc.)
  • Monitor that your application is working as expected
  • Detect issues with external systems that your application is using

The error instrumentation should always be enabled as it is the only way to automatically capture errors. You can, however, disable this instrumentation if you are tracking errors by other means.

Note: When an error is caught in a try/catch block, the error needs to be re-thrown or reported manually.

ts
const buggyFn = () => {
  throw new Error('Buggy function');
};

try {
  buggyFn();
} catch (err) {
  // Re-throw the error so it can be caught by the instrumentation
  throw err;

  // Alternatively, report it manually
  faro.api.pushError(err);
}

How to use the error instrumentation

The following error instrumentation is enabled by default. No additional configuration is required.

ts
initializeFaro({
  url: 'https://my-domain.my-tld/collect/{app-key}',
  app: {
    name: 'my-app',
  },
});

Note

If you overwrite the instrumentations array when you initialize the Grafana Faro Web SDK, you must manually include the error instrumentation.

To manually include the error instrumentation, use the following getWebInstrumentations helper function.

ts
initializeFaro({
  url: 'https://my-domain.my-tld/collect/{app-key}',
  app: {
    name: 'my-app',
  },
  instrumentations: [...getWebInstrumentations()],
});

Alternatively, if you want to fine-tune which instrumentations are enabled, you can use the following ErrorInstrumentation class.

ts
initializeFaro({
  url: 'https://my-domain.my-tld/collect/{app-key}',
  app: {
    name: 'my-app',
  },
  instrumentations: [new ErrorsInstrumentation()],
});

Web Vitals

Web Vitals measure the real-world performance of your site so that developers, site owners, and others can improve it. For more information about Web Vitals, refer to web.dev.

The Web Vitals instrumentation uses the web-vitals package to collect and report data.

Tracking Web Vitals helps you to:

  • Analyze the loading times of your pages
  • Detect large repaints that take a lot of time
  • Detect delays until first inputs from your users

How to use the Web Vitals instrumentation

The following Web Vitals instrumentation is enabled by default. No additional configuration is required.

ts
initializeFaro({
  url: 'https://my-domain.my-tld/collect/{app-key}',
  app: {
    name: 'my-app',
  },
});

**Note: ** If you overwrite the instrumentations array when you initialize the Grafana Faro Web SDK, you must manually include the Web Vitals instrumentation.

To manually include the Web Vitals instrumentation, use the following getWebInstrumentations helper function.

ts
initializeFaro({
  url: 'https://my-domain.my-tld/collect/{app-key}',
  app: {
    name: 'my-app',
  },
  instrumentations: [...getWebInstrumentations()],
});

Alternatively, if you want to fine-tune which instrumentations are enabled, you can use the following WebVitalsInstrumentation class.

ts
initializeFaro({
  url: 'https://my-domain.my-tld/collect/{app-key}',
  app: {
    name: 'my-app',
  },
  instrumentations: [new WebVitalsInstrumentation()],
});

Session tracking

Note

Session tracking requires Faro Web SDK version 1.3.6 or greater. In order to visualize your data in the Grafana Cloud Frontend Observability plugin, you must instrument your frontend application with at least v1.3.6.

The session tracking instrumentation reports the change of the session meta as an event. This is useful for RUM because you can track when a new session starts.

Using the session tracking instrumentation, you can correlate errors, logs, and events that occurred for a particular user during a single session using the application.

In order for the session tracking instrumentation to work correctly, the session meta should not be removed.

What is a session?

A session is defined as the time that a user spends in a Frontend App from start to finish or timeout. Sessions are either created or resumed when a user visits and uses the app.

An individual session ends after a maximum lifetime of 4 hours or after an inactivity timeout of 15 minutes. When the user returns after the timeout period elapses, the SDK automatically creates a new user session.

How session tracking works

Faro automatically creates a new session if a user visits a page, or depending on the chosen mode, picks the last session of a recurring user.

A Faro session has a max. lifetime of 4 hours and a max. inactivity time of 15 minutes.
The maximum time Faro keeps a tracked session (see below) is 45 minutes by default but can be configured.

If either of the session timeouts is reached, Faro creates a new session and updates the session meta object with the new session id as well as the previous session id. The latter gets added via the previousSession attribute.
Every time a new session is created Faro calls the optional onSessionChange callback and provides the oldSession and newSession meta as parameters. The oldSession meta is null when the initial session is created.

The Faro receiver also checks the related session of every beacon sent by Faro for its validity, that is the validity of the max. lifetime and activity-time.
If a beacons session is invalid it drops it.
The web-sdk takes care of creating new sessions if either of the aforementioned time ranges is reached, so there should never be a need for the receiver to drop beacons.

Faro attaches the x-faro-session-id custom header, with the value set to the current session-id, to each HTTP request sent to the collector url, so that the backend can keep track of user-sessions and enforce constraints.

To help users identify lifecycle events, Faro will send different lifecycle events throughout the life of a session.

The lifecycle events are:

  • session_start: if no session is found in web-storage during the initialization phase.
  • session_resume: if a valid session is found in web-storage during the initialization phase.
  • session_extend: if Faro auto-creates a new session because one of the session timeouts is reached or if setSession(...) is called manually.

Faro provides two modes for session tracking, which are described in the next two sections.

Tracked sessions mode

If the tracked sessions mode is enabled, Faro will persist session information in the browsers Local Storage.
This enables session information to outlive page reloads as well as to sync sessions between the different browsing contexts like tabs or new windows from within the same browser.

Faro also keeps the session for a configurable amount of time so it can pick up the session in case a site visitor closes the browser and returns after some time.

Note:

Faro can not differentiate session between users. So if different users use the same browser, Faro picks up the last persisted valid session. This can lead to a situation where different users share the same session Id. Faro provides multiple ways to deal with the situation, depending on your needs. Please see the examples at the end of the chapter.

Volatile sessions mode

If the volatile sessions mode is enabled, Faro will store session information in the browsers Session Storage. This means session information can outlive page reloads within the same site but it will not persist the session between browsing context like other tabs or windows. Opening a new tab or window with the same site will create a new session.

After closing a tab the session is gone, which means it is not possible to have a recurring visitor to pick up on the last session.

Sampling

Faro facilitates sampling by making sampling decisions on a per-session basis. Within a sampled session, every signal is transmitted, while signals from unsampled sessions are discarded.

Sampling decisions in Faro are determined by the configured sampling rate or a custom sampler function. The sampling rate can be set between 0 and 1, where the sampling function must produce a value within this range. A value of 0 (0%) implies that no signals are sent, while a value of 1 (100%) ensures that all signals are transmitted. By default, the sampling rate is set to 1 (100%).

The sampler function computes a sampling decision based on other properties for tailored sampling decisions. It gets contextual information injected, which currently contains multiple Faro Metadata objects.

A sampling decision is calculated at the following points in time:

  • On page load, when Faro is initialized and an invalid session is found in web-storage.
  • On session extend, this is when Faro auto-creates a new session because one of the session timeouts is reached or if setSession(...) is called manually.

Config options

The session manager can be configured with the sessionTracking object via the Faro initializer function.

It provides the following options:

OptionsDescriptionDefault value
enabledEnable or disable session managementfalse
persistentWether to use tracked or volatile sessionsfalse
sessionInitial session object, only for very special demandsundefined
maxSessionPersistenceTimeHow long to keep a session to identify a recurring visitor (tracked sessions only)15 minutes + 1 minute buffer
onSessionChangeCalled each time a new session is createdundefined
samplingRateNumber between 0 (0% of signals sent) and 1 (100% of signals sent).1
samplerCalculate a sampling decision based on other properties. Should return number between 0 and 1.undefined
generateSessionIdIf present, Faro calls this function each time it needs to create a new sessionId.undefined
ts
sessionTracking?: {
  enabled?: boolean;
  persistent?: boolean;
  session?: MetaSession;
  maxSessionPersistenceTime?: number;
  onSessionChange?: (oldSession: MetaSession | null, newSession: MetaSession) => void;
  samplingRate?: number;
  sampler?: (context: SamplingContext) => number;
  generateSessionId?: () => string;
};

Adding the session tracking instrumentation

The following session tracking instrumentation is enabled by default. No additional configuration is required.

ts
initializeFaro({
  url: 'https://my-domain.my-tld/collect/{app-key}',
  app: {
    name: 'my-app',
  },
});

Note

If you overwrite the instrumentations array when you initialize the Grafana Faro Web SDK, you must manually include the Session tracking instrumentation.

To manually include the session instrumentation, use the following getWebInstrumentations helper function.

ts
initializeFaro({
  url: 'https://my-domain.my-tld/collect/{app-key}',
  app: {
    name: 'my-app',
  },
  instrumentations: [...getWebInstrumentations()],
});

Alternatively, if you want to fine-tune which instrumentations are enabled, you can use the following SessionInstrumentation class.

ts
initializeFaro({
  url: 'https://my-domain.my-tld/collect/{app-key}',
  app: {
    name: 'my-app',
  },
  instrumentations: [new SessionInstrumentation()],
});

Configure the session tracking instrumentation

ts
initializeFaro({
  ...
  sessionTracking?: {
    enabled: true;
    persistent: true;
    onSessionChange: (oldSession: MetaSession | null, newSession: MetaSession) => {
      console.log(`Session ${oldSession == null ? 'created' : 'changed'}`, {oldSession, newSession})
    } ;
  };
});

If multiple users share the same browser

If persistent sessions are activated, Faro persists the users session in the browser Local Storage. If a user opens the site, Faro will pick up the session from storage if it’s valid or creates a new one if not.

But if multiple users share the same browser, this may lead to a situation where user A leaves the site, then a bit later user B navigates to that site. So Faro will pick up the valid persisted session. This causes the user B having the same sessionId as user A.

Since Faro can not derive if a visitor is the same person or a new one, you need to take care of this. Faro provides some options to mitigate the issue, which are the following:

First possibility is to define a shorter maxSessionPersistenceTime (default 15 minutes + 1 minute buffer).

ts
initializeFaro({
  ...
  sessionTracking?: {
    enabled: true;
    persistent: true;
    maxSessionPersistenceTime: 1 * 60 * 1000 // 1 minute
    onSessionChange: (oldSession: MetaSession | null, newSession: MetaSession) => {
      console.log(`Session ${oldSession == null ? 'created' : 'changed'}`, {oldSession, newSession})
    } ;
  };
});

Second possibility is to delete the Session manually when you consider a session as finished.
The easiest way is to use the static method removeUserSession() on PersistentSessionsManager.
This will remove the session from Local Storage and Faro will create a new one on the next page load.

ts
import { PersistentSessionsManager } from '@grafana/faro-web-sdk';

function handleSessionClose() {
  PersistentSessionsManager.removeUserSession();
}

Use the sampler function to calculate tailored sampling decisions

Use a custom sampler function to calculate a tailored sampling decision.

ts
initializeFaro({
  ...
  user: {
    attributes: {
      planet: 'mars',
    },
  },
  ...
  sessionTracking?: {
    enabled: true;
    onSessionChange: (oldSession: MetaSession | null, newSession: MetaSession) => {
      console.log(`Session ${oldSession == null ? 'created' : 'changed'}`, {oldSession, newSession})
    };
    sampler(context) {
      const planet = context.metas.user?.attributes?.['planet'];

      if (!planet) {
         return 0; // 0%
      }

      if (planet === 'mars') {
         return 0.8; // 80%
      }

      if (planet === 'moon') {
         return 0.3; // 30%
      }

      if (planet === 'earth') {
         return 0.1; // 10%
      }

      return 1;
    },
  };
});

Create custom session ids

In case a customId scheme is needed, Faro provides the generateSessionId() function. If this function is present in the config object, Faro will always call this function instead of its internal sessionId generator.

ts
initializeFaro({
  ...
  sessionTracking?: {
    generateSessionId() {
      // generate and return custom sessionId here.
    }
  };
});

View tracking

Note

The view tracking instrumentation is available as of Grafana Faro Web SDK version 1.0.0-beta6.

The view tracking instrumentation reports the change of the view meta as an event. This is useful for RUM because you can track when a new view is set.

For example, you can set the following different views for an application:

  • The register and login pages can be considered auth views
  • The homepage page can be considered a home view
  • The users listing and user profile pages can be seen as users views

Using the view tracking instrumentation, you can correlate errors, logs, and events that occurred for a particular user when using a particular section of the application.

In order for the view tracking instrumentation to work correctly, the view meta should be set accordingly.

How to use the view tracking instrumentation

The following view tracking instrumentation is enabled by default. No additional configuration is required.

ts
initializeFaro({
  url: 'https://my-domain.my-tld/collect/{app-key}',
  app: {
    name: 'my-app',
  },
});

Note

If you overwrite the instrumentations array when you initialize the Grafana Faro Web SDK, you must manually include the View tracking instrumentation.

To manually include the view instrumentation, use the following getWebInstrumentations helper function.

ts
initializeFaro({
  url: 'https://my-domain.my-tld/collect/{app-key}',
  app: {
    name: 'my-app',
  },
  instrumentations: [...getWebInstrumentations()],
});

Alternatively, if you want to fine-tune which instrumentations are enabled, you can use the following ViewInstrumentation class.

ts
initializeFaro({
  url: 'https://my-domain.my-tld/collect/{app-key}',
  app: {
    name: 'my-app',
  },
  instrumentations: [new ViewInstrumentation()],
});

Tracing

Tracing collects and reports a detailed view of what occurs each time a user interacts with an application. Because tracing captures an application’s flow and data progression, it yields a considerable amount of data.

The tracing instrumentation performs analysis on the browser of your application. It relies on OpenTelemetry to collect the data and it is leverages the faro.api.pushTraces() function to report it.

For example, if you have an application with a front end that calls an API that stores data in a database, you can use the tracing instrumentation to:

  • Track what happens in your application when the user clicks a button, which APIs are called, how much time is spent on the request, and so on.
  • Associate actions that occur in the frontend of your application with actions that occur in the API of your application
  • Determine the amount of time spent on specific HTTP requests and the amount of time spent on different events of an HTTP request, for example, domain lookup and so on.

Because tracing produces a lot of data and can compromise application performance, we recommend that you enable it in a production environment only when you need it. You can also enable tracing in a testing environment, or when you want to fine-tune the OpenTelemetry instrumentations.

Do not use tracing:

  • If you don’t have a good reason to enable it.
  • If your application has an instrumented API. In this case, you can also disable only the fetch tracing instrumentation.
  • If you are not interested in tracing the user interactions, for example, clicks. In this case, you can also disable only the user interaction instrumentation.

By default, tracing uses the following OpenTelemetry instrumentations:

Note

To link the traces from the front end with the traces from the API, the tracing instrumentation relies on a trace propagator, which by default uses W3CTraceContextPropagator. This propagator adds a traceparent header to all fetch requests which is then used by your API.

How to use the tracing instrumentation

The following tracing instrumentation is not enabled by default. You must manually enable it.

ts
initializeFaro({
  url: 'https://my-domain.my-tld/collect/{app-key}',
  app: {
    name: 'my-app',
  },
  instrumentations: [
    // Other instrumentations

    // Don't forget to add the other instrumentations as well
    ...getWebInstrumentations(),

    new TracingInstrumentation({
      // Optional, if you want to add custom attributes to the resource
      resourceAttributes: {
        'my.attribute': 'my-attribute-value',
      },

      // Optional, if you want to replace the default W3C Trace Context Propagator
      propagator: new MyPropagator(),

      // Optional, if you want to overwrite the default Zone Context Manager
      contextManager: new MyContextManager(),

      // Optional, if you want to overwrite the default instrumentations or set ignore URLs
      instrumentations: [
        ...getDefaultOTELInstrumentations({
          // URLs defined here are ignored by the HTTP requests instrumentations
          ignoreUrls: [/localhost:3000/],
        }),

        new MyOtherOTELInstrumentation(),
      ],
    }),
  ],
});