prod/native/extension/code/ModuleEntry.cpp (137 lines of code) (raw):
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch B.V. licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
#ifdef HAVE_CONFIG_H
# include "config.h"
#endif
#include "elastic_otel_version.h"
#include "ModuleGlobals.h"
// external libraries
#include <main/php.h>
#include <Zend/zend_types.h>
#include "ModuleInit.h"
#include "AutoZval.h"
#include "os/OsUtils.h"
#include "CallOnScopeExit.h"
#include "ConfigurationManager.h"
#include "InferredSpans.h"
#include "InstrumentedFunctionHooksStorage.h"
#include "InternalFunctionInstrumentation.h"
#include "Logger.h"
#include "ModuleInfo.h"
#include "ModuleFunctions.h"
#include "PeriodicTaskExecutor.h"
#include "PhpBridge.h"
#include "PhpBridgeInterface.h"
#include "RequestScope.h"
#include "SharedMemoryState.h"
ZEND_DECLARE_MODULE_GLOBALS( elastic_otel )
elasticapm::php::ConfigurationManager configManager([](std::string_view iniName) -> std::optional<std::string> {
zend_bool exists = false;
auto value = zend_ini_string_ex(iniName.data(), iniName.length(), 0, &exists);
if (!value) {
return std::nullopt;
}
return std::string(value);
});
#ifndef ZEND_PARSE_PARAMETERS_NONE
# define ZEND_PARSE_PARAMETERS_NONE() \
ZEND_PARSE_PARAMETERS_START(0, 0) \
ZEND_PARSE_PARAMETERS_END()
#endif
PHP_RINIT_FUNCTION(elastic_otel) {
ELASTICAPM_G(globals)->requestScope_->onRequestInit();
return SUCCESS;
}
PHP_RSHUTDOWN_FUNCTION(elastic_otel) {
ELASTICAPM_G(globals)->requestScope_->onRequestShutdown();
return SUCCESS;
}
ZEND_RESULT_CODE elasticApmRequestPostDeactivate(void) {
ELASTICAPM_G(globals)->requestScope_->onRequestPostDeactivate();
return ZEND_RESULT_CODE::SUCCESS;
}
PHP_MINFO_FUNCTION(elastic_otel) {
printPhpInfo(zend_module);
}
static PHP_GINIT_FUNCTION(elastic_otel) {
//TODO for ZTS logger must be initialized in MINIT! (share fd between threads) - different lifecycle
//TODO store in globals and allow watch for config change (change of level)
auto logSinkStdErr = std::make_shared<elasticapm::php::LoggerSinkStdErr>();
auto logSinkSysLog = std::make_shared<elasticapm::php::LoggerSinkSysLog>();
auto logSinkFile = std::make_shared<elasticapm::php::LoggerSinkFile>();
auto logger = std::make_shared<elasticapm::php::Logger>(std::vector<std::shared_ptr<elasticapm::php::LoggerSinkInterface>>{logSinkStdErr, logSinkSysLog, logSinkFile});
configManager.attachLogger(logger);
ELOGF_DEBUG(logger, MODULE, "%s: GINIT called; parent PID: %d", __FUNCTION__, static_cast<int>(elasticapm::osutils::getParentProcessId()));
elastic_otel_globals->globals = nullptr;
auto phpBridge = std::make_shared<elasticapm::php::PhpBridge>(logger);
auto hooksStorage = std::make_shared<elasticapm::php::InstrumentedFunctionHooksStorage_t>();
auto inferredSpans = std::make_shared<elasticapm::php::InferredSpans>([interruptFlag = reinterpret_cast<void *>(&EG(vm_interrupt))]() {
#if PHP_VERSION_ID >= 80200
zend_atomic_bool_store_ex(reinterpret_cast<zend_atomic_bool *>(interruptFlag), true);
#else
*static_cast<zend_bool *>(interruptFlag) = 1;
#endif
}, [phpBridge](elasticapm::php::InferredSpans::time_point_t requestTime, elasticapm::php::InferredSpans::time_point_t now) {
phpBridge->callInferredSpans(now - requestTime);
});
try {
elastic_otel_globals->globals = new elasticapm::php::AgentGlobals(logger, std::move(logSinkStdErr), std::move(logSinkSysLog), std::move(logSinkFile), std::move(phpBridge), std::move(hooksStorage), std::move(inferredSpans), [](elasticapm::php::ConfigurationSnapshot &cfg) { return configManager.updateIfChanged(cfg); });
} catch (std::exception const &e) {
ELOGF_CRITICAL(logger, MODULE, "Unable to allocate AgentGlobals. '%s'", e.what());
}
// ZVAL_UNDEF(&elastic_otel_globals->lastException);
// new (&elastic_otel_globals->lastErrorData) std::unique_ptr<elasticapm::php::PhpErrorData>;
elastic_otel_globals->captureErrors = false;
}
PHP_GSHUTDOWN_FUNCTION(elastic_otel) {
if (elastic_otel_globals->globals) {
ELOGF_DEBUG(elastic_otel_globals->globals->logger_, MODULE, "%s: GSHUTDOWN called; parent PID: %d", __FUNCTION__, static_cast<int>(elasticapm::osutils::getParentProcessId()));
delete elastic_otel_globals->globals;
}
// if (elastic_otel_globals->lastErrorData) {
// // 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();
// }
}
zend_module_entry elastic_otel_fake = {STANDARD_MODULE_HEADER,
"opentelemetry", /* Extension name */
nullptr, /* zend_function_entry */
nullptr, /* PHP_MINIT - Module initialization */
nullptr, /* PHP_MSHUTDOWN - Module shutdown */
nullptr, /* PHP_RINIT - Request initialization */
nullptr, /* PHP_RSHUTDOWN - Request shutdown */
nullptr, /* PHP_MINFO - Module info */
"2.0", /* Version */
0, /* globals size */
nullptr, /* PHP_MODULE_GLOBALS */
nullptr, /* PHP_GINIT */
nullptr, /* PHP_GSHUTDOWN */
nullptr, /* post deactivate */
STANDARD_MODULE_PROPERTIES_EX};
PHP_MINIT_FUNCTION(elastic_otel) {
REGISTER_LONG_CONSTANT("ELASTIC_OTEL_LOG_LEVEL_OFF", logLevel_off, CONST_CS | CONST_PERSISTENT);
REGISTER_LONG_CONSTANT("ELASTIC_OTEL_LOG_LEVEL_CRITICAL", logLevel_critical, CONST_CS | CONST_PERSISTENT);
REGISTER_LONG_CONSTANT("ELASTIC_OTEL_LOG_LEVEL_ERROR", logLevel_error, CONST_CS | CONST_PERSISTENT);
REGISTER_LONG_CONSTANT("ELASTIC_OTEL_LOG_LEVEL_WARNING", logLevel_warning, CONST_CS | CONST_PERSISTENT);
REGISTER_LONG_CONSTANT("ELASTIC_OTEL_LOG_LEVEL_INFO", logLevel_info, CONST_CS | CONST_PERSISTENT);
REGISTER_LONG_CONSTANT("ELASTIC_OTEL_LOG_LEVEL_DEBUG", logLevel_debug, CONST_CS | CONST_PERSISTENT);
REGISTER_LONG_CONSTANT("ELASTIC_OTEL_LOG_LEVEL_TRACE", logLevel_trace, CONST_CS | CONST_PERSISTENT);
elasticApmModuleInit(type, module_number);
if (!zend_register_internal_module(&elastic_otel_fake)) {
ELOGF_WARNING(ELASTICAPM_G(globals)->logger_, MODULE, "Unable to create artificial opentelemetry extension. There might be stability issues.");
}
return SUCCESS;
}
PHP_MSHUTDOWN_FUNCTION(elastic_otel) {
elasticApmModuleShutdown(type, module_number);
return SUCCESS;
}
zend_module_entry elastic_otel_module_entry = {
STANDARD_MODULE_HEADER,
"elastic_otel", /* Extension name */
elastic_otel_functions, /* zend_function_entry */
PHP_MINIT(elastic_otel), /* PHP_MINIT - Module initialization */
PHP_MSHUTDOWN(elastic_otel), /* PHP_MSHUTDOWN - Module shutdown */
PHP_RINIT(elastic_otel), /* PHP_RINIT - Request initialization */
PHP_RSHUTDOWN(elastic_otel), /* PHP_RSHUTDOWN - Request shutdown */
PHP_MINFO(elastic_otel), /* PHP_MINFO - Module info */
ELASTIC_OTEL_VERSION, /* Version */
PHP_MODULE_GLOBALS(elastic_otel), /* PHP_MODULE_GLOBALS */
PHP_GINIT(elastic_otel), /* PHP_GINIT */
PHP_GSHUTDOWN(elastic_otel), /* PHP_GSHUTDOWN */
elasticApmRequestPostDeactivate, /* post deactivate */
STANDARD_MODULE_PROPERTIES_EX
};
# ifdef ZTS
ZEND_TSRMLS_CACHE_DEFINE()
# endif
extern "C" ZEND_GET_MODULE(elastic_otel)