2022-07-07 20:11:50 +00:00
|
|
|
// Copyright 2018 Google LLC.
|
|
|
|
// Use of this source code is governed by a BSD-style
|
|
|
|
// license that can be found in the LICENSE file.
|
|
|
|
|
2024-02-24 00:34:55 +00:00
|
|
|
//go:build go1.8
|
2022-07-07 20:11:50 +00:00
|
|
|
// +build go1.8
|
|
|
|
|
|
|
|
// Package propagation implements X-Cloud-Trace-Context header propagation used
|
|
|
|
// by Google Cloud products.
|
|
|
|
package propagation
|
|
|
|
|
|
|
|
import (
|
|
|
|
"encoding/binary"
|
|
|
|
"encoding/hex"
|
|
|
|
"fmt"
|
|
|
|
"net/http"
|
|
|
|
"strconv"
|
|
|
|
"strings"
|
|
|
|
|
|
|
|
"go.opencensus.io/trace"
|
|
|
|
"go.opencensus.io/trace/propagation"
|
|
|
|
)
|
|
|
|
|
|
|
|
const (
|
|
|
|
httpHeaderMaxSize = 200
|
|
|
|
httpHeader = `X-Cloud-Trace-Context`
|
|
|
|
)
|
|
|
|
|
|
|
|
var _ propagation.HTTPFormat = (*HTTPFormat)(nil)
|
|
|
|
|
|
|
|
// HTTPFormat implements propagation.HTTPFormat to propagate
|
|
|
|
// traces in HTTP headers for Google Cloud Platform and Stackdriver Trace.
|
|
|
|
type HTTPFormat struct{}
|
|
|
|
|
|
|
|
// SpanContextFromRequest extracts a Stackdriver Trace span context from incoming requests.
|
|
|
|
func (f *HTTPFormat) SpanContextFromRequest(req *http.Request) (sc trace.SpanContext, ok bool) {
|
|
|
|
h := req.Header.Get(httpHeader)
|
|
|
|
// See https://cloud.google.com/trace/docs/faq for the header HTTPFormat.
|
|
|
|
// Return if the header is empty or missing, or if the header is unreasonably
|
|
|
|
// large, to avoid making unnecessary copies of a large string.
|
|
|
|
if h == "" || len(h) > httpHeaderMaxSize {
|
|
|
|
return trace.SpanContext{}, false
|
|
|
|
}
|
|
|
|
|
|
|
|
// Parse the trace id field.
|
|
|
|
slash := strings.Index(h, `/`)
|
|
|
|
if slash == -1 {
|
|
|
|
return trace.SpanContext{}, false
|
|
|
|
}
|
|
|
|
tid, h := h[:slash], h[slash+1:]
|
|
|
|
|
|
|
|
buf, err := hex.DecodeString(tid)
|
|
|
|
if err != nil {
|
|
|
|
return trace.SpanContext{}, false
|
|
|
|
}
|
|
|
|
copy(sc.TraceID[:], buf)
|
|
|
|
|
|
|
|
// Parse the span id field.
|
|
|
|
spanstr := h
|
|
|
|
semicolon := strings.Index(h, `;`)
|
|
|
|
if semicolon != -1 {
|
|
|
|
spanstr, h = h[:semicolon], h[semicolon+1:]
|
|
|
|
}
|
|
|
|
sid, err := strconv.ParseUint(spanstr, 10, 64)
|
|
|
|
if err != nil {
|
|
|
|
return trace.SpanContext{}, false
|
|
|
|
}
|
|
|
|
binary.BigEndian.PutUint64(sc.SpanID[:], sid)
|
|
|
|
|
|
|
|
// Parse the options field, options field is optional.
|
|
|
|
if !strings.HasPrefix(h, "o=") {
|
|
|
|
return sc, true
|
|
|
|
}
|
|
|
|
o, err := strconv.ParseUint(h[2:], 10, 64)
|
|
|
|
if err != nil {
|
|
|
|
return trace.SpanContext{}, false
|
|
|
|
}
|
|
|
|
sc.TraceOptions = trace.TraceOptions(o)
|
|
|
|
return sc, true
|
|
|
|
}
|
|
|
|
|
|
|
|
// SpanContextToRequest modifies the given request to include a Stackdriver Trace header.
|
|
|
|
func (f *HTTPFormat) SpanContextToRequest(sc trace.SpanContext, req *http.Request) {
|
|
|
|
sid := binary.BigEndian.Uint64(sc.SpanID[:])
|
|
|
|
header := fmt.Sprintf("%s/%d;o=%d", hex.EncodeToString(sc.TraceID[:]), sid, int64(sc.TraceOptions))
|
|
|
|
req.Header.Set(httpHeader, header)
|
|
|
|
}
|