trace.go 2.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293
  1. // Copyright 2023 Google LLC
  2. //
  3. // Licensed under the Apache License, Version 2.0 (the "License");
  4. // you may not use this file except in compliance with the License.
  5. // You may obtain a copy of the License at
  6. //
  7. // http://www.apache.org/licenses/LICENSE-2.0
  8. //
  9. // Unless required by applicable law or agreed to in writing, software
  10. // distributed under the License is distributed on an "AS IS" BASIS,
  11. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. // See the License for the specific language governing permissions and
  13. // limitations under the License.
  14. package httptransport
  15. import (
  16. "encoding/binary"
  17. "encoding/hex"
  18. "fmt"
  19. "net/http"
  20. "strconv"
  21. "strings"
  22. "go.opencensus.io/trace"
  23. "go.opencensus.io/trace/propagation"
  24. )
  25. const (
  26. httpHeaderMaxSize = 200
  27. cloudTraceHeader = `X-Cloud-Trace-Context`
  28. )
  29. // asserts the httpFormat fulfills this foreign interface
  30. var _ propagation.HTTPFormat = (*httpFormat)(nil)
  31. // httpFormat implements propagation.httpFormat to propagate
  32. // traces in HTTP headers for Google Cloud Platform and Cloud Trace.
  33. type httpFormat struct{}
  34. // SpanContextFromRequest extracts a Cloud Trace span context from incoming requests.
  35. func (f *httpFormat) SpanContextFromRequest(req *http.Request) (sc trace.SpanContext, ok bool) {
  36. h := req.Header.Get(cloudTraceHeader)
  37. // See https://cloud.google.com/trace/docs/faq for the header HTTPFormat.
  38. // Return if the header is empty or missing, or if the header is unreasonably
  39. // large, to avoid making unnecessary copies of a large string.
  40. if h == "" || len(h) > httpHeaderMaxSize {
  41. return trace.SpanContext{}, false
  42. }
  43. // Parse the trace id field.
  44. slash := strings.Index(h, `/`)
  45. if slash == -1 {
  46. return trace.SpanContext{}, false
  47. }
  48. tid, h := h[:slash], h[slash+1:]
  49. buf, err := hex.DecodeString(tid)
  50. if err != nil {
  51. return trace.SpanContext{}, false
  52. }
  53. copy(sc.TraceID[:], buf)
  54. // Parse the span id field.
  55. spanstr := h
  56. semicolon := strings.Index(h, `;`)
  57. if semicolon != -1 {
  58. spanstr, h = h[:semicolon], h[semicolon+1:]
  59. }
  60. sid, err := strconv.ParseUint(spanstr, 10, 64)
  61. if err != nil {
  62. return trace.SpanContext{}, false
  63. }
  64. binary.BigEndian.PutUint64(sc.SpanID[:], sid)
  65. // Parse the options field, options field is optional.
  66. if !strings.HasPrefix(h, "o=") {
  67. return sc, true
  68. }
  69. o, err := strconv.ParseUint(h[2:], 10, 32)
  70. if err != nil {
  71. return trace.SpanContext{}, false
  72. }
  73. sc.TraceOptions = trace.TraceOptions(o)
  74. return sc, true
  75. }
  76. // SpanContextToRequest modifies the given request to include a Cloud Trace header.
  77. func (f *httpFormat) SpanContextToRequest(sc trace.SpanContext, req *http.Request) {
  78. sid := binary.BigEndian.Uint64(sc.SpanID[:])
  79. header := fmt.Sprintf("%s/%d;o=%d", hex.EncodeToString(sc.TraceID[:]), sid, int64(sc.TraceOptions))
  80. req.Header.Set(cloudTraceHeader, header)
  81. }