-
Notifications
You must be signed in to change notification settings - Fork 6
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
feat(honeycomb-opentelemetry-web): Add data attributtes for LCP. #309
Changes from 6 commits
ca84821
56a9c13
04dc434
8ed947c
b1c7867
c3f6422
ae0ce75
217f708
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,23 +1,23 @@ | ||
<!DOCTYPE html> | ||
<!doctype html> | ||
<html lang="en"> | ||
<head> | ||
<meta charset="utf-8" /> | ||
<meta name="viewport" content="width=device-width, initial-scale=1" /> | ||
<title>Honeycomb OpenTelemetry Web Distro</title> | ||
</head> | ||
<body> | ||
<head> | ||
<meta charset="utf-8" /> | ||
<meta name="viewport" content="width=device-width, initial-scale=1" /> | ||
<title>Honeycomb OpenTelemetry Web Distro</title> | ||
</head> | ||
<body> | ||
<ul id="spans-go-here"></ul> | ||
<section class="example-app"> | ||
<header class="header"> | ||
<h1>👋 Hello World</h1> | ||
</header> | ||
<section class="example-app"> | ||
<header class="header"> | ||
<h1 data-hello="hello" data-foo="42" data-bar-biz>👋 Hello World</h1> | ||
</header> | ||
|
||
<button id="loadDadJoke">Get A Random Dad Joke</button> | ||
<div> | ||
<span id="dadJokeText"></span> | ||
</div> | ||
</section> | ||
<!-- Scripts here. Don't remove ↓ --> | ||
<script type="module" src="build/bundle.js"></script> | ||
</body> | ||
<button id="loadDadJoke">Get A Random Dad Joke</button> | ||
<div> | ||
<span id="dadJokeText"></span> | ||
</div> | ||
</section> | ||
<!-- Scripts here. Don't remove ↓ --> | ||
<script type="module" src="build/bundle.js"></script> | ||
</body> | ||
</html> |
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -88,11 +88,19 @@ interface VitalOpts extends ReportOpts { | |
applyCustomAttributes?: ApplyCustomAttributesFn; | ||
} | ||
|
||
interface VitalOptsWithTimings extends VitalOpts { | ||
interface LcpVitalOpts extends VitalOpts { | ||
/** | ||
* Will filter the values of these data attributes if provided, otherwise will send all data-* attributes an LCP entry | ||
* An empty allow list, such as { dataAttributes: [] } will disable sending data-* attributes | ||
*/ | ||
dataAttributes?: string[]; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This pattern could be useful if we wanted to add in the ability to collect There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Hmmmmm that's in interesting point, I agree that its nice to have this array if you only want to look up a certain subset of There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ooo. default to sending all and filter down? I like that better for this instance. |
||
} | ||
|
||
interface InpVitalOpts extends VitalOpts { | ||
/** | ||
* if this is true it will create spans from the PerformanceLongAnimationFrameTiming frames | ||
*/ | ||
includeTimingsAsSpans: boolean; | ||
includeTimingsAsSpans?: boolean; | ||
} | ||
|
||
// To avoid importing InstrumentationAbstract from: | ||
|
@@ -210,13 +218,13 @@ export interface WebVitalsInstrumentationConfig extends InstrumentationConfig { | |
vitalsToTrack?: Array<Metric['name']>; | ||
|
||
/** Config specific to LCP (Largest Contentful Paint) */ | ||
lcp?: VitalOpts; | ||
lcp?: LcpVitalOpts; | ||
|
||
/** Config specific to CLS (Cumulative Layout Shift) */ | ||
cls?: VitalOpts; | ||
|
||
/** Config specific to INP (Interaction to Next Paint) */ | ||
inp?: VitalOptsWithTimings; | ||
inp?: InpVitalOpts; | ||
|
||
/** Config specific to FID (First Input Delay) */ | ||
fid?: VitalOpts; | ||
|
@@ -235,9 +243,9 @@ export interface WebVitalsInstrumentationConfig extends InstrumentationConfig { | |
*/ | ||
export class WebVitalsInstrumentation extends InstrumentationAbstract { | ||
readonly vitalsToTrack: Array<Metric['name']>; | ||
readonly lcpOpts?: VitalOpts; | ||
readonly lcpOpts?: LcpVitalOpts; | ||
readonly clsOpts?: VitalOpts; | ||
readonly inpOpts?: VitalOptsWithTimings; | ||
readonly inpOpts?: InpVitalOpts; | ||
readonly fidOpts?: VitalOpts; | ||
readonly fcpOpts?: VitalOpts; | ||
readonly ttfbOpts?: VitalOpts; | ||
|
@@ -280,41 +288,37 @@ export class WebVitalsInstrumentation extends InstrumentationAbstract { | |
private _setupWebVitalsCallbacks() { | ||
if (this.vitalsToTrack.includes('CLS')) { | ||
onCLS((vital) => { | ||
this.onReportCLS(vital, this.clsOpts?.applyCustomAttributes); | ||
this.onReportCLS(vital, this.clsOpts); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Update all the |
||
}, this.clsOpts); | ||
} | ||
|
||
if (this.vitalsToTrack.includes('LCP')) { | ||
onLCP((vital) => { | ||
this.onReportLCP(vital, this.lcpOpts?.applyCustomAttributes); | ||
this.onReportLCP(vital, this.lcpOpts); | ||
}, this.lcpOpts); | ||
} | ||
|
||
if (this.vitalsToTrack.includes('INP')) { | ||
onINP((vital) => { | ||
this.onReportINP( | ||
vital, | ||
this.inpOpts?.applyCustomAttributes, | ||
this.inpOpts?.includeTimingsAsSpans, | ||
); | ||
this.onReportINP(vital, this.inpOpts); | ||
}, this.inpOpts); | ||
} | ||
|
||
if (this.vitalsToTrack.includes('FID')) { | ||
onFID((vital) => { | ||
this.onReportFID(vital, this.fidOpts?.applyCustomAttributes); | ||
this.onReportFID(vital, this.fidOpts); | ||
}, this.fidOpts); | ||
} | ||
|
||
if (this.vitalsToTrack.includes('TTFB')) { | ||
onTTFB((vital) => { | ||
this.onReportTTFB(vital, this.ttfbOpts?.applyCustomAttributes); | ||
this.onReportTTFB(vital, this.ttfbOpts); | ||
}, this.ttfbOpts); | ||
} | ||
|
||
if (this.vitalsToTrack.includes('FCP')) { | ||
onFCP((vital) => { | ||
this.onReportFCP(vital, this.fcpOpts?.applyCustomAttributes); | ||
this.onReportFCP(vital, this.fcpOpts); | ||
}, this.fcpOpts); | ||
} | ||
} | ||
|
@@ -417,10 +421,8 @@ export class WebVitalsInstrumentation extends InstrumentationAbstract { | |
}); | ||
} | ||
|
||
onReportCLS = ( | ||
cls: CLSMetricWithAttribution, | ||
applyCustomAttributes?: ApplyCustomAttributesFn, | ||
) => { | ||
onReportCLS = (cls: CLSMetricWithAttribution, clsOpts: VitalOpts = {}) => { | ||
const { applyCustomAttributes } = clsOpts; | ||
if (!this.isEnabled()) return; | ||
|
||
const { name, attribution } = cls; | ||
|
@@ -451,10 +453,8 @@ export class WebVitalsInstrumentation extends InstrumentationAbstract { | |
span.end(); | ||
}; | ||
|
||
onReportLCP = ( | ||
lcp: LCPMetricWithAttribution, | ||
applyCustomAttributes?: ApplyCustomAttributesFn, | ||
) => { | ||
onReportLCP = (lcp: LCPMetricWithAttribution, lcpOpts: LcpVitalOpts = {}) => { | ||
const { applyCustomAttributes, dataAttributes } = lcpOpts; | ||
if (!this.isEnabled()) return; | ||
|
||
const { name, attribution } = lcp; | ||
|
@@ -465,6 +465,7 @@ export class WebVitalsInstrumentation extends InstrumentationAbstract { | |
resourceLoadDelay, | ||
resourceLoadDuration, | ||
elementRenderDelay, | ||
lcpEntry, | ||
}: LCPAttribution = attribution; | ||
const attrPrefix = this.getAttrPrefix(name); | ||
|
||
|
@@ -481,6 +482,36 @@ export class WebVitalsInstrumentation extends InstrumentationAbstract { | |
[`${attrPrefix}.resource_load_time`]: resourceLoadDuration, | ||
}); | ||
|
||
const el: HTMLElement = lcpEntry?.element as HTMLElement; | ||
if (el.dataset) { | ||
for (const attrName in el.dataset) { | ||
const attrValue = el.dataset[attrName]; | ||
if ( | ||
// Value exists (including the empty string AND either | ||
attrValue !== undefined && | ||
// dataAttributes is undefined (i.e. send all values as span attributes) OR | ||
(dataAttributes === undefined || | ||
// dataAttributes is specified AND attrName is in dataAttributes (i.e attribute name is in the supplied allowList) | ||
(dataAttributes && attrName in dataAttributes)) | ||
) { | ||
span.setAttribute( | ||
`${attrPrefix}.element.data.${attrName}`, | ||
attrValue, | ||
); | ||
} | ||
} | ||
} | ||
if (dataAttributes) | ||
dataAttributes?.forEach((attrName) => { | ||
const attrValue = el.dataset[attrName]; | ||
if (attrValue !== undefined) { | ||
span.setAttribute( | ||
`${attrPrefix}.element.data.${attrName}`, | ||
attrValue, | ||
); | ||
} | ||
}); | ||
|
||
if (applyCustomAttributes) { | ||
applyCustomAttributes(lcp, span); | ||
} | ||
|
@@ -490,9 +521,9 @@ export class WebVitalsInstrumentation extends InstrumentationAbstract { | |
|
||
onReportINP = ( | ||
inp: INPMetricWithAttribution, | ||
applyCustomAttributes?: ApplyCustomAttributesFn, | ||
includeTimingsAsSpans = false, | ||
inpOpts: InpVitalOpts = { includeTimingsAsSpans: false }, | ||
) => { | ||
const { applyCustomAttributes, includeTimingsAsSpans } = inpOpts; | ||
if (!this.isEnabled()) return; | ||
|
||
const { name, attribution } = inp; | ||
|
@@ -550,10 +581,8 @@ export class WebVitalsInstrumentation extends InstrumentationAbstract { | |
); | ||
}; | ||
|
||
onReportFCP = ( | ||
fcp: FCPMetricWithAttribution, | ||
applyCustomAttributes?: ApplyCustomAttributesFn, | ||
) => { | ||
onReportFCP = (fcp: FCPMetricWithAttribution, fcpOpts: VitalOpts = {}) => { | ||
const { applyCustomAttributes } = fcpOpts; | ||
if (!this.isEnabled()) return; | ||
|
||
const { name, attribution } = fcp; | ||
|
@@ -580,10 +609,8 @@ export class WebVitalsInstrumentation extends InstrumentationAbstract { | |
/** | ||
* @deprecated this will be removed in the next major version, use INP instead. | ||
*/ | ||
onReportFID = ( | ||
fid: FIDMetricWithAttribution, | ||
applyCustomAttributes?: ApplyCustomAttributesFn, | ||
) => { | ||
onReportFID = (fid: FIDMetricWithAttribution, fidOpts: VitalOpts = {}) => { | ||
const { applyCustomAttributes } = fidOpts; | ||
if (!this.isEnabled()) return; | ||
|
||
const { name, attribution } = fid; | ||
|
@@ -608,8 +635,9 @@ export class WebVitalsInstrumentation extends InstrumentationAbstract { | |
|
||
onReportTTFB = ( | ||
ttfb: TTFBMetricWithAttribution, | ||
applyCustomAttributes?: ApplyCustomAttributesFn, | ||
ttfbOpts: VitalOpts = {}, | ||
) => { | ||
const { applyCustomAttributes } = ttfbOpts; | ||
if (!this.isEnabled()) return; | ||
|
||
const { name, attribution } = ttfb; | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think this is how the values end up being added as attrs, according to the code each one is an attribute by itself with
.data.
prefix, can we change that here?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
💯 I wanted to make sure we were 👍 with c3f6422 before updating all the docs. 😅