Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ package otelgrpc // import "go.opentelemetry.io/contrib/instrumentation/google.g

import (
"context"
"strconv"
"sync/atomic"
"time"

Expand Down Expand Up @@ -328,9 +329,9 @@ func (c *config) handleRPC(
var s *status.Status
if rs.Error != nil {
s, _ = status.FromError(rs.Error)
rpcStatusAttr = semconv.RPCResponseStatusCode(s.Code().String())
rpcStatusAttr = semconv.RPCResponseStatusCode(canonicalString(s.Code()))
} else {
rpcStatusAttr = semconv.RPCResponseStatusCode(grpc_codes.OK.String())
rpcStatusAttr = semconv.RPCResponseStatusCode(canonicalString(grpc_codes.OK))
}
if span.IsRecording() {
if s != nil {
Expand Down Expand Up @@ -362,3 +363,44 @@ func (c *config) handleRPC(
return
}
}

func canonicalString(code grpc_codes.Code) string {
switch code {
case grpc_codes.OK:
return "OK"
case grpc_codes.Canceled:
return "CANCELLED"
Copy link
Copy Markdown

@bincyber bincyber Mar 29, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should not this be spelled the same way as grpc_codes.Canceled? @haines

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The string representation of this error does have two L in the grpc spec.

https://github.com/grpc/grpc/blob/v1.75.0/doc/statuscodes.md

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

And this is what is used as the canonical string internally in the Go gRPC implementation as well, unfortunately they named the constant using the alternate spelling which makes things confusing: https://github.com/grpc/grpc-go/blob/b71c26202050d6a6c528e42f96a2712baa74eb61/codes/code_string.go#L77

case grpc_codes.Unknown:
return "UNKNOWN"
case grpc_codes.InvalidArgument:
return "INVALID_ARGUMENT"
case grpc_codes.DeadlineExceeded:
return "DEADLINE_EXCEEDED"
case grpc_codes.NotFound:
return "NOT_FOUND"
case grpc_codes.AlreadyExists:
return "ALREADY_EXISTS"
case grpc_codes.PermissionDenied:
return "PERMISSION_DENIED"
case grpc_codes.ResourceExhausted:
return "RESOURCE_EXHAUSTED"
case grpc_codes.FailedPrecondition:
return "FAILED_PRECONDITION"
case grpc_codes.Aborted:
return "ABORTED"
case grpc_codes.OutOfRange:
return "OUT_OF_RANGE"
case grpc_codes.Unimplemented:
return "UNIMPLEMENTED"
case grpc_codes.Internal:
return "INTERNAL"
case grpc_codes.Unavailable:
return "UNAVAILABLE"
case grpc_codes.DataLoss:
return "DATA_LOSS"
case grpc_codes.Unauthenticated:
return "UNAUTHENTICATED"
default:
Comment thread
pellared marked this conversation as resolved.
return "CODE(" + strconv.FormatInt(int64(code), 10) + ")"
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -37,91 +37,109 @@
grpcCode grpc_codes.Code
wantSpanCode otelcode.Code
wantSpanStatusDescription string
wantRPCResponseStatusCode string
}{
{
grpcCode: grpc_codes.OK,
wantSpanCode: otelcode.Unset,
wantSpanStatusDescription: "",
wantRPCResponseStatusCode: "OK",
},
{
grpcCode: grpc_codes.Canceled,
wantSpanCode: otelcode.Unset,
wantSpanStatusDescription: "",
wantRPCResponseStatusCode: "CANCELLED",
},
{
grpcCode: grpc_codes.Unknown,
wantSpanCode: otelcode.Error,
wantSpanStatusDescription: grpc_codes.Unknown.String(),
wantRPCResponseStatusCode: "UNKNOWN",
},
{
grpcCode: grpc_codes.InvalidArgument,
wantSpanCode: otelcode.Unset,
wantSpanStatusDescription: "",
wantRPCResponseStatusCode: "INVALID_ARGUMENT",
},
{
grpcCode: grpc_codes.DeadlineExceeded,
wantSpanCode: otelcode.Error,
wantSpanStatusDescription: grpc_codes.DeadlineExceeded.String(),
wantRPCResponseStatusCode: "DEADLINE_EXCEEDED",
},
{
grpcCode: grpc_codes.NotFound,
wantSpanCode: otelcode.Unset,
wantSpanStatusDescription: "",
wantRPCResponseStatusCode: "NOT_FOUND",
},
{
grpcCode: grpc_codes.AlreadyExists,
wantSpanCode: otelcode.Unset,
wantSpanStatusDescription: "",
wantRPCResponseStatusCode: "ALREADY_EXISTS",
},
{
grpcCode: grpc_codes.PermissionDenied,
wantSpanCode: otelcode.Unset,
wantSpanStatusDescription: "",
wantRPCResponseStatusCode: "PERMISSION_DENIED",
},
{
grpcCode: grpc_codes.ResourceExhausted,
wantSpanCode: otelcode.Unset,
wantSpanStatusDescription: "",
wantRPCResponseStatusCode: "RESOURCE_EXHAUSTED",
},
{
grpcCode: grpc_codes.FailedPrecondition,
wantSpanCode: otelcode.Unset,
wantSpanStatusDescription: "",
wantRPCResponseStatusCode: "FAILED_PRECONDITION",
},
{
grpcCode: grpc_codes.Aborted,
wantSpanCode: otelcode.Unset,
wantSpanStatusDescription: "",
wantRPCResponseStatusCode: "ABORTED",
},
{
grpcCode: grpc_codes.OutOfRange,
wantSpanCode: otelcode.Unset,
wantSpanStatusDescription: "",
wantRPCResponseStatusCode: "OUT_OF_RANGE",
},
{
grpcCode: grpc_codes.Unimplemented,
wantSpanCode: otelcode.Error,
wantSpanStatusDescription: grpc_codes.Unimplemented.String(),
wantRPCResponseStatusCode: "UNIMPLEMENTED",
},
{
grpcCode: grpc_codes.Internal,
wantSpanCode: otelcode.Error,
wantSpanStatusDescription: grpc_codes.Internal.String(),
wantRPCResponseStatusCode: "INTERNAL",
},
{
grpcCode: grpc_codes.Unavailable,
wantSpanCode: otelcode.Error,
wantSpanStatusDescription: grpc_codes.Unavailable.String(),
wantRPCResponseStatusCode: "UNAVAILABLE",
},
{
grpcCode: grpc_codes.DataLoss,
wantSpanCode: otelcode.Error,
wantSpanStatusDescription: grpc_codes.DataLoss.String(),
wantRPCResponseStatusCode: "DATA_LOSS",
},
{
grpcCode: grpc_codes.Unauthenticated,
wantSpanCode: otelcode.Unset,
wantSpanStatusDescription: "",
wantRPCResponseStatusCode: "UNAUTHENTICATED",
},
}

Expand Down Expand Up @@ -158,15 +176,15 @@
// validate span
span, ok := getSpanFromRecorder(sr, methodName)
require.True(t, ok, "missing span %s", methodName)
assertServerSpan(t, check.wantSpanCode, check.wantSpanStatusDescription, check.grpcCode, span)
assertServerSpan(t, check.wantSpanCode, check.wantSpanStatusDescription, check.wantRPCResponseStatusCode, span)

// validate metric
assertStatsHandlerServerMetrics(t, mr, serviceName, name, check.grpcCode)
assertStatsHandlerServerMetrics(t, mr, serviceName, name, check.wantRPCResponseStatusCode)
})
}
}

func assertServerSpan(t *testing.T, wantSpanCode otelcode.Code, wantSpanStatusDescription string, wantGrpcCode grpc_codes.Code, span trace.ReadOnlySpan) {
func assertServerSpan(t *testing.T, wantSpanCode otelcode.Code, wantSpanStatusDescription string, wantGrpcCode string, span trace.ReadOnlySpan) {

Check failure on line 187 in instrumentation/google.golang.org/grpc/otelgrpc/stats_handlertest_test.go

View workflow job for this annotation

GitHub Actions / lint

paramTypeCombine: func(t *testing.T, wantSpanCode otelcode.Code, wantSpanStatusDescription string, wantGrpcCode string, span trace.ReadOnlySpan) could be replaced with func(t *testing.T, wantSpanCode otelcode.Code, wantSpanStatusDescription, wantGrpcCode string, span trace.ReadOnlySpan) (gocritic)
// validate span status
assert.Equal(t, wantSpanCode, span.Status().Code)
assert.Equal(t, wantSpanStatusDescription, span.Status().Description)
Expand All @@ -181,10 +199,10 @@
}

require.True(t, codeAttr.Valid(), "attributes contain gRPC status code")
assert.Equal(t, attribute.StringValue(wantGrpcCode.String()), codeAttr.Value)
assert.Equal(t, attribute.StringValue(wantGrpcCode), codeAttr.Value)
}

func assertStatsHandlerServerMetrics(t *testing.T, reader metric.Reader, serviceName, name string, code grpc_codes.Code) {
func assertStatsHandlerServerMetrics(t *testing.T, reader metric.Reader, serviceName, name string, code string) {

Check failure on line 205 in instrumentation/google.golang.org/grpc/otelgrpc/stats_handlertest_test.go

View workflow job for this annotation

GitHub Actions / lint

paramTypeCombine: func(t *testing.T, reader metric.Reader, serviceName, name string, code string) could be replaced with func(t *testing.T, reader metric.Reader, serviceName, name, code string) (gocritic)
want := metricdata.ScopeMetrics{
Scope: wantInstrumentationScope,
Metrics: []metricdata.Metrics{
Expand All @@ -199,7 +217,7 @@
Attributes: attribute.NewSet(
semconv.RPCMethod(serviceName+"/"+name),
semconv.RPCSystemNameGRPC,
semconv.RPCResponseStatusCode(code.String()),
semconv.RPCResponseStatusCode(code),
testMetricAttr,
),
},
Expand Down
Loading