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

Commit

Permalink
feat(tracer): separate cls from tracer (#484)
Browse files Browse the repository at this point in the history
* 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
  • Loading branch information
hekike authored and Bogdan Drutu committed May 3, 2019
1 parent 2e6709a commit 92edaa9
Show file tree
Hide file tree
Showing 26 changed files with 772 additions and 582 deletions.
26 changes: 26 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,32 @@ All notable changes to this project will be documented in this file.

## Unreleased

**This release has a breaking change. Please test your code accordingly after upgrading.**

- removing Tracer's `startChildSpan(name?: string, kind?: types.SpanKind)` interface

##### Old code

```js
// Multi argument interface
const span = tracer.startChildSpan('my-span', types.SpanKind.SERVER);

// Or options object interface
const span = tracer.startChildSpan({
name: 'my-span',
kind: types.SpanKind.SERVER
});
```

##### New code

```js
// Only options object interface is supported
const span = tracer.startChildSpan({
name: 'my-span',
kind: types.SpanKind.SERVER
});
```

## 0.0.11 - 2019-04-08
- Gauge: Add support for constant labels.
Expand Down
2 changes: 1 addition & 1 deletion examples/grpc/capitalize_server.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ const rpcProto = grpc.loadPackageDefinition(definition).rpc;

/** Implements the Capitalize RPC method. */
function capitalize (call, callback) {
const span = tracer.startChildSpan('octutorials.FetchImpl.capitalize');
const span = tracer.startChildSpan({ name: 'octutorials.FetchImpl.capitalize' });
const data = call.request.data.toString('utf8');
const capitalized = data.toUpperCase();
for (let i = 0; i < 100000000; i++) {}
Expand Down
2 changes: 1 addition & 1 deletion examples/http/server.js
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ function startServer (port) {

/** A function which handles requests and send response. */
function handleRequest (request, response) {
const span = tracer.startChildSpan('octutorials.handleRequest');
const span = tracer.startChildSpan({ name: 'octutorials.handleRequest' });
try {
let body = [];
request.on('error', err => console.log(err));
Expand Down
2 changes: 1 addition & 1 deletion examples/trace/multi-span-tracing.js
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ function main () {
function doWork () {
// 5. Start another span. In this example, the main method already started a
// span, so that'll be the parent span, and this will be a child span.
const span = tracer.startChildSpan('doWork');
const span = tracer.startChildSpan({ name: 'doWork' });

console.log('doing busy work');
for (let i = 0; i <= 40000000; i++) {} // short delay
Expand Down
1 change: 1 addition & 0 deletions packages/opencensus-core/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ export {Metric, MetricDescriptor, TimeSeries, MetricDescriptorType, LabelKey, La
// classes

// domain models impls
export * from './trace/model/tracer-base';
export * from './trace/model/tracer';

// sampler impl
Expand Down
6 changes: 3 additions & 3 deletions packages/opencensus-core/src/trace/instrumentation/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
*/

import {Stats} from '../../stats/types';
import {Tracer} from '../model/types';
import {TracerBase} from '../model/types';

/** Interface Plugin to apply patch. */
export interface Plugin {
Expand All @@ -30,8 +30,8 @@ export interface Plugin {
* @param stats an optional stats instance
*/
enable<T>(
moduleExports: T, tracer: Tracer, version: string, options: PluginConfig,
basedir?: string, stats?: Stats): T;
moduleExports: T, tracer: TracerBase, version: string,
options: PluginConfig, basedir?: string, stats?: Stats): T;
/** Method to disable the instrumentation */
disable(): void;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ import {NoRecordSpan} from './no-record-span';
/** Implementation for the Span class that does not record trace events. */
export class NoRecordRootSpan extends NoRecordSpan {
/** A tracer object */
private tracer: types.Tracer;
private tracer: types.TracerBase;
/** Its trace ID. */
private traceIdLocal: string;
/** Its trace state. */
Expand All @@ -43,8 +43,8 @@ export class NoRecordRootSpan extends NoRecordSpan {
* @param traceState Optional traceState.
*/
constructor(
tracer: types.Tracer, name: string, kind: types.SpanKind, traceId: string,
parentSpanId: string, traceState?: types.TraceState) {
tracer: types.TracerBase, name: string, kind: types.SpanKind,
traceId: string, parentSpanId: string, traceState?: types.TraceState) {
super();
this.tracer = tracer;
this.traceIdLocal = traceId;
Expand Down
6 changes: 3 additions & 3 deletions packages/opencensus-core/src/trace/model/root-span.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ import * as types from './types';
/** Defines a root span */
export class RootSpan extends Span {
/** A tracer object */
private tracer: types.Tracer;
private tracer: types.TracerBase;
/** Its trace ID. */
private traceIdLocal: string;
/** Its trace state. */
Expand All @@ -43,8 +43,8 @@ export class RootSpan extends Span {
* @param traceState An optional traceState.
*/
constructor(
tracer: types.Tracer, name: string, kind: types.SpanKind, traceId: string,
parentSpanId: string, traceState?: types.TraceState) {
tracer: types.TracerBase, name: string, kind: types.SpanKind,
traceId: string, parentSpanId: string, traceState?: types.TraceState) {
super();
this.tracer = tracer;
this.traceIdLocal = traceId;
Expand Down
232 changes: 232 additions & 0 deletions packages/opencensus-core/src/trace/model/tracer-base.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,232 @@
/**
* Copyright 2018, OpenCensus Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

import * as uuid from 'uuid';
import * as logger from '../../common/console-logger';
import * as loggerTypes from '../../common/types';
import * as configTypes from '../config/types';
import {TraceParams} from '../config/types';
import {noopPropagation} from '../propagation/noop-propagation';
import {Propagation} from '../propagation/types';
import {DEFAULT_SAMPLING_RATE, SamplerBuilder, TraceParamsBuilder} from '../sampler/sampler';
import * as samplerTypes from '../sampler/types';
import {NoRecordRootSpan} from './no-record/no-record-root-span';
import {NoRecordSpan} from './no-record/no-record-span';
import {RootSpan} from './root-span';
import * as types from './types';

/**
* This class represents a tracer.
*/
export class CoreTracerBase implements types.TracerBase {
/** Indicates if the tracer is active */
private activeLocal: boolean;
/** A configuration for starting the tracer */
private config!: configTypes.TracerConfig;
/** A list of end span event listeners */
private eventListenersLocal: types.SpanEventListener[] = [];
/** Bit to represent whether trace is sampled or not. */
private readonly IS_SAMPLED = 0x1;
/** A sampler used to make sample decisions */
sampler!: samplerTypes.Sampler;
/** An object to log information */
logger: loggerTypes.Logger = logger.logger();
/** A configuration object for trace parameters */
activeTraceParams: TraceParams;

/** Constructs a new TraceImpl instance. */
constructor() {
this.activeLocal = false;
this.activeTraceParams = {};
}

/** A propagation instance */
get propagation(): Propagation {
if (this.config && this.config.propagation) {
return this.config.propagation;
}
return noopPropagation;
}

/**
* Starts a tracer.
* @param config A tracer configuration object to start a tracer.
*/
start(config: configTypes.TracerConfig): this {
this.activeLocal = true;
this.config = config;
this.logger = this.config.logger || logger.logger();
this.sampler =
SamplerBuilder.getSampler(config.samplingRate || DEFAULT_SAMPLING_RATE);
if (config.traceParams) {
this.activeTraceParams.numberOfAnnontationEventsPerSpan =
TraceParamsBuilder.getNumberOfAnnotationEventsPerSpan(
config.traceParams);
this.activeTraceParams.numberOfAttributesPerSpan =
TraceParamsBuilder.getNumberOfAttributesPerSpan(config.traceParams);
this.activeTraceParams.numberOfMessageEventsPerSpan =
TraceParamsBuilder.getNumberOfMessageEventsPerSpan(
config.traceParams);
this.activeTraceParams.numberOfLinksPerSpan =
TraceParamsBuilder.getNumberOfLinksPerSpan(config.traceParams);
}
return this;
}

/** Stops the tracer. */
stop(): this {
this.activeLocal = false;
return this;
}

/** Gets the list of event listeners. */
get eventListeners(): types.SpanEventListener[] {
return this.eventListenersLocal;
}

/** Indicates if the tracer is active or not. */
get active(): boolean {
return this.activeLocal;
}

/**
* Starts a root span.
* @param options A TraceOptions object to start a root span.
* @param fn A callback function to run after starting a root span.
*/
startRootSpan<T>(options: types.TraceOptions, fn: (root: types.Span) => T):
T {
const spanContext: types.SpanContext = options.spanContext ||
{spanId: '', traceId: uuid.v4().split('-').join('')};
const parentSpanId = spanContext.spanId;
const traceId = spanContext.traceId;
const name = options.name || 'span';
const kind = options.kind || types.SpanKind.UNSPECIFIED;
const traceState = spanContext.traceState;

// Tracer is active
if (this.active) {
const sampleDecision = this.makeSamplingDecision(options, traceId);
// Sampling is on
if (sampleDecision) {
const rootSpan =
new RootSpan(this, name, kind, traceId, parentSpanId, traceState);
rootSpan.start();
return fn(rootSpan);
}

// Sampling is off
this.logger.debug('Sampling is off, starting new no record root span');
const noRecordRootSpan = new NoRecordRootSpan(
this, name, kind, traceId, parentSpanId, traceState);
return fn(noRecordRootSpan);
}

// Tracer is inactive
this.logger.debug('Tracer is inactive, starting new no record root span');
const noRecordRootSpan = new NoRecordRootSpan(
this, name, kind, traceId, parentSpanId, traceState);
return fn(noRecordRootSpan);
}

/** Notifies listeners of the span start. */
onStartSpan(root: types.Span): void {
if (!this.active) return;
if (!root) {
return this.logger.debug('cannot start trace - no active trace found');
}
this.notifyStartSpan(root);
}

/** Notifies listeners of the span end. */
onEndSpan(root: types.Span): void {
if (!this.active) return;
if (!root) {
this.logger.debug('cannot end trace - no active trace found');
return;
}
this.notifyEndSpan(root);
}

/**
* Registers an end span event listener.
* @param listener The listener to register.
*/
registerSpanEventListener(listener: types.SpanEventListener) {
this.eventListenersLocal.push(listener);
}

/**
* Unregisters an end span event listener.
* @param listener The listener to unregister.
*/
unregisterSpanEventListener(listener: types.SpanEventListener) {
const index = this.eventListenersLocal.indexOf(listener, 0);
if (index > -1) {
this.eventListeners.splice(index, 1);
}
}

private notifyStartSpan(root: types.Span) {
this.logger.debug('starting to notify listeners the start of rootspans');
if (this.eventListenersLocal && this.eventListenersLocal.length > 0) {
for (const listener of this.eventListenersLocal) {
listener.onStartSpan(root);
}
}
}

private notifyEndSpan(root: types.Span) {
this.logger.debug('starting to notify listeners the end of rootspans');
if (this.eventListenersLocal && this.eventListenersLocal.length > 0) {
for (const listener of this.eventListenersLocal) {
listener.onEndSpan(root);
}
}
}

/**
* Starts a span.
* @param [options] A SpanOptions object to start a child span.
*/
startChildSpan(options?: types.SpanOptions): types.Span {
if (!options || !options.childOf) {
this.logger.debug(
'no current trace found - must start a new root span first');
return new NoRecordSpan();
}
return options.childOf.startChildSpan(options.name, options.kind);
}

/** 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;
}

// Propagated sample or use the default global sampler
return !!propagatedSample || this.sampler.shouldSample(traceId);
}
}
Loading

0 comments on commit 92edaa9

Please sign in to comment.