diff --git a/example_trace_test.go b/example_trace_test.go index eb0cceb1c2c7037da63ea6b6c90d0687666452e6..597aef54f7a8636f7727f296a8da9a83b700b7b0 100644 --- a/example_trace_test.go +++ b/example_trace_test.go @@ -24,7 +24,8 @@ import ( ) func ExampleNewTracer() { - r, err := reporter.NewGRPCReporter("hello.com:11800") + // Use gRPC reporter for production + r, err := reporter.NewLogReporter() if err != nil { log.Fatalf("new reporter error %v \n", err) } @@ -33,24 +34,25 @@ func ExampleNewTracer() { if err != nil { log.Fatalf("create tracer error %v \n", err) } + // This for test + tracer.WaitUntilRegister() span, ctx, err := tracer.CreateLocalSpan(context.Background()) if err != nil { log.Fatalf("create new local span error %v \n", err) } span.SetOperationName("invoke data") span.Tag("kind", "outer") - time.Sleep(2 * time.Second) + time.Sleep(time.Second) subSpan, _, err := tracer.CreateLocalSpan(ctx) if err != nil { log.Fatalf("create new sub local span error %v \n", err) } subSpan.SetOperationName("invoke inner") subSpan.Log(time.Now(), "inner", "this is right") - time.Sleep(2 * time.Second) + time.Sleep(time.Second) subSpan.End() - time.Sleep(1 * time.Second) + time.Sleep(500 * time.Millisecond) span.End() - time.Sleep(time.Minute) - //fmt.Print("aa") - // Output: aa + time.Sleep(time.Second) + // Output: } diff --git a/go.mod b/go.mod index 209f6783c1dfa9253f38bd416fd2d32a55fb1360..304996d6c668c8e74e348fc30f247c4203d9469d 100644 --- a/go.mod +++ b/go.mod @@ -6,6 +6,7 @@ require ( github.com/golang/mock v1.2.0 github.com/golang/protobuf v1.3.1 github.com/google/uuid v1.1.1 + github.com/gorilla/mux v1.7.1 github.com/pkg/errors v0.8.1 google.golang.org/grpc v1.19.1 ) diff --git a/go.sum b/go.sum index 223edd6f2c5f7ce1fb76780a57aa2b5968775d31..a4a0b82719d3a144e37b7fd13da29d9376c9c5ff 100644 --- a/go.sum +++ b/go.sum @@ -12,6 +12,8 @@ github.com/golang/protobuf v1.3.1 h1:YF8+flBXS5eO826T4nzqPrxfhQThhXl0YzfuUPu4SBg github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/google/uuid v1.1.1 h1:Gkbcsh/GbpXz7lPftLA3P6TYMwjCLYm83jiFQZF/3gY= github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/gorilla/mux v1.7.1 h1:Dw4jY2nghMMRsh1ol8dv1axHkDwMQK2DHerMNJsIpJU= +github.com/gorilla/mux v1.7.1/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I= github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= diff --git a/noop.go b/noop.go index 10c54c26796d12705827f9dd4cd680af6900cd10..ed83fa5c186cf084cc65f642f2b31c83b1fb679a 100644 --- a/noop.go +++ b/noop.go @@ -32,7 +32,10 @@ func (*NoopSpan) SetPeer(string) { func (*NoopSpan) SetSpanLayer(common.SpanLayer) { } -func (*NoopSpan) Tag(string, string) { +func (*NoopSpan) SetComponent(int32) { +} + +func (*NoopSpan) Tag(Tag, string) { } func (*NoopSpan) Log(time.Time, ...string) { diff --git a/noop_test.go b/noop_test.go index 44c667e9f2ec11ba1babd66ba3f5ce4649a1d647..6a57b466a0a35f6daebb03560d262ab91ad930a6 100644 --- a/noop_test.go +++ b/noop_test.go @@ -110,6 +110,7 @@ func TestNoopMethod(t *testing.T) { n.SetOperationName("aa") n.SetPeer("localhost:1111") n.SetSpanLayer(common.SpanLayer_Database) + n.SetComponent(2) n.Tag("key", "value") n.Log(time.Now(), "key", "value") n.Error(time.Now(), "key", "value") diff --git a/pkg/error.go b/pkg/error.go new file mode 100644 index 0000000000000000000000000000000000000000..6dd74199d25f7a676fb3a37d5810d80cc2bdfbcc --- /dev/null +++ b/pkg/error.go @@ -0,0 +1,21 @@ +// Copyright 2019 Tetrate Labs +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package pkg + +type Error string + +func (e Error) Error() string { + return string(e) +} diff --git a/plugins/http/client.go b/plugins/http/client.go new file mode 100644 index 0000000000000000000000000000000000000000..b07bad5772593034c9adf55918e3fdabae6ea9c3 --- /dev/null +++ b/plugins/http/client.go @@ -0,0 +1,117 @@ +// Copyright 2019 Tetrate Labs +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package http + +import ( + "net/http" + "strconv" + "time" + + "github.com/tetratelabs/go2sky" + "github.com/tetratelabs/go2sky/propagation" + "github.com/tetratelabs/go2sky/reporter/grpc/common" +) + +const httpClientComponentID int32 = 2 + +type ClientConfig struct { + name string + client *http.Client + tracer *go2sky.Tracer + extraTags map[string]string +} + +// ClientOption allows optional configuration of Client. +type ClientOption func(*ClientConfig) + +// WithOperationName override default operation name. +func WithClientOperationName(name string) ClientOption { + return func(c *ClientConfig) { + c.name = name + } +} + +// WithClientTag adds extra tag to client spans. +func WithClientTag(key string, value string) ClientOption { + return func(c *ClientConfig) { + if c.extraTags == nil { + c.extraTags = make(map[string]string) + } + c.extraTags[key] = value + } +} + +// WithClient set customer http client. +func WithClient(client *http.Client) ClientOption { + return func(c *ClientConfig) { + c.client = client + } +} + +// NewClient returns an HTTP Client with tracer +func NewClient(tracer *go2sky.Tracer, options ...ClientOption) (*http.Client, error) { + if tracer == nil { + return nil, errInvalidTracer + } + co := &ClientConfig{tracer: tracer} + for _, option := range options { + option(co) + } + if co.client == nil { + co.client = &http.Client{} + } + tp := &transport{ + ClientConfig: co, + delegated: http.DefaultTransport, + } + if co.client.Transport != nil { + tp.delegated = co.client.Transport + } + co.client.Transport = tp + return co.client, nil +} + +type transport struct { + *ClientConfig + delegated http.RoundTripper +} + +func (t *transport) RoundTrip(req *http.Request) (res *http.Response, err error) { + span, err := t.tracer.CreateExitSpan(req.Context(), getOperationName(t.name, req), req.Host, func(header string) error { + req.Header.Set(propagation.Header, header) + return nil + }) + if err != nil { + return t.delegated.RoundTrip(req) + } + defer span.End() + span.SetComponent(httpClientComponentID) + for k, v := range t.extraTags { + span.Tag(go2sky.Tag(k), v) + } + span.Tag(go2sky.TagHTTPMethod, req.Method) + span.Tag(go2sky.TagURL, req.URL.String()) + span.SetSpanLayer(common.SpanLayer_Http) + res, err = t.delegated.RoundTrip(req) + if err != nil { + span.Error(time.Now(), err.Error()) + return + } + span.Tag(go2sky.TagStatusCode, strconv.Itoa(res.StatusCode)) + if res.StatusCode >= 400 { + span.Error(time.Now(), "Errors on handling client") + } + return res, nil +} diff --git a/plugins/http/example_http_test.go b/plugins/http/example_http_test.go new file mode 100644 index 0000000000000000000000000000000000000000..51c72d97461dd6c7b590ff7dd00e7ab7fdf18998 --- /dev/null +++ b/plugins/http/example_http_test.go @@ -0,0 +1,111 @@ +// Copyright 2019 Tetrate Labs +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package http + +import ( + "fmt" + "log" + "net/http" + "net/http/httptest" + "time" + + "github.com/gorilla/mux" + + "github.com/tetratelabs/go2sky" + "github.com/tetratelabs/go2sky/reporter" +) + +func ExampleNewServerMiddleware() { + // Use gRPC reporter for production + r, err := reporter.NewLogReporter() + if err != nil { + log.Fatalf("new reporter error %v \n", err) + } + defer r.Close() + + tracer, err := go2sky.NewTracer("example", go2sky.WithReporter(r)) + if err != nil { + log.Fatalf("create tracer error %v \n", err) + } + tracer.WaitUntilRegister() + + sm, err := NewServerMiddleware(tracer) + if err != nil { + log.Fatalf("create server middleware error %v \n", err) + } + + client, err := NewClient(tracer) + if err != nil { + log.Fatalf("create client error %v \n", err) + } + + router := mux.NewRouter() + + // create test server + ts := httptest.NewServer(sm(router)) + defer ts.Close() + + // add handlers + router.Methods("GET").Path("/middle").HandlerFunc(middleFunc(client, ts.URL)) + router.Methods("POST").Path("/end").HandlerFunc(endFunc()) + + // call end service + request, err := http.NewRequest("GET", fmt.Sprintf("%s/middle", ts.URL), nil) + if err != nil { + log.Fatalf("unable to create http request: %+v\n", err) + } + res, err := client.Do(request) + if err != nil { + log.Fatalf("unable to do http request: %+v\n", err) + } + _ = res.Body.Close() + time.Sleep(time.Second) + + // Output: +} + +func middleFunc(client *http.Client, url string) http.HandlerFunc { + return func(w http.ResponseWriter, r *http.Request) { + log.Printf("middle func called with method: %s\n", r.Method) + + // do some operation + time.Sleep(100 * time.Millisecond) + + newRequest, err := http.NewRequest("POST", url+"/end", nil) + if err != nil { + log.Printf("unable to create client: %+v\n", err) + http.Error(w, err.Error(), 500) + return + } + + //Link the context of entry and exit spans + newRequest = newRequest.WithContext(r.Context()) + + res, err := client.Do(newRequest) + if err != nil { + log.Printf("call to end fund returned error: %+v\n", err) + http.Error(w, err.Error(), 500) + return + } + _ = res.Body.Close() + } +} + +func endFunc() http.HandlerFunc { + return func(w http.ResponseWriter, r *http.Request) { + log.Printf("end func called with method: %s\n", r.Method) + time.Sleep(50 * time.Millisecond) + } +} diff --git a/plugins/http/server.go b/plugins/http/server.go new file mode 100644 index 0000000000000000000000000000000000000000..bf87913495071dfcbba06468f4f40a8a7b5f96cf --- /dev/null +++ b/plugins/http/server.go @@ -0,0 +1,129 @@ +// Copyright 2019 Tetrate Labs +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package http + +import ( + "fmt" + "net/http" + "strconv" + "time" + + "github.com/tetratelabs/go2sky" + "github.com/tetratelabs/go2sky/pkg" + "github.com/tetratelabs/go2sky/propagation" + "github.com/tetratelabs/go2sky/reporter/grpc/common" +) + +const ( + httpServerComponentID int32 = 49 + errInvalidTracer = pkg.Error("invalid tracer") +) + +type handler struct { + tracer *go2sky.Tracer + name string + next http.Handler + extraTags map[string]string +} + +// ServerOption allows Middleware to be optionally configured. +type ServerOption func(*handler) + +// Tag adds extra tag to server spans. +func WithServerTag(key string, value string) ServerOption { + return func(h *handler) { + if h.extraTags == nil { + h.extraTags = make(map[string]string) + } + h.extraTags[key] = value + } +} + +// WithOperationName override default operation name. +func WithServerOperationName(name string) ServerOption { + return func(h *handler) { + h.name = name + } +} + +// NewServerMiddleware returns a http.Handler middleware with tracing. +func NewServerMiddleware(tracer *go2sky.Tracer, options ...ServerOption) (func(http.Handler) http.Handler, error) { + if tracer == nil { + return nil, errInvalidTracer + } + return func(next http.Handler) http.Handler { + h := &handler{ + tracer: tracer, + next: next, + } + for _, option := range options { + option(h) + } + return h + }, nil +} + +// ServeHTTP implements http.Handler. +func (h handler) ServeHTTP(w http.ResponseWriter, r *http.Request) { + span, ctx, err := h.tracer.CreateEntrySpan(r.Context(), getOperationName(h.name, r), func() (string, error) { + return r.Header.Get(propagation.Header), nil + }) + if err != nil { + h.next.ServeHTTP(w, r) + return + } + span.SetComponent(httpServerComponentID) + for k, v := range h.extraTags { + span.Tag(go2sky.Tag(k), v) + } + span.Tag(go2sky.TagHTTPMethod, r.Method) + span.Tag(go2sky.TagURL, fmt.Sprintf("%s%s", r.Host, r.URL.Path)) + span.SetSpanLayer(common.SpanLayer_Http) + + rww := &responseWriterWrapper{w: w, statusCode: 200} + defer func() { + code := rww.statusCode + if code >= 400 { + span.Error(time.Now(), "Error on handling request") + } + span.Tag(go2sky.TagStatusCode, strconv.Itoa(code)) + span.End() + }() + h.next.ServeHTTP(rww, r.WithContext(ctx)) +} + +type responseWriterWrapper struct { + w http.ResponseWriter + statusCode int +} + +func (rww *responseWriterWrapper) Header() http.Header { + return rww.w.Header() +} + +func (rww *responseWriterWrapper) Write(bytes []byte) (int, error) { + return rww.w.Write(bytes) +} + +func (rww *responseWriterWrapper) WriteHeader(statusCode int) { + rww.statusCode = statusCode +} + +func getOperationName(name string, r *http.Request) string { + if name == "" { + return fmt.Sprintf("/%s%s", r.Method, r.URL.Path) + } + return name +} diff --git a/propagation/propagation.go b/propagation/propagation.go index d2a70fe50a6cf332fb2881660a30284597448a6c..96e13b4b75936e5613c9553dbde37ba3013efec0 100644 --- a/propagation/propagation.go +++ b/propagation/propagation.go @@ -23,8 +23,11 @@ import ( "github.com/pkg/errors" ) -const splitToken string = "-" -const idToken string = "." +const ( + Header string = "sw6" + splitToken string = "-" + idToken string = "." +) var ( errEmptyHeader = errors.New("empty header") diff --git a/reporter/grpc.go b/reporter/grpc.go index 2590283fab809373d26764c3e182b5c93878652a..9afd39edb7c0cf9b7c12b1961573a229914b6dda 100644 --- a/reporter/grpc.go +++ b/reporter/grpc.go @@ -23,6 +23,7 @@ import ( "github.com/golang/protobuf/proto" "google.golang.org/grpc" + "google.golang.org/grpc/connectivity" "github.com/tetratelabs/go2sky" "github.com/tetratelabs/go2sky/pkg" @@ -44,7 +45,7 @@ var ( // NewGRPCReporter create a new reporter to send data to gRPC oap server func NewGRPCReporter(serverAddr string, opts ...GRPCReporterOption) (go2sky.Reporter, error) { r := &gRPCReporter{ - logger: log.New(os.Stderr, "go2sky", log.LstdFlags), + logger: log.New(os.Stderr, "go2sky-gRPC", log.LstdFlags), sendCh: make(chan *common.UpstreamSegment, maxSendQueueSize), pingInterval: defaultPingInterval, } @@ -196,6 +197,7 @@ func (r *gRPCReporter) Send(spans []go2sky.ReportedSpan) { IsError: s.IsError(), Tags: s.Tags(), Logs: s.Logs(), + ComponentId: s.ComponentID(), } srr := make([]*v2.SegmentReference, 0) if i == (spanSize-1) && spanCtx.ParentSpanID > -1 { @@ -244,12 +246,6 @@ func (r *gRPCReporter) Send(spans []go2sky.ReportedSpan) { func (r *gRPCReporter) Close() { close(r.sendCh) - if r.conn != nil { - err := r.conn.Close() - if err != nil { - r.logger.Print(err) - } - } } func (r *gRPCReporter) initSendPipeline() { @@ -274,6 +270,12 @@ func (r *gRPCReporter) initSendPipeline() { } } r.closeStream(stream) + if r.conn != nil { + if err := r.conn.Close(); err != nil { + r.logger.Print(err) + } + } + break } }() } @@ -291,6 +293,9 @@ func (r *gRPCReporter) ping() { } go func() { for { + if r.conn.GetState() == connectivity.Shutdown { + break + } _, err := r.pingClient.DoPing(context.Background(), ®ister.ServiceInstancePingPkg{ Time: pkg.Millisecond(time.Now()), ServiceInstanceId: r.instanceID, diff --git a/reporter/log.go b/reporter/log.go new file mode 100644 index 0000000000000000000000000000000000000000..285bf8594dfcf29bef516232d548ab12bed3d1ae --- /dev/null +++ b/reporter/log.go @@ -0,0 +1,59 @@ +// Copyright 2019 Tetrate Labs +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package reporter + +import ( + "encoding/json" + "log" + "os" + + "github.com/tetratelabs/go2sky" +) + +const ( + mockServiceID = 1 + mockInstanceID = 1 +) + +func NewLogReporter() (go2sky.Reporter, error) { + return &logReporter{logger: log.New(os.Stderr, "go2sky-log", log.LstdFlags)}, nil +} + +type logReporter struct { + logger *log.Logger +} + +func (lr *logReporter) Register(service string, instance string) (int32, int32, error) { + lr.logger.Println("Register log reporter") + // Mock register results for log reporter + return mockServiceID, mockInstanceID, nil +} + +func (lr *logReporter) Send(spans []go2sky.ReportedSpan) { + if spans == nil { + return + } + b, err := json.Marshal(spans) + if err != nil { + lr.logger.Printf("Error: %s", err) + return + } + root := spans[len(spans)-1] + lr.logger.Printf("Segment-%v: %s \n", root.Context().SegmentID, b) +} + +func (lr *logReporter) Close() { + lr.logger.Println("Close log reporter") +} diff --git a/segment.go b/segment.go index 6b3631d578bcf1761a607c4cadacf7f8fe4ec618..3cea3050bb1d3f35f900bc55f570ec9965629b9d 100644 --- a/segment.go +++ b/segment.go @@ -64,6 +64,7 @@ type ReportedSpan interface { IsError() bool Tags() []*common.KeyStringValuePair Logs() []*v2.Log + ComponentID() int32 } type segmentSpan interface { @@ -97,39 +98,43 @@ func (s *segmentSpanImpl) Refs() []*propagation.SpanContext { } func (s *segmentSpanImpl) StartTime() int64 { - return pkg.Millisecond(s.startTime) + return pkg.Millisecond(s.defaultSpan.StartTime) } func (s *segmentSpanImpl) EndTime() int64 { - return pkg.Millisecond(s.endTime) + return pkg.Millisecond(s.defaultSpan.EndTime) } func (s *segmentSpanImpl) OperationName() string { - return s.operationName + return s.defaultSpan.OperationName } func (s *segmentSpanImpl) Peer() string { - return s.peer + return s.defaultSpan.Peer } func (s *segmentSpanImpl) SpanType() common.SpanType { - return common.SpanType(s.spanType) + return common.SpanType(s.defaultSpan.SpanType) } func (s *segmentSpanImpl) SpanLayer() common.SpanLayer { - return s.layer + return s.defaultSpan.Layer } func (s *segmentSpanImpl) IsError() bool { - return s.isError + return s.defaultSpan.IsError } func (s *segmentSpanImpl) Tags() []*common.KeyStringValuePair { - return s.tags + return s.defaultSpan.Tags } func (s *segmentSpanImpl) Logs() []*v2.Log { - return s.logs + return s.defaultSpan.Logs +} + +func (s *segmentSpanImpl) ComponentID() int32 { + return s.defaultSpan.ComponentID } func (s *segmentSpanImpl) context() SegmentContext { diff --git a/span.go b/span.go index 2338333fd51b4b84c542844c3b7ad9afcdd272f5..323b5a27976f41d62200bfc617c5841005ab1136 100644 --- a/span.go +++ b/span.go @@ -41,7 +41,8 @@ type Span interface { SetOperationName(string) SetPeer(string) SetSpanLayer(common.SpanLayer) - Tag(string, string) + SetComponent(int32) + Tag(Tag, string) Log(time.Time, ...string) Error(time.Time, ...string) End() @@ -50,41 +51,46 @@ type Span interface { func newLocalSpan(t *Tracer) *defaultSpan { return &defaultSpan{ tracer: t, - startTime: time.Now(), - spanType: SpanTypeLocal, + StartTime: time.Now(), + SpanType: SpanTypeLocal, } } type defaultSpan struct { Refs []*propagation.SpanContext tracer *Tracer - startTime time.Time - endTime time.Time - operationName string - peer string - layer common.SpanLayer - tags []*common.KeyStringValuePair - logs []*v2.Log - isError bool - spanType SpanType + StartTime time.Time + EndTime time.Time + OperationName string + Peer string + Layer common.SpanLayer + ComponentID int32 + Tags []*common.KeyStringValuePair + Logs []*v2.Log + IsError bool + SpanType SpanType } // For Span func (ds *defaultSpan) SetOperationName(name string) { - ds.operationName = name + ds.OperationName = name } func (ds *defaultSpan) SetPeer(peer string) { - ds.peer = peer + ds.Peer = peer } func (ds *defaultSpan) SetSpanLayer(layer common.SpanLayer) { - ds.layer = layer + ds.Layer = layer } -func (ds *defaultSpan) Tag(key string, value string) { - ds.tags = append(ds.tags, &common.KeyStringValuePair{Key: key, Value: value}) +func (ds *defaultSpan) SetComponent(componentID int32) { + ds.ComponentID = componentID +} + +func (ds *defaultSpan) Tag(key Tag, value string) { + ds.Tags = append(ds.Tags, &common.KeyStringValuePair{Key: string(key), Value: value}) } func (ds *defaultSpan) Log(time time.Time, ll ...string) { @@ -101,18 +107,36 @@ func (ds *defaultSpan) Log(time time.Time, ll ...string) { } } } - ds.logs = append(ds.logs, &v2.Log{Time: pkg.Millisecond(time), Data: data}) + ds.Logs = append(ds.Logs, &v2.Log{Time: pkg.Millisecond(time), Data: data}) } func (ds *defaultSpan) Error(time time.Time, ll ...string) { - ds.isError = true + ds.IsError = true ds.Log(time, ll...) } func (ds *defaultSpan) End() { - ds.endTime = time.Now() + ds.EndTime = time.Now() } // SpanOption allows for functional options to adjust behaviour // of a Span to be created by CreateLocalSpan type SpanOption func(s *defaultSpan) + +// Tag are supported by sky-walking engine. +// As default, all Tags will be stored, but these ones have +// particular meanings. +type Tag string + +const ( + TagURL Tag = "url" + TagStatusCode Tag = "status_code" + TagHTTPMethod Tag = "http.method" + TagDBType Tag = "db.type" + TagDBInstance Tag = "db.instance" + TagDBStatement Tag = "db.statement" + TagDBBindVariables Tag = "db.bind_vars" + TagMQQueue Tag = "mq.queue" + TagMQBroker Tag = "mq.broker" + TagMQTopic Tag = "mq.topic" +) diff --git a/span_opts.go b/span_opts.go index 55e381d30caf8648c8c54f3055226084dce0c88f..31d49763abdc426d94b86ca068a00860d0da405b 100644 --- a/span_opts.go +++ b/span_opts.go @@ -29,6 +29,6 @@ func WithContext(sc *propagation.SpanContext) SpanOption { // WithSpanType setup span type of a span func WithSpanType(spanType SpanType) SpanOption { return func(s *defaultSpan) { - s.spanType = spanType + s.SpanType = spanType } } diff --git a/span_test.go b/span_test.go index edee91f868f0b80bed2e2acc52517a3a443e5a08..4fb36a4e34a7b232bec9e4b0457d85a5d591edf2 100644 --- a/span_test.go +++ b/span_test.go @@ -39,7 +39,7 @@ func Test_defaultSpan_SetOperationName(t *testing.T) { t.Run(tt.name, func(t *testing.T) { ds := &defaultSpan{} ds.SetOperationName(tt.args.name) - if ds.operationName != tt.args.name { + if ds.OperationName != tt.args.name { t.Error("operation name is different") } }) @@ -55,7 +55,7 @@ func Test_defaultSpan_SetPeer(t *testing.T) { args args }{ { - "set peer", + "set Peer", struct{ name string }{name: "localhost:9999"}, }, } @@ -63,8 +63,8 @@ func Test_defaultSpan_SetPeer(t *testing.T) { t.Run(tt.name, func(t *testing.T) { ds := &defaultSpan{} ds.SetPeer(tt.args.name) - if ds.peer != tt.args.name { - t.Error("peer is different") + if ds.Peer != tt.args.name { + t.Error("Peer is different") } }) } @@ -107,8 +107,8 @@ func Test_defaultSpan_SetSpanLayer(t *testing.T) { t.Run(tt.name, func(t *testing.T) { ds := &defaultSpan{} ds.SetSpanLayer(tt.args.layer) - if ds.layer != tt.args.layer { - t.Error("span layer is different") + if ds.Layer != tt.args.layer { + t.Error("span Layer is different") } }) } @@ -116,7 +116,7 @@ func Test_defaultSpan_SetSpanLayer(t *testing.T) { func Test_defaultSpan_Tag(t *testing.T) { type args struct { - key string + key Tag value string } tests := []struct { @@ -146,8 +146,8 @@ func Test_defaultSpan_Tag(t *testing.T) { for _, a := range tt.args { ds.Tag(a.key, a.value) } - if len(ds.tags) != len(tt.args) { - t.Error("tags are not set property") + if len(ds.Tags) != len(tt.args) { + t.Error("Tags are not set property") } }) } @@ -159,19 +159,19 @@ func Test_defaultSpan_Log(t *testing.T) { ll []string }{ { - "set null logs", + "set null Logs", []string{}, }, { - "set logs", + "set Logs", []string{"name", "value", "name1", "value1"}, }, { - "set duplicated logs", + "set duplicated Logs", []string{"name", "value", "name1", "value1"}, }, { - "set invalid logs", + "set invalid Logs", []string{"name", "value", "name1"}, }, } @@ -179,12 +179,12 @@ func Test_defaultSpan_Log(t *testing.T) { t.Run(tt.name, func(t *testing.T) { ds := &defaultSpan{} ds.Log(time.Now(), tt.ll...) - if len(ds.logs) != 1 { - t.Error("logs are not set property") + if len(ds.Logs) != 1 { + t.Error("Logs are not set property") } - for _, l := range ds.logs { + for _, l := range ds.Logs { if len(l.Data) != int(math.Ceil(float64(len(tt.ll))/2)) { - t.Error("logs are not set property") + t.Error("Logs are not set property") } } }) @@ -213,13 +213,13 @@ func Test_defaultSpan_Error(t *testing.T) { t.Run(tt.name, func(t *testing.T) { ds := &defaultSpan{} ds.Error(time.Now(), tt.ll...) - if !ds.isError { + if !ds.IsError { t.Error("errors are not set property") } - if len(ds.logs) != 1 { + if len(ds.Logs) != 1 { t.Error("errors are not set property") } - for _, l := range ds.logs { + for _, l := range ds.Logs { if len(l.Data) != int(math.Ceil(float64(len(tt.ll))/2)) { t.Error("errors are not set property") } diff --git a/trace.go b/trace.go index f0828bc8b33dc39fe3a3556200574a6d57537de9..b0254fa9c07c180bbf29e788ba170aec9d6b1202 100644 --- a/trace.go +++ b/trace.go @@ -23,10 +23,11 @@ import ( "github.com/google/uuid" "github.com/pkg/errors" + "github.com/tetratelabs/go2sky/pkg" "github.com/tetratelabs/go2sky/propagation" ) -var errParameter = errors.New("parameter are nil") +const errParameter = pkg.Error("parameter are nil") // Tracer is go2sky tracer implementation. type Tracer struct { @@ -145,7 +146,7 @@ func (t *Tracer) CreateLocalSpan(ctx context.Context, opts ...SpanOption) (s Spa if ctx == nil { return nil, nil, errParameter } - if s, _ = t.createNoop(ctx); s != nil { + if s, c = t.createNoop(ctx); s != nil { return } ds := newLocalSpan(t) @@ -185,8 +186,9 @@ func (t *Tracer) CreateExitSpan(ctx context.Context, operationName string, peer spanContext.ParentSegmentID = span.Context().SegmentID spanContext.NetworkAddress = peer spanContext.ParentServiceInstanceID = t.instanceID - // TODO confirm client spanContext.EntryServiceInstanceID = t.instanceID + spanContext.EntryEndpoint = operationName + spanContext.ParentEndpoint = operationName ref, ok := ctx.Value(refKeyInstance).(*propagation.SpanContext) if ok && ref != nil { spanContext.Sample = ref.Sample diff --git a/trace_test.go b/trace_test.go index 74b3ebbd91f5ab16517a2a05fe0e66219ec926b7..130324366bd91a449b223f6a20d3baa29bb1bf7f 100644 --- a/trace_test.go +++ b/trace_test.go @@ -21,6 +21,7 @@ import ( "testing" "github.com/pkg/errors" + "github.com/tetratelabs/go2sky/propagation" ) @@ -197,7 +198,7 @@ func TestTracer_CreateEntrySpan_Parameter(t *testing.T) { true, }, { - "operationName is nil", + "OperationName is nil", struct { ctx context.Context operationName string @@ -301,7 +302,7 @@ func TestTracer_CreateExitSpan_Parameter(t *testing.T) { true, }, { - "operationName is nil", + "OperationName is nil", struct { ctx context.Context operationName string @@ -313,7 +314,7 @@ func TestTracer_CreateExitSpan_Parameter(t *testing.T) { true, }, { - "peer is nil", + "Peer is nil", struct { ctx context.Context operationName string