Skip to content

Commit ead5d74

Browse files
Merge pull request #952 from mirackara/cloudentities
Cloud Services Entity Relationship Changes
2 parents 65528e1 + 7543b05 commit ead5d74

File tree

9 files changed

+551
-52
lines changed

9 files changed

+551
-52
lines changed

v3/integrations/nramqp/examples/consumer/main.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,8 @@ func main() {
3232

3333
nrApp.WaitForConnection(time.Second * 5)
3434

35-
conn, err := amqp.Dial("amqp://guest:guest@localhost:5672/")
35+
amqpURL := "amqp://guest:guest@localhost:5672/"
36+
conn, err := amqp.Dial(amqpURL)
3637
failOnError(err, "Failed to connect to RabbitMQ")
3738
defer conn.Close()
3839

v3/integrations/nramqp/examples/publisher/main.go

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -40,13 +40,15 @@ type amqpServer struct {
4040
ch *amqp.Channel
4141
exchange string
4242
routingKey string
43+
url string
4344
}
4445

45-
func NewServer(channel *amqp.Channel, exchangeName, routingKeyName string) *amqpServer {
46+
func NewServer(channel *amqp.Channel, exchangeName, routingKeyName string, url string) *amqpServer {
4647
return &amqpServer{
4748
channel,
4849
exchangeName,
4950
routingKeyName,
51+
url,
5052
}
5153
}
5254

@@ -65,6 +67,7 @@ func (serv *amqpServer) publishPlainTxtMessage(w http.ResponseWriter, r *http.Re
6567
ctx,
6668
serv.exchange, // exchange
6769
serv.routingKey, // routing key
70+
serv.url, // url
6871
false, // mandatory
6972
false, // immediate
7073
amqp.Publishing{
@@ -94,7 +97,8 @@ func main() {
9497

9598
nrApp.WaitForConnection(time.Second * 5)
9699

97-
conn, err := amqp.Dial("amqp://guest:guest@localhost:5672/")
100+
amqpURL := "amqp://guest:guest@localhost:5672/"
101+
conn, err := amqp.Dial(amqpURL)
98102
failOnError(err, "Failed to connect to RabbitMQ")
99103
defer conn.Close()
100104

@@ -112,7 +116,7 @@ func main() {
112116
)
113117
failOnError(err, "Failed to declare a queue")
114118

115-
server := NewServer(ch, "", q.Name)
119+
server := NewServer(ch, "", q.Name, amqpURL)
116120

117121
http.HandleFunc(newrelic.WrapHandleFunc(nrApp, "/", server.index))
118122
http.HandleFunc(newrelic.WrapHandleFunc(nrApp, "/message", server.publishPlainTxtMessage))

v3/integrations/nramqp/nramqp.go

Lines changed: 35 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package nramqp
22

33
import (
44
"context"
5+
"strings"
56

67
amqp "github.com/rabbitmq/amqp091-go"
78

@@ -16,7 +17,7 @@ const (
1617

1718
func init() { internal.TrackUsage("integration", "messagebroker", "nramqp") }
1819

19-
func creatProducerSegment(exchange, key string) *newrelic.MessageProducerSegment {
20+
func createProducerSegment(exchange, key string) *newrelic.MessageProducerSegment {
2021
s := newrelic.MessageProducerSegment{
2122
Library: RabbitMQLibrary,
2223
DestinationName: "Default",
@@ -33,13 +34,34 @@ func creatProducerSegment(exchange, key string) *newrelic.MessageProducerSegment
3334
return &s
3435
}
3536

37+
func GetHostAndPortFromURL(url string) (string, string) {
38+
// url is of format amqp://user:password@host:port or amqp://host:port
39+
var hostPortPart string
40+
41+
// extract the part after "@" symbol, if present
42+
if parts := strings.Split(url, "@"); len(parts) == 2 {
43+
hostPortPart = parts[1]
44+
} else {
45+
// assume the whole url after "amqp://" is the host:port part
46+
hostPortPart = strings.TrimPrefix(url, "amqp://")
47+
}
48+
49+
// split the host:port part
50+
strippedURL := strings.Split(hostPortPart, ":")
51+
if len(strippedURL) != 2 {
52+
return "", ""
53+
}
54+
return strippedURL[0], strippedURL[1]
55+
}
56+
3657
// PublishedWithContext looks for a newrelic transaction in the context object, and if found, creates a message producer segment.
3758
// It will also inject distributed tracing headers into the message.
38-
func PublishWithContext(ch *amqp.Channel, ctx context.Context, exchange, key string, mandatory, immediate bool, msg amqp.Publishing) error {
59+
func PublishWithContext(ch *amqp.Channel, ctx context.Context, exchange, key, url string, mandatory, immediate bool, msg amqp.Publishing) error {
60+
host, port := GetHostAndPortFromURL(url)
3961
txn := newrelic.FromContext(ctx)
4062
if txn != nil {
4163
// generate message broker segment
42-
s := creatProducerSegment(exchange, key)
64+
s := createProducerSegment(exchange, key)
4365

4466
// capture telemetry for AMQP producer
4567
if msg.Headers != nil && len(msg.Headers) > 0 {
@@ -49,15 +71,18 @@ func PublishWithContext(ch *amqp.Channel, ctx context.Context, exchange, key str
4971
}
5072
integrationsupport.AddAgentSpanAttribute(txn, newrelic.AttributeMessageHeaders, hdrStr)
5173
}
74+
s.StartTime = txn.StartSegmentNow()
5275

76+
// inject DT headers into headers object
77+
msg.Headers = injectDtHeaders(txn, msg.Headers)
78+
integrationsupport.AddAgentSpanAttribute(txn, newrelic.AttributeSpanKind, "producer")
79+
integrationsupport.AddAgentSpanAttribute(txn, newrelic.AttributeServerAddress, host)
80+
integrationsupport.AddAgentSpanAttribute(txn, newrelic.AttributeServerPort, port)
81+
integrationsupport.AddAgentSpanAttribute(txn, newrelic.AttributeMessageDestinationName, exchange)
5382
integrationsupport.AddAgentSpanAttribute(txn, newrelic.AttributeMessageRoutingKey, key)
5483
integrationsupport.AddAgentSpanAttribute(txn, newrelic.AttributeMessageCorrelationID, msg.CorrelationId)
5584
integrationsupport.AddAgentSpanAttribute(txn, newrelic.AttributeMessageReplyTo, msg.ReplyTo)
5685

57-
// inject DT headers into headers object
58-
msg.Headers = injectDtHeaders(txn, msg.Headers)
59-
60-
s.StartTime = txn.StartSegmentNow()
6186
err := ch.PublishWithContext(ctx, exchange, key, mandatory, immediate, msg)
6287
s.End()
6388
return err
@@ -91,8 +116,10 @@ func Consume(app *newrelic.Application, ch *amqp.Channel, queue, consumer string
91116
integrationsupport.AddAgentAttribute(txn, newrelic.AttributeMessageHeaders, hdrStr, nil)
92117
}
93118
}
94-
119+
integrationsupport.AddAgentAttribute(txn, newrelic.AttributeSpanKind, "consumer", nil)
95120
integrationsupport.AddAgentAttribute(txn, newrelic.AttributeMessageQueueName, queue, nil)
121+
integrationsupport.AddAgentAttribute(txn, newrelic.AttributeMessageDestinationName, queue, nil)
122+
integrationsupport.AddAgentAttribute(txn, newrelic.AttributeMessagingDestinationPublishName, delivery.Exchange, nil)
96123
integrationsupport.AddAgentAttribute(txn, newrelic.AttributeMessageRoutingKey, delivery.RoutingKey, nil)
97124
integrationsupport.AddAgentAttribute(txn, newrelic.AttributeMessageCorrelationID, delivery.CorrelationId, nil)
98125
integrationsupport.AddAgentAttribute(txn, newrelic.AttributeMessageReplyTo, delivery.ReplyTo, nil)

v3/integrations/nramqp/nramqp_test.go

Lines changed: 54 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ func BenchmarkCreateProducerSegment(b *testing.B) {
1515
b.ReportAllocs()
1616

1717
for i := 0; i < b.N; i++ {
18-
creatProducerSegment("exchange", "key")
18+
createProducerSegment("exchange", "key")
1919
}
2020
}
2121

@@ -66,7 +66,7 @@ func TestCreateProducerSegment(t *testing.T) {
6666
}
6767

6868
for _, test := range tests {
69-
s := creatProducerSegment(test.exchange, test.key)
69+
s := createProducerSegment(test.exchange, test.key)
7070
if s.DestinationName != test.expect.DestinationName {
7171
t.Errorf("expected destination name %s, got %s", test.expect.DestinationName, s.DestinationName)
7272
}
@@ -76,3 +76,55 @@ func TestCreateProducerSegment(t *testing.T) {
7676
}
7777

7878
}
79+
80+
func TestHostAndPortParsing(t *testing.T) {
81+
app := createTestApp()
82+
txn := app.StartTransaction("test")
83+
defer txn.End()
84+
85+
type testObject struct {
86+
url string
87+
expectHost string
88+
expectPort string
89+
}
90+
91+
tests := []testObject{
92+
{
93+
"amqp://user:password@host:port",
94+
"host",
95+
"port",
96+
},
97+
{
98+
"amqp://host:port",
99+
"host",
100+
"port",
101+
},
102+
{
103+
"aaa://host:port",
104+
"",
105+
"",
106+
},
107+
108+
{
109+
"amqp://user:password@host",
110+
"",
111+
"",
112+
},
113+
{
114+
"amqp://user:password@host:port:extra",
115+
"",
116+
"",
117+
},
118+
}
119+
120+
for _, test := range tests {
121+
host, port := GetHostAndPortFromURL(test.url)
122+
if host != test.expectHost {
123+
t.Errorf("expected host %s, got %s", test.expectHost, host)
124+
}
125+
if port != test.expectPort {
126+
t.Errorf("expected port %s, got %s", test.expectPort, port)
127+
}
128+
}
129+
130+
}

v3/integrations/nrawssdk-v2/go.mod

Lines changed: 35 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,17 +2,45 @@ module github.com/newrelic/go-agent/v3/integrations/nrawssdk-v2
22

33
// As of May 2021, the aws-sdk-go-v2 go.mod file uses 1.15:
44
// https://github.com/aws/aws-sdk-go-v2/blob/master/go.mod
5-
go 1.20
5+
go 1.21
6+
7+
toolchain go1.21.0
68

79
require (
8-
github.com/aws/aws-sdk-go-v2 v1.16.15
9-
github.com/aws/aws-sdk-go-v2/config v1.17.6
10-
github.com/aws/aws-sdk-go-v2/service/dynamodb v1.17.0
11-
github.com/aws/aws-sdk-go-v2/service/lambda v1.24.5
12-
github.com/aws/aws-sdk-go-v2/service/s3 v1.27.10
13-
github.com/aws/smithy-go v1.13.3
10+
github.com/aws/aws-sdk-go-v2 v1.30.4
11+
github.com/aws/aws-sdk-go-v2/config v1.27.31
12+
github.com/aws/aws-sdk-go-v2/service/dynamodb v1.34.6
13+
github.com/aws/aws-sdk-go-v2/service/lambda v1.58.1
14+
github.com/aws/aws-sdk-go-v2/service/s3 v1.61.0
15+
github.com/aws/aws-sdk-go-v2/service/sqs v1.34.6
16+
github.com/aws/smithy-go v1.20.4
1417
github.com/newrelic/go-agent/v3 v3.33.1
1518
)
1619

20+
require (
21+
github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.6.4 // indirect
22+
github.com/aws/aws-sdk-go-v2/credentials v1.17.30 // indirect
23+
github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.12 // indirect
24+
github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.16 // indirect
25+
github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.16 // indirect
26+
github.com/aws/aws-sdk-go-v2/internal/ini v1.8.1 // indirect
27+
github.com/aws/aws-sdk-go-v2/internal/v4a v1.3.16 // indirect
28+
github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.11.4 // indirect
29+
github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.3.18 // indirect
30+
github.com/aws/aws-sdk-go-v2/service/internal/endpoint-discovery v1.9.17 // indirect
31+
github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.11.18 // indirect
32+
github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.17.16 // indirect
33+
github.com/aws/aws-sdk-go-v2/service/sso v1.22.5 // indirect
34+
github.com/aws/aws-sdk-go-v2/service/ssooidc v1.26.5 // indirect
35+
github.com/aws/aws-sdk-go-v2/service/sts v1.30.5 // indirect
36+
github.com/golang/protobuf v1.5.3 // indirect
37+
github.com/jmespath/go-jmespath v0.4.0 // indirect
38+
golang.org/x/net v0.9.0 // indirect
39+
golang.org/x/sys v0.7.0 // indirect
40+
golang.org/x/text v0.9.0 // indirect
41+
google.golang.org/genproto v0.0.0-20230410155749-daa745c078e1 // indirect
42+
google.golang.org/grpc v1.56.3 // indirect
43+
google.golang.org/protobuf v1.30.0 // indirect
44+
)
1745

1846
replace github.com/newrelic/go-agent/v3 => ../..

v3/integrations/nrawssdk-v2/nrawssdk.go

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,9 +28,14 @@ package nrawssdk
2828

2929
import (
3030
"context"
31+
"net/url"
3132
"strconv"
33+
"strings"
3234

35+
"github.com/aws/aws-sdk-go-v2/aws"
3336
awsmiddle "github.com/aws/aws-sdk-go-v2/aws/middleware"
37+
"github.com/aws/aws-sdk-go-v2/service/sqs"
38+
"github.com/aws/smithy-go/middleware"
3439
smithymiddle "github.com/aws/smithy-go/middleware"
3540
smithyhttp "github.com/aws/smithy-go/transport/http"
3641
"github.com/newrelic/go-agent/v3/internal/integrationsupport"
@@ -41,6 +46,11 @@ type nrMiddleware struct {
4146
txn *newrelic.Transaction
4247
}
4348

49+
// Context key for SQS service queue
50+
type contextKey string
51+
52+
const queueURLKey contextKey = "QueueURL"
53+
4454
type endable interface{ End() }
4555

4656
// See https://aws.github.io/aws-sdk-go-v2/docs/middleware/ for a description of
@@ -88,6 +98,24 @@ func (m nrMiddleware) deserializeMiddleware(stack *smithymiddle.Stack) error {
8898
response, ok := out.RawResponse.(*smithyhttp.Response)
8999

90100
if ok {
101+
if serviceName == "sqs" || serviceName == "SQS" {
102+
if queueURL, ok := ctx.Value(queueURLKey).(string); ok {
103+
parsedURL, err := url.Parse(queueURL)
104+
if err == nil {
105+
// Example URL: https://sqs.{region}.amazonaws.com/{account.id}/{queue.name}
106+
pathParts := strings.Split(parsedURL.Path, "/")
107+
if len(pathParts) >= 3 {
108+
accountID := pathParts[1]
109+
queueName := pathParts[2]
110+
integrationsupport.AddAgentSpanAttribute(txn, newrelic.AttributeCloudAccountID, accountID)
111+
integrationsupport.AddAgentSpanAttribute(txn, newrelic.AttributeCloudRegion, region)
112+
integrationsupport.AddAgentSpanAttribute(txn, newrelic.AttributeMessageSystem, "aws_sqs")
113+
integrationsupport.AddAgentSpanAttribute(txn, newrelic.AttributeMessageDestinationName, queueName)
114+
}
115+
}
116+
117+
}
118+
}
91119
// Set additional span attributes
92120
integrationsupport.AddAgentSpanAttribute(txn,
93121
newrelic.AttributeResponseCode, strconv.Itoa(response.StatusCode))
@@ -107,6 +135,51 @@ func (m nrMiddleware) deserializeMiddleware(stack *smithymiddle.Stack) error {
107135
smithymiddle.Before)
108136
}
109137

138+
func (m nrMiddleware) serializeMiddleware(stack *middleware.Stack) error {
139+
return stack.Initialize.Add(middleware.InitializeMiddlewareFunc("NRSerializeMiddleware", func(
140+
ctx context.Context, in middleware.InitializeInput, next middleware.InitializeHandler) (
141+
out middleware.InitializeOutput, metadata middleware.Metadata, err error) {
142+
143+
serviceName := awsmiddle.GetServiceID(ctx)
144+
if serviceName == "sqs" || serviceName == "SQS" {
145+
QueueURL := ""
146+
switch params := in.Parameters.(type) {
147+
case *sqs.SendMessageInput:
148+
QueueURL = aws.ToString(params.QueueUrl)
149+
case *sqs.DeleteQueueInput:
150+
QueueURL = aws.ToString(params.QueueUrl)
151+
case *sqs.ReceiveMessageInput:
152+
QueueURL = aws.ToString(params.QueueUrl)
153+
case *sqs.DeleteMessageInput:
154+
QueueURL = aws.ToString(params.QueueUrl)
155+
case *sqs.ChangeMessageVisibilityInput:
156+
QueueURL = aws.ToString(params.QueueUrl)
157+
case *sqs.ChangeMessageVisibilityBatchInput:
158+
QueueURL = aws.ToString(params.QueueUrl)
159+
case *sqs.DeleteMessageBatchInput:
160+
QueueURL = aws.ToString(params.QueueUrl)
161+
case *sqs.SendMessageBatchInput:
162+
QueueURL = aws.ToString(params.QueueUrl)
163+
case *sqs.PurgeQueueInput:
164+
QueueURL = aws.ToString(params.QueueUrl)
165+
case *sqs.GetQueueAttributesInput:
166+
QueueURL = aws.ToString(params.QueueUrl)
167+
case *sqs.SetQueueAttributesInput:
168+
QueueURL = aws.ToString(params.QueueUrl)
169+
case *sqs.TagQueueInput:
170+
QueueURL = aws.ToString(params.QueueUrl)
171+
case *sqs.UntagQueueInput:
172+
QueueURL = aws.ToString(params.QueueUrl)
173+
default:
174+
QueueURL = ""
175+
}
176+
// Store the QueueURL in the context
177+
ctx = context.WithValue(ctx, queueURLKey, QueueURL)
178+
}
179+
return next.HandleInitialize(ctx, in)
180+
}), middleware.After)
181+
}
182+
110183
// AppendMiddlewares inserts New Relic middleware in the given `apiOptions` for
111184
// the AWS SDK V2 for Go. It must be called only once per AWS configuration.
112185
//
@@ -167,4 +240,6 @@ func (m nrMiddleware) deserializeMiddleware(stack *smithymiddle.Stack) error {
167240
func AppendMiddlewares(apiOptions *[]func(*smithymiddle.Stack) error, txn *newrelic.Transaction) {
168241
m := nrMiddleware{txn: txn}
169242
*apiOptions = append(*apiOptions, m.deserializeMiddleware)
243+
*apiOptions = append(*apiOptions, m.serializeMiddleware)
244+
170245
}

0 commit comments

Comments
 (0)