Skip to content

Commit b21ed97

Browse files
obecnymayurkale22
authored andcommitted
feat(plugin-document-load): new plugin for document load for web tracer (#433)
* feat(plugin-document-load): new plugin for document load for web tracer * chore: lint * chore: removing unused dependency * chore: adding prepare script * chore: cleanup of not used span processor * chore: merging exporter-console into tracing * chore: fixing timeOrigin when browser is using older version of performance (safari for example) * chore: removing @Private * chore: cleaning the docs * chore: using stubs on public instead of private * chore: added explanation when span can be undefined * chore: adding unit test for case when passed "performanceNow" is equal to 0 * chore: adding unit test for case when passed "performanceNow" is null or undefined * chore: fixing unit test with null * chore: bump version * chore: after changing enum keys to capitals I had to use values to align them with performance metric in type "PerformanceEntries" * chore: adding comments for interfaces * feat: adding possibility of setting start time for event * chore: refactoring document load to use events instead of new spans * chore: reformatting * chore: updating loop * chore: changing type for time * chore: refactoring loop, updating jsdoc * chore: splitting events into 2 spans * chore: adding possibility of calling addEvent with 2nd param as time * chore: updating the last event to be "load end" * chore: updating the name for attributes * chore: fixing test * chore: cleanups * chore: adding isTimeInput function with unit tests * chore: adding component name * chore: adding license and readme * chore: updating lint and docs jobs to use node12 image in circleci
1 parent 972faa2 commit b21ed97

File tree

33 files changed

+1530
-87
lines changed

33 files changed

+1530
-87
lines changed

.circleci/config.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ browsers_unit_tests: &browsers_unit_tests
4848
jobs:
4949
lint:
5050
docker:
51-
- image: node
51+
- image: node:12
5252
steps:
5353
- checkout
5454
- run:
@@ -59,7 +59,7 @@ jobs:
5959
command: yarn run check
6060
docs:
6161
docker:
62-
- image: node
62+
- image: node:12
6363
steps:
6464
- checkout
6565
- run:

examples/tracer-web/index.html

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,14 +3,14 @@
33

44
<head>
55
<meta charset="utf-8">
6-
<title>JS Example</title>
6+
<title>Web Tracer Example</title>
77
<base href="/">
88

99
<meta name="viewport" content="width=device-width, initial-scale=1">
1010
</head>
1111

1212
<body>
13-
Testing, debugging in development
13+
Example of using Web Tracer with document load plugin and console exporter
1414
<script type="text/javascript" src="/bundle.js"></script>
1515
</body>
1616

examples/tracer-web/index.js

Lines changed: 7 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -1,38 +1,11 @@
1+
import { ConsoleSpanExporter, SimpleSpanProcessor } from '@opentelemetry/tracing';
12
import { WebTracer } from '@opentelemetry/web';
3+
import { DocumentLoad } from '@opentelemetry/plugin-document-load';
24

3-
import * as shimmer from 'shimmer';
4-
5-
class Tester {
6-
constructor() {
7-
}
8-
add(name) {
9-
console.log('calling add', name);
10-
}
11-
}
12-
13-
const tester = new Tester();
14-
15-
const webTracer = new WebTracer();
16-
const span = webTracer.startSpan('span1');
17-
18-
shimmer.wrap(Tester.prototype, 'add', (originalFunction) => {
19-
return function patchedFunction() {
20-
try {
21-
span.addEvent('start');
22-
} catch (e) {
23-
console.log('error', e);
24-
} finally {
25-
const result = originalFunction.apply(this, arguments);
26-
span.addEvent('after call');
27-
span.end();
28-
return result;
29-
}
30-
};
31-
});
32-
33-
webTracer.withSpan(span, function () {
34-
console.log(this === span);
5+
const webTracer = new WebTracer({
6+
plugins: [
7+
new DocumentLoad()
8+
]
359
});
3610

37-
tester.add('foo');
38-
console.log(span);
11+
webTracer.addSpanProcessor(new SimpleSpanProcessor(new ConsoleSpanExporter()));

examples/tracer-web/package.json

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -26,16 +26,17 @@
2626
},
2727
"devDependencies": {
2828
"@babel/core": "^7.6.0",
29-
"@types/shimmer": "^1.0.1",
3029
"babel-loader": "^8.0.6",
31-
"shimmer": "^1.2.0",
30+
"ts-loader": "^6.0.4",
3231
"webpack": "^4.35.2",
3332
"webpack-cli": "^3.3.9",
3433
"webpack-dev-server": "^3.8.1",
3534
"webpack-merge": "^4.2.2"
3635
},
3736
"dependencies": {
37+
"@opentelemetry/plugin-document-load": "^0.1.1",
38+
"@opentelemetry/tracing": "^0.1.1",
3839
"@opentelemetry/web": "^0.1.1"
3940
},
4041
"homepage": "https://github.com/open-telemetry/opentelemetry-js#readme"
41-
}
42+
}

packages/opentelemetry-core/src/common/time.ts

Lines changed: 37 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616

1717
import * as types from '@opentelemetry/types';
1818
import { otperformance as performance } from '../platform';
19+
import { TimeOriginLegacy } from './types';
1920

2021
const NANOSECOND_DIGITS = 9;
2122
const SECOND_TO_NANOSECONDS = Math.pow(10, NANOSECOND_DIGITS);
@@ -32,10 +33,21 @@ function numberToHrtime(epochMillis: number): types.HrTime {
3233
return [seconds, nanos];
3334
}
3435

36+
function getTimeOrigin(): number {
37+
let timeOrigin = performance.timeOrigin;
38+
if (typeof timeOrigin !== 'number') {
39+
const perf: TimeOriginLegacy = (performance as unknown) as TimeOriginLegacy;
40+
timeOrigin = perf.timing && perf.timing.fetchStart;
41+
}
42+
return timeOrigin;
43+
}
44+
3545
// Returns an hrtime calculated via performance component.
3646
export function hrTime(performanceNow?: number): types.HrTime {
37-
const timeOrigin = numberToHrtime(performance.timeOrigin);
38-
const now = numberToHrtime(performanceNow || performance.now());
47+
const timeOrigin = numberToHrtime(getTimeOrigin());
48+
const now = numberToHrtime(
49+
typeof performanceNow === 'number' ? performanceNow : performance.now()
50+
);
3951

4052
let seconds = timeOrigin[0] + now[0];
4153
let nanos = timeOrigin[1] + now[1];
@@ -52,15 +64,14 @@ export function hrTime(performanceNow?: number): types.HrTime {
5264
// Converts a TimeInput to an HrTime, defaults to _hrtime().
5365
export function timeInputToHrTime(time: types.TimeInput): types.HrTime {
5466
// process.hrtime
55-
if (Array.isArray(time)) {
56-
return time;
67+
if (isTimeInputHrTime(time)) {
68+
return time as types.HrTime;
5769
} else if (typeof time === 'number') {
5870
// Must be a performance.now() if it's smaller than process start time.
59-
if (time < performance.timeOrigin) {
71+
if (time < getTimeOrigin()) {
6072
return hrTime(time);
61-
}
62-
// epoch milliseconds or performance.timeOrigin
63-
else {
73+
} else {
74+
// epoch milliseconds or performance.timeOrigin
6475
return numberToHrtime(time);
6576
}
6677
} else if (time instanceof Date) {
@@ -102,3 +113,21 @@ export function hrTimeToMilliseconds(hrTime: types.HrTime): number {
102113
export function hrTimeToMicroseconds(hrTime: types.HrTime): number {
103114
return Math.round(hrTime[0] * 1e6 + hrTime[1] / 1e3);
104115
}
116+
117+
export function isTimeInputHrTime(value: unknown) {
118+
return (
119+
Array.isArray(value) &&
120+
value.length === 2 &&
121+
typeof value[0] === 'number' &&
122+
typeof value[1] === 'number'
123+
);
124+
}
125+
126+
// check if input value is a correct types.TimeInput
127+
export function isTimeInput(value: unknown) {
128+
return (
129+
isTimeInputHrTime(value) ||
130+
typeof value === 'number' ||
131+
value instanceof Date
132+
);
133+
}

packages/opentelemetry-core/src/common/types.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,3 +21,14 @@ export enum LogLevel {
2121
INFO,
2222
DEBUG,
2323
}
24+
25+
/**
26+
* This interface defines a fallback to read a timeOrigin when it is not available on performance.timeOrigin,
27+
* this happens for example on Safari Mac
28+
* then the timeOrigin is taken from fetchStart - which is the closest to timeOrigin
29+
*/
30+
export interface TimeOriginLegacy {
31+
timing: {
32+
fetchStart: number;
33+
};
34+
}

packages/opentelemetry-core/test/common/time.test.ts

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ import {
2525
hrTimeToNanoseconds,
2626
hrTimeToMilliseconds,
2727
hrTimeToMicroseconds,
28+
isTimeInput,
2829
} from '../../src/common/time';
2930

3031
describe('time', () => {
@@ -62,6 +63,47 @@ describe('time', () => {
6263
const output = hrTime(performanceNow);
6364
assert.deepStrictEqual(output, [0, 23100000]);
6465
});
66+
67+
it('should allow passed "performanceNow" equal to 0', () => {
68+
sandbox.stub(performance, 'timeOrigin').value(11.5);
69+
sandbox.stub(performance, 'now').callsFake(() => 11.3);
70+
71+
const output = hrTime(0);
72+
assert.deepStrictEqual(output, [0, 11500000]);
73+
});
74+
75+
it('should use performance.now() when "performanceNow" is equal to undefined', () => {
76+
sandbox.stub(performance, 'timeOrigin').value(11.5);
77+
sandbox.stub(performance, 'now').callsFake(() => 11.3);
78+
79+
const output = hrTime(undefined);
80+
assert.deepStrictEqual(output, [0, 22800000]);
81+
});
82+
83+
it('should use performance.now() when "performanceNow" is equal to null', () => {
84+
sandbox.stub(performance, 'timeOrigin').value(11.5);
85+
sandbox.stub(performance, 'now').callsFake(() => 11.3);
86+
87+
const output = hrTime(null as any);
88+
assert.deepStrictEqual(output, [0, 22800000]);
89+
});
90+
91+
describe('when timeOrigin is not available', () => {
92+
it('should use the performance.timing.fetchStart as a fallback', () => {
93+
Object.defineProperty(performance, 'timing', {
94+
writable: true,
95+
value: {
96+
fetchStart: 11.5,
97+
},
98+
});
99+
100+
sandbox.stub(performance, 'timeOrigin').value(undefined);
101+
sandbox.stub(performance, 'now').callsFake(() => 11.3);
102+
103+
const output = hrTime();
104+
assert.deepStrictEqual(output, [0, 22800000]);
105+
});
106+
});
65107
});
66108

67109
describe('#timeInputToHrTime', () => {
@@ -134,4 +176,32 @@ describe('time', () => {
134176
assert.deepStrictEqual(output, 1200000);
135177
});
136178
});
179+
describe('#isTimeInput', () => {
180+
it('should return true for a number', () => {
181+
assert.strictEqual(isTimeInput(12), true);
182+
});
183+
it('should return true for a date', () => {
184+
assert.strictEqual(isTimeInput(new Date()), true);
185+
});
186+
it('should return true for an array with 2 elements type number', () => {
187+
assert.strictEqual(isTimeInput([1, 1]), true);
188+
});
189+
it('should return FALSE for different cases for an array ', () => {
190+
assert.strictEqual(isTimeInput([1, 1, 1]), false);
191+
assert.strictEqual(isTimeInput([1]), false);
192+
assert.strictEqual(isTimeInput([1, 'a']), false);
193+
});
194+
it('should return FALSE for a string', () => {
195+
assert.strictEqual(isTimeInput('a'), false);
196+
});
197+
it('should return FALSE for an object', () => {
198+
assert.strictEqual(isTimeInput({}), false);
199+
});
200+
it('should return FALSE for a null', () => {
201+
assert.strictEqual(isTimeInput(null), false);
202+
});
203+
it('should return FALSE for undefined', () => {
204+
assert.strictEqual(isTimeInput(undefined), false);
205+
});
206+
});
137207
});

0 commit comments

Comments
 (0)