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

Commit 64081eb

Browse files
crdgonzalezcadraffensperger
authored andcommitted
Consolidating Span and RootSpan to allow Spans to recursively have children. (#78)
1 parent beb6d8e commit 64081eb

9 files changed

Lines changed: 159 additions & 118 deletions

File tree

packages/opencensus-web-core/src/exporters/noop_exporter.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,11 +17,11 @@
1717
import * as webTypes from '@opencensus/web-types';
1818

1919
export class NoopExporter implements webTypes.Exporter {
20-
publish(roots: webTypes.RootSpan[]): Promise<number | string | void> {
20+
publish(roots: webTypes.Span[]): Promise<number | string | void> {
2121
return Promise.resolve();
2222
}
2323

24-
onStartSpan(root: webTypes.RootSpan) {}
24+
onStartSpan(root: webTypes.Span) {}
2525

26-
onEndSpan(root: webTypes.RootSpan) {}
26+
onEndSpan(root: webTypes.Span) {}
2727
}

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

Lines changed: 1 addition & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -19,13 +19,7 @@ import { randomTraceId } from '../../common/id-util';
1919
import { Span } from './span';
2020

2121
/** Simple mock root span for use in use tests. */
22-
export class RootSpan extends Span implements webTypes.RootSpan {
23-
/** A list of child spans. */
24-
spans: Span[] = [];
25-
26-
/** A number of children. */
27-
private numberOfChildrenLocal: number;
28-
22+
export class RootSpan extends Span {
2923
constructor(
3024
/** Trace associated with this root span. */
3125
private readonly tracer: webTypes.Tracer,
@@ -47,41 +41,6 @@ export class RootSpan extends Span implements webTypes.RootSpan {
4741
} else {
4842
this.traceId = randomTraceId();
4943
}
50-
51-
this.numberOfChildrenLocal = 0;
52-
}
53-
54-
/** Gets the number of child span created for this span. */
55-
get numberOfChildren(): number {
56-
return this.numberOfChildrenLocal;
57-
}
58-
59-
/**
60-
* Starts a new child span in the root span.
61-
* @param nameOrOptions Span name string or SpanOptions object.
62-
* @param kind Span kind if not using options object.
63-
* @param parentSpanId Span parent ID.
64-
*/
65-
startChildSpan(
66-
nameOrOptions?: string | webTypes.SpanOptions,
67-
kind?: webTypes.SpanKind
68-
): Span {
69-
this.numberOfChildrenLocal++;
70-
const child = new Span();
71-
child.traceId = this.traceId;
72-
child.traceState = this.traceState;
73-
74-
const spanName =
75-
typeof nameOrOptions === 'object' ? nameOrOptions.name : nameOrOptions;
76-
const spanKind =
77-
typeof nameOrOptions === 'object' ? nameOrOptions.kind : kind;
78-
if (spanName) child.name = spanName;
79-
if (spanKind) child.kind = spanKind;
80-
81-
child.start();
82-
child.parentSpanId = this.id;
83-
this.spans.push(child);
84-
return child;
8544
}
8645

8746
start() {

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

Lines changed: 52 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,9 @@ export class Span implements webTypes.Span {
2727
constructor(
2828
/** The ID of this span. Defaults to a random span ID. */
2929
public id = randomSpanId()
30-
) {}
30+
) {
31+
this.numberOfChildrenLocal = 0;
32+
}
3133

3234
/** If the parent span is in another process. */
3335
remoteParent = false;
@@ -77,6 +79,12 @@ export class Span implements webTypes.Span {
7779
/** Start time of the span as measured by the browser performance clock. */
7880
startPerfTime = 0;
7981

82+
/** A list of child spans. */
83+
spans: Span[] = [];
84+
85+
/** A number of children. */
86+
private numberOfChildrenLocal: number;
87+
8088
/**
8189
* Number of dropped attributes. This is always zero because OpenCensus web
8290
* does not implement attribute dropping (but may be done by agent on export).
@@ -113,6 +121,11 @@ export class Span implements webTypes.Span {
113121
/** End time of the span as measured by the browser performance clock. */
114122
endPerfTime = 0;
115123

124+
/** Gets the number of child span created for this span. */
125+
get numberOfChildren(): number {
126+
return this.numberOfChildrenLocal;
127+
}
128+
116129
/** End time of the span as a Date. */
117130
get endTime(): Date {
118131
return getDateForPerfTime(this.endPerfTime);
@@ -138,6 +151,44 @@ export class Span implements webTypes.Span {
138151
};
139152
}
140153

154+
/** Recursively gets the descendant spans. */
155+
allDescendants(): webTypes.Span[] {
156+
return this.spans.reduce((acc: webTypes.Span[], cur) => {
157+
acc.push(cur);
158+
const desc = cur.allDescendants();
159+
acc = acc.concat(desc);
160+
return acc;
161+
}, []);
162+
}
163+
164+
/**
165+
* Starts a new child span.
166+
* @param nameOrOptions Span name string or SpanOptions object.
167+
* @param kind Span kind if not using options object.
168+
* @param parentSpanId Span parent ID.
169+
*/
170+
startChildSpan(
171+
nameOrOptions?: string | webTypes.SpanOptions,
172+
kind?: webTypes.SpanKind
173+
): Span {
174+
this.numberOfChildrenLocal++;
175+
const child = new Span();
176+
child.traceId = this.traceId;
177+
child.traceState = this.traceState;
178+
179+
const spanName =
180+
typeof nameOrOptions === 'object' ? nameOrOptions.name : nameOrOptions;
181+
const spanKind =
182+
typeof nameOrOptions === 'object' ? nameOrOptions.kind : kind;
183+
if (spanName) child.name = spanName;
184+
if (spanKind) child.kind = spanKind;
185+
186+
child.start();
187+
child.parentSpanId = this.id;
188+
this.spans.push(child);
189+
return child;
190+
}
191+
141192
/**
142193
* Adds an attribute to the span.
143194
* @param key Describes the value added.

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

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ const NO_HEADERS_PROPAGATION = new NoHeadersPropagation();
2525
/** Tracer manages the current root span and trace header propagation. */
2626
export class Tracer implements webTypes.Tracer {
2727
/** Get and set the currentRootSpan of the tracer. */
28-
currentRootSpan: RootSpan = new RootSpan(this);
28+
currentRootSpan: Span = new RootSpan(this);
2929

3030
/**
3131
* A sampler used to make trace sample decisions. In the case of
@@ -80,24 +80,21 @@ export class Tracer implements webTypes.Tracer {
8080
* @param fn Callback function
8181
* @returns The callback return
8282
*/
83-
startRootSpan<T>(
84-
options: webTypes.TraceOptions,
85-
fn: (root: RootSpan) => T
86-
): T {
83+
startRootSpan<T>(options: webTypes.TraceOptions, fn: (root: Span) => T): T {
8784
this.currentRootSpan = new RootSpan(this, options);
8885
this.currentRootSpan.start();
8986
return fn(this.currentRootSpan);
9087
}
9188

9289
/** Notifies listeners of the span start. */
93-
onStartSpan(root: webTypes.RootSpan) {
90+
onStartSpan(root: webTypes.Span) {
9491
for (const listener of this.eventListeners) {
9592
listener.onStartSpan(root);
9693
}
9794
}
9895

9996
/** Notifies listeners of the span end. */
100-
onEndSpan(root: webTypes.RootSpan) {
97+
onEndSpan(root: webTypes.Span) {
10198
for (const listener of this.eventListeners) {
10299
listener.onEndSpan(root);
103100
}

packages/opencensus-web-core/test/test-root-span.ts

Lines changed: 27 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
* limitations under the License.
1515
*/
1616

17-
import { SpanKind, SpanOptions } from '@opencensus/web-types';
17+
import { SpanKind } from '@opencensus/web-types';
1818
import { RootSpan } from '../src/trace/model/root-span';
1919
import { Tracer } from '../src/trace/model/tracer';
2020

@@ -51,40 +51,6 @@ describe('RootSpan', () => {
5151
});
5252
});
5353

54-
describe('startChildSpan', () => {
55-
it('appends to spans list based on root id and state', () => {
56-
root.traceId = '00000000000000000000000000000001';
57-
root.traceState = 'a=b';
58-
59-
const childSpan = root.startChildSpan('child1', SpanKind.CLIENT);
60-
61-
expect(childSpan.traceId).toBe('00000000000000000000000000000001');
62-
expect(childSpan.traceState).toBe('a=b');
63-
expect(childSpan.name).toBe('child1');
64-
expect(childSpan.kind).toBe(SpanKind.CLIENT);
65-
expect(childSpan.parentSpanId).toBe(root.id);
66-
expect(root.spans).toEqual([childSpan]);
67-
});
68-
69-
it('allows specifying SpanOptions object with name and kind', () => {
70-
root.traceId = '00000000000000000000000000000001';
71-
root.traceState = 'a=b';
72-
73-
const spanOptions: SpanOptions = {
74-
name: 'child1',
75-
kind: SpanKind.CLIENT,
76-
};
77-
const childSpan = root.startChildSpan(spanOptions);
78-
79-
expect(childSpan.traceId).toBe('00000000000000000000000000000001');
80-
expect(childSpan.traceState).toBe('a=b');
81-
expect(childSpan.name).toBe('child1');
82-
expect(childSpan.kind).toBe(SpanKind.CLIENT);
83-
expect(childSpan.parentSpanId).toBe(root.id);
84-
expect(root.spans).toEqual([childSpan]);
85-
});
86-
});
87-
8854
describe('start', () => {
8955
it('sets start time and calls onStartSpan for tracer', () => {
9056
spyOn(tracer, 'onStartSpan');
@@ -121,18 +87,36 @@ describe('RootSpan', () => {
12187
});
12288
});
12389

124-
describe('get numberOfChildren()', () => {
125-
it('should get numberOfChildren from rootspan instance', () => {
126-
root = new RootSpan(tracer);
90+
describe('nested spans', () => {
91+
it('should get nested spans from rootspan instance', () => {
12792
root.start();
12893
expect(root.numberOfChildren).toBe(0);
129-
root.startChildSpan('spanName', SpanKind.UNSPECIFIED);
94+
const child1 = root.startChildSpan('child1', SpanKind.UNSPECIFIED);
13095
expect(root.numberOfChildren).toBe(1);
96+
expect(child1.numberOfChildren).toBe(0);
97+
const child2 = root.startChildSpan('child2', SpanKind.UNSPECIFIED);
98+
expect(root.numberOfChildren).toBe(2);
99+
const grandchild1 = child1.startChildSpan({
100+
name: 'grandchild1',
101+
kind: SpanKind.UNSPECIFIED,
102+
});
103+
expect(root.numberOfChildren).toBe(2);
104+
expect(child1.numberOfChildren).toBe(1);
105+
expect(child2.numberOfChildren).toBe(0);
106+
expect(grandchild1.numberOfChildren).toBe(0);
107+
108+
expect(child1).toBe(root.spans[0]);
109+
expect(child2).toBe(root.spans[1]);
110+
expect(grandchild1.parentSpanId).toBe(child1.id);
111+
112+
expect(child1.spans.length).toBe(1);
113+
expect(grandchild1).toBe(child1.spans[0]);
114+
115+
expect(child2.spans.length).toBe(0);
116+
expect(child2.spans.length).toBe(0);
117+
expect(grandchild1.spans.length).toBe(0);
131118

132-
for (let i = 0; i < 10; i++) {
133-
root.startChildSpan('spanName' + i, SpanKind.UNSPECIFIED);
134-
}
135-
expect(root.numberOfChildren).toBe(11);
119+
expect(root.allDescendants().length).toBe(3);
136120
});
137121
});
138122
});

packages/opencensus-web-core/test/test-span.ts

Lines changed: 54 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,12 @@
1414
* limitations under the License.
1515
*/
1616

17-
import { LinkType, MessageEventType } from '@opencensus/web-types';
17+
import {
18+
LinkType,
19+
MessageEventType,
20+
SpanKind,
21+
SpanOptions,
22+
} from '@opencensus/web-types';
1823
import { Span } from '../src/trace/model/span';
1924
import { mockGetterOrValue, restoreGetterOrValue } from './util';
2025

@@ -181,4 +186,52 @@ describe('Span', () => {
181186
]);
182187
});
183188
});
189+
190+
describe('get numberOfChildren()', () => {
191+
it('should get numberOfChildren from span instance', () => {
192+
span.start();
193+
expect(span.numberOfChildren).toBe(0);
194+
span.startChildSpan('spanName', SpanKind.UNSPECIFIED);
195+
expect(span.numberOfChildren).toBe(1);
196+
197+
for (let i = 0; i < 10; i++) {
198+
span.startChildSpan('spanName' + i, SpanKind.UNSPECIFIED);
199+
}
200+
expect(span.numberOfChildren).toBe(11);
201+
});
202+
});
203+
204+
describe('startChildSpan', () => {
205+
it('appends to spans list based on root id and state', () => {
206+
span.traceId = '00000000000000000000000000000001';
207+
span.traceState = 'a=b';
208+
209+
const childSpan = span.startChildSpan('child1', SpanKind.CLIENT);
210+
211+
expect(childSpan.traceId).toBe('00000000000000000000000000000001');
212+
expect(childSpan.traceState).toBe('a=b');
213+
expect(childSpan.name).toBe('child1');
214+
expect(childSpan.kind).toBe(SpanKind.CLIENT);
215+
expect(childSpan.parentSpanId).toBe(span.id);
216+
expect(span.spans).toEqual([childSpan]);
217+
});
218+
219+
it('allows specifying SpanOptions object with name and kind', () => {
220+
span.traceId = '00000000000000000000000000000001';
221+
span.traceState = 'a=b';
222+
223+
const spanOptions: SpanOptions = {
224+
name: 'child1',
225+
kind: SpanKind.CLIENT,
226+
};
227+
const childSpan = span.startChildSpan(spanOptions);
228+
229+
expect(childSpan.traceId).toBe('00000000000000000000000000000001');
230+
expect(childSpan.traceState).toBe('a=b');
231+
expect(childSpan.name).toBe('child1');
232+
expect(childSpan.kind).toBe(SpanKind.CLIENT);
233+
expect(childSpan.parentSpanId).toBe(span.id);
234+
expect(span.spans).toEqual([childSpan]);
235+
});
236+
});
184237
});

packages/opencensus-web-types/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
"scripts": {
88
"build": "npm run compile",
99
"clean": "rimraf build/*",
10-
"copytypes": "node scripts/copy-types.js 'v0.0.11' && npm run fix",
10+
"copytypes": "node scripts/copy-types.js 'abd41dbc0f2f5836980ae9d79048a81d58a9956f' && npm run fix",
1111
"check": "gts check",
1212
"compile": "tsc -p .",
1313
"fix": "gts fix",

packages/opencensus-web-types/src/exporters/types.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ export interface Exporter extends modelTypes.SpanEventListener {
2525
* Sends a list of root spans to the service.
2626
* @param rootSpans A list of root spans to publish.
2727
*/
28-
publish(rootSpans: modelTypes.RootSpan[]): Promise<number | string | void>;
28+
publish(rootSpans: modelTypes.Span[]): Promise<number | string | void>;
2929
}
3030

3131
/**

0 commit comments

Comments
 (0)