Skip to content
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

Update codes to match specification #1214

Merged
merged 7 commits into from
Oct 5, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
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
5 changes: 5 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,18 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm
### Added

- OTLP Metric exporter supports Histogram aggregation. (#1209)
- The `Code` struct from the `go.opentelemetry.io/otel/codes` package now supports JSON marshaling and unmarshaling as well as implements the `Stringer` interface. (#1214)
- A Baggage API to implement the OpenTelemetry specification. (#1217)

### Changed

- Set default propagator to no-op propagator. (#1184)
- The `HTTPSupplier`, `HTTPExtractor`, `HTTPInjector`, and `HTTPPropagator` from the `go.opentelemetry.io/otel/api/propagation` package were replaced with unified `TextMapCarrier` and `TextMapPropagator` in the `go.opentelemetry.io/otel` package. (#1212)
- The `New` function from the `go.opentelemetry.io/otel/api/propagation` package was replaced with `NewCompositeTextMapPropagator` in the `go.opentelemetry.io/otel` package. (#1212)
- The status codes of the `go.opentelemetry.io/otel/codes` package have been updated to match the latest OpenTelemetry specification.
They now are `Unset`, `Error`, and `Ok`.
They no longer track the gRPC codes. (#1214)
- The `StatusCode` field of the `SpanData` struct in the `go.opentelemetry.io/otel/sdk/export/trace` package now uses the codes package from this package instead of the gRPC project. (#1214)
- Move the `go.opentelemetry.io/otel/api/baggage` package into `go.opentelemetry.io/otel/propagators`. (#1217)

### Removed
Expand Down
2 changes: 1 addition & 1 deletion api/apitest/harness.go
Original file line number Diff line number Diff line change
Expand Up @@ -189,7 +189,7 @@ func (h *Harness) testSpan(tracerFactory func() trace.Tracer) {
span.AddEventWithTimestamp(context.Background(), time.Now(), "test event")
},
"#SetStatus": func(span trace.Span) {
span.SetStatus(codes.Internal, "internal")
span.SetStatus(codes.Error, "internal")
},
"#SetName": func(span trace.Span) {
span.SetName("new name")
Expand Down
2 changes: 1 addition & 1 deletion api/trace/tracetest/span.go
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ func (s *Span) RecordError(ctx context.Context, err error, opts ...trace.ErrorOp
cfg.Timestamp = time.Now()
}

if cfg.StatusCode != codes.OK {
if cfg.StatusCode != codes.Unset {
s.SetStatus(cfg.StatusCode, "")
}

Expand Down
32 changes: 9 additions & 23 deletions api/trace/tracetest/span_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -167,7 +167,7 @@ func TestSpan(t *testing.T) {
},
}}
e.Expect(subject.Events()).ToEqual(expectedEvents)
e.Expect(subject.StatusCode()).ToEqual(codes.OK)
e.Expect(subject.StatusCode()).ToEqual(codes.Unset)
e.Expect(subject.StatusMessage()).ToEqual("")
}
})
Expand All @@ -186,7 +186,7 @@ func TestSpan(t *testing.T) {
errMsg := "test error message"
testErr := ottest.NewTestError(errMsg)
testTime := time.Now()
expStatusCode := codes.Unknown
expStatusCode := codes.Error
subject.RecordError(ctx, testErr, trace.WithErrorTime(testTime), trace.WithErrorStatus(expStatusCode))

expectedEvents := []tracetest.Event{{
Expand Down Expand Up @@ -538,31 +538,17 @@ func TestSpan(t *testing.T) {
subject, ok := span.(*tracetest.Span)
e.Expect(ok).ToBeTrue()

e.Expect(subject.StatusCode()).ToEqual(codes.OK)
e.Expect(subject.StatusCode()).ToEqual(codes.Unset)

subject.End()

e.Expect(subject.StatusCode()).ToEqual(codes.OK)
e.Expect(subject.StatusCode()).ToEqual(codes.Unset)
})

statuses := []codes.Code{
codes.OK,
codes.Canceled,
codes.Unknown,
codes.InvalidArgument,
codes.DeadlineExceeded,
codes.NotFound,
codes.AlreadyExists,
codes.PermissionDenied,
codes.ResourceExhausted,
codes.FailedPrecondition,
codes.Aborted,
codes.OutOfRange,
codes.Unimplemented,
codes.Internal,
codes.Unavailable,
codes.DataLoss,
codes.Unauthenticated,
codes.Unset,
codes.Error,
codes.Ok,
}

for _, status := range statuses {
Expand All @@ -577,7 +563,7 @@ func TestSpan(t *testing.T) {
subject, ok := span.(*tracetest.Span)
e.Expect(ok).ToBeTrue()

subject.SetStatus(codes.OK, "OK")
subject.SetStatus(codes.Ok, "Ok")
subject.SetStatus(status, "Yo!")

e.Expect(subject.StatusCode()).ToEqual(status)
Expand All @@ -595,7 +581,7 @@ func TestSpan(t *testing.T) {
subject, ok := span.(*tracetest.Span)
e.Expect(ok).ToBeTrue()

originalStatus := codes.OK
originalStatus := codes.Ok

subject.SetStatus(originalStatus, "OK")
subject.End()
Expand Down
4 changes: 2 additions & 2 deletions bridge/opentracing/bridge.go
Original file line number Diff line number Diff line change
Expand Up @@ -133,7 +133,7 @@ func (s *bridgeSpan) SetTag(key string, value interface{}) ot.Span {
// TODO: Should we ignore it?
case string(otext.Error):
if b, ok := value.(bool); ok && b {
s.otelSpan.SetStatus(codes.Unknown, "")
s.otelSpan.SetStatus(codes.Error, "")
}
default:
s.otelSpan.SetAttributes(otTagToOTelLabel(key, value))
Expand Down Expand Up @@ -405,7 +405,7 @@ func (t *BridgeTracer) StartSpan(operationName string, opts ...ot.StartSpanOptio
})
}
if hadTrueErrorTag {
otelSpan.SetStatus(codes.Unknown, "")
otelSpan.SetStatus(codes.Error, "")
}
// One does not simply pass a concrete pointer to function
// that takes some interface. In case of passing nil concrete
Expand Down
2 changes: 1 addition & 1 deletion bridge/opentracing/internal/mock.go
Original file line number Diff line number Diff line change
Expand Up @@ -263,7 +263,7 @@ func (s *MockSpan) RecordError(ctx context.Context, err error, opts ...oteltrace
cfg.Timestamp = time.Now()
}

if cfg.StatusCode != codes.OK {
if cfg.StatusCode != codes.Ok {
s.SetStatus(cfg.StatusCode, "")
}

Expand Down
142 changes: 76 additions & 66 deletions codes/codes.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,74 +16,84 @@
//
// It conforms to [the OpenTelemetry
// specification](https://github.com/open-telemetry/opentelemetry-specification/blob/master/specification/trace/api.md#statuscanonicalcode).
// This also means that it follows gRPC codes and is based on
// [google.golang.org/grpc/codes](https://godoc.org/google.golang.org/grpc/codes).
//
// This package was added to this project, instead of using that existing
// package, to avoid the large package size it includes and not impose that
// burden on projects using this package.
package codes // import "go.opentelemetry.io/otel/codes"

// Code is an 32-bit representation of a status state.
type Code uint32
import (
"fmt"
"strconv"
)

// WARNING: any changes here must be propagated to the
// otel/sdk/internal/codes.go file.
const (
// OK means success.
OK Code = 0
// Canceled indicates the operation was canceled (typically by the
// caller).
Canceled Code = 1
// Unknown error. An example of where this error may be returned is if an
// error is raised by a dependant API that does not contain enough
// information to convert it into a more appropriate error.
Unknown Code = 2
// InvalidArgument indicates a client specified an invalid argument. Note
// that this differs from FailedPrecondition. InvalidArgument indicates
// arguments that are problematic regardless of the state of the system.
InvalidArgument Code = 3
// DeadlineExceeded means a deadline expired before the operation could
// complete. For operations that change the state of the system, this error
// may be returned even if the operation has completed successfully.
DeadlineExceeded Code = 4
// NotFound means some requested entity (e.g., file or directory) was not
// found.
NotFound Code = 5
// AlreadyExists means some entity that we attempted to create (e.g., file
// or directory) already exists.
AlreadyExists Code = 6
// PermissionDenied means the caller does not have permission to execute
// the specified operation. PermissionDenied must not be used if the caller
// cannot be identified (use Unauthenticated instead for those errors).
PermissionDenied Code = 7
// ResourceExhausted means some resource has been exhausted, perhaps a
// per-user quota, or perhaps the entire file system is out of space.
ResourceExhausted Code = 8
// FailedPrecondition means the operation was rejected because the system
// is not in a state required for the operation's execution.
FailedPrecondition Code = 9
// Aborted means the operation was aborted, typically due to a concurrency
// issue like sequencer check failures, transaction aborts, etc.
Aborted Code = 10
// OutOfRange means the operation was attempted past the valid range.
// E.g., seeking or reading past end of file. Unlike InvalidArgument, this
// error indicates a problem that may be fixed if the system state
// changes.
OutOfRange Code = 11
// Unimplemented means the operation is not implemented or not
// supported/enabled in this service.
Unimplemented Code = 12
// Internal means an internal errors. It means some invariants expected by
// underlying system has been broken.
Internal Code = 13
// Unavailable means the service is currently unavailable. This is most
// likely a transient condition and may be corrected by retrying with a
// backoff.
Unavailable Code = 14
// DataLoss means unrecoverable data loss or corruption has occurred.
DataLoss Code = 15
// Unauthenticated means the request does not have valid authentication
// credentials for the operation.
Unauthenticated Code = 16
// Unset is the default status code.
Unset Code = 0
// Error indicates the operation contains an error.
Error Code = 1
// Ok indicates operation has been validated by an Application developers
// or Operator to have completed successfully, or contain no error.
Ok Code = 2

maxCode = 3
)

// Code is an 32-bit representation of a status state.
type Code uint32

var codeToStr = map[Code]string{
Unset: "Unset",
Error: "Error",
Ok: "Ok",
}

var strToCode = map[string]Code{
"Unset": Unset,
"Error": Error,
"Ok": Ok,
}

// String returns the Code as a string.
func (c Code) String() string {
return codeToStr[c]
}

// UnmarshalJSON unmarshals b into the Code.
//
// This is based on the functionality in the gRPC codes package:
// https://github.com/grpc/grpc-go/blob/bb64fee312b46ebee26be43364a7a966033521b1/codes/codes.go#L218-L244
func (c *Code) UnmarshalJSON(b []byte) error {
// From json.Unmarshaler: By convention, to approximate the behavior of
// Unmarshal itself, Unmarshalers implement UnmarshalJSON([]byte("null")) as
// a no-op.
if string(b) == "null" {
return nil
}
if c == nil {
return fmt.Errorf("nil receiver passed to UnmarshalJSON")
}

if ci, err := strconv.ParseUint(string(b), 10, 32); err == nil {
if ci >= maxCode {
return fmt.Errorf("invalid code: %q", ci)
}

*c = Code(ci)
return nil
}

if jc, ok := strToCode[string(b)]; ok {
*c = jc
return nil
}
return fmt.Errorf("invalid code: %q", string(b))
}

// MarshalJSON returns c as the JSON encoding of c.
func (c *Code) MarshalJSON() ([]byte, error) {
if c == nil {
return []byte("null"), nil
}
str, ok := codeToStr[*c]
if !ok {
return nil, fmt.Errorf("invalid code: %d", *c)
}
return []byte(fmt.Sprintf("%q", str)), nil
}
Loading