module/apmawssdkgo/sns.go (100 lines of code) (raw):

// Licensed to Elasticsearch B.V. under one or more contributor // license agreements. See the NOTICE file distributed with // this work for additional information regarding copyright // ownership. Elasticsearch B.V. licenses this file to you under // the Apache License, Version 2.0 (the "License"); you may // not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, // software distributed under the License is distributed on an // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY // KIND, either express or implied. See the License for the // specific language governing permissions and limitations // under the License. package apmawssdkgo // import "go.elastic.co/apm/module/apmawssdkgo/v2" import ( "strings" "go.elastic.co/apm/module/apmhttp/v2" "go.elastic.co/apm/v2" "github.com/aws/aws-sdk-go/aws" "github.com/aws/aws-sdk-go/aws/request" "github.com/aws/aws-sdk-go/service/sns" ) type apmSNS struct { name, opName, resourceName, topicName string } func newSNS(req *request.Request) (*apmSNS, error) { if req.Operation.Name != "Publish" { return nil, errMethodNotSupported } name := req.ClientInfo.ServiceID + " PUBLISH" resourceName := serviceSNS topicName := getTopicName(req) if topicName != "" { name += " to " + topicName resourceName += "/" + topicName } s := &apmSNS{ name: name, opName: "publish", resourceName: resourceName, topicName: topicName, } return s, nil } func (s *apmSNS) spanName() string { return s.name } func (s *apmSNS) resource() string { return s.resourceName } func (s *apmSNS) targetName() string { return s.topicName } func (s *apmSNS) setAdditional(span *apm.Span) { span.Action = s.opName // According to the spec: // Wherever the broker terminology uses "topic", this field will // contain the topic name. if s.topicName != "" { span.Context.SetMessage(apm.MessageSpanContext{ QueueName: s.topicName, }) } } func getTopicName(req *request.Request) string { // format: arn:aws:sns:us-east-2:123456789012:My-Topic // should return My-Topic if topicArn := req.HTTPRequest.FormValue("TopicArn"); topicArn != "" { idx := strings.LastIndex(topicArn, ":") if idx == -1 { return "" } // special check for format: arn:aws:sns:us-east-2:123456789012/MyTopic if slashIdx := strings.LastIndex(topicArn, "/"); slashIdx != -1 { return topicArn[slashIdx+1:] } return topicArn[idx+1:] } // format: arn:aws:sns:us-west-2:123456789012:endpoint/GCM/gcmpushapp/5e3e9847-3183-3f18-a7e8-671c3a57d4b3 // should return endpoint/GCM/gcmpushapp if targetArn := req.HTTPRequest.FormValue("TargetArn"); targetArn != "" { idx := strings.LastIndex(targetArn, ":") if idx == -1 { return "" } endIdx := strings.LastIndex(targetArn, "/") if endIdx == -1 { return "" } return targetArn[idx+1 : endIdx] } // The actual phone number MUST NOT be included because it is PII and cardinality is too high. if phoneNumber := req.HTTPRequest.FormValue("PhoneNumber"); phoneNumber != "" { return "[PHONENUMBER]" } return "" } // addMessageAttributesSNS adds message attributes to `Publish` RPC calls. // Other SNS RPC calls are ignored. func addMessageAttributesSNS(req *request.Request, span *apm.Span, propagateLegacyHeader bool) { if req.Operation.Name != "Publish" { return } traceContext := span.TraceContext() msgAttr := &sns.MessageAttributeValue{ DataType: aws.String("String"), StringValue: aws.String(apmhttp.FormatTraceparentHeader(traceContext)), } tracestate := traceContext.State.String() input, ok := req.Params.(*sns.PublishInput) if !ok { return } if input.MessageAttributes == nil { input.MessageAttributes = make(map[string]*sns.MessageAttributeValue) } input.MessageAttributes[apmhttp.W3CTraceparentHeader] = msgAttr if propagateLegacyHeader { input.MessageAttributes[apmhttp.ElasticTraceparentHeader] = msgAttr } if tracestate != "" { input.MessageAttributes[apmhttp.TracestateHeader] = &sns.MessageAttributeValue{ DataType: aws.String("String"), StringValue: aws.String(tracestate), } } } func supportedSNSMethod(req *request.Request) bool { return req.Operation.Name == "Publish" }