Skip to content
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

AWS-APM Entity Relatipnships: SQS #2635

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,11 @@ public interface IAttributeDefinitions
AttributeDefinition<TypeAttributeValue, string> GetTypeAttribute(TypeAttributeValue destination);

AttributeDefinition<bool, bool> LlmTransaction { get; }

public AttributeDefinition<string, string> CloudAccountId { get; }
public AttributeDefinition<string, string> CloudRegion { get; }
public AttributeDefinition<string, string> MessagingSystemName { get; }
AttributeDefinition<string, string> MessagingDestinationName { get; }
}


Expand Down Expand Up @@ -177,7 +182,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 +1084,33 @@ 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));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -24,15 +24,26 @@ public class MessageBrokerSegmentData : AbstractSegmentData

public MetricNames.MessageBrokerAction Action { get; set; }

public string MessagingSystemName {get; set;}
public string CloudAccountId {get; set;}
public string CloudRegion {get; set;}

public MessageBrokerSegmentData(string vendor, string destination, MetricNames.MessageBrokerDestinationType destinationType, MetricNames.MessageBrokerAction action)


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

// attributes required for entity relationship mapping
MessagingSystemName = messagingSystemName;
CloudAccountId = cloudAccountId;
CloudRegion = cloudRegion;
}


public override bool IsCombinableWith(AbstractSegmentData otherData)
{
var otherTypedSegment = otherData as MessageBrokerSegmentData;
Expand All @@ -51,6 +62,15 @@ 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;

return true;
}

Expand All @@ -65,7 +85,16 @@ 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);

AttribDefs.MessagingSystemName.TrySetValue(attribVals, MessagingSystemName);
AttribDefs.MessagingDestinationName.TrySetValue(attribVals, Destination);
AttribDefs.CloudRegion.TrySetValue(attribVals, CloudRegion);
AttribDefs.CloudAccountId.TrySetValue(attribVals, CloudAccountId);
}
}
}
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)
{
#if DEBUG
Log.Finest("Skipping StartMessageBrokerSegment outside of a transaction");
Expand Down
15 changes: 9 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)
{
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);

segment.SetSegmentData(messageBrokerSegmentData);

Expand Down Expand Up @@ -281,15 +284,15 @@ 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)
{
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);
}

public AbstractSegmentData CreateMessageBrokerSerializationSegmentData(MessageBrokerDestinationType destinationType, MessageBrokerAction operation, string brokerVendorName, string destinationName, string kind)
Expand Down Expand Up @@ -784,7 +787,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 +1367,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 @@ -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);

/// <summary>
/// Creates a segment for serializing a key or value in a message brokering system..
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,15 @@ public static class SqsHelper
private static ConcurrentDictionary<Type, Func<object, IDictionary>> _getMessageAttributes = new();
private static Func<object> _messageAttributeValueTypeFactory;

public const string MessagingSystemName = "aws_sqs";
public const string VendorName = "SQS";

private class SqsAttributes
{
public string QueueName { get; }
public string CloudId { get; }
public string Region { get; }
public string ServerAddress { get; }

// https://sqs.us-east-2.amazonaws.com/123456789012/MyQueue
public SqsAttributes(string url)
Expand All @@ -51,12 +53,16 @@ public SqsAttributes(string url)

// subdomain[0] should always be "sqs"
Region = subdomain[1];

ServerAddress = new Uri(url).Host;
}
}

public static ISegment GenerateSegment(ITransaction transaction, MethodCall methodCall, string url, MessageBrokerAction action)
{
var attr = new SqsAttributes(url);
var segment = transaction.StartMessageBrokerSegment(methodCall, MessageBrokerDestinationType.Queue, action, VendorName, attr.QueueName);

var segment = transaction.StartMessageBrokerSegment(methodCall, MessageBrokerDestinationType.Queue, action, VendorName, destinationName: attr.QueueName, messagingSystemName: MessagingSystemName, cloudAccountId: attr.CloudId, cloudRegion: attr.Region);
segment.GetExperimentalApi().MakeLeaf();

return segment;
Expand All @@ -71,7 +77,7 @@ public static void InsertDistributedTraceHeaders(ITransaction transaction, objec

var setHeaders = new Action<object, string, string>((smr, key, value) =>
{
var getMessageAttributes = _getMessageAttributes.GetOrAdd(smr.GetType(), t => VisibilityBypasser.Instance.GeneratePropertyAccessor<IDictionary>(t, "MessageAttributes"));
var getMessageAttributes = _getMessageAttributes.GetOrAdd(smr.GetType(), t => VisibilityBypasser.Instance.GeneratePropertyAccessor<IDictionary>(t, "MessageAttributes"));
var messageAttributes = getMessageAttributes(smr);

// if we can't add all DT headers, don't add any
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
using Xunit;
using Xunit.Abstractions;

namespace NewRelic.Agent.ContainerIntegrationTests.Tests;
namespace NewRelic.Agent.ContainerIntegrationTests.Tests.AwsSdk;

public class AwsSdkSQSTest : NewRelicIntegrationTest<AwsSdkContainerSQSTestFixture>
{
Expand Down Expand Up @@ -120,7 +120,16 @@ public void Test()
() => Assert.True(consumeSpan!.IntrinsicAttributes.ContainsKey("traceId")),
() => Assert.True(processRequestSpan!.IntrinsicAttributes.ContainsKey("parentId")),
() => Assert.Equal(produceSpan!.IntrinsicAttributes["traceId"], consumeSpan!.IntrinsicAttributes["traceId"]),
() => Assert.Equal(produceSpan!.IntrinsicAttributes["guid"], processRequestSpan!.IntrinsicAttributes["parentId"])
() => Assert.Equal(produceSpan!.IntrinsicAttributes["guid"], processRequestSpan!.IntrinsicAttributes["parentId"]),
// entity relationship attributes
() => Assert.Equal(produceSpan!.AgentAttributes["messaging.system"], "aws_sqs"),
() => Assert.Equal(produceSpan!.AgentAttributes["messaging.destination.name"], _testQueueName2),
() => Assert.Equal(consumeSpan!.AgentAttributes["cloud.account.id"], "000000000000"),
() => Assert.Equal(consumeSpan!.AgentAttributes["cloud.region"], "us-west-2"),
() => Assert.Equal(consumeSpan!.AgentAttributes["messaging.system"], "aws_sqs"),
() => Assert.Equal(consumeSpan!.AgentAttributes["messaging.destination.name"], _testQueueName2),
() => Assert.Equal(consumeSpan!.AgentAttributes["cloud.account.id"], "000000000000"),
() => Assert.Equal(consumeSpan!.AgentAttributes["cloud.region"], "us-west-2")
);
}
}
Loading
Loading