Skip to content
This repository was archived by the owner on Oct 3, 2023. It is now read-only.

Commit 4ab8111

Browse files
crdgonzalezcadraffensperger
authored andcommitted
Relate user interaction traces back to the initial page load trace (#145)
1 parent 14d9c91 commit 4ab8111

6 files changed

Lines changed: 125 additions & 10 deletions

File tree

packages/opencensus-web-core/src/trace/model/attribute-keys.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,3 +89,8 @@ export const LONG_TASK_PREFIX = 'long_task.';
8989
* long task.
9090
*/
9191
export const ATTRIBUTE_LONG_TASK_ATTRIBUTION = `${LONG_TASK_PREFIX}attribution`;
92+
93+
/**
94+
* Attribute for spans to be related back to the initial load trace.
95+
*/
96+
export const ATTRIBUTE_INITIAL_LOAD_TRACE_ID = 'initial_load_trace_id';

packages/opencensus-web-instrumentation-perf/src/initial-load-root-span.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import {
2020
ATTRIBUTE_HTTP_USER_AGENT,
2121
ATTRIBUTE_LONG_TASK_ATTRIBUTION,
2222
ATTRIBUTE_NAV_TYPE,
23+
ATTRIBUTE_INITIAL_LOAD_TRACE_ID,
2324
parseUrl,
2425
randomSpanId,
2526
randomTraceId,
@@ -92,6 +93,9 @@ export function getInitialLoadRootSpan(
9293
root.annotations = getNavigationAnnotations(perfEntries);
9394
root.attributes[ATTRIBUTE_HTTP_URL] = navigationUrl;
9495
root.attributes[ATTRIBUTE_HTTP_USER_AGENT] = navigator.userAgent;
96+
// This is included to enable trace search by attribute to find an initial
97+
// load trace and its interaction traces via a single attribute query.
98+
root.attributes[ATTRIBUTE_INITIAL_LOAD_TRACE_ID] = root.traceId;
9599

96100
if (navTiming) {
97101
root.endPerfTime = navTiming.loadEventEnd;

packages/opencensus-web-instrumentation-perf/test/test-initial-load-root-span.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,7 @@ const EXPECTED_ROOT_ATTRIBUTES: Attributes = {
128128
'http.url': 'http://localhost:4200/',
129129
'http.user_agent': USER_AGENT,
130130
'nav.type': 'navigate',
131+
initial_load_trace_id: '0000000000000000000000000000000b',
131132
};
132133
const EXPECTED_ROOT_ANNOTATIONS: Annotation[] = [
133134
{

packages/opencensus-web-instrumentation-zone/src/on-page-interaction-stop-watch.ts

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,10 @@ import {
1919
ATTRIBUTE_HTTP_URL,
2020
ATTRIBUTE_HTTP_USER_AGENT,
2121
ATTRIBUTE_HTTP_PATH,
22+
ATTRIBUTE_INITIAL_LOAD_TRACE_ID,
23+
LinkType,
2224
} from '@opencensus/web-core';
25+
import { getInitialLoadSpanContext } from '@opencensus/web-initial-load';
2326

2427
/** A helper class for tracking on page interactions. */
2528
export class OnPageInteractionStopwatch {
@@ -44,7 +47,8 @@ export class OnPageInteractionStopwatch {
4447
}
4548

4649
/**
47-
* Stops the stopwatch, fills root span attributes and ends the span.
50+
* Stops the stopwatch. Adds root span attributes and link to the initial
51+
* load page, also, ends the span.
4852
* If has remaining tasks do not end the root span.
4953
*/
5054
stopAndRecord(): void {
@@ -56,6 +60,18 @@ export class OnPageInteractionStopwatch {
5660
rootSpan.addAttribute(ATTRIBUTE_HTTP_URL, this.data.startLocationHref);
5761
rootSpan.addAttribute(ATTRIBUTE_HTTP_PATH, this.data.startLocationPath);
5862
rootSpan.addAttribute(ATTRIBUTE_HTTP_USER_AGENT, navigator.userAgent);
63+
const initialLoadSpanContext = getInitialLoadSpanContext();
64+
// This is included to enable trace search by attribute to find an initial
65+
// load trace and its interaction traces via a single attribute query.
66+
rootSpan.addAttribute(
67+
ATTRIBUTE_INITIAL_LOAD_TRACE_ID,
68+
initialLoadSpanContext.traceId
69+
);
70+
rootSpan.addLink(
71+
initialLoadSpanContext.traceId,
72+
initialLoadSpanContext.spanId,
73+
LinkType.PARENT_LINKED_SPAN
74+
);
5975
rootSpan.end();
6076
}
6177
}

packages/opencensus-web-instrumentation-zone/test/test-interaction-tracker.ts

Lines changed: 79 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@ import {
2121
ATTRIBUTE_HTTP_STATUS_CODE,
2222
ATTRIBUTE_HTTP_METHOD,
2323
WindowWithOcwGlobals,
24+
LinkType,
25+
Link,
2426
} from '@opencensus/web-core';
2527
import {
2628
InteractionTracker,
@@ -32,18 +34,27 @@ import {
3234
} from '../src/monkey-patching';
3335
import { spanContextToTraceParent } from '@opencensus/web-propagation-tracecontext';
3436
import { createFakePerfResourceEntry, spyPerfEntryByType } from './util';
35-
import { getInitialLoadSpanContext } from '@opencensus/web-initial-load';
3637

3738
describe('InteractionTracker', () => {
3839
doPatching();
3940
InteractionTracker.startTracking();
4041
let onEndSpanSpy: jasmine.Spy;
4142
const windowWithOcwGlobals = window as WindowWithOcwGlobals;
42-
// Sample 100% of interactions for the testing. Necessary as the sampling
43-
// decision is supposed to be done in the initial load page and the
44-
// interaction tracker uses the same sampling decision.
45-
windowWithOcwGlobals.ocSampleRate = 1.0;
46-
getInitialLoadSpanContext();
43+
// Set the traceparent to fake the initial load Span Context. Also, Sample
44+
// 100% of interactions for the testing. Necessary as this is supposed to be
45+
// done by the initial load page.
46+
const INITIAL_LOAD_TRACE_ID = '0af7651916cd43dd8448eb211c80319c';
47+
const INITIAL_LOAD_SPAN_ID = 'b7ad6b7169203331';
48+
windowWithOcwGlobals.traceparent = `00-${INITIAL_LOAD_TRACE_ID}-${INITIAL_LOAD_SPAN_ID}-01`;
49+
50+
const EXPECTED_LINKS: Link[] = [
51+
{
52+
traceId: INITIAL_LOAD_TRACE_ID,
53+
spanId: INITIAL_LOAD_SPAN_ID,
54+
type: LinkType.PARENT_LINKED_SPAN,
55+
attributes: {},
56+
},
57+
];
4758

4859
// Use Buffer time as we expect that these interactions take
4960
// a little extra time to complete due to the setTimeout that
@@ -66,10 +77,14 @@ describe('InteractionTracker', () => {
6677
expect(rootSpan.name).toBe('test interaction');
6778
expect(rootSpan.attributes['EventType']).toBe('click');
6879
expect(rootSpan.attributes['TargetElement']).toBe(BUTTON_TAG_NAME);
80+
expect(rootSpan.attributes['initial_load_trace_id']).toBe(
81+
INITIAL_LOAD_TRACE_ID
82+
);
83+
expect(rootSpan.links).toEqual(EXPECTED_LINKS);
6984
expect(rootSpan.ended).toBeTruthy();
7085
// As there is another setTimeOut that completes the interaction, the
71-
// span duraction is not precise, then only test if the interaction duration
72-
// finishes within a range.
86+
// span duraction is not precise, then only test if the interaction
87+
// duration finishes within a range.
7388
expect(rootSpan.duration).toBeLessThan(TIME_BUFFER);
7489
done();
7590
});
@@ -85,6 +100,10 @@ describe('InteractionTracker', () => {
85100
expect(rootSpan.name).toBe('test interaction');
86101
expect(rootSpan.attributes['EventType']).toBe('click');
87102
expect(rootSpan.attributes['TargetElement']).toBe(BUTTON_TAG_NAME);
103+
expect(rootSpan.attributes['initial_load_trace_id']).toBe(
104+
INITIAL_LOAD_TRACE_ID
105+
);
106+
expect(rootSpan.links).toEqual(EXPECTED_LINKS);
88107
expect(rootSpan.ended).toBeTruthy();
89108
expect(rootSpan.duration).toBeGreaterThanOrEqual(SET_TIMEOUT_TIME);
90109
expect(rootSpan.duration).toBeLessThanOrEqual(
@@ -105,6 +124,10 @@ describe('InteractionTracker', () => {
105124
expect(rootSpan.name).toBe('test interaction');
106125
expect(rootSpan.attributes['EventType']).toBe('click');
107126
expect(rootSpan.attributes['TargetElement']).toBe(BUTTON_TAG_NAME);
127+
expect(rootSpan.attributes['initial_load_trace_id']).toBe(
128+
INITIAL_LOAD_TRACE_ID
129+
);
130+
expect(rootSpan.links).toEqual(EXPECTED_LINKS);
108131
expect(rootSpan.ended).toBeTruthy();
109132
expect(rootSpan.duration).toBeLessThanOrEqual(TIME_BUFFER);
110133
done();
@@ -125,6 +148,10 @@ describe('InteractionTracker', () => {
125148
expect(rootSpan.name).toBe('test interaction');
126149
expect(rootSpan.attributes['EventType']).toBe('click');
127150
expect(rootSpan.attributes['TargetElement']).toBe(BUTTON_TAG_NAME);
151+
expect(rootSpan.attributes['initial_load_trace_id']).toBe(
152+
INITIAL_LOAD_TRACE_ID
153+
);
154+
expect(rootSpan.links).toEqual(EXPECTED_LINKS);
128155
expect(rootSpan.ended).toBeTruthy();
129156
expect(rootSpan.duration).toBeGreaterThanOrEqual(interactionTime);
130157
//The duration has to be less than set to the canceled timeout.
@@ -152,6 +179,10 @@ describe('InteractionTracker', () => {
152179
expect(rootSpan.name).toBe('test interaction');
153180
expect(rootSpan.attributes['EventType']).toBe('click');
154181
expect(rootSpan.attributes['TargetElement']).toBe(BUTTON_TAG_NAME);
182+
expect(rootSpan.attributes['initial_load_trace_id']).toBe(
183+
INITIAL_LOAD_TRACE_ID
184+
);
185+
expect(rootSpan.links).toEqual(EXPECTED_LINKS);
155186
expect(rootSpan.ended).toBeTruthy();
156187
expect(rootSpan.duration).toBeGreaterThanOrEqual(SET_TIMEOUT_TIME);
157188
expect(rootSpan.duration).toBeLessThanOrEqual(
@@ -172,6 +203,10 @@ describe('InteractionTracker', () => {
172203
expect(rootSpan.name).toBe('button#test_element click');
173204
expect(rootSpan.attributes['EventType']).toBe('click');
174205
expect(rootSpan.attributes['TargetElement']).toBe(BUTTON_TAG_NAME);
206+
expect(rootSpan.attributes['initial_load_trace_id']).toBe(
207+
INITIAL_LOAD_TRACE_ID
208+
);
209+
expect(rootSpan.links).toEqual(EXPECTED_LINKS);
175210
expect(rootSpan.ended).toBeTruthy();
176211
expect(rootSpan.duration).toBeGreaterThanOrEqual(SET_TIMEOUT_TIME);
177212
expect(rootSpan.duration).toBeLessThanOrEqual(
@@ -193,6 +228,10 @@ describe('InteractionTracker', () => {
193228
expect(rootSpan.name).toBe('test interaction');
194229
expect(rootSpan.attributes['EventType']).toBe('click');
195230
expect(rootSpan.attributes['TargetElement']).toBe(BUTTON_TAG_NAME);
231+
expect(rootSpan.attributes['initial_load_trace_id']).toBe(
232+
INITIAL_LOAD_TRACE_ID
233+
);
234+
expect(rootSpan.links).toEqual(EXPECTED_LINKS);
196235
expect(rootSpan.ended).toBeTruthy();
197236
expect(rootSpan.duration).toBeLessThanOrEqual(TIME_BUFFER);
198237
done();
@@ -217,6 +256,10 @@ describe('InteractionTracker', () => {
217256
expect(rootSpan.name).toBe('test interaction');
218257
expect(rootSpan.attributes['EventType']).toBe('click');
219258
expect(rootSpan.attributes['TargetElement']).toBe(BUTTON_TAG_NAME);
259+
expect(rootSpan.attributes['initial_load_trace_id']).toBe(
260+
INITIAL_LOAD_TRACE_ID
261+
);
262+
expect(rootSpan.links).toEqual(EXPECTED_LINKS);
220263
expect(rootSpan.ended).toBeTruthy();
221264
// As this click is done at 'RESET_TRACING_ZONE_DELAY - 10' and this click has a
222265
// setTimeout, the minimum time taken by this click is the sum of these values.
@@ -248,6 +291,10 @@ describe('InteractionTracker', () => {
248291
expect(rootSpan.name).toBe('test interaction');
249292
expect(rootSpan.attributes['EventType']).toBe('click');
250293
expect(rootSpan.attributes['TargetElement']).toBe(BUTTON_TAG_NAME);
294+
expect(rootSpan.attributes['initial_load_trace_id']).toBe(
295+
INITIAL_LOAD_TRACE_ID
296+
);
297+
expect(rootSpan.links).toEqual(EXPECTED_LINKS);
251298
expect(rootSpan.ended).toBeTruthy();
252299
// Test related to the first interaction
253300
if (onEndSpanSpy.calls.count() === 1) {
@@ -288,6 +335,10 @@ describe('InteractionTracker', () => {
288335
expect(rootSpan.name).toBe('Navigation /test_navigation');
289336
expect(rootSpan.attributes['EventType']).toBe('click');
290337
expect(rootSpan.attributes['TargetElement']).toBe(BUTTON_TAG_NAME);
338+
expect(rootSpan.attributes['initial_load_trace_id']).toBe(
339+
INITIAL_LOAD_TRACE_ID
340+
);
341+
expect(rootSpan.links).toEqual(EXPECTED_LINKS);
291342
expect(rootSpan.ended).toBeTruthy();
292343
expect(rootSpan.duration).toBeGreaterThanOrEqual(SET_TIMEOUT_TIME);
293344
expect(rootSpan.duration).toBeLessThanOrEqual(
@@ -311,6 +362,10 @@ describe('InteractionTracker', () => {
311362
expect(rootSpan.name).toBe('Test navigation');
312363
expect(rootSpan.attributes['EventType']).toBe('click');
313364
expect(rootSpan.attributes['TargetElement']).toBe(BUTTON_TAG_NAME);
365+
expect(rootSpan.attributes['initial_load_trace_id']).toBe(
366+
INITIAL_LOAD_TRACE_ID
367+
);
368+
expect(rootSpan.links).toEqual(EXPECTED_LINKS);
314369
expect(rootSpan.ended).toBeTruthy();
315370
expect(rootSpan.duration).toBeGreaterThanOrEqual(SET_TIMEOUT_TIME);
316371
expect(rootSpan.duration).toBeLessThanOrEqual(
@@ -338,6 +393,10 @@ describe('InteractionTracker', () => {
338393
expect(rootSpan.name).toBe('test interaction');
339394
expect(rootSpan.attributes['EventType']).toBe('click');
340395
expect(rootSpan.attributes['TargetElement']).toBe(BUTTON_TAG_NAME);
396+
expect(rootSpan.attributes['initial_load_trace_id']).toBe(
397+
INITIAL_LOAD_TRACE_ID
398+
);
399+
expect(rootSpan.links).toEqual(EXPECTED_LINKS);
341400
expect(rootSpan.ended).toBeTruthy();
342401
expect(rootSpan.spans.length).toBe(1);
343402
const childSpan = rootSpan.spans[0];
@@ -381,6 +440,10 @@ describe('InteractionTracker', () => {
381440
expect(rootSpan.name).toBe('test interaction');
382441
expect(rootSpan.attributes['EventType']).toBe('click');
383442
expect(rootSpan.attributes['TargetElement']).toBe(BUTTON_TAG_NAME);
443+
expect(rootSpan.attributes['initial_load_trace_id']).toBe(
444+
INITIAL_LOAD_TRACE_ID
445+
);
446+
expect(rootSpan.links).toEqual(EXPECTED_LINKS);
384447
expect(rootSpan.ended).toBeTruthy();
385448
expect(rootSpan.duration).toBeGreaterThanOrEqual(XHR_TIME);
386449
expect(rootSpan.duration).toBeLessThanOrEqual(XHR_TIME + TIME_BUFFER);
@@ -432,6 +495,10 @@ describe('InteractionTracker', () => {
432495
expect(rootSpan.name).toBe('test interaction');
433496
expect(rootSpan.attributes['EventType']).toBe('click');
434497
expect(rootSpan.attributes['TargetElement']).toBe(BUTTON_TAG_NAME);
498+
expect(rootSpan.attributes['initial_load_trace_id']).toBe(
499+
INITIAL_LOAD_TRACE_ID
500+
);
501+
expect(rootSpan.links).toEqual(EXPECTED_LINKS);
435502
expect(rootSpan.ended).toBeTruthy();
436503
expect(rootSpan.duration).toBeGreaterThanOrEqual(XHR_TIME);
437504
expect(rootSpan.duration).toBeLessThanOrEqual(XHR_TIME + TIME_BUFFER);
@@ -509,6 +576,10 @@ describe('InteractionTracker', () => {
509576
expect(rootSpan.name).toBe('test interaction');
510577
expect(rootSpan.attributes['EventType']).toBe('click');
511578
expect(rootSpan.attributes['TargetElement']).toBe(BUTTON_TAG_NAME);
579+
expect(rootSpan.attributes['initial_load_trace_id']).toBe(
580+
INITIAL_LOAD_TRACE_ID
581+
);
582+
expect(rootSpan.links).toEqual(EXPECTED_LINKS);
512583
expect(rootSpan.ended).toBeTruthy();
513584
expect(rootSpan.duration).toBeGreaterThanOrEqual(interactionTime);
514585
expect(rootSpan.duration).toBeLessThanOrEqual(

packages/opencensus-web-instrumentation-zone/test/test-on-page-interaction-stop-watch.ts

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,14 +13,20 @@
1313
* See the License for the specific language governing permissions and
1414
* limitations under the License.
1515
*/
16-
import { RootSpan, Tracer } from '@opencensus/web-core';
16+
import {
17+
RootSpan,
18+
Tracer,
19+
WindowWithOcwGlobals,
20+
LinkType,
21+
} from '@opencensus/web-core';
1722
import { OnPageInteractionStopwatch } from '../src/on-page-interaction-stop-watch';
1823

1924
describe('OnPageInteractionStopWatch', () => {
2025
let root: RootSpan;
2126
let tracer: Tracer;
2227
let interaction: OnPageInteractionStopwatch;
2328
let target: HTMLElement;
29+
const windowWithOcwGlobals = window as WindowWithOcwGlobals;
2430

2531
describe('Tasks tracking', () => {
2632
beforeEach(() => {
@@ -53,6 +59,9 @@ describe('OnPageInteractionStopWatch', () => {
5359
});
5460

5561
describe('stopAndRecord()', () => {
62+
const traceId = '0af7651916cd43dd8448eb211c80319c';
63+
const spanId = 'b7ad6b7169203331';
64+
windowWithOcwGlobals.traceparent = `00-${traceId}-${spanId}-01`;
5665
beforeEach(() => {
5766
tracer = Tracer.instance;
5867
root = new RootSpan(tracer, { name: 'root1' });
@@ -72,6 +81,15 @@ describe('OnPageInteractionStopWatch', () => {
7281

7382
expect(root.attributes['EventType']).toBe('click');
7483
expect(root.attributes['TargetElement']).toBe(target.tagName);
84+
expect(root.attributes['initial_load_trace_id']).toBe(traceId);
85+
expect(root.links).toEqual([
86+
{
87+
traceId,
88+
spanId,
89+
type: LinkType.PARENT_LINKED_SPAN,
90+
attributes: {},
91+
},
92+
]);
7593
expect(tracer.onEndSpan).toHaveBeenCalledWith(root);
7694
});
7795
it('Should not finish the interaction when there are remaining tasks', () => {

0 commit comments

Comments
 (0)