-
Notifications
You must be signed in to change notification settings - Fork 66
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat(agent): Add RabbitMQ instrumentation using the php-amqplib library #1009
base: dev
Are you sure you want to change the base?
Conversation
Initial commit does the following: * Detect library via magic file * Detect package and version information. * Basic unit tests
|
Initial commit does the following: * Detect library via magic file * Detect package and version information. * Basic unit tests
2df782f
to
0b184ca
Compare
…ewrelic-php-agent into feat/rabbitmq_instrumentation
Test version detection when class exists but version const doesn't. Fixed typos.
Codecov ReportAttention: Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## dev #1009 +/- ##
==========================================
- Coverage 78.02% 77.76% -0.26%
==========================================
Files 197 198 +1
Lines 27418 27586 +168
==========================================
+ Hits 21392 21453 +61
- Misses 6026 6133 +107
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Sentry. |
* Creates message segment on basic_publish call.
* While the RabbitMQ tutorial for using with the dockerized RabbitMQ setup * correctly and loads the PhpAmqpLib\\Channel\\AMQPChannel class in time for * the agent to wrap the instrumented functions, there are AWS MQ_BROKER * specific but valid scenarios where the PhpAmqpLib\\Channel\\AMQPChannel class * file does not explicitly load or does not load in time, and the instrumented * functions are NEVER wrapped regardless of how many times they are called in * one txn. Specifically, this centered around the very slight but impactful * differences when using the PhpAmqpLib\Connection\AMQPStreamConnection which * causes an explicit load of the AMQPChannel class/file and * PhpAmqpLib\Connection\AMQPSSLConnection which does NOT cause an explicit load * of the AMQPChannelclass/file. The following method is thus the only way to * ensure the class is loaded in time for the functions to be wrapped.
result = zend_eval_string( | ||
"(function() {" | ||
" $nr_php_amqplib_version = '';" | ||
" try {" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I believe PHP provides tools to make sure this constant exists so it would avoid triggering an exception in the first place.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That would be doing an PHP extra call when this call already verifies and retrieves the value and is future proofed against any future exceptions. Additionally, it will automatically load the class if it's not already loaded.
agent/lib_php_amqplib.c
Outdated
amqp_port | ||
= nr_php_zend_hash_index_find(Z_ARRVAL_P(connect_constructor_params), | ||
AMQP_CONSTRUCT_PARAMS_PORT_INDEX); | ||
if (nr_php_is_zval_valid_scalar(amqp_port)) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This checks if it is a bool, a valid string, long or double. Are these all valid values for the port?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good point, only allow long:
095f82d
= nr_php_zend_hash_index_find(Z_ARRVAL_P(connect_constructor_params), | ||
AMQP_CONSTRUCT_PARAMS_PORT_INDEX); | ||
if (nr_php_is_zval_valid_scalar(amqp_port)) { | ||
message_params->server_port = Z_LVAL_P(amqp_port); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The check above does not guarantee the value is a long.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
verify long: 095f82d
*/ | ||
message_params.destination_name | ||
= ENSURE_PERSISTENCE(Z_STRVAL_P(amqp_exchange)); | ||
} else { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What if ampq_exchange
is not a string at all?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
agent/lib_php_amqplib.c
Outdated
* strdup server_address, destination_name, and | ||
* messaging_destination_routing_key. | ||
*/ | ||
nr_free(message_params.server_address); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it could be cleaner to have a matching macro to ENSURE_PERSISTENCE
that could be used to un-persist these values in the appropriate way for the PHP is use.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Great idea!
71e5e02
* Default in case of empty string. */ | ||
message_params.messaging_destination_publish_name | ||
= Z_STRVAL_P(amqp_exchange); | ||
} else { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What if ampq_exchange
is not a string? (This applies to this pattern in any wrappers in this PR).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The function param list and/or class definition dictates strings or not.
nr_php_is_zval_non_empty_string verifies that the value is not null, is not empty, and is a zval string. Only after that do we do anything with it.
|
||
/* Extract the version for aws-sdk 3+ */ | ||
nr_php_amqplib_handle_version(); | ||
nr_php_amqplib_ensure_class(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I am concerned on how this all works - if the file used to trigger this enable function is, for example, in a sequence of PHP calls that was in response to autoloading the class we need, and inside this sequence we start another sequence to load this class - is composer setup to handle this?
Is there not a file we can use in the situations this class is not triggered (the AWS context I guess?).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No. None of the files (autoload files or files in general that get loaded through the course of the test case) can cause this class to be loaded in order to wrap the function properly during AWS test cases. This is different behavior from the rabbitmq server used with multiverse so the difference is either due to the slight variance in making an SSL connection or due to the older rabbitmq engine that is running in AWS. None of the files that loaded when running AWS tests worked to allow our instrumentation to be wrapped (different than the non-ssl, non-aws rabbitmq server which actually loaded the file as well as the class).
All this is doing is calling class_exists
so pretty innocuous. This is a php function which other functions can call in the middle of their logic and have easily handled as well. It's analogous to function calls that make calls to other classes. If you turn the show_* on, classes get loaded inside classes all the time because unless the file is loaded previously, the class doesn't actually get loaded until it's used.
Yes composer can handle it. That change was enough to finally get the functions wrapped for AWS setups, and the segments were created and made it to the backend with appropriate attributes.
Initial commit does the following:
Subsequent commits:
Remaining: