From 20ac820639d4e430482df808ffd689c344bcda53 Mon Sep 17 00:00:00 2001 From: Sergey Kleyman Date: Tue, 16 Jul 2024 13:43:35 +0300 Subject: [PATCH] Added PHP part --- composer.json | 3 +- packaging/test/smokeTest.php | 6 +- packaging/test/smokeTestUninstalled.php | 4 +- prod/native/extension/code/ModuleEntry.cpp | 2 +- prod/native/extension/code/ModuleInit.cpp | 10 +- prod/native/extension/code/util_for_PHP.h | 14 +- prod/native/extension/phpt/run.sh | 1 - .../phpt/tests/includes/bootstrap_mock.inc | 20 +- .../phpt/tests/includes/tests_util.inc | 12 +- .../tests/instrumentation_int_method.phpt | 4 +- prod/native/libcommon/code/basic_macros.h | 4 +- prod/native/libphpbridge/code/PhpBridge.cpp | 16 +- .../ElasticOTel/AutoInstrument/Autoloader.php | 103 --- .../AutoInstrument/PhpPartFacade.php | 442 ------------ .../TransactionForExtensionRequest.php | 683 ------------------ .../AutoInstrument/bootstrap_php_part.php | 30 - .../BootstrapStageLogger.php | 54 +- .../Config/EnvVarsRawSnapshotSource.php | 2 +- prod/php/ElasticOTel/Log/Backend.php | 4 +- prod/php/ElasticOTel/Log/SinkToCExt.php | 4 +- prod/php/ElasticOTel/PhpPartFacade.php | 142 ++++ .../{SrcRootDir.php => ProdPhpDir.php} | 2 +- ...nUtil.php => ElasticOTelExtensionUtil.php} | 21 +- prod/php/ElasticOTel/Util/StackTraceUtil.php | 8 +- prod/php/bootstrap_php_part.php | 17 +- tools/build/build_native.sh | 18 +- tools/build/test_phpt.sh | 2 + 27 files changed, 241 insertions(+), 1387 deletions(-) delete mode 100644 prod/php/ElasticOTel/AutoInstrument/Autoloader.php delete mode 100644 prod/php/ElasticOTel/AutoInstrument/PhpPartFacade.php delete mode 100644 prod/php/ElasticOTel/AutoInstrument/TransactionForExtensionRequest.php delete mode 100644 prod/php/ElasticOTel/AutoInstrument/bootstrap_php_part.php rename prod/php/ElasticOTel/{AutoInstrument => }/BootstrapStageLogger.php (65%) create mode 100644 prod/php/ElasticOTel/PhpPartFacade.php rename prod/php/ElasticOTel/{SrcRootDir.php => ProdPhpDir.php} (97%) rename prod/php/ElasticOTel/Util/{ElasticApmExtensionUtil.php => ElasticOTelExtensionUtil.php} (66%) diff --git a/composer.json b/composer.json index fcdddc8..0bd2a3a 100644 --- a/composer.json +++ b/composer.json @@ -13,6 +13,7 @@ "nyholm/psr7": "^1.8", "open-telemetry/exporter-otlp": "1.0.4", "open-telemetry/opentelemetry-auto-laravel": "0.0.25", + "open-telemetry/opentelemetry-auto-slim": "1.0.6", "open-telemetry/sdk": "1.0.8", "php-http/guzzle7-adapter": "^1.0", "symfony/http-client": "6.*||7.*" @@ -23,7 +24,7 @@ "require-dev": { "php-parallel-lint/php-console-highlighter": "^1.0", "php-parallel-lint/php-parallel-lint": "1.3.2", - "phpstan/phpstan": "1.10.57", + "phpstan/phpstan": "1.11.4", "phpstan/phpstan-phpunit": "^1.3.15", "phpunit/phpunit": "^9.6||^10.5", "slevomat/coding-standard": "8.14.1", diff --git a/packaging/test/smokeTest.php b/packaging/test/smokeTest.php index ee2edb9..9dd9799 100644 --- a/packaging/test/smokeTest.php +++ b/packaging/test/smokeTest.php @@ -29,14 +29,14 @@ echo CGREEN."OK\n".CDEF; echo "Looking for PhpPartFacade class: "; -if (array_search("Elastic\OTel\AutoInstrument\PhpPartFacade", get_declared_classes()) === false) { - echo CRED."FAILED. Elastic\OTel\AutoInstrument\PhpPartFacade class not found. Bootstrap failed\n".CDEF; +if (array_search("Elastic\OTel\PhpPartFacade", get_declared_classes()) === false) { + echo CRED."FAILED. Elastic\OTel\PhpPartFacade class not found. Bootstrap failed\n".CDEF; exit(1); } echo CGREEN."OK\n".CDEF; echo "Trying to log something to stderr: "; -Elastic\OTel\AutoInstrument\BootstrapStageLogger::logCritical("This is just a message to test logger", __LINE__, __FUNCTION__); +Elastic\OTel\BootstrapStageLogger::logCritical("This is just a message to test logger", __LINE__, __FUNCTION__); echo CGREEN."OK\n".CDEF; echo CGREEN."Smoke test passed\n".CDEF; diff --git a/packaging/test/smokeTestUninstalled.php b/packaging/test/smokeTestUninstalled.php index f131cff..8abd8f5 100644 --- a/packaging/test/smokeTestUninstalled.php +++ b/packaging/test/smokeTestUninstalled.php @@ -21,8 +21,8 @@ echo CGREEN."OK\n".CDEF; echo "Looking for PhpPartFacade class: "; -if (array_search("Elastic\OTel\AutoInstrument\PhpPartFacade", get_declared_classes()) !== false) { - echo CRED."FAILED. Elastic\OTel\AutoInstrument\PhpPartFacade class not found. Bootstrap failed\n".CDEF; +if (array_search("Elastic\OTel\PhpPartFacade", get_declared_classes()) !== false) { + echo CRED."FAILED. Elastic\OTel\PhpPartFacade class not found. Bootstrap failed\n".CDEF; exit(1); } echo CGREEN."OK\n".CDEF; diff --git a/prod/native/extension/code/ModuleEntry.cpp b/prod/native/extension/code/ModuleEntry.cpp index 6e9d2fd..8b3ae31 100644 --- a/prod/native/extension/code/ModuleEntry.cpp +++ b/prod/native/extension/code/ModuleEntry.cpp @@ -120,7 +120,7 @@ PHP_GSHUTDOWN_FUNCTION(elastic_otel) { } // if (elastic_otel_globals->lastErrorData) { - // // elastic_otel_LOG_DIRECT_WARNING( "%s: still holding error", __FUNCTION__); + // // ELASTIC_OTEL_LOG_DIRECT_WARNING( "%s: still holding error", __FUNCTION__); // // we need to relese any dangling php error data beacause it is already freed (it was allocated in request pool) // elastic_otel_globals->lastErrorData.release(); // } diff --git a/prod/native/extension/code/ModuleInit.cpp b/prod/native/extension/code/ModuleInit.cpp index 6ccf8e0..31cb62d 100644 --- a/prod/native/extension/code/ModuleInit.cpp +++ b/prod/native/extension/code/ModuleInit.cpp @@ -129,12 +129,12 @@ void elasticApmModuleShutdown( int moduleType, int moduleNumber ) { // } // array_init( return_value ); -// ELASTIC_APM_ZEND_ADD_ASSOC(return_value, "type", long, static_cast(ELASTICAPM_G(lastErrorData)->getType())); -// ELASTIC_APM_ZEND_ADD_ASSOC_NULLABLE_STRING( return_value, "fileName", ELASTICAPM_G(lastErrorData)->getFileName().data() ); -// ELASTIC_APM_ZEND_ADD_ASSOC(return_value, "lineNumber", long, static_cast(ELASTICAPM_G(lastErrorData)->getLineNumber())); -// ELASTIC_APM_ZEND_ADD_ASSOC_NULLABLE_STRING( return_value, "message", ELASTICAPM_G(lastErrorData)->getMessage().data()); +// ELASTIC_OTEL_ZEND_ADD_ASSOC(return_value, "type", long, static_cast(ELASTICAPM_G(lastErrorData)->getType())); +// ELASTIC_OTEL_ZEND_ADD_ASSOC_NULLABLE_STRING( return_value, "fileName", ELASTICAPM_G(lastErrorData)->getFileName().data() ); +// ELASTIC_OTEL_ZEND_ADD_ASSOC(return_value, "lineNumber", long, static_cast(ELASTICAPM_G(lastErrorData)->getLineNumber())); +// ELASTIC_OTEL_ZEND_ADD_ASSOC_NULLABLE_STRING( return_value, "message", ELASTICAPM_G(lastErrorData)->getMessage().data()); // Z_TRY_ADDREF_P((ELASTICAPM_G(lastErrorData)->getStackTrace())); -// ELASTIC_APM_ZEND_ADD_ASSOC(return_value, "stackTrace", zval, (ELASTICAPM_G(lastErrorData)->getStackTrace())); +// ELASTIC_OTEL_ZEND_ADD_ASSOC(return_value, "stackTrace", zval, (ELASTICAPM_G(lastErrorData)->getStackTrace())); // } // // void elasticApmGetLastThrown(zval *return_value) { diff --git a/prod/native/extension/code/util_for_PHP.h b/prod/native/extension/code/util_for_PHP.h index 2f04de8..14f96e7 100644 --- a/prod/native/extension/code/util_for_PHP.h +++ b/prod/native/extension/code/util_for_PHP.h @@ -21,18 +21,18 @@ -#define ELASTIC_APM_PP_CONCAT_IMPL( token1, token2 ) token1##token2 -#define ELASTIC_APM_PP_CONCAT( token1, token2 ) ELASTIC_APM_PP_CONCAT_IMPL( token1, token2 ) +#define ELASTIC_OTEL_PP_CONCAT_IMPL( token1, token2 ) token1##token2 +#define ELASTIC_OTEL_PP_CONCAT( token1, token2 ) ELASTIC_OTEL_PP_CONCAT_IMPL( token1, token2 ) -#define ELASTIC_APM_ZEND_ADD_ASSOC( map, key, valueType, value ) ELASTIC_APM_PP_CONCAT( ELASTIC_APM_PP_CONCAT( add_assoc_, valueType ), _ex)( (map), (key), sizeof( key ) - 1, (value) ) +#define ELASTIC_OTEL_ZEND_ADD_ASSOC( map, key, valueType, value ) ELASTIC_OTEL_PP_CONCAT( ELASTIC_OTEL_PP_CONCAT( add_assoc_, valueType ), _ex)( (map), (key), sizeof( key ) - 1, (value) ) -#define ELASTIC_APM_ZEND_ADD_ASSOC_NULLABLE_STRING( map, key, value ) \ +#define ELASTIC_OTEL_ZEND_ADD_ASSOC_NULLABLE_STRING( map, key, value ) \ do { \ if ( (value) == NULL ) \ { \ - zval elastic_apm_zend_add_assoc_nullable_string_aux_zval; \ - ZVAL_NULL( &elastic_apm_zend_add_assoc_nullable_string_aux_zval ); \ - add_assoc_zval_ex( (map), (key), sizeof( key ) - 1, &elastic_apm_zend_add_assoc_nullable_string_aux_zval ); \ + zval elastic_otel_zend_add_assoc_nullable_string_aux_zval; \ + ZVAL_NULL( &elastic_otel_zend_add_assoc_nullable_string_aux_zval ); \ + add_assoc_zval_ex( (map), (key), sizeof( key ) - 1, &elastic_otel_zend_add_assoc_nullable_string_aux_zval ); \ } \ else \ { \ diff --git a/prod/native/extension/phpt/run.sh b/prod/native/extension/phpt/run.sh index bb8a438..95d4e0d 100755 --- a/prod/native/extension/phpt/run.sh +++ b/prod/native/extension/phpt/run.sh @@ -78,7 +78,6 @@ cp -R "${TESTS_TO_RUN}" "${LOCAL_TMP_DIR}/${TESTS_TO_RUN}" docker run --rm \ --platform ${DOCKER_PLATFORM} \ -v ${LOCAL_TMP_DIR}/tests:/phpt-tests/tests \ - -v ./tests_util:/phpt-tests/tests_util \ -v ${LOCAL_LOG_FAILED_TESTS}:${LOG_FAILED_TESTS} \ -v ${LOCAL_LOG_TEST_RUN}:${LOG_TEST_RUN} \ -v ${ELASTIC_AGENT_PHP_PATH}:/elastic/php \ diff --git a/prod/native/extension/phpt/tests/includes/bootstrap_mock.inc b/prod/native/extension/phpt/tests/includes/bootstrap_mock.inc index 973d726..0667840 100644 --- a/prod/native/extension/phpt/tests/includes/bootstrap_mock.inc +++ b/prod/native/extension/phpt/tests/includes/bootstrap_mock.inc @@ -1,32 +1,18 @@ - object(DateTime)#4 (3) { + object(DateTime)#%d (3) { ["date"]=> string(26) "2010-01-01 00:00:00.000000" ["timezone_type"]=> @@ -59,7 +59,7 @@ array(6) { args: array(8) { [0]=> - object(DateTime)#4 (3) { + object(DateTime)#%d (3) { ["date"]=> string(26) "2010-01-01 00:00:00.000000" ["timezone_type"]=> diff --git a/prod/native/libcommon/code/basic_macros.h b/prod/native/libcommon/code/basic_macros.h index ee6e7c5..2f166b1 100644 --- a/prod/native/libcommon/code/basic_macros.h +++ b/prod/native/libcommon/code/basic_macros.h @@ -17,5 +17,5 @@ * under the License. */ -#define ELASTIC_APM_PP_STRINGIZE_IMPL( token ) #token -#define EL_STRINGIFY( token ) ELASTIC_APM_PP_STRINGIZE_IMPL( token ) +#define ELASTIC_OTEL_PP_STRINGIZE_IMPL( token ) #token +#define EL_STRINGIFY( token ) ELASTIC_OTEL_PP_STRINGIZE_IMPL( token ) diff --git a/prod/native/libphpbridge/code/PhpBridge.cpp b/prod/native/libphpbridge/code/PhpBridge.cpp index be4b296..55125d0 100644 --- a/prod/native/libphpbridge/code/PhpBridge.cpp +++ b/prod/native/libphpbridge/code/PhpBridge.cpp @@ -52,7 +52,7 @@ std::optional PhpBridge::getCurrentExceptionMessage() const { } bool PhpBridge::callInferredSpans(std::chrono::milliseconds duration) const { - auto phpPartFacadeClass = findClassEntry("elastic\\apm\\impl\\autoinstrument\\phppartfacade"sv); + auto phpPartFacadeClass = findClassEntry("elastic\\apm\\impl\\phppartfacade"sv); if (!phpPartFacadeClass) { return false; } @@ -63,7 +63,7 @@ bool PhpBridge::callInferredSpans(std::chrono::milliseconds duration) const { } auto transactionForExtensionRequest = getClassPropertyValue(phpPartFacadeClass, objectOfPhpPartFacade, "transactionForExtensionRequest"sv); - if (!isObjectOfClass(transactionForExtensionRequest, "Elastic\\Apm\\Impl\\AutoInstrument\\TransactionForExtensionRequest")) { + if (!isObjectOfClass(transactionForExtensionRequest, "Elastic\\Apm\\Impl\\TransactionForExtensionRequest")) { return false; } @@ -174,28 +174,28 @@ void PhpBridge::compileAndExecuteFile(std::string_view fileName) const { } bool PhpBridge::callPHPSideEntryPoint(LogLevel logLevel, std::chrono::time_point requestInitStart) const { - auto phpPartFacadeClass = findClassEntry("elastic\\otel\\autoinstrument\\phppartfacade"sv); + auto phpPartFacadeClass = findClassEntry("elastic\\otel\\phppartfacade"sv); if (!phpPartFacadeClass) { return false; } std::array arguments{logLevel, (double)std::chrono::duration_cast(requestInitStart.time_since_epoch()).count()}; AutoZval rv; - return callMethod(nullptr, "\\Elastic\\OTel\\AutoInstrument\\PhpPartFacade::bootstrap"sv, arguments.data()->get(), arguments.size(), rv.get()); + return callMethod(nullptr, "\\Elastic\\OTel\\PhpPartFacade::bootstrap"sv, arguments.data()->get(), arguments.size(), rv.get()); } bool PhpBridge::callPHPSideExitPoint() const { - auto phpPartFacadeClass = findClassEntry("elastic\\otel\\autoinstrument\\phppartfacade"sv); + auto phpPartFacadeClass = findClassEntry("elastic\\otel\\phppartfacade"sv); if (!phpPartFacadeClass) { return false; } AutoZval rv; - return callMethod(nullptr, "Elastic\\OTel\\AutoInstrument\\PhpPartFacade::shutdown"sv, nullptr, 0, rv.get()); + return callMethod(nullptr, "Elastic\\OTel\\PhpPartFacade::shutdown"sv, nullptr, 0, rv.get()); } bool PhpBridge::callPHPSideErrorHandler(int type, std::string_view errorFilename, uint32_t errorLineno, std::string_view message) const { - auto phpPartFacadeClass = findClassEntry("elastic\\otel\\autoinstrument\\phppartfacade"sv); + auto phpPartFacadeClass = findClassEntry("elastic\\otel\\phppartfacade"sv); if (!phpPartFacadeClass) { return false; } @@ -203,7 +203,7 @@ bool PhpBridge::callPHPSideErrorHandler(int type, std::string_view errorFilename std::array arguments{type, errorFilename, errorLineno, message}; AutoZval rv; - return callMethod(nullptr, "\\Elastic\\OTel\\AutoInstrument\\PhpPartFacade::handle_error"sv, arguments.data()->get(), arguments.size(), rv.get()); + return callMethod(nullptr, "\\Elastic\\OTel\\PhpPartFacade::handleError"sv, arguments.data()->get(), arguments.size(), rv.get()); } diff --git a/prod/php/ElasticOTel/AutoInstrument/Autoloader.php b/prod/php/ElasticOTel/AutoInstrument/Autoloader.php deleted file mode 100644 index 9e2c215..0000000 --- a/prod/php/ElasticOTel/AutoInstrument/Autoloader.php +++ /dev/null @@ -1,103 +0,0 @@ -transactionForExtensionRequest = new TransactionForExtensionRequest($requestInitStartTime); - } - - /** - * Called by elastic_otel extension - * - * @noinspection PhpUnused - * - * @param int $maxEnabledLogLevel - * @param float $requestInitStartTime - * - * @return bool - */ - public static function bootstrap(int $maxEnabledLogLevel, float $requestInitStartTime): bool - { - BootstrapStageLogger::configure($maxEnabledLogLevel); - BootstrapStageLogger::logDebug( - 'Starting bootstrap sequence...' . " maxEnabledLogLevel: $maxEnabledLogLevel", - __LINE__, - __FUNCTION__ - ); - - if (self::$singletonInstance !== null) { - BootstrapStageLogger::logCritical( - 'bootstrap() is called even though singleton instance is already created' - . ' (probably bootstrap() is called more than once)', - __LINE__, - __FUNCTION__ - ); - return false; - } - - try { - self::$singletonInstance = new self($requestInitStartTime); - } catch (Throwable $throwable) { - BootstrapStageLogger::logCriticalThrowable( - $throwable, - 'One of the steps in bootstrap sequence let a throwable escape', - __LINE__, - __FUNCTION__ - ); - return false; - } - - BootstrapStageLogger::logDebug('Successfully completed bootstrap sequence', __LINE__, __FUNCTION__); - return true; - } - - private static function singletonInstance(): self - { - if (self::$singletonInstance === null) { - throw new RuntimeException( - 'Trying to use singleton instance that is not set' - . ' (probably either before call to bootstrap() or after failed call to bootstrap())' - ); - } - - return self::$singletonInstance; - } - - /** - * @param string $dbgCallDesc - * @param Closure $implFunc - * - * @return void - * - * @phpstan-param Closure(self): void $implFunc - */ - private static function callAndSwallowThrowable(string $dbgCallDesc, Closure $implFunc): void - { - BootstrapStageLogger::logDebug( - 'Starting to handle ' . $dbgCallDesc . ' call...', - __LINE__, - __FUNCTION__ - ); - - if (self::$singletonInstance === null) { - BootstrapStageLogger::logWarning( - 'Received ' . $dbgCallDesc . ' call but singleton instance is not created' - . ' (probably because bootstrap sequence failed)', - __LINE__, - __FUNCTION__ - ); - return; - } - - try { - $implFunc(self::singletonInstance()); - } catch (Throwable $throwable) { - BootstrapStageLogger::logCriticalThrowable( - $throwable, - 'Handling ' . $dbgCallDesc - . ' call let a throwable escape - skipping the rest of the steps', - __LINE__, - __FUNCTION__ - ); - return; - } - - BootstrapStageLogger::logDebug( - 'Successfully finished handling ' . $dbgCallDesc . ' call...', - __LINE__, - __FUNCTION__ - ); - } - - /** - * @param string $dbgCallDesc - * @param Closure $implFunc - * - * @return void - * - * @phpstan-param Closure(TransactionForExtensionRequest): void $implFunc - */ - private static function callWithTransactionForExtensionRequest(string $dbgCallDesc, Closure $implFunc): void - { - self::callAndSwallowThrowable( - $dbgCallDesc, - function (PhpPartFacade $singletonInstance) use ($implFunc): void { - if ($singletonInstance->transactionForExtensionRequest === null) { - BootstrapStageLogger::logDebug( - 'Received call but transactionForExtensionRequest is null' - . ' - just returning...', - __LINE__, - __FUNCTION__ - ); - return; - } - - $implFunc($singletonInstance->transactionForExtensionRequest); - } - ); - } - - public static function ensureHaveLatestDataDeferredByExtension(): void - { - self::callWithTransactionForExtensionRequest( - __FUNCTION__, - function (TransactionForExtensionRequest $transactionForExtensionRequest): void { - self::ensureHaveLastErrorData($transactionForExtensionRequest); - } - ); - } - - private static function ensureHaveLastErrorData( - TransactionForExtensionRequest $transactionForExtensionRequest - ): void { - if (!$transactionForExtensionRequest->getConfig()->captureErrors()) { - return; - } - - /** - * The last thrown should be fetched before last PHP error because if the error is for "Uncaught Exception" - * agent will use the last thrown exception - */ - self::ensureHaveLastThrown($transactionForExtensionRequest); - self::ensureHaveLastPhpError($transactionForExtensionRequest); - } - - private static function ensureHaveLastThrown(TransactionForExtensionRequest $transactionForExtensionRequest): void - { - /** - * elastic_otel_* functions are provided by the elastic_otel extension - * - * @var mixed $lastThrown - * - * @noinspection PhpFullyQualifiedNameUsageInspection, PhpUndefinedFunctionInspection - * @phpstan-ignore-next-line - */ - $lastThrown = \elastic_otel_get_last_thrown(); - if ($lastThrown === null) { - return; - } - - $transactionForExtensionRequest->setLastThrown($lastThrown); - } - - /** - * @param string $expectedType - * @param mixed $actualValue - * - * @return void - */ - private static function logUnexpectedType(string $expectedType, $actualValue): void - { - BootstrapStageLogger::logCritical( - 'Actual type does not match the expected type' - . '; ' . 'expected type: ' . $expectedType - . ', ' . 'actual type: ' . DbgUtil::getType($actualValue) - . ', ' . 'actual value: ' . LoggableToString::convert($actualValue), - __LINE__, - __FUNCTION__ - ); - } - - /** - * @param string $expectedKey - * @param array $actualArray - * - * @return bool - */ - private static function verifyKeyExists(string $expectedKey, array $actualArray): bool - { - if (array_key_exists($expectedKey, $actualArray)) { - return true; - } - - BootstrapStageLogger::logCritical( - 'Expected key does not exist' - . '; ' . 'expected key: ' . $expectedKey - . ', ' . 'actual array keys: ' . json_encode(array_keys($actualArray)), - __LINE__, - __FUNCTION__ - ); - return false; - } - - /** - * @param array $dataFromExt - * @param string $key - * - * @return ?int - */ - private static function getIntFromPhpErrorData(array $dataFromExt, string $key): ?int - { - if (!self::verifyKeyExists($key, $dataFromExt)) { - return null; - } - $value = $dataFromExt[$key]; - if (!is_int($value)) { - self::logUnexpectedType('int', $value); - return null; - } - return $value; - } - - /** - * @param array $dataFromExt - * @param string $key - * - * @return ?string - */ - private static function getNullableStringFromPhpErrorData(array $dataFromExt, string $key): ?string - { - if (!self::verifyKeyExists($key, $dataFromExt)) { - return null; - } - $value = $dataFromExt[$key]; - if (!($value === null || is_string($value))) { - self::logUnexpectedType('string|null', $value); - return null; - } - return $value; - } - - /** - * @param array $dataFromExt - * @param string $key - * - * @return null|array[] - */ - private static function getStackTraceFromPhpErrorData(array $dataFromExt, string $key): ?array - { - if (!self::verifyKeyExists($key, $dataFromExt)) { - return null; - } - $stackTrace = $dataFromExt[$key]; - if (!is_array($stackTrace)) { - self::logUnexpectedType('array', $stackTrace); - return null; - } - if (!ArrayUtil::isList($stackTrace)) { - BootstrapStageLogger::logCritical( - 'Stack trace array should be a list but it is not' - . '; ' . 'stackTrace keys: ' . json_encode(array_keys($stackTrace)) - . ', ' . 'stackTrace: ' . LoggableToString::convert($stackTrace), - __LINE__, - __FUNCTION__ - ); - return null; - } - - /** @var array[] $stackTrace */ - return $stackTrace; - } - - /** - * @param array $dataFromExt - * - * @return PhpErrorData - */ - private static function buildPhpErrorData(array $dataFromExt): PhpErrorData - { - $result = new PhpErrorData(); - $result->type = self::getIntFromPhpErrorData($dataFromExt, 'type'); - $result->fileName = self::getNullableStringFromPhpErrorData($dataFromExt, 'fileName'); - $result->lineNumber = self::getIntFromPhpErrorData($dataFromExt, 'lineNumber'); - $result->message = self::getNullableStringFromPhpErrorData($dataFromExt, 'message'); - $result->stackTrace = self::getStackTraceFromPhpErrorData($dataFromExt, 'stackTrace'); - return $result; - } - - private static function ensureHaveLastPhpError(TransactionForExtensionRequest $transactionForExtensionRequest): void - { - /** - * elastic_otel_* functions are provided by the elastic_otel extension - * - * @noinspection PhpFullyQualifiedNameUsageInspection, PhpUndefinedFunctionInspection - * @phpstan-ignore-next-line - */ - $lastPhpErrorData = \elastic_otel_get_last_php_error(); - if ($lastPhpErrorData === null) { - return; - } - - if (is_array($lastPhpErrorData)) { - BootstrapStageLogger::logDebug( - 'Type of value returned by elastic_otel_get_last_php_error(): ' . DbgUtil::getType($lastPhpErrorData), - __LINE__, - __FUNCTION__ - ); - } else { - BootstrapStageLogger::logCritical( - 'Value returned by elastic_otel_get_last_php_error() is not an array' - . ', ' . 'returned value type: ' . DbgUtil::getType($lastPhpErrorData) - . ', ' . 'returned value: ' . $lastPhpErrorData, - __LINE__, - __FUNCTION__ - ); - return; - } - /** @var array $lastPhpErrorData */ - - $transactionForExtensionRequest->onPhpError(self::buildPhpErrorData($lastPhpErrorData)); - } - - /** - * Called by elastic_otel extension - * - * @noinspection PhpUnused - */ - public static function shutdown(): void - { - self::callWithTransactionForExtensionRequest( - __FUNCTION__, - function (TransactionForExtensionRequest $transactionForExtensionRequest): void { - $transactionForExtensionRequest->onShutdown(); - } - ); - - self::$singletonInstance = null; - } - - /** - * Called by elastic_otel extension - * - * @noinspection PhpUnused - * - * @param int $maxEnabledLogLevel - * @param float $requestInitStartTime - * - * @return bool - */ - public static function handle_error() - { - var_dump(func_get_args()); - } - -} \ No newline at end of file diff --git a/prod/php/ElasticOTel/AutoInstrument/TransactionForExtensionRequest.php b/prod/php/ElasticOTel/AutoInstrument/TransactionForExtensionRequest.php deleted file mode 100644 index 395cf34..0000000 --- a/prod/php/ElasticOTel/AutoInstrument/TransactionForExtensionRequest.php +++ /dev/null @@ -1,683 +0,0 @@ -tracer = $tracer; - $this->logger = $tracer->loggerFactory() - ->loggerForClass(LogCategory::AUTO_INSTRUMENTATION, __NAMESPACE__, __CLASS__, __FILE__) - ->addContext('this', $this); - - $this->transactionForRequest = $this->beginTransaction($requestInitStartTime); - - $this->tracer->onNewCurrentTransactionHasBegun->add( - function (Transaction $transaction): void { - PhpPartFacade::ensureHaveLatestDataDeferredByExtension(); - $transaction->onAboutToEnd->add( - function (/** @noinspection PhpUnusedParameterInspection */ Transaction $ignored): void { - PhpPartFacade::ensureHaveLatestDataDeferredByExtension(); - } - ); - $transaction->onCurrentSpanChanged->add( - function (?Span $span): void { - PhpPartFacade::ensureHaveLatestDataDeferredByExtension(); - if ($span !== null) { - $span->onAboutToEnd->add( - function (/** @noinspection PhpUnusedParameterInspection */ Span $ignored): void { - PhpPartFacade::ensureHaveLatestDataDeferredByExtension(); - } - ); - } - } - ); - } - ); - } - - public function getConfig(): ConfigSnapshot - { - return $this->tracer->getConfig(); - } - - private function beginTransaction(float $requestInitStartTime): ?TransactionInterface - { - if (!self::isCliScript()) { - if (!$this->discoverHttpRequestData()) { - return null; - } - } - $name = self::isCliScript() ? $this->discoverCliName() : $this->discoverHttpName(); - $type = self::isCliScript() ? Constants::TRANSACTION_TYPE_CLI : Constants::TRANSACTION_TYPE_REQUEST; - $timestamp = $this->discoverStartTime($requestInitStartTime); - $distributedTracingHeaders = $this->getDistributedTracingHeaders(); - $distributedTracingHeaderExtractor = function (string $headerName) use ($distributedTracingHeaders): ?string { - return ArrayUtil::getValueIfKeyExistsElse($headerName, $distributedTracingHeaders, null); - }; - $tx = $this->tracer->newTransaction($name, $type) - ->asCurrent() - ->timestamp($timestamp) - ->distributedTracingHeaderExtractor($distributedTracingHeaderExtractor) - ->begin(); - if (!self::isCliScript() && !$tx->isNoop()) { - $this->setTxPropsBasedOnHttpRequestData($tx); - } - - return $tx; - } - - private static function isGlobalServerVarSet(): bool - { - /** - * Sometimes $_SERVER is not set. It seems related to auto_globals_jit - * but it's not easily reproducible even with auto_globals_jit=On - * See also https://bugs.php.net/bug.php?id=69081 - * - * Disable PHPStan complaining: - * Variable $_SERVER in isset() always exists and is not nullable. - * - * @noinspection PhpConditionCheckedByNextConditionInspection - * - * @phpstan-ignore-next-line - */ - return isset($_SERVER) && !empty($_SERVER); - } - - private function discoverHttpRequestData(): bool - { - /** @phpstan-ignore-next-line */ - if (!self::isGlobalServerVarSet()) { - ($loggerProxy = $this->logger->ifDebugLevelEnabled(__LINE__, __FUNCTION__)) - && $loggerProxy->log('$_SERVER variable is not populated - forcing PHP engine to populate it...'); - - /** @phpstan-ignore-next-line */ - if (!self::isGlobalServerVarSet()) { - ($loggerProxy = $this->logger->ifErrorLevelEnabled(__LINE__, __FUNCTION__)) - && $loggerProxy->log( - '$_SERVER variable is not populated even after forcing PHP engine to populate it' - . ' - agent will have to fallback on defaults' - ); - return true; - } - } - - /** @var ?string $urlPath */ - $urlPath = null; - /** @var ?string $urlQuery */ - $urlQuery = null; - - $pathQuery = $this->getMandatoryServerVarStringElement('REQUEST_URI'); - if (is_string($pathQuery)) { - UrlUtil::splitPathQuery($pathQuery, /* ref */ $urlPath, /* ref */ $urlQuery); - if ($urlPath === null) { - ($loggerProxy = $this->logger->ifErrorLevelEnabled(__LINE__, __FUNCTION__)) - && $loggerProxy->log( - 'Failed to extract path part from $_SERVER["REQUEST_URI"]', - ['$_SERVER["REQUEST_URI"]' => $pathQuery] - ); - } else { - if ($this->shouldHttpTransactionBeIgnored($urlPath)) { - return false; - } - } - } - - $this->httpMethod = $this->getMandatoryServerVarStringElement('REQUEST_METHOD'); - - $this->urlParts = new UrlParts(); - $this->urlParts->path = $urlPath; - $this->urlParts->query = $urlQuery; - - $serverHttps = self::getOptionalServerVarElement('HTTPS'); - $this->urlParts->scheme = !empty($serverHttps) ? 'https' : 'http'; - - $hostPort = $this->getMandatoryServerVarStringElement('HTTP_HOST'); - if ($hostPort !== null) { - UrlUtil::splitHostPort($hostPort, /* ref */ $this->urlParts->host, /* ref */ $this->urlParts->port); - if ($this->urlParts->host === null) { - ($loggerProxy = $this->logger->ifErrorLevelEnabled(__LINE__, __FUNCTION__)) - && $loggerProxy->log( - 'Failed to extract host part from $_SERVER["HTTP_HOST"]', - ['$_SERVER["HTTP_HOST"]' => $hostPort] - ); - } - } - - $queryString = self::getOptionalServerVarElement('QUERY_STRING'); - if (is_string($queryString)) { - $this->urlParts->query = $queryString; - } - - $this->fullUrl = self::buildFullUrl($this->urlParts->scheme, $hostPort, $pathQuery); - return true; - } - - private function shouldHttpTransactionBeIgnored(string $urlPath): bool - { - $ignoreMatcher = $this->tracer->getConfig()->transactionIgnoreUrls(); - $matchedIgnoreExpr = WildcardListMatcher::matchNullable($ignoreMatcher, $urlPath); - if ($matchedIgnoreExpr !== null) { - ($loggerProxy = $this->logger->ifDebugLevelEnabled(__LINE__, __FUNCTION__)) - && $loggerProxy->log( - 'Transaction is ignored because its URL path matched ' . OptionNames::TRANSACTION_IGNORE_URLS - . ' configuration', - [ - 'urlPath' => $urlPath, - 'matched ignore expression' => $matchedIgnoreExpr, - OptionNames::TRANSACTION_IGNORE_URLS . ' configuration' => $ignoreMatcher, - ] - ); - return true; - } - - return false; - } - - private static function buildFullUrl(?string $scheme, ?string $hostPort, ?string $pathQuery): ?string - { - if ($hostPort === null) { - return null; - } - - $fullUrl = ''; - - if ($scheme !== null) { - $fullUrl .= $scheme . '://'; - } - - $fullUrl .= $hostPort; - - if ($pathQuery !== null) { - $fullUrl .= $pathQuery; - } - - return $fullUrl; - } - - private function setTxPropsBasedOnHttpRequestData(TransactionInterface $tx): void - { - if ($this->httpMethod !== null) { - $tx->context()->request()->setMethod($this->httpMethod); - } - - if ($this->urlParts !== null) { - if ($this->urlParts->host !== null) { - $tx->context()->request()->url()->setDomain($this->urlParts->host); - } - if ($this->urlParts->path !== null) { - $tx->context()->request()->url()->setPath($this->urlParts->path); - } - if ($this->urlParts->port !== null) { - $tx->context()->request()->url()->setPort($this->urlParts->port); - } - if ($this->urlParts->scheme !== null) { - $tx->context()->request()->url()->setProtocol($this->urlParts->scheme); - } - if ($this->urlParts->query !== null) { - $tx->context()->request()->url()->setQuery($this->urlParts->query); - } - } - - if ($this->fullUrl !== null) { - $tx->context()->request()->url()->setFull($this->fullUrl); - $tx->context()->request()->url()->setOriginal($this->fullUrl); - } - } - - private function beforeHttpEnd(TransactionInterface $tx): void - { - if ($tx->getResult() === null) { - $this->discoverHttpResult($tx); - } - - if ($tx->getOutcome() === null) { - $this->discoverHttpOutcome($tx); - } - - if ($tx->getOutcome() === Constants::OUTCOME_FAILURE && $this->lastThrown !== null) { - $this->tracer->createErrorFromThrowable($this->lastThrown); - } - } - - private function logGcStatus(): void - { - if (!function_exists('gc_status')) { - return; - } - - /** @phpstan-ignore-next-line */ - $gcStatusRetVal = gc_status(); - - ($loggerProxy = $this->logger->ifDebugLevelEnabled(__LINE__, __FUNCTION__)) - && $loggerProxy->log('Called gc_status()', ['gc_status() return value' => $gcStatusRetVal]); - } - - public function onPhpError(PhpErrorData $phpErrorData): void - { - $relatedThrowable = null; - if ( - $this->lastThrown !== null - && $phpErrorData->message !== null - && TextUtil::isPrefixOf('Uncaught Exception: ', $phpErrorData->message, /* isCaseSensitive: */ false) - ) { - $relatedThrowable = $this->lastThrown; - $this->lastThrown = null; - } - $this->tracer->onPhpError($phpErrorData, $relatedThrowable, /* numberOfStackFramesToSkip */ 1); - } - - /** - * @param mixed $lastThrown - * - * @return void - */ - public function setLastThrown($lastThrown): void - { - ($loggerProxy = $this->logger->ifDebugLevelEnabled(__LINE__, __FUNCTION__)) - && $loggerProxy->log('Entered', ['lastThrown' => $lastThrown]); - - if (!($lastThrown instanceof Throwable)) { - ($loggerProxy = $this->logger->ifErrorLevelEnabled(__LINE__, __FUNCTION__)) - && $loggerProxy->log( - 'lastThrown is not an instance of Throwable - ignoring it...', - ['lastThrown' => $lastThrown] - ); - return; - } - - $this->lastThrown = $lastThrown; - } - - public function onShutdown(): void - { - ($loggerProxy = $this->logger->ifDebugLevelEnabled(__LINE__, __FUNCTION__)) - && $loggerProxy->log('Entered'); - - PhpPartFacade::ensureHaveLatestDataDeferredByExtension(); - - $tx = $this->transactionForRequest; - if ($tx === null || $tx->isNoop() || $tx->hasEnded()) { - return; - } - - if (!self::isCliScript()) { - $this->beforeHttpEnd($tx); - } - - $tx->end(); - - if ($this->tracer->getConfig()->devInternal()->gcCollectCyclesAfterEveryTransaction()) { - $this->logGcStatus(); - - $numberOfCollectedCycles = gc_collect_cycles(); - ($loggerProxy = $this->logger->ifDebugLevelEnabled(__LINE__, __FUNCTION__)) - && $loggerProxy->log( - 'Called gc_collect_cycles() because ' . OptionNames::DEV_INTERNAL - . ' sub-option ' . DevInternalSubOptionNames::GC_COLLECT_CYCLES_AFTER_EVERY_TRANSACTION . ' is set', - ['numberOfCollectedCycles' => $numberOfCollectedCycles] - ); - - $this->logGcStatus(); - } - - if ($this->tracer->getConfig()->devInternal()->gcMemCachesAfterEveryTransaction()) { - $numberOfBytesFreed = gc_mem_caches(); - ($loggerProxy = $this->logger->ifDebugLevelEnabled(__LINE__, __FUNCTION__)) - && $loggerProxy->log( - 'Called gc_mem_caches() because ' . OptionNames::DEV_INTERNAL - . ' sub-option ' . DevInternalSubOptionNames::GC_MEM_CACHES_AFTER_EVERY_TRANSACTION . ' is set', - ['numberOfBytesFreed' => $numberOfBytesFreed] - ); - } - } - - private static function isCliScript(): bool - { - return PHP_SAPI === 'cli'; - } - - private function discoverCliName(): string - { - global $argc, $argv; - - /** @noinspection PhpConditionAlreadyCheckedInspection */ - if ( - !isset($argc) - || ($argc <= 0) - || !isset($argv) - || (count($argv) == 0) - || !is_string($argv[0]) - || TextUtil::isEmptyString($argv[0]) - ) { - ($loggerProxy = $this->logger->ifDebugLevelEnabled(__LINE__, __FUNCTION__)) - && $loggerProxy->log( - 'Could not discover CLI script name - using default transaction name', - ['DEFAULT_NAME' => self::DEFAULT_NAME] - ); - return self::DEFAULT_NAME; - } - - $cliScriptName = basename($argv[0]); - if ( - ($argc < 2) - || (count($argv) < 2) - || ($cliScriptName !== self::LARAVEL_ARTISAN_COMMAND_SCRIPT) - ) { - ($loggerProxy = $this->logger->ifDebugLevelEnabled(__LINE__, __FUNCTION__)) - && $loggerProxy->log( - 'Using CLI script name as transaction name', - ['cliScriptName' => $cliScriptName, 'argc' => $argc, 'argv' => $argv] - ); - return $cliScriptName; - } - - $txName = $cliScriptName . ' ' . $argv[1]; - ($loggerProxy = $this->logger->ifDebugLevelEnabled(__LINE__, __FUNCTION__)) - && $loggerProxy->log( - 'CLI script is Laravel ' . self::LARAVEL_ARTISAN_COMMAND_SCRIPT . ' command with arguments' - . ' - including the first argument in transaction name', - ['txName' => $txName, 'argc' => $argc, 'argv' => $argv] - ); - return $txName; - } - - /** - * @param string $key - * - * @return mixed - */ - private function getOptionalServerVarElement(string $key) - { - /** @noinspection PhpIssetCanBeReplacedWithCoalesceInspection */ - return isset($_SERVER[$key]) ? $_SERVER[$key] : null; - } - - /** - * @param string $key - * - * @return mixed - */ - private function getMandatoryServerVarElement(string $key) - { - $val = $this->getOptionalServerVarElement($key); - if ($val === null) { - ($loggerProxy = $this->logger->ifErrorLevelEnabled(__LINE__, __FUNCTION__)) - && $loggerProxy->log('$_SERVER does not contain `' . $key . '\' key'); - return null; - } - - return $_SERVER[$key]; - } - - private function getMandatoryServerVarStringElement(string $key): ?string - { - $val = $this->getMandatoryServerVarElement($key); - if ($val === null) { - /** @noinspection PhpExpressionAlwaysNullInspection */ - return $val; - } - - if (!is_string($val)) { - ($loggerProxy = $this->logger->ifErrorLevelEnabled(__LINE__, __FUNCTION__)) - && $loggerProxy->log( - '$_SERVER contains `' . $key . '\' key but the value is not a string', - ['value type' => DbgUtil::getType($val)] - ); - return null; - } - - return $val; - } - - private function discoverHttpName(): string - { - if ($this->urlParts === null || $this->urlParts->path === null) { - ($loggerProxy = $this->logger->ifErrorLevelEnabled(__LINE__, __FUNCTION__)) - && $loggerProxy->log( - 'Failed to discover path part of URL to derive transaction name - using default transaction name', - ['DEFAULT_NAME' => self::DEFAULT_NAME] - ); - return self::DEFAULT_NAME; - } - - $urlGroupsMatcher = $this->tracer->getConfig()->urlGroups(); - $urlPath = $this->urlParts->path; - - $urlPathGroup = WildcardListMatcher::matchNullable($urlGroupsMatcher, $urlPath); - if ($urlPathGroup !== null) { - ($loggerProxy = $this->logger->ifDebugLevelEnabled(__LINE__, __FUNCTION__)) - && $loggerProxy->log( - 'For transaction name URL path is mapped to matched URL group', - [ - 'urlPath' => $urlPath, - 'matched URL group' => $urlPathGroup, - OptionNames::URL_GROUPS . ' configuration' => $urlGroupsMatcher, - ] - ); - } - - if ($urlPathGroup === null) { - $urlPathGroup = $urlPath; - } - - $name = ($this->httpMethod === null) - ? $urlPathGroup - : ($this->httpMethod . ' ' . $urlPathGroup); - - ($loggerProxy = $this->logger->ifDebugLevelEnabled(__LINE__, __FUNCTION__)) - && $loggerProxy->log('Successfully discovered HTTP data to derive transaction name', ['name' => $name]); - - return $name; - } - - private function discoverStartTime(float $requestInitStartTime): float - { - $serverRequestTimeAsString = self::getMandatoryServerVarElement('REQUEST_TIME_FLOAT'); - if ($serverRequestTimeAsString === null) { - ($loggerProxy = $this->logger->ifDebugLevelEnabled(__LINE__, __FUNCTION__)) - && $loggerProxy->log( - 'Using requestInitStartTime for transaction start time' - . ' because $_SERVER[\'REQUEST_TIME_FLOAT\'] is not set', - ['requestInitStartTime' => $requestInitStartTime] - ); - return $requestInitStartTime; - } - - $serverRequestTimeInSeconds = floatval($serverRequestTimeAsString); - $serverRequestTimeInMicroseconds = $serverRequestTimeInSeconds * TimeUtil::NUMBER_OF_MICROSECONDS_IN_SECOND; - if ($requestInitStartTime < $serverRequestTimeInMicroseconds) { - ($loggerProxy = $this->logger->ifDebugLevelEnabled(__LINE__, __FUNCTION__)) - && $loggerProxy->log( - 'Using requestInitStartTime for transaction start time' - . ' because $_SERVER[\'REQUEST_TIME_FLOAT\'] is later' - . ' (further into the future) than requestInitStartTime', - [ - 'requestInitStartTime' => $requestInitStartTime, - '$_SERVER[\'REQUEST_TIME_FLOAT\']' => $serverRequestTimeInMicroseconds, - '$_SERVER[\'REQUEST_TIME_FLOAT\'] - requestInitStartTime (seconds)' - => TimeUtil::microsecondsToSeconds( - $serverRequestTimeInMicroseconds - $requestInitStartTime - ), - ] - ); - return $requestInitStartTime; - } - - ($loggerProxy = $this->logger->ifDebugLevelEnabled(__LINE__, __FUNCTION__)) - && $loggerProxy->log( - 'Using $_SERVER[\'REQUEST_TIME_FLOAT\'] for transaction start time', - [ - '$_SERVER[\'REQUEST_TIME_FLOAT\']' => $serverRequestTimeInMicroseconds, - 'requestInitStartTime' => $requestInitStartTime, - 'requestInitStartTime - $_SERVER[\'REQUEST_TIME_FLOAT\'] (seconds)' - => TimeUtil::microsecondsToSeconds( - $serverRequestTimeInMicroseconds - $requestInitStartTime - ), - ] - ); - - return $serverRequestTimeInMicroseconds; - } - - /** - * @return array - */ - private function getDistributedTracingHeaders(): array - { - $result = []; - $traceParentHeaderValue = $this->getHttpHeader(HttpDistributedTracing::TRACE_PARENT_HEADER_NAME); - if ($traceParentHeaderValue === null) { - return []; - } - $result[HttpDistributedTracing::TRACE_PARENT_HEADER_NAME] = $traceParentHeaderValue; - - $traceStateHeaderValue = $this->getHttpHeader(HttpDistributedTracing::TRACE_STATE_HEADER_NAME); - if ($traceStateHeaderValue !== null) { - $result[HttpDistributedTracing::TRACE_STATE_HEADER_NAME] = $traceStateHeaderValue; - } - - return $result; - } - - private function getHttpHeader(string $headerName): ?string - { - $headerKey = 'HTTP_' . strtoupper($headerName); - - $traceParentHeaderValue = self::getOptionalServerVarElement($headerKey); - if ($traceParentHeaderValue === null) { - ($loggerProxy = $this->logger->ifDebugLevelEnabled(__LINE__, __FUNCTION__)) - && $loggerProxy->log('Incoming ' . $headerName . ' HTTP request header not found'); - return null; - } - - if (!is_string($traceParentHeaderValue)) { - ($loggerProxy = $this->logger->ifErrorLevelEnabled(__LINE__, __FUNCTION__)) - && $loggerProxy->log( - '$_SERVER contains `' . $headerKey . '\' key but the value is not a string', - ['value type' => DbgUtil::getType($traceParentHeaderValue)] - ); - return null; - } - - ($loggerProxy = $this->logger->ifDebugLevelEnabled(__LINE__, __FUNCTION__)) - && $loggerProxy->log( - 'Incoming ' . HttpDistributedTracing::TRACE_PARENT_HEADER_NAME . ' HTTP request header found', - ['traceParentHeaderValue' => $traceParentHeaderValue] - ); - - return $traceParentHeaderValue; - } - - private function discoverHttpStatusCode(): ?int - { - $statusCode = http_response_code(); - if (!is_int($statusCode)) { - ($loggerProxy = $this->logger->ifDebugLevelEnabled(__LINE__, __FUNCTION__)) - && $loggerProxy->log( - 'http_response_code() returned a value that is not an int', - ['statusCode' => $statusCode] - ); - return null; - } - - return $statusCode; - } - - private function discoverHttpResult(TransactionInterface $tx): void - { - $httpStatusCode = $this->discoverHttpStatusCode(); - if ($httpStatusCode === null) { - return; - } - - $httpStatusCode100s = intdiv($httpStatusCode, 100); - $result = 'HTTP ' . $httpStatusCode100s . 'xx'; - $tx->setResult($result); - - ($loggerProxy = $this->logger->ifDebugLevelEnabled(__LINE__, __FUNCTION__)) - && $loggerProxy->log( - 'Discovered result for HTTP transaction', - ['httpStatusCode' => $httpStatusCode, 'result' => $result, 'httpStatusCode100s' => $httpStatusCode100s] - ); - } - - private function discoverHttpOutcome(TransactionInterface $tx): void - { - $httpStatusCode = $this->discoverHttpStatusCode(); - if ($httpStatusCode === null) { - return; - } - - $outcome = (500 <= $httpStatusCode && $httpStatusCode < 600) - ? Constants::OUTCOME_FAILURE - : Constants::OUTCOME_SUCCESS; - $tx->setOutcome($outcome); - - ($loggerProxy = $this->logger->ifDebugLevelEnabled(__LINE__, __FUNCTION__)) - && $loggerProxy->log( - 'Discovered outcome for HTTP transaction', - ['httpStatusCode' => $httpStatusCode, 'outcome' => $outcome] - ); - } -} \ No newline at end of file diff --git a/prod/php/ElasticOTel/AutoInstrument/bootstrap_php_part.php b/prod/php/ElasticOTel/AutoInstrument/bootstrap_php_part.php deleted file mode 100644 index bc3e529..0000000 --- a/prod/php/ElasticOTel/AutoInstrument/bootstrap_php_part.php +++ /dev/null @@ -1,30 +0,0 @@ -maxEnabledLevel = $maxEnabledLevel; $this->logSink = $logSink ?? - (ElasticApmExtensionUtil::isLoaded() + (ElasticOTelExtensionUtil::isLoaded() ? new SinkToCExt() : NoopLogSink::singletonInstance()); } diff --git a/prod/php/ElasticOTel/Log/SinkToCExt.php b/prod/php/ElasticOTel/Log/SinkToCExt.php index 8789a86..9981049 100644 --- a/prod/php/ElasticOTel/Log/SinkToCExt.php +++ b/prod/php/ElasticOTel/Log/SinkToCExt.php @@ -39,12 +39,12 @@ protected function consumePreformatted( string $messageWithContext ): void { /** - * elastic_apm_* functions are provided by the elastic_apm extension + * elastic_otel_* functions are provided by the elastic_apm extension * * @noinspection PhpFullyQualifiedNameUsageInspection, PhpUndefinedFunctionInspection * @phpstan-ignore-next-line */ - \elastic_apm_log( + \elastic_otel_log( 0 /* $isForced */, $statementLevel, $category, diff --git a/prod/php/ElasticOTel/PhpPartFacade.php b/prod/php/ElasticOTel/PhpPartFacade.php new file mode 100644 index 0000000..547123e --- /dev/null +++ b/prod/php/ElasticOTel/PhpPartFacade.php @@ -0,0 +1,142 @@ +loggerFactory = $loggerFactory; $this->logger = $this->loggerFactory->loggerForClass(LogCategory::INFRASTRUCTURE, __NAMESPACE__, __CLASS__, __FILE__); diff --git a/prod/php/bootstrap_php_part.php b/prod/php/bootstrap_php_part.php index 19e6ed9..b5b02a1 100644 --- a/prod/php/bootstrap_php_part.php +++ b/prod/php/bootstrap_php_part.php @@ -1,15 +1,15 @@ [--ncpu ] [--conan_user_home ] [--skip_configure]" + echo "Usage: $0 --build_architecture [--ncpu ] [--conan_user_home ] [--skip_configure] [--skip_unit_tests]" echo echo "Arguments:" echo " --build_architecture Required. Build architecture (e.g., 'linux-x86-64')." @@ -11,6 +13,7 @@ show_help() { echo " --conan_user_home Optional. Path to local user cache for Conan." echo " --skip_configure Optional. Skip the configuration step." echo " --interactive Optional. Run container in interactive mode." + echo " --skip_unit_tests Optional. Skip unit tests. Default is to run unit tests." echo echo "Example:" echo " $0 --build_architecture linux-x86-64 --ncpu 4 --conan_user_home ~/ --skip_configure" @@ -37,6 +40,9 @@ parse_args() { --interactive) INTERACTIVE=" -i " ;; + --skip_unit_tests) + SKIP_UNIT_TESTS=true + ;; --help) show_help exit 0 @@ -83,10 +89,18 @@ else USERID=" -u $(id -u):$(id -g) " fi +if [ "$SKIP_UNIT_TESTS" = true ]; then + UNIT_TESTS="echo \"Skipped unit tests (SKIP_UNIT_TESTS: $SKIP_UNIT_TESTS).\"" +else + UNIT_TESTS="ctest --preset ${BUILD_ARCHITECTURE}-release --verbose" +fi + +ls -al "${PWD}" + docker run --rm -t ${INTERACTIVE} ${USERID} -v ${PWD}:/source \ ${CONAN_USER_HOME_MP} \ -w /source/prod/native \ -e GITHUB_SHA=${GITHUB_SHA} \ elasticobservability/apm-agent-php-dev:native-build-gcc-12.2.0-${BUILD_ARCHITECTURE}-0.0.2 \ - sh -c "id && echo CONAN_USER_HOME=\$CONAN_USER_HOME && ${CONFIGURE} cmake --build --preset ${BUILD_ARCHITECTURE}-release ${NCPU} && ctest --preset ${BUILD_ARCHITECTURE}-release --verbose" + sh -c "id && echo CONAN_USER_HOME=\$CONAN_USER_HOME && ${CONFIGURE} cmake --build --preset ${BUILD_ARCHITECTURE}-release ${NCPU} && ${UNIT_TESTS}" diff --git a/tools/build/test_phpt.sh b/tools/build/test_phpt.sh index 7c439c6..f335080 100755 --- a/tools/build/test_phpt.sh +++ b/tools/build/test_phpt.sh @@ -1,5 +1,7 @@ #!/bin/bash +set -x + show_help() { echo "Usage: $0 --build_architecture --php_versions --results_path " echo