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
contrib/google.golang.org/grpc: add WithErrorCheck
option
#2035
base: main
Are you sure you want to change the base?
Changes from 10 commits
08a7e75
252e275
06ce818
6a8cac1
057eaab
c6b7f79
fb4336f
50c90b2
0fd73e7
b925a3a
100083f
6a3227e
5b83467
4fca224
cff830c
ca200c3
7f0c9b5
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
@@ -663,6 +663,221 @@ func waitForSpans(mt mocktracer.Tracer, sz int) { | |||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||
func TestNonErrorFunc(t *testing.T) { | ||||||||||||||||||||||||||||||||||||||
t.Run("unary", func(t *testing.T) { | ||||||||||||||||||||||||||||||||||||||
for name, tt := range map[string]struct { | ||||||||||||||||||||||||||||||||||||||
nonErrorFunc func(method string, err error) bool | ||||||||||||||||||||||||||||||||||||||
message string | ||||||||||||||||||||||||||||||||||||||
withError bool | ||||||||||||||||||||||||||||||||||||||
wantCode string | ||||||||||||||||||||||||||||||||||||||
wantMessage string | ||||||||||||||||||||||||||||||||||||||
}{ | ||||||||||||||||||||||||||||||||||||||
"Invalid_with_no_error": { | ||||||||||||||||||||||||||||||||||||||
message: "invalid", | ||||||||||||||||||||||||||||||||||||||
nonErrorFunc: func(method string, err error) bool { | ||||||||||||||||||||||||||||||||||||||
if err == nil { | ||||||||||||||||||||||||||||||||||||||
return true | ||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||
errCode := status.Code(err) | ||||||||||||||||||||||||||||||||||||||
if errCode == codes.InvalidArgument && method == "/grpc.Fixture/Ping" { | ||||||||||||||||||||||||||||||||||||||
return true | ||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||
return false | ||||||||||||||||||||||||||||||||||||||
}, | ||||||||||||||||||||||||||||||||||||||
withError: false, | ||||||||||||||||||||||||||||||||||||||
wantCode: codes.InvalidArgument.String(), | ||||||||||||||||||||||||||||||||||||||
wantMessage: "invalid", | ||||||||||||||||||||||||||||||||||||||
}, | ||||||||||||||||||||||||||||||||||||||
"Invalid_with_error": { | ||||||||||||||||||||||||||||||||||||||
message: "invalid", | ||||||||||||||||||||||||||||||||||||||
nonErrorFunc: func(method string, err error) bool { | ||||||||||||||||||||||||||||||||||||||
if err == nil { | ||||||||||||||||||||||||||||||||||||||
return true | ||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||
errCode := status.Code(err) | ||||||||||||||||||||||||||||||||||||||
if errCode == codes.InvalidArgument && method == "/some/endpoint" { | ||||||||||||||||||||||||||||||||||||||
return true | ||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||
return false | ||||||||||||||||||||||||||||||||||||||
}, | ||||||||||||||||||||||||||||||||||||||
withError: true, | ||||||||||||||||||||||||||||||||||||||
wantCode: codes.InvalidArgument.String(), | ||||||||||||||||||||||||||||||||||||||
wantMessage: "invalid", | ||||||||||||||||||||||||||||||||||||||
}, | ||||||||||||||||||||||||||||||||||||||
"Invalid_with_error_without_nonErrorFunc": { | ||||||||||||||||||||||||||||||||||||||
message: "invalid", | ||||||||||||||||||||||||||||||||||||||
nonErrorFunc: nil, | ||||||||||||||||||||||||||||||||||||||
withError: true, | ||||||||||||||||||||||||||||||||||||||
wantCode: codes.InvalidArgument.String(), | ||||||||||||||||||||||||||||||||||||||
wantMessage: "invalid", | ||||||||||||||||||||||||||||||||||||||
}, | ||||||||||||||||||||||||||||||||||||||
} { | ||||||||||||||||||||||||||||||||||||||
t.Run(name, func(t *testing.T) { | ||||||||||||||||||||||||||||||||||||||
mt := mocktracer.Start() | ||||||||||||||||||||||||||||||||||||||
defer mt.Stop() | ||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||
var ( | ||||||||||||||||||||||||||||||||||||||
rig *rig | ||||||||||||||||||||||||||||||||||||||
err error | ||||||||||||||||||||||||||||||||||||||
) | ||||||||||||||||||||||||||||||||||||||
if tt.nonErrorFunc == nil { | ||||||||||||||||||||||||||||||||||||||
rig, err = newRig(true) | ||||||||||||||||||||||||||||||||||||||
} else { | ||||||||||||||||||||||||||||||||||||||
rig, err = newRig(true, NonErrorFunc(tt.nonErrorFunc)) | ||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||
if err != nil { | ||||||||||||||||||||||||||||||||||||||
t.Fatalf("error setting up rig: %s", err) | ||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||
client := rig.client | ||||||||||||||||||||||||||||||||||||||
_, err = client.Ping(context.Background(), &FixtureRequest{Name: tt.message}) | ||||||||||||||||||||||||||||||||||||||
assert.Error(t, err) | ||||||||||||||||||||||||||||||||||||||
assert.Equal(t, tt.wantCode, status.Code(err).String()) | ||||||||||||||||||||||||||||||||||||||
assert.Equal(t, tt.wantMessage, status.Convert(err).Message()) | ||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||
spans := mt.FinishedSpans() | ||||||||||||||||||||||||||||||||||||||
assert.Len(t, spans, 2) | ||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||
var serverSpan, clientSpan mocktracer.Span | ||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||
for _, s := range spans { | ||||||||||||||||||||||||||||||||||||||
// order of traces in buffer is not garanteed | ||||||||||||||||||||||||||||||||||||||
switch s.OperationName() { | ||||||||||||||||||||||||||||||||||||||
case "grpc.server": | ||||||||||||||||||||||||||||||||||||||
serverSpan = s | ||||||||||||||||||||||||||||||||||||||
case "grpc.client": | ||||||||||||||||||||||||||||||||||||||
clientSpan = s | ||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||
if tt.withError { | ||||||||||||||||||||||||||||||||||||||
assert.NotNil(t, clientSpan.Tag(ext.Error)) | ||||||||||||||||||||||||||||||||||||||
assert.NotNil(t, serverSpan.Tag(ext.Error)) | ||||||||||||||||||||||||||||||||||||||
} else { | ||||||||||||||||||||||||||||||||||||||
assert.Nil(t, clientSpan.Tag(ext.Error)) | ||||||||||||||||||||||||||||||||||||||
assert.Nil(t, serverSpan.Tag(ext.Error)) | ||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||
rig.Close() | ||||||||||||||||||||||||||||||||||||||
mt.Reset() | ||||||||||||||||||||||||||||||||||||||
}) | ||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||
}) | ||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||
t.Run("stream", func(t *testing.T) { | ||||||||||||||||||||||||||||||||||||||
mt := mocktracer.Start() | ||||||||||||||||||||||||||||||||||||||
defer mt.Stop() | ||||||||||||||||||||||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Please move the start of the mocktracer this into the test case run There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is this the correct revision of your point? |
||||||||||||||||||||||||||||||||||||||
for name, tt := range map[string]struct { | ||||||||||||||||||||||||||||||||||||||
nonErrorFunc func(method string, err error) bool | ||||||||||||||||||||||||||||||||||||||
message string | ||||||||||||||||||||||||||||||||||||||
withError bool | ||||||||||||||||||||||||||||||||||||||
wantCode string | ||||||||||||||||||||||||||||||||||||||
wantMessage string | ||||||||||||||||||||||||||||||||||||||
}{ | ||||||||||||||||||||||||||||||||||||||
"Invalid_with_no_error": { | ||||||||||||||||||||||||||||||||||||||
message: "invalid", | ||||||||||||||||||||||||||||||||||||||
nonErrorFunc: func(method string, err error) bool { | ||||||||||||||||||||||||||||||||||||||
if err == nil { | ||||||||||||||||||||||||||||||||||||||
return true | ||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||
errCode := status.Code(err) | ||||||||||||||||||||||||||||||||||||||
if errCode == codes.InvalidArgument && method == "/grpc.Fixture/StreamPing" { | ||||||||||||||||||||||||||||||||||||||
return true | ||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||
return false | ||||||||||||||||||||||||||||||||||||||
}, | ||||||||||||||||||||||||||||||||||||||
withError: false, | ||||||||||||||||||||||||||||||||||||||
wantCode: codes.InvalidArgument.String(), | ||||||||||||||||||||||||||||||||||||||
wantMessage: "invalid", | ||||||||||||||||||||||||||||||||||||||
}, | ||||||||||||||||||||||||||||||||||||||
"Invalid_with_error": { | ||||||||||||||||||||||||||||||||||||||
message: "invalid", | ||||||||||||||||||||||||||||||||||||||
nonErrorFunc: func(method string, err error) bool { | ||||||||||||||||||||||||||||||||||||||
if err == nil { | ||||||||||||||||||||||||||||||||||||||
return true | ||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||
errCode := status.Code(err) | ||||||||||||||||||||||||||||||||||||||
if errCode == codes.InvalidArgument && method == "/some/endpoint" { | ||||||||||||||||||||||||||||||||||||||
return true | ||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||
return false | ||||||||||||||||||||||||||||||||||||||
}, | ||||||||||||||||||||||||||||||||||||||
withError: true, | ||||||||||||||||||||||||||||||||||||||
wantCode: codes.InvalidArgument.String(), | ||||||||||||||||||||||||||||||||||||||
wantMessage: "invalid", | ||||||||||||||||||||||||||||||||||||||
}, | ||||||||||||||||||||||||||||||||||||||
"Invalid_with_error_without_nonErrorFunc": { | ||||||||||||||||||||||||||||||||||||||
message: "invalid", | ||||||||||||||||||||||||||||||||||||||
nonErrorFunc: nil, | ||||||||||||||||||||||||||||||||||||||
withError: true, | ||||||||||||||||||||||||||||||||||||||
wantCode: codes.InvalidArgument.String(), | ||||||||||||||||||||||||||||||||||||||
wantMessage: "invalid", | ||||||||||||||||||||||||||||||||||||||
}, | ||||||||||||||||||||||||||||||||||||||
} { | ||||||||||||||||||||||||||||||||||||||
t.Run(name, func(t *testing.T) { | ||||||||||||||||||||||||||||||||||||||
var ( | ||||||||||||||||||||||||||||||||||||||
rig *rig | ||||||||||||||||||||||||||||||||||||||
err error | ||||||||||||||||||||||||||||||||||||||
) | ||||||||||||||||||||||||||||||||||||||
if tt.nonErrorFunc == nil { | ||||||||||||||||||||||||||||||||||||||
rig, err = newRig(true) | ||||||||||||||||||||||||||||||||||||||
} else { | ||||||||||||||||||||||||||||||||||||||
rig, err = newRig(true, NonErrorFunc(tt.nonErrorFunc)) | ||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Thank you so much! |
||||||||||||||||||||||||||||||||||||||
if err != nil { | ||||||||||||||||||||||||||||||||||||||
t.Fatalf("error setting up rig: %s", err) | ||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||
ctx, done := context.WithCancel(context.Background()) | ||||||||||||||||||||||||||||||||||||||
client := rig.client | ||||||||||||||||||||||||||||||||||||||
stream, err := client.StreamPing(ctx) | ||||||||||||||||||||||||||||||||||||||
assert.NoError(t, err) | ||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||
err = stream.Send(&FixtureRequest{Name: tt.message}) | ||||||||||||||||||||||||||||||||||||||
assert.NoError(t, err) | ||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||
_, err = stream.Recv() | ||||||||||||||||||||||||||||||||||||||
assert.Error(t, err) | ||||||||||||||||||||||||||||||||||||||
assert.Equal(t, tt.wantCode, status.Code(err).String()) | ||||||||||||||||||||||||||||||||||||||
assert.Equal(t, tt.wantMessage, status.Convert(err).Message()) | ||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||
assert.NoError(t, stream.CloseSend()) | ||||||||||||||||||||||||||||||||||||||
done() // close stream from client side | ||||||||||||||||||||||||||||||||||||||
rig.Close() | ||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||
waitForSpans(mt, 5) | ||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||
spans := mt.FinishedSpans() | ||||||||||||||||||||||||||||||||||||||
assert.Len(t, spans, 5) | ||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||
noError := true | ||||||||||||||||||||||||||||||||||||||
for _, s := range spans { | ||||||||||||||||||||||||||||||||||||||
if s.Tag(ext.Error) != nil { | ||||||||||||||||||||||||||||||||||||||
noError = false | ||||||||||||||||||||||||||||||||||||||
break | ||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||
if tt.withError { | ||||||||||||||||||||||||||||||||||||||
assert.False(t, noError) | ||||||||||||||||||||||||||||||||||||||
} else { | ||||||||||||||||||||||||||||||||||||||
assert.True(t, noError) | ||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I had not thought of this idea. Thank you so much! |
||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||
mt.Reset() | ||||||||||||||||||||||||||||||||||||||
}) | ||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||
}) | ||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||
func TestAnalyticsSettings(t *testing.T) { | ||||||||||||||||||||||||||||||||||||||
assertRate := func(t *testing.T, mt mocktracer.Tracer, rate interface{}, opts ...InterceptorOption) { | ||||||||||||||||||||||||||||||||||||||
rig, err := newRig(true, opts...) | ||||||||||||||||||||||||||||||||||||||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -29,6 +29,7 @@ type config struct { | |
serviceName func() string | ||
spanName string | ||
nonErrorCodes map[codes.Code]bool | ||
nonErrorFunc func(method string, err error) bool | ||
traceStreamCalls bool | ||
traceStreamMessages bool | ||
noDebugStack bool | ||
|
@@ -129,6 +130,15 @@ func NonErrorCodes(cs ...codes.Code) InterceptorOption { | |
} | ||
} | ||
|
||
// NonErrorFunc sets a custom function to determine whether an error should not be considered as an error for tracing purposes. | ||
// This function is evaluated when an error occurs, and if it returns true, the error will not be recorded in the trace. | ||
// f: A function taking the gRPC method and error as arguments, returning a boolean to indicate if the error should be ignored. | ||
func NonErrorFunc(f func(method string, err error) bool) Option { | ||
return func(cfg *config) { | ||
cfg.nonErrorFunc = f | ||
} | ||
} | ||
|
||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I suggest aligning naming with other Options in this package, for example 'WithErrorCheck' naming is widely used across other packages. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I have renamed the function name and config field names considering this comment as well. Thanks! |
||
// WithAnalytics enables Trace Analytics for all started spans. | ||
func WithAnalytics(on bool) Option { | ||
return func(cfg *config) { | ||
|
Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
@@ -59,7 +59,15 @@ func (h *clientStatsHandler) HandleRPC(ctx context.Context, rs stats.RPCStats) { | |||||||||||||||||||||||
span.SetTag(ext.TargetPort, port) | ||||||||||||||||||||||||
} | ||||||||||||||||||||||||
case *stats.End: | ||||||||||||||||||||||||
finishWithError(span, rs.Error, h.cfg) | ||||||||||||||||||||||||
val := ctx.Value(fullMethodNameKey{}) | ||||||||||||||||||||||||
if val == nil { | ||||||||||||||||||||||||
return | ||||||||||||||||||||||||
} | ||||||||||||||||||||||||
fullMethod, ok := val.(string) | ||||||||||||||||||||||||
if !ok { | ||||||||||||||||||||||||
return | ||||||||||||||||||||||||
} | ||||||||||||||||||||||||
finishWithError(span, rs.Error, fullMethod, h.cfg) | ||||||||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Same here, don't we want to run
Suggested change
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I fixed it, too. |
||||||||||||||||||||||||
} | ||||||||||||||||||||||||
} | ||||||||||||||||||||||||
|
||||||||||||||||||||||||
|
Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
@@ -43,6 +43,7 @@ func (h *serverStatsHandler) TagRPC(ctx context.Context, rti *stats.RPCTagInfo) | |||||||||||||||||||||||||
h.cfg.serviceName, | ||||||||||||||||||||||||||
spanOpts..., | ||||||||||||||||||||||||||
) | ||||||||||||||||||||||||||
ctx = context.WithValue(ctx, fullMethodNameKey{}, rti.FullMethodName) | ||||||||||||||||||||||||||
return ctx | ||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||
|
||||||||||||||||||||||||||
|
@@ -52,8 +53,16 @@ func (h *serverStatsHandler) HandleRPC(ctx context.Context, rs stats.RPCStats) { | |||||||||||||||||||||||||
if !ok { | ||||||||||||||||||||||||||
return | ||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||
val := ctx.Value(fullMethodNameKey{}) | ||||||||||||||||||||||||||
if val == nil { | ||||||||||||||||||||||||||
return | ||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||
fullMethod, ok := val.(string) | ||||||||||||||||||||||||||
if !ok { | ||||||||||||||||||||||||||
return | ||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Returning early here will result in
Suggested change
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You are correct, finishWithError needs to be called. I made a mistake. Thanks. |
||||||||||||||||||||||||||
if v, ok := rs.(*stats.End); ok { | ||||||||||||||||||||||||||
finishWithError(span, v.Error, h.cfg) | ||||||||||||||||||||||||||
finishWithError(span, v.Error, fullMethod, h.cfg) | ||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||
|
||||||||||||||||||||||||||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.