UNPKG

@opentelemetry/core

Version:

OpenTelemetry Core provides constants and utilities shared by all OpenTelemetry SDK packages.

142 lines 5.46 kB
"use strict"; /* * Copyright The OpenTelemetry Authors * SPDX-License-Identifier: Apache-2.0 */ Object.defineProperty(exports, "__esModule", { value: true }); exports.TraceState = void 0; const validators_1 = require("../internal/validators"); const MAX_TRACE_STATE_ITEMS = 32; const MAX_TRACE_STATE_LEN = 512; const LIST_MEMBERS_SEPARATOR = ','; const LIST_MEMBER_KEY_VALUE_SPLITTER = '='; /** * TraceState must be a class and not a simple object type because of the spec * requirement (https://www.w3.org/TR/trace-context/#tracestate-field). * * Here is the list of allowed mutations: * - New key-value pair should be added into the beginning of the list * - The value of any key can be updated. Modified keys MUST be moved to the * beginning of the list. */ class TraceState { _length; _rawTraceState; _internalState; constructor(rawTraceState) { this._rawTraceState = typeof rawTraceState === 'string' ? rawTraceState : ''; this._length = this._rawTraceState.length; } set(key, value) { if (!(0, validators_1.validateKey)(key) || !(0, validators_1.validateValue)(value)) { return this; } const currState = this._getState(); const currValue = currState.get(key); // Get the new length depending if we already have a value or not // - for existing keys we add the difference between the length of the values // - for new keys is the key & value lenght plus // - +1 for the key/value splitter // - +1 for the separator if there are other keys let newLength = this._length; if (typeof currValue === 'string') { newLength += value.length - currValue.length; } else { newLength += key.length + value.length + (currState.size > 0 ? 2 : 1); } if (newLength > MAX_TRACE_STATE_LEN) { return this; } const newState = new Map(currState); newState.delete(key); newState.set(key, value); return this._fromState(newState, newLength); } unset(key) { const currState = this._getState(); const currValue = currState.get(key); // No need to create a new instance if the key does not exist if (typeof currValue !== 'string') { return this; } // Get the new length depending if we already have a value or not // - for existing keys we substract key and value length plus // - +1 for the key/value splitter // - +1 for the separator if there are other keys let newLength = this._length - (key.length + currValue.length + 1); if (currState.size > 1) { // remove separator from length if there's no key or only one. newLength = newLength - 1; } const newState = new Map(currState); newState.delete(key); return this._fromState(newState, newLength); } get(key) { const currState = this._getState(); return currState.get(key); } serialize() { // Maps put new entries at the end. We prepend the seralized entry // to get the right order according to the spec (updated members go 1st) let serialized = ''; let index = 0; for (const entry of this._getState()) { if (index > 0) { serialized = LIST_MEMBERS_SEPARATOR + serialized; } serialized = `${entry[0]}${LIST_MEMBER_KEY_VALUE_SPLITTER}${entry[1]}` + serialized; index++; } return serialized; } _getState() { if (this._internalState) { return this._internalState; } // Not parsed yet, lets do it const vendorMembers = this._rawTraceState.split(LIST_MEMBERS_SEPARATOR); // This Map will have the order reversed const vendorEntries = new Map(); let currentLength = 0; for (const member of vendorMembers) { const m = member.trim(); const idx = m.indexOf(LIST_MEMBER_KEY_VALUE_SPLITTER); if (idx === -1) { continue; } const key = m.slice(0, idx); const value = m.slice(idx + 1); if (!(0, validators_1.validateKey)(key) || !(0, validators_1.validateValue)(value)) { continue; } // Skip if adding the new member exceeds the length const futureLength = currentLength + m.length + (vendorEntries.size > 0 ? 1 : 0); if (futureLength > MAX_TRACE_STATE_LEN) { continue; } // All good, add it vendorEntries.set(key, value); currentLength = futureLength; // Check if we reached the max items if (vendorEntries.size >= MAX_TRACE_STATE_ITEMS) { break; } } // Now we set the length & the Map in the right order this._length = currentLength; this._internalState = new Map(Array.from(vendorEntries.entries()).reverse()); return this._internalState; } _fromState(state, length) { const traceState = Object.create(TraceState.prototype); traceState._internalState = state; traceState._length = length; return traceState; } } exports.TraceState = TraceState; //# sourceMappingURL=TraceState.js.map