From ff9605aea9ef4f66985c8b816c5179405d5633dd Mon Sep 17 00:00:00 2001 From: Cristian Gonzalez Date: Wed, 17 Jul 2019 16:23:01 -0400 Subject: [PATCH 1/4] Update example and documentation on how to create custom spans --- examples/user_interaction/client/src/App.js | 12 +++++--- .../user_interaction/client/src/SecondPage.js | 2 +- examples/user_interaction/client/src/index.js | 2 +- .../README.md | 29 +++++++++++++++++-- 4 files changed, 36 insertions(+), 9 deletions(-) diff --git a/examples/user_interaction/client/src/App.js b/examples/user_interaction/client/src/App.js index 8a312d9a..a2e96256 100644 --- a/examples/user_interaction/client/src/App.js +++ b/examples/user_interaction/client/src/App.js @@ -5,7 +5,7 @@ * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * - * gRPC://www.apache.org/licenses/LICENSE-2.0 + * https://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, @@ -15,6 +15,7 @@ */ import React from 'react'; +import { tracing } from '@opencensus/web-core'; class App extends React.Component { @@ -28,6 +29,7 @@ class App extends React.Component { handleClick() { // Use promises to test behavior on MicroTasks. + const childSpan = tracing.tracer.startChildSpan({ name: 'promise' }); const promise = new Promise(resolve => { setTimeout(function () { resolve(); @@ -35,15 +37,17 @@ class App extends React.Component { }); promise.then(() => { - console.log("Resolving promise"); + childSpan.end(); this.callSleepApi(); }); } callSleepApi() { const xhr = new XMLHttpRequest(); + const span = tracing.tracer.startChildSpan({ name: 'Sleep API' }); xhr.onreadystatechange = () => { if (xhr.readyState === XMLHttpRequest.DONE) { + span.end(); this.callPrimeNumbersApi(); } }; @@ -66,10 +70,10 @@ class App extends React.Component { } callCalculatePi() { + const span = tracing.tracer.startChildSpan({ name: 'Calculate PI' }); const time = Date.now(); - console.log("Calculating PI"); const pi = this.calculatePi(); - console.log("Finished calculating PI"); + span.end(); return { time: (Date.now() - time), value: pi }; } diff --git a/examples/user_interaction/client/src/SecondPage.js b/examples/user_interaction/client/src/SecondPage.js index 380fde23..4c4b0462 100644 --- a/examples/user_interaction/client/src/SecondPage.js +++ b/examples/user_interaction/client/src/SecondPage.js @@ -5,7 +5,7 @@ * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * - * gRPC://www.apache.org/licenses/LICENSE-2.0 + * https://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, diff --git a/examples/user_interaction/client/src/index.js b/examples/user_interaction/client/src/index.js index 1c9e08c8..d642c76d 100644 --- a/examples/user_interaction/client/src/index.js +++ b/examples/user_interaction/client/src/index.js @@ -5,7 +5,7 @@ * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * - * gRPC://www.apache.org/licenses/LICENSE-2.0 + * https://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, diff --git a/packages/opencensus-web-instrumentation-zone/README.md b/packages/opencensus-web-instrumentation-zone/README.md index 91d69771..cf2ce8e2 100644 --- a/packages/opencensus-web-instrumentation-zone/README.md +++ b/packages/opencensus-web-instrumentation-zone/README.md @@ -11,9 +11,31 @@ The library is in alpha stage and the API is subject to change. ## Usage -Currently the primary intended usage of OpenCensus Web is to collect -spans from the resource timing waterfall of an initial page load. See the -[OpenCensus Web readme][oc-web-readme-url] for details. +#### Custom spans +In addition to automatic user interaction tracing, it is possible to create +your own spans for the tasks or code involved in a user interaction. +Here is an example for JavaScript + +```javascript +import { tracing } from '@opencensus/web-core'; + +function handleClick() { + // Start child span which will be child of the current root span on the current interaction. + // To make sure the span is attached to the root span, add this in code that the button is running. + const childSpan = tracing.tracer.startChildSpan({ + name: 'name of your child span' + }); + // Do some operation... + // Finish the child span at the end of it's operation + childSpan.end(); +} + +// Create a fake button to point out the custom span is created in the click handler. +const button = document.createElement('button'); +button.onclick = handleClick; +``` +Check the [user interaction client example][client-example-url] which instruments the package and +create some custom spans. ## Useful links - For more information on OpenCensus, visit: @@ -31,3 +53,4 @@ Apache 2.0 - See [LICENSE][license-url] for more information. [nav-timing-url]: https://www.w3.org/TR/navigation-timing-2/ [resource-timing-url]: https://www.w3.org/TR/resource-timing-2/ [long-tasks-url]: https://w3c.github.io/longtasks/ +[client-example-url]: https://github.com/census-instrumentation/opencensus-web/tree/master/examples/user_interaction/client \ No newline at end of file From 14be981c94484d35003a2c2f13fe46649263d8ff Mon Sep 17 00:00:00 2001 From: Cristian Gonzalez Date: Wed, 17 Jul 2019 16:32:46 -0400 Subject: [PATCH 2/4] Update comments --- examples/user_interaction/client/src/App.js | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/examples/user_interaction/client/src/App.js b/examples/user_interaction/client/src/App.js index a2e96256..20285e3a 100644 --- a/examples/user_interaction/client/src/App.js +++ b/examples/user_interaction/client/src/App.js @@ -28,8 +28,11 @@ class App extends React.Component { } handleClick() { - // Use promises to test behavior on MicroTasks. + // Start a child span for the Promise, this span will be son of the + // current root span related to the current user interaction. + // These spans should be created in the code the click handler will run. const childSpan = tracing.tracer.startChildSpan({ name: 'promise' }); + // Use promises to test behavior on MicroTasks. const promise = new Promise(resolve => { setTimeout(function () { resolve(); @@ -37,6 +40,7 @@ class App extends React.Component { }); promise.then(() => { + // End the span as the Promise already finished. childSpan.end(); this.callSleepApi(); }); @@ -44,9 +48,11 @@ class App extends React.Component { callSleepApi() { const xhr = new XMLHttpRequest(); + // Create a child span for the XHR. const span = tracing.tracer.startChildSpan({ name: 'Sleep API' }); xhr.onreadystatechange = () => { if (xhr.readyState === XMLHttpRequest.DONE) { + // End the XHR span once it is DONE. span.end(); this.callPrimeNumbersApi(); } @@ -70,6 +76,7 @@ class App extends React.Component { } callCalculatePi() { + // Start span for synchronous code. const span = tracing.tracer.startChildSpan({ name: 'Calculate PI' }); const time = Date.now(); const pi = this.calculatePi(); From f4a0e07fcd5da8dd56e85b122314a6f4b6ad809a Mon Sep 17 00:00:00 2001 From: Cristian Gonzalez Date: Fri, 19 Jul 2019 09:52:38 -0400 Subject: [PATCH 3/4] Add testing for custom spans and some refactoring --- examples/user_interaction/client/src/App.js | 22 ++++++------ .../test/test-interaction-tracker.ts | 35 +++++++++++++++++++ 2 files changed, 47 insertions(+), 10 deletions(-) diff --git a/examples/user_interaction/client/src/App.js b/examples/user_interaction/client/src/App.js index 20285e3a..be67e246 100644 --- a/examples/user_interaction/client/src/App.js +++ b/examples/user_interaction/client/src/App.js @@ -28,20 +28,22 @@ class App extends React.Component { } handleClick() { - // Start a child span for the Promise, this span will be son of the - // current root span related to the current user interaction. - // These spans should be created in the code the click handler will run. - const childSpan = tracing.tracer.startChildSpan({ name: 'promise' }); // Use promises to test behavior on MicroTasks. const promise = new Promise(resolve => { + // Start a child span for the setTimeout as this is the operation we want + // to measure. + // This span will be child of the current root span related to the + // current user interaction. Additionally, these spans should be created + // in the code the click handler will run. + const setTimeoutCustomSpan = tracing.tracer.startChildSpan({ name: 'setTimeout custom span' }); setTimeout(function () { resolve(); + // End the span as the setTimeout has finished running the callback. + setTimeoutCustomSpan.end(); }, 1000); }); promise.then(() => { - // End the span as the Promise already finished. - childSpan.end(); this.callSleepApi(); }); } @@ -49,11 +51,11 @@ class App extends React.Component { callSleepApi() { const xhr = new XMLHttpRequest(); // Create a child span for the XHR. - const span = tracing.tracer.startChildSpan({ name: 'Sleep API' }); + const callSleepApiCustomSpan = tracing.tracer.startChildSpan({ name: 'Call Sleep API' }); xhr.onreadystatechange = () => { if (xhr.readyState === XMLHttpRequest.DONE) { // End the XHR span once it is DONE. - span.end(); + callSleepApiCustomSpan.end(); this.callPrimeNumbersApi(); } }; @@ -77,10 +79,10 @@ class App extends React.Component { callCalculatePi() { // Start span for synchronous code. - const span = tracing.tracer.startChildSpan({ name: 'Calculate PI' }); + const calculatePiCustomSpan = tracing.tracer.startChildSpan({ name: 'Calculate PI' }); const time = Date.now(); const pi = this.calculatePi(); - span.end(); + calculatePiCustomSpan.end(); return { time: (Date.now() - time), value: pi }; } diff --git a/packages/opencensus-web-instrumentation-zone/test/test-interaction-tracker.ts b/packages/opencensus-web-instrumentation-zone/test/test-interaction-tracker.ts index be5081b7..f3ac6ef6 100644 --- a/packages/opencensus-web-instrumentation-zone/test/test-interaction-tracker.ts +++ b/packages/opencensus-web-instrumentation-zone/test/test-interaction-tracker.ts @@ -280,6 +280,41 @@ describe('InteractionTracker', () => { }); }); + describe('Custom Spans', () => { + it('Should handle the custom spans and add them to the current root span as child spans', done => { + const onclick = () => { + // Start a custom span for the setTimeout. + const setTimeoutCustomSpan = tracing.tracer.startChildSpan({ + name: 'setTimeout custom span', + }); + setTimeout(() => { + noop(); + setTimeoutCustomSpan.end(); + }, SET_TIMEOUT_TIME); + }; + fakeInteraction(onclick); + + onEndSpanSpy.and.callFake((rootSpan: Span) => { + expect(rootSpan.name).toBe('test interaction'); + expect(rootSpan.attributes['EventType']).toBe('click'); + expect(rootSpan.attributes['TargetElement']).toBe(BUTTON_TAG_NAME); + expect(rootSpan.ended).toBeTruthy(); + expect(rootSpan.spans.length).toBe(1); + const childSpan = rootSpan.spans[0]; + expect(childSpan.name).toBe('setTimeout custom span'); + expect(childSpan.duration).toBeGreaterThanOrEqual(SET_TIMEOUT_TIME); + expect(childSpan.duration).toBeLessThanOrEqual( + SET_TIMEOUT_TIME + TIME_BUFFER + ); + expect(rootSpan.duration).toBeGreaterThanOrEqual(SET_TIMEOUT_TIME); + expect(rootSpan.duration).toBeLessThanOrEqual( + SET_TIMEOUT_TIME + TIME_BUFFER + ); + done(); + }); + }); + }); + describe('HTTP requests', () => { // Value to be full when the XMLHttpRequest.send method is faked, // That way the perfornamce resource entries have a accurate timing. From ebfc930a4dbd664a0d33ddb70a3c9859ee74d477 Mon Sep 17 00:00:00 2001 From: Cristian Gonzalez Date: Fri, 19 Jul 2019 14:18:22 -0400 Subject: [PATCH 4/4] Refactoring --- examples/user_interaction/client/src/App.js | 4 +++- .../test/test-interaction-tracker.ts | 1 - 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/examples/user_interaction/client/src/App.js b/examples/user_interaction/client/src/App.js index be67e246..53aef5c1 100644 --- a/examples/user_interaction/client/src/App.js +++ b/examples/user_interaction/client/src/App.js @@ -50,7 +50,9 @@ class App extends React.Component { callSleepApi() { const xhr = new XMLHttpRequest(); - // Create a child span for the XHR. + // Create a child span for the XHR. It is possible to create your own spans + // even if the involved task or operation already generates an automatic + // span. In this case, automatic spans are generated for XHRs. const callSleepApiCustomSpan = tracing.tracer.startChildSpan({ name: 'Call Sleep API' }); xhr.onreadystatechange = () => { if (xhr.readyState === XMLHttpRequest.DONE) { diff --git a/packages/opencensus-web-instrumentation-zone/test/test-interaction-tracker.ts b/packages/opencensus-web-instrumentation-zone/test/test-interaction-tracker.ts index f3ac6ef6..df0302c8 100644 --- a/packages/opencensus-web-instrumentation-zone/test/test-interaction-tracker.ts +++ b/packages/opencensus-web-instrumentation-zone/test/test-interaction-tracker.ts @@ -288,7 +288,6 @@ describe('InteractionTracker', () => { name: 'setTimeout custom span', }); setTimeout(() => { - noop(); setTimeoutCustomSpan.end(); }, SET_TIMEOUT_TIME); };