google.golang.org/grpc@v1.72.2/stats/opentelemetry/grpc_trace_bin_propagator.go (about)

     1  /*
     2   *
     3   * Copyright 2024 gRPC authors.
     4   *
     5   * Licensed under the Apache License, Version 2.0 (the "License");
     6   * you may not use this file except in compliance with the License.
     7   * You may obtain a copy of the License at
     8   *
     9   *     http://www.apache.org/licenses/LICENSE-2.0
    10   *
    11   * Unless required by applicable law or agreed to in writing, software
    12   * distributed under the License is distributed on an "AS IS" BASIS,
    13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    14   * See the License for the specific language governing permissions and
    15   * limitations under the License.
    16   *
    17   */
    18  
    19  package opentelemetry
    20  
    21  import (
    22  	"context"
    23  
    24  	otelpropagation "go.opentelemetry.io/otel/propagation"
    25  	oteltrace "go.opentelemetry.io/otel/trace"
    26  )
    27  
    28  // gRPCTraceBinHeaderKey is the gRPC metadata header key `grpc-trace-bin` used
    29  // to propagate trace context in binary format.
    30  const grpcTraceBinHeaderKey = "grpc-trace-bin"
    31  
    32  // GRPCTraceBinPropagator is an OpenTelemetry TextMapPropagator which is used
    33  // to extract and inject trace context data from and into headers exchanged by
    34  // gRPC applications. It propagates trace data in binary format using the
    35  // `grpc-trace-bin` header.
    36  type GRPCTraceBinPropagator struct{}
    37  
    38  // Inject sets OpenTelemetry span context from the Context into the carrier as
    39  // a `grpc-trace-bin` header if span context is valid.
    40  //
    41  // If span context is not valid, it returns without setting `grpc-trace-bin`
    42  // header.
    43  func (GRPCTraceBinPropagator) Inject(ctx context.Context, carrier otelpropagation.TextMapCarrier) {
    44  	sc := oteltrace.SpanFromContext(ctx)
    45  	if !sc.SpanContext().IsValid() {
    46  		return
    47  	}
    48  
    49  	bd := toBinary(sc.SpanContext())
    50  	carrier.Set(grpcTraceBinHeaderKey, string(bd))
    51  }
    52  
    53  // Extract reads OpenTelemetry span context from the `grpc-trace-bin` header of
    54  // carrier into the provided context, if present.
    55  //
    56  // If a valid span context is retrieved from `grpc-trace-bin`, it returns a new
    57  // context containing the extracted OpenTelemetry span context marked as
    58  // remote.
    59  //
    60  // If `grpc-trace-bin` header is not present, it returns the context as is.
    61  func (GRPCTraceBinPropagator) Extract(ctx context.Context, carrier otelpropagation.TextMapCarrier) context.Context {
    62  	h := carrier.Get(grpcTraceBinHeaderKey)
    63  	if h == "" {
    64  		return ctx
    65  	}
    66  
    67  	sc, ok := fromBinary([]byte(h))
    68  	if !ok {
    69  		return ctx
    70  	}
    71  	return oteltrace.ContextWithRemoteSpanContext(ctx, sc)
    72  }
    73  
    74  // Fields returns the keys whose values are set with Inject.
    75  //
    76  // GRPCTraceBinPropagator always returns a slice containing only
    77  // `grpc-trace-bin` key because it only sets the `grpc-trace-bin` header for
    78  // propagating trace context.
    79  func (GRPCTraceBinPropagator) Fields() []string {
    80  	return []string{grpcTraceBinHeaderKey}
    81  }
    82  
    83  // toBinary returns the binary format representation of a SpanContext.
    84  //
    85  // If sc is the zero value, returns nil.
    86  func toBinary(sc oteltrace.SpanContext) []byte {
    87  	if sc.Equal(oteltrace.SpanContext{}) {
    88  		return nil
    89  	}
    90  	var b [29]byte
    91  	traceID := oteltrace.TraceID(sc.TraceID())
    92  	copy(b[2:18], traceID[:])
    93  	b[18] = 1
    94  	spanID := oteltrace.SpanID(sc.SpanID())
    95  	copy(b[19:27], spanID[:])
    96  	b[27] = 2
    97  	b[28] = byte(oteltrace.TraceFlags(sc.TraceFlags()))
    98  	return b[:]
    99  }
   100  
   101  // fromBinary returns the SpanContext represented by b with Remote set to true.
   102  //
   103  // It returns with zero value SpanContext and false, if any of the
   104  // below condition is not satisfied:
   105  // - Valid header: len(b) = 29
   106  // - Valid version: b[0] = 0
   107  // - Valid traceID prefixed with 0: b[1] = 0
   108  // - Valid spanID prefixed with 1: b[18] = 1
   109  // - Valid traceFlags prefixed with 2: b[27] = 2
   110  func fromBinary(b []byte) (oteltrace.SpanContext, bool) {
   111  	if len(b) != 29 || b[0] != 0 || b[1] != 0 || b[18] != 1 || b[27] != 2 {
   112  		return oteltrace.SpanContext{}, false
   113  	}
   114  
   115  	return oteltrace.SpanContext{}.WithTraceID(
   116  		oteltrace.TraceID(b[2:18])).WithSpanID(
   117  		oteltrace.SpanID(b[19:27])).WithTraceFlags(
   118  		oteltrace.TraceFlags(b[28])).WithRemote(true), true
   119  }