Skip to content

Latest commit

 

History

History
274 lines (219 loc) · 8.24 KB

web-vitals.md

File metadata and controls

274 lines (219 loc) · 8.24 KB

Web Vitals

By default, Honeycomb includes events for all web vitals except first-input-delay. First-input-delay is set to be replaced by interaction-to-next-paint as a core web vital in March 2024.

Initialize Instrumentation

This instrumentation is automatically initialized with the HoneycombWebSDK. To disable the instrumentation, use the following config:

import { HoneycombWebSDK } from '@honeycombio/opentelemetry-web'

const sdk = new HoneycombWebSDK({
  serviceName: 'my-app',
  webVitalsInstrumentationConfig: {
    enabled: false,
  },
});

Vitals Attributes

All vitals have the following attributes, they will each be namespaced by the name of the vital:

/**
 * The name of the vital (in acronym form).
 */
"name": 'CLS' | 'FCP' | 'INP' | 'LCP' | 'TTFB';

/**
 * The rating as to whether the metric value is within the "good",
 * "needs improvement", or "poor" thresholds of the metric.
 */
"<vital-rating>.rating": "good" | "needs-improvement" | "poor"

"<vital-id>.id": string;
/* Shared fields */
"cls.name": "CLS",
"cls.rating": "good" | "needs-improvement" | "poor",
"cls.id": string;
"cls.value": number;
"cls.delta": number;

 /**
 * A selector identifying the first element (in document order) that
 * shifted when the single largest layout shift contributing to the page's
 * CLS score occurred.
 */
"cls.largest_shift_target": string

/* if available, the data-attribute on the largest shift target. Otherwise, an alias for cls.largest_shift_target */
"cls.element": attribution.largestShiftTarget

/**
 * The time when the single largest layout shift contributing to the page's
 * CLS score occurred. The number of milliseconds elapsed since navigation.
 * [OPEN QUESTION] should this be cls.elapsed_time? or have something as an
 * alias? it records the ms since navigation (performance.timeOrigin) — it
 * isn't a timestamp as a DateTime
 */
"cls.largest_shift_time": DOMHighResTimeStamp;

/**
 * The layout shift score of the single largest layout shift contributing to
 * the page's CLS score.
 */
"cls.largest_shift_value": number;

/**
 * The loading state of the document at the time when the largest layout
 * shift contribution to the page's CLS score occurred (see
 * https://github.com/GoogleChrome/web-vitals?tab=readme-ov-file#loadstate
 * for details).
 */
"cls.load_state": LoadState

/* attribution.largestShiftEntry.hadRecentInput */
"cls.had_recent_input": boolean;

The event time is when the single largest layout shift contributing to the page's CLS score occurred. (performance.timeOrigin + metric.largestShiftTime)

/** Shared fields */
"lcp.name": "LCP",
"lcp.rating": "good" | "needs-improvement" | "poor",
"lcp.id": string;
"lcp.value": number;
"lcp.delta": number;

/**
* if available, the data attribute on the element corresponding to the largest
* contentful paint for the page. Otherwise, its selector.
*/
"lcp.element": string;

/**
 * The URL (if applicable) of the LCP image resource. If the LCP element
 * is a text node, this value will not be set.
 */
"lcp.url": string;

/**
 * The time from when the user initiates loading the page until when the
 * browser receives the first byte of the response (a.k.a. TTFB). See
 * [Optimize LCP](https://web.dev/articles/optimize-lcp) for details.
 */

"lcp.time_to_first_byte": number;
/**
 * The delta between TTFB and when the browser starts loading the LCP
 * resource (if there is one, otherwise 0). See [Optimize
 * LCP](https://web.dev/articles/optimize-lcp) for details.
 */
"lcp.resource_load_delay": number;

/**
 * The total time it takes to load the LCP resource itself (if there is one,
 * otherwise 0). See [Optimize LCP](https://web.dev/articles/optimize-lcp) for
 * details.
 */
"lcp.resource_load_time": number;

/**
 * The delta between when the LCP resource finishes loading until the LCP
 * element is fully rendered. See [Optimize
 * LCP](https://web.dev/articles/optimize-lcp) for details.
 */
"lcp.element_render_delay": number;

The event time is the start of the page load (performance.timeOrigin), the duration is equivalent to "lcp.value"

/** Shared fields */
"inp.name": "INP",
"inp.rating": "good" | "needs-improvement" | "poor",
"inp.id": string;
/* How long from the user interaction until the next paint */
"inp.value": number;
"inp.delta": number;

/**
 *
 * Either a selector identifying the element that the user interacted with for
 * the event corresponding to INP or its data-attribute (when `dataAttribute`
 * option is set in the config). This element will be the `target` of the
 * `event` dispatched.
 */
"inp.element": string;

/**
 * The `type` of the `event` dispatched corresponding to INP.
 */
"inp.event_type": string;

/**
 * The loading state of the document at the time when the event corresponding
 * to INP occurred (see `LoadState` for details). If the interaction occurred
 * while the document was loading and executing script (e.g. usually in the
 * `dom-interactive` phase) it can result in long delays.
 */
"inp.load_state": LoadState;
}

The event time is equal to the time the interaction began {[TODO] attribution.eventTime + performance.timeOrigin? or just attribution.eventTime}, the duration is equal to inp.value

/** Shared fields */
"fcp.name": "FCP",
"fcp.rating": "good" | "needs-improvement" | "poor",
"fcp.id": string;
/** FCP specific fields */
"fcp.value": number;
"fcp.delta": number;
/**
 * The time from when the user initiates loading the page until when the
 * browser receives the first byte of the response (a.k.a. TTFB).
 */
"fcp.time_to_first_byte": number;

/**
 * The delta between TTFB and the first contentful paint (FCP).
 */
"fcp.time_since_first_byte": number;

/**
 * The loading state of the document at the time when FCP `occurred (see
 * `LoadState` for details). Ideally, documents can paint before they finish
 * loading (e.g. the `loading` or `dom-interactive` phases).
 */
"fcp.load_state": LoadState;

The event time is equal to the start of the page load, the duration is equal to fcp.value

/** Shared fields */
"ttfb.name": "TTFB",
"ttfb.rating": "good" | "needs-improvement" | "poor",
"ttfb.id": string;
/** TTFB specific fields */
"ttfb.value": number;
"ttfb.delta": number;

/**
 * The total time from when the user initiates loading the page to when the
 * DNS lookup begins. This includes redirects, service worker startup, and
 * HTTP cache lookup times.
 */
"ttfb.waiting_time": number;
/**
 * The total time to resolve the DNS for the current request.
 */
"ttfb.dns_time": number;
/**
 * The total time to create the connection to the requested domain.
 */
"ttfb.connection_time": number;
/**
 * The time time from when the request was sent until the first byte of the
 * response was received. This includes network time as well as server
 * processing time.
 */
"ttfb.request_time": number;

The event time is equal to the timeOrigin & the duration is equal to ttfb.value

Customization of event attributes

For more fine-tuned event processing, pass in a custom callback functions for each of the web-vitals:

const sdk = new HoneycombWebSDK({
  webVitalsInstrumentationConfig: {
    lcp: {
      applyCustomAttributes: (vital, span) => {
        // a value under 3000ms is acceptable as a 'good' rating for our team
        // this would otherwise show up as 'needs-improvement' if the value is less than 2500 in 'lcp.rating' according to the
        // set standards but we want to record this as well.
        if (vital.value < 3000) {
          span.setAttribute('lcp.custom_rating', 'good');
        }
      },
    },
  },
});

The callbacks are passed their respective metric with attribution as well as the span. web-vitals docs

Note: passing in a custom callback will augment the attributes created by this package. If the same attribute names are used, they will replace the data generated by this package.