/* * Copyright 2024 gRPC authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package opentelemetry import ( "sync/atomic" "go.opentelemetry.io/otel/attribute" otelcodes "go.opentelemetry.io/otel/codes" "go.opentelemetry.io/otel/trace" "google.golang.org/grpc/stats" "google.golang.org/grpc/status" ) // populateSpan populates span information based on stats passed in, representing // invariants of the RPC lifecycle. It ends the span, triggering its export. // This function handles attempt spans on the client-side and call spans on the // server-side. func populateSpan(rs stats.RPCStats, ai *attemptInfo) { if ai == nil || ai.traceSpan == nil { // Shouldn't happen, tagRPC call comes before this function gets called // which populates this information. logger.Error("ctx passed into stats handler tracing event handling has no traceSpan present") return } span := ai.traceSpan switch rs := rs.(type) { case *stats.Begin: // Note: Go always added Client and FailFast attributes even though they are not // defined by the OpenCensus gRPC spec. Thus, they are unimportant for // correctness. span.SetAttributes( attribute.Bool("Client", rs.Client), attribute.Bool("FailFast", rs.FailFast), attribute.Int64("previous-rpc-attempts", int64(ai.previousRPCAttempts)), attribute.Bool("transparent-retry", rs.IsTransparentRetryAttempt), ) // increment previous rpc attempts applicable for next attempt atomic.AddUint32(&ai.previousRPCAttempts, 1) case *stats.PickerUpdated: span.AddEvent("Delayed LB pick complete") case *stats.InPayload: // message id - "must be calculated as two different counters starting // from one for sent messages and one for received messages." span.AddEvent("Inbound compressed message", trace.WithAttributes( attribute.Int64("sequence-number", int64(ai.countRecvMsg)), attribute.Int64("message-size", int64(rs.Length)), attribute.Int64("message-size-compressed", int64(rs.CompressedLength)), )) ai.countRecvMsg++ case *stats.OutPayload: span.AddEvent("Outbound compressed message", trace.WithAttributes( attribute.Int64("sequence-number", int64(ai.countSentMsg)), attribute.Int64("message-size", int64(rs.Length)), attribute.Int64("message-size-compressed", int64(rs.CompressedLength)), )) ai.countSentMsg++ case *stats.End: if rs.Error != nil { s := status.Convert(rs.Error) span.SetStatus(otelcodes.Error, s.Message()) } else { span.SetStatus(otelcodes.Ok, "Ok") } span.End() } }