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

Add support for overriding sampling per span #394

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ All notable changes to this project will be documented in this file.
- Add NoRecordRootSpan, NoRecordSpan and NoRecordSpanBase.
- Enforce `--strictNullChecks` and `--noUnusedLocals` Compiler Options on [opencensus-exporter-instana] package.
- Add an API `globalStats.unregisterExporter()`.
- Add support for overriding sampling for a span.

## 0.0.9 - 2019-02-12
- Add Metrics API.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@
* limitations under the License.
*/

import * as uuid from 'uuid';
import * as logger from '../../../common/console-logger';
import * as types from '../types';
import {NoRecordSpan} from './no-record-span';
Expand All @@ -35,22 +34,25 @@ export class NoRecordRootSpan extends NoRecordSpanBase implements
/**
* Constructs a new NoRecordRootSpanImpl instance.
* @param tracer A tracer object.
* @param context A trace options object to build the no-record root span.
* @param name The displayed name for the new span.
* @param kind The kind of new span.
* @param traceId The trace Id.
* @param parentSpanId The id of the parent span, or empty if the new span is
* a root span.
* @param traceState Optional traceState.
*/
constructor(tracer: types.Tracer, context?: types.TraceOptions) {
constructor(
tracer: types.Tracer, name: string, kind: types.SpanKind, traceId: string,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should these new parameters be optional to make this less of a breaking change? If we keep them, could you modify the change list to include a mention of it?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Possible. On the other hand, this is internal API and won't affect users in any way.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK, I see, this does not change the RootSpan interface type, just the implementations. In OpenCensus Web, I create spans directly from performance timings, but it seems like the instrumented Node libraries use the helper utilities in Tracer to create, start and end spans. So makes sense this is not really part of the exported API.

parentSpanId: string, traceState?: types.TraceState) {
super();
this.tracer = tracer;
this.traceIdLocal =
context && context.spanContext && context.spanContext.traceId ?
context.spanContext.traceId :
(uuid.v4().split('-').join(''));
this.name = context && context.name ? context.name : 'undefined';
if (context && context.spanContext) {
this.parentSpanId = context.spanContext.spanId || '';
this.traceStateLocal = context.spanContext.traceState;
this.traceIdLocal = traceId;
this.name = name;
this.kind = kind;
this.parentSpanId = parentSpanId;
if (traceState) {
this.traceStateLocal = traceState;
}
this.kind =
context && context.kind ? context.kind : types.SpanKind.UNSPECIFIED;
this.logger = this.tracer.logger || logger.logger();
}

Expand Down
28 changes: 15 additions & 13 deletions packages/opencensus-core/src/trace/model/root-span.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@
* limitations under the License.
*/

import * as uuid from 'uuid';
import * as logger from '../../common/console-logger';
import {Span} from './span';
import {SpanBase} from './span-base';
Expand All @@ -39,23 +38,26 @@ export class RootSpan extends SpanBase implements types.RootSpan {
/**
* Constructs a new RootSpanImpl instance.
* @param tracer A tracer object.
* @param context A trace options object to build the root span.
* @param name The displayed name for the new span.
* @param kind The kind of new span.
* @param traceId The trace Id.
* @param parentSpanId The id of the parent span, or empty if the new span is
* a root span.
* @param traceState Optional traceState.
*/
constructor(tracer: types.Tracer, context?: types.TraceOptions) {
constructor(
tracer: types.Tracer, name: string, kind: types.SpanKind, traceId: string,
parentSpanId: string, traceState?: types.TraceState) {
super();
this.tracer = tracer;
this.traceIdLocal =
context && context.spanContext && context.spanContext.traceId ?
context.spanContext.traceId :
(uuid.v4().split('-').join(''));
this.name = context && context.name ? context.name : 'undefined';
if (context && context.spanContext) {
this.parentSpanId = context.spanContext.spanId || '';
this.traceStateLocal = context.spanContext.traceState;
this.traceIdLocal = traceId;
this.name = name;
this.kind = kind;
this.parentSpanId = parentSpanId;
if (traceState) {
this.traceStateLocal = traceState;
}
this.spansLocal = [];
this.kind =
context && context.kind ? context.kind : types.SpanKind.UNSPECIFIED;
this.logger = tracer.logger || logger.logger();
this.activeTraceParams = tracer.activeTraceParams;
this.numberOfChildrenLocal = 0;
Expand Down
75 changes: 51 additions & 24 deletions packages/opencensus-core/src/trace/model/tracer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
* limitations under the License.
*/

import * as uuid from 'uuid';
import * as logger from '../../common/console-logger';
import * as loggerTypes from '../../common/types';
import * as cls from '../../internal/cls';
Expand All @@ -22,12 +23,10 @@ import {TraceParams} from '../config/types';
import {Propagation} from '../propagation/types';
import {SamplerBuilder, TraceParamsBuilder} from '../sampler/sampler';
import * as samplerTypes from '../sampler/types';

import {NoRecordRootSpan} from './no-record/no-record-root-span';
import {RootSpan} from './root-span';
import * as types from './types';


/**
* This class represent a tracer.
*/
Expand Down Expand Up @@ -125,35 +124,39 @@ export class CoreTracer implements types.Tracer {
startRootSpan<T>(
options: types.TraceOptions, fn: (root: types.RootSpan) => T): T {
return this.contextManager.runAndReturn((root) => {
if (this.active) {
let propagatedSample = null;

// if there is a context propagation, keep the decision
if (options && options.spanContext) {
if (options.spanContext.options) {
propagatedSample =
((options.spanContext.options & this.IS_SAMPLED) !== 0);
}
if (!propagatedSample) {
options.spanContext = null;
}
}
const aRoot = new RootSpan(this, options);
let traceId;
if (options && options.spanContext && options.spanContext.traceId) {
traceId = options.spanContext.traceId;
} else {
// New root span.
traceId = uuid.v4().split('-').join('');
}
const name = options && options.name ? options.name : 'span';
const kind =
options && options.kind ? options.kind : types.SpanKind.UNSPECIFIED;

let sampleDecision: boolean = propagatedSample;
if (!sampleDecision) {
sampleDecision = this.sampler.shouldSample(aRoot.traceId);
}
let parentSpanId = '';
let traceState;
if (options && options.spanContext) {
// New child span.
parentSpanId = options.spanContext.spanId || '';
traceState = options.spanContext.traceState;
}

if (this.active) {
const sampleDecision = this.makeSamplingDecision(options, traceId);
if (sampleDecision) {
this.currentRootSpan = aRoot;
aRoot.start();
return fn(aRoot);
const rootSpan =
new RootSpan(this, name, kind, traceId, parentSpanId, traceState);
this.currentRootSpan = rootSpan;
rootSpan.start();
return fn(rootSpan);
}
} else {
this.logger.debug('Tracer is inactive, can\'t start new RootSpan');
}
const noRecordRootSpan = new NoRecordRootSpan(this, options);
const noRecordRootSpan = new NoRecordRootSpan(
this, name, kind, traceId, parentSpanId, traceState);
this.currentRootSpan = noRecordRootSpan;
return fn(noRecordRootSpan);
});
Expand Down Expand Up @@ -283,4 +286,28 @@ export class CoreTracer implements types.Tracer {
const namespace = this.contextManager;
namespace.bindEmitter(emitter);
}

/** Determine whether to sample request or not. */
private makeSamplingDecision(options: types.TraceOptions, traceId: string):
boolean {
// If users set a specific sampler in the TraceOptions, use it.
if (options && options.samplingRate !== undefined &&
options.samplingRate !== null) {
return SamplerBuilder.getSampler(options.samplingRate)
.shouldSample(traceId);
}
let propagatedSample = null;
// if there is a context propagation, keep the decision
if (options && options.spanContext && options.spanContext.options) {
propagatedSample =
((options.spanContext.options & this.IS_SAMPLED) !== 0);
}

let sampleDecision: boolean = propagatedSample;
if (!sampleDecision) {
// Use the default global sampler
sampleDecision = this.sampler.shouldSample(traceId);
}
return sampleDecision;
}
}
2 changes: 2 additions & 0 deletions packages/opencensus-core/src/trace/model/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -232,6 +232,8 @@ export interface TraceOptions {
spanContext?: SpanContext;
/** Span kind */
kind?: SpanKind;
/** Determines the sampling rate. Ranges from 0.0 to 1.0 */
samplingRate?: number;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this be a Sampler instance per the specs?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am happy to change here, but the global sampler takes the same input

To keep things consistent I use number. WDYT?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, I'm OK with that, but could you create an issue to track changing it in both places to bring it closer to the specs?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

sure.

}

/** Defines the span options */
Expand Down
14 changes: 9 additions & 5 deletions packages/opencensus-core/test/test-console-exporter.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,13 +25,17 @@ const defaultBufferConfig = {
bufferSize: 1,
bufferTimeout: 20000 // time in milliseconds
};
const name = 'MySpanName';
const kind = SpanKind.SERVER;
const traceId = 'd4cda95b652f4a1592b449d5929fda1b';
const parentSpanId = '';

describe('NoopExporter', () => {
/** Should do nothing when calling onEndSpan() */
describe('onEndSpan()', () => {
it('should do nothing', () => {
const exporter = new NoopExporter();
const rootSpan = new RootSpan(tracer);
const rootSpan = new RootSpan(tracer, name, kind, traceId, parentSpanId);
exporter.onEndSpan(rootSpan);
assert.ok(true);
});
Expand All @@ -41,7 +45,7 @@ describe('NoopExporter', () => {
describe('publish()', () => {
it('should do nothing', () => {
const exporter = new NoopExporter();
const rootSpan = new RootSpan(tracer);
const rootSpan = new RootSpan(tracer, name, kind, traceId, parentSpanId);
const queue: RootSpan[] = [rootSpan];

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

const exporter = new ConsoleExporter(defaultBufferConfig);

const rootSpan1 = new RootSpan(tracer);
const rootSpan1 = new RootSpan(tracer, name, kind, traceId, parentSpanId);
exporter.onEndSpan(rootSpan1);
assert.strictEqual(capturedText, '');

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

const exporter = new ConsoleExporter(defaultBufferConfig);
const rootSpan = new RootSpan(tracer);
const rootSpan = new RootSpan(tracer, name, kind, traceId, parentSpanId);
rootSpan.start();
rootSpan.startChildSpan('name', SpanKind.UNSPECIFIED, rootSpan.traceId);
const queue: RootSpan[] = [rootSpan];
Expand Down
17 changes: 12 additions & 5 deletions packages/opencensus-core/test/test-exporter-buffer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@ import {ExporterBuffer} from '../src/exporters/exporter-buffer';
import {RootSpan} from '../src/trace/model/root-span';
import {CoreTracer} from '../src/trace/model/tracer';


const exporter = new NoopExporter();
const DEFAULT_BUFFER_SIZE = 3;
const DEFAULT_BUFFER_TIMEOUT = 2000; // time in milliseconds
Expand All @@ -34,11 +33,16 @@ const defaultBufferConfig = {
logger: logger.logger()
};

const name = 'MySpanName';
const kind = SpanKind.SERVER;
const traceId = 'd4cda95b652f4a1592b449d5929fda1b';
const parentSpanId = '';

const createRootSpans = (num: number): RootSpan[] => {
const rootSpans = [];
for (let i = 0; i < num; i++) {
const rootSpan = new RootSpan(tracer, {name: `rootSpan.${i}`});
const rootSpan =
new RootSpan(tracer, `rootSpan.${i}`, kind, traceId, parentSpanId);
rootSpan.start();
for (let j = 0; j < 10; j++) {
rootSpan.startChildSpan(`childSpan.${i}.${j}`, SpanKind.CLIENT);
Expand Down Expand Up @@ -79,7 +83,8 @@ describe('ExporterBuffer', () => {
describe('addToBuffer', () => {
it('should add one item to the Buffer', () => {
const buffer = new ExporterBuffer(exporter, defaultBufferConfig);
buffer.addToBuffer(new RootSpan(tracer));
buffer.addToBuffer(
new RootSpan(tracer, name, kind, traceId, parentSpanId));
assert.strictEqual(buffer.getQueue().length, 1);
});
});
Expand All @@ -95,7 +100,8 @@ describe('ExporterBuffer', () => {
buffer.addToBuffer(rootSpan);
}
assert.strictEqual(buffer.getQueue().length, buffer.getBufferSize());
buffer.addToBuffer(new RootSpan(tracer));
buffer.addToBuffer(
new RootSpan(tracer, name, kind, traceId, parentSpanId));
assert.strictEqual(buffer.getQueue().length, 0);
});
});
Expand All @@ -106,7 +112,8 @@ describe('ExporterBuffer', () => {
describe('addToBuffer force flush by timeout ', () => {
it('should flush by timeout', (done) => {
const buffer = new ExporterBuffer(exporter, defaultBufferConfig);
buffer.addToBuffer(new RootSpan(tracer));
buffer.addToBuffer(
new RootSpan(tracer, name, kind, traceId, parentSpanId));
assert.strictEqual(buffer.getQueue().length, 1);
setTimeout(() => {
assert.strictEqual(buffer.getQueue().length, 0);
Expand Down
5 changes: 3 additions & 2 deletions packages/opencensus-core/test/test-no-record-root-span.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,14 +14,15 @@
* limitations under the License.
*/

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

const tracer = new CoreTracer();

describe('NoRecordRootSpan()', () => {
it('do not crash', () => {
const noRecordRootSpan = new NoRecordRootSpan(tracer);
const noRecordRootSpan =
new NoRecordRootSpan(tracer, 'name', SpanKind.SERVER, 'traceid', '');
noRecordRootSpan.addAnnotation('MyAnnotation');
noRecordRootSpan.addAnnotation('MyAnnotation', {myString: 'bar'});
noRecordRootSpan.addAnnotation(
Expand Down
7 changes: 4 additions & 3 deletions packages/opencensus-core/test/test-no-record-span.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,16 +14,17 @@
* limitations under the License.
*/

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

const tracer = new CoreTracer();

describe('NoRecordSpan()', () => {
it('do not crash', () => {
const root = new NoRecordRootSpan(tracer);
const noRecordSpan = new NoRecordSpan(root);
const noRecordRootSpan =
new NoRecordRootSpan(tracer, 'name', SpanKind.SERVER, 'traceid', '');
const noRecordSpan = new NoRecordSpan(noRecordRootSpan);
noRecordSpan.addAnnotation('MyAnnotation');
noRecordSpan.addAnnotation('MyAnnotation', {myString: 'bar'});
noRecordSpan.addAnnotation(
Expand Down
Loading