mirror of
https://github.com/go-kratos/kratos.git
synced 2025-03-17 21:07:54 +02:00
fix import pkg (#651)
This commit is contained in:
parent
25db576747
commit
f831dc0b55
4
go.mod
4
go.mod
@ -34,6 +34,7 @@ require (
|
||||
github.com/mattn/go-isatty v0.0.10 // indirect
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
||||
github.com/montanaflynn/stats v0.5.0
|
||||
github.com/opentracing/opentracing-go v1.1.0
|
||||
github.com/openzipkin/zipkin-go v0.2.1
|
||||
github.com/otokaze/mock v0.0.0-20190125081256-8282b7a7c7c3
|
||||
github.com/philchia/agollo v0.0.0-20190728085453-a95533fccea3
|
||||
@ -50,8 +51,11 @@ require (
|
||||
github.com/stretchr/testify v1.4.0
|
||||
github.com/tmc/grpc-websocket-proxy v0.0.0-20200122045848-3419fae592fc // indirect
|
||||
github.com/tsuna/gohbase v0.0.0-20190502052937-24ffed0537aa
|
||||
github.com/uber/jaeger-client-go v2.25.0+incompatible
|
||||
github.com/uber/jaeger-lib v2.4.0+incompatible // indirect
|
||||
github.com/urfave/cli/v2 v2.1.1
|
||||
go.etcd.io/etcd v0.0.0-20200402134248-51bdeb39e698
|
||||
go.uber.org/atomic v1.6.0
|
||||
golang.org/x/crypto v0.0.0-20200323165209-0ec3e9974c59 // indirect
|
||||
golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e
|
||||
golang.org/x/sys v0.0.0-20200331124033-c3d80250170d // indirect
|
||||
|
7
go.sum
7
go.sum
@ -220,6 +220,7 @@ github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1Cpa
|
||||
github.com/onsi/gomega v1.7.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
|
||||
github.com/openconfig/gnmi v0.0.0-20190823184014-89b2bf29312c/go.mod h1:t+O9It+LKzfOAhKTT5O0ehDix+MTqbtT0T9t+7zzOvc=
|
||||
github.com/openconfig/reference v0.0.0-20190727015836-8dfd928c9696/go.mod h1:ym2A+zigScwkSEb/cVQB0/ZMpU3rqiH6X7WRRsxgOGw=
|
||||
github.com/opentracing/opentracing-go v1.1.0 h1:pWlfV3Bxv7k65HYwkikxat0+s3pV4bsqf19k25Ur8rU=
|
||||
github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o=
|
||||
github.com/openzipkin/zipkin-go v0.2.1 h1:noL5/5Uf1HpVl3wNsfkZhIKbSWCVi5jgqkONNx8PXcA=
|
||||
github.com/openzipkin/zipkin-go v0.2.1/go.mod h1:NaW6tEwdmWMaCDZzg8sh+IBNOxHMPnhQw8ySjnjRyN4=
|
||||
@ -322,6 +323,12 @@ github.com/tmc/grpc-websocket-proxy v0.0.0-20200122045848-3419fae592fc h1:yUaosF
|
||||
github.com/tmc/grpc-websocket-proxy v0.0.0-20200122045848-3419fae592fc/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U=
|
||||
github.com/tsuna/gohbase v0.0.0-20190502052937-24ffed0537aa h1:V/ABqiqsgqmpoIcLDSpJ1KqPfbxRmkcfET5+BRy9ctM=
|
||||
github.com/tsuna/gohbase v0.0.0-20190502052937-24ffed0537aa/go.mod h1:3HfLQly3YNLGxNv/2YOfmz30vcjG9hbuME1GpxoLlGs=
|
||||
github.com/uber/jaeger-client-go v1.6.0 h1:3+zLlq+4npI5fg8IsgAje3YsP7TcEdNzJScyqFIzxEQ=
|
||||
github.com/uber/jaeger-client-go v2.25.0+incompatible h1:IxcNZ7WRY1Y3G4poYlx24szfsn/3LvK9QHCq9oQw8+U=
|
||||
github.com/uber/jaeger-client-go v2.25.0+incompatible/go.mod h1:WVhlPFC8FDjOFMMWRy2pZqQJSXxYSwNYOkTr/Z6d3Kk=
|
||||
github.com/uber/jaeger-lib v1.5.0 h1:OHbgr8l656Ub3Fw5k9SWnBfIEwvoHQ+W2y+Aa9D1Uyo=
|
||||
github.com/uber/jaeger-lib v2.4.0+incompatible h1:fY7QsGQWiCt8pajv4r7JEvmATdCVaWxXbjwyYwsNaLQ=
|
||||
github.com/uber/jaeger-lib v2.4.0+incompatible/go.mod h1:ComeNDZlWwrWnDv8aPp0Ba6+uUTzImX/AauajbLI56U=
|
||||
github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0=
|
||||
github.com/urfave/cli v1.20.0 h1:fDqGv3UG/4jbVl/QkFwEdddtEDjh/5Ov6X+0B/3bPaw=
|
||||
github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA=
|
||||
|
11
pkg/net/trace/jaeger/config.go
Normal file
11
pkg/net/trace/jaeger/config.go
Normal file
@ -0,0 +1,11 @@
|
||||
package jaeger
|
||||
|
||||
import (
|
||||
"github.com/go-kratos/kratos/pkg/conf/env"
|
||||
"github.com/go-kratos/kratos/pkg/net/trace"
|
||||
)
|
||||
|
||||
func Init() {
|
||||
c := &Config{Endpoint: "http://127.0.0.1:9191", BatchSize: 120}
|
||||
trace.SetGlobalTracer(trace.NewTracer(env.AppID, newReport(c), true))
|
||||
}
|
314
pkg/net/trace/jaeger/http_transport.go
Normal file
314
pkg/net/trace/jaeger/http_transport.go
Normal file
@ -0,0 +1,314 @@
|
||||
package jaeger
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"github.com/opentracing/opentracing-go"
|
||||
ja "github.com/uber/jaeger-client-go"
|
||||
"github.com/uber/jaeger-client-go/thrift"
|
||||
j "github.com/uber/jaeger-client-go/thrift-gen/jaeger"
|
||||
)
|
||||
|
||||
// Default timeout for http request in seconds
|
||||
const defaultHTTPTimeout = time.Second * 5
|
||||
|
||||
// HTTPTransport implements Transport by forwarding spans to a http server.
|
||||
type HTTPTransport struct {
|
||||
url string
|
||||
client *http.Client
|
||||
batchSize int
|
||||
spans []*j.Span
|
||||
process *j.Process
|
||||
httpCredentials *HTTPBasicAuthCredentials
|
||||
headers map[string]string
|
||||
}
|
||||
|
||||
// HTTPBasicAuthCredentials stores credentials for HTTP basic auth.
|
||||
type HTTPBasicAuthCredentials struct {
|
||||
username string
|
||||
password string
|
||||
}
|
||||
|
||||
// HTTPOption sets a parameter for the HttpCollector
|
||||
type HTTPOption func(c *HTTPTransport)
|
||||
|
||||
// HTTPTimeout sets maximum timeout for http request.
|
||||
func HTTPTimeout(duration time.Duration) HTTPOption {
|
||||
return func(c *HTTPTransport) { c.client.Timeout = duration }
|
||||
}
|
||||
|
||||
// HTTPBatchSize sets the maximum batch size, after which a collect will be
|
||||
// triggered. The default batch size is 100 spans.
|
||||
func HTTPBatchSize(n int) HTTPOption {
|
||||
return func(c *HTTPTransport) { c.batchSize = n }
|
||||
}
|
||||
|
||||
// HTTPBasicAuth sets the credentials required to perform HTTP basic auth
|
||||
func HTTPBasicAuth(username string, password string) HTTPOption {
|
||||
return func(c *HTTPTransport) {
|
||||
c.httpCredentials = &HTTPBasicAuthCredentials{username: username, password: password}
|
||||
}
|
||||
}
|
||||
|
||||
// HTTPRoundTripper configures the underlying Transport on the *http.Client
|
||||
// that is used
|
||||
func HTTPRoundTripper(transport http.RoundTripper) HTTPOption {
|
||||
return func(c *HTTPTransport) {
|
||||
c.client.Transport = transport
|
||||
}
|
||||
}
|
||||
|
||||
// HTTPHeaders defines the HTTP headers that will be attached to the jaeger client's HTTP request
|
||||
func HTTPHeaders(headers map[string]string) HTTPOption {
|
||||
return func(c *HTTPTransport) {
|
||||
c.headers = headers
|
||||
}
|
||||
}
|
||||
|
||||
// NewHTTPTransport returns a new HTTP-backend transport. url should be an http
|
||||
// url of the collector to handle POST request, typically something like:
|
||||
// http://hostname:14268/api/traces?format=jaeger.thrift
|
||||
func NewHTTPTransport(url string, options ...HTTPOption) *HTTPTransport {
|
||||
c := &HTTPTransport{
|
||||
url: url,
|
||||
client: &http.Client{Timeout: defaultHTTPTimeout},
|
||||
batchSize: 100,
|
||||
spans: []*j.Span{},
|
||||
}
|
||||
|
||||
for _, option := range options {
|
||||
option(c)
|
||||
}
|
||||
return c
|
||||
}
|
||||
|
||||
// Append implements Transport.
|
||||
func (c *HTTPTransport) Append(span *Span) (int, error) {
|
||||
if c.process == nil {
|
||||
process := j.NewProcess()
|
||||
process.ServiceName = span.ServiceName()
|
||||
c.process = process
|
||||
}
|
||||
jSpan := BuildJaegerThrift(span)
|
||||
c.spans = append(c.spans, jSpan)
|
||||
if len(c.spans) >= c.batchSize {
|
||||
return c.Flush()
|
||||
}
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
// Flush implements Transport.
|
||||
func (c *HTTPTransport) Flush() (int, error) {
|
||||
count := len(c.spans)
|
||||
if count == 0 {
|
||||
return 0, nil
|
||||
}
|
||||
err := c.send(c.spans)
|
||||
c.spans = c.spans[:0]
|
||||
return count, err
|
||||
}
|
||||
|
||||
// Close implements Transport.
|
||||
func (c *HTTPTransport) Close() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *HTTPTransport) send(spans []*j.Span) error {
|
||||
batch := &j.Batch{
|
||||
Spans: spans,
|
||||
Process: c.process,
|
||||
}
|
||||
body, err := serializeThrift(batch)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
req, err := http.NewRequest("POST", c.url, body)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
req.Header.Set("Content-Type", "application/x-thrift")
|
||||
for k, v := range c.headers {
|
||||
req.Header.Set(k, v)
|
||||
}
|
||||
|
||||
if c.httpCredentials != nil {
|
||||
req.SetBasicAuth(c.httpCredentials.username, c.httpCredentials.password)
|
||||
}
|
||||
|
||||
resp, err := c.client.Do(req)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
io.Copy(ioutil.Discard, resp.Body)
|
||||
resp.Body.Close()
|
||||
if resp.StatusCode >= http.StatusBadRequest {
|
||||
return fmt.Errorf("error from collector: %d", resp.StatusCode)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func serializeThrift(obj thrift.TStruct) (*bytes.Buffer, error) {
|
||||
t := thrift.NewTMemoryBuffer()
|
||||
p := thrift.NewTBinaryProtocolTransport(t)
|
||||
if err := obj.Write(p); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return t.Buffer, nil
|
||||
}
|
||||
|
||||
func BuildJaegerThrift(span *Span) *j.Span {
|
||||
span.Lock()
|
||||
defer span.Unlock()
|
||||
startTime := span.startTime.UnixNano() / 1000
|
||||
duration := span.duration.Nanoseconds() / int64(time.Microsecond)
|
||||
jaegerSpan := &j.Span{
|
||||
TraceIdLow: int64(span.context.traceID.Low),
|
||||
TraceIdHigh: int64(span.context.traceID.High),
|
||||
SpanId: int64(span.context.spanID),
|
||||
ParentSpanId: int64(span.context.parentID),
|
||||
OperationName: span.operationName,
|
||||
Flags: int32(span.context.samplingState.flags()),
|
||||
StartTime: startTime,
|
||||
Duration: duration,
|
||||
Tags: buildTags(span.tags, 100),
|
||||
Logs: buildLogs(span.logs),
|
||||
References: buildReferences(span.references),
|
||||
}
|
||||
return jaegerSpan
|
||||
}
|
||||
|
||||
func stringify(value interface{}) string {
|
||||
if s, ok := value.(string); ok {
|
||||
return s
|
||||
}
|
||||
return fmt.Sprintf("%+v", value)
|
||||
}
|
||||
|
||||
func truncateString(value string, maxLength int) string {
|
||||
// we ignore the problem of utf8 runes possibly being sliced in the middle,
|
||||
// as it is rather expensive to iterate through each tag just to find rune
|
||||
// boundaries.
|
||||
if len(value) > maxLength {
|
||||
return value[:maxLength]
|
||||
}
|
||||
return value
|
||||
}
|
||||
|
||||
func buildTags(tags []Tag, maxTagValueLength int) []*j.Tag {
|
||||
jTags := make([]*j.Tag, 0, len(tags))
|
||||
for _, tag := range tags {
|
||||
jTag := buildTag(&tag, maxTagValueLength)
|
||||
jTags = append(jTags, jTag)
|
||||
}
|
||||
return jTags
|
||||
}
|
||||
func buildTag(tag *Tag, maxTagValueLength int) *j.Tag {
|
||||
jTag := &j.Tag{Key: tag.key}
|
||||
switch value := tag.value.(type) {
|
||||
case string:
|
||||
vStr := truncateString(value, maxTagValueLength)
|
||||
jTag.VStr = &vStr
|
||||
jTag.VType = j.TagType_STRING
|
||||
case []byte:
|
||||
if len(value) > maxTagValueLength {
|
||||
value = value[:maxTagValueLength]
|
||||
}
|
||||
jTag.VBinary = value
|
||||
jTag.VType = j.TagType_BINARY
|
||||
case int:
|
||||
vLong := int64(value)
|
||||
jTag.VLong = &vLong
|
||||
jTag.VType = j.TagType_LONG
|
||||
case uint:
|
||||
vLong := int64(value)
|
||||
jTag.VLong = &vLong
|
||||
jTag.VType = j.TagType_LONG
|
||||
case int8:
|
||||
vLong := int64(value)
|
||||
jTag.VLong = &vLong
|
||||
jTag.VType = j.TagType_LONG
|
||||
case uint8:
|
||||
vLong := int64(value)
|
||||
jTag.VLong = &vLong
|
||||
jTag.VType = j.TagType_LONG
|
||||
case int16:
|
||||
vLong := int64(value)
|
||||
jTag.VLong = &vLong
|
||||
jTag.VType = j.TagType_LONG
|
||||
case uint16:
|
||||
vLong := int64(value)
|
||||
jTag.VLong = &vLong
|
||||
jTag.VType = j.TagType_LONG
|
||||
case int32:
|
||||
vLong := int64(value)
|
||||
jTag.VLong = &vLong
|
||||
jTag.VType = j.TagType_LONG
|
||||
case uint32:
|
||||
vLong := int64(value)
|
||||
jTag.VLong = &vLong
|
||||
jTag.VType = j.TagType_LONG
|
||||
case int64:
|
||||
vLong := int64(value)
|
||||
jTag.VLong = &vLong
|
||||
jTag.VType = j.TagType_LONG
|
||||
case uint64:
|
||||
vLong := int64(value)
|
||||
jTag.VLong = &vLong
|
||||
jTag.VType = j.TagType_LONG
|
||||
case float32:
|
||||
vDouble := float64(value)
|
||||
jTag.VDouble = &vDouble
|
||||
jTag.VType = j.TagType_DOUBLE
|
||||
case float64:
|
||||
vDouble := float64(value)
|
||||
jTag.VDouble = &vDouble
|
||||
jTag.VType = j.TagType_DOUBLE
|
||||
case bool:
|
||||
vBool := value
|
||||
jTag.VBool = &vBool
|
||||
jTag.VType = j.TagType_BOOL
|
||||
default:
|
||||
vStr := truncateString(stringify(value), maxTagValueLength)
|
||||
jTag.VStr = &vStr
|
||||
jTag.VType = j.TagType_STRING
|
||||
}
|
||||
return jTag
|
||||
}
|
||||
|
||||
func buildLogs(logs []opentracing.LogRecord) []*j.Log {
|
||||
jLogs := make([]*j.Log, 0, len(logs))
|
||||
for _, log := range logs {
|
||||
jLog := &j.Log{
|
||||
Timestamp: log.Timestamp.UnixNano() / 1000,
|
||||
Fields: ja.ConvertLogsToJaegerTags(log.Fields),
|
||||
}
|
||||
jLogs = append(jLogs, jLog)
|
||||
}
|
||||
return jLogs
|
||||
}
|
||||
|
||||
func buildReferences(references []Reference) []*j.SpanRef {
|
||||
retMe := make([]*j.SpanRef, 0, len(references))
|
||||
for _, ref := range references {
|
||||
if ref.Type == opentracing.ChildOfRef {
|
||||
retMe = append(retMe, spanRef(ref.Context, j.SpanRefType_CHILD_OF))
|
||||
} else if ref.Type == opentracing.FollowsFromRef {
|
||||
retMe = append(retMe, spanRef(ref.Context, j.SpanRefType_FOLLOWS_FROM))
|
||||
}
|
||||
}
|
||||
return retMe
|
||||
}
|
||||
|
||||
func spanRef(ctx SpanContext, refType j.SpanRefType) *j.SpanRef {
|
||||
return &j.SpanRef{
|
||||
RefType: refType,
|
||||
TraceIdLow: int64(ctx.traceID.Low),
|
||||
TraceIdHigh: int64(ctx.traceID.High),
|
||||
SpanId: int64(ctx.spanID),
|
||||
}
|
||||
}
|
49
pkg/net/trace/jaeger/jaeger.go
Normal file
49
pkg/net/trace/jaeger/jaeger.go
Normal file
@ -0,0 +1,49 @@
|
||||
package jaeger
|
||||
|
||||
import (
|
||||
"github.com/go-kratos/kratos/pkg/log"
|
||||
"github.com/go-kratos/kratos/pkg/net/trace"
|
||||
)
|
||||
|
||||
type Config struct {
|
||||
Endpoint string
|
||||
BatchSize int
|
||||
}
|
||||
|
||||
type JaegerReporter struct {
|
||||
transport *HTTPTransport
|
||||
}
|
||||
|
||||
func newReport(c *Config) *JaegerReporter {
|
||||
transport := NewHTTPTransport(c.Endpoint)
|
||||
transport.batchSize = c.BatchSize
|
||||
return &JaegerReporter{transport: transport}
|
||||
}
|
||||
|
||||
func (r *JaegerReporter) WriteSpan(raw *trace.Span) (err error) {
|
||||
ctx := raw.Context()
|
||||
traceID := TraceID{Low: ctx.TraceID}
|
||||
spanID := SpanID(ctx.SpanID)
|
||||
parentID := SpanID(ctx.ParentID)
|
||||
tags := raw.Tags()
|
||||
log.Info("[info] write span")
|
||||
span := &Span{
|
||||
context: NewSpanContext(traceID, spanID, parentID, true, nil),
|
||||
operationName: raw.OperationName(),
|
||||
startTime: raw.StartTime(),
|
||||
duration: raw.Duration(),
|
||||
}
|
||||
|
||||
span.serviceName = raw.ServiceName()
|
||||
|
||||
for _, t := range tags {
|
||||
span.SetTag(t.Key, t.Value)
|
||||
}
|
||||
|
||||
r.transport.Append(span)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (rpt *JaegerReporter) Close() error {
|
||||
return rpt.transport.Close()
|
||||
}
|
53
pkg/net/trace/jaeger/jaeger_test.go
Normal file
53
pkg/net/trace/jaeger/jaeger_test.go
Normal file
@ -0,0 +1,53 @@
|
||||
package jaeger
|
||||
|
||||
import (
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"testing"
|
||||
|
||||
"github.com/go-kratos/kratos/pkg/net/trace"
|
||||
)
|
||||
|
||||
func TestJaegerReporter(t *testing.T) {
|
||||
var handler = func(w http.ResponseWriter, r *http.Request) {
|
||||
if r.Method != "POST" {
|
||||
t.Errorf("expected 'POST' request, got '%s'", r.Method)
|
||||
}
|
||||
|
||||
aSpanPayload, err := ioutil.ReadAll(r.Body)
|
||||
if err != nil {
|
||||
t.Errorf("unexpected error: %s", err.Error())
|
||||
}
|
||||
|
||||
t.Logf("%s\n", aSpanPayload)
|
||||
}
|
||||
ht := httptest.NewServer(http.HandlerFunc(handler))
|
||||
defer ht.Close()
|
||||
|
||||
c := &Config{
|
||||
Endpoint: ht.URL,
|
||||
BatchSize: 1,
|
||||
}
|
||||
|
||||
//c.Endpoint = "http://127.0.0.1:14268/api/traces"
|
||||
|
||||
report := newReport(c)
|
||||
t1 := trace.NewTracer("jaeger_test_1", report, true)
|
||||
t2 := trace.NewTracer("jaeger_test_2", report, true)
|
||||
sp1 := t1.New("option_1")
|
||||
sp2 := sp1.Fork("service3", "opt_client")
|
||||
sp2.SetLog(trace.Log("log_k", "log_v"))
|
||||
// inject
|
||||
header := make(http.Header)
|
||||
t1.Inject(sp2, trace.HTTPFormat, header)
|
||||
t.Log(header)
|
||||
sp3, err := t2.Extract(trace.HTTPFormat, header)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
sp3.Finish(nil)
|
||||
sp2.Finish(nil)
|
||||
sp1.Finish(nil)
|
||||
report.Close()
|
||||
}
|
9
pkg/net/trace/jaeger/reference.go
Normal file
9
pkg/net/trace/jaeger/reference.go
Normal file
@ -0,0 +1,9 @@
|
||||
package jaeger
|
||||
|
||||
import "github.com/opentracing/opentracing-go"
|
||||
|
||||
// Reference represents a causal reference to other Spans (via their SpanContext).
|
||||
type Reference struct {
|
||||
Type opentracing.SpanReferenceType
|
||||
Context SpanContext
|
||||
}
|
345
pkg/net/trace/jaeger/span.go
Normal file
345
pkg/net/trace/jaeger/span.go
Normal file
@ -0,0 +1,345 @@
|
||||
package jaeger
|
||||
|
||||
import (
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
"github.com/opentracing/opentracing-go"
|
||||
"github.com/opentracing/opentracing-go/log"
|
||||
)
|
||||
|
||||
// Span implements opentracing.Span
|
||||
type Span struct {
|
||||
// referenceCounter used to increase the lifetime of
|
||||
// the object before return it into the pool.
|
||||
referenceCounter int32
|
||||
|
||||
serviceName string
|
||||
|
||||
sync.RWMutex
|
||||
|
||||
// TODO: (breaking change) change to use a pointer
|
||||
context SpanContext
|
||||
|
||||
// The name of the "operation" this span is an instance of.
|
||||
// Known as a "span name" in some implementations.
|
||||
operationName string
|
||||
|
||||
// firstInProcess, if true, indicates that this span is the root of the (sub)tree
|
||||
// of spans in the current process. In other words it's true for the root spans,
|
||||
// and the ingress spans when the process joins another trace.
|
||||
firstInProcess bool
|
||||
|
||||
// startTime is the timestamp indicating when the span began, with microseconds precision.
|
||||
startTime time.Time
|
||||
|
||||
// duration returns duration of the span with microseconds precision.
|
||||
// Zero value means duration is unknown.
|
||||
duration time.Duration
|
||||
|
||||
// tags attached to this span
|
||||
tags []Tag
|
||||
|
||||
// The span's "micro-log"
|
||||
logs []opentracing.LogRecord
|
||||
|
||||
// The number of logs dropped because of MaxLogsPerSpan.
|
||||
numDroppedLogs int
|
||||
|
||||
// references for this span
|
||||
references []Reference
|
||||
}
|
||||
|
||||
// Tag is a simple key value wrapper.
|
||||
// TODO (breaking change) deprecate in the next major release, use opentracing.Tag instead.
|
||||
type Tag struct {
|
||||
key string
|
||||
value interface{}
|
||||
}
|
||||
|
||||
// NewTag creates a new Tag.
|
||||
// TODO (breaking change) deprecate in the next major release, use opentracing.Tag instead.
|
||||
func NewTag(key string, value interface{}) Tag {
|
||||
return Tag{key: key, value: value}
|
||||
}
|
||||
|
||||
// SetOperationName sets or changes the operation name.
|
||||
func (s *Span) SetOperationName(operationName string) opentracing.Span {
|
||||
s.Lock()
|
||||
s.operationName = operationName
|
||||
s.Unlock()
|
||||
return s
|
||||
}
|
||||
|
||||
// SetTag implements SetTag() of opentracing.Span
|
||||
func (s *Span) SetTag(key string, value interface{}) opentracing.Span {
|
||||
return s.setTagInternal(key, value, true)
|
||||
}
|
||||
|
||||
func (s *Span) setTagInternal(key string, value interface{}, lock bool) opentracing.Span {
|
||||
if lock {
|
||||
s.Lock()
|
||||
defer s.Unlock()
|
||||
}
|
||||
s.appendTagNoLocking(key, value)
|
||||
return s
|
||||
}
|
||||
|
||||
// SpanContext returns span context
|
||||
func (s *Span) SpanContext() SpanContext {
|
||||
s.Lock()
|
||||
defer s.Unlock()
|
||||
return s.context
|
||||
}
|
||||
|
||||
// StartTime returns span start time
|
||||
func (s *Span) StartTime() time.Time {
|
||||
s.Lock()
|
||||
defer s.Unlock()
|
||||
return s.startTime
|
||||
}
|
||||
|
||||
// Duration returns span duration
|
||||
func (s *Span) Duration() time.Duration {
|
||||
s.Lock()
|
||||
defer s.Unlock()
|
||||
return s.duration
|
||||
}
|
||||
|
||||
// Tags returns tags for span
|
||||
func (s *Span) Tags() opentracing.Tags {
|
||||
s.Lock()
|
||||
defer s.Unlock()
|
||||
var result = make(opentracing.Tags, len(s.tags))
|
||||
for _, tag := range s.tags {
|
||||
result[tag.key] = tag.value
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
// Logs returns micro logs for span
|
||||
func (s *Span) Logs() []opentracing.LogRecord {
|
||||
s.Lock()
|
||||
defer s.Unlock()
|
||||
|
||||
logs := append([]opentracing.LogRecord(nil), s.logs...)
|
||||
if s.numDroppedLogs != 0 {
|
||||
fixLogs(logs, s.numDroppedLogs)
|
||||
}
|
||||
|
||||
return logs
|
||||
}
|
||||
|
||||
// References returns references for this span
|
||||
func (s *Span) References() []opentracing.SpanReference {
|
||||
s.Lock()
|
||||
defer s.Unlock()
|
||||
|
||||
if s.references == nil || len(s.references) == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
result := make([]opentracing.SpanReference, len(s.references))
|
||||
for i, r := range s.references {
|
||||
result[i] = opentracing.SpanReference{Type: r.Type, ReferencedContext: r.Context}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
func (s *Span) appendTagNoLocking(key string, value interface{}) {
|
||||
s.tags = append(s.tags, Tag{key: key, value: value})
|
||||
}
|
||||
|
||||
// LogFields implements opentracing.Span API
|
||||
func (s *Span) LogFields(fields ...log.Field) {
|
||||
s.Lock()
|
||||
defer s.Unlock()
|
||||
if !s.context.IsSampled() {
|
||||
return
|
||||
}
|
||||
s.logFieldsNoLocking(fields...)
|
||||
}
|
||||
|
||||
// this function should only be called while holding a Write lock
|
||||
func (s *Span) logFieldsNoLocking(fields ...log.Field) {
|
||||
lr := opentracing.LogRecord{
|
||||
Fields: fields,
|
||||
Timestamp: time.Now(),
|
||||
}
|
||||
s.appendLogNoLocking(lr)
|
||||
}
|
||||
|
||||
// LogKV implements opentracing.Span API
|
||||
func (s *Span) LogKV(alternatingKeyValues ...interface{}) {
|
||||
s.RLock()
|
||||
sampled := s.context.IsSampled()
|
||||
s.RUnlock()
|
||||
if !sampled {
|
||||
return
|
||||
}
|
||||
fields, err := log.InterleavedKVToFields(alternatingKeyValues...)
|
||||
if err != nil {
|
||||
s.LogFields(log.Error(err), log.String("function", "LogKV"))
|
||||
return
|
||||
}
|
||||
s.LogFields(fields...)
|
||||
}
|
||||
|
||||
// LogEvent implements opentracing.Span API
|
||||
func (s *Span) LogEvent(event string) {
|
||||
s.Log(opentracing.LogData{Event: event})
|
||||
}
|
||||
|
||||
// LogEventWithPayload implements opentracing.Span API
|
||||
func (s *Span) LogEventWithPayload(event string, payload interface{}) {
|
||||
s.Log(opentracing.LogData{Event: event, Payload: payload})
|
||||
}
|
||||
|
||||
// Log implements opentracing.Span API
|
||||
func (s *Span) Log(ld opentracing.LogData) {
|
||||
s.Lock()
|
||||
defer s.Unlock()
|
||||
if s.context.IsSampled() {
|
||||
s.appendLogNoLocking(ld.ToLogRecord())
|
||||
}
|
||||
}
|
||||
|
||||
// this function should only be called while holding a Write lock
|
||||
func (s *Span) appendLogNoLocking(lr opentracing.LogRecord) {
|
||||
maxLogs := 100
|
||||
// We have too many logs. We don't touch the first numOld logs; we treat the
|
||||
// rest as a circular buffer and overwrite the oldest log among those.
|
||||
numOld := (maxLogs - 1) / 2
|
||||
numNew := maxLogs - numOld
|
||||
s.logs[numOld+s.numDroppedLogs%numNew] = lr
|
||||
s.numDroppedLogs++
|
||||
}
|
||||
|
||||
// rotateLogBuffer rotates the records in the buffer: records 0 to pos-1 move at
|
||||
// the end (i.e. pos circular left shifts).
|
||||
func rotateLogBuffer(buf []opentracing.LogRecord, pos int) {
|
||||
// This algorithm is described in:
|
||||
// http://www.cplusplus.com/reference/algorithm/rotate
|
||||
for first, middle, next := 0, pos, pos; first != middle; {
|
||||
buf[first], buf[next] = buf[next], buf[first]
|
||||
first++
|
||||
next++
|
||||
if next == len(buf) {
|
||||
next = middle
|
||||
} else if first == middle {
|
||||
middle = next
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func fixLogs(logs []opentracing.LogRecord, numDroppedLogs int) {
|
||||
// We dropped some log events, which means that we used part of Logs as a
|
||||
// circular buffer (see appendLog). De-circularize it.
|
||||
numOld := (len(logs) - 1) / 2
|
||||
numNew := len(logs) - numOld
|
||||
rotateLogBuffer(logs[numOld:], numDroppedLogs%numNew)
|
||||
|
||||
// Replace the log in the middle (the oldest "new" log) with information
|
||||
// about the dropped logs. This means that we are effectively dropping one
|
||||
// more "new" log.
|
||||
numDropped := numDroppedLogs + 1
|
||||
logs[numOld] = opentracing.LogRecord{
|
||||
// Keep the timestamp of the last dropped event.
|
||||
Timestamp: logs[numOld].Timestamp,
|
||||
Fields: []log.Field{
|
||||
log.String("event", "dropped Span logs"),
|
||||
log.Int("dropped_log_count", numDropped),
|
||||
log.String("component", "jaeger-client"),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (s *Span) fixLogsIfDropped() {
|
||||
if s.numDroppedLogs == 0 {
|
||||
return
|
||||
}
|
||||
fixLogs(s.logs, s.numDroppedLogs)
|
||||
s.numDroppedLogs = 0
|
||||
}
|
||||
|
||||
// SetBaggageItem implements SetBaggageItem() of opentracing.SpanContext
|
||||
func (s *Span) SetBaggageItem(key, value string) opentracing.Span {
|
||||
s.context.baggage[key] = value
|
||||
return s
|
||||
}
|
||||
|
||||
// BaggageItem implements BaggageItem() of opentracing.SpanContext
|
||||
func (s *Span) BaggageItem(key string) string {
|
||||
s.RLock()
|
||||
defer s.RUnlock()
|
||||
return s.context.baggage[key]
|
||||
}
|
||||
|
||||
// Finish implements opentracing.Span API
|
||||
// After finishing the Span object it returns back to the allocator unless the reporter retains it again,
|
||||
// so after that, the Span object should no longer be used because it won't be valid anymore.
|
||||
func (s *Span) Finish() {
|
||||
s.FinishWithOptions(opentracing.FinishOptions{})
|
||||
}
|
||||
|
||||
// FinishWithOptions implements opentracing.Span API
|
||||
func (s *Span) FinishWithOptions(options opentracing.FinishOptions) {
|
||||
}
|
||||
|
||||
// Context implements opentracing.Span API
|
||||
func (s *Span) Context() opentracing.SpanContext {
|
||||
s.Lock()
|
||||
defer s.Unlock()
|
||||
return s.context
|
||||
}
|
||||
|
||||
// Tracer implements opentracing.Span API
|
||||
func (s *Span) Tracer() opentracing.Tracer {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *Span) String() string {
|
||||
s.RLock()
|
||||
defer s.RUnlock()
|
||||
return s.context.String()
|
||||
}
|
||||
|
||||
// OperationName allows retrieving current operation name.
|
||||
func (s *Span) OperationName() string {
|
||||
s.RLock()
|
||||
defer s.RUnlock()
|
||||
return s.operationName
|
||||
}
|
||||
|
||||
// Retain increases object counter to increase the lifetime of the object
|
||||
func (s *Span) Retain() *Span {
|
||||
atomic.AddInt32(&s.referenceCounter, 1)
|
||||
return s
|
||||
}
|
||||
|
||||
// Release decrements object counter and return to the
|
||||
// allocator manager when counter will below zero
|
||||
func (s *Span) Release() {
|
||||
|
||||
}
|
||||
|
||||
// reset span state and release unused data
|
||||
func (s *Span) reset() {
|
||||
s.firstInProcess = false
|
||||
s.context = emptyContext
|
||||
s.operationName = ""
|
||||
s.startTime = time.Time{}
|
||||
s.duration = 0
|
||||
atomic.StoreInt32(&s.referenceCounter, 0)
|
||||
|
||||
// Note: To reuse memory we can save the pointers on the heap
|
||||
s.tags = s.tags[:0]
|
||||
s.logs = s.logs[:0]
|
||||
s.numDroppedLogs = 0
|
||||
s.references = s.references[:0]
|
||||
}
|
||||
|
||||
func (s *Span) ServiceName() string {
|
||||
return s.serviceName
|
||||
}
|
369
pkg/net/trace/jaeger/span_context.go
Normal file
369
pkg/net/trace/jaeger/span_context.go
Normal file
@ -0,0 +1,369 @@
|
||||
package jaeger
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"go.uber.org/atomic"
|
||||
)
|
||||
|
||||
const (
|
||||
flagSampled = 1
|
||||
flagDebug = 2
|
||||
flagFirehose = 8
|
||||
)
|
||||
|
||||
var (
|
||||
errEmptyTracerStateString = errors.New("Cannot convert empty string to tracer state")
|
||||
errMalformedTracerStateString = errors.New("String does not match tracer state format")
|
||||
|
||||
emptyContext = SpanContext{}
|
||||
)
|
||||
|
||||
// TraceID represents unique 128bit identifier of a trace
|
||||
type TraceID struct {
|
||||
High, Low uint64
|
||||
}
|
||||
|
||||
// SpanID represents unique 64bit identifier of a span
|
||||
type SpanID uint64
|
||||
|
||||
// SpanContext represents propagated span identity and state
|
||||
type SpanContext struct {
|
||||
// traceID represents globally unique ID of the trace.
|
||||
// Usually generated as a random number.
|
||||
traceID TraceID
|
||||
|
||||
// spanID represents span ID that must be unique within its trace,
|
||||
// but does not have to be globally unique.
|
||||
spanID SpanID
|
||||
|
||||
// parentID refers to the ID of the parent span.
|
||||
// Should be 0 if the current span is a root span.
|
||||
parentID SpanID
|
||||
|
||||
// Distributed Context baggage. The is a snapshot in time.
|
||||
baggage map[string]string
|
||||
|
||||
// debugID can be set to some correlation ID when the context is being
|
||||
// extracted from a TextMap carrier.
|
||||
//
|
||||
// See JaegerDebugHeader in constants.go
|
||||
debugID string
|
||||
|
||||
// samplingState is shared across all spans
|
||||
samplingState *samplingState
|
||||
|
||||
// remote indicates that span context represents a remote parent
|
||||
remote bool
|
||||
}
|
||||
|
||||
type samplingState struct {
|
||||
// Span context's state flags that are propagated across processes. Only lower 8 bits are used.
|
||||
// We use an int32 instead of byte to be able to use CAS operations.
|
||||
stateFlags atomic.Int32
|
||||
|
||||
// When state is not final, sampling will be retried on other span write operations,
|
||||
// like SetOperationName / SetTag, and the spans will remain writable.
|
||||
final atomic.Bool
|
||||
|
||||
// localRootSpan stores the SpanID of the first span created in this process for a given trace.
|
||||
localRootSpan SpanID
|
||||
|
||||
// extendedState allows samplers to keep intermediate state.
|
||||
// The keys and values in this map are completely opaque: interface{} -> interface{}.
|
||||
extendedState sync.Map
|
||||
}
|
||||
|
||||
func (s *samplingState) isLocalRootSpan(id SpanID) bool {
|
||||
return id == s.localRootSpan
|
||||
}
|
||||
|
||||
func (s *samplingState) setFlag(newFlag int32) {
|
||||
swapped := false
|
||||
for !swapped {
|
||||
old := s.stateFlags.Load()
|
||||
swapped = s.stateFlags.CAS(old, old|newFlag)
|
||||
}
|
||||
}
|
||||
|
||||
func (s *samplingState) unsetFlag(newFlag int32) {
|
||||
swapped := false
|
||||
for !swapped {
|
||||
old := s.stateFlags.Load()
|
||||
swapped = s.stateFlags.CAS(old, old&^newFlag)
|
||||
}
|
||||
}
|
||||
|
||||
func (s *samplingState) setSampled() {
|
||||
s.setFlag(flagSampled)
|
||||
}
|
||||
|
||||
func (s *samplingState) unsetSampled() {
|
||||
s.unsetFlag(flagSampled)
|
||||
}
|
||||
|
||||
func (s *samplingState) setDebugAndSampled() {
|
||||
s.setFlag(flagDebug | flagSampled)
|
||||
}
|
||||
|
||||
func (s *samplingState) setFirehose() {
|
||||
s.setFlag(flagFirehose)
|
||||
}
|
||||
|
||||
func (s *samplingState) setFlags(flags byte) {
|
||||
s.stateFlags.Store(int32(flags))
|
||||
}
|
||||
|
||||
func (s *samplingState) setFinal() {
|
||||
s.final.Store(true)
|
||||
}
|
||||
|
||||
func (s *samplingState) flags() byte {
|
||||
return byte(s.stateFlags.Load())
|
||||
}
|
||||
|
||||
func (s *samplingState) isSampled() bool {
|
||||
return s.stateFlags.Load()&flagSampled == flagSampled
|
||||
}
|
||||
|
||||
func (s *samplingState) isDebug() bool {
|
||||
return s.stateFlags.Load()&flagDebug == flagDebug
|
||||
}
|
||||
|
||||
func (s *samplingState) isFirehose() bool {
|
||||
return s.stateFlags.Load()&flagFirehose == flagFirehose
|
||||
}
|
||||
|
||||
func (s *samplingState) isFinal() bool {
|
||||
return s.final.Load()
|
||||
}
|
||||
|
||||
func (s *samplingState) extendedStateForKey(key interface{}, initValue func() interface{}) interface{} {
|
||||
if value, ok := s.extendedState.Load(key); ok {
|
||||
return value
|
||||
}
|
||||
value := initValue()
|
||||
value, _ = s.extendedState.LoadOrStore(key, value)
|
||||
return value
|
||||
}
|
||||
|
||||
// ForeachBaggageItem implements ForeachBaggageItem() of opentracing.SpanContext
|
||||
func (c SpanContext) ForeachBaggageItem(handler func(k, v string) bool) {
|
||||
for k, v := range c.baggage {
|
||||
if !handler(k, v) {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// IsSampled returns whether this trace was chosen for permanent storage
|
||||
// by the sampling mechanism of the tracer.
|
||||
func (c SpanContext) IsSampled() bool {
|
||||
return c.samplingState.isSampled()
|
||||
}
|
||||
|
||||
// IsDebug indicates whether sampling was explicitly requested by the service.
|
||||
func (c SpanContext) IsDebug() bool {
|
||||
return c.samplingState.isDebug()
|
||||
}
|
||||
|
||||
// IsSamplingFinalized indicates whether the sampling decision has been finalized.
|
||||
func (c SpanContext) IsSamplingFinalized() bool {
|
||||
return c.samplingState.isFinal()
|
||||
}
|
||||
|
||||
// IsFirehose indicates whether the firehose flag was set
|
||||
func (c SpanContext) IsFirehose() bool {
|
||||
return c.samplingState.isFirehose()
|
||||
}
|
||||
|
||||
// ExtendedSamplingState returns the custom state object for a given key. If the value for this key does not exist,
|
||||
// it is initialized via initValue function. This state can be used by samplers (e.g. x.PrioritySampler).
|
||||
func (c SpanContext) ExtendedSamplingState(key interface{}, initValue func() interface{}) interface{} {
|
||||
return c.samplingState.extendedStateForKey(key, initValue)
|
||||
}
|
||||
|
||||
// IsValid indicates whether this context actually represents a valid trace.
|
||||
func (c SpanContext) IsValid() bool {
|
||||
return c.traceID.IsValid() && c.spanID != 0
|
||||
}
|
||||
|
||||
// SetFirehose enables firehose mode for this trace.
|
||||
func (c SpanContext) SetFirehose() {
|
||||
c.samplingState.setFirehose()
|
||||
}
|
||||
|
||||
func (c SpanContext) String() string {
|
||||
if c.traceID.High == 0 {
|
||||
return fmt.Sprintf("%016x:%016x:%016x:%x", c.traceID.Low, uint64(c.spanID), uint64(c.parentID), c.samplingState.stateFlags.Load())
|
||||
}
|
||||
return fmt.Sprintf("%016x%016x:%016x:%016x:%x", c.traceID.High, c.traceID.Low, uint64(c.spanID), uint64(c.parentID), c.samplingState.stateFlags.Load())
|
||||
}
|
||||
|
||||
// ContextFromString reconstructs the Context encoded in a string
|
||||
func ContextFromString(value string) (SpanContext, error) {
|
||||
var context SpanContext
|
||||
if value == "" {
|
||||
return emptyContext, errEmptyTracerStateString
|
||||
}
|
||||
parts := strings.Split(value, ":")
|
||||
if len(parts) != 4 {
|
||||
return emptyContext, errMalformedTracerStateString
|
||||
}
|
||||
var err error
|
||||
if context.traceID, err = TraceIDFromString(parts[0]); err != nil {
|
||||
return emptyContext, err
|
||||
}
|
||||
if context.spanID, err = SpanIDFromString(parts[1]); err != nil {
|
||||
return emptyContext, err
|
||||
}
|
||||
if context.parentID, err = SpanIDFromString(parts[2]); err != nil {
|
||||
return emptyContext, err
|
||||
}
|
||||
flags, err := strconv.ParseUint(parts[3], 10, 8)
|
||||
if err != nil {
|
||||
return emptyContext, err
|
||||
}
|
||||
context.samplingState = &samplingState{}
|
||||
context.samplingState.setFlags(byte(flags))
|
||||
return context, nil
|
||||
}
|
||||
|
||||
// TraceID returns the trace ID of this span context
|
||||
func (c SpanContext) TraceID() TraceID {
|
||||
return c.traceID
|
||||
}
|
||||
|
||||
// SpanID returns the span ID of this span context
|
||||
func (c SpanContext) SpanID() SpanID {
|
||||
return c.spanID
|
||||
}
|
||||
|
||||
// ParentID returns the parent span ID of this span context
|
||||
func (c SpanContext) ParentID() SpanID {
|
||||
return c.parentID
|
||||
}
|
||||
|
||||
// Flags returns the bitmap containing such bits as 'sampled' and 'debug'.
|
||||
func (c SpanContext) Flags() byte {
|
||||
return c.samplingState.flags()
|
||||
}
|
||||
|
||||
// NewSpanContext creates a new instance of SpanContext
|
||||
func NewSpanContext(traceID TraceID, spanID, parentID SpanID, sampled bool, baggage map[string]string) SpanContext {
|
||||
samplingState := &samplingState{}
|
||||
if sampled {
|
||||
samplingState.setSampled()
|
||||
}
|
||||
|
||||
return SpanContext{
|
||||
traceID: traceID,
|
||||
spanID: spanID,
|
||||
parentID: parentID,
|
||||
samplingState: samplingState,
|
||||
baggage: baggage}
|
||||
}
|
||||
|
||||
// CopyFrom copies data from ctx into this context, including span identity and baggage.
|
||||
// TODO This is only used by interop.go. Remove once TChannel Go supports OpenTracing.
|
||||
func (c *SpanContext) CopyFrom(ctx *SpanContext) {
|
||||
c.traceID = ctx.traceID
|
||||
c.spanID = ctx.spanID
|
||||
c.parentID = ctx.parentID
|
||||
c.samplingState = ctx.samplingState
|
||||
if l := len(ctx.baggage); l > 0 {
|
||||
c.baggage = make(map[string]string, l)
|
||||
for k, v := range ctx.baggage {
|
||||
c.baggage[k] = v
|
||||
}
|
||||
} else {
|
||||
c.baggage = nil
|
||||
}
|
||||
}
|
||||
|
||||
// WithBaggageItem creates a new context with an extra baggage item.
|
||||
func (c SpanContext) WithBaggageItem(key, value string) SpanContext {
|
||||
var newBaggage map[string]string
|
||||
if c.baggage == nil {
|
||||
newBaggage = map[string]string{key: value}
|
||||
} else {
|
||||
newBaggage = make(map[string]string, len(c.baggage)+1)
|
||||
for k, v := range c.baggage {
|
||||
newBaggage[k] = v
|
||||
}
|
||||
newBaggage[key] = value
|
||||
}
|
||||
// Use positional parameters so the compiler will help catch new fields.
|
||||
return SpanContext{c.traceID, c.spanID, c.parentID, newBaggage, "", c.samplingState, c.remote}
|
||||
}
|
||||
|
||||
// isDebugIDContainerOnly returns true when the instance of the context is only
|
||||
// used to return the debug/correlation ID from extract() method. This happens
|
||||
// in the situation when "jaeger-debug-id" header is passed in the carrier to
|
||||
// the extract() method, but the request otherwise has no span context in it.
|
||||
// Previously this would've returned opentracing.ErrSpanContextNotFound from the
|
||||
// extract method, but now it returns a dummy context with only debugID filled in.
|
||||
//
|
||||
// See JaegerDebugHeader in constants.go
|
||||
// See TextMapPropagator#Extract
|
||||
func (c *SpanContext) isDebugIDContainerOnly() bool {
|
||||
return !c.traceID.IsValid() && c.debugID != ""
|
||||
}
|
||||
|
||||
// ------- TraceID -------
|
||||
|
||||
func (t TraceID) String() string {
|
||||
if t.High == 0 {
|
||||
return fmt.Sprintf("%x", t.Low)
|
||||
}
|
||||
return fmt.Sprintf("%x%016x", t.High, t.Low)
|
||||
}
|
||||
|
||||
// TraceIDFromString creates a TraceID from a hexadecimal string
|
||||
func TraceIDFromString(s string) (TraceID, error) {
|
||||
var hi, lo uint64
|
||||
var err error
|
||||
if len(s) > 32 {
|
||||
return TraceID{}, fmt.Errorf("TraceID cannot be longer than 32 hex characters: %s", s)
|
||||
} else if len(s) > 16 {
|
||||
hiLen := len(s) - 16
|
||||
if hi, err = strconv.ParseUint(s[0:hiLen], 16, 64); err != nil {
|
||||
return TraceID{}, err
|
||||
}
|
||||
if lo, err = strconv.ParseUint(s[hiLen:], 16, 64); err != nil {
|
||||
return TraceID{}, err
|
||||
}
|
||||
} else {
|
||||
if lo, err = strconv.ParseUint(s, 16, 64); err != nil {
|
||||
return TraceID{}, err
|
||||
}
|
||||
}
|
||||
return TraceID{High: hi, Low: lo}, nil
|
||||
}
|
||||
|
||||
// IsValid checks if the trace ID is valid, i.e. not zero.
|
||||
func (t TraceID) IsValid() bool {
|
||||
return t.High != 0 || t.Low != 0
|
||||
}
|
||||
|
||||
// ------- SpanID -------
|
||||
|
||||
func (s SpanID) String() string {
|
||||
return fmt.Sprintf("%x", uint64(s))
|
||||
}
|
||||
|
||||
// SpanIDFromString creates a SpanID from a hexadecimal string
|
||||
func SpanIDFromString(s string) (SpanID, error) {
|
||||
if len(s) > 16 {
|
||||
return SpanID(0), fmt.Errorf("SpanID cannot be longer than 16 hex characters: %s", s)
|
||||
}
|
||||
id, err := strconv.ParseUint(s, 16, 64)
|
||||
if err != nil {
|
||||
return SpanID(0), err
|
||||
}
|
||||
return SpanID(id), nil
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user