-
Notifications
You must be signed in to change notification settings - Fork 70
feat(agent): add additional arguments as error attributes for newrelic_notice_error #942
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
Conversation
Codecov ReportAttention: Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## dev #942 +/- ##
==========================================
- Coverage 78.56% 78.17% -0.40%
==========================================
Files 193 194 +1
Lines 27294 26911 -383
==========================================
- Hits 21444 21038 -406
- Misses 5850 5873 +23
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Sentry. |
agent/php_api.c
Outdated
char* buf1 = nr_strndup(errormsgstr, errormsglen); | ||
char* buf2 = nr_strndup(error_file, error_file_len); | ||
char* buf3 = nr_strndup(error_context, error_context_len); | ||
char* stack_json = nr_php_backtrace_to_json(NULL TSRMLS_CC); | ||
|
||
nr_txn_record_error_with_additional_attributes( | ||
NRPRG(txn), priority, true, buf1, errclass, buf2, | ||
error_line, buf3, error_number, stack_json); | ||
|
||
nr_free(buf1); | ||
nr_free(buf2); | ||
nr_free(buf3); | ||
nr_free(stack_json); |
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 is the purpose of copying these strings only to immediately free them? Is there a reason why we cannot just directly pass in errormsgstr
, error_file
, and error_context
?
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, fixed: b8e6402
axiom/nr_errors.c
Outdated
nr_free(error->message); | ||
nr_free(error->klass); | ||
nr_free(error->error_file); | ||
nr_free(error->error_context); | ||
nr_free(error->span_id); | ||
nr_free(error->stacktrace_json); | ||
nr_realfree((void**)error_ptr); |
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.
Based on the name of this function, I would expect it to only destroy the additional params, and thus still require the calling of nr_error_destroy
afterward to fully free the error.
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.
To reduce code duplication, I have removed this function and added the additional logic in nr_error_destroy
inside of a if statement: f917ced
axiom/nr_errors_private.h
Outdated
char* error_file; /* Error file */ | ||
int error_line; /* Error line */ | ||
char* error_context; /* Error context */ | ||
int error_no; /* Error number */ | ||
int option; /* Error option */ |
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.
As far as I can tell, these are specifically user-provided through the API. It would be useful to specify that in the name of the variables, or at least their comment.
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, I have updated the comments: 9f3e7e6
axiom/nr_errors_private.h
Outdated
int error_line; /* Error line */ | ||
char* error_context; /* Error context */ | ||
int error_no; /* Error number */ | ||
int option; /* Error option */ |
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.
So am I correct in that the option
variable indicates whether the other user-provided variables are populated? Could we not just check, for example, error_file
for null behind an API like nr_error_has_user_attributes
rather than having this confusing variable? As far as I can tell, these attributes are either all-or-nothing.
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.
Agree, the option
variable isn't necessary and I have removed it: 9f3e7e6
@@ -1638,6 +1641,109 @@ void nr_txn_record_error(nrtxn_t* txn, | |||
nr_free(span_id); | |||
} | |||
|
|||
void nr_txn_record_error_with_additional_attributes( |
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.
There is a large amount of code duplication in this API. It would be nice to create a more unified API such that we don't have multiple copies of large functions.
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.
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.
Could you remove nr_txn_record_error_helper
and then just have nr_txn_record_error
call nr_txn_record_error_with_additional_attributes
with the unused fields filled with nothing values?
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/php_api.c
Outdated
ZEND_NUM_ARGS() TSRMLS_CC, "lsslz!", | ||
&ignore1, &errormsgstr, &errormsglen, | ||
&ignore2, &ignore3, &ignore4, &ignore5)) { | ||
ZEND_NUM_ARGS() TSRMLS_CC, "lssls!", |
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 format for the parameters changes so the last value is a 's' instead of 'z' - what is the rationale behind this change?
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 has been changed to a 's' because in the docs API, it mentions that error_context
is a string which is the 5th parameter for this function.
agent/php_api.c
Outdated
@@ -168,13 +172,23 @@ PHP_FUNCTION(newrelic_notice_error) { | |||
} | |||
} | |||
|
|||
{ | |||
char* buf = nr_strndup(errormsgstr, errormsglen); | |||
if (!determine) { |
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 code seems to be able to be reduced as there are common elements to each possible path - also use an else
instead of having two separate if
statements testing logically opposite conditions.
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.
Fixed: 66a069d
const char* stacktrace_json, | ||
const char* span_id, | ||
nrtime_t when) { | ||
nr_error_t* error; | ||
|
||
if (0 == message) { |
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.
Why were these guards removed?
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.
These were removed because these guards are being checked before this helper function is called. An example of this is in nr_error_create.
axiom/nr_errors.c
Outdated
error = (nr_error_t*)nr_zalloc(sizeof(nr_error_t)); | ||
error->priority = priority; | ||
error->when = when; | ||
error->message = nr_strdup(message); | ||
error->klass = nr_strdup(klass); | ||
error->stacktrace_json = nr_strdup(stacktrace_json); | ||
if (NULL != error_file && NULL != error_context) { |
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.
If only one of these two values is defined do we still want to create the associated field?
Is there a value for the error_line
and error_no
that indicates it is not defined that needs to be checked?
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.
After discussion, it has been decided that if one attribute is passed in as NULL
, the rest of the attributes should still show up. I will create a commit for this.
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 have added an if statement for each separate value to ensure all values that are defined will be reported: 788c5a0
axiom/nr_errors.c
Outdated
const char* stacktrace_json, | ||
const char* span_id, | ||
nrtime_t when) { | ||
if (0 == message || 0 == klass || 0 == error_file || 0 == error_context |
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.
Please change usages in this file of 0
to NULL
if used to test a pointer value. The usage of 0
is from old code and we do not use that form now.
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.
Fixed: ace780f
axiom/nr_errors.c
Outdated
@@ -94,6 +153,10 @@ void nr_error_destroy(nr_error_t** error_ptr) { | |||
nr_free(error->klass); | |||
nr_free(error->span_id); | |||
nr_free(error->stacktrace_json); | |||
if ((NULL != error->error_file) && (NULL != error->error_context)) { |
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.
nr_free
should handle this case.
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.
Fixed: c058df8
* 8. String containing stack trace in JSON format. | ||
* 9. Span ID | ||
* 10. When the error occurred. | ||
* |
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.
Would be good to explain if all are required or if there are certain combinations allowed.
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.
Added: 9fad7fa
axiom/nr_errors.h
Outdated
/* | ||
* Purpose : Get the error file of an error. | ||
* | ||
* Returns : The error file of the error or NULL on failure. |
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.
Is it a 'failure' or simply it is not defined?
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, it should instead say that it was not defined, fixed: 9fad7fa
axiom/nr_segment.c
Outdated
if (NULL == segment->error) { | ||
segment->error = nr_zalloc(sizeof(nr_segment_error_t)); | ||
} | ||
|
||
nr_free(segment->error->error_message); | ||
nr_free(segment->error->error_class); | ||
if (NULL != error_file && NULL != error_context) { |
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.
Couldn't this code be pulled into the conditional below which has the same check?
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 have removed the if statement, so that nr_free() is still called: ed191c0
axiom/nr_segment.c
Outdated
|
||
segment->error->error_message | ||
= error_message ? nr_strdup(error_message) : NULL; | ||
segment->error->error_class = error_class ? nr_strdup(error_class) : NULL; | ||
if (NULL != error_file && NULL != error_context) { |
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'm not clear on the behavior of this function if the segment had all fields defined previously and then this function is called and error_file
and error_context
are NULL
. It seems the previous values would be retained. That could be confusing as it seems there is no way to clear those fields once set.
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.
After discussion, it has been decided that if NULL
is passed in, the attribute should not show up. I will modify the code and create a commit for this.
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.
axiom/nr_segment.c
Outdated
return; | ||
} | ||
|
||
static void nr_segment_set_error_helper(nr_segment_t* segment, |
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 would recommend putting a comment on each of these static helper functions throughout this file to make it clear no NULL checking is done. It is unlikely any new code would call these directly but I think it is a good safety guard to make sure a coder is aware of this.
Do you think we need unit tests for these helper functions throughout this file?
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.
axiom/nr_segment.h
Outdated
@@ -117,6 +117,10 @@ typedef struct _nr_segment_metric_t { | |||
typedef struct _nr_segment_error_t { | |||
char* error_message; /* The error message that will appear on a span event. */ | |||
char* error_class; /* The error class that will appear on a span event. */ | |||
char* error_file; |
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.
Can you add a comment for each of these fields like the existing ones have?
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.
Added: eb64129
axiom/nr_txn.c
Outdated
@@ -2479,6 +2536,13 @@ nr_analytics_event_t* nr_error_to_event(const nrtxn_t* txn) { | |||
nro_set_hash_string(params, "error.class", nr_error_get_klass(txn->error)); | |||
nro_set_hash_string(params, "error.message", | |||
nr_error_get_message(txn->error)); | |||
if (nr_error_get_file(txn->error) && nr_error_get_context(txn->error)) { |
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.
Is it ever possible we'd only one to write a subset of these 4 or do they either all exist or not exist?
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.
After discussion, it has been decided that the values that are defined should still be reported. I have modified the code to check each value individually in separate if statements: e177f40
@@ -471,6 +471,17 @@ extern nr_status_t nr_txn_record_error_worthy(const nrtxn_t* txn, int priority); | |||
#define NR_TXN_ALLOW_RAW_EXCEPTION_MESSAGE \ | |||
"Message removed by New Relic security settings" | |||
|
|||
extern void nr_txn_record_error_with_additional_attributes(nrtxn_t* txn, |
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.
Could you document the record error functions (both forms)?
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.
Added: c5a40cc
axiom/tests/test_segment.c
Outdated
@@ -2868,6 +2874,12 @@ static void test_segment_set_error_attributes(void) { | |||
tlib_pass_if_str_equal("error.class", "error.class 1", | |||
segment.error->error_class); | |||
|
|||
nr_segment_set_error_with_additional_params(&segment, "error.message", "error.class", "error.file", 125, "rand3", 100); |
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.
Some tests exercising the additional new params which are pointers with NULL
values would be good. Because there are short circuiting conditions when checking for NULL
I would recommend exercising each param individually with a NULL
instead of a single test setting them all to NULL
.
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, added: 06430a2
axiom/tests/test_segment.c
Outdated
@@ -2868,6 +2874,12 @@ static void test_segment_set_error_attributes(void) { | |||
tlib_pass_if_str_equal("error.class", "error.class 1", | |||
segment.error->error_class); | |||
|
|||
nr_segment_set_error_with_additional_params(&segment, "error.message", "error.class", "error.file", 125, "rand3", 100); |
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 is the expected behavior if any of the strings are ""
?
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.
If the user passes in an empty string, then the attribute will still show up. The attribute will not show up if NULL
is passed in.
Co-authored-by: Michael Fulbright <[email protected]>
a2ad43b
to
8b6a0e3
Compare
PR #960 supersedes the work in this PR. The conclusion was that the agent spec does not support these current parameters currently as attributes, and much of the information is available from the stack trace or by adding the desired information to the error message passed to the API. |
This PR does the following:
nr_txn_record_error_with_additional_attributes
, which takes in the additional parameters that are passed innewrelic_notice_error
and adds them as error attributes.