UNPKG

couchbase

Version:

The official Couchbase Node.js Client Library.

734 lines (733 loc) 23.8 kB
"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;