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

Commit 842fc5c

Browse files
Add sample rate option and update README for it (#58)
1 parent d05169d commit 842fc5c

File tree

5 files changed

+85
-28
lines changed

5 files changed

+85
-28
lines changed

README.md

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -55,22 +55,26 @@ it from the `/static` folder of your site.
5555
Once the OpenCensus Web packages are released to NPM, you will also be able to
5656
include them in your JavaScript build pipeline as an imported dependency.
5757

58-
In order to indicate the endpoint of the OpenCensus Agent so that traces can be
59-
written, you will need to include a snippet of JavaScript that sets the
60-
`ocAgent` global variable, for instance:
58+
In order to indicate the trace sampling rate and endpoint of the OpenCensus
59+
Agent so that traces can be written, you will need to include a snippet of
60+
JavaScript that sets the `ocAgent` and `ocSampleRate` global variables,
61+
for instance:
6162

6263
```html
63-
<script>ocAgent = 'https://example.com/my-opencensus-agent-endpoint'</script>
64+
<script>
65+
ocAgent = 'https://example.com/my-opencensus-agent-endpoint';
66+
// Sample all requests for trace, which is useful when testing.
67+
// By default this is set to sample 1/10000 requests.
68+
ocSampleRate = 1.0;
69+
</script>
6470
```
6571

6672
### 3. Optional: Send a trace parent and sampling decision from your server
6773

68-
Currently, the OpenCensus Web will sample all requests by default. This is
69-
useful for experimentation with the library but is not appropriate for a real
70-
application.
71-
7274
OpenCensus Web also supports connecting the server side spans for the initial
7375
HTML load with the client side span for the load from the browser's timing API.
76+
This works by having the server send its parent trace context (trace ID, span ID
77+
and trace sampling decision) to the client.
7478

7579
Because the browser does not send a trace context header for the initial page
7680
navigation, the server needs to fake a trace context header in a middleware and

packages/opencensus-web-all/src/initial-load-context.ts

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -21,27 +21,37 @@ import {WindowWithOcwGlobals} from './types';
2121

2222
const windowWithOcwGlobals = window as WindowWithOcwGlobals;
2323

24+
/**
25+
* The default trace sampling rate if no `traceparent` and no `ocSampleRate`
26+
* are specified on the `window`.
27+
*/
28+
const DEFAULT_SAMPLE_RATE = 0.0001;
29+
2430
/**
2531
* Gets a span context for the initial page load from the `window.traceparent`,
2632
* or generates a new random span context if it is missing. For now the new
2733
* random span context generated if `window.traceparent` is missing is always
2834
* marked sampled.
2935
*/
3036
export function getInitialLoadSpanContext(): SpanContext {
31-
if (!windowWithOcwGlobals.traceparent) return alwaysSampledSpanContext();
37+
if (!windowWithOcwGlobals.traceparent) return randomSampledSpanContext();
3238
const spanContext =
3339
traceParentToSpanContext(windowWithOcwGlobals.traceparent);
3440
if (!spanContext) {
3541
console.log(`Invalid traceparent: ${windowWithOcwGlobals.traceparent}`);
36-
return alwaysSampledSpanContext();
42+
return randomSampledSpanContext();
3743
}
3844
return spanContext;
3945
}
4046

41-
function alwaysSampledSpanContext() {
47+
function randomSampledSpanContext() {
48+
const sampleRate = windowWithOcwGlobals.ocSampleRate || DEFAULT_SAMPLE_RATE;
4249
return {
4350
traceId: randomTraceId(),
4451
spanId: randomSpanId(),
45-
options: 0x1, // Sampled
52+
// Math.random returns a number in the 0-1 range (inclusive of 0 but not 1).
53+
// That means we should use the strict `<` operator to compare it to the
54+
// sample rate. A value of 1 for `options` indicates trace sampling.
55+
options: Math.random() < sampleRate ? 1 : 0,
4656
};
4757
}

packages/opencensus-web-all/src/types.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,12 @@ export declare interface WindowWithOcwGlobals extends WindowWithLongTasks {
4040
* See https://www.w3.org/TR/trace-context/ for details.
4141
*/
4242
traceparent?: string;
43+
/**
44+
* If the `traceparent` global variable described above is not present on the
45+
* `window`, then a trace sampling decision will be made randomly with the
46+
* specified sample rate. If not specified, a default sampling rate is used.
47+
*/
48+
ocSampleRate?: number;
4349
/**
4450
* List to collect long task timings as they are observed. This is on the
4551
* window so that the code to instrument the long tasks and the code that

packages/opencensus-web-all/test/test-export-initial-load.ts

Lines changed: 29 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -38,35 +38,23 @@ function hexToBase64(hexString: string): string {
3838
}
3939

4040
describe('exportRootSpanAfterLoadEvent', () => {
41-
let realOcwAgent: string|undefined;
41+
let realOcAgent: string|undefined;
4242
let realTraceparent: string|undefined;
4343
let sendSpy: jasmine.Spy;
4444
beforeEach(() => {
4545
jasmine.clock().install();
4646
spyOn(XMLHttpRequest.prototype, 'open');
4747
sendSpy = spyOn(XMLHttpRequest.prototype, 'send');
4848
spyOn(XMLHttpRequest.prototype, 'setRequestHeader');
49-
realOcwAgent = windowWithOcwGlobals.ocAgent;
49+
realOcAgent = windowWithOcwGlobals.ocAgent;
5050
realTraceparent = windowWithOcwGlobals.traceparent;
5151
});
5252
afterEach(() => {
5353
jasmine.clock().uninstall();
54-
windowWithOcwGlobals.ocAgent = realOcwAgent;
54+
windowWithOcwGlobals.ocAgent = realOcAgent;
5555
windowWithOcwGlobals.traceparent = realTraceparent;
5656
});
5757

58-
it('exports spans to agent if agent is configured', () => {
59-
windowWithOcwGlobals.ocAgent = 'http://agent';
60-
windowWithOcwGlobals.traceparent = undefined;
61-
62-
exportRootSpanAfterLoadEvent();
63-
64-
jasmine.clock().tick(300000);
65-
expect(XMLHttpRequest.prototype.open)
66-
.toHaveBeenCalledWith('POST', 'http://agent/v1/trace');
67-
expect(XMLHttpRequest.prototype.send).toHaveBeenCalled();
68-
});
69-
7058
it('does not export if agent not configured', () => {
7159
windowWithOcwGlobals.ocAgent = undefined;
7260
windowWithOcwGlobals.traceparent = undefined;
@@ -96,7 +84,7 @@ describe('exportRootSpanAfterLoadEvent', () => {
9684
expect(sendBody).toContain(hexToBase64(traceId));
9785
});
9886

99-
it('does not export spans if traceparent sampling hint not set', () => {
87+
it('does not export spans if traceparent sampling hint set to zero', () => {
10088
windowWithOcwGlobals.ocAgent = 'http://agent';
10189
windowWithOcwGlobals.traceparent =
10290
'00-0af7651916cd43dd8448eb211c80319c-b7ad6b7169203331-00';
@@ -107,4 +95,29 @@ describe('exportRootSpanAfterLoadEvent', () => {
10795
expect(XMLHttpRequest.prototype.open).not.toHaveBeenCalled();
10896
expect(XMLHttpRequest.prototype.send).not.toHaveBeenCalled();
10997
});
98+
99+
it('does not export spans if traceparent unset and random is big', () => {
100+
spyOn(Math, 'random').and.returnValue(0.99);
101+
windowWithOcwGlobals.ocAgent = 'http://agent';
102+
windowWithOcwGlobals.traceparent = '';
103+
104+
exportRootSpanAfterLoadEvent();
105+
106+
jasmine.clock().tick(300000);
107+
expect(XMLHttpRequest.prototype.open).not.toHaveBeenCalled();
108+
expect(XMLHttpRequest.prototype.send).not.toHaveBeenCalled();
109+
});
110+
111+
it('exports spans if traceparent unset and random number is small', () => {
112+
spyOn(Math, 'random').and.returnValue(0);
113+
windowWithOcwGlobals.ocAgent = 'http://agent';
114+
windowWithOcwGlobals.traceparent = undefined;
115+
116+
exportRootSpanAfterLoadEvent();
117+
118+
jasmine.clock().tick(300000);
119+
expect(XMLHttpRequest.prototype.open)
120+
.toHaveBeenCalledWith('POST', 'http://agent/v1/trace');
121+
expect(XMLHttpRequest.prototype.send).toHaveBeenCalled();
122+
});
110123
});

packages/opencensus-web-all/test/test-initial-load-context.ts

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,11 +24,14 @@ const TRACE_ID_REGEX = /[0-9a-f]{32}/;
2424

2525
describe('getInitialLoadSpanContext', () => {
2626
let realTraceparent: string|undefined;
27+
let realOcSampleRate: number|undefined;
2728
beforeEach(() => {
2829
realTraceparent = windowWithOcwGlobals.traceparent;
30+
realOcSampleRate = windowWithOcwGlobals.ocSampleRate;
2931
});
3032
afterEach(() => {
3133
windowWithOcwGlobals.traceparent = realTraceparent;
34+
windowWithOcwGlobals.ocSampleRate = realOcSampleRate;
3235
});
3336

3437
it('sets trace and span ID from global `traceparent` when specified', () => {
@@ -43,17 +46,38 @@ describe('getInitialLoadSpanContext', () => {
4346

4447
it('generates a new random span context if `traceparent` unspecified', () => {
4548
windowWithOcwGlobals.traceparent = undefined;
49+
spyOn(Math, 'random').and.returnValue(0);
4650
const spanContext = getInitialLoadSpanContext();
4751
expect(spanContext.traceId).toMatch(TRACE_ID_REGEX);
4852
expect(spanContext.spanId).toMatch(SPAN_ID_REGEX);
4953
expect(spanContext.options).toBe(1);
54+
expect(Math.random).toHaveBeenCalled();
5055
});
5156

5257
it('generates a new random span context if `traceparent` is invalid', () => {
58+
spyOn(Math, 'random').and.returnValue(0);
5359
windowWithOcwGlobals.traceparent = 'invalid trace parent header!';
5460
const spanContext = getInitialLoadSpanContext();
5561
expect(spanContext.traceId).toMatch(TRACE_ID_REGEX);
5662
expect(spanContext.spanId).toMatch(SPAN_ID_REGEX);
5763
expect(spanContext.options).toBe(1);
64+
expect(Math.random).toHaveBeenCalled();
65+
});
66+
67+
describe('specifying the sampling rate with window.ocSampleRate', () => {
68+
beforeEach(() => {
69+
spyOn(Math, 'random').and.returnValue(0.5);
70+
windowWithOcwGlobals.traceparent = undefined;
71+
});
72+
it('sets trace options to unsampled if random above sample rate', () => {
73+
windowWithOcwGlobals.ocSampleRate = 0.1;
74+
const spanContext = getInitialLoadSpanContext();
75+
expect(spanContext.options).toBe(0);
76+
});
77+
it('sets trace options to sampled if random below sample rate', () => {
78+
windowWithOcwGlobals.ocSampleRate = 1.0;
79+
const spanContext = getInitialLoadSpanContext();
80+
expect(spanContext.options).toBe(1);
81+
});
5882
});
5983
});

0 commit comments

Comments
 (0)