Skip to content

Commit

Permalink
feat(axiom): Add message segment functionality (#991)
Browse files Browse the repository at this point in the history
1) Added message segment.
Agent will now handle generic, external, datastore, and message
segments.

2) Added unit tests for the changes

3) New functionality involves adding message specific attributes to
segments/spans, generating message specific metrics, and honoring the
message_tracer_parameters_enabled INI setting to disable/enable the
attributes.

4)Things to keep in mind while reviewing:
* the commits describe a lot of what is going on
* Functionality-wise, message segment is a mix of external and datastore
* nr_segment_message is the new file

---------

Co-authored-by: ZNeumann <[email protected]>
Co-authored-by: Michael Fulbright <[email protected]>
  • Loading branch information
3 people authored Jan 17, 2025
1 parent 94d1575 commit 996dcc9
Show file tree
Hide file tree
Showing 28 changed files with 2,077 additions and 48 deletions.
6 changes: 6 additions & 0 deletions agent/php_newrelic.h
Original file line number Diff line number Diff line change
Expand Up @@ -596,6 +596,12 @@ nrinibool_t
nrinibool_t
vulnerability_management_composer_api_enabled; /* newrelic.vulnerability_management.composer_api.enabled */

/*
* Configuration options for recording Messaging APIs
*/
nrinibool_t
message_tracer_segment_parameters_enabled; /* newrelic.segment_tracer.segment_parameters.enabled */

#if ZEND_MODULE_API_NO < ZEND_7_4_X_API_NO
/*
* pid and user_function_wrappers are used to store user function wrappers.
Expand Down
11 changes: 11 additions & 0 deletions agent/php_nrini.c
Original file line number Diff line number Diff line change
Expand Up @@ -3100,6 +3100,17 @@ STD_PHP_INI_ENTRY_EX("newrelic.vulnerability_management.composer_api.enabled",
newrelic_globals,
nr_enabled_disabled_dh)

/*
* Messaging API
*/
STD_PHP_INI_ENTRY_EX("newrelic.message_tracer.segment_parameters.enabled",
"1",
NR_PHP_REQUEST,
nr_boolean_mh,
message_tracer_segment_parameters_enabled,
zend_newrelic_globals,
newrelic_globals,
nr_enabled_disabled_dh)
PHP_INI_END() /* } */

void nr_php_register_ini_entries(int module_number TSRMLS_DC) {
Expand Down
4 changes: 3 additions & 1 deletion agent/php_txn.c
Original file line number Diff line number Diff line change
Expand Up @@ -854,6 +854,8 @@ nr_status_t nr_php_txn_begin(const char* appnames,
opts.log_forwarding_log_level = NRINI(log_forwarding_log_level);
opts.log_events_max_samples_stored = NRINI(log_events_max_samples_stored);
opts.log_metrics_enabled = NRINI(log_metrics_enabled);
opts.message_tracer_segment_parameters_enabled
= NRINI(message_tracer_segment_parameters_enabled);

/*
* Enable the behaviour whereby asynchronous time is discounted from the total
Expand Down Expand Up @@ -1165,7 +1167,7 @@ nr_status_t nr_php_txn_end(int ignoretxn, int in_post_deactivate TSRMLS_DC) {
#if ZEND_MODULE_API_NO >= ZEND_8_0_X_API_NO \
&& !defined OVERWRITE_ZEND_EXECUTE_DATA
nr_segment_t* segment = nr_txn_get_current_segment(NRPRG(txn), NULL);
while(NULL != segment && segment != NRTXN(segment_root)) {
while (NULL != segment && segment != NRTXN(segment_root)) {
nr_segment_end(&segment);
segment = nr_txn_get_current_segment(NRPRG(txn), NULL);
}
Expand Down
11 changes: 11 additions & 0 deletions agent/scripts/newrelic.ini.template
Original file line number Diff line number Diff line change
Expand Up @@ -1341,3 +1341,14 @@ newrelic.daemon.logfile = "/var/log/newrelic/newrelic-daemon.log"
; to gather package information for vulnerability management.
;
;newrelic.vulnerability_management.composer_api.enabled = false

; Setting: newrelic.message_tracer.segment_parameters.enabled
; Type : boolean
; Scope : per-directory
; Default: true
; Info : If this setting is true, then message parameters will be captured and
; stored on their respective segments. While enabled, specific attributes
; can be filtered by using newrelic.attributes.include/exclude and
; newrelic.span_events.attributes.include/exclude
;
;newrelic.message_tracer.segment_parameters.enabled = true
1 change: 1 addition & 0 deletions axiom/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,7 @@ OBJS := \
nr_segment_children.o \
nr_segment_datastore.o \
nr_segment_external.o \
nr_segment_message.o \
nr_segment_private.o \
nr_segment_terms.o \
nr_segment_traces.o \
Expand Down
52 changes: 50 additions & 2 deletions axiom/nr_segment.c
Original file line number Diff line number Diff line change
Expand Up @@ -313,6 +313,26 @@ static void nr_populate_http_spans(nr_span_event_t* span_event,
segment->typed_attributes->external.status);
}

static void nr_populate_message_spans(nr_span_event_t* span_event,
const nr_segment_t* segment) {
nr_span_event_set_category(span_event, NR_SPAN_MESSAGE);

if (nrunlikely(NULL == segment || NULL == segment->typed_attributes)) {
return;
}

nr_span_event_set_spankind(span_event,
segment->typed_attributes->message.message_action);
nr_span_event_set_message(
span_event, NR_SPAN_MESSAGE_DESTINATION_NAME,
segment->typed_attributes->message.destination_name);
nr_span_event_set_message(
span_event, NR_SPAN_MESSAGE_MESSAGING_SYSTEM,
segment->typed_attributes->message.messaging_system);
nr_span_event_set_message(span_event, NR_SPAN_MESSAGE_SERVER_ADDRESS,
segment->typed_attributes->message.server_address);
}

static nr_status_t add_user_attribute_to_span_event(const char* key,
const nrobj_t* val,
void* ptr) {
Expand Down Expand Up @@ -431,8 +451,8 @@ nr_span_event_t* nr_segment_to_span_event(nr_segment_t* segment) {
nr_span_event_set_trusted_parent_id(
event, nr_distributed_trace_inbound_get_trusted_parent_id(
segment->txn->distributed_trace));
nr_span_event_set_parent_id(event,
nr_distributed_trace_inbound_get_guid(segment->txn->distributed_trace));
nr_span_event_set_parent_id(event, nr_distributed_trace_inbound_get_guid(
segment->txn->distributed_trace));

nr_span_event_set_transaction_name(event, segment->txn->name);

Expand Down Expand Up @@ -482,6 +502,10 @@ nr_span_event_t* nr_segment_to_span_event(nr_segment_t* segment) {
nr_populate_http_spans(event, segment);
break;

case NR_SEGMENT_MESSAGE:
nr_populate_message_spans(event, segment);
break;

case NR_SEGMENT_CUSTOM:
nr_span_event_set_category(event, NR_SPAN_GENERIC);
break;
Expand Down Expand Up @@ -599,6 +623,30 @@ bool nr_segment_set_external(nr_segment_t* segment,
return true;
}

bool nr_segment_set_message(nr_segment_t* segment,
const nr_segment_message_t* message) {
if (nrunlikely((NULL == segment) || (NULL == message))) {
return false;
}

nr_segment_destroy_typed_attributes(segment->type,
&segment->typed_attributes);
segment->type = NR_SEGMENT_MESSAGE;
segment->typed_attributes = nr_zalloc(sizeof(nr_segment_typed_attributes_t));

// clang-format off
// Initialize the fields of the message attributes, one field per line.
segment->typed_attributes->message = (nr_segment_message_t){
.message_action = message->message_action,
.destination_name = nr_strempty(message->destination_name) ? NULL: nr_strdup(message->destination_name),
.messaging_system = nr_strempty(message->messaging_system) ? NULL: nr_strdup(message->messaging_system),
.server_address = nr_strempty(message->server_address) ? NULL: nr_strdup(message->server_address),
};
// clang-format on

return true;
}

bool nr_segment_add_child(nr_segment_t* parent, nr_segment_t* child) {
if (nrunlikely((NULL == parent) || (NULL == child))) {
return false;
Expand Down
61 changes: 58 additions & 3 deletions axiom/nr_segment.h
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,8 @@ typedef struct _nrtxn_t nrtxn_t;
typedef enum _nr_segment_type_t {
NR_SEGMENT_CUSTOM,
NR_SEGMENT_DATASTORE,
NR_SEGMENT_EXTERNAL
NR_SEGMENT_EXTERNAL,
NR_SEGMENT_MESSAGE
} nr_segment_type_t;

/*
Expand Down Expand Up @@ -109,6 +110,48 @@ typedef struct _nr_segment_external_t {
uint64_t status;
} nr_segment_external_t;

typedef struct _nr_segment_message_t {
/*
* Attributes needed for entity relationship building.
* Compare to OTEL attributes:
* https://opentelemetry.io/docs/specs/semconv/attributes-registry/cloud/
* cloud.account.id, cloud.region, messaging.system and server.address are
* used to create relationships between APM and cloud services. It may not
* make sense to add these attributes unless they are used for creating one of
* the relationships in Entity Relationships.
*/

nr_span_spankind_t
message_action; /*The action of the message, e.g.,Produce/Consume.*/
char* destination_name; /*The name of the Queue, Topic, or Exchange;
otherwise, Temp. Needed for SQS relationship.*/
char* messaging_system; /* for ex: aws_sqs. Needed for SQS relationship.*/
char* server_address; /*The server domain name or IP address. Needed for
MQBROKER relationship.*/
} nr_segment_message_t;

typedef struct _nr_segment_cloud_attrs_t {
/*
* Attributes needed for entity relationship building.
* Compare to OTEL attributes:
* https://opentelemetry.io/docs/specs/semconv/attributes-registry/cloud/
* cloud.account.id, cloud.region, messaging.system and server.address are
* used to create relationships between APM and cloud services. It may not
* make sense to add these attributes unless they are used for creating one of
* the relationships in Entity Relationships.
* These attributes aren't specific to a segment category so don't belong as
* typed attributes and can be added whenever they are available.
*/
char* cloud_region; /*Targeted region; ex:us-east-1*. Needed for SQS
relationship.*/
char* cloud_account_id; /*The cloud provider account ID. Needed for SQS
relationship.*/
char* cloud_resource_id; /*Unique cloud provider identifier. For AWS, this is
the ARN of the AWS resource being accessed.*/
char* aws_operation; /*AWS specific operation name.*/

} nr_segment_cloud_attrs_t;

typedef struct _nr_segment_metric_t {
char* name;
bool scoped;
Expand All @@ -132,6 +175,7 @@ typedef struct _nr_segment_error_t {
typedef union {
nr_segment_datastore_t datastore;
nr_segment_external_t external;
nr_segment_message_t message;
} nr_segment_typed_attributes_t;

typedef struct _nr_segment_t {
Expand Down Expand Up @@ -179,8 +223,8 @@ typedef struct _nr_segment_t {
int priority; /* Used to determine which segments are preferred for span event
creation */
nr_segment_typed_attributes_t* typed_attributes; /* Attributes specific to
external or datastore
segments. */
external, datastore,
or message segments. */
nr_segment_error_t* error; /* segment error attributes */
#if ZEND_MODULE_API_NO >= ZEND_8_0_X_API_NO \
&& !defined OVERWRITE_ZEND_EXECUTE_DATA /* PHP 8.0+ and OAPI */
Expand Down Expand Up @@ -314,6 +358,17 @@ extern bool nr_segment_set_datastore(nr_segment_t* segment,
*/
extern bool nr_segment_set_external(nr_segment_t* segment,
const nr_segment_external_t* external);

/*
* Purpose : Mark the segment as being a message segment.
*
* Params : 1. The pointer to the segment.
* 2. The message attributes, which will be copied into the segment.
*
* Returns : true if successful, false otherwise.
*/
extern bool nr_segment_set_message(nr_segment_t* segment,
const nr_segment_message_t* message);
/*
* Purpose : Add a child to a segment.
*
Expand Down
4 changes: 2 additions & 2 deletions axiom/nr_segment_external.c
Original file line number Diff line number Diff line change
Expand Up @@ -57,8 +57,8 @@ static void nr_segment_external_set_attrs(
* External/{host}/all non-CAT
* ExternalTransaction/{host}/{external_id}/{external_txnname} CAT
*
* These metrics are dictated by the spec located here:
* https://source.datanerd.us/agents/agent-specs/blob/master/Cross-Application-Tracing-PORTED.md
* These metrics are dictated by the agent-spec in this file:
* Cross-Application-Tracing-PORTED.md
*/
static void nr_segment_external_create_metrics(nr_segment_t* segment,
const char* uri,
Expand Down
Loading

0 comments on commit 996dcc9

Please sign in to comment.