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

Commit

Permalink
Add a NoRecord RootSpan and Span (#389)
Browse files Browse the repository at this point in the history
* Add a no-op RootSpan and Span

This will allow user to instrument a library without enabling sampling (i.e. using ```NeverSampler```) or using ```ProbabilitySampler``` (Sampler that samples a given fraction of traces).

* Rename: NoopSpan ->  NoRecordSpan, NoopRootSpan -> NoRecordRootSpan
  • Loading branch information
mayurkale22 committed Mar 6, 2019
1 parent 58eff0f commit 1140dd3
Show file tree
Hide file tree
Showing 8 changed files with 401 additions and 10 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ All notable changes to this project will be documented in this file.
- Add support for recording HTTP stats.
- Enforce `--strictNullChecks` and `--noUnusedLocals` Compiler Options on [opencensus-instrumentation-http], [opencensus-instrumentation-grpc] and [opencensus-propagation-tracecontext] packages.
- Enforce `--strictNullChecks` and `--noUnusedLocals` Compiler Options on [opencensus-exporter-zipkin] packages.
- Add NoRecordRootSpan, NoRecordSpan and NoRecordSpanBase.

## 0.0.9 - 2019-02-12
- Add Metrics API.
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,116 @@
/**
* Copyright 2019, 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 types from '../types';
import {NoRecordSpan} from './no-record-span';
import {NoRecordSpanBase} from './no-record-span-base';

/** Implementation for the RootSpan class that does not record trace events. */
export class NoRecordRootSpan extends NoRecordSpanBase implements
types.RootSpan {
/** A tracer object */
private tracer: types.Tracer;
/** Its trace ID. */
private traceIdLocal: string;
/** Its trace state. */
private traceStateLocal: types.TraceState;
/** set isRootSpan = true */
readonly isRootSpan = true;

/**
* Constructs a new NoRecordRootSpanImpl instance.
* @param tracer A tracer object.
* @param context A trace options object to build the no-record root span.
*/
constructor(tracer: types.Tracer, context?: types.TraceOptions) {
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.kind =
context && context.kind ? context.kind : types.SpanKind.UNSPECIFIED;
this.logger = this.tracer.logger || logger.logger();
}

/** No-op implementation of this method. */
get spans(): types.Span[] {
return [];
}

/** No-op implementation of this method. */
get traceId(): string {
return this.traceIdLocal;
}

/** No-op implementation of this method. */
get traceState(): types.TraceState {
return this.traceStateLocal;
}

/** No-op implementation of this method. */
get numberOfChildren(): number {
return 0;
}

/** No-op implementation of this method. */
start() {
super.start();
}

/** No-op implementation of this method. */
end() {
super.end();
}

/**
* Starts a new child span in the noop root span.
* @param name Span name.
* @param kind Span kind.
* @param parentSpanId Span parent ID.
*/
startChildSpan(
nameOrOptions?: string|types.SpanOptions, kind?: types.SpanKind,
parentSpanId?: string): types.Span {
const newSpan = new NoRecordSpan(this);
let spanName;
let spanKind;
if (typeof nameOrOptions === 'object') {
spanName = nameOrOptions.name;
spanKind = nameOrOptions.kind;
} else {
spanName = nameOrOptions;
spanKind = kind;
}

if (spanName) {
newSpan.name = spanName;
}
if (spanKind) {
newSpan.kind = spanKind;
}
newSpan.start();
return newSpan;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,149 @@
/**
* Copyright 2019, 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 {Logger} from '../../../common/types';
import {randomSpanId} from '../../../internal/util';
import * as configTypes from '../../config/types';
import * as types from '../types';

const STATUS_OK = {
code: types.CanonicalCode.OK
};

/** Implementation for the SpanBase class that does not record trace events. */
export abstract class NoRecordSpanBase implements types.Span {
/** Indicates if this span was started */
private startedLocal = false;
/** Indicates if this span was ended */
private endedLocal = false;
/** Indicates if this span was forced to end */
// @ts-ignore
private truncated = false;
/** The Span ID of this span */
readonly id: string;
/** An object to log information to */
logger: Logger;
/** A set of attributes, each in the format [KEY]:[VALUE] */
attributes: types.Attributes = {};
/** A text annotation with a set of attributes. */
annotations: types.Annotation[] = [];
/** An event describing a message sent/received between Spans */
messageEvents: types.MessageEvent[] = [];
/** Pointers from the current span to another span */
links: types.Link[] = [];
/** If the parent span is in another process. */
remoteParent: boolean;
/** The span ID of this span's parent. If it's a root span, must be empty */
parentSpanId: string = null;
/** The resource name of the span */
name: string = null;
/** Kind of span. */
kind: types.SpanKind = types.SpanKind.UNSPECIFIED;
/** A final status for this span */
status: types.Status = STATUS_OK;
/** set isRootSpan */
abstract get isRootSpan(): boolean;
/** Trace Parameters */
activeTraceParams: configTypes.TraceParams;

/** The number of dropped attributes. */
droppedAttributesCount = 0;
/** The number of dropped links. */
droppedLinksCount = 0;
/** The number of dropped annotations. */
droppedAnnotationsCount = 0;
/** The number of dropped message events. */
droppedMessageEventsCount = 0;

/** Constructs a new SpanBaseModel instance. */
constructor() {
this.id = randomSpanId();
}

/** Gets the trace ID. */
abstract get traceId(): string;

/** Gets the trace state */
abstract get traceState(): types.TraceState;

/** Indicates if span was started. */
get started(): boolean {
return this.startedLocal;
}

/** Indicates if span was ended. */
get ended(): boolean {
return this.endedLocal;
}

/** No-op implementation of this method. */
get startTime(): Date {
return new Date();
}

/** No-op implementation of this method. */
get endTime(): Date {
return new Date();
}

/** Gives the TraceContext of the span. */
get spanContext(): types.SpanContext {
return {
traceId: this.traceId,
spanId: this.id,
options: 0,
traceState: this.traceState
};
}

/** No-op implementation of this method. */
get duration(): number {
return 0;
}

/** No-op implementation of this method. */
addAttribute(key: string, value: string|number|boolean) {}

/** No-op implementation of this method. */
addAnnotation(
description: string, attributes?: types.Attributes, timestamp = 0) {}

/** No-op implementation of this method. */
addLink(
traceId: string, spanId: string, type: types.LinkType,
attributes?: types.Attributes) {}

/** No-op implementation of this method. */
addMessageEvent(
type: types.MessageEventType, id: string, timestamp = 0,
uncompressedSize?: number, compressedSize?: number) {}

/** No-op implementation of this method. */
setStatus(code: types.CanonicalCode, message?: string) {}

/** No-op implementation of this method. */
start() {
this.startedLocal = true;
}

/** No-op implementation of this method. */
end(): void {
this.startedLocal = false;
this.endedLocal = true;
}

/** No-op implementation of this method. */
truncate() {}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
/**
* Copyright 2019, 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 logger from '../../../common/console-logger';
import * as types from '../types';
import {NoRecordSpanBase} from './no-record-span-base';

/** Implementation for the Span class that does not record trace events. */
export class NoRecordSpan extends NoRecordSpanBase implements types.Span {
private root: types.RootSpan;
/** set isRootSpan = false */
readonly isRootSpan = false;

/**
* Constructs a new NoRecordSpanImpl instance.
* @param root
*/
constructor(root: types.RootSpan) {
super();
this.root = root;
this.logger = this.root.logger || logger.logger();
this.parentSpanId = root.id;
}

/** Gets trace id of no-record span. */
get traceId(): string {
return this.root.traceId;
}

get traceState(): string {
return this.root.traceState;
}

/** No-op implementation of this method. */
start() {}

/** No-op implementation of this method. */
end() {}
}
10 changes: 6 additions & 4 deletions packages/opencensus-core/src/trace/model/tracer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ 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';

Expand Down Expand Up @@ -124,11 +125,10 @@ export class CoreTracer implements types.Tracer {
startRootSpan<T>(
options: types.TraceOptions, fn: (root: types.RootSpan) => T): T {
return this.contextManager.runAndReturn((root) => {
let newRoot = null;
if (this.active) {
let propagatedSample = null;

// if there is a context propagation, keep the decistion
// if there is a context propagation, keep the decision
if (options && options.spanContext) {
if (options.spanContext.options) {
propagatedSample =
Expand All @@ -148,12 +148,14 @@ export class CoreTracer implements types.Tracer {
if (sampleDecision) {
this.currentRootSpan = aRoot;
aRoot.start();
newRoot = aRoot;
return fn(aRoot);
}
} else {
this.logger.debug('Tracer is inactive, can\'t start new RootSpan');
}
return fn(newRoot);
const noRecordRootSpan = new NoRecordRootSpan(this, options);
this.currentRootSpan = noRecordRootSpan;
return fn(noRecordRootSpan);
});
}

Expand Down
Loading

0 comments on commit 1140dd3

Please sign in to comment.