Skip to content

Commit

Permalink
feat: Additional metrics and attributes for some instrumented librari…
Browse files Browse the repository at this point in the history
…es (#2675)

---------

Co-authored-by: Jacob Affinito <jaffinito@newrelic.com>
Co-authored-by: Alex Hemsath <ahemsath@newrelic.com>
  • Loading branch information
3 people authored Aug 9, 2024
1 parent d3117a4 commit a033b81
Show file tree
Hide file tree
Showing 29 changed files with 1,117 additions and 67 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,11 @@ public static AttributeDefinitionBuilder<long, long> CreateLong(string name, Att
return Create<long>(name, classification);
}

public static AttributeDefinitionBuilder<int, int> CreateInt(string name, AttributeClassification classification)
{
return Create<int>(name, classification);
}

public static AttributeDefinitionBuilder<string, string> CreateDBStatement(string name, AttributeClassification classification)
{
const int dbStmtMaxLength = 1999;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,17 @@ public interface IAttributeDefinitions
AttributeDefinition<TypeAttributeValue, string> GetTypeAttribute(TypeAttributeValue destination);

AttributeDefinition<bool, bool> LlmTransaction { get; }

AttributeDefinition<string, string> CloudAccountId { get; }
AttributeDefinition<string, string> CloudRegion { get; }
AttributeDefinition<string, string> MessagingSystemName { get; }
AttributeDefinition<string, string> MessagingDestinationName { get; }
AttributeDefinition<string, string> BrokerServerAddress { get; }
AttributeDefinition<int, int> BrokerServerPort { get; }
AttributeDefinition<string, string> MessageQueueName { get; }
AttributeDefinition<string, string> MessageRoutingKey { get; }
AttributeDefinition<string, string> MessagingRabbitMqDestinationRoutingKey { get; }
AttributeDefinition<string, string> MessagingDestinationPublishName { get; }
}


Expand Down Expand Up @@ -177,7 +188,7 @@ public AttributeDefinitions(IAttributeFilter attribFilter)
private readonly ConcurrentDictionary<string, AttributeDefinition<string, string>> _requestParameterAttributes = new ConcurrentDictionary<string, AttributeDefinition<string, string>>();
private readonly ConcurrentDictionary<string, AttributeDefinition<string, string>> _requestHeadersAttributes = new ConcurrentDictionary<string, AttributeDefinition<string, string>>();
private readonly ConcurrentDictionary<string, AttributeDefinition<object, object>> _lambdaAttributes = new ConcurrentDictionary<string, AttributeDefinition<object, object>>();

private readonly ConcurrentDictionary<TypeAttributeValue, AttributeDefinition<TypeAttributeValue, string>> _typeAttributes = new ConcurrentDictionary<TypeAttributeValue, AttributeDefinition<TypeAttributeValue, string>>();


Expand Down Expand Up @@ -1079,5 +1090,73 @@ private static string IgnoreEmptyAndWhitespaceErrorGroupValues(string errorGroup
.AppliesTo(AttributeDestinations.TransactionEvent)
.AppliesTo(AttributeDestinations.TransactionTrace)
.Build(_attribFilter));

private AttributeDefinition<string, string> _cloudAccountId;
public AttributeDefinition<string, string> CloudAccountId => _cloudAccountId ?? (_cloudAccountId =
AttributeDefinitionBuilder.CreateString("cloud.account.id", AttributeClassification.AgentAttributes)
.AppliesTo(AttributeDestinations.SpanEvent)
.AppliesTo(AttributeDestinations.TransactionTrace)
.Build(_attribFilter));

private AttributeDefinition<string, string> _cloudRegion;
public AttributeDefinition<string, string> CloudRegion => _cloudRegion ?? (_cloudRegion =
AttributeDefinitionBuilder.CreateString("cloud.region", AttributeClassification.AgentAttributes)
.AppliesTo(AttributeDestinations.SpanEvent)
.AppliesTo(AttributeDestinations.TransactionTrace)
.Build(_attribFilter));

private AttributeDefinition<string, string> _messagingSystem;
public AttributeDefinition<string, string> MessagingSystemName => _messagingSystem ?? (_messagingSystem =
AttributeDefinitionBuilder.CreateString("messaging.system", AttributeClassification.AgentAttributes)
.AppliesTo(AttributeDestinations.SpanEvent)
.AppliesTo(AttributeDestinations.TransactionTrace)
.Build(_attribFilter));

private AttributeDefinition<string, string> _messagingDestinationName;
public AttributeDefinition<string, string> MessagingDestinationName => _messagingDestinationName ?? (_messagingDestinationName =
AttributeDefinitionBuilder.CreateString("messaging.destination.name", AttributeClassification.AgentAttributes)
.AppliesTo(AttributeDestinations.SpanEvent)
.AppliesTo(AttributeDestinations.TransactionTrace)
.Build(_attribFilter));

// new attribute for MessageBrokers - same name as the Externals attribute, but in Agent attributes.
// From messaging api spec.
private AttributeDefinition<string, string> _brokerServerAddress;
public AttributeDefinition<string, string> BrokerServerAddress => _brokerServerAddress ?? (_brokerServerAddress =
AttributeDefinitionBuilder.CreateString("server.address", AttributeClassification.AgentAttributes)
.AppliesTo(AttributeDestinations.SpanEvent)
.AppliesTo(AttributeDestinations.TransactionTrace)
.Build(_attribFilter));

private AttributeDefinition<int, int> _brokerServerPort;
public AttributeDefinition<int, int> BrokerServerPort => _brokerServerPort ?? (_brokerServerPort =
AttributeDefinitionBuilder.CreateInt("server.port", AttributeClassification.AgentAttributes)
.AppliesTo(AttributeDestinations.SpanEvent)
.AppliesTo(AttributeDestinations.TransactionTrace)
.Build(_attribFilter));

private AttributeDefinition<string, string> _messageQueueName;
public AttributeDefinition<string, string> MessageQueueName => _messageQueueName ?? (_messageQueueName =
AttributeDefinitionBuilder.CreateString("message.queueName", AttributeClassification.AgentAttributes)
.AppliesTo(AttributeDestinations.All)
.Build(_attribFilter));

private AttributeDefinition<string, string> _messageRoutingKey;
public AttributeDefinition<string, string> MessageRoutingKey => _messageRoutingKey ?? (_messageRoutingKey =
AttributeDefinitionBuilder.CreateString("message.routingKey", AttributeClassification.AgentAttributes)
.AppliesTo(AttributeDestinations.All)
.Build(_attribFilter));

private AttributeDefinition<string, string> _messagingRabbitMqDestinationRoutingKey;
public AttributeDefinition<string, string> MessagingRabbitMqDestinationRoutingKey => _messagingRabbitMqDestinationRoutingKey ?? (_messagingRabbitMqDestinationRoutingKey =
AttributeDefinitionBuilder.CreateString("messaging.rabbitmq.destination.routing_key", AttributeClassification.AgentAttributes)
.AppliesTo(AttributeDestinations.SpanEvent)
.Build(_attribFilter));

private AttributeDefinition<string, string> _messagingDestinationPublishName;
public AttributeDefinition<string, string> MessagingDestinationPublishName => _messagingDestinationPublishName ?? (_messagingDestinationPublishName =
AttributeDefinitionBuilder.CreateString("messaging.destination_publish.name", AttributeClassification.AgentAttributes)
.AppliesTo(AttributeDestinations.SpanEvent)
.Build(_attribFilter));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -24,15 +24,39 @@ public class MessageBrokerSegmentData : AbstractSegmentData

public MetricNames.MessageBrokerAction Action { get; set; }

public string MessagingSystemName {get; set;}

public MessageBrokerSegmentData(string vendor, string destination, MetricNames.MessageBrokerDestinationType destinationType, MetricNames.MessageBrokerAction action)
public string CloudAccountId {get; set;}

public string CloudRegion {get; set;}

public string ServerAddress {get; set;}

public int? ServerPort {get; set;}

public string RoutingKey { get; set; }


public MessageBrokerSegmentData(string vendor, string destination,
MetricNames.MessageBrokerDestinationType destinationType, MetricNames.MessageBrokerAction action,
string messagingSystemName = null, string cloudAccountId = null, string cloudRegion = null,
string serverAddress = null, int? serverPort = null, string routingKey = null)
{
Vendor = vendor;
Destination = destination;
DestinationType = destinationType;
Action = action;

// attributes required for entity relationship mapping
MessagingSystemName = messagingSystemName;
CloudAccountId = cloudAccountId;
CloudRegion = cloudRegion;
ServerAddress = serverAddress;
ServerPort = serverPort;
RoutingKey = routingKey;
}


public override bool IsCombinableWith(AbstractSegmentData otherData)
{
var otherTypedSegment = otherData as MessageBrokerSegmentData;
Expand All @@ -51,6 +75,23 @@ public override bool IsCombinableWith(AbstractSegmentData otherData)
if (Action != otherTypedSegment.Action)
return false;

if (MessagingSystemName != otherTypedSegment.MessagingSystemName)
return false;

if (CloudAccountId != otherTypedSegment.CloudAccountId)
return false;

if (CloudRegion != otherTypedSegment.CloudRegion)
return false;

if (ServerAddress != otherTypedSegment.ServerAddress)
return false;

if (ServerPort != otherTypedSegment.ServerPort)
return false;

// Not using routing key for segment combination since it is not present for BasicGet and might be unique for each message.

return true;
}

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

MetricBuilder.TryBuildMessageBrokerSegmentMetric(Vendor, Destination, DestinationType, Action, duration, exclusiveDuration, txStats);
}

public override void SetSpanTypeSpecificAttributes(SpanAttributeValueCollection attribVals)
{
base.SetSpanTypeSpecificAttributes(attribVals);

if (Action == MetricNames.MessageBrokerAction.Produce)
{
AttribDefs.SpanKind.TrySetValue(attribVals, "producer");
}
else if (Action == MetricNames.MessageBrokerAction.Consume)
{
AttribDefs.SpanKind.TrySetValue(attribVals, "consumer");
AttribDefs.MessageQueueName.TrySetValue(attribVals, Destination);
AttribDefs.MessagingDestinationPublishName.TrySetValue(attribVals, Destination);
}
// else purge action - do not set the attribute

if (ServerPort.HasValue)
{
AttribDefs.BrokerServerPort.TrySetValue(attribVals, ServerPort.Value);
}

AttribDefs.BrokerServerAddress.TrySetValue(attribVals, ServerAddress);
AttribDefs.MessagingSystemName.TrySetValue(attribVals, MessagingSystemName);
AttribDefs.CloudRegion.TrySetValue(attribVals, CloudRegion);
AttribDefs.CloudAccountId.TrySetValue(attribVals, CloudAccountId);
AttribDefs.MessagingDestinationName.TrySetValue(attribVals, Destination);
AttribDefs.MessageRoutingKey.TrySetValue(attribVals, RoutingKey);
AttribDefs.MessagingRabbitMqDestinationRoutingKey.TrySetValue(attribVals, RoutingKey);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,10 @@ public ISegment StartExternalRequestSegment(MethodCall methodCall, Uri destinati
return Segment.NoOpSegment;
}

public ISegment StartMessageBrokerSegment(MethodCall methodCall, MessageBrokerDestinationType destinationType, MessageBrokerAction operation, string brokerVendorName, string destinationName)
public ISegment StartMessageBrokerSegment(MethodCall methodCall, MessageBrokerDestinationType destinationType,
MessageBrokerAction operation, string brokerVendorName, string destinationName,
string messagingSystemName = null, string cloudAccountId = null, string cloudRegion = null,
string serverAddress = null, int? serverPort = null, string routingKey = null)
{
#if DEBUG
Log.Finest("Skipping StartMessageBrokerSegment outside of a transaction");
Expand Down
18 changes: 12 additions & 6 deletions src/Agent/NewRelic/Agent/Core/Transactions/Transaction.cs
Original file line number Diff line number Diff line change
Expand Up @@ -243,7 +243,10 @@ public AbstractSegmentData CreateCustomSegmentData(string segmentName)
return new CustomSegmentData(segmentName);
}

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


var segment = StartSegmentImpl(methodCall);
var messageBrokerSegmentData = CreateMessageBrokerSegmentData(destinationType, operation, brokerVendorName, destinationName);
var messageBrokerSegmentData = CreateMessageBrokerSegmentData(destinationType, operation, brokerVendorName, destinationName, messagingSystemName, cloudAccountId, cloudRegion, serverAddress, serverPort, routingKey);

segment.SetSegmentData(messageBrokerSegmentData);

Expand Down Expand Up @@ -281,15 +284,18 @@ public ISegment StartMessageBrokerSerializationSegment(MethodCall methodCall, Me
return segment;
}

public AbstractSegmentData CreateMessageBrokerSegmentData(MessageBrokerDestinationType destinationType, MessageBrokerAction operation, string brokerVendorName, string destinationName)
public AbstractSegmentData CreateMessageBrokerSegmentData(MessageBrokerDestinationType destinationType,
MessageBrokerAction operation, string brokerVendorName, string destinationName,
string messagingSystemName = null, string cloudAccountId = null, string cloudRegion = null,
string serverAddress = null, int? serverPort = null, string routingKey = null)
{
if (brokerVendorName == null)
throw new ArgumentNullException("brokerVendorName");

var action = AgentWrapperApiEnumToMetricNamesEnum(operation);
var destType = AgentWrapperApiEnumToMetricNamesEnum(destinationType);

return new MessageBrokerSegmentData(brokerVendorName, destinationName, destType, action);
return new MessageBrokerSegmentData(brokerVendorName, destinationName, destType, action, messagingSystemName: messagingSystemName, cloudAccountId: cloudAccountId, cloudRegion: cloudRegion, serverAddress: serverAddress, serverPort: serverPort, routingKey: routingKey);
}

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

public void Release()
{
End(captureResponseTime: false);
End(false);
}

private void SetTransactionName(ITransactionName transactionName, TransactionNamePriority priority)
Expand Down Expand Up @@ -1364,7 +1370,7 @@ public void SetLlmTransaction(bool isLlmTransaction)
/// <param name="value">Value for attribute.</param>
public void AddLambdaAttribute(string name, object value)
{
if(string.IsNullOrWhiteSpace(name))
if (string.IsNullOrWhiteSpace(name))
{
Log.Debug($"AddLambdaAttribute - Unable to set Lambda value on transaction because the key is null/empty");
return;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@ public static void Initialize(string installPathExtensionsDirectory)
{ "TransportConfigLegacyWrapper", Path.Combine(_installPathExtensionsDirectory, "NewRelic.Providers.Wrapper.MassTransitLegacy.dll") },

// Kafka
{ "KafkaBuilderWrapper", Path.Combine(_installPathExtensionsDirectory, "NewRelic.Providers.Wrapper.Kafka.dll") },
{ "KafkaProducerWrapper", Path.Combine(_installPathExtensionsDirectory, "NewRelic.Providers.Wrapper.Kafka.dll") },
{ "KafkaSerializerWrapper", Path.Combine(_installPathExtensionsDirectory, "NewRelic.Providers.Wrapper.Kafka.dll") },
{ "KafkaConsumerWrapper", Path.Combine(_installPathExtensionsDirectory, "NewRelic.Providers.Wrapper.Kafka.dll") }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -87,9 +87,15 @@ public interface ITransaction
/// <param name="operation"></param>
/// <param name="brokerVendorName">Must not be null.</param>
/// <param name="destinationName">Can be null.</param>
/// <param name="messagingSystemName"></param>
/// <param name="cloudAccountId"></param>
/// <param name="cloudRegion"></param>
/// <exception cref="ArgumentNullException"></exception>
/// <returns>an opaque object that will be needed when you want to end the segment.</returns>
ISegment StartMessageBrokerSegment(MethodCall methodCall, MessageBrokerDestinationType destinationType, MessageBrokerAction operation, string brokerVendorName, string destinationName = null);
ISegment StartMessageBrokerSegment(MethodCall methodCall, MessageBrokerDestinationType destinationType,
MessageBrokerAction operation, string brokerVendorName, string destinationName = null,
string messagingSystemName = null, string cloudAccountId = null, string cloudRegion = null,
string serverAddress = null, int? serverPort = null, string routingKey = null);

/// <summary>
/// Creates a segment for serializing a key or value in a message brokering system..
Expand Down
Loading

0 comments on commit a033b81

Please sign in to comment.