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

Commit 92edaa9

Browse files
hekikeBogdan Drutu
authored andcommitted
feat(tracer): separate cls from tracer (#484)
* feat(tracer): separate cls from tracer BREAKING CHANGE: startChildSpan only accepts option object * refactor(tracer): keep CLS CoreTracer the default and introduce CoreTraceBase * refactor(tracer): address PR comments * refactor(tracer): address PR comments * test(tracer): separate CoreTracerBasic and CoreTracer tests * refactor(opencensus-core): tracer base code improvement * refactor(opencensus-core): tracer base code improvement
1 parent 2e6709a commit 92edaa9

File tree

26 files changed

+772
-582
lines changed

26 files changed

+772
-582
lines changed

CHANGELOG.md

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,32 @@ All notable changes to this project will be documented in this file.
44

55
## Unreleased
66

7+
**This release has a breaking change. Please test your code accordingly after upgrading.**
8+
9+
- removing Tracer's `startChildSpan(name?: string, kind?: types.SpanKind)` interface
10+
11+
##### Old code
12+
13+
```js
14+
// Multi argument interface
15+
const span = tracer.startChildSpan('my-span', types.SpanKind.SERVER);
16+
17+
// Or options object interface
18+
const span = tracer.startChildSpan({
19+
name: 'my-span',
20+
kind: types.SpanKind.SERVER
21+
});
22+
```
23+
24+
##### New code
25+
26+
```js
27+
// Only options object interface is supported
28+
const span = tracer.startChildSpan({
29+
name: 'my-span',
30+
kind: types.SpanKind.SERVER
31+
});
32+
```
733

834
## 0.0.11 - 2019-04-08
935
- Gauge: Add support for constant labels.

examples/grpc/capitalize_server.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ const rpcProto = grpc.loadPackageDefinition(definition).rpc;
3131

3232
/** Implements the Capitalize RPC method. */
3333
function capitalize (call, callback) {
34-
const span = tracer.startChildSpan('octutorials.FetchImpl.capitalize');
34+
const span = tracer.startChildSpan({ name: 'octutorials.FetchImpl.capitalize' });
3535
const data = call.request.data.toString('utf8');
3636
const capitalized = data.toUpperCase();
3737
for (let i = 0; i < 100000000; i++) {}

examples/http/server.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ function startServer (port) {
4242

4343
/** A function which handles requests and send response. */
4444
function handleRequest (request, response) {
45-
const span = tracer.startChildSpan('octutorials.handleRequest');
45+
const span = tracer.startChildSpan({ name: 'octutorials.handleRequest' });
4646
try {
4747
let body = [];
4848
request.on('error', err => console.log(err));

examples/trace/multi-span-tracing.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ function main () {
5252
function doWork () {
5353
// 5. Start another span. In this example, the main method already started a
5454
// span, so that'll be the parent span, and this will be a child span.
55-
const span = tracer.startChildSpan('doWork');
55+
const span = tracer.startChildSpan({ name: 'doWork' });
5656

5757
console.log('doing busy work');
5858
for (let i = 0; i <= 40000000; i++) {} // short delay

packages/opencensus-core/src/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ export {Metric, MetricDescriptor, TimeSeries, MetricDescriptorType, LabelKey, La
2929
// classes
3030

3131
// domain models impls
32+
export * from './trace/model/tracer-base';
3233
export * from './trace/model/tracer';
3334

3435
// sampler impl

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

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
*/
1616

1717
import {Stats} from '../../stats/types';
18-
import {Tracer} from '../model/types';
18+
import {TracerBase} from '../model/types';
1919

2020
/** Interface Plugin to apply patch. */
2121
export interface Plugin {
@@ -30,8 +30,8 @@ export interface Plugin {
3030
* @param stats an optional stats instance
3131
*/
3232
enable<T>(
33-
moduleExports: T, tracer: Tracer, version: string, options: PluginConfig,
34-
basedir?: string, stats?: Stats): T;
33+
moduleExports: T, tracer: TracerBase, version: string,
34+
options: PluginConfig, basedir?: string, stats?: Stats): T;
3535
/** Method to disable the instrumentation */
3636
disable(): void;
3737
}

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

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ import {NoRecordSpan} from './no-record-span';
2121
/** Implementation for the Span class that does not record trace events. */
2222
export class NoRecordRootSpan extends NoRecordSpan {
2323
/** A tracer object */
24-
private tracer: types.Tracer;
24+
private tracer: types.TracerBase;
2525
/** Its trace ID. */
2626
private traceIdLocal: string;
2727
/** Its trace state. */
@@ -43,8 +43,8 @@ export class NoRecordRootSpan extends NoRecordSpan {
4343
* @param traceState Optional traceState.
4444
*/
4545
constructor(
46-
tracer: types.Tracer, name: string, kind: types.SpanKind, traceId: string,
47-
parentSpanId: string, traceState?: types.TraceState) {
46+
tracer: types.TracerBase, name: string, kind: types.SpanKind,
47+
traceId: string, parentSpanId: string, traceState?: types.TraceState) {
4848
super();
4949
this.tracer = tracer;
5050
this.traceIdLocal = traceId;

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

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ import * as types from './types';
2121
/** Defines a root span */
2222
export class RootSpan extends Span {
2323
/** A tracer object */
24-
private tracer: types.Tracer;
24+
private tracer: types.TracerBase;
2525
/** Its trace ID. */
2626
private traceIdLocal: string;
2727
/** Its trace state. */
@@ -43,8 +43,8 @@ export class RootSpan extends Span {
4343
* @param traceState An optional traceState.
4444
*/
4545
constructor(
46-
tracer: types.Tracer, name: string, kind: types.SpanKind, traceId: string,
47-
parentSpanId: string, traceState?: types.TraceState) {
46+
tracer: types.TracerBase, name: string, kind: types.SpanKind,
47+
traceId: string, parentSpanId: string, traceState?: types.TraceState) {
4848
super();
4949
this.tracer = tracer;
5050
this.traceIdLocal = traceId;
Lines changed: 232 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,232 @@
1+
/**
2+
* Copyright 2018, OpenCensus Authors
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
import * as uuid from 'uuid';
18+
import * as logger from '../../common/console-logger';
19+
import * as loggerTypes from '../../common/types';
20+
import * as configTypes from '../config/types';
21+
import {TraceParams} from '../config/types';
22+
import {noopPropagation} from '../propagation/noop-propagation';
23+
import {Propagation} from '../propagation/types';
24+
import {DEFAULT_SAMPLING_RATE, SamplerBuilder, TraceParamsBuilder} from '../sampler/sampler';
25+
import * as samplerTypes from '../sampler/types';
26+
import {NoRecordRootSpan} from './no-record/no-record-root-span';
27+
import {NoRecordSpan} from './no-record/no-record-span';
28+
import {RootSpan} from './root-span';
29+
import * as types from './types';
30+
31+
/**
32+
* This class represents a tracer.
33+
*/
34+
export class CoreTracerBase implements types.TracerBase {
35+
/** Indicates if the tracer is active */
36+
private activeLocal: boolean;
37+
/** A configuration for starting the tracer */
38+
private config!: configTypes.TracerConfig;
39+
/** A list of end span event listeners */
40+
private eventListenersLocal: types.SpanEventListener[] = [];
41+
/** Bit to represent whether trace is sampled or not. */
42+
private readonly IS_SAMPLED = 0x1;
43+
/** A sampler used to make sample decisions */
44+
sampler!: samplerTypes.Sampler;
45+
/** An object to log information */
46+
logger: loggerTypes.Logger = logger.logger();
47+
/** A configuration object for trace parameters */
48+
activeTraceParams: TraceParams;
49+
50+
/** Constructs a new TraceImpl instance. */
51+
constructor() {
52+
this.activeLocal = false;
53+
this.activeTraceParams = {};
54+
}
55+
56+
/** A propagation instance */
57+
get propagation(): Propagation {
58+
if (this.config && this.config.propagation) {
59+
return this.config.propagation;
60+
}
61+
return noopPropagation;
62+
}
63+
64+
/**
65+
* Starts a tracer.
66+
* @param config A tracer configuration object to start a tracer.
67+
*/
68+
start(config: configTypes.TracerConfig): this {
69+
this.activeLocal = true;
70+
this.config = config;
71+
this.logger = this.config.logger || logger.logger();
72+
this.sampler =
73+
SamplerBuilder.getSampler(config.samplingRate || DEFAULT_SAMPLING_RATE);
74+
if (config.traceParams) {
75+
this.activeTraceParams.numberOfAnnontationEventsPerSpan =
76+
TraceParamsBuilder.getNumberOfAnnotationEventsPerSpan(
77+
config.traceParams);
78+
this.activeTraceParams.numberOfAttributesPerSpan =
79+
TraceParamsBuilder.getNumberOfAttributesPerSpan(config.traceParams);
80+
this.activeTraceParams.numberOfMessageEventsPerSpan =
81+
TraceParamsBuilder.getNumberOfMessageEventsPerSpan(
82+
config.traceParams);
83+
this.activeTraceParams.numberOfLinksPerSpan =
84+
TraceParamsBuilder.getNumberOfLinksPerSpan(config.traceParams);
85+
}
86+
return this;
87+
}
88+
89+
/** Stops the tracer. */
90+
stop(): this {
91+
this.activeLocal = false;
92+
return this;
93+
}
94+
95+
/** Gets the list of event listeners. */
96+
get eventListeners(): types.SpanEventListener[] {
97+
return this.eventListenersLocal;
98+
}
99+
100+
/** Indicates if the tracer is active or not. */
101+
get active(): boolean {
102+
return this.activeLocal;
103+
}
104+
105+
/**
106+
* Starts a root span.
107+
* @param options A TraceOptions object to start a root span.
108+
* @param fn A callback function to run after starting a root span.
109+
*/
110+
startRootSpan<T>(options: types.TraceOptions, fn: (root: types.Span) => T):
111+
T {
112+
const spanContext: types.SpanContext = options.spanContext ||
113+
{spanId: '', traceId: uuid.v4().split('-').join('')};
114+
const parentSpanId = spanContext.spanId;
115+
const traceId = spanContext.traceId;
116+
const name = options.name || 'span';
117+
const kind = options.kind || types.SpanKind.UNSPECIFIED;
118+
const traceState = spanContext.traceState;
119+
120+
// Tracer is active
121+
if (this.active) {
122+
const sampleDecision = this.makeSamplingDecision(options, traceId);
123+
// Sampling is on
124+
if (sampleDecision) {
125+
const rootSpan =
126+
new RootSpan(this, name, kind, traceId, parentSpanId, traceState);
127+
rootSpan.start();
128+
return fn(rootSpan);
129+
}
130+
131+
// Sampling is off
132+
this.logger.debug('Sampling is off, starting new no record root span');
133+
const noRecordRootSpan = new NoRecordRootSpan(
134+
this, name, kind, traceId, parentSpanId, traceState);
135+
return fn(noRecordRootSpan);
136+
}
137+
138+
// Tracer is inactive
139+
this.logger.debug('Tracer is inactive, starting new no record root span');
140+
const noRecordRootSpan = new NoRecordRootSpan(
141+
this, name, kind, traceId, parentSpanId, traceState);
142+
return fn(noRecordRootSpan);
143+
}
144+
145+
/** Notifies listeners of the span start. */
146+
onStartSpan(root: types.Span): void {
147+
if (!this.active) return;
148+
if (!root) {
149+
return this.logger.debug('cannot start trace - no active trace found');
150+
}
151+
this.notifyStartSpan(root);
152+
}
153+
154+
/** Notifies listeners of the span end. */
155+
onEndSpan(root: types.Span): void {
156+
if (!this.active) return;
157+
if (!root) {
158+
this.logger.debug('cannot end trace - no active trace found');
159+
return;
160+
}
161+
this.notifyEndSpan(root);
162+
}
163+
164+
/**
165+
* Registers an end span event listener.
166+
* @param listener The listener to register.
167+
*/
168+
registerSpanEventListener(listener: types.SpanEventListener) {
169+
this.eventListenersLocal.push(listener);
170+
}
171+
172+
/**
173+
* Unregisters an end span event listener.
174+
* @param listener The listener to unregister.
175+
*/
176+
unregisterSpanEventListener(listener: types.SpanEventListener) {
177+
const index = this.eventListenersLocal.indexOf(listener, 0);
178+
if (index > -1) {
179+
this.eventListeners.splice(index, 1);
180+
}
181+
}
182+
183+
private notifyStartSpan(root: types.Span) {
184+
this.logger.debug('starting to notify listeners the start of rootspans');
185+
if (this.eventListenersLocal && this.eventListenersLocal.length > 0) {
186+
for (const listener of this.eventListenersLocal) {
187+
listener.onStartSpan(root);
188+
}
189+
}
190+
}
191+
192+
private notifyEndSpan(root: types.Span) {
193+
this.logger.debug('starting to notify listeners the end of rootspans');
194+
if (this.eventListenersLocal && this.eventListenersLocal.length > 0) {
195+
for (const listener of this.eventListenersLocal) {
196+
listener.onEndSpan(root);
197+
}
198+
}
199+
}
200+
201+
/**
202+
* Starts a span.
203+
* @param [options] A SpanOptions object to start a child span.
204+
*/
205+
startChildSpan(options?: types.SpanOptions): types.Span {
206+
if (!options || !options.childOf) {
207+
this.logger.debug(
208+
'no current trace found - must start a new root span first');
209+
return new NoRecordSpan();
210+
}
211+
return options.childOf.startChildSpan(options.name, options.kind);
212+
}
213+
214+
/** Determine whether to sample request or not. */
215+
private makeSamplingDecision(options: types.TraceOptions, traceId: string):
216+
boolean {
217+
// If users set a specific sampler in the TraceOptions, use it.
218+
if (options && options.samplingRate !== undefined &&
219+
options.samplingRate !== null) {
220+
return SamplerBuilder.getSampler(options.samplingRate)
221+
.shouldSample(traceId);
222+
}
223+
let propagatedSample = null;
224+
// if there is a context propagation, keep the decision
225+
if (options && options.spanContext && options.spanContext.options) {
226+
propagatedSample = (options.spanContext.options & this.IS_SAMPLED) !== 0;
227+
}
228+
229+
// Propagated sample or use the default global sampler
230+
return !!propagatedSample || this.sampler.shouldSample(traceId);
231+
}
232+
}

0 commit comments

Comments
 (0)