Skip to content

Commit 996dcc9

Browse files
zsistlaZNeumannmfulb
authored
feat(axiom): Add message segment functionality (#991)
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]>
1 parent 94d1575 commit 996dcc9

28 files changed

+2077
-48
lines changed

agent/php_newrelic.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -596,6 +596,12 @@ nrinibool_t
596596
nrinibool_t
597597
vulnerability_management_composer_api_enabled; /* newrelic.vulnerability_management.composer_api.enabled */
598598

599+
/*
600+
* Configuration options for recording Messaging APIs
601+
*/
602+
nrinibool_t
603+
message_tracer_segment_parameters_enabled; /* newrelic.segment_tracer.segment_parameters.enabled */
604+
599605
#if ZEND_MODULE_API_NO < ZEND_7_4_X_API_NO
600606
/*
601607
* pid and user_function_wrappers are used to store user function wrappers.

agent/php_nrini.c

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3100,6 +3100,17 @@ STD_PHP_INI_ENTRY_EX("newrelic.vulnerability_management.composer_api.enabled",
31003100
newrelic_globals,
31013101
nr_enabled_disabled_dh)
31023102

3103+
/*
3104+
* Messaging API
3105+
*/
3106+
STD_PHP_INI_ENTRY_EX("newrelic.message_tracer.segment_parameters.enabled",
3107+
"1",
3108+
NR_PHP_REQUEST,
3109+
nr_boolean_mh,
3110+
message_tracer_segment_parameters_enabled,
3111+
zend_newrelic_globals,
3112+
newrelic_globals,
3113+
nr_enabled_disabled_dh)
31033114
PHP_INI_END() /* } */
31043115

31053116
void nr_php_register_ini_entries(int module_number TSRMLS_DC) {

agent/php_txn.c

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -854,6 +854,8 @@ nr_status_t nr_php_txn_begin(const char* appnames,
854854
opts.log_forwarding_log_level = NRINI(log_forwarding_log_level);
855855
opts.log_events_max_samples_stored = NRINI(log_events_max_samples_stored);
856856
opts.log_metrics_enabled = NRINI(log_metrics_enabled);
857+
opts.message_tracer_segment_parameters_enabled
858+
= NRINI(message_tracer_segment_parameters_enabled);
857859

858860
/*
859861
* Enable the behaviour whereby asynchronous time is discounted from the total
@@ -1165,7 +1167,7 @@ nr_status_t nr_php_txn_end(int ignoretxn, int in_post_deactivate TSRMLS_DC) {
11651167
#if ZEND_MODULE_API_NO >= ZEND_8_0_X_API_NO \
11661168
&& !defined OVERWRITE_ZEND_EXECUTE_DATA
11671169
nr_segment_t* segment = nr_txn_get_current_segment(NRPRG(txn), NULL);
1168-
while(NULL != segment && segment != NRTXN(segment_root)) {
1170+
while (NULL != segment && segment != NRTXN(segment_root)) {
11691171
nr_segment_end(&segment);
11701172
segment = nr_txn_get_current_segment(NRPRG(txn), NULL);
11711173
}

agent/scripts/newrelic.ini.template

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1341,3 +1341,14 @@ newrelic.daemon.logfile = "/var/log/newrelic/newrelic-daemon.log"
13411341
; to gather package information for vulnerability management.
13421342
;
13431343
;newrelic.vulnerability_management.composer_api.enabled = false
1344+
1345+
; Setting: newrelic.message_tracer.segment_parameters.enabled
1346+
; Type : boolean
1347+
; Scope : per-directory
1348+
; Default: true
1349+
; Info : If this setting is true, then message parameters will be captured and
1350+
; stored on their respective segments. While enabled, specific attributes
1351+
; can be filtered by using newrelic.attributes.include/exclude and
1352+
; newrelic.span_events.attributes.include/exclude
1353+
;
1354+
;newrelic.message_tracer.segment_parameters.enabled = true

axiom/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,7 @@ OBJS := \
113113
nr_segment_children.o \
114114
nr_segment_datastore.o \
115115
nr_segment_external.o \
116+
nr_segment_message.o \
116117
nr_segment_private.o \
117118
nr_segment_terms.o \
118119
nr_segment_traces.o \

axiom/nr_segment.c

Lines changed: 50 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -313,6 +313,26 @@ static void nr_populate_http_spans(nr_span_event_t* span_event,
313313
segment->typed_attributes->external.status);
314314
}
315315

316+
static void nr_populate_message_spans(nr_span_event_t* span_event,
317+
const nr_segment_t* segment) {
318+
nr_span_event_set_category(span_event, NR_SPAN_MESSAGE);
319+
320+
if (nrunlikely(NULL == segment || NULL == segment->typed_attributes)) {
321+
return;
322+
}
323+
324+
nr_span_event_set_spankind(span_event,
325+
segment->typed_attributes->message.message_action);
326+
nr_span_event_set_message(
327+
span_event, NR_SPAN_MESSAGE_DESTINATION_NAME,
328+
segment->typed_attributes->message.destination_name);
329+
nr_span_event_set_message(
330+
span_event, NR_SPAN_MESSAGE_MESSAGING_SYSTEM,
331+
segment->typed_attributes->message.messaging_system);
332+
nr_span_event_set_message(span_event, NR_SPAN_MESSAGE_SERVER_ADDRESS,
333+
segment->typed_attributes->message.server_address);
334+
}
335+
316336
static nr_status_t add_user_attribute_to_span_event(const char* key,
317337
const nrobj_t* val,
318338
void* ptr) {
@@ -431,8 +451,8 @@ nr_span_event_t* nr_segment_to_span_event(nr_segment_t* segment) {
431451
nr_span_event_set_trusted_parent_id(
432452
event, nr_distributed_trace_inbound_get_trusted_parent_id(
433453
segment->txn->distributed_trace));
434-
nr_span_event_set_parent_id(event,
435-
nr_distributed_trace_inbound_get_guid(segment->txn->distributed_trace));
454+
nr_span_event_set_parent_id(event, nr_distributed_trace_inbound_get_guid(
455+
segment->txn->distributed_trace));
436456

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

@@ -482,6 +502,10 @@ nr_span_event_t* nr_segment_to_span_event(nr_segment_t* segment) {
482502
nr_populate_http_spans(event, segment);
483503
break;
484504

505+
case NR_SEGMENT_MESSAGE:
506+
nr_populate_message_spans(event, segment);
507+
break;
508+
485509
case NR_SEGMENT_CUSTOM:
486510
nr_span_event_set_category(event, NR_SPAN_GENERIC);
487511
break;
@@ -599,6 +623,30 @@ bool nr_segment_set_external(nr_segment_t* segment,
599623
return true;
600624
}
601625

626+
bool nr_segment_set_message(nr_segment_t* segment,
627+
const nr_segment_message_t* message) {
628+
if (nrunlikely((NULL == segment) || (NULL == message))) {
629+
return false;
630+
}
631+
632+
nr_segment_destroy_typed_attributes(segment->type,
633+
&segment->typed_attributes);
634+
segment->type = NR_SEGMENT_MESSAGE;
635+
segment->typed_attributes = nr_zalloc(sizeof(nr_segment_typed_attributes_t));
636+
637+
// clang-format off
638+
// Initialize the fields of the message attributes, one field per line.
639+
segment->typed_attributes->message = (nr_segment_message_t){
640+
.message_action = message->message_action,
641+
.destination_name = nr_strempty(message->destination_name) ? NULL: nr_strdup(message->destination_name),
642+
.messaging_system = nr_strempty(message->messaging_system) ? NULL: nr_strdup(message->messaging_system),
643+
.server_address = nr_strempty(message->server_address) ? NULL: nr_strdup(message->server_address),
644+
};
645+
// clang-format on
646+
647+
return true;
648+
}
649+
602650
bool nr_segment_add_child(nr_segment_t* parent, nr_segment_t* child) {
603651
if (nrunlikely((NULL == parent) || (NULL == child))) {
604652
return false;

axiom/nr_segment.h

Lines changed: 58 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,8 @@ typedef struct _nrtxn_t nrtxn_t;
3434
typedef enum _nr_segment_type_t {
3535
NR_SEGMENT_CUSTOM,
3636
NR_SEGMENT_DATASTORE,
37-
NR_SEGMENT_EXTERNAL
37+
NR_SEGMENT_EXTERNAL,
38+
NR_SEGMENT_MESSAGE
3839
} nr_segment_type_t;
3940

4041
/*
@@ -109,6 +110,48 @@ typedef struct _nr_segment_external_t {
109110
uint64_t status;
110111
} nr_segment_external_t;
111112

113+
typedef struct _nr_segment_message_t {
114+
/*
115+
* Attributes needed for entity relationship building.
116+
* Compare to OTEL attributes:
117+
* https://opentelemetry.io/docs/specs/semconv/attributes-registry/cloud/
118+
* cloud.account.id, cloud.region, messaging.system and server.address are
119+
* used to create relationships between APM and cloud services. It may not
120+
* make sense to add these attributes unless they are used for creating one of
121+
* the relationships in Entity Relationships.
122+
*/
123+
124+
nr_span_spankind_t
125+
message_action; /*The action of the message, e.g.,Produce/Consume.*/
126+
char* destination_name; /*The name of the Queue, Topic, or Exchange;
127+
otherwise, Temp. Needed for SQS relationship.*/
128+
char* messaging_system; /* for ex: aws_sqs. Needed for SQS relationship.*/
129+
char* server_address; /*The server domain name or IP address. Needed for
130+
MQBROKER relationship.*/
131+
} nr_segment_message_t;
132+
133+
typedef struct _nr_segment_cloud_attrs_t {
134+
/*
135+
* Attributes needed for entity relationship building.
136+
* Compare to OTEL attributes:
137+
* https://opentelemetry.io/docs/specs/semconv/attributes-registry/cloud/
138+
* cloud.account.id, cloud.region, messaging.system and server.address are
139+
* used to create relationships between APM and cloud services. It may not
140+
* make sense to add these attributes unless they are used for creating one of
141+
* the relationships in Entity Relationships.
142+
* These attributes aren't specific to a segment category so don't belong as
143+
* typed attributes and can be added whenever they are available.
144+
*/
145+
char* cloud_region; /*Targeted region; ex:us-east-1*. Needed for SQS
146+
relationship.*/
147+
char* cloud_account_id; /*The cloud provider account ID. Needed for SQS
148+
relationship.*/
149+
char* cloud_resource_id; /*Unique cloud provider identifier. For AWS, this is
150+
the ARN of the AWS resource being accessed.*/
151+
char* aws_operation; /*AWS specific operation name.*/
152+
153+
} nr_segment_cloud_attrs_t;
154+
112155
typedef struct _nr_segment_metric_t {
113156
char* name;
114157
bool scoped;
@@ -132,6 +175,7 @@ typedef struct _nr_segment_error_t {
132175
typedef union {
133176
nr_segment_datastore_t datastore;
134177
nr_segment_external_t external;
178+
nr_segment_message_t message;
135179
} nr_segment_typed_attributes_t;
136180

137181
typedef struct _nr_segment_t {
@@ -179,8 +223,8 @@ typedef struct _nr_segment_t {
179223
int priority; /* Used to determine which segments are preferred for span event
180224
creation */
181225
nr_segment_typed_attributes_t* typed_attributes; /* Attributes specific to
182-
external or datastore
183-
segments. */
226+
external, datastore,
227+
or message segments. */
184228
nr_segment_error_t* error; /* segment error attributes */
185229
#if ZEND_MODULE_API_NO >= ZEND_8_0_X_API_NO \
186230
&& !defined OVERWRITE_ZEND_EXECUTE_DATA /* PHP 8.0+ and OAPI */
@@ -314,6 +358,17 @@ extern bool nr_segment_set_datastore(nr_segment_t* segment,
314358
*/
315359
extern bool nr_segment_set_external(nr_segment_t* segment,
316360
const nr_segment_external_t* external);
361+
362+
/*
363+
* Purpose : Mark the segment as being a message segment.
364+
*
365+
* Params : 1. The pointer to the segment.
366+
* 2. The message attributes, which will be copied into the segment.
367+
*
368+
* Returns : true if successful, false otherwise.
369+
*/
370+
extern bool nr_segment_set_message(nr_segment_t* segment,
371+
const nr_segment_message_t* message);
317372
/*
318373
* Purpose : Add a child to a segment.
319374
*

axiom/nr_segment_external.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -57,8 +57,8 @@ static void nr_segment_external_set_attrs(
5757
* External/{host}/all non-CAT
5858
* ExternalTransaction/{host}/{external_id}/{external_txnname} CAT
5959
*
60-
* These metrics are dictated by the spec located here:
61-
* https://source.datanerd.us/agents/agent-specs/blob/master/Cross-Application-Tracing-PORTED.md
60+
* These metrics are dictated by the agent-spec in this file:
61+
* Cross-Application-Tracing-PORTED.md
6262
*/
6363
static void nr_segment_external_create_metrics(nr_segment_t* segment,
6464
const char* uri,

0 commit comments

Comments
 (0)