Skip to content

Commit

Permalink
Merge pull request #349 from newrelic/develop
Browse files Browse the repository at this point in the history
Release 3.14.1
  • Loading branch information
nr-swilloughby committed Jul 20, 2021
2 parents d628ce8 + 98cd98a commit 88a31e1
Show file tree
Hide file tree
Showing 5 changed files with 64 additions and 45 deletions.
21 changes: 15 additions & 6 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,25 +1,34 @@
# ChangeLog

## Unreleased
## 3.14.1

### Fixed
* A typographical error in the nrgrpc unit tests was fixed. Fixes [Issue #344](https://github.com/newrelic/go-agent/issues/344).
This updates the nrgrpc integration to version 1.3.1.

### 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.


## 3.14.0

## Fixed
### Fixed
* Integration tags and `go.mod` files for integrations were updated so that [pkg.go.dev]() displays the documentation for each integration correctly.
* The `nrgrpc` server integration was reporting all non-`OK` grpc statuses as errors. This has now been changed so that only selected grpc status codes will be reported as errors. Others are shown (via transaction attributes) as "warnings" or "informational" messages. There is a built-in set of defaults as to which status codes are reported at which severity levels, but this may be overridden by the caller as desired. Also supports custom grpc error handling functions supplied by the user.
* This is implemented by adding `WithStatusHandler()` options to the end of the `UnaryServerInterceptor()` and `StreamServerInterceptor()` calls, thus extending the capability of those functions while retaining the existing functionality and usage syntax for backward compatibility.
* Added advice on the recommended usage of the `app.WaitForConnection()` method. Fixes [https://github.com/newrelic/go-agent/issues/296](Issue #296)
* Added advice on the recommended usage of the `app.WaitForConnection()` method. Fixes [Issue #296](https://github.com/newrelic/go-agent/issues/296)

## Added
### Added
* Added a convenience function to build distributed trace header set from a JSON string for use with the `AcceptDistributedTraceHeaders()` method. Normally, you must create a valid set of HTTP headers representing the trace identification information from the other trace so the new trace will be associated with it. This needs to be in a Go `http.Header` type value.
* If working only in Go, this may be just fine as it is. However, if the other trace information came from another source, possibly in a different language or environment, it is often the case that the trace data is already presented to you in the form of a JSON string.
* This new function, `DistributedTraceHeadersFromJSON()`, creates the required `http.Header` value from the JSON string without requiring manual effort on your part.
* We also provide a new all-in-one method `AcceptDistributedTraceHeadersFromJSON()` to be used in place of `AcceptDistributedTraceHeaders()`. It accepts a JSON string rather than an `http.Header`, adding its trace info to the new transaction in one step.
* Fixes [https://github.com/newrelic/go-agent/issues/331](Issue #331)
* Fixes [Issue #331](https://github.com/newrelic/go-agent/issues/331)

### Changed
* Improved the NR AWS SDK V2 integration to use the current transaction rather than the one passed in during middleware creation, if `nil` is passed into nrawssdk-v2.AppendMiddlewares. Thanks to @HenriBeck for noticing and suggesting improvement, and thanks to @nc-wittj for the fantastic PR! [#328](https://github.com/newrelic/go-agent/pull/328)

## Support Statement
### 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.

## 3.13.0
Expand Down
2 changes: 1 addition & 1 deletion v3/integrations/nrawssdk-v2/example/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ import (
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
// environment variable called NEW_RELIC_LICENSE_KEY. This example turns on
// Distributed Tracing, but that's not required.
app, err := newrelic.NewApplication(
newrelic.ConfigFromEnvironment(),
Expand Down
80 changes: 45 additions & 35 deletions v3/integrations/nrgrpc/nrgrpc_server.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

//
// This integration instruments grpc service calls via
// UnaryServerInterceptor() and StreamServerInterceptor() functions.
// UnaryServerInterceptor and StreamServerInterceptor functions.
//
// The results of these calls are reported as errors or as informational
// messages (of levels OK, Info, Warning, or Error) based on the gRPC status
Expand All @@ -23,9 +23,9 @@
//
// The disposition of each, in terms of how to report each of the various
// gRPC status codes, is determined by a built-in set of defaults. These
// may be overridden on a case-by-case basis using WithStatusHandler()
// options to each UnaryServerInterceptor() or StreamServerInterceptor()
// call, or globally via the Configure() function.
// may be overridden on a case-by-case basis using WithStatusHandler
// options to each UnaryServerInterceptor or StreamServerInterceptor
// call, or globally via the Configure function.
//
// Full example:
// https://github.com/newrelic/go-agent/blob/master/v3/integrations/nrgrpc/example/server/server.go
Expand Down Expand Up @@ -111,13 +111,17 @@ var interceptorStatusHandlerRegistry = statusHandlerMap{
codes.Unauthenticated: InfoInterceptorStatusHandler,
}

type handlerOption func(statusHandlerMap)
//
// HandlerOption is the type for options passed to the interceptor
// functions to specify gRPC status handlers.
//
type HandlerOption func(statusHandlerMap)

//
// WithStatusHandler() indicates a handler function to be used to
// WithStatusHandler indicates a handler function to be used to
// report the indicated gRPC status. Zero or more of these may be
// given to the Configure(), StreamServiceInterceptor(), or
// UnaryServiceInterceptor() functions.
// given to the Configure, StreamServiceInterceptor, or
// UnaryServiceInterceptor functions.
//
// The ErrorHandler parameter is generally one of the provided standard
// reporting functions:
Expand All @@ -142,45 +146,50 @@ type handlerOption func(statusHandlerMap)
// If you wish to use your custom handler for a code such as codes.NotFound, you would
// include the parameter
// WithStatusHandler(codes.NotFound, myHandler)
// to your Configure(), StreamServiceInterceptor(), or UnaryServiceInterceptor() function.
// to your Configure, StreamServiceInterceptor, or UnaryServiceInterceptor function.
//
func WithStatusHandler(c codes.Code, h ErrorHandler) handlerOption {
func WithStatusHandler(c codes.Code, h ErrorHandler) HandlerOption {
return func(m statusHandlerMap) {
m[c] = h
}
}

//
// Configure() takes a list of WithStatusHandler() options and sets them
// Configure takes a list of WithStatusHandler options and sets them
// as the new default handlers for the specified gRPC status codes, in the same
// way as if WithStatusHandler() were given to the StreamServiceInterceptor()
// or UnaryServiceInterceptor() functions (q.v.); however, in this case the new handlers
// way as if WithStatusHandler were given to the StreamServiceInterceptor
// or UnaryServiceInterceptor functions (q.v.); however, in this case the new handlers
// become the default for any subsequent interceptors created by the above functions.
//
func Configure(options ...handlerOption) {
func Configure(options ...HandlerOption) {
for _, option := range options {
option(interceptorStatusHandlerRegistry)
}
}

//
// Standard handler: IGNORE
// This does not report anything at all in the context.
// IgnoreInterceptorStatusHandler is our standard handler for
// gRPC statuses which we want to ignore (in terms of any gRPC-specific
// reporting on the transaction).
//
func IgnoreInterceptorStatusHandler(_ context.Context, _ *newrelic.Transaction, _ *status.Status) {}

//
// Standard handler: OK
// Reports only that the RPC call was successful by setting the web response header
// value.
// OKInterceptorStatusHandler is our standard handler for
// gRPC statuses which we want to report as being successful, as with the
// status code OK.
//
// This adds no additional attributes on the transaction other than
// the fact that it was successful.
//
func OKInterceptorStatusHandler(ctx context.Context, txn *newrelic.Transaction, s *status.Status) {
txn.SetWebResponse(nil).WriteHeader(int(codes.OK))
}

//
// Standard handler: ERROR
// Reports the transaction as an error, with the relevant error messages and
// ErrorInterceptorStatusHandler is our standard handler for
// gRPC statuses which we want to report as being errors,
// with the relevant error messages and
// contextual information gleaned from the error value received from the RPC call.
//
func ErrorInterceptorStatusHandler(ctx context.Context, txn *newrelic.Transaction, s *status.Status) {
Expand All @@ -194,11 +203,10 @@ func ErrorInterceptorStatusHandler(ctx context.Context, txn *newrelic.Transactio
txn.AddAttribute("GrpcStatusCode", s.Code().String())
}

// examples
// Class "LoginError", Attr: "username": username

//
// Standard handler: WARNING
// WarningInterceptorStatusHandler is our standard handler for
// gRPC statuses which we want to report as warnings.
//
// Reports the transaction's status with attributes containing information gleaned
// from the error value returned, but does not count this as an error.
//
Expand All @@ -210,7 +218,9 @@ func WarningInterceptorStatusHandler(ctx context.Context, txn *newrelic.Transact
}

//
// Standard handler: INFO
// InfoInterceptorStatusHandler is our standard handler for
// gRPC statuses which we want to report as informational messages only.
//
// Reports the transaction's status with attributes containing information gleaned
// from the error value returned, but does not count this as an error.
//
Expand All @@ -222,14 +232,14 @@ func InfoInterceptorStatusHandler(ctx context.Context, txn *newrelic.Transaction
}

//
// Standard handler: DEFAULT
// The DefaultInterceptorStatusHander is used for any status code which is not
// DefaultInterceptorStatusHandler indicates which of our standard handlers
// will be used for any status code which is not
// explicitly assigned a handler.
//
var DefaultInterceptorStatusHandler = InfoInterceptorStatusHandler

//
// Common routine for reporting any kind of interceptor.
// reportInterceptorStatus is the common routine for reporting any kind of interceptor.
//
func reportInterceptorStatus(ctx context.Context, txn *newrelic.Transaction, handlers statusHandlerMap, err error) {
grpcStatus := status.Convert(err)
Expand Down Expand Up @@ -265,23 +275,23 @@ func reportInterceptorStatus(ctx context.Context, txn *newrelic.Transaction, han
//
// The nrgrpc integration has a built-in set of handlers for each gRPC status
// code encountered. Serious errors are reported as error traces à la the
// newrelic.NoticeError() function, while the others are reported but not
// newrelic.NoticeError function, while the others are reported but not
// counted as errors.
//
// If you wish to change this behavior, you may do so at a global level for
// all intercepted functions by calling the Configure() function, passing
// all intercepted functions by calling the Configure function, passing
// any number of WithStatusHandler(code, handler) functions as parameters.
//
// You can specify a custom set of handlers with each interceptor creation by adding
// WithStatusHandler() calls at the end of the <type>StreamInterceptor() call's parameter list,
// WithStatusHandler calls at the end of the <type>StreamInterceptor call's parameter list,
// like so:
// grpc.UnaryInterceptor(nrgrpc.UnaryServerInterceptor(app,
// nrgrpc.WithStatusHandler(codes.OutOfRange, nrgrpc.WarningInterceptorStatusHandler),
// nrgrpc.WithStatusHandler(codes.Unimplemented, nrgrpc.InfoInterceptorStatusHandler)))
// In this case, those two handlers are used (along with the current defaults for the other status
// codes) only for that interceptor.
//
func UnaryServerInterceptor(app *newrelic.Application, options ...handlerOption) grpc.UnaryServerInterceptor {
func UnaryServerInterceptor(app *newrelic.Application, options ...HandlerOption) grpc.UnaryServerInterceptor {
if app == nil {
return func(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (interface{}, error) {
return handler(ctx, req)
Expand Down Expand Up @@ -332,9 +342,9 @@ func newWrappedServerStream(stream grpc.ServerStream, txn *newrelic.Transaction)
// UnaryServerInterceptor and StreamServerInterceptor to instrument unary and
// streaming calls.
//
// See the notes and examples for the UnaryServerInterceptor() function.
// See the notes and examples for the UnaryServerInterceptor function.
//
func StreamServerInterceptor(app *newrelic.Application, options ...handlerOption) grpc.StreamServerInterceptor {
func StreamServerInterceptor(app *newrelic.Application, options ...HandlerOption) grpc.StreamServerInterceptor {
if app == nil {
return func(srv interface{}, ss grpc.ServerStream, info *grpc.StreamServerInfo, handler grpc.StreamHandler) error {
return handler(srv, ss)
Expand Down
4 changes: 2 additions & 2 deletions v3/integrations/nrgrpc/nrgrpc_server_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -196,7 +196,7 @@ func TestUnaryServerInterceptorError(t *testing.T) {
}})
app.ExpectErrorEvents(t, []internal.WantEvent{{
Intrinsics: map[string]interface{}{
"error.class": "gRPC Error: DataLoss",
"error.class": "gRPC Status: DataLoss",
"error.message": "oooooops!",
"guid": internal.MatchAnything,
"priority": internal.MatchAnything,
Expand Down Expand Up @@ -606,7 +606,7 @@ func TestStreamServerInterceptorError(t *testing.T) {
}})
app.ExpectErrorEvents(t, []internal.WantEvent{{
Intrinsics: map[string]interface{}{
"error.class": "gRPC Error: DataLoss",
"error.class": "gRPC Status: DataLoss",
"error.message": "oooooops!",
"guid": internal.MatchAnything,
"priority": internal.MatchAnything,
Expand Down
2 changes: 1 addition & 1 deletion v3/newrelic/version.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import (

const (
// Version is the full string version of this Go Agent.
Version = "3.14.0"
Version = "3.14.1"
)

var (
Expand Down

0 comments on commit 88a31e1

Please sign in to comment.