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

Commit 36ec183

Browse files
authored
Support sampling per span (#394)
1 parent 462eea5 commit 36ec183

13 files changed

+217
-133
lines changed

CHANGELOG.md

+1
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ All notable changes to this project will be documented in this file.
1818
- Add NoRecordRootSpan, NoRecordSpan and NoRecordSpanBase.
1919
- Enforce `--strictNullChecks` and `--noUnusedLocals` Compiler Options on [opencensus-exporter-instana] package.
2020
- Add an API `globalStats.unregisterExporter()`.
21+
- Add support for overriding sampling for a span.
2122

2223
## 0.0.9 - 2019-02-12
2324
- Add Metrics API.

packages/opencensus-core/src/trace/model/no-record/no-record-root-span.ts

+15-13
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@
1414
* limitations under the License.
1515
*/
1616

17-
import * as uuid from 'uuid';
1817
import * as logger from '../../../common/console-logger';
1918
import * as types from '../types';
2019
import {NoRecordSpan} from './no-record-span';
@@ -35,22 +34,25 @@ export class NoRecordRootSpan extends NoRecordSpanBase implements
3534
/**
3635
* Constructs a new NoRecordRootSpanImpl instance.
3736
* @param tracer A tracer object.
38-
* @param context A trace options object to build the no-record root span.
37+
* @param name The displayed name for the new span.
38+
* @param kind The kind of new span.
39+
* @param traceId The trace Id.
40+
* @param parentSpanId The id of the parent span, or empty if the new span is
41+
* a root span.
42+
* @param traceState Optional traceState.
3943
*/
40-
constructor(tracer: types.Tracer, context?: types.TraceOptions) {
44+
constructor(
45+
tracer: types.Tracer, name: string, kind: types.SpanKind, traceId: string,
46+
parentSpanId: string, traceState?: types.TraceState) {
4147
super();
4248
this.tracer = tracer;
43-
this.traceIdLocal =
44-
context && context.spanContext && context.spanContext.traceId ?
45-
context.spanContext.traceId :
46-
(uuid.v4().split('-').join(''));
47-
this.name = context && context.name ? context.name : 'undefined';
48-
if (context && context.spanContext) {
49-
this.parentSpanId = context.spanContext.spanId || '';
50-
this.traceStateLocal = context.spanContext.traceState;
49+
this.traceIdLocal = traceId;
50+
this.name = name;
51+
this.kind = kind;
52+
this.parentSpanId = parentSpanId;
53+
if (traceState) {
54+
this.traceStateLocal = traceState;
5155
}
52-
this.kind =
53-
context && context.kind ? context.kind : types.SpanKind.UNSPECIFIED;
5456
this.logger = this.tracer.logger || logger.logger();
5557
}
5658

packages/opencensus-core/src/trace/model/root-span.ts

+15-13
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@
1414
* limitations under the License.
1515
*/
1616

17-
import * as uuid from 'uuid';
1817
import * as logger from '../../common/console-logger';
1918
import {Span} from './span';
2019
import {SpanBase} from './span-base';
@@ -39,23 +38,26 @@ export class RootSpan extends SpanBase implements types.RootSpan {
3938
/**
4039
* Constructs a new RootSpanImpl instance.
4140
* @param tracer A tracer object.
42-
* @param context A trace options object to build the root span.
41+
* @param name The displayed name for the new span.
42+
* @param kind The kind of new span.
43+
* @param traceId The trace Id.
44+
* @param parentSpanId The id of the parent span, or empty if the new span is
45+
* a root span.
46+
* @param traceState Optional traceState.
4347
*/
44-
constructor(tracer: types.Tracer, context?: types.TraceOptions) {
48+
constructor(
49+
tracer: types.Tracer, name: string, kind: types.SpanKind, traceId: string,
50+
parentSpanId: string, traceState?: types.TraceState) {
4551
super();
4652
this.tracer = tracer;
47-
this.traceIdLocal =
48-
context && context.spanContext && context.spanContext.traceId ?
49-
context.spanContext.traceId :
50-
(uuid.v4().split('-').join(''));
51-
this.name = context && context.name ? context.name : 'undefined';
52-
if (context && context.spanContext) {
53-
this.parentSpanId = context.spanContext.spanId || '';
54-
this.traceStateLocal = context.spanContext.traceState;
53+
this.traceIdLocal = traceId;
54+
this.name = name;
55+
this.kind = kind;
56+
this.parentSpanId = parentSpanId;
57+
if (traceState) {
58+
this.traceStateLocal = traceState;
5559
}
5660
this.spansLocal = [];
57-
this.kind =
58-
context && context.kind ? context.kind : types.SpanKind.UNSPECIFIED;
5961
this.logger = tracer.logger || logger.logger();
6062
this.activeTraceParams = tracer.activeTraceParams;
6163
this.numberOfChildrenLocal = 0;

packages/opencensus-core/src/trace/model/tracer.ts

+51-24
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
* limitations under the License.
1515
*/
1616

17+
import * as uuid from 'uuid';
1718
import * as logger from '../../common/console-logger';
1819
import * as loggerTypes from '../../common/types';
1920
import * as cls from '../../internal/cls';
@@ -22,12 +23,10 @@ import {TraceParams} from '../config/types';
2223
import {Propagation} from '../propagation/types';
2324
import {SamplerBuilder, TraceParamsBuilder} from '../sampler/sampler';
2425
import * as samplerTypes from '../sampler/types';
25-
2626
import {NoRecordRootSpan} from './no-record/no-record-root-span';
2727
import {RootSpan} from './root-span';
2828
import * as types from './types';
2929

30-
3130
/**
3231
* This class represent a tracer.
3332
*/
@@ -125,35 +124,39 @@ export class CoreTracer implements types.Tracer {
125124
startRootSpan<T>(
126125
options: types.TraceOptions, fn: (root: types.RootSpan) => T): T {
127126
return this.contextManager.runAndReturn((root) => {
128-
if (this.active) {
129-
let propagatedSample = null;
130-
131-
// if there is a context propagation, keep the decision
132-
if (options && options.spanContext) {
133-
if (options.spanContext.options) {
134-
propagatedSample =
135-
((options.spanContext.options & this.IS_SAMPLED) !== 0);
136-
}
137-
if (!propagatedSample) {
138-
options.spanContext = null;
139-
}
140-
}
141-
const aRoot = new RootSpan(this, options);
127+
let traceId;
128+
if (options && options.spanContext && options.spanContext.traceId) {
129+
traceId = options.spanContext.traceId;
130+
} else {
131+
// New root span.
132+
traceId = uuid.v4().split('-').join('');
133+
}
134+
const name = options && options.name ? options.name : 'span';
135+
const kind =
136+
options && options.kind ? options.kind : types.SpanKind.UNSPECIFIED;
142137

143-
let sampleDecision: boolean = propagatedSample;
144-
if (!sampleDecision) {
145-
sampleDecision = this.sampler.shouldSample(aRoot.traceId);
146-
}
138+
let parentSpanId = '';
139+
let traceState;
140+
if (options && options.spanContext) {
141+
// New child span.
142+
parentSpanId = options.spanContext.spanId || '';
143+
traceState = options.spanContext.traceState;
144+
}
147145

146+
if (this.active) {
147+
const sampleDecision = this.makeSamplingDecision(options, traceId);
148148
if (sampleDecision) {
149-
this.currentRootSpan = aRoot;
150-
aRoot.start();
151-
return fn(aRoot);
149+
const rootSpan =
150+
new RootSpan(this, name, kind, traceId, parentSpanId, traceState);
151+
this.currentRootSpan = rootSpan;
152+
rootSpan.start();
153+
return fn(rootSpan);
152154
}
153155
} else {
154156
this.logger.debug('Tracer is inactive, can\'t start new RootSpan');
155157
}
156-
const noRecordRootSpan = new NoRecordRootSpan(this, options);
158+
const noRecordRootSpan = new NoRecordRootSpan(
159+
this, name, kind, traceId, parentSpanId, traceState);
157160
this.currentRootSpan = noRecordRootSpan;
158161
return fn(noRecordRootSpan);
159162
});
@@ -283,4 +286,28 @@ export class CoreTracer implements types.Tracer {
283286
const namespace = this.contextManager;
284287
namespace.bindEmitter(emitter);
285288
}
289+
290+
/** Determine whether to sample request or not. */
291+
private makeSamplingDecision(options: types.TraceOptions, traceId: string):
292+
boolean {
293+
// If users set a specific sampler in the TraceOptions, use it.
294+
if (options && options.samplingRate !== undefined &&
295+
options.samplingRate !== null) {
296+
return SamplerBuilder.getSampler(options.samplingRate)
297+
.shouldSample(traceId);
298+
}
299+
let propagatedSample = null;
300+
// if there is a context propagation, keep the decision
301+
if (options && options.spanContext && options.spanContext.options) {
302+
propagatedSample =
303+
((options.spanContext.options & this.IS_SAMPLED) !== 0);
304+
}
305+
306+
let sampleDecision: boolean = propagatedSample;
307+
if (!sampleDecision) {
308+
// Use the default global sampler
309+
sampleDecision = this.sampler.shouldSample(traceId);
310+
}
311+
return sampleDecision;
312+
}
286313
}

packages/opencensus-core/src/trace/model/types.ts

+2
Original file line numberDiff line numberDiff line change
@@ -232,6 +232,8 @@ export interface TraceOptions {
232232
spanContext?: SpanContext;
233233
/** Span kind */
234234
kind?: SpanKind;
235+
/** Determines the sampling rate. Ranges from 0.0 to 1.0 */
236+
samplingRate?: number;
235237
}
236238

237239
/** Defines the span options */

packages/opencensus-core/test/test-console-exporter.ts

+9-5
Original file line numberDiff line numberDiff line change
@@ -25,13 +25,17 @@ const defaultBufferConfig = {
2525
bufferSize: 1,
2626
bufferTimeout: 20000 // time in milliseconds
2727
};
28+
const name = 'MySpanName';
29+
const kind = SpanKind.SERVER;
30+
const traceId = 'd4cda95b652f4a1592b449d5929fda1b';
31+
const parentSpanId = '';
2832

2933
describe('NoopExporter', () => {
3034
/** Should do nothing when calling onEndSpan() */
3135
describe('onEndSpan()', () => {
3236
it('should do nothing', () => {
3337
const exporter = new NoopExporter();
34-
const rootSpan = new RootSpan(tracer);
38+
const rootSpan = new RootSpan(tracer, name, kind, traceId, parentSpanId);
3539
exporter.onEndSpan(rootSpan);
3640
assert.ok(true);
3741
});
@@ -41,7 +45,7 @@ describe('NoopExporter', () => {
4145
describe('publish()', () => {
4246
it('should do nothing', () => {
4347
const exporter = new NoopExporter();
44-
const rootSpan = new RootSpan(tracer);
48+
const rootSpan = new RootSpan(tracer, name, kind, traceId, parentSpanId);
4549
const queue: RootSpan[] = [rootSpan];
4650

4751
return exporter.publish(queue);
@@ -61,11 +65,11 @@ describe('ConsoleLogExporter', () => {
6165

6266
const exporter = new ConsoleExporter(defaultBufferConfig);
6367

64-
const rootSpan1 = new RootSpan(tracer);
68+
const rootSpan1 = new RootSpan(tracer, name, kind, traceId, parentSpanId);
6569
exporter.onEndSpan(rootSpan1);
6670
assert.strictEqual(capturedText, '');
6771

68-
const rootSpan2 = new RootSpan(tracer);
72+
const rootSpan2 = new RootSpan(tracer, name, kind, traceId, parentSpanId);
6973
exporter.onEndSpan(rootSpan2);
7074
[rootSpan1, rootSpan2].map(rootSpan => {
7175
assert.ok(capturedText.indexOf(rootSpan.traceId) >= 0);
@@ -85,7 +89,7 @@ describe('ConsoleLogExporter', () => {
8589
});
8690

8791
const exporter = new ConsoleExporter(defaultBufferConfig);
88-
const rootSpan = new RootSpan(tracer);
92+
const rootSpan = new RootSpan(tracer, name, kind, traceId, parentSpanId);
8993
rootSpan.start();
9094
rootSpan.startChildSpan('name', SpanKind.UNSPECIFIED, rootSpan.traceId);
9195
const queue: RootSpan[] = [rootSpan];

packages/opencensus-core/test/test-exporter-buffer.ts

+12-5
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,6 @@ import {ExporterBuffer} from '../src/exporters/exporter-buffer';
2222
import {RootSpan} from '../src/trace/model/root-span';
2323
import {CoreTracer} from '../src/trace/model/tracer';
2424

25-
2625
const exporter = new NoopExporter();
2726
const DEFAULT_BUFFER_SIZE = 3;
2827
const DEFAULT_BUFFER_TIMEOUT = 2000; // time in milliseconds
@@ -34,11 +33,16 @@ const defaultBufferConfig = {
3433
logger: logger.logger()
3534
};
3635

36+
const name = 'MySpanName';
37+
const kind = SpanKind.SERVER;
38+
const traceId = 'd4cda95b652f4a1592b449d5929fda1b';
39+
const parentSpanId = '';
3740

3841
const createRootSpans = (num: number): RootSpan[] => {
3942
const rootSpans = [];
4043
for (let i = 0; i < num; i++) {
41-
const rootSpan = new RootSpan(tracer, {name: `rootSpan.${i}`});
44+
const rootSpan =
45+
new RootSpan(tracer, `rootSpan.${i}`, kind, traceId, parentSpanId);
4246
rootSpan.start();
4347
for (let j = 0; j < 10; j++) {
4448
rootSpan.startChildSpan(`childSpan.${i}.${j}`, SpanKind.CLIENT);
@@ -79,7 +83,8 @@ describe('ExporterBuffer', () => {
7983
describe('addToBuffer', () => {
8084
it('should add one item to the Buffer', () => {
8185
const buffer = new ExporterBuffer(exporter, defaultBufferConfig);
82-
buffer.addToBuffer(new RootSpan(tracer));
86+
buffer.addToBuffer(
87+
new RootSpan(tracer, name, kind, traceId, parentSpanId));
8388
assert.strictEqual(buffer.getQueue().length, 1);
8489
});
8590
});
@@ -95,7 +100,8 @@ describe('ExporterBuffer', () => {
95100
buffer.addToBuffer(rootSpan);
96101
}
97102
assert.strictEqual(buffer.getQueue().length, buffer.getBufferSize());
98-
buffer.addToBuffer(new RootSpan(tracer));
103+
buffer.addToBuffer(
104+
new RootSpan(tracer, name, kind, traceId, parentSpanId));
99105
assert.strictEqual(buffer.getQueue().length, 0);
100106
});
101107
});
@@ -106,7 +112,8 @@ describe('ExporterBuffer', () => {
106112
describe('addToBuffer force flush by timeout ', () => {
107113
it('should flush by timeout', (done) => {
108114
const buffer = new ExporterBuffer(exporter, defaultBufferConfig);
109-
buffer.addToBuffer(new RootSpan(tracer));
115+
buffer.addToBuffer(
116+
new RootSpan(tracer, name, kind, traceId, parentSpanId));
110117
assert.strictEqual(buffer.getQueue().length, 1);
111118
setTimeout(() => {
112119
assert.strictEqual(buffer.getQueue().length, 0);

packages/opencensus-core/test/test-no-record-root-span.ts

+3-2
Original file line numberDiff line numberDiff line change
@@ -14,14 +14,15 @@
1414
* limitations under the License.
1515
*/
1616

17-
import {CanonicalCode, CoreTracer, LinkType, MessageEventType} from '../src';
17+
import {CanonicalCode, CoreTracer, LinkType, MessageEventType, SpanKind} from '../src';
1818
import {NoRecordRootSpan} from '../src/trace/model/no-record/no-record-root-span';
1919

2020
const tracer = new CoreTracer();
2121

2222
describe('NoRecordRootSpan()', () => {
2323
it('do not crash', () => {
24-
const noRecordRootSpan = new NoRecordRootSpan(tracer);
24+
const noRecordRootSpan =
25+
new NoRecordRootSpan(tracer, 'name', SpanKind.SERVER, 'traceid', '');
2526
noRecordRootSpan.addAnnotation('MyAnnotation');
2627
noRecordRootSpan.addAnnotation('MyAnnotation', {myString: 'bar'});
2728
noRecordRootSpan.addAnnotation(

packages/opencensus-core/test/test-no-record-span.ts

+4-3
Original file line numberDiff line numberDiff line change
@@ -14,16 +14,17 @@
1414
* limitations under the License.
1515
*/
1616

17-
import {CanonicalCode, CoreTracer, LinkType, MessageEventType} from '../src';
17+
import {CanonicalCode, CoreTracer, LinkType, MessageEventType, SpanKind} from '../src';
1818
import {NoRecordRootSpan} from '../src/trace/model/no-record/no-record-root-span';
1919
import {NoRecordSpan} from '../src/trace/model/no-record/no-record-span';
2020

2121
const tracer = new CoreTracer();
2222

2323
describe('NoRecordSpan()', () => {
2424
it('do not crash', () => {
25-
const root = new NoRecordRootSpan(tracer);
26-
const noRecordSpan = new NoRecordSpan(root);
25+
const noRecordRootSpan =
26+
new NoRecordRootSpan(tracer, 'name', SpanKind.SERVER, 'traceid', '');
27+
const noRecordSpan = new NoRecordSpan(noRecordRootSpan);
2728
noRecordSpan.addAnnotation('MyAnnotation');
2829
noRecordSpan.addAnnotation('MyAnnotation', {myString: 'bar'});
2930
noRecordSpan.addAnnotation(

0 commit comments

Comments
 (0)