Skip to content

Commit a033b81

Browse files
tippmar-nrjaffinitonr-ahemsath
authored
feat: Additional metrics and attributes for some instrumented libraries (#2675)
--------- Co-authored-by: Jacob Affinito <[email protected]> Co-authored-by: Alex Hemsath <[email protected]>
1 parent d3117a4 commit a033b81

File tree

29 files changed

+1117
-67
lines changed

29 files changed

+1117
-67
lines changed

src/Agent/NewRelic/Agent/Core/Attributes/AttributeDefinition.cs

+5
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,11 @@ public static AttributeDefinitionBuilder<long, long> CreateLong(string name, Att
7575
return Create<long>(name, classification);
7676
}
7777

78+
public static AttributeDefinitionBuilder<int, int> CreateInt(string name, AttributeClassification classification)
79+
{
80+
return Create<int>(name, classification);
81+
}
82+
7883
public static AttributeDefinitionBuilder<string, string> CreateDBStatement(string name, AttributeClassification classification)
7984
{
8085
const int dbStmtMaxLength = 1999;

src/Agent/NewRelic/Agent/Core/Attributes/AttributeDefinitionService.cs

+80-1
Original file line numberDiff line numberDiff line change
@@ -133,6 +133,17 @@ public interface IAttributeDefinitions
133133
AttributeDefinition<TypeAttributeValue, string> GetTypeAttribute(TypeAttributeValue destination);
134134

135135
AttributeDefinition<bool, bool> LlmTransaction { get; }
136+
137+
AttributeDefinition<string, string> CloudAccountId { get; }
138+
AttributeDefinition<string, string> CloudRegion { get; }
139+
AttributeDefinition<string, string> MessagingSystemName { get; }
140+
AttributeDefinition<string, string> MessagingDestinationName { get; }
141+
AttributeDefinition<string, string> BrokerServerAddress { get; }
142+
AttributeDefinition<int, int> BrokerServerPort { get; }
143+
AttributeDefinition<string, string> MessageQueueName { get; }
144+
AttributeDefinition<string, string> MessageRoutingKey { get; }
145+
AttributeDefinition<string, string> MessagingRabbitMqDestinationRoutingKey { get; }
146+
AttributeDefinition<string, string> MessagingDestinationPublishName { get; }
136147
}
137148

138149

@@ -177,7 +188,7 @@ public AttributeDefinitions(IAttributeFilter attribFilter)
177188
private readonly ConcurrentDictionary<string, AttributeDefinition<string, string>> _requestParameterAttributes = new ConcurrentDictionary<string, AttributeDefinition<string, string>>();
178189
private readonly ConcurrentDictionary<string, AttributeDefinition<string, string>> _requestHeadersAttributes = new ConcurrentDictionary<string, AttributeDefinition<string, string>>();
179190
private readonly ConcurrentDictionary<string, AttributeDefinition<object, object>> _lambdaAttributes = new ConcurrentDictionary<string, AttributeDefinition<object, object>>();
180-
191+
181192
private readonly ConcurrentDictionary<TypeAttributeValue, AttributeDefinition<TypeAttributeValue, string>> _typeAttributes = new ConcurrentDictionary<TypeAttributeValue, AttributeDefinition<TypeAttributeValue, string>>();
182193

183194

@@ -1079,5 +1090,73 @@ private static string IgnoreEmptyAndWhitespaceErrorGroupValues(string errorGroup
10791090
.AppliesTo(AttributeDestinations.TransactionEvent)
10801091
.AppliesTo(AttributeDestinations.TransactionTrace)
10811092
.Build(_attribFilter));
1093+
1094+
private AttributeDefinition<string, string> _cloudAccountId;
1095+
public AttributeDefinition<string, string> CloudAccountId => _cloudAccountId ?? (_cloudAccountId =
1096+
AttributeDefinitionBuilder.CreateString("cloud.account.id", AttributeClassification.AgentAttributes)
1097+
.AppliesTo(AttributeDestinations.SpanEvent)
1098+
.AppliesTo(AttributeDestinations.TransactionTrace)
1099+
.Build(_attribFilter));
1100+
1101+
private AttributeDefinition<string, string> _cloudRegion;
1102+
public AttributeDefinition<string, string> CloudRegion => _cloudRegion ?? (_cloudRegion =
1103+
AttributeDefinitionBuilder.CreateString("cloud.region", AttributeClassification.AgentAttributes)
1104+
.AppliesTo(AttributeDestinations.SpanEvent)
1105+
.AppliesTo(AttributeDestinations.TransactionTrace)
1106+
.Build(_attribFilter));
1107+
1108+
private AttributeDefinition<string, string> _messagingSystem;
1109+
public AttributeDefinition<string, string> MessagingSystemName => _messagingSystem ?? (_messagingSystem =
1110+
AttributeDefinitionBuilder.CreateString("messaging.system", AttributeClassification.AgentAttributes)
1111+
.AppliesTo(AttributeDestinations.SpanEvent)
1112+
.AppliesTo(AttributeDestinations.TransactionTrace)
1113+
.Build(_attribFilter));
1114+
1115+
private AttributeDefinition<string, string> _messagingDestinationName;
1116+
public AttributeDefinition<string, string> MessagingDestinationName => _messagingDestinationName ?? (_messagingDestinationName =
1117+
AttributeDefinitionBuilder.CreateString("messaging.destination.name", AttributeClassification.AgentAttributes)
1118+
.AppliesTo(AttributeDestinations.SpanEvent)
1119+
.AppliesTo(AttributeDestinations.TransactionTrace)
1120+
.Build(_attribFilter));
1121+
1122+
// new attribute for MessageBrokers - same name as the Externals attribute, but in Agent attributes.
1123+
// From messaging api spec.
1124+
private AttributeDefinition<string, string> _brokerServerAddress;
1125+
public AttributeDefinition<string, string> BrokerServerAddress => _brokerServerAddress ?? (_brokerServerAddress =
1126+
AttributeDefinitionBuilder.CreateString("server.address", AttributeClassification.AgentAttributes)
1127+
.AppliesTo(AttributeDestinations.SpanEvent)
1128+
.AppliesTo(AttributeDestinations.TransactionTrace)
1129+
.Build(_attribFilter));
1130+
1131+
private AttributeDefinition<int, int> _brokerServerPort;
1132+
public AttributeDefinition<int, int> BrokerServerPort => _brokerServerPort ?? (_brokerServerPort =
1133+
AttributeDefinitionBuilder.CreateInt("server.port", AttributeClassification.AgentAttributes)
1134+
.AppliesTo(AttributeDestinations.SpanEvent)
1135+
.AppliesTo(AttributeDestinations.TransactionTrace)
1136+
.Build(_attribFilter));
1137+
1138+
private AttributeDefinition<string, string> _messageQueueName;
1139+
public AttributeDefinition<string, string> MessageQueueName => _messageQueueName ?? (_messageQueueName =
1140+
AttributeDefinitionBuilder.CreateString("message.queueName", AttributeClassification.AgentAttributes)
1141+
.AppliesTo(AttributeDestinations.All)
1142+
.Build(_attribFilter));
1143+
1144+
private AttributeDefinition<string, string> _messageRoutingKey;
1145+
public AttributeDefinition<string, string> MessageRoutingKey => _messageRoutingKey ?? (_messageRoutingKey =
1146+
AttributeDefinitionBuilder.CreateString("message.routingKey", AttributeClassification.AgentAttributes)
1147+
.AppliesTo(AttributeDestinations.All)
1148+
.Build(_attribFilter));
1149+
1150+
private AttributeDefinition<string, string> _messagingRabbitMqDestinationRoutingKey;
1151+
public AttributeDefinition<string, string> MessagingRabbitMqDestinationRoutingKey => _messagingRabbitMqDestinationRoutingKey ?? (_messagingRabbitMqDestinationRoutingKey =
1152+
AttributeDefinitionBuilder.CreateString("messaging.rabbitmq.destination.routing_key", AttributeClassification.AgentAttributes)
1153+
.AppliesTo(AttributeDestinations.SpanEvent)
1154+
.Build(_attribFilter));
1155+
1156+
private AttributeDefinition<string, string> _messagingDestinationPublishName;
1157+
public AttributeDefinition<string, string> MessagingDestinationPublishName => _messagingDestinationPublishName ?? (_messagingDestinationPublishName =
1158+
AttributeDefinitionBuilder.CreateString("messaging.destination_publish.name", AttributeClassification.AgentAttributes)
1159+
.AppliesTo(AttributeDestinations.SpanEvent)
1160+
.Build(_attribFilter));
10821161
}
10831162
}

src/Agent/NewRelic/Agent/Core/Segments/MessageBrokerSegmentData.cs

+71-1
Original file line numberDiff line numberDiff line change
@@ -24,15 +24,39 @@ public class MessageBrokerSegmentData : AbstractSegmentData
2424

2525
public MetricNames.MessageBrokerAction Action { get; set; }
2626

27+
public string MessagingSystemName {get; set;}
2728

28-
public MessageBrokerSegmentData(string vendor, string destination, MetricNames.MessageBrokerDestinationType destinationType, MetricNames.MessageBrokerAction action)
29+
public string CloudAccountId {get; set;}
30+
31+
public string CloudRegion {get; set;}
32+
33+
public string ServerAddress {get; set;}
34+
35+
public int? ServerPort {get; set;}
36+
37+
public string RoutingKey { get; set; }
38+
39+
40+
public MessageBrokerSegmentData(string vendor, string destination,
41+
MetricNames.MessageBrokerDestinationType destinationType, MetricNames.MessageBrokerAction action,
42+
string messagingSystemName = null, string cloudAccountId = null, string cloudRegion = null,
43+
string serverAddress = null, int? serverPort = null, string routingKey = null)
2944
{
3045
Vendor = vendor;
3146
Destination = destination;
3247
DestinationType = destinationType;
3348
Action = action;
49+
50+
// attributes required for entity relationship mapping
51+
MessagingSystemName = messagingSystemName;
52+
CloudAccountId = cloudAccountId;
53+
CloudRegion = cloudRegion;
54+
ServerAddress = serverAddress;
55+
ServerPort = serverPort;
56+
RoutingKey = routingKey;
3457
}
3558

59+
3660
public override bool IsCombinableWith(AbstractSegmentData otherData)
3761
{
3862
var otherTypedSegment = otherData as MessageBrokerSegmentData;
@@ -51,6 +75,23 @@ public override bool IsCombinableWith(AbstractSegmentData otherData)
5175
if (Action != otherTypedSegment.Action)
5276
return false;
5377

78+
if (MessagingSystemName != otherTypedSegment.MessagingSystemName)
79+
return false;
80+
81+
if (CloudAccountId != otherTypedSegment.CloudAccountId)
82+
return false;
83+
84+
if (CloudRegion != otherTypedSegment.CloudRegion)
85+
return false;
86+
87+
if (ServerAddress != otherTypedSegment.ServerAddress)
88+
return false;
89+
90+
if (ServerPort != otherTypedSegment.ServerPort)
91+
return false;
92+
93+
// Not using routing key for segment combination since it is not present for BasicGet and might be unique for each message.
94+
5495
return true;
5596
}
5697

@@ -65,7 +106,36 @@ public override void AddMetricStats(Segment segment, TimeSpan durationOfChildren
65106
var exclusiveDuration = TimeSpanMath.Max(TimeSpan.Zero, duration - durationOfChildren);
66107

67108
MetricBuilder.TryBuildMessageBrokerSegmentMetric(Vendor, Destination, DestinationType, Action, duration, exclusiveDuration, txStats);
109+
}
68110

111+
public override void SetSpanTypeSpecificAttributes(SpanAttributeValueCollection attribVals)
112+
{
113+
base.SetSpanTypeSpecificAttributes(attribVals);
114+
115+
if (Action == MetricNames.MessageBrokerAction.Produce)
116+
{
117+
AttribDefs.SpanKind.TrySetValue(attribVals, "producer");
118+
}
119+
else if (Action == MetricNames.MessageBrokerAction.Consume)
120+
{
121+
AttribDefs.SpanKind.TrySetValue(attribVals, "consumer");
122+
AttribDefs.MessageQueueName.TrySetValue(attribVals, Destination);
123+
AttribDefs.MessagingDestinationPublishName.TrySetValue(attribVals, Destination);
124+
}
125+
// else purge action - do not set the attribute
126+
127+
if (ServerPort.HasValue)
128+
{
129+
AttribDefs.BrokerServerPort.TrySetValue(attribVals, ServerPort.Value);
130+
}
131+
132+
AttribDefs.BrokerServerAddress.TrySetValue(attribVals, ServerAddress);
133+
AttribDefs.MessagingSystemName.TrySetValue(attribVals, MessagingSystemName);
134+
AttribDefs.CloudRegion.TrySetValue(attribVals, CloudRegion);
135+
AttribDefs.CloudAccountId.TrySetValue(attribVals, CloudAccountId);
136+
AttribDefs.MessagingDestinationName.TrySetValue(attribVals, Destination);
137+
AttribDefs.MessageRoutingKey.TrySetValue(attribVals, RoutingKey);
138+
AttribDefs.MessagingRabbitMqDestinationRoutingKey.TrySetValue(attribVals, RoutingKey);
69139
}
70140
}
71141
}

src/Agent/NewRelic/Agent/Core/Transactions/NoOpTransaction.cs

+4-1
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,10 @@ public ISegment StartExternalRequestSegment(MethodCall methodCall, Uri destinati
7373
return Segment.NoOpSegment;
7474
}
7575

76-
public ISegment StartMessageBrokerSegment(MethodCall methodCall, MessageBrokerDestinationType destinationType, MessageBrokerAction operation, string brokerVendorName, string destinationName)
76+
public ISegment StartMessageBrokerSegment(MethodCall methodCall, MessageBrokerDestinationType destinationType,
77+
MessageBrokerAction operation, string brokerVendorName, string destinationName,
78+
string messagingSystemName = null, string cloudAccountId = null, string cloudRegion = null,
79+
string serverAddress = null, int? serverPort = null, string routingKey = null)
7780
{
7881
#if DEBUG
7982
Log.Finest("Skipping StartMessageBrokerSegment outside of a transaction");

src/Agent/NewRelic/Agent/Core/Transactions/Transaction.cs

+12-6
Original file line numberDiff line numberDiff line change
@@ -243,7 +243,10 @@ public AbstractSegmentData CreateCustomSegmentData(string segmentName)
243243
return new CustomSegmentData(segmentName);
244244
}
245245

246-
public ISegment StartMessageBrokerSegment(MethodCall methodCall, MessageBrokerDestinationType destinationType, MessageBrokerAction operation, string brokerVendorName, string destinationName)
246+
public ISegment StartMessageBrokerSegment(MethodCall methodCall, MessageBrokerDestinationType destinationType,
247+
MessageBrokerAction operation, string brokerVendorName, string destinationName,
248+
string messagingSystemName = null, string cloudAccountId = null, string cloudRegion = null,
249+
string serverAddress = null, int? serverPort = null, string routingKey = null)
247250
{
248251
if (Ignored)
249252
return Segment.NoOpSegment;
@@ -252,7 +255,7 @@ public ISegment StartMessageBrokerSegment(MethodCall methodCall, MessageBrokerDe
252255

253256

254257
var segment = StartSegmentImpl(methodCall);
255-
var messageBrokerSegmentData = CreateMessageBrokerSegmentData(destinationType, operation, brokerVendorName, destinationName);
258+
var messageBrokerSegmentData = CreateMessageBrokerSegmentData(destinationType, operation, brokerVendorName, destinationName, messagingSystemName, cloudAccountId, cloudRegion, serverAddress, serverPort, routingKey);
256259

257260
segment.SetSegmentData(messageBrokerSegmentData);
258261

@@ -281,15 +284,18 @@ public ISegment StartMessageBrokerSerializationSegment(MethodCall methodCall, Me
281284
return segment;
282285
}
283286

284-
public AbstractSegmentData CreateMessageBrokerSegmentData(MessageBrokerDestinationType destinationType, MessageBrokerAction operation, string brokerVendorName, string destinationName)
287+
public AbstractSegmentData CreateMessageBrokerSegmentData(MessageBrokerDestinationType destinationType,
288+
MessageBrokerAction operation, string brokerVendorName, string destinationName,
289+
string messagingSystemName = null, string cloudAccountId = null, string cloudRegion = null,
290+
string serverAddress = null, int? serverPort = null, string routingKey = null)
285291
{
286292
if (brokerVendorName == null)
287293
throw new ArgumentNullException("brokerVendorName");
288294

289295
var action = AgentWrapperApiEnumToMetricNamesEnum(operation);
290296
var destType = AgentWrapperApiEnumToMetricNamesEnum(destinationType);
291297

292-
return new MessageBrokerSegmentData(brokerVendorName, destinationName, destType, action);
298+
return new MessageBrokerSegmentData(brokerVendorName, destinationName, destType, action, messagingSystemName: messagingSystemName, cloudAccountId: cloudAccountId, cloudRegion: cloudRegion, serverAddress: serverAddress, serverPort: serverPort, routingKey: routingKey);
293299
}
294300

295301
public AbstractSegmentData CreateMessageBrokerSerializationSegmentData(MessageBrokerDestinationType destinationType, MessageBrokerAction operation, string brokerVendorName, string destinationName, string kind)
@@ -784,7 +790,7 @@ public void Hold()
784790

785791
public void Release()
786792
{
787-
End(captureResponseTime: false);
793+
End(false);
788794
}
789795

790796
private void SetTransactionName(ITransactionName transactionName, TransactionNamePriority priority)
@@ -1364,7 +1370,7 @@ public void SetLlmTransaction(bool isLlmTransaction)
13641370
/// <param name="value">Value for attribute.</param>
13651371
public void AddLambdaAttribute(string name, object value)
13661372
{
1367-
if(string.IsNullOrWhiteSpace(name))
1373+
if (string.IsNullOrWhiteSpace(name))
13681374
{
13691375
Log.Debug($"AddLambdaAttribute - Unable to set Lambda value on transaction because the key is null/empty");
13701376
return;

src/Agent/NewRelic/Agent/Core/Utilities/ExtensionsLoader.cs

+1
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,7 @@ public static void Initialize(string installPathExtensionsDirectory)
8181
{ "TransportConfigLegacyWrapper", Path.Combine(_installPathExtensionsDirectory, "NewRelic.Providers.Wrapper.MassTransitLegacy.dll") },
8282

8383
// Kafka
84+
{ "KafkaBuilderWrapper", Path.Combine(_installPathExtensionsDirectory, "NewRelic.Providers.Wrapper.Kafka.dll") },
8485
{ "KafkaProducerWrapper", Path.Combine(_installPathExtensionsDirectory, "NewRelic.Providers.Wrapper.Kafka.dll") },
8586
{ "KafkaSerializerWrapper", Path.Combine(_installPathExtensionsDirectory, "NewRelic.Providers.Wrapper.Kafka.dll") },
8687
{ "KafkaConsumerWrapper", Path.Combine(_installPathExtensionsDirectory, "NewRelic.Providers.Wrapper.Kafka.dll") }

src/Agent/NewRelic/Agent/Extensions/NewRelic.Agent.Extensions/Api/ITransaction.cs

+7-1
Original file line numberDiff line numberDiff line change
@@ -87,9 +87,15 @@ public interface ITransaction
8787
/// <param name="operation"></param>
8888
/// <param name="brokerVendorName">Must not be null.</param>
8989
/// <param name="destinationName">Can be null.</param>
90+
/// <param name="messagingSystemName"></param>
91+
/// <param name="cloudAccountId"></param>
92+
/// <param name="cloudRegion"></param>
9093
/// <exception cref="ArgumentNullException"></exception>
9194
/// <returns>an opaque object that will be needed when you want to end the segment.</returns>
92-
ISegment StartMessageBrokerSegment(MethodCall methodCall, MessageBrokerDestinationType destinationType, MessageBrokerAction operation, string brokerVendorName, string destinationName = null);
95+
ISegment StartMessageBrokerSegment(MethodCall methodCall, MessageBrokerDestinationType destinationType,
96+
MessageBrokerAction operation, string brokerVendorName, string destinationName = null,
97+
string messagingSystemName = null, string cloudAccountId = null, string cloudRegion = null,
98+
string serverAddress = null, int? serverPort = null, string routingKey = null);
9399

94100
/// <summary>
95101
/// Creates a segment for serializing a key or value in a message brokering system..

0 commit comments

Comments
 (0)