From f8c1d58c8189e508182ff9f097c988386dbf8d4f Mon Sep 17 00:00:00 2001 From: Sergey Kleyman Date: Thu, 22 Jun 2023 09:10:30 +0300 Subject: [PATCH] Added SPAN_STACK_TRACE_MIN_DURATION configuration option (#996) * Set g_elasticApmDirectLogLevel* to default values so that they can be used even before configuration is parsed * Added documentation for configuration options * Added options to AllOptionsMetadata.php * Added SPAN_COMPRESSION_* option names to OptionNames.php * Added Span Compression options to Config/Snapshot.php * Added COMPRESSION_STRATEGY_* constants * Added implementation * Added Span Compression related options to src/ext/ConfigManager.c * Added Span Compression related options to src/ext/ConfigManager.h * Added Span Compression related options to src/ext/elastic_apm.c * Added Span Compression related options to tests/ElasticApmTests/ComponentTests/ConfigSettingTest.php * MySQLiTest: Disable Span Compression feature to have all the expected spans individually * PDOTest: Disable Span Compression feature to have all the expected spans individually * Added tests/ElasticApmTests/ComponentTests/SpanCompressionComponentTest.php * Clarified comment about "Disable Span Compression" * StackTraceComponentTest: Disable Span Compression feature to have all the expected spans individually * Added TracerUnitTestCaseBase::isCompatibleWithSpanCompression * InferredSpansBuilderTest: Disable Span Compression feature to have all the expected spans individually * StackTraceUnitTest: Disable Span Compression feature to have all the expected spans individually * Added tests/ElasticApmTests/UnitTests/SpanCompressionUnitTest.php * Added tests\ElasticApmTests\Util\AssertValidTrait::assertValidNonNullableString * Updated ElasticApmTests\Util\SpanDto * Updated SpanExpectations * Added optional dbgParamName for better diagnostics * Distinguish between plugin name and keywords Both name and keywords can be used to disable a plugin * Removed unused imports * Added 'internal-func' to names related to existing instrumentation (part 2) to distinguish from other instrumentation mechanisms (for example the upcoming AST processing based) * Added 'internal-func' to names related to existing instrumentation (part 3) to distinguish from other instrumentation mechanisms (for example the upcoming AST processing based) * Added 'internal-func' to names related to existing instrumentation (part 3) to distinguish from other instrumentation mechanisms (for example the upcoming AST processing based) * Added verifyExactArgsCount * Implemented WordPress instrumentation - part 1 * Added AST_PROCESS_* options to AllOptionsMetadata.php * Added option names to OptionNames.php * Added AST processing options to Config/Snapshot.php * Switch to span types wordpress_plugin and wordpress_theme * Fixed issue with assertDirectoryDoesNotExist assertDirectoryDoesNotExist that was added only in PHPUnit 9 and it does not exist in PHPUnit 8.5 that we still use when testing under older PHP versions * Removed unused imports * Added isStringViewSuffix * Added new .c files to src/ext/config.m4 * Added AST processing related options to src/ext/ConfigManager.c * Added AST processing related options to src/ext/ConfigManager.h * Added ELASTIC_APM_WORDPRESS_DIRECT_CALL_METHOD_SET_READY_TO_WRAP_FILTER_CALLBACKS* constants * Added AST processing related options to src/ext/elastic_apm.c * Added AST processing related API from extension (native part) to PHP part of the agent * Added AST processing related APIs to src/ext/elastic_apm_API.h * Added calls for AST processing from lifecycle stages * Instrumentation using AST processing (files src/ext/tracer_PHP_part.c|h) files src/ext/tracer_PHP_part.h and src/ext/tracer_PHP_part.c) * Removed duplicate isStringViewSuffix * Added streamZVal * Added ELASTIC_APM_PHP_TESTS_COMPARE_AST_CONVERTED_BACK_TO_SOURCE * Added AppCodeHostParams::setAgentOptionIfNotDefaultValue * Added AppCodeHostParams::setAgentOptions * Excluded WordPress mock source from static analysis * Instrumentation using AST processing (file tests/ElasticApmTests/ComponentTests/Util/ComponentTestCaseBase.php) * Added ELASTIC_APM_PHP_TESTS_COMPARE_AST_CONVERTED_BACK_TO_SOURCE (part 2) * Added AppCodeHostParams::setAgentOptionIfNotDefaultValue (part 2) * Moved ArrayUtil::append back to ArrayUtilForTests::append * Removed unused imports * Added AST processing related options to tests/ElasticApmTests/ComponentTests/ConfigSettingTest.php * Added testReferencesInArray * Added FileUtilForTests::createTempSubDir * Added missing import * Removed redundant local variable * Added clarifying comments * Reverted references of ELASTIC_APM_EMPTY_STRING_VIEW back to makeEmptyStringView() since ELASTIC_APM_EMPTY_STRING_VIEW is added in a later PR * Fixed incorrect merge * Reverted references of ELASTIC_APM_EMPTY_STRING_VIEW back to makeEmptyStringView() since ELASTIC_APM_EMPTY_STRING_VIEW is added in a later PR * Fixed incorrect merge * Added documentation for configuration options * Added options to AllOptionsMetadata.php * Added SPAN_COMPRESSION_* option names to OptionNames.php * Added Span Compression options to Config/Snapshot.php * Added COMPRESSION_STRATEGY_* constants * Added implementation * Added Span Compression related options to src/ext/ConfigManager.c * Added Span Compression related options to src/ext/ConfigManager.h * Added Span Compression related options to src/ext/elastic_apm.c * Added Span Compression related options to tests/ElasticApmTests/ComponentTests/ConfigSettingTest.php * MySQLiTest: Disable Span Compression feature to have all the expected spans individually * PDOTest: Disable Span Compression feature to have all the expected spans individually * Added tests/ElasticApmTests/ComponentTests/SpanCompressionComponentTest.php * Clarified comment about "Disable Span Compression" * StackTraceComponentTest: Disable Span Compression feature to have all the expected spans individually * Added TracerUnitTestCaseBase::isCompatibleWithSpanCompression * InferredSpansBuilderTest: Disable Span Compression feature to have all the expected spans individually * StackTraceUnitTest: Disable Span Compression feature to have all the expected spans individually * Added tests/ElasticApmTests/UnitTests/SpanCompressionUnitTest.php * Added tests\ElasticApmTests\Util\AssertValidTrait::assertValidNonNullableString * Updated ElasticApmTests\Util\SpanDto * Updated SpanExpectations * Removed unused imports * Extended span compression to spans without service target * Removed redundant comment * Clarified docs * Added workaround for false positives from static analysis * Simplified parallel-lint invocation * Fixed unit tests failing on PHP 7.2 * Fixed issue found by static analysis * Fixed formatting * Fixed formatting * Added --exclude ./tests/polyfills/ * Removed TestCaseBaseShim * Fixed failing unit test * Added ELASTIC_APM_PHP_TESTS_IS_LONG_RUN_MODE * Reduced number of datasets from dataProviderForTestReasonsCompressionStops * Run testOneCompressedSequence only in isLongRunMode * Added check for isLongRunMode dataProviderForTestReasonsCompressionStops * Fixed static analysis issue * Fixed failing unit tests * Removed unused import * Fixed merge * Fixed merge * Fixed merge * Fixed merge * Temporarily hide TransactionMaxSpansUnitTest behind isLongRunMode * Temporarily disable unit tests * Fixed unit tests * Added diagnostics * Fixed failing unit tests * Added re-run with escalated log level to HttpTransactionTest::testHttpStatus * Fixed failing component test * Revert "Temporarily disable unit tests" This reverts commit 3c26cc5095d8eb53631e1300da054e3c7437f208. * Re-enabled SpanCompressionUnitTest::testReasonsCompressionStops * Fixed merge * Temporarily disabled component tests * Fixed bad merge * Temporarily run only PDOAutoInstrumentationTest * Fixed merge * Re-enabled component tests * Fixed line endings * Fixed line endings (CRLF -> LF) * Fixed not cleaning up connection data in sync backend comm. mode * Fixed not joining background sender thread if there was fork Fixed not joining background sender thread if there was fork after module init * Fixed system.container.id being detected but not stored in metadata * Added component test for metadata's system.container.id detection * Updated docs for service_node_name configuration option * Set metadata's service.framework to WordPress/ * Added check for stack trace to WordPressAutoInstrumentationTest * Fixed ErrorComponentTest * Fixed ErrorComponentTest * Added WordPress and MySQLi to supported technologies * Converted file path to Linux directory separators * Added count for number of calls to WordPressFilterCallbackWrapper ctor/dtor For supportability and component tests * Replaced emalloc in cloneAstDecl with an array on stack * Run composer install with retries for unit tests as well * Converted StackTraceUtil from static class to class with instance state * Added option part 1 * Remove outdated comment * Added AssertMessageStack begin/end markers * Added verification of top stack frame in WordPressAutoInstrumentationTest * Make all properties of StackTraceFrameExpectations be Optional * Made StackTraceUtil instance class * Refactored call stack trace capturing code * Refactor AutoInstrumentationUtil->captureCurrentSpan * Fixed static analysis failure * Use StackTraceFrameExpectations for $expectedCallbackStackTraceTopFrame * Added docs for stack_trace_limit config * Added tests * Fixed failing component test * Removed redundant import * Added timestamps to static-check-unit-test.sh * Added timeout to .phpt tests runs * Added SPAN_STACK_TRACE_MIN_DURATION config to native part * Removed unrelated changes * Removed ELASTIC_APM_PHP_TESTS_PHPT_TIMEOUT_SECONDS * Use fixed run-tests.php from PHP8.0 * modified run-tests.php * disabled non-blocking read on std streams * changed reading method * Added test for config STACK_TRACE_LIMIT for inferred spans * Updated documentation * Removed redundant redirection * Removed redundant comments * Revert changes related to phpt_timeout to merge them separately * Added documentation * Allow any integer value for STACK_TRACE_LIMIT * Added adaptToSmoke to StackTraceLimitComponentTest * Fixed $cachedStackTraceLimitConfig type hint * Removed redundant static analysis suppression * Updated description in the documentation * Implemented SPAN_STACK_TRACE_MIN_DURATION (production code) * Added tests for SPAN_STACK_TRACE_MIN_DURATION * Added tests for SPAN_STACK_TRACE_MIN_DURATION part 2 * Fixed failing SpanStackTraceComponentTest * Update docs/configuration.asciidoc Co-authored-by: Brandon Morelli * Updated documentation * Refactored to disableTimingDependentFeatures * Removed redundant import * Fix issue found by static analysis * Update docs/configuration.asciidoc Co-authored-by: Brandon Morelli * Fixed wording in SPAN_STACK_TRACE_MIN_DURATION description --------- Co-authored-by: Pawel Filipczak Co-authored-by: Brandon Morelli --- docs/configuration.asciidoc | 33 ++++ .../Impl/Config/AllOptionsMetadata.php | 6 + .../Impl/Config/OptionDefaultValues.php | 1 + src/ElasticApm/Impl/Config/OptionNames.php | 1 + src/ElasticApm/Impl/Config/Snapshot.php | 8 + src/ElasticApm/Impl/InferredSpansBuilder.php | 12 +- src/ElasticApm/Impl/Span.php | 4 +- src/ElasticApm/Impl/Transaction.php | 37 +++++ src/ext/ConfigManager.c | 7 + src/ext/ConfigManager.h | 2 + src/ext/ConfigSnapshot.h | 1 + src/ext/elastic_apm.c | 1 + .../ComponentTests/ConfigSettingTest.php | 1 + .../MySQLiAutoInstrumentationTest.php | 3 +- .../PDOAutoInstrumentationTest.php | 3 +- .../SpanStackTraceComponentTest.php | 16 +- .../StackTraceLimitComponentTest.php | 2 + .../Util/ComponentTestCaseBase.php | 8 + .../WordPressAutoInstrumentationTest.php | 3 +- .../UnitTests/InferredSpansBuilderTest.php | 92 +++++++++- .../SpanStackTraceMinDurationUnitTest.php | 157 ++++++++++++++++++ .../UnitTests/SpanStackTraceUnitTest.php | 21 ++- .../UnitTests/StackTraceLimitUnitTest.php | 2 + 23 files changed, 396 insertions(+), 25 deletions(-) create mode 100644 tests/ElasticApmTests/UnitTests/SpanStackTraceMinDurationUnitTest.php diff --git a/docs/configuration.asciidoc b/docs/configuration.asciidoc index 3cd98b513..a89e24990 100644 --- a/docs/configuration.asciidoc +++ b/docs/configuration.asciidoc @@ -683,6 +683,39 @@ This configuration option supports the duration suffixes: `ms`, `s` and `m`. For example: `10ms`. This option's default unit is `ms`, so `5` is interpreted as `5ms`. +[float] +[[config-span-stack-trace-min-duration]] +==== `span_stack_trace_min_duration` + +[options="header"] +|============ +| Environment variable name | Option name in `php.ini` +| `ELASTIC_APM_SPAN_STACK_TRACE_MIN_DURATION` | `elastic_apm.span_stack_trace_min_duration` +|============ + +[options="header"] +|============ +| Default | Type +| `5ms` | Duration +|============ + +While it might be very helpful to have stack trace attached to a span, +collecting stack traces does have some overhead. +This configuration controls the minimum span duration at which stack traces are collected. +A higher value means lower overhead as stack trace collection is skipped for quick spans. + +Set this config to: + +- any positive value (e.g. `5ms`) - to limit stack trace collection to spans with duration + equal to or greater than the given value (e.g. 5 milliseconds) +- `0` (or `0` with any duration units e.g. `0ms`) - to collect stack traces + for spans with any duration +- any negative value (e.g. `-1ms`) - to disable stack trace collection for spans completely + +This configuration option supports the duration suffixes: `ms`, `s` and `m`. +For example: `10ms`. +This option's default unit is `ms`, so `5` is interpreted as `5ms`. + [float] [[config-stack-trace-limit]] ==== `stack_trace_limit` diff --git a/src/ElasticApm/Impl/Config/AllOptionsMetadata.php b/src/ElasticApm/Impl/Config/AllOptionsMetadata.php index fbee5db06..fb5c3609e 100644 --- a/src/ElasticApm/Impl/Config/AllOptionsMetadata.php +++ b/src/ElasticApm/Impl/Config/AllOptionsMetadata.php @@ -50,6 +50,11 @@ private static function buildDurationMetadataInMillisecondsWithMin(int $min, int return new DurationOptionMetadata(floatval($min), /* max */ null, DurationUnits::MILLISECONDS, floatval($default)); } + private static function buildDurationMetadataInMillisecondsNoMin(int $default): DurationOptionMetadata + { + return new DurationOptionMetadata(/* min */ null, /* max */ null, DurationUnits::MILLISECONDS, floatval($default)); + } + private static function buildDurationMetadataInMilliseconds(int $default): DurationOptionMetadata { return self::buildDurationMetadataInMillisecondsWithMin(/* min */ 0, $default); @@ -107,6 +112,7 @@ public static function get(): array OptionNames::SPAN_COMPRESSION_ENABLED => new BoolOptionMetadata(/* default */ true), OptionNames::SPAN_COMPRESSION_EXACT_MATCH_MAX_DURATION => self::buildDurationMetadataInMilliseconds(/* default */ 50), OptionNames::SPAN_COMPRESSION_SAME_KIND_MAX_DURATION => self::buildDurationMetadataInMilliseconds(/* default */ 0), + OptionNames::SPAN_STACK_TRACE_MIN_DURATION => self::buildDurationMetadataInMillisecondsNoMin(/* default */ OptionDefaultValues::SPAN_STACK_TRACE_MIN_DURATION), OptionNames::STACK_TRACE_LIMIT => new IntOptionMetadata(/* min */ null, /* max */ null, /* default */ OptionDefaultValues::STACK_TRACE_LIMIT), OptionNames::TRANSACTION_IGNORE_URLS => new NullableWildcardListOptionMetadata(), OptionNames::TRANSACTION_MAX_SPANS => self::buildPositiveOrZeroIntMetadata(OptionDefaultValues::TRANSACTION_MAX_SPANS), diff --git a/src/ElasticApm/Impl/Config/OptionDefaultValues.php b/src/ElasticApm/Impl/Config/OptionDefaultValues.php index b24ba9436..b45bb2adb 100644 --- a/src/ElasticApm/Impl/Config/OptionDefaultValues.php +++ b/src/ElasticApm/Impl/Config/OptionDefaultValues.php @@ -34,6 +34,7 @@ final class OptionDefaultValues { use StaticClassTrait; + public const SPAN_STACK_TRACE_MIN_DURATION = 5; public const STACK_TRACE_LIMIT = 50; public const TRANSACTION_MAX_SPANS = 500; } diff --git a/src/ElasticApm/Impl/Config/OptionNames.php b/src/ElasticApm/Impl/Config/OptionNames.php index 1e8751928..b9df5b298 100644 --- a/src/ElasticApm/Impl/Config/OptionNames.php +++ b/src/ElasticApm/Impl/Config/OptionNames.php @@ -66,6 +66,7 @@ final class OptionNames public const SPAN_COMPRESSION_ENABLED = 'span_compression_enabled'; public const SPAN_COMPRESSION_EXACT_MATCH_MAX_DURATION = 'span_compression_exact_match_max_duration'; public const SPAN_COMPRESSION_SAME_KIND_MAX_DURATION = 'span_compression_same_kind_max_duration'; + public const SPAN_STACK_TRACE_MIN_DURATION = 'span_stack_trace_min_duration'; public const STACK_TRACE_LIMIT = 'stack_trace_limit'; public const TRANSACTION_IGNORE_URLS = 'transaction_ignore_urls'; public const TRANSACTION_MAX_SPANS = 'transaction_max_spans'; diff --git a/src/ElasticApm/Impl/Config/Snapshot.php b/src/ElasticApm/Impl/Config/Snapshot.php index c24596418..4eaf9dc34 100644 --- a/src/ElasticApm/Impl/Config/Snapshot.php +++ b/src/ElasticApm/Impl/Config/Snapshot.php @@ -188,6 +188,9 @@ final class Snapshot implements LoggableInterface /** @var float */ private $spanCompressionSameKindMaxDuration; + /** @var float */ + private $spanStackTraceMinDuration; + /** @var int */ private $stackTraceLimit; @@ -371,6 +374,11 @@ public function spanCompressionSameKindMaxDuration(): float return $this->spanCompressionSameKindMaxDuration; } + public function spanStackTraceMinDuration(): float + { + return $this->spanStackTraceMinDuration; + } + public function stackTraceLimit(): int { return $this->stackTraceLimit; diff --git a/src/ElasticApm/Impl/InferredSpansBuilder.php b/src/ElasticApm/Impl/InferredSpansBuilder.php index 8a9aa7869..6ae90eca8 100644 --- a/src/ElasticApm/Impl/InferredSpansBuilder.php +++ b/src/ElasticApm/Impl/InferredSpansBuilder.php @@ -186,9 +186,15 @@ private function buildStackTrace(int $forFrameIndex, ?ClassicFormatStackTraceFra private function sendFrameAsSpan(int $openFrameIndex, string $parentId, ?ClassicFormatStackTraceFrame $frameCopy, ?int $frameCopyIndex): void { $frame = $this->openFramesReverseOrder[$openFrameIndex]; - $stackTrace = ($maxNumberOfFrames = StackTraceUtil::convertLimitConfigToMaxNumberOfFrames($this->transaction->getStackTraceLimitConfig())) === 0 - ? null - : $this->tracer->stackTraceUtil()->convertClassicToApmFormat($this->buildStackTrace($openFrameIndex, $frameCopy, $frameCopyIndex), $maxNumberOfFrames); + $stackTrace = null; + /** @var float $spanDurationInMilliseconds */ + $spanDurationInMilliseconds = $frame->duration; + if ( + $this->transaction->shouldCollectStackTraceForSpanDuration($spanDurationInMilliseconds) + && (($maxNumberOfFrames = StackTraceUtil::convertLimitConfigToMaxNumberOfFrames($this->transaction->getStackTraceLimitConfig())) !== 0) + ) { + $stackTrace = $this->tracer->stackTraceUtil()->convertClassicToApmFormat($this->buildStackTrace($openFrameIndex, $frameCopy, $frameCopyIndex), $maxNumberOfFrames); + } $frame->prepareForSerialization($this->transaction, $parentId, $stackTrace); $this->tracer->sendSpanToApmServer($frame); } diff --git a/src/ElasticApm/Impl/Span.php b/src/ElasticApm/Impl/Span.php index 90cc367dd..d0ea79d75 100644 --- a/src/ElasticApm/Impl/Span.php +++ b/src/ElasticApm/Impl/Span.php @@ -481,7 +481,9 @@ public function endSpanEx(int $numberOfStackFramesToSkip, ?float $duration = nul $this->onAboutToEnd->callCallbacks($this); if ($this->shouldBeSentToApmServer()) { - $this->stackTrace = $this->containingTransaction->captureApmFormatStackTrace($numberOfStackFramesToSkip + 1); + if ($this->containingTransaction->shouldCollectStackTraceForSpanDuration($this->duration)) { + $this->stackTrace = $this->containingTransaction->captureApmFormatStackTrace($numberOfStackFramesToSkip + 1); + } $this->prepareForSerialization(); $this->parentExecutionSegment->onChildSpanEnded($this); } diff --git a/src/ElasticApm/Impl/Transaction.php b/src/ElasticApm/Impl/Transaction.php index e355e68ae..2f30760d1 100644 --- a/src/ElasticApm/Impl/Transaction.php +++ b/src/ElasticApm/Impl/Transaction.php @@ -99,6 +99,9 @@ final class Transaction extends ExecutionSegment implements TransactionInterface /** @var ?int */ private $cachedStackTraceLimitConfig = null; + /** @var ?float */ + private $cachedSpanStackTraceMinDurationConfig = null; + public function __construct(TransactionBuilder $builder) { $this->tracer = $builder->tracer; @@ -594,6 +597,40 @@ public function getStackTraceLimitConfig(): int return $this->cachedStackTraceLimitConfig; } + private function getSpanStackTraceMinDurationConfig(): float + { + if ($this->cachedSpanStackTraceMinDurationConfig === null) { + $this->cachedSpanStackTraceMinDurationConfig = $this->getConfig()->spanStackTraceMinDuration(); + + /** + * span_stack_trace_min_duration + * 0 - collect stack traces for spans with any duration + * any positive value - it limits stack trace collection to spans with duration equal to or greater than + * any negative value - it disable stack trace collection for spans completely + */ + $msgPrefix = $this->cachedSpanStackTraceMinDurationConfig === 0.0 + ? 'Span stack trace collection is enabled for spans with any duration' + : ($this->cachedSpanStackTraceMinDurationConfig < 0 + ? 'Span stack trace collection is DISABLED for spans with any duration' + : 'Span stack trace collection is enabled for spans with duration >= ' . $this->cachedSpanStackTraceMinDurationConfig . ' ms'); + ($loggerProxy = $this->logger->ifDebugLevelEnabled(__LINE__, __FUNCTION__)) + && $loggerProxy->log($msgPrefix . ' (set by configuration option `' . OptionNames::SPAN_STACK_TRACE_MIN_DURATION . '\')'); + } + + return $this->cachedSpanStackTraceMinDurationConfig; + } + + public function shouldCollectStackTraceForSpanDuration(float $durationInMilliseconds): bool + { + /** + * span_stack_trace_min_duration + * 0 - collect stack traces for spans with any duration + * any positive value - it limits stack trace collection to spans with duration equal to or greater than + * any negative value - it disable stack trace collection for spans completely + */ + return (($stackTraceMinDuration = $this->getSpanStackTraceMinDurationConfig()) >= 0) && $durationInMilliseconds >= $stackTraceMinDuration; + } + /** * @param int $numberOfStackFramesToSkip * diff --git a/src/ext/ConfigManager.c b/src/ext/ConfigManager.c index b4153142b..39bdb139f 100644 --- a/src/ext/ConfigManager.c +++ b/src/ext/ConfigManager.c @@ -807,6 +807,7 @@ ELASTIC_APM_DEFINE_FIELD_ACCESS_FUNCS( stringValue, serviceVersion ) ELASTIC_APM_DEFINE_FIELD_ACCESS_FUNCS( boolValue, spanCompressionEnabled ) ELASTIC_APM_DEFINE_FIELD_ACCESS_FUNCS( stringValue, spanCompressionExactMatchMaxDuration ) ELASTIC_APM_DEFINE_FIELD_ACCESS_FUNCS( stringValue, spanCompressionSameKindMaxDuration ) +ELASTIC_APM_DEFINE_FIELD_ACCESS_FUNCS( stringValue, spanStackTraceMinDuration ) ELASTIC_APM_DEFINE_FIELD_ACCESS_FUNCS( stringValue, stackTraceLimit ) ELASTIC_APM_DEFINE_FIELD_ACCESS_FUNCS( stringValue, transactionIgnoreUrls ) ELASTIC_APM_DEFINE_FIELD_ACCESS_FUNCS( stringValue, transactionMaxSpans ) @@ -1146,6 +1147,12 @@ static void initOptionsMetadata( OptionMetadata* optsMeta ) ELASTIC_APM_CFG_OPT_NAME_SPAN_COMPRESSION_SAME_KIND_MAX_DURATION, /* defaultValue: */ NULL ); + ELASTIC_APM_INIT_METADATA( + buildStringOptionMetadata, + spanStackTraceMinDuration, + ELASTIC_APM_CFG_OPT_NAME_SPAN_STACK_TRACE_MIN_DURATION, + /* defaultValue: */ NULL ); + ELASTIC_APM_INIT_METADATA( buildStringOptionMetadata, stackTraceLimit, diff --git a/src/ext/ConfigManager.h b/src/ext/ConfigManager.h index 59e3b1876..e585cf555 100644 --- a/src/ext/ConfigManager.h +++ b/src/ext/ConfigManager.h @@ -109,6 +109,7 @@ enum OptionId optionId_spanCompressionEnabled, optionId_spanCompressionExactMatchMaxDuration, optionId_spanCompressionSameKindMaxDuration, + optionId_spanStackTraceMinDuration, optionId_stackTraceLimit, optionId_transactionIgnoreUrls, optionId_transactionMaxSpans, @@ -317,6 +318,7 @@ const ConfigSnapshot* getGlobalCurrentConfigSnapshot(); #define ELASTIC_APM_CFG_OPT_NAME_SPAN_COMPRESSION_ENABLED "span_compression_enabled" #define ELASTIC_APM_CFG_OPT_NAME_SPAN_COMPRESSION_EXACT_MATCH_MAX_DURATION "span_compression_exact_match_max_duration" #define ELASTIC_APM_CFG_OPT_NAME_SPAN_COMPRESSION_SAME_KIND_MAX_DURATION "span_compression_same_kind_max_duration" +#define ELASTIC_APM_CFG_OPT_NAME_SPAN_STACK_TRACE_MIN_DURATION "span_stack_trace_min_duration" #define ELASTIC_APM_CFG_OPT_NAME_STACK_TRACE_LIMIT "stack_trace_limit" #define ELASTIC_APM_CFG_OPT_NAME_TRANSACTION_IGNORE_URLS "transaction_ignore_urls" #define ELASTIC_APM_CFG_OPT_NAME_TRANSACTION_MAX_SPANS "transaction_max_spans" diff --git a/src/ext/ConfigSnapshot.h b/src/ext/ConfigSnapshot.h index aa61dcd1e..a8605033a 100644 --- a/src/ext/ConfigSnapshot.h +++ b/src/ext/ConfigSnapshot.h @@ -80,6 +80,7 @@ struct ConfigSnapshot bool spanCompressionEnabled; String spanCompressionExactMatchMaxDuration; String spanCompressionSameKindMaxDuration; + String spanStackTraceMinDuration; String stackTraceLimit; String transactionIgnoreUrls; String transactionMaxSpans; diff --git a/src/ext/elastic_apm.c b/src/ext/elastic_apm.c index ad22e7d73..ad259d2e5 100644 --- a/src/ext/elastic_apm.c +++ b/src/ext/elastic_apm.c @@ -184,6 +184,7 @@ PHP_INI_BEGIN() ELASTIC_APM_INI_ENTRY( ELASTIC_APM_CFG_OPT_NAME_SPAN_COMPRESSION_ENABLED ) ELASTIC_APM_INI_ENTRY( ELASTIC_APM_CFG_OPT_NAME_SPAN_COMPRESSION_EXACT_MATCH_MAX_DURATION ) ELASTIC_APM_INI_ENTRY( ELASTIC_APM_CFG_OPT_NAME_SPAN_COMPRESSION_SAME_KIND_MAX_DURATION ) + ELASTIC_APM_INI_ENTRY( ELASTIC_APM_CFG_OPT_NAME_SPAN_STACK_TRACE_MIN_DURATION ) ELASTIC_APM_INI_ENTRY( ELASTIC_APM_CFG_OPT_NAME_STACK_TRACE_LIMIT ) ELASTIC_APM_INI_ENTRY( ELASTIC_APM_CFG_OPT_NAME_TRANSACTION_IGNORE_URLS ) ELASTIC_APM_INI_ENTRY( ELASTIC_APM_CFG_OPT_NAME_TRANSACTION_MAX_SPANS ) diff --git a/tests/ElasticApmTests/ComponentTests/ConfigSettingTest.php b/tests/ElasticApmTests/ComponentTests/ConfigSettingTest.php index a0213b54f..0d60b2510 100644 --- a/tests/ElasticApmTests/ComponentTests/ConfigSettingTest.php +++ b/tests/ElasticApmTests/ComponentTests/ConfigSettingTest.php @@ -160,6 +160,7 @@ private static function buildOptionNameToRawToValue(): array => $durationRawToParsedValues, OptionNames::SPAN_COMPRESSION_SAME_KIND_MAX_DURATION => $durationRawToParsedValues, + OptionNames::SPAN_STACK_TRACE_MIN_DURATION => $durationRawToParsedValues, OptionNames::STACK_TRACE_LIMIT => $intRawToParsedValues, OptionNames::TRANSACTION_IGNORE_URLS => $wildcardListRawToParsedValues, OptionNames::TRANSACTION_MAX_SPANS => $intRawToParsedValues, diff --git a/tests/ElasticApmTests/ComponentTests/MySQLiAutoInstrumentationTest.php b/tests/ElasticApmTests/ComponentTests/MySQLiAutoInstrumentationTest.php index ab3522f40..14eb41dab 100644 --- a/tests/ElasticApmTests/ComponentTests/MySQLiAutoInstrumentationTest.php +++ b/tests/ElasticApmTests/ComponentTests/MySQLiAutoInstrumentationTest.php @@ -479,8 +479,7 @@ function (AppCodeHostParams $appCodeParams) use ($disableInstrumentationsOptVal) if (!empty($disableInstrumentationsOptVal)) { $appCodeParams->setAgentOption(OptionNames::DISABLE_INSTRUMENTATIONS, $disableInstrumentationsOptVal); } - // Disable Span Compression feature to have all the expected spans individually - $appCodeParams->setAgentOption(OptionNames::SPAN_COMPRESSION_ENABLED, false); + self::disableTimingDependentFeatures($appCodeParams); } ); $appCodeHost->sendRequest( diff --git a/tests/ElasticApmTests/ComponentTests/PDOAutoInstrumentationTest.php b/tests/ElasticApmTests/ComponentTests/PDOAutoInstrumentationTest.php index b41f1358f..cc692508d 100644 --- a/tests/ElasticApmTests/ComponentTests/PDOAutoInstrumentationTest.php +++ b/tests/ElasticApmTests/ComponentTests/PDOAutoInstrumentationTest.php @@ -291,8 +291,7 @@ function (AppCodeHostParams $appCodeParams) use ($disableInstrumentationsOptVal) if (!empty($disableInstrumentationsOptVal)) { $appCodeParams->setAgentOption(OptionNames::DISABLE_INSTRUMENTATIONS, $disableInstrumentationsOptVal); } - // Disable Span Compression feature to have all the expected spans individually - $appCodeParams->setAgentOption(OptionNames::SPAN_COMPRESSION_ENABLED, false); + self::disableTimingDependentFeatures($appCodeParams); } ); $appCodeHost->sendRequest( diff --git a/tests/ElasticApmTests/ComponentTests/SpanStackTraceComponentTest.php b/tests/ElasticApmTests/ComponentTests/SpanStackTraceComponentTest.php index 1a61114a9..99d1aafd0 100644 --- a/tests/ElasticApmTests/ComponentTests/SpanStackTraceComponentTest.php +++ b/tests/ElasticApmTests/ComponentTests/SpanStackTraceComponentTest.php @@ -23,7 +23,6 @@ namespace ElasticApmTests\ComponentTests; -use Elastic\Apm\Impl\Config\OptionNames; use Elastic\Apm\Impl\Log\LoggableToString; use Elastic\Apm\Impl\Util\TextUtil; use ElasticApmTests\ComponentTests\Util\AppCodeHostParams; @@ -55,7 +54,7 @@ protected function isSpanCompressionCompatible(): bool */ private static function sharedCodeForTestAllSpanCreatingApis(): array { - /** @var array */ + /** @var array $expectedData */ $expectedData = []; $createSpanApis = SpanStackTraceTestSharedCode::allSpanCreatingApis(/* ref */ $expectedData); @@ -74,19 +73,15 @@ public static function appCodeForTestAllSpanCreatingApis(): void public function testAllSpanCreatingApis(): void { $sharedCodeResult = self::sharedCodeForTestAllSpanCreatingApis(); - /** @var array */ + /** @var array $expectedData */ $expectedData = $sharedCodeResult['expectedData']; - /** - * @var array - * @phpstan-var array - */ + /** @var array $createSpanApis */ $createSpanApis = $sharedCodeResult['createSpanApis']; $testCaseHandle = $this->getTestCaseHandle(); $appCodeHost = $testCaseHandle->ensureMainAppCodeHost( function (AppCodeHostParams $appCodeParams): void { - // Disable Span Compression feature to have all the expected spans individually - $appCodeParams->setAgentOption(OptionNames::SPAN_COMPRESSION_ENABLED, false); + self::disableTimingDependentFeatures($appCodeParams); } ); $appCodeHost->sendRequest(AppCodeTarget::asRouted([__CLASS__, 'appCodeForTestAllSpanCreatingApis'])); @@ -102,8 +97,7 @@ public function testTopLevelTransactionBeginCurrentSpanApi(): void $testCaseHandle = $this->getTestCaseHandle(); $appCodeHost = $testCaseHandle->ensureMainAppCodeHost( function (AppCodeHostParams $appCodeParams): void { - // Disable Span Compression feature to have all the expected spans individually - $appCodeParams->setAgentOption(OptionNames::SPAN_COMPRESSION_ENABLED, false); + self::disableTimingDependentFeatures($appCodeParams); } ); $appCodeHost->sendRequest(AppCodeTarget::asTopLevel(TopLevelCodeId::SPAN_BEGIN_END)); diff --git a/tests/ElasticApmTests/ComponentTests/StackTraceLimitComponentTest.php b/tests/ElasticApmTests/ComponentTests/StackTraceLimitComponentTest.php index 659a27693..346fc6d41 100644 --- a/tests/ElasticApmTests/ComponentTests/StackTraceLimitComponentTest.php +++ b/tests/ElasticApmTests/ComponentTests/StackTraceLimitComponentTest.php @@ -64,6 +64,8 @@ public function testVariousConfigValues(MixedMap $testArgs): void $appCodeHost = $testCaseHandle->ensureMainAppCodeHost( function (AppCodeHostParams $appCodeParams) use ($testArgs): void { + // Enable span stack trace collection for span with any duration + $appCodeParams->setAgentOption(OptionNames::SPAN_STACK_TRACE_MIN_DURATION, 0); if (($optVal = $testArgs->getNullableString(OptionNames::STACK_TRACE_LIMIT)) !== null) { $appCodeParams->setAgentOption(OptionNames::STACK_TRACE_LIMIT, $optVal); } diff --git a/tests/ElasticApmTests/ComponentTests/Util/ComponentTestCaseBase.php b/tests/ElasticApmTests/ComponentTests/Util/ComponentTestCaseBase.php index a50c61c13..005666bb1 100644 --- a/tests/ElasticApmTests/ComponentTests/Util/ComponentTestCaseBase.php +++ b/tests/ElasticApmTests/ComponentTests/Util/ComponentTestCaseBase.php @@ -556,4 +556,12 @@ public static function getContextCustom(TransactionContextDto $ctx, string $key) self::assertSame($receivedCrc, $calculatedCrc); return unserialize(base64_decode($valueSerialized)); } + + protected static function disableTimingDependentFeatures(AppCodeHostParams $appCodeParams): void + { + // Disable Span Compression feature to have all the expected spans individually + $appCodeParams->setAgentOption(OptionNames::SPAN_COMPRESSION_ENABLED, false); + // Enable span stack trace collection for span with any duration + $appCodeParams->setAgentOption(OptionNames::SPAN_STACK_TRACE_MIN_DURATION, 0); + } } diff --git a/tests/ElasticApmTests/ComponentTests/WordPressAutoInstrumentationTest.php b/tests/ElasticApmTests/ComponentTests/WordPressAutoInstrumentationTest.php index b4a3ebb8a..a4f416cb7 100644 --- a/tests/ElasticApmTests/ComponentTests/WordPressAutoInstrumentationTest.php +++ b/tests/ElasticApmTests/ComponentTests/WordPressAutoInstrumentationTest.php @@ -537,8 +537,7 @@ private function implTestOnMockSource(MixedMap $testArgs): void function (AppCodeHostParams $appCodeParams) use ($testArgs): void { $appCodeParams->setAgentOptionIfNotDefaultValue(OptionNames::AST_PROCESS_ENABLED, $testArgs->getBool(self::IS_AST_PROCESS_ENABLED_KEY)); $appCodeParams->setAgentOption(OptionNames::DISABLE_INSTRUMENTATIONS, $testArgs->getString(AutoInstrumentationUtilForTests::DISABLE_INSTRUMENTATIONS_KEY)); - // Disable span compression to have all the expected spans individually - $appCodeParams->setAgentOption(OptionNames::SPAN_COMPRESSION_ENABLED, false); + self::disableTimingDependentFeatures($appCodeParams); $appCodeParams->setAgentOption(OptionNames::NON_KEYWORD_STRING_MAX_LENGTH, self::NON_KEYWORD_STRING_MAX_LENGTH); } ); diff --git a/tests/ElasticApmTests/UnitTests/InferredSpansBuilderTest.php b/tests/ElasticApmTests/UnitTests/InferredSpansBuilderTest.php index 2d5b51106..78a309cee 100644 --- a/tests/ElasticApmTests/UnitTests/InferredSpansBuilderTest.php +++ b/tests/ElasticApmTests/UnitTests/InferredSpansBuilderTest.php @@ -334,6 +334,8 @@ private function charDiagramTestImpl(array $args): void $inputOptions = ArrayUtil::getValueIfKeyExistsElse(self::INPUT_OPTIONS_KEY, $args, []); $this->setUpTestEnv( function (TracerBuilderForTests $tracerBuilder) use ($inputOptions): void { + // Enable span stack trace collection for span with any duration + $tracerBuilder->withConfig(OptionNames::SPAN_STACK_TRACE_MIN_DURATION, '0'); foreach ($inputOptions as $optName => $optVal) { $tracerBuilder->withConfig($optName, strval($optVal)); } @@ -1250,7 +1252,7 @@ public static function dataProviderForTestStackTraceLimit(): iterable return StackTraceLimitTestSharedCode::dataProviderForTestVariousConfigValues(); } - private function minDurationInMillisecondsConfig(): float + private function profilingInferredSpansMinDurationInMillisecondsConfig(): float { self::assertInstanceOf(Tracer::class, $this->tracer); /** @var Tracer $tracer */ @@ -1258,6 +1260,14 @@ private function minDurationInMillisecondsConfig(): float return $tracer->getConfig()->profilingInferredSpansMinDurationInMilliseconds(); } + private function spanStackTraceMinDurationConfig(): float + { + self::assertInstanceOf(Tracer::class, $this->tracer); + /** @var Tracer $tracer */ + $tracer = $this->tracer; + return $tracer->getConfig()->spanStackTraceMinDuration(); + } + private static function genFrameForSpanWithStackDepth(int $spanIndex, int $stackFrameIndex, int $stackDepth): ClassicFormatStackTraceFrame { $result = new ClassicFormatStackTraceFrame('file_for_span_' . $spanIndex . '.php', $stackFrameIndex + 1); @@ -1296,7 +1306,7 @@ private function createSpanWithStackDepth(InferredSpansBuilder $builder, int $sp } $builder->addStackTrace($stackTrace); - $this->mockClock->fastForwardMilliseconds($this->minDurationInMillisecondsConfig()); + $this->mockClock->fastForwardMilliseconds(max($this->profilingInferredSpansMinDurationInMillisecondsConfig(), $this->spanStackTraceMinDurationConfig())); $builder->addStackTrace($stackTrace); } @@ -1369,4 +1379,82 @@ function (InferredSpansBuilder $builder) use ($stackDepthVariants): void { } $dbgCtx->popSubScope(); } + + /** + * @return iterable + */ + public static function dataProviderForTestSpanStackTraceMinDuration(): iterable + { + return SpanStackTraceMinDurationUnitTest::dataProviderForTestVariousConfigValues(); + } + + /** + * @dataProvider dataProviderForTestSpanStackTraceMinDuration + */ + public function testSpanStackTraceMinDuration(MixedMap $testArgs): void + { + AssertMessageStack::newScope(/* out */ $dbgCtx, AssertMessageStack::funcArgs()); + + /** + * Arrange + */ + + $this->setUpTestEnv( + function (TracerBuilderForTests $builder) use ($testArgs): void { + $builder->withConfig(OptionNames::PROFILING_INFERRED_SPANS_MIN_DURATION, '0'); + if (($optVal = $testArgs->getNullableString(OptionNames::SPAN_STACK_TRACE_MIN_DURATION)) !== null) { + $builder->withConfig(OptionNames::SPAN_STACK_TRACE_MIN_DURATION, $optVal); + } + } + ); + + $expectedSpanStackTraceMinDuration = $testArgs->getFloat(SpanStackTraceMinDurationUnitTest::EXPECTED_SPAN_STACK_TRACE_MIN_DURATION_KEY); + $spanDurations = SpanStackTraceMinDurationUnitTest::genSpanDurations($expectedSpanStackTraceMinDuration); + $dbgCtx->add(['spanDurations' => $spanDurations]); + + /** + * Act + */ + + $this->withInferredSpansBuilderDuringTransaction( + function (InferredSpansBuilder $builder) use ($spanDurations): void { + foreach (RangeUtil::generateUpTo(count($spanDurations)) as $spanIndex) { + $stackFrame = new ClassicFormatStackTraceFrame('file_for_span_' . $spanIndex . '.php', $spanIndex, /* class */ null, /* isStaticMethod */ null, 'test_span_' . $spanIndex); + $builder->addStackTrace([$stackFrame]); + $this->mockClock->fastForwardMilliseconds($spanDurations[$spanIndex]); + $builder->addStackTrace([$stackFrame]); + } + } + ); + + /** + * Assert + */ + + $dbgCtx->add(['dataFromAgent' => $this->mockEventSink->dataFromAgent]); + TestCaseBase::assertCount(count($spanDurations), $this->mockEventSink->dataFromAgent->idToSpan); + + $dbgCtx->pushSubScope(); + foreach (RangeUtil::generateUpTo(count($spanDurations)) as $spanIndex) { + $dbgCtx->add(['spanIndex' => $spanIndex, '$spanDurations[$spanIndex]' => $spanDurations[$spanIndex]]); + $span = $this->mockEventSink->dataFromAgent->singleSpanByName('test_span_' . $spanIndex); + self::assertEquals($spanDurations[$spanIndex], $span->duration); + /** + * span_stack_trace_min_duration + * 0 - collect stack traces for spans with any duration + * any positive value - it limits stack trace collection to spans with duration equal to or greater than + * any negative value - it disable stack trace collection for spans completely + */ + if ($expectedSpanStackTraceMinDuration < 0) { + self::assertNull($span->stackTrace); + } elseif ($expectedSpanStackTraceMinDuration === 0.0) { + self::assertNotNull($span->stackTrace); + } elseif ($expectedSpanStackTraceMinDuration <= $span->duration) { + self::assertNotNull($span->stackTrace); + } else { + self::assertNull($span->stackTrace); + } + } + $dbgCtx->popSubScope(); + } } diff --git a/tests/ElasticApmTests/UnitTests/SpanStackTraceMinDurationUnitTest.php b/tests/ElasticApmTests/UnitTests/SpanStackTraceMinDurationUnitTest.php new file mode 100644 index 000000000..7a600aaaa --- /dev/null +++ b/tests/ElasticApmTests/UnitTests/SpanStackTraceMinDurationUnitTest.php @@ -0,0 +1,157 @@ + + */ + public static function dataProviderForTestVariousConfigValues(): iterable + { + /** + * @return iterable> + */ + $genDataSets = function (): iterable { + foreach ([null, -5, -2, -1, 0, 1, 2, 3, OptionDefaultValues::SPAN_STACK_TRACE_MIN_DURATION, OptionDefaultValues::SPAN_STACK_TRACE_MIN_DURATION + 10] as $optVal) { + yield [ + OptionNames::SPAN_STACK_TRACE_MIN_DURATION => $optVal === null ? null : strval($optVal), + self::EXPECTED_SPAN_STACK_TRACE_MIN_DURATION_KEY => floatval($optVal === null ? OptionDefaultValues::SPAN_STACK_TRACE_MIN_DURATION : $optVal), + ]; + } + }; + + return DataProviderForTestBuilder::convertEachDataSetToMixedMapAndAddDesc($genDataSets); + } + + /** + * @param float $expectedSpanStackTraceMinDuration + * + * @return float[] + */ + public static function genSpanDurations(float $expectedSpanStackTraceMinDuration): array + { + $spanDurations = [0]; + $addToSpanDurations = function (float $spanDuration) use (&$spanDurations): void { + if ($spanDuration >= 0) { + ArrayUtilForTests::addToListIfNotAlreadyPresent($spanDuration, /* ref */ $spanDurations); + } + }; + + foreach ([0, 0.123, 0.5, 1, 2] as $diff) { + $addToSpanDurations($expectedSpanStackTraceMinDuration + $diff); + $addToSpanDurations($expectedSpanStackTraceMinDuration - $diff); + } + + return $spanDurations; + } + + /** + * @dataProvider dataProviderForTestVariousConfigValues + */ + public function testVariousConfigValuesWithMockClock(MixedMap $testArgs): void + { + AssertMessageStack::newScope(/* out */ $dbgCtx, AssertMessageStack::funcArgs()); + $expectedSpanStackTraceMinDuration = $testArgs->getFloat(self::EXPECTED_SPAN_STACK_TRACE_MIN_DURATION_KEY); + + /** + * Arrange + */ + + $spanDurations = self::genSpanDurations($expectedSpanStackTraceMinDuration); + $dbgCtx->add(['spanDurations' => $spanDurations]); + + $mockClock = new MockClock(); + $this->setUpTestEnv( + function (TracerBuilderForTests $tracerBuilder) use ($testArgs, $mockClock): void { + $tracerBuilder->withClock($mockClock); + if (($optVal = $testArgs->getNullableString(OptionNames::SPAN_STACK_TRACE_MIN_DURATION)) !== null) { + $tracerBuilder->withConfig(OptionNames::SPAN_STACK_TRACE_MIN_DURATION, $optVal); + } + } + ); + + /** + * Act + */ + + $tx = $this->tracer->beginCurrentTransaction('test_TX_name', 'test_TX_type'); + + $produceSpanWithDuration = function (int $spanIndex) use ($spanDurations, $mockClock): void { + $span = $this->tracer->getCurrentTransaction()->beginCurrentSpan('test_span_' . $spanIndex, 'test_span_' . $spanIndex . '_type'); + $mockClock->fastForwardMilliseconds($spanDurations[$spanIndex]); + $span->end(); + }; + + foreach (RangeUtil::generateUpTo(count($spanDurations)) as $spanIndex) { + $produceSpanWithDuration($spanIndex); + } + + $tx->end(); + + /** + * Assert + */ + + $dbgCtx->add(['dataFromAgent' => $this->mockEventSink->dataFromAgent]); + TestCaseBase::assertCount(count($spanDurations), $this->mockEventSink->dataFromAgent->idToSpan); + + $dbgCtx->pushSubScope(); + foreach (RangeUtil::generateUpTo(count($spanDurations)) as $spanIndex) { + $dbgCtx->add(['spanIndex' => $spanIndex, '$spanDurations[$spanIndex]' => $spanDurations[$spanIndex]]); + $span = $this->mockEventSink->dataFromAgent->singleSpanByName('test_span_' . $spanIndex); + self::assertEquals($spanDurations[$spanIndex], $span->duration); + /** + * span_stack_trace_min_duration + * 0 - collect stack traces for spans with any duration + * any positive value - it limits stack trace collection to spans with duration equal to or greater than + * any negative value - it disable stack trace collection for spans completely + */ + if ($expectedSpanStackTraceMinDuration < 0) { + self::assertNull($span->stackTrace); + } elseif ($expectedSpanStackTraceMinDuration === 0.0) { + self::assertNotNull($span->stackTrace); + } elseif ($expectedSpanStackTraceMinDuration <= $span->duration) { + self::assertNotNull($span->stackTrace); + } else { + self::assertNull($span->stackTrace); + } + } + $dbgCtx->popSubScope(); + } +} diff --git a/tests/ElasticApmTests/UnitTests/SpanStackTraceUnitTest.php b/tests/ElasticApmTests/UnitTests/SpanStackTraceUnitTest.php index 93f6772e1..22d45b81d 100644 --- a/tests/ElasticApmTests/UnitTests/SpanStackTraceUnitTest.php +++ b/tests/ElasticApmTests/UnitTests/SpanStackTraceUnitTest.php @@ -24,8 +24,10 @@ namespace ElasticApmTests\UnitTests; use Elastic\Apm\ElasticApm; +use Elastic\Apm\Impl\Config\OptionNames; use ElasticApmTests\TestsSharedCode\SpanStackTraceTestSharedCode; use ElasticApmTests\UnitTests\Util\TracerUnitTestCaseBase; +use ElasticApmTests\Util\TracerBuilderForTests; class SpanStackTraceUnitTest extends TracerUnitTestCaseBase { @@ -42,7 +44,20 @@ protected function isSpanCompressionCompatible(): bool public function testAllSpanCreatingApis(): void { - // Act + /** + * Arrange + */ + + $this->setUpTestEnv( + function (TracerBuilderForTests $builder): void { + // Enable span stack trace collection for span with any duration + $builder->withConfig(OptionNames::SPAN_STACK_TRACE_MIN_DURATION, '0'); + } + ); + + /** + * Act + */ $tx = ElasticApm::beginCurrentTransaction(__FUNCTION__, 'test_TX_type'); @@ -56,7 +71,9 @@ public function testAllSpanCreatingApis(): void $tx->end(); - // Assert + /** + * Assert + */ $this->assertSame(__FUNCTION__, $this->mockEventSink->singleTransaction()->name); SpanStackTraceTestSharedCode::assertPartImpl(count($createSpanApis), $expectedData, $this->mockEventSink->idToSpan()); diff --git a/tests/ElasticApmTests/UnitTests/StackTraceLimitUnitTest.php b/tests/ElasticApmTests/UnitTests/StackTraceLimitUnitTest.php index 97a702d5d..237253d7e 100644 --- a/tests/ElasticApmTests/UnitTests/StackTraceLimitUnitTest.php +++ b/tests/ElasticApmTests/UnitTests/StackTraceLimitUnitTest.php @@ -50,6 +50,8 @@ public function testVariousConfigValues(MixedMap $testArgs): void $this->setUpTestEnv( function (TracerBuilderForTests $builder) use ($testArgs): void { + // Enable span stack trace collection for span with any duration + $builder->withConfig(OptionNames::SPAN_STACK_TRACE_MIN_DURATION, '0'); if (($optVal = $testArgs->getNullableString(OptionNames::STACK_TRACE_LIMIT)) !== null) { $builder->withConfig(OptionNames::STACK_TRACE_LIMIT, $optVal); }