Skip to content

Commit 8c68c38

Browse files
johnbleydyladanobecny
authored
[Plugin User Interaction]: Improve causality of spans from bubbled events (open-telemetry#172)
* feat: trace causality of bubbled events * chore: rework handling of event arg Co-authored-by: Daniel Dyla <dyladan@users.noreply.github.com> * chore: update field name Co-authored-by: Daniel Dyla <dyladan@users.noreply.github.com> Co-authored-by: Bartlomiej Obecny <bobecny@gmail.com>
1 parent 6af1022 commit 8c68c38

File tree

2 files changed

+40
-2
lines changed

2 files changed

+40
-2
lines changed

plugins/web/opentelemetry-plugin-user-interaction/src/userInteraction.ts

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ import {
3232
} from './types';
3333
import { AttributeNames } from './enums/AttributeNames';
3434
import { VERSION } from './version';
35+
import { Span } from '@opentelemetry/api';
3536

3637
const ZONE_CONTEXT_KEY = 'OT_ZONE_CONTEXT';
3738
const EVENT_NAVIGATION_NAME = 'Navigation:';
@@ -52,6 +53,8 @@ export class UserInteractionPlugin extends BasePlugin<unknown> {
5253
Function,
5354
Map<string, Map<HTMLElement, Function>>
5455
>();
56+
// for event bubbling
57+
private _eventsSpanMap: WeakMap<Event, Span> = new WeakMap<Event, Span>();
5558

5659
constructor() {
5760
super('@opentelemetry/plugin-user-interaction', VERSION);
@@ -92,7 +95,8 @@ export class UserInteractionPlugin extends BasePlugin<unknown> {
9295
*/
9396
private _createSpan(
9497
element: HTMLElement,
95-
eventName: string
98+
eventName: string,
99+
parentSpan?: Span | undefined
96100
): api.Span | undefined {
97101
if (!element.getAttribute) {
98102
return undefined;
@@ -106,6 +110,7 @@ export class UserInteractionPlugin extends BasePlugin<unknown> {
106110
const xpath = getElementXPath(element, true);
107111
try {
108112
const span = this._tracer.startSpan(eventName, {
113+
parent: parentSpan,
109114
attributes: {
110115
[AttributeNames.COMPONENT]: this.component,
111116
[AttributeNames.EVENT_TYPE]: eventName,
@@ -239,11 +244,19 @@ export class UserInteractionPlugin extends BasePlugin<unknown> {
239244
const once = useCapture && useCapture.once;
240245
const patchedListener = (...args: any[]) => {
241246
const target = this;
247+
let parentSpan: Span | undefined;
248+
const event: Event | undefined = args[0];
249+
if (event) {
250+
parentSpan = plugin._eventsSpanMap.get(event);
251+
}
242252
if (once) {
243253
plugin.removePatchedListener(this, type, listener);
244254
}
245-
const span = plugin._createSpan(target, type);
255+
const span = plugin._createSpan(target, type, parentSpan);
246256
if (span) {
257+
if (event) {
258+
plugin._eventsSpanMap.set(event, span);
259+
}
247260
return plugin._tracer.withSpan(span, () => {
248261
const result = listener.apply(target, args);
249262
// no zone so end span immediately

plugins/web/opentelemetry-plugin-user-interaction/test/userInteraction.nozone.test.ts

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -304,6 +304,31 @@ describe('UserInteractionPlugin', () => {
304304
});
305305
});
306306

307+
it('should trace causality of bubbled events', () => {
308+
let callCount = 0;
309+
const listener1 = function () {
310+
callCount++;
311+
};
312+
const listener2 = function () {
313+
callCount++;
314+
};
315+
document.body.addEventListener('click', listener1);
316+
document.body.firstElementChild?.addEventListener('click', listener2);
317+
document.body.firstElementChild?.dispatchEvent(
318+
new MouseEvent('click', { bubbles: true })
319+
);
320+
assert.strictEqual(callCount, 2);
321+
assert.strictEqual(exportSpy.args.length, 2);
322+
assert.strictEqual(
323+
exportSpy.args[0][0][0].traceId,
324+
exportSpy.args[1][0][0].traceId
325+
);
326+
assert.strictEqual(
327+
exportSpy.args[0][0][0].spanId,
328+
exportSpy.args[1][0][0].context.parentSpanId
329+
);
330+
});
331+
307332
it('should handle 3 overlapping interactions', done => {
308333
const btn1 = document.createElement('button');
309334
btn1.setAttribute('id', 'btn1');

0 commit comments

Comments
 (0)