diff --git a/examples/user_interaction/client/src/App.js b/examples/user_interaction/client/src/App.js index 8a312d9a..53aef5c1 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 { @@ -29,21 +30,34 @@ class App extends React.Component { handleClick() { // 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(() => { - console.log("Resolving promise"); this.callSleepApi(); }); } callSleepApi() { const xhr = new XMLHttpRequest(); + // 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) { + // End the XHR span once it is DONE. + callSleepApiCustomSpan.end(); this.callPrimeNumbersApi(); } }; @@ -66,10 +80,11 @@ class App extends React.Component { } callCalculatePi() { + // Start span for synchronous code. + const calculatePiCustomSpan = tracing.tracer.startChildSpan({ name: 'Calculate PI' }); const time = Date.now(); - console.log("Calculating PI"); const pi = this.calculatePi(); - console.log("Finished calculating PI"); + calculatePiCustomSpan.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 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..df0302c8 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,40 @@ 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(() => { + 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.