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

Commit

Permalink
Relate user interaction traces back to the initial page load trace (#145
Browse files Browse the repository at this point in the history
)
  • Loading branch information
crdgonzalezca authored and draffensperger committed Jul 26, 2019
1 parent 14d9c91 commit 4ab8111
Show file tree
Hide file tree
Showing 6 changed files with 125 additions and 10 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -89,3 +89,8 @@ export const LONG_TASK_PREFIX = 'long_task.';
* long task.
*/
export const ATTRIBUTE_LONG_TASK_ATTRIBUTION = `${LONG_TASK_PREFIX}attribution`;

/**
* Attribute for spans to be related back to the initial load trace.
*/
export const ATTRIBUTE_INITIAL_LOAD_TRACE_ID = 'initial_load_trace_id';
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import {
ATTRIBUTE_HTTP_USER_AGENT,
ATTRIBUTE_LONG_TASK_ATTRIBUTION,
ATTRIBUTE_NAV_TYPE,
ATTRIBUTE_INITIAL_LOAD_TRACE_ID,
parseUrl,
randomSpanId,
randomTraceId,
Expand Down Expand Up @@ -92,6 +93,9 @@ export function getInitialLoadRootSpan(
root.annotations = getNavigationAnnotations(perfEntries);
root.attributes[ATTRIBUTE_HTTP_URL] = navigationUrl;
root.attributes[ATTRIBUTE_HTTP_USER_AGENT] = navigator.userAgent;
// This is included to enable trace search by attribute to find an initial
// load trace and its interaction traces via a single attribute query.
root.attributes[ATTRIBUTE_INITIAL_LOAD_TRACE_ID] = root.traceId;

if (navTiming) {
root.endPerfTime = navTiming.loadEventEnd;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,7 @@ const EXPECTED_ROOT_ATTRIBUTES: Attributes = {
'http.url': 'http://localhost:4200/',
'http.user_agent': USER_AGENT,
'nav.type': 'navigate',
initial_load_trace_id: '0000000000000000000000000000000b',
};
const EXPECTED_ROOT_ANNOTATIONS: Annotation[] = [
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,10 @@ import {
ATTRIBUTE_HTTP_URL,
ATTRIBUTE_HTTP_USER_AGENT,
ATTRIBUTE_HTTP_PATH,
ATTRIBUTE_INITIAL_LOAD_TRACE_ID,
LinkType,
} from '@opencensus/web-core';
import { getInitialLoadSpanContext } from '@opencensus/web-initial-load';

/** A helper class for tracking on page interactions. */
export class OnPageInteractionStopwatch {
Expand All @@ -44,7 +47,8 @@ export class OnPageInteractionStopwatch {
}

/**
* Stops the stopwatch, fills root span attributes and ends the span.
* Stops the stopwatch. Adds root span attributes and link to the initial
* load page, also, ends the span.
* If has remaining tasks do not end the root span.
*/
stopAndRecord(): void {
Expand All @@ -56,6 +60,18 @@ export class OnPageInteractionStopwatch {
rootSpan.addAttribute(ATTRIBUTE_HTTP_URL, this.data.startLocationHref);
rootSpan.addAttribute(ATTRIBUTE_HTTP_PATH, this.data.startLocationPath);
rootSpan.addAttribute(ATTRIBUTE_HTTP_USER_AGENT, navigator.userAgent);
const initialLoadSpanContext = getInitialLoadSpanContext();
// This is included to enable trace search by attribute to find an initial
// load trace and its interaction traces via a single attribute query.
rootSpan.addAttribute(
ATTRIBUTE_INITIAL_LOAD_TRACE_ID,
initialLoadSpanContext.traceId
);
rootSpan.addLink(
initialLoadSpanContext.traceId,
initialLoadSpanContext.spanId,
LinkType.PARENT_LINKED_SPAN
);
rootSpan.end();
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ import {
ATTRIBUTE_HTTP_STATUS_CODE,
ATTRIBUTE_HTTP_METHOD,
WindowWithOcwGlobals,
LinkType,
Link,
} from '@opencensus/web-core';
import {
InteractionTracker,
Expand All @@ -32,18 +34,27 @@ import {
} from '../src/monkey-patching';
import { spanContextToTraceParent } from '@opencensus/web-propagation-tracecontext';
import { createFakePerfResourceEntry, spyPerfEntryByType } from './util';
import { getInitialLoadSpanContext } from '@opencensus/web-initial-load';

describe('InteractionTracker', () => {
doPatching();
InteractionTracker.startTracking();
let onEndSpanSpy: jasmine.Spy;
const windowWithOcwGlobals = window as WindowWithOcwGlobals;
// Sample 100% of interactions for the testing. Necessary as the sampling
// decision is supposed to be done in the initial load page and the
// interaction tracker uses the same sampling decision.
windowWithOcwGlobals.ocSampleRate = 1.0;
getInitialLoadSpanContext();
// Set the traceparent to fake the initial load Span Context. Also, Sample
// 100% of interactions for the testing. Necessary as this is supposed to be
// done by the initial load page.
const INITIAL_LOAD_TRACE_ID = '0af7651916cd43dd8448eb211c80319c';
const INITIAL_LOAD_SPAN_ID = 'b7ad6b7169203331';
windowWithOcwGlobals.traceparent = `00-${INITIAL_LOAD_TRACE_ID}-${INITIAL_LOAD_SPAN_ID}-01`;

const EXPECTED_LINKS: Link[] = [
{
traceId: INITIAL_LOAD_TRACE_ID,
spanId: INITIAL_LOAD_SPAN_ID,
type: LinkType.PARENT_LINKED_SPAN,
attributes: {},
},
];

// Use Buffer time as we expect that these interactions take
// a little extra time to complete due to the setTimeout that
Expand All @@ -66,10 +77,14 @@ describe('InteractionTracker', () => {
expect(rootSpan.name).toBe('test interaction');
expect(rootSpan.attributes['EventType']).toBe('click');
expect(rootSpan.attributes['TargetElement']).toBe(BUTTON_TAG_NAME);
expect(rootSpan.attributes['initial_load_trace_id']).toBe(
INITIAL_LOAD_TRACE_ID
);
expect(rootSpan.links).toEqual(EXPECTED_LINKS);
expect(rootSpan.ended).toBeTruthy();
// As there is another setTimeOut that completes the interaction, the
// span duraction is not precise, then only test if the interaction duration
// finishes within a range.
// span duraction is not precise, then only test if the interaction
// duration finishes within a range.
expect(rootSpan.duration).toBeLessThan(TIME_BUFFER);
done();
});
Expand All @@ -85,6 +100,10 @@ describe('InteractionTracker', () => {
expect(rootSpan.name).toBe('test interaction');
expect(rootSpan.attributes['EventType']).toBe('click');
expect(rootSpan.attributes['TargetElement']).toBe(BUTTON_TAG_NAME);
expect(rootSpan.attributes['initial_load_trace_id']).toBe(
INITIAL_LOAD_TRACE_ID
);
expect(rootSpan.links).toEqual(EXPECTED_LINKS);
expect(rootSpan.ended).toBeTruthy();
expect(rootSpan.duration).toBeGreaterThanOrEqual(SET_TIMEOUT_TIME);
expect(rootSpan.duration).toBeLessThanOrEqual(
Expand All @@ -105,6 +124,10 @@ describe('InteractionTracker', () => {
expect(rootSpan.name).toBe('test interaction');
expect(rootSpan.attributes['EventType']).toBe('click');
expect(rootSpan.attributes['TargetElement']).toBe(BUTTON_TAG_NAME);
expect(rootSpan.attributes['initial_load_trace_id']).toBe(
INITIAL_LOAD_TRACE_ID
);
expect(rootSpan.links).toEqual(EXPECTED_LINKS);
expect(rootSpan.ended).toBeTruthy();
expect(rootSpan.duration).toBeLessThanOrEqual(TIME_BUFFER);
done();
Expand All @@ -125,6 +148,10 @@ describe('InteractionTracker', () => {
expect(rootSpan.name).toBe('test interaction');
expect(rootSpan.attributes['EventType']).toBe('click');
expect(rootSpan.attributes['TargetElement']).toBe(BUTTON_TAG_NAME);
expect(rootSpan.attributes['initial_load_trace_id']).toBe(
INITIAL_LOAD_TRACE_ID
);
expect(rootSpan.links).toEqual(EXPECTED_LINKS);
expect(rootSpan.ended).toBeTruthy();
expect(rootSpan.duration).toBeGreaterThanOrEqual(interactionTime);
//The duration has to be less than set to the canceled timeout.
Expand Down Expand Up @@ -152,6 +179,10 @@ describe('InteractionTracker', () => {
expect(rootSpan.name).toBe('test interaction');
expect(rootSpan.attributes['EventType']).toBe('click');
expect(rootSpan.attributes['TargetElement']).toBe(BUTTON_TAG_NAME);
expect(rootSpan.attributes['initial_load_trace_id']).toBe(
INITIAL_LOAD_TRACE_ID
);
expect(rootSpan.links).toEqual(EXPECTED_LINKS);
expect(rootSpan.ended).toBeTruthy();
expect(rootSpan.duration).toBeGreaterThanOrEqual(SET_TIMEOUT_TIME);
expect(rootSpan.duration).toBeLessThanOrEqual(
Expand All @@ -172,6 +203,10 @@ describe('InteractionTracker', () => {
expect(rootSpan.name).toBe('button#test_element click');
expect(rootSpan.attributes['EventType']).toBe('click');
expect(rootSpan.attributes['TargetElement']).toBe(BUTTON_TAG_NAME);
expect(rootSpan.attributes['initial_load_trace_id']).toBe(
INITIAL_LOAD_TRACE_ID
);
expect(rootSpan.links).toEqual(EXPECTED_LINKS);
expect(rootSpan.ended).toBeTruthy();
expect(rootSpan.duration).toBeGreaterThanOrEqual(SET_TIMEOUT_TIME);
expect(rootSpan.duration).toBeLessThanOrEqual(
Expand All @@ -193,6 +228,10 @@ describe('InteractionTracker', () => {
expect(rootSpan.name).toBe('test interaction');
expect(rootSpan.attributes['EventType']).toBe('click');
expect(rootSpan.attributes['TargetElement']).toBe(BUTTON_TAG_NAME);
expect(rootSpan.attributes['initial_load_trace_id']).toBe(
INITIAL_LOAD_TRACE_ID
);
expect(rootSpan.links).toEqual(EXPECTED_LINKS);
expect(rootSpan.ended).toBeTruthy();
expect(rootSpan.duration).toBeLessThanOrEqual(TIME_BUFFER);
done();
Expand All @@ -217,6 +256,10 @@ describe('InteractionTracker', () => {
expect(rootSpan.name).toBe('test interaction');
expect(rootSpan.attributes['EventType']).toBe('click');
expect(rootSpan.attributes['TargetElement']).toBe(BUTTON_TAG_NAME);
expect(rootSpan.attributes['initial_load_trace_id']).toBe(
INITIAL_LOAD_TRACE_ID
);
expect(rootSpan.links).toEqual(EXPECTED_LINKS);
expect(rootSpan.ended).toBeTruthy();
// As this click is done at 'RESET_TRACING_ZONE_DELAY - 10' and this click has a
// setTimeout, the minimum time taken by this click is the sum of these values.
Expand Down Expand Up @@ -248,6 +291,10 @@ describe('InteractionTracker', () => {
expect(rootSpan.name).toBe('test interaction');
expect(rootSpan.attributes['EventType']).toBe('click');
expect(rootSpan.attributes['TargetElement']).toBe(BUTTON_TAG_NAME);
expect(rootSpan.attributes['initial_load_trace_id']).toBe(
INITIAL_LOAD_TRACE_ID
);
expect(rootSpan.links).toEqual(EXPECTED_LINKS);
expect(rootSpan.ended).toBeTruthy();
// Test related to the first interaction
if (onEndSpanSpy.calls.count() === 1) {
Expand Down Expand Up @@ -288,6 +335,10 @@ describe('InteractionTracker', () => {
expect(rootSpan.name).toBe('Navigation /test_navigation');
expect(rootSpan.attributes['EventType']).toBe('click');
expect(rootSpan.attributes['TargetElement']).toBe(BUTTON_TAG_NAME);
expect(rootSpan.attributes['initial_load_trace_id']).toBe(
INITIAL_LOAD_TRACE_ID
);
expect(rootSpan.links).toEqual(EXPECTED_LINKS);
expect(rootSpan.ended).toBeTruthy();
expect(rootSpan.duration).toBeGreaterThanOrEqual(SET_TIMEOUT_TIME);
expect(rootSpan.duration).toBeLessThanOrEqual(
Expand All @@ -311,6 +362,10 @@ describe('InteractionTracker', () => {
expect(rootSpan.name).toBe('Test navigation');
expect(rootSpan.attributes['EventType']).toBe('click');
expect(rootSpan.attributes['TargetElement']).toBe(BUTTON_TAG_NAME);
expect(rootSpan.attributes['initial_load_trace_id']).toBe(
INITIAL_LOAD_TRACE_ID
);
expect(rootSpan.links).toEqual(EXPECTED_LINKS);
expect(rootSpan.ended).toBeTruthy();
expect(rootSpan.duration).toBeGreaterThanOrEqual(SET_TIMEOUT_TIME);
expect(rootSpan.duration).toBeLessThanOrEqual(
Expand Down Expand Up @@ -338,6 +393,10 @@ describe('InteractionTracker', () => {
expect(rootSpan.name).toBe('test interaction');
expect(rootSpan.attributes['EventType']).toBe('click');
expect(rootSpan.attributes['TargetElement']).toBe(BUTTON_TAG_NAME);
expect(rootSpan.attributes['initial_load_trace_id']).toBe(
INITIAL_LOAD_TRACE_ID
);
expect(rootSpan.links).toEqual(EXPECTED_LINKS);
expect(rootSpan.ended).toBeTruthy();
expect(rootSpan.spans.length).toBe(1);
const childSpan = rootSpan.spans[0];
Expand Down Expand Up @@ -381,6 +440,10 @@ describe('InteractionTracker', () => {
expect(rootSpan.name).toBe('test interaction');
expect(rootSpan.attributes['EventType']).toBe('click');
expect(rootSpan.attributes['TargetElement']).toBe(BUTTON_TAG_NAME);
expect(rootSpan.attributes['initial_load_trace_id']).toBe(
INITIAL_LOAD_TRACE_ID
);
expect(rootSpan.links).toEqual(EXPECTED_LINKS);
expect(rootSpan.ended).toBeTruthy();
expect(rootSpan.duration).toBeGreaterThanOrEqual(XHR_TIME);
expect(rootSpan.duration).toBeLessThanOrEqual(XHR_TIME + TIME_BUFFER);
Expand Down Expand Up @@ -432,6 +495,10 @@ describe('InteractionTracker', () => {
expect(rootSpan.name).toBe('test interaction');
expect(rootSpan.attributes['EventType']).toBe('click');
expect(rootSpan.attributes['TargetElement']).toBe(BUTTON_TAG_NAME);
expect(rootSpan.attributes['initial_load_trace_id']).toBe(
INITIAL_LOAD_TRACE_ID
);
expect(rootSpan.links).toEqual(EXPECTED_LINKS);
expect(rootSpan.ended).toBeTruthy();
expect(rootSpan.duration).toBeGreaterThanOrEqual(XHR_TIME);
expect(rootSpan.duration).toBeLessThanOrEqual(XHR_TIME + TIME_BUFFER);
Expand Down Expand Up @@ -509,6 +576,10 @@ describe('InteractionTracker', () => {
expect(rootSpan.name).toBe('test interaction');
expect(rootSpan.attributes['EventType']).toBe('click');
expect(rootSpan.attributes['TargetElement']).toBe(BUTTON_TAG_NAME);
expect(rootSpan.attributes['initial_load_trace_id']).toBe(
INITIAL_LOAD_TRACE_ID
);
expect(rootSpan.links).toEqual(EXPECTED_LINKS);
expect(rootSpan.ended).toBeTruthy();
expect(rootSpan.duration).toBeGreaterThanOrEqual(interactionTime);
expect(rootSpan.duration).toBeLessThanOrEqual(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,14 +13,20 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { RootSpan, Tracer } from '@opencensus/web-core';
import {
RootSpan,
Tracer,
WindowWithOcwGlobals,
LinkType,
} from '@opencensus/web-core';
import { OnPageInteractionStopwatch } from '../src/on-page-interaction-stop-watch';

describe('OnPageInteractionStopWatch', () => {
let root: RootSpan;
let tracer: Tracer;
let interaction: OnPageInteractionStopwatch;
let target: HTMLElement;
const windowWithOcwGlobals = window as WindowWithOcwGlobals;

describe('Tasks tracking', () => {
beforeEach(() => {
Expand Down Expand Up @@ -53,6 +59,9 @@ describe('OnPageInteractionStopWatch', () => {
});

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

expect(root.attributes['EventType']).toBe('click');
expect(root.attributes['TargetElement']).toBe(target.tagName);
expect(root.attributes['initial_load_trace_id']).toBe(traceId);
expect(root.links).toEqual([
{
traceId,
spanId,
type: LinkType.PARENT_LINKED_SPAN,
attributes: {},
},
]);
expect(tracer.onEndSpan).toHaveBeenCalledWith(root);
});
it('Should not finish the interaction when there are remaining tasks', () => {
Expand Down

0 comments on commit 4ab8111

Please sign in to comment.