You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
177 lines
4.4 KiB
177 lines
4.4 KiB
package trace |
|
|
|
import ( |
|
"net/http" |
|
|
|
"github.com/pkg/errors" |
|
"google.golang.org/grpc/metadata" |
|
) |
|
|
|
var ( |
|
// ErrUnsupportedFormat occurs when the `format` passed to Tracer.Inject() or |
|
// Tracer.Extract() is not recognized by the Tracer implementation. |
|
ErrUnsupportedFormat = errors.New("trace: Unknown or unsupported Inject/Extract format") |
|
|
|
// ErrTraceNotFound occurs when the `carrier` passed to |
|
// Tracer.Extract() is valid and uncorrupted but has insufficient |
|
// information to extract a Trace. |
|
ErrTraceNotFound = errors.New("trace: Trace not found in Extract carrier") |
|
|
|
// ErrInvalidTrace errors occur when Tracer.Inject() is asked to |
|
// operate on a Trace which it is not prepared to handle (for |
|
// example, since it was created by a different tracer implementation). |
|
ErrInvalidTrace = errors.New("trace: Trace type incompatible with tracer") |
|
|
|
// ErrInvalidCarrier errors occur when Tracer.Inject() or Tracer.Extract() |
|
// implementations expect a different type of `carrier` than they are |
|
// given. |
|
ErrInvalidCarrier = errors.New("trace: Invalid Inject/Extract carrier") |
|
|
|
// ErrTraceCorrupted occurs when the `carrier` passed to |
|
// Tracer.Extract() is of the expected type but is corrupted. |
|
ErrTraceCorrupted = errors.New("trace: Trace data corrupted in Extract carrier") |
|
) |
|
|
|
// BuiltinFormat is used to demarcate the values within package `trace` |
|
// that are intended for use with the Tracer.Inject() and Tracer.Extract() |
|
// methods. |
|
type BuiltinFormat byte |
|
|
|
// support format list |
|
const ( |
|
// HTTPFormat represents Trace as HTTP header string pairs. |
|
// |
|
// the HTTPFormat format requires that the keys and values |
|
// be valid as HTTP headers as-is (i.e., character casing may be unstable |
|
// and special characters are disallowed in keys, values should be |
|
// URL-escaped, etc). |
|
// |
|
// the carrier must be a `http.Header`. |
|
HTTPFormat BuiltinFormat = iota |
|
// GRPCFormat represents Trace as gRPC metadata. |
|
// |
|
// the carrier must be a `google.golang.org/grpc/metadata.MD`. |
|
GRPCFormat |
|
) |
|
|
|
// Carrier propagator must convert generic interface{} to something this |
|
// implement Carrier interface, Trace can use Carrier to represents itself. |
|
type Carrier interface { |
|
Set(key, val string) |
|
Get(key string) string |
|
} |
|
|
|
// propagator is responsible for injecting and extracting `Trace` instances |
|
// from a format-specific "carrier" |
|
type propagator interface { |
|
Inject(carrier interface{}) (Carrier, error) |
|
Extract(carrier interface{}) (Carrier, error) |
|
} |
|
|
|
type httpPropagator struct{} |
|
|
|
type httpCarrier http.Header |
|
|
|
func (h httpCarrier) Set(key, val string) { |
|
http.Header(h).Set(key, val) |
|
} |
|
|
|
func (h httpCarrier) Get(key string) string { |
|
return http.Header(h).Get(key) |
|
} |
|
|
|
func (httpPropagator) Inject(carrier interface{}) (Carrier, error) { |
|
header, ok := carrier.(http.Header) |
|
if !ok { |
|
return nil, ErrInvalidCarrier |
|
} |
|
if header == nil { |
|
return nil, ErrInvalidTrace |
|
} |
|
return httpCarrier(header), nil |
|
} |
|
|
|
func (httpPropagator) Extract(carrier interface{}) (Carrier, error) { |
|
header, ok := carrier.(http.Header) |
|
if !ok { |
|
return nil, ErrInvalidCarrier |
|
} |
|
if header == nil { |
|
return nil, ErrTraceNotFound |
|
} |
|
return httpCarrier(header), nil |
|
} |
|
|
|
const legacyGRPCKey = "trace" |
|
|
|
type grpcPropagator struct{} |
|
|
|
type grpcCarrier map[string][]string |
|
|
|
func (g grpcCarrier) Get(key string) string { |
|
if v, ok := g[key]; ok && len(v) > 0 { |
|
return v[0] |
|
} |
|
ts := g[legacyGRPCKey] |
|
if len(ts) != 8 { |
|
return "" |
|
} |
|
switch key { |
|
case KeyTraceID: |
|
return ts[0] |
|
case KeyTraceSpanID: |
|
return ts[1] |
|
case KeyTraceParentID: |
|
return ts[2] |
|
case KeyTraceLevel: |
|
return ts[3] |
|
case KeyTraceSampled: |
|
return ts[4] |
|
case KeyTraceCaller: |
|
return ts[5] |
|
} |
|
return "" |
|
} |
|
|
|
func (g grpcCarrier) Set(key, val string) { |
|
ts := make([]string, 8) |
|
g[legacyGRPCKey] = ts |
|
switch key { |
|
case KeyTraceID: |
|
ts[0] = val |
|
case KeyTraceSpanID: |
|
ts[1] = val |
|
case KeyTraceParentID: |
|
ts[2] = val |
|
case KeyTraceLevel: |
|
ts[3] = val |
|
case KeyTraceSampled: |
|
ts[4] = val |
|
case KeyTraceCaller: |
|
ts[5] = val |
|
default: |
|
g[key] = append(g[key], val) |
|
} |
|
} |
|
|
|
func (grpcPropagator) Inject(carrier interface{}) (Carrier, error) { |
|
md, ok := carrier.(metadata.MD) |
|
if !ok { |
|
return nil, ErrInvalidCarrier |
|
} |
|
if md == nil { |
|
return nil, ErrInvalidTrace |
|
} |
|
return grpcCarrier(md), nil |
|
} |
|
|
|
func (grpcPropagator) Extract(carrier interface{}) (Carrier, error) { |
|
md, ok := carrier.(metadata.MD) |
|
if !ok { |
|
return nil, ErrInvalidCarrier |
|
} |
|
if md == nil { |
|
return nil, ErrTraceNotFound |
|
} |
|
return grpcCarrier(md), nil |
|
}
|
|
|