diff --git a/debug.md b/debug.md index 2010ed7a6f9c..7979d14af3f4 100644 --- a/debug.md +++ b/debug.md @@ -187,9 +187,8 @@ for OpenCensus is now deprecated in the Google Cloud client libraries for Go. See [OpenCensus](#opencensus) below for details. The Google Cloud client libraries for Go now use the -[OpenTelemetry](https://opentelemetry.io/docs/what-is-opentelemetry/) project by -default. Temporary opt-in support for OpenCensus is still available. The -transition from OpenCensus to OpenTelemetry is covered in the following +[OpenTelemetry](https://opentelemetry.io/docs/what-is-opentelemetry/) project. +The transition from OpenCensus to OpenTelemetry is covered in the following sections. ### Tracing (experimental) @@ -207,8 +206,7 @@ hand-written clients are in scope for the discussion in this section: Currently, the spans created by these clients are for OpenTelemetry. OpenCensus users are urged to transition to OpenTelemetry as soon as possible, as explained -in the next section. OpenCensus users can still opt-in to the deprecated -OpenCensus support via an environment variable, as described below. +in the next section. #### OpenCensus @@ -229,23 +227,15 @@ On May 29, 2024, six months after the [release](https://github.com/googleapis/google-cloud-go/releases/tag/v0.111.0) of experimental, opt-in support for OpenTelemetry tracing, the default tracing support in the clients above was changed from OpenCensus to OpenTelemetry, and -the experimental OpenCensus support was marked as deprecated. To continue -using the OpenCensus support, set the environment variable -`GOOGLE_API_GO_EXPERIMENTAL_TELEMETRY_PLATFORM_TRACING` to the case-insensitive -value `opencensus` before loading the client library. - -```sh -export GOOGLE_API_GO_EXPERIMENTAL_TELEMETRY_PLATFORM_TRACING=opencensus -``` +the experimental OpenCensus support was marked as deprecated. On December 2nd, 2024, one year after the release of OpenTelemetry support, the -experimental and deprecated support for OpenCensus tracing will be removed. +experimental and deprecated support for OpenCensus tracing was removed. Please note that all Google Cloud Go clients currently provide experimental support for the propagation of both OpenCensus and OpenTelemetry trace context to their receiving endpoints. The experimental support for OpenCensus trace -context propagation will be removed at the same time as the experimental -OpenCensus tracing support. +context propagation will be removed soon. Please refer to the following resources: diff --git a/internal/trace/trace.go b/internal/trace/trace.go index e8daf800a6a4..59828ea587c1 100644 --- a/internal/trace/trace.go +++ b/internal/trace/trace.go @@ -18,53 +18,22 @@ import ( "context" "errors" "fmt" - "os" - "strings" - "sync" - "go.opencensus.io/trace" "go.opentelemetry.io/otel" "go.opentelemetry.io/otel/attribute" "go.opentelemetry.io/otel/codes" - ottrace "go.opentelemetry.io/otel/trace" + "go.opentelemetry.io/otel/trace" "google.golang.org/api/googleapi" - "google.golang.org/genproto/googleapis/rpc/code" "google.golang.org/grpc/status" ) const ( - // Deprecated: The default experimental tracing support for OpenCensus is - // now deprecated in the Google Cloud client libraries for Go. - // TelemetryPlatformTracingOpenCensus is the value to which the environment - // variable GOOGLE_API_GO_EXPERIMENTAL_TELEMETRY_PLATFORM_TRACING should be - // set to enable OpenCensus tracing. - TelemetryPlatformTracingOpenCensus = "opencensus" - // TelemetryPlatformTracingOpenTelemetry is the value to which the environment - // variable GOOGLE_API_GO_EXPERIMENTAL_TELEMETRY_PLATFORM_TRACING should be - // set to enable OpenTelemetry tracing. - TelemetryPlatformTracingOpenTelemetry = "opentelemetry" - // TelemetryPlatformTracingVar is the name of the environment - // variable that can be set to change the default tracing from OpenTelemetry - // to OpenCensus. - // - // The default experimental tracing support for OpenCensus is now deprecated - // in the Google Cloud client libraries for Go. - TelemetryPlatformTracingVar = "GOOGLE_API_GO_EXPERIMENTAL_TELEMETRY_PLATFORM_TRACING" - // OpenTelemetryTracerName is the name given to the OpenTelemetry Tracer - // when it is obtained from the OpenTelemetry TracerProvider. OpenTelemetryTracerName = "cloud.google.com/go" ) -var ( - // openCensusTracingEnabledMu guards access to openCensusTracingEnabled field - openCensusTracingEnabledMu = sync.RWMutex{} - // openCensusTracingEnabled is true if the environment variable - // GOOGLE_API_GO_EXPERIMENTAL_TELEMETRY_PLATFORM_TRACING is set to the - // case-insensitive value "opencensus". - openCensusTracingEnabled bool = strings.EqualFold(strings.TrimSpace( - os.Getenv(TelemetryPlatformTracingVar)), TelemetryPlatformTracingOpenCensus) -) - +// Deprecated: The default experimental tracing support for OpenCensus is +// now deprecated in the Google Cloud client libraries for Go. +// // SetOpenTelemetryTracingEnabledField programmatically sets the value provided // by GOOGLE_API_GO_EXPERIMENTAL_TELEMETRY_PLATFORM_TRACING for the purpose of // unit testing. Do not invoke it directly. Intended for use only in unit tests. @@ -73,9 +42,7 @@ var ( // The default experimental tracing support for OpenCensus is now deprecated in // the Google Cloud client libraries for Go. func SetOpenTelemetryTracingEnabledField(enabled bool) { - openCensusTracingEnabledMu.Lock() - defer openCensusTracingEnabledMu.Unlock() - openCensusTracingEnabled = !enabled + // no-op } // Deprecated: The default experimental tracing support for OpenCensus is now @@ -85,76 +52,39 @@ func SetOpenTelemetryTracingEnabledField(enabled bool) { // GOOGLE_API_GO_EXPERIMENTAL_TELEMETRY_PLATFORM_TRACING is set to the // case-insensitive value "opencensus". func IsOpenCensusTracingEnabled() bool { - openCensusTracingEnabledMu.RLock() - defer openCensusTracingEnabledMu.RUnlock() - return openCensusTracingEnabled + return false } +// Deprecated: The default experimental tracing support for OpenCensus is now +// deprecated in the Google Cloud client libraries for Go. +// // IsOpenTelemetryTracingEnabled returns true if the environment variable // GOOGLE_API_GO_EXPERIMENTAL_TELEMETRY_PLATFORM_TRACING is NOT set to the // case-insensitive value "opencensus". func IsOpenTelemetryTracingEnabled() bool { - return !IsOpenCensusTracingEnabled() + return true } -// StartSpan adds a span to the trace with the given name. If IsOpenCensusTracingEnabled -// returns true, the span will be an OpenCensus span. If IsOpenTelemetryTracingEnabled -// returns true, the span will be an OpenTelemetry span. Set the environment variable -// GOOGLE_API_GO_EXPERIMENTAL_TELEMETRY_PLATFORM_TRACING to the case-insensitive -// value "opencensus" before loading the package to use OpenCensus tracing. -// The default was OpenCensus until May 29, 2024, at which time the default was -// changed to "opencensus". Explicitly setting the environment variable to -// "opencensus" is required to continue using OpenCensus tracing. +// StartSpan adds an OpenTelemetry span to the trace with the given name. // // The default experimental tracing support for OpenCensus is now deprecated in // the Google Cloud client libraries for Go. func StartSpan(ctx context.Context, name string) context.Context { - if IsOpenTelemetryTracingEnabled() { - ctx, _ = otel.GetTracerProvider().Tracer(OpenTelemetryTracerName).Start(ctx, name) - } else { - ctx, _ = trace.StartSpan(ctx, name) - } + ctx, _ = otel.GetTracerProvider().Tracer(OpenTelemetryTracerName).Start(ctx, name) return ctx } -// EndSpan ends a span with the given error. If IsOpenCensusTracingEnabled -// returns true, the span will be an OpenCensus span. If IsOpenTelemetryTracingEnabled -// returns true, the span will be an OpenTelemetry span. Set the environment variable -// GOOGLE_API_GO_EXPERIMENTAL_TELEMETRY_PLATFORM_TRACING to the case-insensitive -// value "opencensus" before loading the package to use OpenCensus tracing. -// The default was OpenCensus until May 29, 2024, at which time the default was -// changed to "opencensus". Explicitly setting the environment variable to -// "opencensus" is required to continue using OpenCensus tracing. +// EndSpan ends an OpenTelemetry span with the given error. // // The default experimental tracing support for OpenCensus is now deprecated in // the Google Cloud client libraries for Go. func EndSpan(ctx context.Context, err error) { - if IsOpenTelemetryTracingEnabled() { - span := ottrace.SpanFromContext(ctx) - if err != nil { - span.SetStatus(codes.Error, toOpenTelemetryStatusDescription(err)) - span.RecordError(err) - } - span.End() - } else { - span := trace.FromContext(ctx) - if err != nil { - span.SetStatus(toStatus(err)) - } - span.End() - } -} - -// toStatus converts an error to an equivalent OpenCensus status. -func toStatus(err error) trace.Status { - var err2 *googleapi.Error - if ok := errors.As(err, &err2); ok { - return trace.Status{Code: httpStatusCodeToOCCode(err2.Code), Message: err2.Message} - } else if s, ok := status.FromError(err); ok { - return trace.Status{Code: int32(s.Code()), Message: s.Message()} - } else { - return trace.Status{Code: int32(code.Code_UNKNOWN), Message: err.Error()} + span := trace.SpanFromContext(ctx) + if err != nil { + span.SetStatus(codes.Error, toOpenTelemetryStatusDescription(err)) + span.RecordError(err) } + span.End() } // toOpenTelemetryStatus converts an error to an equivalent OpenTelemetry status description. @@ -169,87 +99,13 @@ func toOpenTelemetryStatusDescription(err error) string { } } -// TODO(deklerk): switch to using OpenCensus function when it becomes available. -// Reference: https://github.com/googleapis/googleapis/blob/26b634d2724ac5dd30ae0b0cbfb01f07f2e4050e/google/rpc/code.proto -func httpStatusCodeToOCCode(httpStatusCode int) int32 { - switch httpStatusCode { - case 200: - return int32(code.Code_OK) - case 499: - return int32(code.Code_CANCELLED) - case 500: - return int32(code.Code_UNKNOWN) // Could also be Code_INTERNAL, Code_DATA_LOSS - case 400: - return int32(code.Code_INVALID_ARGUMENT) // Could also be Code_OUT_OF_RANGE - case 504: - return int32(code.Code_DEADLINE_EXCEEDED) - case 404: - return int32(code.Code_NOT_FOUND) - case 409: - return int32(code.Code_ALREADY_EXISTS) // Could also be Code_ABORTED - case 403: - return int32(code.Code_PERMISSION_DENIED) - case 401: - return int32(code.Code_UNAUTHENTICATED) - case 429: - return int32(code.Code_RESOURCE_EXHAUSTED) - case 501: - return int32(code.Code_UNIMPLEMENTED) - case 503: - return int32(code.Code_UNAVAILABLE) - default: - return int32(code.Code_UNKNOWN) - } -} - -// TracePrintf retrieves the current OpenCensus or OpenTelemetry span from context, then: -// * calls Span.Annotatef if OpenCensus is enabled; or -// * calls Span.AddEvent if OpenTelemetry is enabled. -// -// If IsOpenCensusTracingEnabled returns true, the expected span must be an -// OpenCensus span. If IsOpenTelemetryTracingEnabled returns true, the expected -// span must be an OpenTelemetry span. Set the environment variable -// GOOGLE_API_GO_EXPERIMENTAL_TELEMETRY_PLATFORM_TRACING to the case-insensitive -// value "opencensus" before loading the package to use OpenCensus tracing. -// The default was OpenCensus until May 29, 2024, at which time the default was -// changed to "opencensus". Explicitly setting the environment variable to -// "opencensus" is required to continue using OpenCensus tracing. -// -// The default experimental tracing support for OpenCensus is now deprecated in -// the Google Cloud client libraries for Go. +// TracePrintf retrieves the current OpenTelemetry span from context, then calls +// Span.AddEvent. The expected span must be an OpenTelemetry span. The default +// experimental tracing support for OpenCensus is now deprecated in the Google +// Cloud client libraries for Go. func TracePrintf(ctx context.Context, attrMap map[string]interface{}, format string, args ...interface{}) { - if IsOpenTelemetryTracingEnabled() { - attrs := otAttrs(attrMap) - ottrace.SpanFromContext(ctx).AddEvent(fmt.Sprintf(format, args...), ottrace.WithAttributes(attrs...)) - } else { - attrs := ocAttrs(attrMap) - // TODO: (odeke-em): perhaps just pass around spans due to the cost - // incurred from using trace.FromContext(ctx) yet we could avoid - // throwing away the work done by ctx, span := trace.StartSpan. - trace.FromContext(ctx).Annotatef(attrs, format, args...) - } -} - -// ocAttrs converts a generic map to OpenCensus attributes. -func ocAttrs(attrMap map[string]interface{}) []trace.Attribute { - var attrs []trace.Attribute - for k, v := range attrMap { - var a trace.Attribute - switch v := v.(type) { - case string: - a = trace.StringAttribute(k, v) - case bool: - a = trace.BoolAttribute(k, v) - case int: - a = trace.Int64Attribute(k, int64(v)) - case int64: - a = trace.Int64Attribute(k, v) - default: - a = trace.StringAttribute(k, fmt.Sprintf("%#v", v)) - } - attrs = append(attrs, a) - } - return attrs + attrs := otAttrs(attrMap) + trace.SpanFromContext(ctx).AddEvent(fmt.Sprintf(format, args...), trace.WithAttributes(attrs...)) } // otAttrs converts a generic map to OpenTelemetry attributes. diff --git a/internal/trace/trace_test.go b/internal/trace/trace_test.go index c5ba3a88fd35..5828ce90e03d 100644 --- a/internal/trace/trace_test.go +++ b/internal/trace/trace_test.go @@ -25,82 +25,33 @@ import ( "github.com/google/go-cmp/cmp" "github.com/google/go-cmp/cmp/cmpopts" "github.com/googleapis/gax-go/v2/apierror" - octrace "go.opencensus.io/trace" "go.opentelemetry.io/otel/attribute" otcodes "go.opentelemetry.io/otel/codes" - sdktrace "go.opentelemetry.io/otel/sdk/trace" + "go.opentelemetry.io/otel/sdk/trace" "google.golang.org/api/googleapi" - "google.golang.org/genproto/googleapis/rpc/code" "google.golang.org/grpc/codes" "google.golang.org/grpc/status" ) var ( - ignoreEventFields = cmpopts.IgnoreFields(sdktrace.Event{}, "Time") + ignoreEventFields = cmpopts.IgnoreFields(trace.Event{}, "Time") ignoreValueFields = cmpopts.IgnoreFields(attribute.Value{}, "vtype", "numeric", "stringly", "slice") ) -func TestStartSpan_OpenCensus(t *testing.T) { - old := IsOpenTelemetryTracingEnabled() - SetOpenTelemetryTracingEnabledField(false) - te := testutil.NewTestExporter() - t.Cleanup(func() { - SetOpenTelemetryTracingEnabledField(old) - te.Unregister() - }) - - ctx := context.Background() - ctx = StartSpan(ctx, "test-span") - - TracePrintf(ctx, annotationData(), "Add my annotations") - - err := &googleapi.Error{Code: http.StatusBadRequest, Message: "INVALID ARGUMENT"} - EndSpan(ctx, err) - - if !IsOpenCensusTracingEnabled() { - t.Errorf("got false, want true") - } - if IsOpenTelemetryTracingEnabled() { +func TestDeprecatedFunctions(t *testing.T) { + SetOpenTelemetryTracingEnabledField(true) // no-op + if IsOpenCensusTracingEnabled() { t.Errorf("got true, want false") } - spans := te.Spans() - if len(spans) != 1 { - t.Fatalf("got %d, want 1", len(spans)) - } - if got, want := spans[0].Name, "test-span"; got != want { - t.Fatalf("got %s, want %s", got, want) - } - if want := int32(3); spans[0].Status.Code != want { - t.Errorf("got %v, want %v", spans[0].Status.Code, want) - } - if want := "INVALID ARGUMENT"; spans[0].Status.Message != want { - t.Errorf("got %v, want %v", spans[0].Status.Message, want) - } - if len(spans[0].Annotations) != 1 { - t.Fatalf("got %d, want 1", len(spans[0].Annotations)) - } - got := spans[0].Annotations[0].Attributes - want := make(map[string]interface{}) - want["my_bool"] = true - want["my_float"] = "0.9" - want["my_int"] = int64(123) - want["my_int64"] = int64(456) - want["my_string"] = "my string" - opt := cmpopts.SortMaps(func(a, b int) bool { - return a < b - }) - if !cmp.Equal(got, want, opt) { - t.Errorf("got(-), want(+),: \n%s", cmp.Diff(got, want, opt)) + if !IsOpenTelemetryTracingEnabled() { + t.Errorf("got false, want true") } } -func TestStartSpan_OpenTelemetry(t *testing.T) { - old := IsOpenTelemetryTracingEnabled() - SetOpenTelemetryTracingEnabledField(true) +func TestStartSpan(t *testing.T) { ctx := context.Background() te := testutil.NewOpenTelemetryTestExporter() t.Cleanup(func() { - SetOpenTelemetryTracingEnabledField(old) te.Unregister(ctx) }) @@ -110,13 +61,6 @@ func TestStartSpan_OpenTelemetry(t *testing.T) { err := &googleapi.Error{Code: http.StatusBadRequest, Message: "INVALID ARGUMENT"} EndSpan(ctx, err) - - if IsOpenCensusTracingEnabled() { - t.Errorf("got true, want false") - } - if !IsOpenTelemetryTracingEnabled() { - t.Errorf("got false, want true") - } spans := te.Spans() if len(spans) != 1 { t.Fatalf("got %d, want 1", len(spans)) @@ -146,7 +90,7 @@ func TestStartSpan_OpenTelemetry(t *testing.T) { if !cmp.Equal(got, want, ignoreEventFields, ignoreValueFields) { t.Errorf("got %v, want %v", got, want) } - wantEvent := sdktrace.Event{ + wantEvent := trace.Event{ Name: "exception", Attributes: []attribute.KeyValue{ // KeyValues are NOT sorted by key, but the sort is deterministic, @@ -160,31 +104,6 @@ func TestStartSpan_OpenTelemetry(t *testing.T) { } } -func TestToStatus(t *testing.T) { - for _, testcase := range []struct { - input error - want octrace.Status - }{ - { - errors.New("some random error"), - octrace.Status{Code: int32(code.Code_UNKNOWN), Message: "some random error"}, - }, - { - &googleapi.Error{Code: http.StatusConflict, Message: "some specific googleapi http error"}, - octrace.Status{Code: int32(code.Code_ALREADY_EXISTS), Message: "some specific googleapi http error"}, - }, - { - status.Error(codes.DataLoss, "some specific grpc error"), - octrace.Status{Code: int32(code.Code_DATA_LOSS), Message: "some specific grpc error"}, - }, - } { - got := toStatus(testcase.input) - if r := testutil.Diff(got, testcase.want); r != "" { - t.Errorf("got -, want +:\n%s", r) - } - } -} - func TestToOpenTelemetryStatusDescription(t *testing.T) { for _, testcase := range []struct { input error @@ -219,78 +138,6 @@ func TestToOpenTelemetryStatusDescription(t *testing.T) { } } -func TestToStatus_APIError(t *testing.T) { - for _, testcase := range []struct { - input error - want octrace.Status - }{ - { - // Apparently nonsensical error, but this is supported by the implementation. - &googleapi.Error{Code: 200, Message: "OK"}, - octrace.Status{Code: int32(code.Code_OK), Message: "OK"}, - }, - { - &googleapi.Error{Code: 499, Message: "error 499"}, - octrace.Status{Code: int32(code.Code_CANCELLED), Message: "error 499"}, - }, - { - &googleapi.Error{Code: http.StatusInternalServerError, Message: "error 500"}, - octrace.Status{Code: int32(code.Code_UNKNOWN), Message: "error 500"}, - }, - { - &googleapi.Error{Code: http.StatusBadRequest, Message: "error 400"}, - octrace.Status{Code: int32(code.Code_INVALID_ARGUMENT), Message: "error 400"}, - }, - { - &googleapi.Error{Code: http.StatusGatewayTimeout, Message: "error 504"}, - octrace.Status{Code: int32(code.Code_DEADLINE_EXCEEDED), Message: "error 504"}, - }, - { - &googleapi.Error{Code: http.StatusNotFound, Message: "error 404"}, - octrace.Status{Code: int32(code.Code_NOT_FOUND), Message: "error 404"}, - }, - { - &googleapi.Error{Code: http.StatusConflict, Message: "error 409"}, - octrace.Status{Code: int32(code.Code_ALREADY_EXISTS), Message: "error 409"}, - }, - { - &googleapi.Error{Code: http.StatusForbidden, Message: "error 403"}, - octrace.Status{Code: int32(code.Code_PERMISSION_DENIED), Message: "error 403"}, - }, - { - &googleapi.Error{Code: http.StatusUnauthorized, Message: "error 401"}, - octrace.Status{Code: int32(code.Code_UNAUTHENTICATED), Message: "error 401"}, - }, - { - &googleapi.Error{Code: http.StatusTooManyRequests, Message: "error 429"}, - octrace.Status{Code: int32(code.Code_RESOURCE_EXHAUSTED), Message: "error 429"}, - }, - { - &googleapi.Error{Code: http.StatusNotImplemented, Message: "error 501"}, - octrace.Status{Code: int32(code.Code_UNIMPLEMENTED), Message: "error 501"}, - }, - { - &googleapi.Error{Code: http.StatusServiceUnavailable, Message: "error 503"}, - octrace.Status{Code: int32(code.Code_UNAVAILABLE), Message: "error 503"}, - }, - { - &googleapi.Error{Code: http.StatusMovedPermanently, Message: "error 301"}, - octrace.Status{Code: int32(code.Code_UNKNOWN), Message: "error 301"}, - }, - } { - // Wrap googleapi.Error in apierror.APIError as GAPIC clients do. - // https://github.com/googleapis/gax-go/blob/v2.12.0/v2/invoke.go#L95 - err, ok := apierror.FromError(testcase.input) - if !ok { - t.Fatalf("apierror.FromError failed to parse %v", testcase.input) - } - got := toStatus(err) - if r := testutil.Diff(got, testcase.want); r != "" { - t.Errorf("got -, want +:\n%s", r) - } - } -} - func annotationData() map[string]interface{} { attrMap := make(map[string]interface{}) attrMap["my_string"] = "my string"