Skip to content

Commit

Permalink
feat(sdk): add ReadableSpan (jaegertracing#150)
Browse files Browse the repository at this point in the history
* feat(sdk): add ReadableSpan

* fix: use toReadableSpan for toString method

* fix: rebase master

* fix: review comments

* fix: add comment
  • Loading branch information
mayurkale22 committed Aug 9, 2019
1 parent bf4d5fd commit 0e76e7e
Show file tree
Hide file tree
Showing 4 changed files with 194 additions and 46 deletions.
68 changes: 30 additions & 38 deletions packages/opentelemetry-basic-tracer/src/Span.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,55 +16,57 @@

import * as types from '@opentelemetry/types';
import { performance } from 'perf_hooks';
import { SpanKind, SpanContext } from '@opentelemetry/types';
import { ReadableSpan } from './export/ReadableSpan';

/**
* This class represents a span.
*/
export class Span implements types.Span {
private readonly _spanContext: types.SpanContext;
export class Span implements types.Span, ReadableSpan {
private readonly _tracer: types.Tracer;
private readonly _parentId?: string;
private readonly _kind: types.SpanKind;
private readonly _attributes: types.Attributes = {};
private readonly _links: types.Link[] = [];
private readonly _events: types.Event[] = [];
private _status: types.Status = {
// Below properties are included to implement ReadableSpan for export
// purposes but are not intended to be written-to directly.
readonly spanContext: types.SpanContext;
readonly kind: types.SpanKind;
readonly parentSpanId?: string;
readonly attributes: types.Attributes = {};
readonly links: types.Link[] = [];
readonly events: types.Event[] = [];
readonly startTime: number;
name: string;
status: types.Status = {
code: types.CanonicalCode.OK,
};
private _name: string;
endTime = 0;
private _ended = false;
private _startTime: number;
private _endTime = 0;

/** Constructs a new Span instance. */
constructor(
parentTracer: types.Tracer,
spanName: string,
spanContext: SpanContext,
kind: SpanKind,
spanContext: types.SpanContext,
kind: types.SpanKind,
parentSpanId?: string,
startTime?: number
) {
this._tracer = parentTracer;
this._name = spanName;
this._spanContext = spanContext;
this._parentId = parentSpanId;
this._kind = kind;
this._startTime = startTime || performance.now();
this.name = spanName;
this.spanContext = spanContext;
this.parentSpanId = parentSpanId;
this.kind = kind;
this.startTime = startTime || performance.now();
}

tracer(): types.Tracer {
return this._tracer;
}

context(): types.SpanContext {
return this._spanContext;
return this.spanContext;
}

setAttribute(key: string, value: unknown): this {
if (this._isSpanEnded()) return this;
this._attributes[key] = value;
this.attributes[key] = value;
return this;
}

Expand All @@ -77,51 +79,41 @@ export class Span implements types.Span {

addEvent(name: string, attributes?: types.Attributes): this {
if (this._isSpanEnded()) return this;
this._events.push({ name, attributes });
this.events.push({ name, attributes });
return this;
}

addLink(spanContext: types.SpanContext, attributes?: types.Attributes): this {
if (this._isSpanEnded()) return this;
this._links.push({ spanContext, attributes });
this.links.push({ spanContext, attributes });
return this;
}

setStatus(status: types.Status): this {
if (this._isSpanEnded()) return this;
this._status = status;
this.status = status;
return this;
}

updateName(name: string): this {
if (this._isSpanEnded()) return this;
this._name = name;
this.name = name;
return this;
}

end(endTime?: number): void {
if (this._isSpanEnded()) return;
this._ended = true;
this._endTime = endTime || performance.now();
this.endTime = endTime || performance.now();
// @todo: record or export the span
}

isRecordingEvents(): boolean {
return true;
}

toString() {
const json = JSON.stringify({
traceId: this._spanContext.traceId,
spanId: this._spanContext.spanId,
parentId: this._parentId,
name: this._name,
kind: this._kind,
status: this._status,
startTime: this._startTime,
endTime: this._endTime,
});
return `Span${json}`;
toReadableSpan(): ReadableSpan {
return this;
}

private _isSpanEnded(): boolean {
Expand Down
37 changes: 37 additions & 0 deletions packages/opentelemetry-basic-tracer/src/export/ReadableSpan.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
/**
* Copyright 2019, OpenTelemetry 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
*
* https://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 {
SpanKind,
Status,
Attributes,
Link,
Event,
SpanContext,
} from '@opentelemetry/types';

export interface ReadableSpan {
readonly name: string;
readonly kind: SpanKind;
readonly spanContext: SpanContext;
readonly parentSpanId?: string;
readonly startTime: number;
readonly endTime: number;
readonly status: Status;
readonly attributes: Attributes;
readonly links: Link[];
readonly events: Event[];
}
1 change: 1 addition & 0 deletions packages/opentelemetry-basic-tracer/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
*/

export * from './export/ExportResult';
export * from './export/ReadableSpan';
export * from './export/SpanExporter';
export * from './types';
export * from './BasicTracer';
Expand Down
134 changes: 126 additions & 8 deletions packages/opentelemetry-basic-tracer/test/Span.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -88,20 +88,138 @@ describe('Span', () => {
});
});

it('should return toString', () => {
it('should return ReadableSpan', () => {
const parentId = '5c1c63257de34c67';
const span = new Span(
tracer,
name,
'my-span',
spanContext,
SpanKind.SERVER,
parentId,
100
SpanKind.INTERNAL,
parentId
);
const context = span.context();

const readableSpan = span.toReadableSpan();
assert.strictEqual(readableSpan.name, 'my-span');
assert.strictEqual(readableSpan.kind, SpanKind.INTERNAL);
assert.strictEqual(readableSpan.parentSpanId, parentId);
assert.strictEqual(readableSpan.spanContext.traceId, spanContext.traceId);
assert.deepStrictEqual(readableSpan.status, {
code: CanonicalCode.OK,
});
assert.deepStrictEqual(readableSpan.attributes, {});
assert.deepStrictEqual(readableSpan.links, []);
assert.deepStrictEqual(readableSpan.events, []);
});

it('should return ReadableSpan with attributes', () => {
const span = new Span(tracer, 'my-span', spanContext, SpanKind.CLIENT);
span.setAttribute('attr1', 'value1');
let readableSpan = span.toReadableSpan();
assert.deepStrictEqual(readableSpan.attributes, { attr1: 'value1' });

span.setAttributes({ attr2: 123, attr1: false });
readableSpan = span.toReadableSpan();
assert.deepStrictEqual(readableSpan.attributes, {
attr1: false,
attr2: 123,
});

span.end();
// shouldn't add new attribute
span.setAttribute('attr3', 'value3');
readableSpan = span.toReadableSpan();
assert.deepStrictEqual(readableSpan.attributes, {
attr1: false,
attr2: 123,
});
});

it('should return ReadableSpan with links', () => {
const span = new Span(tracer, 'my-span', spanContext, SpanKind.CLIENT);
span.addLink(spanContext);
let readableSpan = span.toReadableSpan();
assert.strictEqual(readableSpan.links.length, 1);
assert.deepStrictEqual(readableSpan.links, [
{
attributes: undefined,
spanContext: {
spanId: '6e0c63257de34c92',
traceId: 'd4cda95b652f4a1592b449d5929fda1b',
traceOptions: 1,
},
},
]);

span.addLink(spanContext, { attr1: 'value', attr2: 123, attr3: true });
readableSpan = span.toReadableSpan();
assert.strictEqual(readableSpan.links.length, 2);
assert.deepStrictEqual(readableSpan.links, [
{
attributes: undefined,
spanContext,
},
{
attributes: { attr1: 'value', attr2: 123, attr3: true },
spanContext,
},
]);

span.end();
// shouldn't add new link
span.addLink(spanContext);
readableSpan = span.toReadableSpan();
assert.strictEqual(readableSpan.links.length, 2);
});

it('should return ReadableSpan with events', () => {
const span = new Span(tracer, 'my-span', spanContext, SpanKind.CLIENT);
span.addEvent('sent');
let readableSpan = span.toReadableSpan();
assert.strictEqual(readableSpan.events.length, 1);
assert.deepStrictEqual(readableSpan.events, [
{
attributes: undefined,
name: 'sent',
},
]);

span.addEvent('rev', { attr1: 'value', attr2: 123, attr3: true });
readableSpan = span.toReadableSpan();
assert.strictEqual(readableSpan.events.length, 2);
assert.deepStrictEqual(readableSpan.events, [
{
attributes: undefined,
name: 'sent',
},
{
attributes: {
attr1: 'value',
attr2: 123,
attr3: true,
},
name: 'rev',
},
]);

span.end();
// shouldn't add new event
span.addEvent('sent');
assert.strictEqual(readableSpan.events.length, 2);
readableSpan = span.toReadableSpan();
assert.strictEqual(readableSpan.events.length, 2);
});

it('should return ReadableSpan with new status', () => {
const span = new Span(tracer, name, spanContext, SpanKind.CLIENT);
span.setStatus({
code: CanonicalCode.PERMISSION_DENIED,
message: 'This is an error',
});
const readableSpan = span.toReadableSpan();
assert.strictEqual(
span.toString(),
`Span{"traceId":"${context.traceId}","spanId":"${context.spanId}","parentId":"${parentId}","name":"${name}","kind":1,"status":{"code":0},"startTime":100,"endTime":0}`
readableSpan.status.code,
CanonicalCode.PERMISSION_DENIED
);
assert.strictEqual(readableSpan.status.message, 'This is an error');
});
});

0 comments on commit 0e76e7e

Please sign in to comment.