couchbase
Version:
The official Couchbase Node.js Client Library.
734 lines (733 loc) • 23.8 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.WrappedSpan = exports.ObservableRequestHandler = void 0;
const observability_1 = require("./observability");
const observabilitytypes_1 = require("./observabilitytypes");
const observabilityutilities_1 = require("./observabilityutilities");
const observabilityutilities_2 = require("./observabilityutilities");
const utilities_1 = require("./utilities");
/**
* @internal
*/
class ObservableRequestHandlerTracerImpl {
constructor(opType, observabilityInstruments, parentSpan) {
this._startTime = (0, observabilityutilities_1.timeInputToHiResTime)();
this._opType = opType;
this._serviceName = (0, observabilitytypes_1.serviceNameFromOpType)(opType);
this._tracer = observabilityInstruments.tracer;
this._getClusterLabelsFn = observabilityInstruments.clusterLabelsFn;
this._wrappedSpan = new WrappedSpan(this._serviceName, this._opType, this._tracer, this._startTime, parentSpan);
this._processedCoreSpan = false;
}
/**
* @internal
*/
get clusterName() {
return this._wrappedSpan.clusterName;
}
/**
* @internal
*/
get clusterUUID() {
return this._wrappedSpan.clusterUUID;
}
/**
* @internal
*/
get wrapperSpanName() {
return this._wrappedSpan.name;
}
/**
* @internal
*/
get wrappedSpan() {
return this._wrappedSpan;
}
/**
* @internal
*/
maybeAddEncodingSpan(encodeFn) {
return this._wrappedSpan.maybeAddEncodingSpan(encodeFn);
}
/**
* @internal
*/
maybeCreateEncodingSpan(encodeFn) {
return this._wrappedSpan.maybeCreateEncodingSpan(encodeFn);
}
/**
* @internal
*/
end() {
this._endTime = (0, observabilityutilities_1.timeInputToHiResTime)();
this._wrappedSpan.end(this._endTime);
}
/**
* @internal
*/
endWithError(error) {
if (!this._processedCoreSpan && this._getClusterLabelsFn) {
const clusterLabels = this._getClusterLabelsFn();
this._wrappedSpan.setClusterLabels(clusterLabels);
this._wrappedSpan.setRetryAttribute(0);
}
if (error) {
this._wrappedSpan.setStatus({
code: observabilitytypes_1.SpanStatusCode.ERROR,
message: (0, utilities_1.getErrorMessage)(error),
});
}
else {
this._wrappedSpan.setStatus({
code: observabilitytypes_1.SpanStatusCode.ERROR,
});
}
this.end();
}
/**
* @internal
*/
reset(opType, parentSpan, withError = false) {
if (withError) {
this.endWithError();
}
this._startTime = (0, observabilityutilities_1.timeInputToHiResTime)();
this._opType = opType;
this._serviceName = (0, observabilitytypes_1.serviceNameFromOpType)(opType);
this._endTime = undefined;
this._wrappedSpan = new WrappedSpan(this._serviceName, this._opType, this._tracer, this._startTime, parentSpan);
}
/**
* @internal
*/
processCoreSpan(coreSpan) {
if (!coreSpan) {
return;
}
this._wrappedSpan.processCoreSpan(coreSpan);
this._processedCoreSpan = true;
}
/**
* @internal
*/
setRequestHttpAttributes(options) {
const opAttrs = (0, observabilityutilities_1.getAttributesForHttpOpType)(this._opType, options);
for (const [k, v] of Object.entries(opAttrs)) {
this._wrappedSpan.setAttribute(k, v);
}
}
/**
* @internal
*/
setRequestKeyValueAttributes(cppDocId, durability) {
const opAttrs = (0, observabilityutilities_1.getAttributesForKeyValueOpType)(this._opType, cppDocId, durability);
for (const [k, v] of Object.entries(opAttrs)) {
this._wrappedSpan.setAttribute(k, v);
}
// TODO: meter attrs
}
}
/**
* @internal
*/
class ObservableRequestHandlerNoOpTracerImpl {
constructor(opType, _observabilityInstruments, _parentSpan) {
this._opType = opType;
}
/**
* @internal
*/
get clusterName() {
return undefined;
}
/**
* @internal
*/
get clusterUUID() {
return undefined;
}
/**
* @internal
*/
get wrapperSpanName() {
return '';
}
/**
* @internal
*/
get wrappedSpan() {
return this._wrappedSpan;
}
/**
* @internal
*/
end() { }
/**
* @internal
*/
endWithError(_error) { }
/**
* @internal
*/
maybeAddEncodingSpan(encodeFn) {
return encodeFn();
}
/**
* @internal
*/
maybeCreateEncodingSpan(encodeFn) {
return encodeFn();
}
/**
* @internal
*/
processCoreSpan(_coreSpan) { }
/**
* @internal
*/
reset(_opType, _parentSpan, _withError = false) { }
/**
* @internal
*/
setRequestHttpAttributes(_options) { }
/**
* @internal
*/
setRequestKeyValueAttributes(_cppDocId, _durability) { }
}
/**
* @internal
*/
class ObservableRequestHandlerNoOpMeterImpl {
constructor(opType, _observabilityInstruments) {
this._opType = opType;
}
/**
* @internal
*/
setRequestKeyValueAttributes(_cppDocId, _durability) { }
/**
* @internal
*/
setRequestHttpAttributes(_options) { }
/**
* @internal
*/
processEnd(_clusterName, _clusterUUID, _error) { }
/**
* @internal
*/
reset(_opType, _clusterName, _clusterUUID, _error) { }
}
/**
* @internal
*/
class ObservableRequestHandlerMeterImpl {
constructor(opType, observabilityInstruments) {
this._attrs = {};
this._opType = opType;
this._serviceName = (0, observabilitytypes_1.serviceNameFromOpType)(opType);
this._meter = observabilityInstruments.meter;
this._getClusterLabelsFn = observabilityInstruments.clusterLabelsFn;
this._startTime = (0, observabilityutilities_1.timeInputToHiResTime)();
this._ignoreTopLevelOp = Object.values(observabilitytypes_1.DatastructureOp).includes(opType);
}
/**
* @internal
*/
setRequestKeyValueAttributes(cppDocId) {
this._attrs = (0, observabilityutilities_1.getAttributesForKeyValueOpType)(this._opType, cppDocId);
}
/**
* @internal
*/
setRequestHttpAttributes(options) {
this._attrs = (0, observabilityutilities_1.getAttributesForHttpOpType)(this._opType, options);
}
/**
* @internal
*/
processEnd(clusterName, clusterUUID, error) {
const endTime = (0, observabilityutilities_1.timeInputToHiResTime)();
const duration = (0, observabilityutilities_2.hiResTimeToMicros)((0, observabilityutilities_2.getHiResTimeDelta)(this._startTime, endTime));
if (this._ignoreTopLevelOp) {
return;
}
// Get cluster labels if not provided
if (this._getClusterLabelsFn &&
(clusterName === undefined || clusterUUID === undefined)) {
const clusterLabels = this._getClusterLabelsFn();
clusterName = clusterName || clusterLabels.clusterName;
clusterUUID = clusterUUID || clusterLabels.clusterUUID;
}
// Build final tags
const tags = {
...this._attrs,
};
if (Object.keys(tags).length === 0) {
tags[observabilitytypes_1.OpAttributeName.SystemName] = 'couchbase';
tags[observabilitytypes_1.OpAttributeName.Service] = this._serviceName;
tags[observabilitytypes_1.OpAttributeName.OperationName] = this._getOpName();
tags[observabilitytypes_1.OpAttributeName.ReservedUnit] = observabilitytypes_1.OpAttributeName.ReservedUnitSeconds;
}
else {
tags[observabilitytypes_1.OpAttributeName.ReservedUnit] = observabilitytypes_1.OpAttributeName.ReservedUnitSeconds;
}
// Add cluster labels
if (clusterName) {
tags[observabilitytypes_1.OpAttributeName.ClusterName] = clusterName;
}
if (clusterUUID) {
tags[observabilitytypes_1.OpAttributeName.ClusterUUID] = clusterUUID;
}
// Handle error
if (error) {
this._handleError(tags, error);
}
// Record the duration
this._meter
.valueRecorder(observabilitytypes_1.OpAttributeName.MeterNameOpDuration, tags)
.recordValue(duration);
}
/**
* @internal
*/
reset(opType, clusterName, clusterUUID, error) {
this.processEnd(clusterName, clusterUUID, error);
this._opType = opType;
this._serviceName = (0, observabilitytypes_1.serviceNameFromOpType)(opType);
this._startTime = (0, observabilityutilities_1.timeInputToHiResTime)();
this._attrs = {};
}
_getOpName() {
return this._opType.toString();
}
_handleError(tags, error) {
var _a;
if (typeof error !== 'object' || error === null) {
tags[observabilitytypes_1.OpAttributeName.ErrorType] = '_OTHER';
return;
}
let errorType = (error === null || error === void 0 ? void 0 : error.name) || ((_a = error === null || error === void 0 ? void 0 : error.constructor) === null || _a === void 0 ? void 0 : _a.name) || '_OTHER';
if (errorType.endsWith('Error')) {
errorType = errorType.slice(0, -5);
}
tags[observabilitytypes_1.OpAttributeName.ErrorType] = errorType;
}
}
/**
* @internal
*/
class ObservableRequestHandler {
constructor(opType, observabilityInstruments, parentSpan) {
this._opType = opType;
this._requestHasEnded = false;
if (!observabilityInstruments.tracer ||
observabilityInstruments.tracer instanceof observability_1.NoOpTracer) {
this._tracerImpl = new ObservableRequestHandlerNoOpTracerImpl(opType, observabilityInstruments, parentSpan);
}
else {
this._tracerImpl = new ObservableRequestHandlerTracerImpl(opType, observabilityInstruments, parentSpan);
}
if (observabilityInstruments.meter instanceof observability_1.NoOpMeter) {
this._meterImpl = new ObservableRequestHandlerNoOpMeterImpl(opType, observabilityInstruments);
}
else {
this._meterImpl = new ObservableRequestHandlerMeterImpl(opType, observabilityInstruments);
}
}
/**
* @internal
*/
get opType() {
return this._opType;
}
/**
* @internal
*/
get requestHasEnded() {
return this._requestHasEnded;
}
/**
* @internal
*/
get wrapperSpanName() {
return this._tracerImpl.wrapperSpanName;
}
/**
* @internal
*/
get wrappedSpan() {
return this._tracerImpl.wrappedSpan;
}
/**
* @internal
*/
get clusterName() {
return this._tracerImpl.clusterName;
}
/**
* @internal
*/
get clusterUUID() {
return this._tracerImpl.clusterUUID;
}
/**
* @internal
*/
end() {
this._requestHasEnded = true;
this._tracerImpl.end();
this._meterImpl.processEnd(this.clusterName, this.clusterUUID);
}
/**
* @internal
*/
endWithError(error) {
this._requestHasEnded = true;
this._tracerImpl.endWithError(error);
this._meterImpl.processEnd(this.clusterName, this.clusterUUID, error);
}
/**
* @internal
*/
maybeAddEncodingSpan(encodeFn) {
return this._tracerImpl.maybeAddEncodingSpan(encodeFn);
}
/**
* @internal
*/
maybeCreateEncodingSpan(encodeFn) {
return this._tracerImpl.maybeCreateEncodingSpan(encodeFn);
}
/**
* @internal
*/
reset(opType, parentSpan, withError = false) {
this._opType = opType;
this._tracerImpl.reset(opType, parentSpan, withError);
this._meterImpl.reset(opType, this.clusterName, this.clusterUUID, withError ? undefined : undefined);
}
/**
* @internal
*/
processCoreSpan(coreSpan) {
this._tracerImpl.processCoreSpan(coreSpan);
}
/**
* @internal
*/
setRequestHttpAttributes(options) {
this._tracerImpl.setRequestHttpAttributes(options);
this._meterImpl.setRequestHttpAttributes(options);
}
/**
* @internal
*/
setRequestKeyValueAttributes(cppDocId, durability) {
this._tracerImpl.setRequestKeyValueAttributes(cppDocId, durability);
this._meterImpl.setRequestKeyValueAttributes(cppDocId, durability);
}
}
exports.ObservableRequestHandler = ObservableRequestHandler;
/**
* @internal
*/
class WrappedEncodingSpan {
constructor(span, endTime) {
this.span = span;
this.endTime = endTime;
}
}
/**
* @internal
*/
class WrappedSpan {
constructor(serviceName, opType, tracer, startTime, parentSpan) {
this._serviceName = serviceName;
this._opType = opType;
this._tracer = tracer;
this._parentSpan = parentSpan;
// requestSpan's parent needs to be a RequestSpan
const pSpan = this._parentSpan instanceof WrappedSpan
? this._parentSpan._requestSpan
: this._parentSpan;
this._hasMultipleEncodingSpans = this._opType == observabilitytypes_1.KeyValueOp.MutateIn;
this._requestSpan = this._createRequestSpan(this._opType, startTime, pSpan);
this._endedEncodingSpans = false;
this._startTime = startTime;
this._endTimeWatermark = (0, observabilityutilities_1.timeInputToHiResTime)();
}
/**
* @internal
*/
get clusterName() {
return this._clusterName;
}
/**
* @internal
*/
get clusterUUID() {
return this._clusterUUID;
}
/**
* @internal
*/
get name() {
return this._opType;
}
/**
* @internal
*/
get requestSpan() {
return this._requestSpan;
}
/**
* @internal
*/
maybeAddEncodingSpan(encodeFn) {
if (!this._hasMultipleEncodingSpans) {
return encodeFn();
}
if (!this._encodingSpans) {
this._encodingSpans = [];
}
const encodingSpan = this._tracer.requestSpan(observabilitytypes_1.OpAttributeName.EncodingSpanName, this._requestSpan, (0, observabilityutilities_1.timeInputToHiResTime)());
encodingSpan.setAttribute(observabilitytypes_1.OpAttributeName.SystemName, 'couchbase');
try {
const encodedOutput = encodeFn();
return encodedOutput;
}
catch (e) {
encodingSpan.setStatus({
code: observabilitytypes_1.SpanStatusCode.ERROR,
message: (0, utilities_1.getErrorMessage)(e),
});
throw e;
}
finally {
// we wait to set the end time until we process the underylying
// core span so that we can add the cluster_[name|uuid] attributes
this._encodingSpans.push(new WrappedEncodingSpan(encodingSpan, (0, observabilityutilities_1.timeInputToHiResTime)()));
}
}
/**
* @internal
*/
maybeCreateEncodingSpan(encodeFn) {
// mutateIn can have multiple encoding spans, should use maybeAddEncodingSpan instead
if (this._hasMultipleEncodingSpans) {
return encodeFn();
}
const encodingSpan = this._tracer.requestSpan(observabilitytypes_1.OpAttributeName.EncodingSpanName, this._requestSpan, (0, observabilityutilities_1.timeInputToHiResTime)());
encodingSpan.setAttribute(observabilitytypes_1.OpAttributeName.SystemName, 'couchbase');
try {
const encodedOutput = encodeFn();
return encodedOutput;
}
catch (e) {
encodingSpan.setStatus({
code: observabilitytypes_1.SpanStatusCode.ERROR,
message: (0, utilities_1.getErrorMessage)(e),
});
throw e;
}
finally {
// we wait to set the end time until we process the underylying
// core span so that we can add the cluster_[name|uuid] attributes
this._encodingSpan = new WrappedEncodingSpan(encodingSpan, (0, observabilityutilities_1.timeInputToHiResTime)());
}
}
/**
* @internal
*/
maybeUpdateEndTimeWatermark(endTime) {
this._endTimeWatermark = (0, observabilityutilities_1.getLatestTime)(this._endTimeWatermark, endTime);
}
/**
* @internal
*/
_setAttributeOnAllSpans(attrName, attrValue, skip_encoding_span) {
this.setAttribute(attrName, attrValue);
if (this._parentSpan instanceof WrappedSpan) {
this._parentSpan._setAttributeOnAllSpans(attrName, attrValue);
}
if (skip_encoding_span) {
return;
}
if (this._encodingSpans instanceof Array) {
for (const span of this._encodingSpans) {
span.span.setAttribute(attrName, attrValue);
}
}
else if (this._encodingSpan) {
this._encodingSpan.span.setAttribute(attrName, attrValue);
}
}
/**
* @internal
*/
_maybeSetAttributeFromCoreSpan(coreSpan, attrName, skip_encoding_span) {
var _a;
let attrVal;
const coreSpanAttr = (_a = coreSpan.attributes) === null || _a === void 0 ? void 0 : _a[attrName];
if (coreSpanAttr) {
attrVal = coreSpanAttr;
}
if (typeof attrVal === 'undefined' && attrName == 'retries') {
attrVal = 0;
}
if (typeof attrVal !== 'undefined') {
let filteredAttrName;
if ((0, observabilitytypes_1.isCppAttribute)(attrName)) {
filteredAttrName = observabilitytypes_1.CppOpAttributeNameToOpAttributeNameMap[attrName];
}
else {
filteredAttrName = attrName;
}
this._setAttributeOnAllSpans(filteredAttrName, attrVal, skip_encoding_span);
}
}
/**
* @internal
*/
_buildCoreSpans(coreSpans, parentSpan) {
for (const span of coreSpans) {
if (span.name === observabilitytypes_1.OpAttributeName.DispatchSpanName) {
this._buildDispatchCoreSpan(span, parentSpan);
}
else {
this._buildNonDispatchCoreSpan(span, parentSpan);
}
}
}
/**
* @internal
*/
_buildDispatchCoreSpan(coreSpan, parentSpan) {
var _a;
const pSpan = parentSpan instanceof WrappedSpan ? parentSpan.requestSpan : parentSpan;
const latestStartTime = (0, observabilityutilities_1.getLatestTime)(this._startTime, coreSpan.start);
const newSpan = this._createRequestSpan(coreSpan.name, latestStartTime, pSpan);
const children = coreSpan.children;
if (children) {
this._buildCoreSpans(children, newSpan);
}
for (const [attrName, attrVal] of Object.entries((_a = coreSpan.attributes) !== null && _a !== void 0 ? _a : {})) {
newSpan.setAttribute(attrName, attrVal);
}
const coreSpanEndTime = (0, observabilityutilities_1.getCoreSpanEndTime)(coreSpan.end);
this._endTimeWatermark = (0, observabilityutilities_1.getLatestTime)(this._endTimeWatermark, coreSpanEndTime);
newSpan.end(coreSpanEndTime);
}
/**
* @internal
*/
_buildNonDispatchCoreSpan(coreSpan, parentSpan) {
var _a;
const pSpan = parentSpan instanceof WrappedSpan ? parentSpan.requestSpan : parentSpan;
const latestStartTime = (0, observabilityutilities_1.getLatestTime)(this._startTime, coreSpan.start);
const newSpan = new WrappedSpan(this._serviceName, coreSpan.name, this._tracer, latestStartTime, pSpan);
const children = coreSpan.children;
if (children) {
this._buildCoreSpans(children, newSpan);
}
for (const [attrName, attrVal] of Object.entries((_a = coreSpan.attributes) !== null && _a !== void 0 ? _a : {})) {
newSpan.setAttribute(attrName, attrVal);
}
const coreSpanEndTime = (0, observabilityutilities_1.getCoreSpanEndTime)(coreSpan.end);
this._endTimeWatermark = (0, observabilityutilities_1.getLatestTime)(this._endTimeWatermark, coreSpanEndTime);
newSpan.end(coreSpanEndTime);
}
/**
* @internal
*/
_endEncodingSpans() {
if (this._endedEncodingSpans) {
return;
}
this._endedEncodingSpans = true;
if (this._encodingSpans instanceof Array) {
for (const span of this._encodingSpans) {
span.span.end(span.endTime);
}
}
else if (this._encodingSpan) {
this._encodingSpan.span.end(this._encodingSpan.endTime);
}
}
/**
* @internal
*/
processCoreSpan(coreSpan) {
this._maybeSetAttributeFromCoreSpan(coreSpan, observabilitytypes_1.CppOpAttributeName.ClusterName);
this._maybeSetAttributeFromCoreSpan(coreSpan, observabilitytypes_1.CppOpAttributeName.ClusterUUID);
this._maybeSetAttributeFromCoreSpan(coreSpan, observabilitytypes_1.CppOpAttributeName.RetryCount, true);
// we can now end encoding spans since we have added (or atleast attempted) the cluster_[name|uuid] attributes
this._endEncodingSpans();
if (coreSpan.children) {
this._buildCoreSpans(coreSpan.children, this);
}
}
/**
* @internal
*/
setClusterLabels(clusterLabels) {
if (clusterLabels.clusterName) {
this._setAttributeOnAllSpans(observabilitytypes_1.OpAttributeName.ClusterName, clusterLabels.clusterName);
}
if (clusterLabels.clusterUUID) {
this._setAttributeOnAllSpans(observabilitytypes_1.OpAttributeName.ClusterUUID, clusterLabels.clusterUUID);
}
}
/**
* @internal
*/
setRetryAttribute(retrCount = 0) {
this._setAttributeOnAllSpans(observabilitytypes_1.OpAttributeName.RetryCount, retrCount, true);
}
/**
* @internal
*/
addEvent() { }
/**
* @internal
*/
end(endTime) {
this._endTimeWatermark = (0, observabilityutilities_1.getLatestTime)(this._endTimeWatermark, endTime);
if (this._parentSpan && this._parentSpan instanceof WrappedSpan) {
this._parentSpan.maybeUpdateEndTimeWatermark(this._endTimeWatermark);
}
this._endEncodingSpans();
this._requestSpan.end(this._endTimeWatermark);
}
/**
* @internal
*/
setAttribute(key, value) {
if (key === observabilitytypes_1.OpAttributeName.ClusterName) {
this._clusterName = value;
}
else if (key === observabilitytypes_1.OpAttributeName.ClusterUUID) {
this._clusterUUID = value;
}
this._requestSpan.setAttribute(key, value);
}
/**
* @internal
*/
setStatus(status) {
var _a;
(_a = this._requestSpan) === null || _a === void 0 ? void 0 : _a.setStatus(status);
}
/**
* @internal
*/
_createRequestSpan(spanName, startTime, parentSpan) {
if (typeof startTime === 'undefined') {
startTime = (0, observabilityutilities_1.timeInputToHiResTime)();
}
return this._tracer.requestSpan(spanName, parentSpan, startTime);
}
}
exports.WrappedSpan = WrappedSpan;