Skip to content
Draft
27 changes: 26 additions & 1 deletion agent/native/ext/ConfigManager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -365,7 +365,11 @@ static void parsedOptionalBoolValueToZval( const OptionMetadata* optMeta, Parsed
ELASTIC_APM_ASSERT_EQ_UINT64( parsedValue.type, optMeta->defaultValue.type );
ELASTIC_APM_ASSERT_VALID_PTR( return_value );

RETURN_STRING( optionalBoolToString( parsedValue.u.optionalBoolValue ) );
if (parsedValue.u.optionalBoolValue.isSet) {
RETURN_BOOL(parsedValue.u.optionalBoolValue.value);
} else {
RETURN_NULL();
}
}

static ResultCode parseDurationValue( const OptionMetadata* optMeta, String rawValue, /* out */ ParsedOptionValue* parsedValue )
Expand Down Expand Up @@ -801,8 +805,11 @@ ELASTIC_APM_DEFINE_FIELD_ACCESS_FUNCS( optionalBoolValue, asyncBackendComm )
ELASTIC_APM_DEFINE_FIELD_ACCESS_FUNCS( stringValue, bootstrapPhpPartFile )
ELASTIC_APM_DEFINE_FIELD_ACCESS_FUNCS( boolValue, breakdownMetrics )
ELASTIC_APM_DEFINE_FIELD_ACCESS_FUNCS( boolValue, captureErrors )
ELASTIC_APM_DEFINE_FIELD_ACCESS_FUNCS( boolValue, captureErrorsWithPhpPart )
ELASTIC_APM_DEFINE_FIELD_ACCESS_FUNCS( optionalBoolValue, captureExceptions )
ELASTIC_APM_DEFINE_FIELD_ACCESS_FUNCS( stringValue, devInternal )
ELASTIC_APM_DEFINE_FIELD_ACCESS_FUNCS( boolValue, devInternalBackendCommLogVerbose )
ELASTIC_APM_DEFINE_FIELD_ACCESS_FUNCS( boolValue, devInternalCaptureErrorsOnlyToLog )
ELASTIC_APM_DEFINE_FIELD_ACCESS_FUNCS( stringValue, disableInstrumentations )
ELASTIC_APM_DEFINE_FIELD_ACCESS_FUNCS( boolValue, disableSend )
ELASTIC_APM_DEFINE_FIELD_ACCESS_FUNCS( boolValue, enabled )
Expand Down Expand Up @@ -1008,6 +1015,18 @@ static void initOptionsMetadata( OptionMetadata* optsMeta )
ELASTIC_APM_CFG_OPT_NAME_CAPTURE_ERRORS,
/* defaultValue: */ true );

ELASTIC_APM_INIT_METADATA(
buildBoolOptionMetadata,
captureErrorsWithPhpPart,
ELASTIC_APM_CFG_OPT_NAME_CAPTURE_ERRORS_WITH_PHP_PART,
/* defaultValue: */ false );

ELASTIC_APM_INIT_METADATA(
buildOptionalBoolOptionMetadata,
captureExceptions,
ELASTIC_APM_CFG_OPT_NAME_CAPTURE_EXCEPTIONS,
/* defaultValue: */ makeNotSetOptionalBool() );

ELASTIC_APM_INIT_METADATA(
buildStringOptionMetadata,
devInternal,
Expand All @@ -1020,6 +1039,12 @@ static void initOptionsMetadata( OptionMetadata* optsMeta )
ELASTIC_APM_CFG_OPT_NAME_DEV_INTERNAL_BACKEND_COMM_LOG_VERBOSE,
/* defaultValue: */ false );

ELASTIC_APM_INIT_METADATA(
buildBoolOptionMetadata,
devInternalCaptureErrorsOnlyToLog,
ELASTIC_APM_CFG_OPT_NAME_DEV_INTERNAL_CAPTURE_ERRORS_ONLY_TO_LOG,
/* defaultValue: */ false );

ELASTIC_APM_INIT_METADATA(
buildStringOptionMetadata,
disableInstrumentations,
Expand Down
9 changes: 6 additions & 3 deletions agent/native/ext/ConfigManager.h
Original file line number Diff line number Diff line change
Expand Up @@ -74,8 +74,11 @@ enum OptionId
optionId_bootstrapPhpPartFile,
optionId_breakdownMetrics,
optionId_captureErrors,
optionId_captureErrorsWithPhpPart,
optionId_captureExceptions,
optionId_devInternal,
optionId_devInternalBackendCommLogVerbose,
optionId_devInternalCaptureErrorsOnlyToLog,
optionId_disableInstrumentations,
optionId_disableSend,
optionId_enabled,
Expand Down Expand Up @@ -257,16 +260,16 @@ const ConfigSnapshot* getGlobalCurrentConfigSnapshot();
#define ELASTIC_APM_CFG_OPT_NAME_BOOTSTRAP_PHP_PART_FILE "bootstrap_php_part_file"
#define ELASTIC_APM_CFG_OPT_NAME_BREAKDOWN_METRICS "breakdown_metrics"

/**
* Internal configuration option (not included in public documentation)
*/
#define ELASTIC_APM_CFG_OPT_NAME_CAPTURE_ERRORS "capture_errors"
#define ELASTIC_APM_CFG_OPT_NAME_CAPTURE_ERRORS_WITH_PHP_PART "capture_errors_with_php_part"
#define ELASTIC_APM_CFG_OPT_NAME_CAPTURE_EXCEPTIONS "capture_exceptions"

/**
* Internal configuration option (not included in public documentation)
*/
#define ELASTIC_APM_CFG_OPT_NAME_DEV_INTERNAL "dev_internal"
#define ELASTIC_APM_CFG_OPT_NAME_DEV_INTERNAL_BACKEND_COMM_LOG_VERBOSE "dev_internal_backend_comm_log_verbose"
#define ELASTIC_APM_CFG_OPT_NAME_DEV_INTERNAL_CAPTURE_ERRORS_ONLY_TO_LOG "dev_internal_capture_errors_only_to_log"

#define ELASTIC_APM_CFG_OPT_NAME_DISABLE_INSTRUMENTATIONS "disable_instrumentations"
#define ELASTIC_APM_CFG_OPT_NAME_DISABLE_SEND "disable_send"
Expand Down
7 changes: 5 additions & 2 deletions agent/native/ext/ConfigSnapshot.h
Original file line number Diff line number Diff line change
Expand Up @@ -41,12 +41,16 @@ struct ConfigSnapshot
bool astProcessDebugDumpConvertedBackToSource = false;
String astProcessDebugDumpForPathPrefix = nullptr;
String astProcessDebugDumpOutDir = nullptr;
OptionalBool asyncBackendComm = {false, false};
OptionalBool asyncBackendComm = ELASTIC_APM_MAKE_NOT_SET_OPTIONAL_BOOL();
String bootstrapPhpPartFile = nullptr;
bool breakdownMetrics = false;
bool captureErrors = false;
bool captureErrorsWithPhpPart = false;
OptionalBool captureExceptions = ELASTIC_APM_MAKE_NOT_SET_OPTIONAL_BOOL();
String debugDiagnosticsFile = nullptr;
String devInternal = nullptr;
bool devInternalBackendCommLogVerbose = false;
bool devInternalCaptureErrorsOnlyToLog = false;
String disableInstrumentations = nullptr;
bool disableSend = false;
bool enabled = false;
Expand Down Expand Up @@ -88,5 +92,4 @@ struct ConfigSnapshot
String transactionSampleRate = nullptr;
String urlGroups = nullptr;
bool verifyServerCert = false;
String debugDiagnosticsFile = nullptr;
};
43 changes: 28 additions & 15 deletions agent/native/ext/Hooking.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -26,25 +26,35 @@ void elastic_apm_error_cb(int type, zend_string *error_filename, const uint32_t
#endif
using namespace std::string_view_literals;

if (ELASTICAPM_G(captureErrors)) {
if (ELASTICAPM_G(captureErrorsUsingNative)) {
ELASTICAPM_G(lastErrorData) = nullptr;
std::unique_ptr<elasticapm::php::PhpErrorData> errorData;
#if PHP_VERSION_ID < 80000
char * message = nullptr;
va_list messageArgsCopy;
char * message = nullptr;
va_list messageArgsCopy;
va_copy(messageArgsCopy, args);
vspprintf(/* out */ &message, 0, format, messageArgsCopy); // vspprintf allocates memory for the resulted string buffer and it needs to be freed with efree()
va_end(messageArgsCopy);
vspprintf(/* out */ &message, 0, format, messageArgsCopy); // vspprintf allocates memory for the resulted string buffer and it needs to be freed with efree()
va_end(messageArgsCopy);

ELASTICAPM_G(lastErrorData) = std::make_unique<elasticapm::php::PhpErrorData>(type, error_filename ? error_filename : ""sv, error_lineno, message ? message : ""sv);
errorData = std::make_unique<elasticapm::php::PhpErrorData>(type, error_filename ? error_filename : ""sv, error_lineno, message ? message : ""sv);

if (message) {
efree(message);
}
if (message) {
efree(message);
}
#elif PHP_VERSION_ID < 80100
ELASTICAPM_G(lastErrorData) = std::make_unique<elasticapm::php::PhpErrorData>(type, error_filename ? error_filename : ""sv, error_lineno, message ? std::string_view{ZSTR_VAL(message), ZSTR_LEN(message)} : ""sv);
errorData = std::make_unique<elasticapm::php::PhpErrorData>(type, error_filename ? error_filename : ""sv, error_lineno, message ? std::string_view{ZSTR_VAL(message), ZSTR_LEN(message)} : ""sv);
#else
ELASTICAPM_G(lastErrorData) = nullptr;
ELASTICAPM_G(lastErrorData) = std::make_unique<elasticapm::php::PhpErrorData>(type, error_filename ? std::string_view{ZSTR_VAL(error_filename), ZSTR_LEN(error_filename)} : ""sv, error_lineno, message ? std::string_view{ZSTR_VAL(message), ZSTR_LEN(message)} : ""sv);
errorData = std::make_unique<elasticapm::php::PhpErrorData>(type, error_filename ? std::string_view{ZSTR_VAL(error_filename), ZSTR_LEN(error_filename)} : ""sv, error_lineno, message ? std::string_view{ZSTR_VAL(message), ZSTR_LEN(message)} : ""sv);
#endif

if (ELASTICAPM_G(captureErrorsToLogOnly)) {
ELASTIC_APM_LOG_DEBUG(
"Captured error but only to log it; error_filename: " ELASTIC_APM_PRINTF_STRING_VIEW_FMT_SPEC() "; error_lineno: %d; message: " ELASTIC_APM_PRINTF_STRING_VIEW_FMT_SPEC(),
ELASTIC_APM_PRINTF_STD_STRING_VIEW_ARG(errorData->getFileName()), errorData->getLineNumber(), ELASTIC_APM_PRINTF_STD_STRING_VIEW_ARG(errorData->getMessage())
);
} else {
ELASTICAPM_G(lastErrorData) = std::move(errorData);
}
}

auto original = Hooking::getInstance().getOriginalZendErrorCb();
Expand Down Expand Up @@ -92,7 +102,7 @@ static void elastic_interrupt_function(zend_execute_data *execute_data) {
} zend_end_try();
}

void Hooking::replaceHooks(bool cfgCaptureErrors, bool cfgInferredSpansEnabled) {
void Hooking::replaceHooks(bool cfgCaptureErrors, bool cfgCaptureErrorsWithPhpPart, bool cfgInferredSpansEnabled) {
if (cfgInferredSpansEnabled) {
zend_execute_internal = elastic_execute_internal;
zend_interrupt_function = elastic_interrupt_function;
Expand All @@ -101,11 +111,14 @@ void Hooking::replaceHooks(bool cfgCaptureErrors, bool cfgInferredSpansEnabled)
ELASTIC_APM_LOG_DEBUG( "NOT replacing zend_execute_internal and zend_interrupt_function hooks because profiling_inferred_spans_enabled configuration option is set to false" );
}

if (cfgCaptureErrors) {
if (cfgCaptureErrors && (!cfgCaptureErrorsWithPhpPart)) {
zend_error_cb = elastic_apm_error_cb;
ELASTIC_APM_LOG_DEBUG( "Replaced zend_error_cb hook" );
} else {
ELASTIC_APM_LOG_DEBUG( "NOT replacing zend_error_cb hook because capture_errors configuration option is set to false" );
ELASTIC_APM_LOG_DEBUG(
"NOT replacing zend_error_cb hook because configuration options capture_errors is %s and capture_errors_with_php_part is %s",
boolToString(cfgCaptureErrors), boolToString(cfgCaptureErrorsWithPhpPart)
);
}
}

Expand Down
2 changes: 1 addition & 1 deletion agent/native/ext/Hooking.h
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ class Hooking {
zend_error_cb = original_zend_error_cb_;
}

void replaceHooks(bool cfgCaptureErrors, bool cfgInferredSpansEnabled);
void replaceHooks(bool cfgCaptureErrors, bool cfgCaptureErrorsWithPhpPart, bool cfgInferredSpansEnabled);

zend_execute_internal_t getOriginalExecuteInternal() {
return original_execute_internal_;
Expand Down
4 changes: 3 additions & 1 deletion agent/native/ext/OptionalBool.h
Original file line number Diff line number Diff line change
Expand Up @@ -35,9 +35,11 @@ static inline String optionalBoolToString( OptionalBool optionalBoolValue )
return optionalBoolValue.isSet ? "not set" : boolToString( optionalBoolValue.value );
}

#define ELASTIC_APM_MAKE_NOT_SET_OPTIONAL_BOOL() ((OptionalBool){ .isSet = false, .value = false })

static inline OptionalBool makeNotSetOptionalBool()
{
return (OptionalBool){ .isSet = false, .value = false };
return ELASTIC_APM_MAKE_NOT_SET_OPTIONAL_BOOL();
}

static inline OptionalBool makeSetOptionalBool( bool value )
Expand Down
6 changes: 5 additions & 1 deletion agent/native/ext/elastic_apm.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -151,8 +151,11 @@ PHP_INI_BEGIN()
ELASTIC_APM_INI_ENTRY( ELASTIC_APM_CFG_OPT_NAME_BOOTSTRAP_PHP_PART_FILE )
ELASTIC_APM_INI_ENTRY( ELASTIC_APM_CFG_OPT_NAME_BREAKDOWN_METRICS )
ELASTIC_APM_INI_ENTRY( ELASTIC_APM_CFG_OPT_NAME_CAPTURE_ERRORS )
ELASTIC_APM_INI_ENTRY( ELASTIC_APM_CFG_OPT_NAME_CAPTURE_ERRORS_WITH_PHP_PART )
ELASTIC_APM_INI_ENTRY( ELASTIC_APM_CFG_OPT_NAME_CAPTURE_EXCEPTIONS )
ELASTIC_APM_INI_ENTRY( ELASTIC_APM_CFG_OPT_NAME_DEV_INTERNAL )
ELASTIC_APM_INI_ENTRY( ELASTIC_APM_CFG_OPT_NAME_DEV_INTERNAL_BACKEND_COMM_LOG_VERBOSE )
ELASTIC_APM_INI_ENTRY( ELASTIC_APM_CFG_OPT_NAME_DEV_INTERNAL_CAPTURE_ERRORS_ONLY_TO_LOG )
ELASTIC_APM_INI_ENTRY( ELASTIC_APM_CFG_OPT_NAME_DISABLE_INSTRUMENTATIONS )
ELASTIC_APM_INI_ENTRY( ELASTIC_APM_CFG_OPT_NAME_DISABLE_SEND )
ELASTIC_APM_NOT_RELOADABLE_INI_ENTRY( ELASTIC_APM_CFG_OPT_NAME_ENABLED )
Expand Down Expand Up @@ -290,7 +293,8 @@ static PHP_GINIT_FUNCTION(elastic_apm)

ZVAL_UNDEF(&elastic_apm_globals->lastException);
new (&elastic_apm_globals->lastErrorData) std::unique_ptr<elasticapm::php::PhpErrorData>;
elastic_apm_globals->captureErrors = false;
elastic_apm_globals->captureErrorsUsingNative = false;
elastic_apm_globals->captureErrorsToLogOnly = false;
}

static PHP_GSHUTDOWN_FUNCTION(elastic_apm) {
Expand Down
44 changes: 30 additions & 14 deletions agent/native/ext/lifecycle.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -156,18 +156,20 @@ void elasticApmZendThrowExceptionHookImpl(
#endif
)
{
ELASTIC_APM_LOG_DEBUG_FUNCTION_ENTRY_MSG("lastException set: %s", boolToString(Z_TYPE(ELASTICAPM_G(lastException)) != IS_UNDEF));

ELASTIC_APM_LOG_DEBUG_FUNCTION_ENTRY_MSG( "lastException set: %s", boolToString( Z_TYPE(ELASTICAPM_G(lastException)) != IS_UNDEF ) );

resetLastThrown();

if (ELASTICAPM_G(captureErrorsToLogOnly)) {
ELASTIC_APM_LOG_DEBUG("Captured exception but only to log it");
} else {
resetLastThrown();
#if PHP_MAJOR_VERSION >= 8 /* if PHP version is 8.* and later */
ZVAL_OBJ_COPY(&ELASTICAPM_G( lastException ), thrownAsPzobj );
ZVAL_OBJ_COPY(&ELASTICAPM_G( lastException ), thrownAsPzobj );
#else
ZVAL_COPY(&ELASTICAPM_G(lastException), thrownAsPzval );
ZVAL_COPY(&ELASTICAPM_G(lastException), thrownAsPzval );
#endif
}

ELASTIC_APM_LOG_DEBUG_FUNCTION_EXIT();
ELASTIC_APM_LOG_DEBUG_FUNCTION_EXIT_MSG("lastException set: %s", boolToString(Z_TYPE(ELASTICAPM_G(lastException)) != IS_UNDEF));
}

void elasticApmGetLastThrown(zval *return_value) {
Expand Down Expand Up @@ -201,8 +203,12 @@ void elasticApmZendThrowExceptionHook(


static void registerExceptionHooks(const ConfigSnapshot& config) {
if (!config.captureErrors) {
ELASTIC_APM_LOG_DEBUG( "NOT replacing zend_throw_exception_hook hook because capture_errors configuration option is set to false" );
bool shouldCaptureExceptions = config.captureExceptions.isSet ? config.captureExceptions.value : config.captureErrors;
if ((!shouldCaptureExceptions) || config.captureErrorsWithPhpPart) {
ELASTIC_APM_LOG_DEBUG(
"NOT replacing zend_throw_exception_hook hook because configuration options capture_exceptions is %s, capture_errors is %s and capture_errors_with_php_part is %s",
optionalBoolToString(config.captureExceptions), boolToString(config.captureErrors), boolToString(config.captureErrorsWithPhpPart)
);
return;
}

Expand Down Expand Up @@ -291,7 +297,7 @@ void elasticApmModuleInit( int moduleType, int moduleNumber )

astInstrumentationOnModuleInit( config );

elasticapm::php::Hooking::getInstance().replaceHooks(config->captureErrors, config->profilingInferredSpansEnabled);
elasticapm::php::Hooking::getInstance().replaceHooks(config->captureErrors, config->captureErrorsWithPhpPart, config->profilingInferredSpansEnabled);

if (php_check_open_basedir_ex(config->bootstrapPhpPartFile, false) != 0) {
ELASTIC_APM_LOG_WARNING(
Expand Down Expand Up @@ -484,10 +490,20 @@ void elasticApmRequestInit()
goto finally;
}

if (!config->captureErrors) {
ELASTICAPM_G(captureErrorsUsingNative) = false;
if (config->captureErrors) {
if (config->captureErrorsWithPhpPart) {
ELASTIC_APM_LOG_DEBUG( "capture_errors_with_php_part (captureErrorsWithPhpPart) configuration option is set to true which means errors will be captured by PHP part of the agent" );
} else {
ELASTICAPM_G(captureErrorsUsingNative) = true;
}
if (config->devInternalCaptureErrorsOnlyToLog) {
ELASTIC_APM_LOG_DEBUG( "dev_internal_capture_errors_only_to_log (devInternalCaptureErrorsOnlyToLog) configuration option is set to true which means errors will be logged only" );
}
} else {
ELASTIC_APM_LOG_DEBUG( "capture_errors (captureErrors) configuration option is set to false which means errors will NOT be captured" );
}
ELASTICAPM_G(captureErrors) = config->captureErrors;
}
ELASTICAPM_G(captureErrorsToLogOnly) = config->devInternalCaptureErrorsOnlyToLog;

if ( config->astProcessEnabled )
{
Expand Down Expand Up @@ -590,7 +606,7 @@ void elasticApmRequestShutdown()
ELASTICAPM_G(globals)->periodicTaskExecutor_->suspendPeriodicTasks();
}

ELASTICAPM_G(captureErrors) = false; // disabling error capturing on shutdown
ELASTICAPM_G(captureErrorsUsingNative) = false; // disabling error capturing on shutdown

tracerPhpPartOnRequestShutdown();

Expand Down
3 changes: 3 additions & 0 deletions agent/native/ext/log.h
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,9 @@
#include "TextOutputStream.h"
#include "platform.h"

#define ELASTIC_APM_PRINTF_STRING_VIEW_FMT_SPEC() "%.*s"
#define ELASTIC_APM_PRINTF_STD_STRING_VIEW_ARG(stdStrVw) (static_cast<int>((stdStrVw).length())), ((stdStrVw).data())

extern String logLevelNames[ numberOfLogLevels ];

enum LogSinkType
Expand Down
3 changes: 2 additions & 1 deletion agent/native/ext/php_elastic_apm.h
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,8 @@ ZEND_BEGIN_MODULE_GLOBALS(elastic_apm)
elasticapm::php::AgentGlobals *globals;
zval lastException;
std::unique_ptr<elasticapm::php::PhpErrorData> lastErrorData;
bool captureErrors;
bool captureErrorsUsingNative;
bool captureErrorsToLogOnly;
ZEND_END_MODULE_GLOBALS(elastic_apm)

ZEND_EXTERN_MODULE_GLOBALS(elastic_apm)
Expand Down
Loading
Loading