@opencensus/core
Version:
OpenCensus is a toolkit for collecting application performance and behavior data.
324 lines • 11.7 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.Span = void 0;
/**
* 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.
*/
const noop_logger_1 = require("../../common/noop-logger");
const clock_1 = require("../../internal/clock");
const util_1 = require("../../internal/util");
const no_record_span_1 = require("./no-record/no-record-span");
const types = require("./types");
const STATUS_OK = {
code: types.CanonicalCode.OK,
};
/** Defines a base model for spans. */
class Span {
/** Constructs a new Span instance. */
constructor(tracer, parent) {
/** Indicates if this span was started */
this.startedLocal = false;
/** Indicates if this span was ended */
this.endedLocal = false;
/** An object to log information to */
this.logger = noop_logger_1.noopLogger;
/** A set of attributes, each in the format [KEY]:[VALUE] */
this.attributes = {};
/** A text annotation with a set of attributes. */
this.annotations = [];
/** An event describing a message sent/received between Spans */
this.messageEvents = [];
/** Pointers from the current span to another span */
this.links = [];
/** If the parent span is in another process. */
this.remoteParent = false;
/** The resource name of the span */
this.name = 'span';
/** Kind of span. */
this.kind = types.SpanKind.UNSPECIFIED;
/** A final status for this span */
this.status = STATUS_OK;
/** Trace Parameters */
this.activeTraceParams = {};
/** The number of dropped attributes. */
this.droppedAttributesCount = 0;
/** The number of dropped links. */
this.droppedLinksCount = 0;
/** The number of dropped annotations. */
this.droppedAnnotationsCount = 0;
/** The number of dropped message events. */
this.droppedMessageEventsCount = 0;
this.tracer = tracer;
this.className = this.constructor.name;
this.id = util_1.randomSpanId();
this.spansLocal = [];
if (parent) {
this.root = parent.root;
this.parentSpan = parent;
this.activeTraceParams = this.root.activeTraceParams;
}
else {
this.root = this;
}
this.logger = (this.root && this.root.logger) || this.logger;
}
/** Returns whether a span is root or not. */
isRootSpan() {
return false;
}
/** Gets the trace ID. */
get traceId() {
return this.root.traceId;
}
/** Gets the trace state */
get traceState() {
return this.root.traceState;
}
/**
* Gets the ID of the parent span.
* RootSpan doesn't have a parentSpan but it override this method.
*/
get parentSpanId() {
if (!this.parentSpan) {
return 'no-parent';
}
return this.parentSpan.id;
}
/** Indicates if span was started. */
get started() {
return this.startedLocal;
}
/** Indicates if span was ended. */
get ended() {
return this.endedLocal;
}
/**
* Gives a timestamp that indicates the span's start time in RFC3339 UTC
* "Zulu" format.
*/
get startTime() {
if (!this.clock) {
this.logger.debug('calling startTime() on null clock');
return new Date();
}
return this.clock.startTime;
}
/** Recursively gets the descendant spans. */
allDescendants() {
return this.spansLocal.reduce((acc, cur) => {
acc.push(cur);
const desc = cur.allDescendants();
acc = acc.concat(desc);
return acc;
}, []);
}
/** The list of immediate child spans. */
get spans() {
return this.spansLocal;
}
/** The number of direct children. */
get numberOfChildren() {
return this.spansLocal.length;
}
/**
* Gives a timestamp that indicates the span's end time in RFC3339 UTC
* "Zulu" format.
*/
get endTime() {
if (!this.clock) {
this.logger.debug('calling endTime() on null clock');
return new Date();
}
return this.clock.endTime;
}
/**
* Gets the duration of the clock.
*/
get duration() {
if (!this.clock) {
this.logger.debug('calling duration() on null clock');
return 0;
}
return this.clock.duration;
}
/** Gives the TraceContext of the span. */
get spanContext() {
return {
traceId: this.traceId,
spanId: this.id,
options: 0x1,
traceState: this.traceState,
};
}
/**
* Adds an atribute to the span.
* @param key Describes the value added.
* @param value The result of an operation. If the value is a typeof object
* it has to be JSON.stringify-able, cannot contain circular dependencies.
*/
addAttribute(key, value) {
if (this.attributes[key]) {
delete this.attributes[key];
}
if (Object.keys(this.attributes).length >=
this.activeTraceParams.numberOfAttributesPerSpan) {
this.droppedAttributesCount++;
const attributeKeyToDelete = Object.keys(this.attributes).shift();
if (attributeKeyToDelete) {
delete this.attributes[attributeKeyToDelete];
}
}
const serializedValue = typeof value === 'object' ? JSON.stringify(value) : value;
this.attributes[key] = serializedValue;
}
/**
* Adds an annotation to the span.
* @param description Describes the event.
* @param attributes A set of attributes on the annotation.
* @param timestamp A time, in milliseconds. Defaults to Date.now()
*/
addAnnotation(description, attributes = {}, timestamp = Date.now()) {
if (this.annotations.length >=
this.activeTraceParams.numberOfAnnontationEventsPerSpan) {
this.annotations.shift();
this.droppedAnnotationsCount++;
}
this.annotations.push({ description, attributes, timestamp });
}
/**
* Adds a link to the span.
* @param traceId The trace ID for a trace within a project.
* @param spanId The span ID for a span within a trace.
* @param type The relationship of the current span relative to the linked.
* @param attributes A set of attributes on the link.
*/
addLink(traceId, spanId, type, attributes = {}) {
if (this.links.length >= this.activeTraceParams.numberOfLinksPerSpan) {
this.links.shift();
this.droppedLinksCount++;
}
this.links.push({ traceId, spanId, type, attributes });
}
/**
* Adds a message event to the span.
* @param type The type of message event.
* @param id An identifier for the message event.
* @param timestamp A time in milliseconds. Defaults to Date.now()
* @param uncompressedSize The number of uncompressed bytes sent or received
* @param compressedSize The number of compressed bytes sent or received. If
* zero or undefined, assumed to be the same size as uncompressed.
*/
addMessageEvent(type, id, timestamp = Date.now(), uncompressedSize, compressedSize) {
if (this.messageEvents.length >=
this.activeTraceParams.numberOfMessageEventsPerSpan) {
this.messageEvents.shift();
this.droppedMessageEventsCount++;
}
this.messageEvents.push({
type,
id,
timestamp,
uncompressedSize,
compressedSize,
});
}
/**
* Sets a status to the span.
* @param code The canonical status code.
* @param message optional A developer-facing error message.
*/
setStatus(code, message) {
this.status = { code, message };
}
/** Starts the span. */
start() {
if (this.started) {
this.logger.debug('calling %s.start() on already started %s %o', this.className, this.className, { id: this.id, name: this.name, type: this.kind });
return;
}
// start child span's clock from root's current time to preserve integrity.
if (this.parentSpan) {
this.clock = new clock_1.Clock(this.parentSpan.clock.currentDate);
}
else {
this.clock = new clock_1.Clock();
}
this.startedLocal = true;
this.logger.debug('starting %s %o', this.className, {
traceId: this.traceId,
id: this.id,
name: this.name,
parentSpanId: this.parentSpanId,
traceState: this.traceState,
});
if (this.isRootSpan())
this.tracer.setCurrentRootSpan(this);
this.tracer.onStartSpan(this);
}
/** Ends the span and all of its children, recursively. */
end() {
if (this.ended) {
this.logger.debug('calling %s.end() on already ended %s %o', this.className, this.className, { id: this.id, name: this.name, type: this.kind });
return;
}
if (!this.started) {
this.logger.error('calling %s.end() on un-started %s %o', this.className, this.className, { id: this.id, name: this.name, type: this.kind });
return;
}
this.startedLocal = false;
this.endedLocal = true;
this.clock.end();
// TODO: Should ending a span force its children to end by default?
// Issue: https://github.com/open-telemetry/opentelemetry-node/issues/4
for (const span of this.spansLocal) {
if (!span.ended && span.started) {
span.truncate();
}
}
this.tracer.onEndSpan(this);
}
/** Forces the span to end. */
truncate() {
this.end();
this.logger.debug('truncating %s %o', this.className, {
id: this.id,
name: this.name,
});
}
/**
* Starts a new child span.
* @param [options] A SpanOptions object to start a child span.
*/
startChildSpan(options) {
if (this.ended) {
this.logger.debug('calling %s.startSpan() on ended %s %o', this.className, this.className, { id: this.id, name: this.name, kind: this.kind });
return new no_record_span_1.NoRecordSpan(this.tracer);
}
if (!this.started) {
this.logger.debug('calling %s.startSpan() on un-started %s %o', this.className, this.className, { id: this.id, name: this.name, kind: this.kind });
return new no_record_span_1.NoRecordSpan(this.tracer);
}
const child = new Span(this.tracer, this);
if (options && options.name)
child.name = options.name;
if (options && options.kind)
child.kind = options.kind;
child.start();
this.spansLocal.push(child);
return child;
}
}
exports.Span = Span;
//# sourceMappingURL=span.js.map