UNPKG

elastic-apm-node

Version:

The official Elastic APM agent for Node.js

240 lines (209 loc) 6.63 kB
/* * Copyright Elasticsearch B.V. and other contributors where applicable. * Licensed under the BSD 2-Clause License; you may not use this file except in * compliance with the BSD 2-Clause License. */ 'use strict'; const STRATEGY_EXACT_MATCH = 'exact_match'; const STRATEGY_SAME_KIND = 'same_kind'; class SpanCompression { constructor(agent) { this._bufferedSpan = null; this._agent = agent; this.timestamp = null; this.duration = 0; this.composite = { count: 0, sum: 0, compression_strategy: null, }; } setBufferedSpan(span) { this._bufferedSpan = span; } getBufferedSpan() { return this._bufferedSpan; } // Compares two spans and returns which compression strategy to use // or returns false if the second span can't be compressed into the // first. // // @param Span compositeSpan // @param Span toCompressSpan // @returns boolean|String _getCompressionStrategy(compositeSpan, toCompressSpan) { if ( !this._isEnabled() || !compositeSpan._serviceTarget || !toCompressSpan._serviceTarget ) { return false; } const isSameKind = this.isSameKind(compositeSpan, toCompressSpan); if (!isSameKind) { return false; } let strategy = STRATEGY_SAME_KIND; if (compositeSpan.name === toCompressSpan.name) { strategy = STRATEGY_EXACT_MATCH; } return strategy; } isSameKind(compositeSpan, toCompressSpan) { return ( compositeSpan.type === toCompressSpan.type && compositeSpan.subtype === toCompressSpan.subtype && compositeSpan._serviceTarget.type === toCompressSpan._serviceTarget.type && compositeSpan._serviceTarget.name === toCompressSpan._serviceTarget.name ); } // Sets initial composite values or confirms strategy matches // // Returns true if spanToCompress can be compressed into compositeSpan, // returns false otherwise. // // @param Span compositeSpan // @param Span spanToCompress // @returns boolean _initCompressionStrategy(compositeSpan, spanToCompress) { if (!this.composite.compression_strategy) { // If no strategy is set, check if strategizable or not. If so, // set initial values. If not, bail. this.composite.compression_strategy = this._getCompressionStrategy( compositeSpan, spanToCompress, ); if (!this.composite.compression_strategy) { return false; } // set initial composite context values this.timestamp = compositeSpan.timestamp; this.composite.count = 1; this.composite.sum = compositeSpan._duration; } else { // if so, compare with the compression strat and bail if mismatch const strat = this._getCompressionStrategy(compositeSpan, spanToCompress); if (strat !== this.composite.compression_strategy) { return false; } } return true; } // Attempts to compression the second span into the first // // @param Span compositeSpan // @param Span spanToCompress // @return boolean tryToCompress(compositeSpan, spanToCompress) { if (!this._isEnabled()) { return false; } // sets initial compression strategy value. returns // false if not compression eligable if (!this._initCompressionStrategy(compositeSpan, spanToCompress)) { return false; } const isAlreadyComposite = this.isComposite(); const canBeCompressed = isAlreadyComposite ? this.tryToCompressComposite(compositeSpan, spanToCompress) : this.tryToCompressRegular(compositeSpan, spanToCompress); if (!canBeCompressed) { return false; } if (!isAlreadyComposite) { this.composite.count = 1; this.composite.sum = compositeSpan._duration; } this.composite.count++; this.composite.sum += spanToCompress._duration; this.duration = (spanToCompress._endTimestamp - compositeSpan.timestamp) / 1000; return true; } tryToCompressRegular(compositeSpan, spanToCompress) { if (!this.isSameKind(compositeSpan, spanToCompress)) { return false; } if (compositeSpan.name === spanToCompress.name) { if ( this.duration <= this._agent._conf.spanCompressionExactMatchMaxDuration * 1000 && spanToCompress._duration <= this._agent._conf.spanCompressionExactMatchMaxDuration * 1000 ) { this.composite.compression_strategy = STRATEGY_EXACT_MATCH; return true; } return false; } if ( this.duration <= this._agent._conf.spanCompressionSameKindMaxDuration * 1000 && spanToCompress._duration <= this._agent._conf.spanCompressionSameKindMaxDuration * 1000 ) { this.composite.compression_strategy = STRATEGY_SAME_KIND; compositeSpan.name = this._spanNameFromCompositeSpan(compositeSpan); return true; } return false; } tryToCompressComposite(compositeSpan, spanToCompress) { switch (this.composite.compression_strategy) { case STRATEGY_EXACT_MATCH: return ( this.isSameKind(compositeSpan, spanToCompress) && compositeSpan.name === spanToCompress.name && spanToCompress._duration <= this._agent._conf.spanCompressionExactMatchMaxDuration * 1000 ); case STRATEGY_SAME_KIND: return ( this.isSameKind(compositeSpan, spanToCompress) && spanToCompress._duration <= this._agent._conf.spanCompressionSameKindMaxDuration * 1000 ); } } _spanNameFromCompositeSpan(compositeSpan) { const prefix = 'Calls to '; const serviceTarget = compositeSpan._serviceTarget; if (!serviceTarget.type) { if (!serviceTarget.name) { return prefix + 'unknown'; } else { return prefix + serviceTarget.name; } } else if (!serviceTarget.name) { return prefix + serviceTarget.type; } else { return prefix + serviceTarget.type + '/' + serviceTarget.name; } } _isEnabled() { return this._agent._conf.spanCompressionEnabled; } isCompositeSameKind() { return this.composite.compression_strategy === STRATEGY_SAME_KIND; } isComposite() { return this.composite.count > 1; } // Encodes/Serializes composite span properties // @return Object encode() { return { compression_strategy: this.composite.compression_strategy, count: this.composite.count, sum: this.composite.sum, }; } } module.exports = { SpanCompression, constants: { STRATEGY_EXACT_MATCH, STRATEGY_SAME_KIND, }, };