Skip to content

Commit

Permalink
Merge pull request #326 from newrelic/develop
Browse files Browse the repository at this point in the history
Merging develop to create v3.13.0
  • Loading branch information
RichVanderwal authored Jun 2, 2021
2 parents 5a0eaeb + 11f7414 commit d083918
Show file tree
Hide file tree
Showing 14 changed files with 411 additions and 423 deletions.
6 changes: 3 additions & 3 deletions .github/workflows/ci.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -113,17 +113,17 @@ jobs:
extratesting: go get -u github.com/sirupsen/logrus@master
- go-version: 1.15.x
dirs: v3/integrations/nrawssdk-v1
extratesting: go get -u github.com/aws/aws-sdk-go@master
extratesting: go get -u github.com/aws/aws-sdk-go@main
- go-version: 1.15.x
dirs: v3/integrations/nrawssdk-v2
extratesting: go get -u github.com/aws/aws-sdk-go-v2@master
extratesting: go get -u github.com/aws/aws-sdk-go-v2@main
- go-version: 1.15.x
dirs: v3/integrations/nrecho-v3
# Test against the latest v3 Echo:
extratesting: go get -u github.com/labstack/echo@v3
- go-version: 1.15.x
dirs: v3/integrations/nrecho-v4
extratesting: go get -u github.com/labstack/echo/v4@master
extratesting: go get -u github.com/labstack/echo@master
- go-version: 1.15.x
dirs: v3/integrations/nrelasticsearch-v7
extratesting: go get -u github.com/elastic/go-elasticsearch/[email protected]
Expand Down
15 changes: 15 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,21 @@

## Unreleased

## 3.13.0

### Fixed
* Replaced the NR AWS SDK V2 integration for the v3 agent with a new version that works. See the v3/integrations/nrawssdk-v2/example/main.go file for an example of how to use it. Issues [#250](https://github.com/newrelic/go-agent/issues/250) and [#288](https://github.com/newrelic/go-agent/issues/288) are fixed by this PR. [#309](https://github.com/newrelic/go-agent/pull/309)

* Fixes issue [#221](https://github.com/newrelic/go-agent/issues/221): grpc errors reported in code watched by `UnaryServerInterceptor()` or `StreamServerInterceptor()` now create error events which are reported to the UI with the error message string included. [#317](https://github.com/newrelic/go-agent/pull/317)

* Fixes documentation in `GUIDE.md` for `txn.StartExternalSegment()` to reflect the v3 usage. Thanks to @abeltay for calling this to our attention and submitting PR [#320](https://github.com/newrelic/go-agent/pull/320).

### Changes
* The v3/examples/server/main.go example now uses `newrelic.ConfigFromEnvironment()`, rather than explicitly pulling in the license key with `newrelic.ConfigLicense(os.Getenv("NEW_RELIC_LICENSE_KEY"))`. The team is starting to use this as a general systems integration testing script, and this facilitates testing with different settings enabled.

### Support Statement
* New Relic recommends that you upgrade the agent regularly to ensure that you're getting the latest features and performance benefits. Additionally, older releases will no longer be supported when they reach [end-of-life](https://docs.newrelic.com/docs/using-new-relic/cross-product-functions/install-configure/notification-changes-new-relic-saas-features-distributed-software).

## 3.12.0

### Changes
Expand Down
4 changes: 2 additions & 2 deletions GUIDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -369,8 +369,8 @@ ways to use this functionality:
For example:

```go
func external(txn newrelic.Transaction, req *http.Request) (*http.Response, error) {
s := txn.StartExternalSegment(req)
func external(txn *newrelic.Transaction, req *http.Request) (*http.Response, error) {
s := newrelic.StartExternalSegment(txn, req)
response, err := http.DefaultClient.Do(req)
s.Response = response
s.End()
Expand Down
20 changes: 15 additions & 5 deletions build-script.sh
Original file line number Diff line number Diff line change
Expand Up @@ -38,12 +38,15 @@ for dir in $DIRS; do
else
# Only v3 code version 1.9+ needs GRPC dependencies
VERSION=$(go version)
V17="1.7"
V18="1.8"
V19="1.9"
if [[ "$VERSION" =~ .*"$V17".* || "$VERSION" =~ .*"$V18".* ]]; then
V1_7="1.7"
V1_8="1.8"
V1_9="1.9"
V1_10="1.10"
V1_11="1.11"
V1_12="1.12"
if [[ "$VERSION" =~ .*"$V1_7".* || "$VERSION" =~ .*"$V1_8".* ]]; then
echo "Not installing GRPC for old versions"
elif [[ "$VERSION" =~ .*"$V19" ]]; then
elif [[ "$VERSION" =~ .*"$V1_9" || "$VERSION" =~ .*"$V1_10" || "$VERSION" =~ .*"$V1_11" || "$VERSION" =~ .*"$V1_12" ]]; then
# install v3 dependencies that support this go version
set +e
go get -u google.golang.org/grpc # this go get will fail to build
Expand All @@ -52,6 +55,13 @@ for dir in $DIRS; do
git checkout v1.31.0
cd -

set +e
go get -u golang.org/x/net/http2 # this go get will fail to build
set -e
cd $GOPATH/src/golang.org/x/net/http2
git checkout 7fd8e65b642006927f6cec5cb4241df7f98a2210
cd -

go get -u github.com/golang/protobuf/protoc-gen-go
else
go get -u github.com/golang/protobuf/protoc-gen-go
Expand Down
2 changes: 1 addition & 1 deletion v3/examples/server/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -249,7 +249,7 @@ func browser(w http.ResponseWriter, r *http.Request) {
func main() {
app, err := newrelic.NewApplication(
newrelic.ConfigAppName("Example App"),
newrelic.ConfigLicense(os.Getenv("NEW_RELIC_LICENSE_KEY")),
newrelic.ConfigFromEnvironment(),
newrelic.ConfigDebugLogger(os.Stdout),
)
if nil != err {
Expand Down
67 changes: 67 additions & 0 deletions v3/integrations/nrawssdk-v2/example/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
package main

import (
"context"
"fmt"
"log"
"os"
"time"

"github.com/aws/aws-sdk-go-v2/aws"
"github.com/aws/aws-sdk-go-v2/config"
"github.com/aws/aws-sdk-go-v2/service/s3"
nraws "github.com/newrelic/go-agent/v3/integrations/nrawssdk-v2"
"github.com/newrelic/go-agent/v3/newrelic"
)

func main() {

// Create a New Relic application. This will look for your license key in an
// environment varluable called NEW_RELIC_LICENSE_KEY. This example turns on
// Distributed Tracing, but that's not required.
app, err := newrelic.NewApplication(
newrelic.ConfigFromEnvironment(),
newrelic.ConfigAppName("Example App"),
newrelic.ConfigInfoLogger(os.Stdout),
newrelic.ConfigDistributedTracerEnabled(true),
)
if nil != err {
fmt.Println(err)
os.Exit(1)
}

// For demo purposes only. Don't use the app.WaitForConnection call in
// production unless this is a very short-lived process and the caller
// doesn't block or exit if there's an error.
app.WaitForConnection(5 * time.Second)

// Start recording a New Relic transaction
txn := app.StartTransaction("My sample transaction")

ctx := context.Background()
awsConfig, err := config.LoadDefaultConfig(ctx)
if err != nil {
log.Fatal(err)
}

// Instrument all new AWS clients with New Relic
nraws.AppendMiddlewares(&awsConfig.APIOptions, txn)

s3Client := s3.NewFromConfig(awsConfig)
output, err := s3Client.ListBuckets(ctx, nil)
if err != nil {
log.Fatal(err)
}

for _, object := range output.Buckets {
log.Printf("Bucket name is %s\n", aws.ToString(object.Name))
}

// End the New Relic transaction
txn.End()

// Force all the harvests and shutdown. Like the app.WaitForConnection call
// above, this is for the purposes of this demo only and can be safely
// removed for longer-running processes.
app.Shutdown(10 * time.Second)
}
16 changes: 11 additions & 5 deletions v3/integrations/nrawssdk-v2/go.mod
Original file line number Diff line number Diff line change
@@ -1,12 +1,18 @@
module github.com/newrelic/go-agent/v3/integrations/nrawssdk-v2

// As of Dec 2019, the aws-sdk-go-v2 go.mod file uses 1.12:
// As of May 2021, the aws-sdk-go-v2 go.mod file uses 1.15:
// https://github.com/aws/aws-sdk-go-v2/blob/master/go.mod
go 1.12
go 1.15

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

require (
// v0.8.0 is the earliest aws-sdk-go-v2 version where
// dynamodb.DescribeTableRequest.Send takes a context.Context parameter.
github.com/aws/aws-sdk-go-v2 v0.8.0
github.com/aws/aws-sdk-go-v2 v1.4.0
github.com/aws/aws-sdk-go-v2/config v1.1.7
github.com/aws/aws-sdk-go-v2/service/dynamodb v1.2.3
github.com/aws/aws-sdk-go-v2/service/lambda v1.2.3
github.com/aws/aws-sdk-go-v2/service/s3 v1.6.0
github.com/aws/smithy-go v1.4.0
github.com/newrelic/go-agent/v3 v3.0.0
golang.org/x/tools v0.1.0 // indirect
)
184 changes: 110 additions & 74 deletions v3/integrations/nrawssdk-v2/nrawssdk.go
Original file line number Diff line number Diff line change
@@ -1,88 +1,124 @@
// Copyright 2020 New Relic Corporation. All rights reserved.
// SPDX-License-Identifier: Apache-2.0
// Copyright The OpenTelemetry Authors
//
// Licensed 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 nrawssdk instruments https://github.com/aws/aws-sdk-go-v2 requests.
// Package nrawssdk instruments requests made by the
// https://github.com/aws/aws-sdk-go-v2 library.
//
// For most operations, external segments and spans are automatically created
// for display in the New Relic UI on the External services section. For
// DynamoDB operations, datastore segements and spans are created and will be
// displayed on the Databases page. All operations will also be displayed on
// transaction traces and distributed traces.
//
// To use this integration, simply apply the AppendMiddlewares fuction to the apiOptions in
// your AWS Config object before performing any AWS operations. See
// example/main.go for a working sample.
package nrawssdk

import (
"github.com/aws/aws-sdk-go-v2/aws"
"github.com/newrelic/go-agent/v3/internal"
"github.com/newrelic/go-agent/v3/internal/awssupport"
)
"context"
"strconv"

func init() { internal.TrackUsage("integration", "library", "aws-sdk-go-v2") }
awsmiddle "github.com/aws/aws-sdk-go-v2/aws/middleware"
smithymiddle "github.com/aws/smithy-go/middleware"
smithyhttp "github.com/aws/smithy-go/transport/http"
"github.com/newrelic/go-agent/v3/internal/integrationsupport"
"github.com/newrelic/go-agent/v3/newrelic"
)

func startSegment(req *aws.Request) {
input := awssupport.StartSegmentInputs{
HTTPRequest: req.HTTPRequest,
ServiceName: req.Metadata.ServiceName,
Operation: req.Operation.Name,
Region: req.Metadata.SigningRegion,
Params: req.Params,
}
req.HTTPRequest = awssupport.StartSegment(input)
type nrMiddleware struct {
txn *newrelic.Transaction
}

func endSegment(req *aws.Request) {
ctx := req.HTTPRequest.Context()
awssupport.EndSegment(ctx, req.HTTPResponse.Header)
type endable interface{ End() }

// See https://aws.github.io/aws-sdk-go-v2/docs/middleware/ for a description of
// AWS SDK V2 middleware.
func (m nrMiddleware) deserializeMiddleware(stack *smithymiddle.Stack) error {
return stack.Deserialize.Add(smithymiddle.DeserializeMiddlewareFunc("NRDeserializeMiddleware", func(
ctx context.Context, in smithymiddle.DeserializeInput, next smithymiddle.DeserializeHandler) (
out smithymiddle.DeserializeOutput, metadata smithymiddle.Metadata, err error) {

smithyRequest := in.Request.(*smithyhttp.Request)

// The actual http.Request is inside the smithyhttp.Request
httpRequest := smithyRequest.Request
serviceName := awsmiddle.GetServiceID(ctx)
operation := awsmiddle.GetOperationName(ctx)
region := awsmiddle.GetRegion(ctx)

var segment endable
// Service name capitalization is different for v1 and v2.
if serviceName == "dynamodb" || serviceName == "DynamoDB" {
segment = &newrelic.DatastoreSegment{
Product: newrelic.DatastoreDynamoDB,
Collection: "", // AWS SDK V2 doesn't expose TableName
Operation: operation,
ParameterizedQuery: "",
QueryParameters: nil,
Host: httpRequest.URL.Host,
PortPathOrID: httpRequest.URL.Port(),
DatabaseName: "",
StartTime: m.txn.StartSegmentNow(),
}
} else {
segment = newrelic.StartExternalSegment(m.txn, httpRequest)
}

// Hand off execution to other middlewares and then perform the request
out, metadata, err = next.HandleDeserialize(ctx, in)

// After the request
response, ok := out.RawResponse.(*smithyhttp.Response)

if ok {
// Set additional span attributes
integrationsupport.AddAgentSpanAttribute(m.txn,
newrelic.AttributeResponseCode, strconv.Itoa(response.StatusCode))
integrationsupport.AddAgentSpanAttribute(m.txn,
newrelic.SpanAttributeAWSOperation, operation)
integrationsupport.AddAgentSpanAttribute(m.txn,
newrelic.SpanAttributeAWSRegion, region)
requestID, ok := awsmiddle.GetRequestIDMetadata(metadata)
if ok {
integrationsupport.AddAgentSpanAttribute(m.txn,
newrelic.AttributeAWSRequestID, requestID)
}
}
segment.End()
return out, metadata, err
}),
smithymiddle.Before)
}

// InstrumentHandlers will add instrumentation to the given *aws.Handlers.
//
// A Segment will be created for each out going request. The Transaction must
// be added to the `http.Request`'s Context in order for the segment to be
// recorded. For DynamoDB calls, these segments will be
// `newrelic.DatastoreSegment` type and for all others they will be
// `newrelic.ExternalSegment` type.
//
// Additional attributes will be added to Transaction Trace Segments and Span
// Events: aws.region, aws.requestId, and aws.operation.
//
// To add instrumentation to a Config and see segments created for each
// invocation that uses that Config, call InstrumentHandlers with the config's
// Handlers and add the current Transaction to the `http.Request`'s Context:
//
// cfg, _ := external.LoadDefaultAWSConfig()
// cfg.Region = "us-west-2"
// // Add instrumentation to handlers
// nrawssdk.InstrumentHandlers(&cfg.Handlers)
// lambdaClient = lambda.New(cfg)
// AppendMiddlewares inserts New Relic middleware in the given `apiOptions` for
// the AWS SDK V2 for Go. It must be called only once per AWS configuration.
//
// req := lambdaClient.InvokeRequest(&lambda.InvokeInput{
// ClientContext: aws.String("MyApp"),
// FunctionName: aws.String("Function"),
// InvocationType: lambda.InvocationTypeEvent,
// LogType: lambda.LogTypeTail,
// Payload: []byte("{}"),
// }
// // Add txn to http.Request's context
// ctx := newrelic.NewContext(req.Context(), txn)
// resp, err := req.Send(ctx)
// Additional attributes will be added to transaction trace segments and span
// events: aws.region, aws.requestId, and aws.operation. In addition,
// http.statusCode will be added to span events.
//
// To add instrumentation to a Request and see a segment created just for the
// individual request, call InstrumentHandlers with the `aws.Request`'s
// Handlers and add the current Transaction to the `http.Request`'s Context:
// To see segments and spans for each AWS invocation, call AppendMiddlewares
// with the AWS Config `apiOptions` and pass in your current New Relic
// transaction. For example:
//
// req := lambdaClient.InvokeRequest(&lambda.InvokeInput{
// ClientContext: aws.String("MyApp"),
// FunctionName: aws.String("Function"),
// InvocationType: lambda.InvocationTypeEvent,
// LogType: lambda.LogTypeTail,
// Payload: []byte("{}"),
// }
// // Add instrumentation to handlers
// nrawssdk.InstrumentHandlers(&req.Handlers)
// // Add txn to http.Request's context
// ctx := newrelic.NewContext(req.Context(), txn)
// resp, err := req.Send(ctx)
func InstrumentHandlers(handlers *aws.Handlers) {
handlers.Send.SetFrontNamed(aws.NamedHandler{
Name: "StartNewRelicSegment",
Fn: startSegment,
})
handlers.Send.SetBackNamed(aws.NamedHandler{
Name: "EndNewRelicSegment",
Fn: endSegment,
})
// awsConfig, err := config.LoadDefaultConfig(ctx)
// if err != nil {
// log.Fatal(err)
// }
// nraws.AppendMiddlewares(ctx, &awsConfig.APIOptions, txn)
func AppendMiddlewares(apiOptions *[]func(*smithymiddle.Stack) error, txn *newrelic.Transaction) {
m := nrMiddleware{txn: txn}
*apiOptions = append(*apiOptions, m.deserializeMiddleware)
}
Loading

0 comments on commit d083918

Please sign in to comment.