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

Commit 1140dd3

Browse files
authored
Add a NoRecord RootSpan and Span (#389)
* 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
1 parent 58eff0f commit 1140dd3

8 files changed

Lines changed: 401 additions & 10 deletions

File tree

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ All notable changes to this project will be documented in this file.
1515
- Add support for recording HTTP stats.
1616
- Enforce `--strictNullChecks` and `--noUnusedLocals` Compiler Options on [opencensus-instrumentation-http], [opencensus-instrumentation-grpc] and [opencensus-propagation-tracecontext] packages.
1717
- Enforce `--strictNullChecks` and `--noUnusedLocals` Compiler Options on [opencensus-exporter-zipkin] packages.
18+
- Add NoRecordRootSpan, NoRecordSpan and NoRecordSpanBase.
1819

1920
## 0.0.9 - 2019-02-12
2021
- Add Metrics API.
Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
/**
2+
* Copyright 2019, 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 types from '../types';
20+
import {NoRecordSpan} from './no-record-span';
21+
import {NoRecordSpanBase} from './no-record-span-base';
22+
23+
/** Implementation for the RootSpan class that does not record trace events. */
24+
export class NoRecordRootSpan extends NoRecordSpanBase implements
25+
types.RootSpan {
26+
/** A tracer object */
27+
private tracer: types.Tracer;
28+
/** Its trace ID. */
29+
private traceIdLocal: string;
30+
/** Its trace state. */
31+
private traceStateLocal: types.TraceState;
32+
/** set isRootSpan = true */
33+
readonly isRootSpan = true;
34+
35+
/**
36+
* Constructs a new NoRecordRootSpanImpl instance.
37+
* @param tracer A tracer object.
38+
* @param context A trace options object to build the no-record root span.
39+
*/
40+
constructor(tracer: types.Tracer, context?: types.TraceOptions) {
41+
super();
42+
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;
51+
}
52+
this.kind =
53+
context && context.kind ? context.kind : types.SpanKind.UNSPECIFIED;
54+
this.logger = this.tracer.logger || logger.logger();
55+
}
56+
57+
/** No-op implementation of this method. */
58+
get spans(): types.Span[] {
59+
return [];
60+
}
61+
62+
/** No-op implementation of this method. */
63+
get traceId(): string {
64+
return this.traceIdLocal;
65+
}
66+
67+
/** No-op implementation of this method. */
68+
get traceState(): types.TraceState {
69+
return this.traceStateLocal;
70+
}
71+
72+
/** No-op implementation of this method. */
73+
get numberOfChildren(): number {
74+
return 0;
75+
}
76+
77+
/** No-op implementation of this method. */
78+
start() {
79+
super.start();
80+
}
81+
82+
/** No-op implementation of this method. */
83+
end() {
84+
super.end();
85+
}
86+
87+
/**
88+
* Starts a new child span in the noop root span.
89+
* @param name Span name.
90+
* @param kind Span kind.
91+
* @param parentSpanId Span parent ID.
92+
*/
93+
startChildSpan(
94+
nameOrOptions?: string|types.SpanOptions, kind?: types.SpanKind,
95+
parentSpanId?: string): types.Span {
96+
const newSpan = new NoRecordSpan(this);
97+
let spanName;
98+
let spanKind;
99+
if (typeof nameOrOptions === 'object') {
100+
spanName = nameOrOptions.name;
101+
spanKind = nameOrOptions.kind;
102+
} else {
103+
spanName = nameOrOptions;
104+
spanKind = kind;
105+
}
106+
107+
if (spanName) {
108+
newSpan.name = spanName;
109+
}
110+
if (spanKind) {
111+
newSpan.kind = spanKind;
112+
}
113+
newSpan.start();
114+
return newSpan;
115+
}
116+
}
Lines changed: 149 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,149 @@
1+
/**
2+
* Copyright 2019, 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+
import {Logger} from '../../../common/types';
17+
import {randomSpanId} from '../../../internal/util';
18+
import * as configTypes from '../../config/types';
19+
import * as types from '../types';
20+
21+
const STATUS_OK = {
22+
code: types.CanonicalCode.OK
23+
};
24+
25+
/** Implementation for the SpanBase class that does not record trace events. */
26+
export abstract class NoRecordSpanBase implements types.Span {
27+
/** Indicates if this span was started */
28+
private startedLocal = false;
29+
/** Indicates if this span was ended */
30+
private endedLocal = false;
31+
/** Indicates if this span was forced to end */
32+
// @ts-ignore
33+
private truncated = false;
34+
/** The Span ID of this span */
35+
readonly id: string;
36+
/** An object to log information to */
37+
logger: Logger;
38+
/** A set of attributes, each in the format [KEY]:[VALUE] */
39+
attributes: types.Attributes = {};
40+
/** A text annotation with a set of attributes. */
41+
annotations: types.Annotation[] = [];
42+
/** An event describing a message sent/received between Spans */
43+
messageEvents: types.MessageEvent[] = [];
44+
/** Pointers from the current span to another span */
45+
links: types.Link[] = [];
46+
/** If the parent span is in another process. */
47+
remoteParent: boolean;
48+
/** The span ID of this span's parent. If it's a root span, must be empty */
49+
parentSpanId: string = null;
50+
/** The resource name of the span */
51+
name: string = null;
52+
/** Kind of span. */
53+
kind: types.SpanKind = types.SpanKind.UNSPECIFIED;
54+
/** A final status for this span */
55+
status: types.Status = STATUS_OK;
56+
/** set isRootSpan */
57+
abstract get isRootSpan(): boolean;
58+
/** Trace Parameters */
59+
activeTraceParams: configTypes.TraceParams;
60+
61+
/** The number of dropped attributes. */
62+
droppedAttributesCount = 0;
63+
/** The number of dropped links. */
64+
droppedLinksCount = 0;
65+
/** The number of dropped annotations. */
66+
droppedAnnotationsCount = 0;
67+
/** The number of dropped message events. */
68+
droppedMessageEventsCount = 0;
69+
70+
/** Constructs a new SpanBaseModel instance. */
71+
constructor() {
72+
this.id = randomSpanId();
73+
}
74+
75+
/** Gets the trace ID. */
76+
abstract get traceId(): string;
77+
78+
/** Gets the trace state */
79+
abstract get traceState(): types.TraceState;
80+
81+
/** Indicates if span was started. */
82+
get started(): boolean {
83+
return this.startedLocal;
84+
}
85+
86+
/** Indicates if span was ended. */
87+
get ended(): boolean {
88+
return this.endedLocal;
89+
}
90+
91+
/** No-op implementation of this method. */
92+
get startTime(): Date {
93+
return new Date();
94+
}
95+
96+
/** No-op implementation of this method. */
97+
get endTime(): Date {
98+
return new Date();
99+
}
100+
101+
/** Gives the TraceContext of the span. */
102+
get spanContext(): types.SpanContext {
103+
return {
104+
traceId: this.traceId,
105+
spanId: this.id,
106+
options: 0,
107+
traceState: this.traceState
108+
};
109+
}
110+
111+
/** No-op implementation of this method. */
112+
get duration(): number {
113+
return 0;
114+
}
115+
116+
/** No-op implementation of this method. */
117+
addAttribute(key: string, value: string|number|boolean) {}
118+
119+
/** No-op implementation of this method. */
120+
addAnnotation(
121+
description: string, attributes?: types.Attributes, timestamp = 0) {}
122+
123+
/** No-op implementation of this method. */
124+
addLink(
125+
traceId: string, spanId: string, type: types.LinkType,
126+
attributes?: types.Attributes) {}
127+
128+
/** No-op implementation of this method. */
129+
addMessageEvent(
130+
type: types.MessageEventType, id: string, timestamp = 0,
131+
uncompressedSize?: number, compressedSize?: number) {}
132+
133+
/** No-op implementation of this method. */
134+
setStatus(code: types.CanonicalCode, message?: string) {}
135+
136+
/** No-op implementation of this method. */
137+
start() {
138+
this.startedLocal = true;
139+
}
140+
141+
/** No-op implementation of this method. */
142+
end(): void {
143+
this.startedLocal = false;
144+
this.endedLocal = true;
145+
}
146+
147+
/** No-op implementation of this method. */
148+
truncate() {}
149+
}
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
/**
2+
* Copyright 2019, 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 logger from '../../../common/console-logger';
18+
import * as types from '../types';
19+
import {NoRecordSpanBase} from './no-record-span-base';
20+
21+
/** Implementation for the Span class that does not record trace events. */
22+
export class NoRecordSpan extends NoRecordSpanBase implements types.Span {
23+
private root: types.RootSpan;
24+
/** set isRootSpan = false */
25+
readonly isRootSpan = false;
26+
27+
/**
28+
* Constructs a new NoRecordSpanImpl instance.
29+
* @param root
30+
*/
31+
constructor(root: types.RootSpan) {
32+
super();
33+
this.root = root;
34+
this.logger = this.root.logger || logger.logger();
35+
this.parentSpanId = root.id;
36+
}
37+
38+
/** Gets trace id of no-record span. */
39+
get traceId(): string {
40+
return this.root.traceId;
41+
}
42+
43+
get traceState(): string {
44+
return this.root.traceState;
45+
}
46+
47+
/** No-op implementation of this method. */
48+
start() {}
49+
50+
/** No-op implementation of this method. */
51+
end() {}
52+
}

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

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ import {Propagation} from '../propagation/types';
2323
import {SamplerBuilder, TraceParamsBuilder} from '../sampler/sampler';
2424
import * as samplerTypes from '../sampler/types';
2525

26+
import {NoRecordRootSpan} from './no-record/no-record-root-span';
2627
import {RootSpan} from './root-span';
2728
import * as types from './types';
2829

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

131-
// if there is a context propagation, keep the decistion
131+
// if there is a context propagation, keep the decision
132132
if (options && options.spanContext) {
133133
if (options.spanContext.options) {
134134
propagatedSample =
@@ -148,12 +148,14 @@ export class CoreTracer implements types.Tracer {
148148
if (sampleDecision) {
149149
this.currentRootSpan = aRoot;
150150
aRoot.start();
151-
newRoot = aRoot;
151+
return fn(aRoot);
152152
}
153153
} else {
154154
this.logger.debug('Tracer is inactive, can\'t start new RootSpan');
155155
}
156-
return fn(newRoot);
156+
const noRecordRootSpan = new NoRecordRootSpan(this, options);
157+
this.currentRootSpan = noRecordRootSpan;
158+
return fn(noRecordRootSpan);
157159
});
158160
}
159161

0 commit comments

Comments
 (0)