mirror of
https://github.com/open-telemetry/opentelemetry-go.git
synced 2024-11-28 08:38:51 +02:00
Trace sdk (#65)
* trace sdk initial commit. * fix imports and comments. * remove tracestate * split trace.go * add attribute over limit test. * add comments and restructure span.go and tracer.go * refactor MessageEvent * defer unlock * some more cleanup in span.go * rename *MessageEvent* to *Event* * cleanup comments in trace_test.go * fix typos. * return full string ID for traceID and spanID.
This commit is contained in:
parent
ed3b26b6c8
commit
0f32efcdaa
@ -53,14 +53,13 @@ func (sc SpanContext) HasSpanID() bool {
|
||||
}
|
||||
|
||||
func (sc SpanContext) SpanIDString() string {
|
||||
p := fmt.Sprintf("%.16x", sc.SpanID)
|
||||
return p[0:3] + ".." + p[13:16]
|
||||
return fmt.Sprintf("%.16x", sc.SpanID)
|
||||
}
|
||||
|
||||
func (sc SpanContext) TraceIDString() string {
|
||||
p1 := fmt.Sprintf("%.16x", sc.TraceID.High)
|
||||
p2 := fmt.Sprintf("%.16x", sc.TraceID.Low)
|
||||
return p1[0:3] + ".." + p2[13:16]
|
||||
return p1 + p2
|
||||
}
|
||||
|
||||
func (sc SpanContext) IsSampled() bool {
|
||||
|
@ -88,11 +88,11 @@ func TestSpanIDString(t *testing.T) {
|
||||
{
|
||||
name: "fourtytwo",
|
||||
sc: SpanContext{SpanID: uint64(42)},
|
||||
want: `000..02a`,
|
||||
want: `000000000000002a`,
|
||||
}, {
|
||||
name: "empty",
|
||||
sc: SpanContext{},
|
||||
want: `000..000`,
|
||||
want: `0000000000000000`,
|
||||
},
|
||||
} {
|
||||
t.Run(testcase.name, func(t *testing.T) {
|
||||
@ -119,11 +119,11 @@ func TestTraceIDString(t *testing.T) {
|
||||
Low: uint64(42),
|
||||
},
|
||||
},
|
||||
want: `000..02a`,
|
||||
want: `000000000000002a000000000000002a`,
|
||||
}, {
|
||||
name: "empty",
|
||||
sc: SpanContext{TraceID: TraceID{}},
|
||||
want: `000..000`,
|
||||
want: `00000000000000000000000000000000`,
|
||||
},
|
||||
} {
|
||||
t.Run(testcase.name, func(t *testing.T) {
|
||||
|
7
go.mod
7
go.mod
@ -5,9 +5,10 @@ go 1.12
|
||||
require (
|
||||
github.com/gogo/protobuf v1.2.1 // indirect
|
||||
github.com/golang/mock v1.2.0 // indirect
|
||||
github.com/golang/protobuf v1.3.1 // indirect
|
||||
github.com/golang/protobuf v1.3.1
|
||||
github.com/golangci/golangci-lint v1.17.1
|
||||
github.com/google/go-cmp v0.3.0
|
||||
github.com/hashicorp/golang-lru v0.5.1
|
||||
github.com/lightstep/tracecontext.go v0.0.0-20181129014701-1757c391b1ac
|
||||
github.com/onsi/ginkgo v1.8.0 // indirect
|
||||
github.com/onsi/gomega v1.5.0 // indirect
|
||||
@ -15,10 +16,10 @@ require (
|
||||
github.com/sirupsen/logrus v1.2.0 // indirect
|
||||
github.com/spf13/cobra v0.0.5 // indirect
|
||||
github.com/stretchr/testify v1.3.0 // indirect
|
||||
go.opencensus.io v0.22.0
|
||||
golang.org/x/crypto v0.0.0-20190424203555-c05e17bb3b2d // indirect
|
||||
golang.org/x/net v0.0.0-20190613194153-d28f0bde5980 // indirect
|
||||
golang.org/x/net v0.0.0-20190613194153-d28f0bde5980
|
||||
golang.org/x/sys v0.0.0-20190614160838-b47fdc937951 // indirect
|
||||
golang.org/x/text v0.3.2 // indirect
|
||||
golang.org/x/tools v0.0.0-20190521203540-521d6ed310dd
|
||||
google.golang.org/appengine v1.4.0 // indirect
|
||||
google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873 // indirect
|
||||
|
10
go.sum
10
go.sum
@ -112,6 +112,8 @@ github.com/google/go-cmp v0.3.0 h1:crn/baboCvb5fXaQ0IJ1SGTsTVrWpDsCWC8EGETZijY=
|
||||
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
|
||||
github.com/gostaticanalysis/analysisutil v0.0.0-20190318220348-4088753ea4d3 h1:JVnpOZS+qxli+rgVl98ILOXVNbW+kb5wcxeGx8ShUIw=
|
||||
github.com/gostaticanalysis/analysisutil v0.0.0-20190318220348-4088753ea4d3/go.mod h1:eEOZF4jCKGi+aprrirO9e7WKB3beBRtWgqGunKl6pKE=
|
||||
github.com/hashicorp/golang-lru v0.5.1 h1:0hERBMJE1eitiLkihrMvRVBYAkpHzc/J3QdDN+dAcgU=
|
||||
github.com/hashicorp/golang-lru v0.5.1/go.mod h1:/m3WP610KZHVQ1SGc6re/UDhFvYD7pJ4Ao+sR/qLZy8=
|
||||
github.com/hashicorp/hcl v0.0.0-20180404174102-ef8a98b0bbce h1:xdsDDbiBDQTKASoGEZ+pEmF1OnWuu8AQ9I8iNbHNeno=
|
||||
github.com/hashicorp/hcl v0.0.0-20180404174102-ef8a98b0bbce/go.mod h1:oZtUIOe8dh44I2q6ScRibXws4Ajl+d+nod3AaR9vL5w=
|
||||
github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4=
|
||||
@ -233,6 +235,10 @@ github.com/valyala/fasthttp v1.2.0/go.mod h1:4vX61m6KN+xDduDNwXrhIAVZaZaZiQ1luJk
|
||||
github.com/valyala/quicktemplate v1.1.1/go.mod h1:EH+4AkTd43SvgIbQHYu59/cJyxDoOVRUAfrukLPuGJ4=
|
||||
github.com/valyala/tcplisten v0.0.0-20161114210144-ceec8f93295a/go.mod h1:v3UYOV9WzVtRmSR+PDvWpU/qWl4Wa5LApYYX4ZtKbio=
|
||||
github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q=
|
||||
go.opencensus.io v0.22.0 h1:C9hSCOW830chIVkdja34wa6Ky+IzWllkUinR+BtRZd4=
|
||||
go.opencensus.io v0.22.0/go.mod h1:+kGneAE2xo2IficOXnaByMWTGM9T73dGwxeWcUqIpI8=
|
||||
go.opentelemetry.io v0.0.0-20190715214921-eed647d11f57 h1:5uH+S26G3/t2aU82KLk7vvytwkFoH7ynSIrSQo5CxSI=
|
||||
go.opentelemetry.io v0.0.0-20190715214921-eed647d11f57/go.mod h1:7pfucu8MX8izd4pjPRY2kCQQMtFi4DWT/IV7KA3sjOQ=
|
||||
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
@ -262,6 +268,7 @@ golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn
|
||||
golang.org/x/net v0.0.0-20190313220215-9f648a60d977 h1:actzWV6iWn3GLqN8dZjzsB+CLt+gaV2+wsxroxiQI8I=
|
||||
golang.org/x/net v0.0.0-20190313220215-9f648a60d977/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190501004415-9ce7a6920f09/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190613194153-d28f0bde5980 h1:dfGZHvZk057jK2MCeWus/TowKpJ8y4AmooUzdBSR9GU=
|
||||
golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||
@ -283,6 +290,7 @@ golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5h
|
||||
golang.org/x/sys v0.0.0-20190312061237-fead79001313 h1:pczuHS43Cp2ktBEEmLwScxgjWsBSzdaQiKzUyf3DTTc=
|
||||
golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190502145724-3ef323f4f1fd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190614160838-b47fdc937951 h1:ZUgGZ7PSkne6oY+VgAvayrB16owfm9/DKAtgWubzgzU=
|
||||
golang.org/x/sys v0.0.0-20190614160838-b47fdc937951/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/text v0.0.0-20170915090833-1cbadb444a80/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
@ -310,9 +318,11 @@ google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9Ywl
|
||||
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
|
||||
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8 h1:Nw54tB0rB7hY/N0NQvRW8DG4Yk3Q6T9cu9RcFQDu1tc=
|
||||
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=
|
||||
google.golang.org/genproto v0.0.0-20190425155659-357c62f0e4bb/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||
google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873 h1:nfPFGzJkUDX6uBmpN/pSw7MbOAWegH5QDQuoXFHedLg=
|
||||
google.golang.org/genproto v0.0.0-20190502173448-54afdca5d873/go.mod h1:VzzqZJRnGkLBvHegQrXjBqPurQTc5/KpmUdxsrq26oE=
|
||||
google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c=
|
||||
google.golang.org/grpc v1.20.1/go.mod h1:10oTOabMzJvdu6/UiuZezV6QK5dSlG84ov/aaiqXj38=
|
||||
google.golang.org/grpc v1.21.1 h1:j6XxA85m/6txkUCHvzlV5f+HBNl/1r5cZ2A/3IEFOO8=
|
||||
google.golang.org/grpc v1.21.1/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ijfRaM=
|
||||
gopkg.in/airbrake/gobrake.v2 v2.0.9 h1:7z2uVWwn7oVeeugY1DtlPAy5H+KYgB1KeKTnqjNatLo=
|
||||
|
0
sdk/README.md
Normal file
0
sdk/README.md
Normal file
37
sdk/internal/internal.go
Normal file
37
sdk/internal/internal.go
Normal file
@ -0,0 +1,37 @@
|
||||
// Copyright 2019, OpenTelemetry Authors
|
||||
//
|
||||
// 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 internal // import "go.opentelemetry.io/sdk/internal"
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
opentelemetry "go.opentelemetry.io/sdk"
|
||||
)
|
||||
|
||||
// UserAgent is the user agent to be added to the outgoing
|
||||
// requests from the exporters.
|
||||
var UserAgent = fmt.Sprintf("opentelemetry-go/%s", opentelemetry.Version())
|
||||
|
||||
// MonotonicEndTime returns the end time at present
|
||||
// but offset from start, monotonically.
|
||||
//
|
||||
// The monotonic clock is used in subtractions hence
|
||||
// the duration since start added back to start gives
|
||||
// end as a monotonic time.
|
||||
// See https://golang.org/pkg/time/#hdr-Monotonic_Clocks
|
||||
func MonotonicEndTime(start time.Time) time.Time {
|
||||
return start.Add(time.Since(start))
|
||||
}
|
50
sdk/internal/sanitize.go
Normal file
50
sdk/internal/sanitize.go
Normal file
@ -0,0 +1,50 @@
|
||||
// Copyright 2019, OpenTelemetry Authors
|
||||
//
|
||||
// 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 internal
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"unicode"
|
||||
)
|
||||
|
||||
const labelKeySizeLimit = 100
|
||||
|
||||
// Sanitize returns a string that is trunacated to 100 characters if it's too
|
||||
// long, and replaces non-alphanumeric characters to underscores.
|
||||
func Sanitize(s string) string {
|
||||
if len(s) == 0 {
|
||||
return s
|
||||
}
|
||||
if len(s) > labelKeySizeLimit {
|
||||
s = s[:labelKeySizeLimit]
|
||||
}
|
||||
s = strings.Map(sanitizeRune, s)
|
||||
if unicode.IsDigit(rune(s[0])) {
|
||||
s = "key_" + s
|
||||
}
|
||||
if s[0] == '_' {
|
||||
s = "key" + s
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
// converts anything that is not a letter or digit to an underscore
|
||||
func sanitizeRune(r rune) rune {
|
||||
if unicode.IsLetter(r) || unicode.IsDigit(r) {
|
||||
return r
|
||||
}
|
||||
// Everything else turns into an underscore
|
||||
return '_'
|
||||
}
|
67
sdk/internal/sanitize_test.go
Normal file
67
sdk/internal/sanitize_test.go
Normal file
@ -0,0 +1,67 @@
|
||||
// Copyright 2019, OpenTelemetry Authors
|
||||
//
|
||||
// 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 internal
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestSanitize(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
input string
|
||||
want string
|
||||
}{
|
||||
{
|
||||
name: "trunacate long string",
|
||||
input: strings.Repeat("a", 101),
|
||||
want: strings.Repeat("a", 100),
|
||||
},
|
||||
{
|
||||
name: "replace character",
|
||||
input: "test/key-1",
|
||||
want: "test_key_1",
|
||||
},
|
||||
{
|
||||
name: "add prefix if starting with digit",
|
||||
input: "0123456789",
|
||||
want: "key_0123456789",
|
||||
},
|
||||
{
|
||||
name: "add prefix if starting with _",
|
||||
input: "_0123456789",
|
||||
want: "key_0123456789",
|
||||
},
|
||||
{
|
||||
name: "starts with _ after sanitization",
|
||||
input: "/0123456789",
|
||||
want: "key_0123456789",
|
||||
},
|
||||
{
|
||||
name: "valid input",
|
||||
input: "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz_0123456789",
|
||||
want: "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz_0123456789",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
if got, want := Sanitize(tt.input), tt.want; got != want {
|
||||
t.Errorf("sanitize() = %q; want %q", got, want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
21
sdk/opentelemetry.go
Normal file
21
sdk/opentelemetry.go
Normal file
@ -0,0 +1,21 @@
|
||||
// Copyright 2019, OpenTelemetry Authors
|
||||
//
|
||||
// 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 opentelemetry contains Go support for OpenTelemetry.
|
||||
package opentelemetry // import "go.opentelemetry.io/sdk"
|
||||
|
||||
// Version is the current release version of OpenTelemetry in use.
|
||||
func Version() string {
|
||||
return "0.1.0"
|
||||
}
|
40
sdk/trace/basetypes.go
Normal file
40
sdk/trace/basetypes.go
Normal file
@ -0,0 +1,40 @@
|
||||
// Copyright 2019, OpenTelemetry Authors
|
||||
//
|
||||
// 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 trace
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"go.opentelemetry.io/api/core"
|
||||
apievent "go.opentelemetry.io/api/event"
|
||||
)
|
||||
|
||||
// event is used to describe an event with a message string and set of
|
||||
// attributes.
|
||||
type event struct {
|
||||
msg string
|
||||
attributes []core.KeyValue
|
||||
time time.Time
|
||||
}
|
||||
|
||||
var _ apievent.Event = &event{}
|
||||
|
||||
func (me *event) Message() string {
|
||||
return me.msg
|
||||
}
|
||||
|
||||
func (me *event) Attributes() []core.KeyValue {
|
||||
return me.attributes
|
||||
}
|
182
sdk/trace/benchmark_test.go
Normal file
182
sdk/trace/benchmark_test.go
Normal file
@ -0,0 +1,182 @@
|
||||
// Copyright 2019, OpenTelemetry Authors
|
||||
//
|
||||
// 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 trace
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"go.opentelemetry.io/api/core"
|
||||
"go.opentelemetry.io/api/key"
|
||||
)
|
||||
|
||||
func BenchmarkStartEndSpan(b *testing.B) {
|
||||
t := tracer{}
|
||||
|
||||
traceBenchmark(b, func(b *testing.B) {
|
||||
ctx := context.Background()
|
||||
b.ResetTimer()
|
||||
for i := 0; i < b.N; i++ {
|
||||
_, span := t.Start(ctx, "/foo")
|
||||
span.Finish()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func BenchmarkSpanWithAttributes_4(b *testing.B) {
|
||||
t := tracer{}
|
||||
|
||||
traceBenchmark(b, func(b *testing.B) {
|
||||
ctx := context.Background()
|
||||
b.ResetTimer()
|
||||
|
||||
for i := 0; i < b.N; i++ {
|
||||
_, span := t.Start(ctx, "/foo")
|
||||
span.SetAttributes(
|
||||
key.New("key1").Bool(false),
|
||||
key.New("key2").String("hello"),
|
||||
key.New("key3").Uint64(123),
|
||||
key.New("key4").Float64(123.456),
|
||||
)
|
||||
span.Finish()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func BenchmarkSpanWithAttributes_8(b *testing.B) {
|
||||
t := tracer{}
|
||||
|
||||
traceBenchmark(b, func(b *testing.B) {
|
||||
ctx := context.Background()
|
||||
b.ResetTimer()
|
||||
|
||||
for i := 0; i < b.N; i++ {
|
||||
_, span := t.Start(ctx, "/foo")
|
||||
span.SetAttributes(
|
||||
key.New("key1").Bool(false),
|
||||
key.New("key2").String("hello"),
|
||||
key.New("key3").Uint64(123),
|
||||
key.New("key4").Float64(123.456),
|
||||
key.New("key21").Bool(false),
|
||||
key.New("key22").String("hello"),
|
||||
key.New("key23").Uint64(123),
|
||||
key.New("key24").Float64(123.456),
|
||||
)
|
||||
span.Finish()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func BenchmarkSpanWithAttributes_all(b *testing.B) {
|
||||
t := tracer{}
|
||||
|
||||
traceBenchmark(b, func(b *testing.B) {
|
||||
ctx := context.Background()
|
||||
b.ResetTimer()
|
||||
|
||||
for i := 0; i < b.N; i++ {
|
||||
_, span := t.Start(ctx, "/foo")
|
||||
span.SetAttributes(
|
||||
key.New("key1").Bool(false),
|
||||
key.New("key2").String("hello"),
|
||||
key.New("key3").Int64(123),
|
||||
key.New("key4").Uint64(123),
|
||||
key.New("key5").Int32(123),
|
||||
key.New("key6").Uint32(123),
|
||||
key.New("key7").Float64(123.456),
|
||||
key.New("key8").Float32(123.456),
|
||||
key.New("key9").Bytes([]byte{1, 2, 3, 4}),
|
||||
key.New("key10").Int(123),
|
||||
key.New("key11").Uint(123),
|
||||
)
|
||||
span.Finish()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func BenchmarkSpanWithAttributes_all_2x(b *testing.B) {
|
||||
t := tracer{}
|
||||
traceBenchmark(b, func(b *testing.B) {
|
||||
ctx := context.Background()
|
||||
b.ResetTimer()
|
||||
|
||||
for i := 0; i < b.N; i++ {
|
||||
_, span := t.Start(ctx, "/foo")
|
||||
span.SetAttributes(
|
||||
key.New("key1").Bool(false),
|
||||
key.New("key2").String("hello"),
|
||||
key.New("key3").Int64(123),
|
||||
key.New("key4").Uint64(123),
|
||||
key.New("key5").Int32(123),
|
||||
key.New("key6").Uint32(123),
|
||||
key.New("key7").Float64(123.456),
|
||||
key.New("key8").Float32(123.456),
|
||||
key.New("key9").Bytes([]byte{1, 2, 3, 4}),
|
||||
key.New("key10").Int(123),
|
||||
key.New("key11").Uint(123),
|
||||
key.New("key21").Bool(false),
|
||||
key.New("key22").String("hello"),
|
||||
key.New("key23").Int64(123),
|
||||
key.New("key24").Uint64(123),
|
||||
key.New("key25").Int32(123),
|
||||
key.New("key26").Uint32(123),
|
||||
key.New("key27").Float64(123.456),
|
||||
key.New("key28").Float32(123.456),
|
||||
key.New("key29").Bytes([]byte{1, 2, 3, 4}),
|
||||
key.New("key210").Int(123),
|
||||
key.New("key211").Uint(123),
|
||||
)
|
||||
span.Finish()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func BenchmarkTraceID_DotString(b *testing.B) {
|
||||
traceBenchmark(b, func(b *testing.B) {
|
||||
sc := core.SpanContext{TraceID: core.TraceID{High: 1, Low: 0x2a}}
|
||||
|
||||
want := "0000000000000001000000000000002a"
|
||||
for i := 0; i < b.N; i++ {
|
||||
if got := sc.TraceIDString(); got != want {
|
||||
b.Fatalf("got = %q want = %q", got, want)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func BenchmarkSpanID_DotString(b *testing.B) {
|
||||
traceBenchmark(b, func(b *testing.B) {
|
||||
sc := core.SpanContext{SpanID: 1}
|
||||
want := "0000000000000001"
|
||||
for i := 0; i < b.N; i++ {
|
||||
if got := sc.SpanIDString(); got != want {
|
||||
b.Fatalf("got = %q want = %q", got, want)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func traceBenchmark(b *testing.B, fn func(*testing.B)) {
|
||||
b.Run("AlwaysSample", func(b *testing.B) {
|
||||
b.ReportAllocs()
|
||||
ApplyConfig(Config{DefaultSampler: AlwaysSample()})
|
||||
fn(b)
|
||||
})
|
||||
b.Run("NeverSample", func(b *testing.B) {
|
||||
b.ReportAllocs()
|
||||
ApplyConfig(Config{DefaultSampler: NeverSample()})
|
||||
fn(b)
|
||||
})
|
||||
}
|
77
sdk/trace/config.go
Normal file
77
sdk/trace/config.go
Normal file
@ -0,0 +1,77 @@
|
||||
// Copyright 2019, OpenTelemetry Authors
|
||||
//
|
||||
// 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 trace
|
||||
|
||||
import (
|
||||
"sync"
|
||||
|
||||
"go.opentelemetry.io/sdk/trace/internal"
|
||||
)
|
||||
|
||||
// Config represents the global tracing configuration.
|
||||
type Config struct {
|
||||
// DefaultSampler is the default sampler used when creating new spans.
|
||||
DefaultSampler Sampler
|
||||
|
||||
// IDGenerator is for internal use only.
|
||||
IDGenerator internal.IDGenerator
|
||||
|
||||
// MaxEventsPerSpan is max number of message events per span
|
||||
MaxEventsPerSpan int
|
||||
|
||||
// MaxAnnotationEventsPerSpan is max number of attributes per span
|
||||
MaxAttributesPerSpan int
|
||||
|
||||
// MaxLinksPerSpan is max number of links per span
|
||||
MaxLinksPerSpan int
|
||||
}
|
||||
|
||||
var configWriteMu sync.Mutex
|
||||
|
||||
const (
|
||||
// DefaultMaxEventsPerSpan is default max number of message events per span
|
||||
DefaultMaxEventsPerSpan = 128
|
||||
|
||||
// DefaultMaxAttributesPerSpan is default max number of attributes per span
|
||||
DefaultMaxAttributesPerSpan = 32
|
||||
|
||||
// DefaultMaxLinksPerSpan is default max number of links per span
|
||||
DefaultMaxLinksPerSpan = 32
|
||||
)
|
||||
|
||||
// ApplyConfig applies changes to the global tracing configuration.
|
||||
//
|
||||
// Fields not provided in the given config are going to be preserved.
|
||||
func ApplyConfig(cfg Config) {
|
||||
configWriteMu.Lock()
|
||||
defer configWriteMu.Unlock()
|
||||
c := *config.Load().(*Config)
|
||||
if cfg.DefaultSampler != nil {
|
||||
c.DefaultSampler = cfg.DefaultSampler
|
||||
}
|
||||
if cfg.IDGenerator != nil {
|
||||
c.IDGenerator = cfg.IDGenerator
|
||||
}
|
||||
if cfg.MaxEventsPerSpan > 0 {
|
||||
c.MaxEventsPerSpan = cfg.MaxEventsPerSpan
|
||||
}
|
||||
if cfg.MaxAttributesPerSpan > 0 {
|
||||
c.MaxAttributesPerSpan = cfg.MaxAttributesPerSpan
|
||||
}
|
||||
if cfg.MaxLinksPerSpan > 0 {
|
||||
c.MaxLinksPerSpan = cfg.MaxLinksPerSpan
|
||||
}
|
||||
config.Store(&c)
|
||||
}
|
81
sdk/trace/config_test.go
Normal file
81
sdk/trace/config_test.go
Normal file
@ -0,0 +1,81 @@
|
||||
// Copyright 2019, OpenTelemetry Authors
|
||||
//
|
||||
// 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 trace
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestApplyConfig(t *testing.T) {
|
||||
testCfgs := []Config{
|
||||
{},
|
||||
{
|
||||
MaxAttributesPerSpan: 1,
|
||||
MaxEventsPerSpan: 3,
|
||||
MaxLinksPerSpan: 4,
|
||||
},
|
||||
{
|
||||
MaxAttributesPerSpan: -1,
|
||||
MaxEventsPerSpan: -3,
|
||||
MaxLinksPerSpan: 5,
|
||||
}}
|
||||
cfg := config.Load().(*Config)
|
||||
wantCfgs := []Config{
|
||||
{
|
||||
DefaultSampler: cfg.DefaultSampler,
|
||||
IDGenerator: cfg.IDGenerator,
|
||||
MaxAttributesPerSpan: DefaultMaxAttributesPerSpan,
|
||||
MaxEventsPerSpan: DefaultMaxEventsPerSpan,
|
||||
MaxLinksPerSpan: DefaultMaxLinksPerSpan,
|
||||
},
|
||||
{
|
||||
DefaultSampler: cfg.DefaultSampler,
|
||||
IDGenerator: cfg.IDGenerator,
|
||||
MaxAttributesPerSpan: 1,
|
||||
MaxEventsPerSpan: 3,
|
||||
MaxLinksPerSpan: 4,
|
||||
},
|
||||
{
|
||||
DefaultSampler: cfg.DefaultSampler,
|
||||
IDGenerator: cfg.IDGenerator,
|
||||
MaxAttributesPerSpan: 1,
|
||||
MaxEventsPerSpan: 3,
|
||||
MaxLinksPerSpan: 5,
|
||||
}}
|
||||
|
||||
for i, newCfg := range testCfgs {
|
||||
ApplyConfig(newCfg)
|
||||
gotCfg := config.Load().(*Config)
|
||||
wantCfg := wantCfgs[i]
|
||||
|
||||
if got, want := reflect.ValueOf(gotCfg.DefaultSampler).Pointer(), reflect.ValueOf(wantCfg.DefaultSampler).Pointer(); got != want {
|
||||
t.Fatalf("testId = %d config.DefaultSampler = %#v; want %#v", i, got, want)
|
||||
}
|
||||
if got, want := gotCfg.IDGenerator, wantCfg.IDGenerator; got != want {
|
||||
t.Fatalf("testId = %d config.IDGenerator = %#v; want %#v", i, got, want)
|
||||
}
|
||||
if got, want := gotCfg.MaxAttributesPerSpan, wantCfg.MaxAttributesPerSpan; got != want {
|
||||
t.Fatalf("testId = %d config.MaxAttributesPerSpan = %#v; want %#v", i, got, want)
|
||||
}
|
||||
if got, want := gotCfg.MaxLinksPerSpan, wantCfg.MaxLinksPerSpan; got != want {
|
||||
t.Fatalf("testId = %d config.MaxLinksPerSpan = %#v; want %#v", i, got, want)
|
||||
}
|
||||
if got, want := gotCfg.MaxEventsPerSpan, wantCfg.MaxEventsPerSpan; got != want {
|
||||
t.Fatalf("testId = %d config.MaxEventsPerSpan = %#v; want %#v", i, got, want)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
21
sdk/trace/doc.go
Normal file
21
sdk/trace/doc.go
Normal file
@ -0,0 +1,21 @@
|
||||
// Copyright 2019, OpenTelemetry Authors
|
||||
//
|
||||
// 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 trace contains support for OpenTelemetry distributed tracing.
|
||||
|
||||
The following assumes a basic familiarity with OpenTelemetry concepts.
|
||||
See http://opentelemetry.io
|
||||
*/
|
||||
package trace // import "go.opentelemetry.io/sdk/trace"
|
38
sdk/trace/evictedqueue.go
Normal file
38
sdk/trace/evictedqueue.go
Normal file
@ -0,0 +1,38 @@
|
||||
// Copyright 2019, OpenTelemetry Authors
|
||||
//
|
||||
// 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 trace
|
||||
|
||||
type evictedQueue struct {
|
||||
queue []interface{}
|
||||
capacity int
|
||||
droppedCount int
|
||||
}
|
||||
|
||||
func newEvictedQueue(capacity int) *evictedQueue {
|
||||
eq := &evictedQueue{
|
||||
capacity: capacity,
|
||||
queue: make([]interface{}, 0),
|
||||
}
|
||||
|
||||
return eq
|
||||
}
|
||||
|
||||
func (eq *evictedQueue) add(value interface{}) {
|
||||
if len(eq.queue) == eq.capacity {
|
||||
eq.queue = eq.queue[1:]
|
||||
eq.droppedCount++
|
||||
}
|
||||
eq.queue = append(eq.queue, value)
|
||||
}
|
65
sdk/trace/evictedqueue_test.go
Normal file
65
sdk/trace/evictedqueue_test.go
Normal file
@ -0,0 +1,65 @@
|
||||
// Copyright 2019, OpenTelemetry Authors
|
||||
//
|
||||
// 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 trace
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func init() {
|
||||
}
|
||||
|
||||
func TestAdd(t *testing.T) {
|
||||
q := newEvictedQueue(3)
|
||||
q.add("value1")
|
||||
q.add("value2")
|
||||
if wantLen, gotLen := 2, len(q.queue); wantLen != gotLen {
|
||||
t.Errorf("got queue length %d want %d", gotLen, wantLen)
|
||||
}
|
||||
}
|
||||
|
||||
func (eq *evictedQueue) queueToArray() []string {
|
||||
arr := make([]string, 0)
|
||||
for _, value := range eq.queue {
|
||||
arr = append(arr, value.(string))
|
||||
}
|
||||
return arr
|
||||
}
|
||||
|
||||
func TestDropCount(t *testing.T) {
|
||||
q := newEvictedQueue(3)
|
||||
q.add("value1")
|
||||
q.add("value2")
|
||||
q.add("value3")
|
||||
q.add("value1")
|
||||
q.add("value4")
|
||||
if wantLen, gotLen := 3, len(q.queue); wantLen != gotLen {
|
||||
t.Errorf("got queue length %d want %d", gotLen, wantLen)
|
||||
}
|
||||
if wantDropCount, gotDropCount := 2, q.droppedCount; wantDropCount != gotDropCount {
|
||||
t.Errorf("got drop count %d want %d", gotDropCount, wantDropCount)
|
||||
}
|
||||
wantArr := []string{"value3", "value1", "value4"}
|
||||
gotArr := q.queueToArray()
|
||||
|
||||
if wantLen, gotLen := len(wantArr), len(gotArr); gotLen != wantLen {
|
||||
t.Errorf("got array len %d want %d", gotLen, wantLen)
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(gotArr, wantArr) {
|
||||
t.Errorf("got array = %#v; want %#v", gotArr, wantArr)
|
||||
}
|
||||
}
|
41
sdk/trace/examples_test.go
Normal file
41
sdk/trace/examples_test.go
Normal file
@ -0,0 +1,41 @@
|
||||
// Copyright 2019, OpenTelemetry Authors
|
||||
//
|
||||
// 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 trace_test
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"go.opentelemetry.io/api/trace"
|
||||
)
|
||||
|
||||
// This example shows how to use trace.Start and (*Span).End to capture
|
||||
// a function execution in a Span. It assumes that the function
|
||||
// has a context.Context argument.
|
||||
func ExampleStart() {
|
||||
printEvens := func(ctx context.Context) {
|
||||
_, span := trace.GlobalTracer().Start(ctx, "my/package.Function")
|
||||
defer span.Finish()
|
||||
|
||||
for i := 0; i < 10; i++ {
|
||||
if i%2 == 0 {
|
||||
fmt.Printf("Even!\n")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ctx := context.Background()
|
||||
printEvens(ctx)
|
||||
}
|
97
sdk/trace/export.go
Normal file
97
sdk/trace/export.go
Normal file
@ -0,0 +1,97 @@
|
||||
// Copyright 2019, OpenTelemetry Authors
|
||||
//
|
||||
// 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 trace
|
||||
|
||||
import (
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
"time"
|
||||
|
||||
"go.opentelemetry.io/api/core"
|
||||
"google.golang.org/grpc/codes"
|
||||
)
|
||||
|
||||
// Exporter is a type for functions that receive sampled trace spans.
|
||||
//
|
||||
// The ExportSpan method should be safe for concurrent use and should return
|
||||
// quickly; if an Exporter takes a significant amount of time to process a
|
||||
// SpanData, that work should be done on another goroutine.
|
||||
//
|
||||
// The SpanData should not be modified, but a pointer to it can be kept.
|
||||
type Exporter interface {
|
||||
ExportSpan(s *SpanData)
|
||||
}
|
||||
|
||||
type exportersMap map[Exporter]struct{}
|
||||
|
||||
var (
|
||||
exporterMu sync.Mutex
|
||||
exporters atomic.Value
|
||||
)
|
||||
|
||||
// RegisterExporter adds to the list of Exporters that will receive sampled
|
||||
// trace spans.
|
||||
//
|
||||
// Binaries can register exporters, libraries shouldn't register exporters.
|
||||
func RegisterExporter(e Exporter) {
|
||||
exporterMu.Lock()
|
||||
defer exporterMu.Unlock()
|
||||
new := make(exportersMap)
|
||||
if old, ok := exporters.Load().(exportersMap); ok {
|
||||
for k, v := range old {
|
||||
new[k] = v
|
||||
}
|
||||
}
|
||||
new[e] = struct{}{}
|
||||
exporters.Store(new)
|
||||
}
|
||||
|
||||
// UnregisterExporter removes from the list of Exporters the Exporter that was
|
||||
// registered with the given name.
|
||||
func UnregisterExporter(e Exporter) {
|
||||
exporterMu.Lock()
|
||||
defer exporterMu.Unlock()
|
||||
new := make(exportersMap)
|
||||
if old, ok := exporters.Load().(exportersMap); ok {
|
||||
for k, v := range old {
|
||||
new[k] = v
|
||||
}
|
||||
}
|
||||
delete(new, e)
|
||||
exporters.Store(new)
|
||||
}
|
||||
|
||||
// SpanData contains all the information collected by a span.
|
||||
type SpanData struct {
|
||||
SpanContext core.SpanContext
|
||||
ParentSpanID uint64
|
||||
SpanKind int
|
||||
Name string
|
||||
StartTime time.Time
|
||||
// The wall clock time of EndTime will be adjusted to always be offset
|
||||
// from StartTime by the duration of the span.
|
||||
EndTime time.Time
|
||||
// The values of Attributes each have type string, bool, or int64.
|
||||
Attributes map[string]interface{}
|
||||
MessageEvents []event
|
||||
Status codes.Code
|
||||
HasRemoteParent bool
|
||||
DroppedAttributeCount int
|
||||
DroppedMessageEventCount int
|
||||
DroppedLinkCount int
|
||||
|
||||
// ChildSpanCount holds the number of child span created for this span.
|
||||
ChildSpanCount int
|
||||
}
|
67
sdk/trace/id_generator.go
Normal file
67
sdk/trace/id_generator.go
Normal file
@ -0,0 +1,67 @@
|
||||
// Copyright 2019, OpenTelemetry Authors
|
||||
//
|
||||
// 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 trace
|
||||
|
||||
import (
|
||||
"math/rand"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
|
||||
"go.opentelemetry.io/api/core"
|
||||
"go.opentelemetry.io/sdk/trace/internal"
|
||||
)
|
||||
|
||||
type defaultIDGenerator struct {
|
||||
sync.Mutex
|
||||
|
||||
// Please keep these as the first fields
|
||||
// so that these 8 byte fields will be aligned on addresses
|
||||
// divisible by 8, on both 32-bit and 64-bit machines when
|
||||
// performing atomic increments and accesses.
|
||||
// See:
|
||||
// * https://github.com/census-instrumentation/opencensus-go/issues/587
|
||||
// * https://github.com/census-instrumentation/opencensus-go/issues/865
|
||||
// * https://golang.org/pkg/sync/atomic/#pkg-note-BUG
|
||||
nextSpanID uint64
|
||||
spanIDInc uint64
|
||||
|
||||
traceIDAdd [2]uint64
|
||||
traceIDRand *rand.Rand
|
||||
}
|
||||
|
||||
var _ internal.IDGenerator = &defaultIDGenerator{}
|
||||
|
||||
// NewSpanID returns a non-zero span ID from a randomly-chosen sequence.
|
||||
func (gen *defaultIDGenerator) NewSpanID() uint64 {
|
||||
var id uint64
|
||||
for id == 0 {
|
||||
id = atomic.AddUint64(&gen.nextSpanID, gen.spanIDInc)
|
||||
}
|
||||
return id
|
||||
}
|
||||
|
||||
// NewTraceID returns a non-zero trace ID from a randomly-chosen sequence.
|
||||
// mu should be held while this function is called.
|
||||
func (gen *defaultIDGenerator) NewTraceID() core.TraceID {
|
||||
gen.Lock()
|
||||
defer gen.Unlock()
|
||||
// Construct the trace ID from two outputs of traceIDRand, with a constant
|
||||
// added to each half for additional entropy.
|
||||
tid := core.TraceID{
|
||||
High: gen.traceIDRand.Uint64() + gen.traceIDAdd[0],
|
||||
Low: gen.traceIDRand.Uint64() + gen.traceIDAdd[1],
|
||||
}
|
||||
return tid
|
||||
}
|
24
sdk/trace/internal/internal.go
Normal file
24
sdk/trace/internal/internal.go
Normal file
@ -0,0 +1,24 @@
|
||||
// Copyright 2019, OpenTelemetry Authors
|
||||
//
|
||||
// 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 internal provides trace internals.
|
||||
package internal
|
||||
|
||||
import "go.opentelemetry.io/api/core"
|
||||
|
||||
// IDGenerator allows custom generators for TraceId and SpanId.
|
||||
type IDGenerator interface {
|
||||
NewTraceID() core.TraceID
|
||||
NewSpanID() uint64
|
||||
}
|
37
sdk/trace/lrumap.go
Normal file
37
sdk/trace/lrumap.go
Normal file
@ -0,0 +1,37 @@
|
||||
// Copyright 2019, OpenTelemetry Authors
|
||||
//
|
||||
// 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 trace
|
||||
|
||||
import (
|
||||
"github.com/hashicorp/golang-lru/simplelru"
|
||||
)
|
||||
|
||||
type lruMap struct {
|
||||
simpleLruMap *simplelru.LRU
|
||||
droppedCount int
|
||||
}
|
||||
|
||||
func newLruMap(size int) *lruMap {
|
||||
lm := &lruMap{}
|
||||
lm.simpleLruMap, _ = simplelru.NewLRU(size, nil)
|
||||
return lm
|
||||
}
|
||||
|
||||
func (lm *lruMap) add(key, value interface{}) {
|
||||
evicted := lm.simpleLruMap.Add(key, value)
|
||||
if evicted {
|
||||
lm.droppedCount++
|
||||
}
|
||||
}
|
75
sdk/trace/sampling.go
Normal file
75
sdk/trace/sampling.go
Normal file
@ -0,0 +1,75 @@
|
||||
// Copyright 2019, OpenTelemetry Authors
|
||||
//
|
||||
// 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 trace
|
||||
|
||||
import (
|
||||
"go.opentelemetry.io/api/core"
|
||||
)
|
||||
|
||||
const defaultSamplingProbability = 1e-4
|
||||
|
||||
// Sampler decides whether a trace should be sampled and exported.
|
||||
type Sampler func(SamplingParameters) SamplingDecision
|
||||
|
||||
// SamplingParameters contains the values passed to a Sampler.
|
||||
type SamplingParameters struct {
|
||||
ParentContext core.SpanContext
|
||||
TraceID core.TraceID
|
||||
SpanID uint64
|
||||
Name string
|
||||
HasRemoteParent bool
|
||||
}
|
||||
|
||||
// SamplingDecision is the value returned by a Sampler.
|
||||
type SamplingDecision struct {
|
||||
Sample bool
|
||||
}
|
||||
|
||||
// ProbabilitySampler returns a Sampler that samples a given fraction of traces.
|
||||
//
|
||||
// It also samples spans whose parents are sampled.
|
||||
func ProbabilitySampler(fraction float64) Sampler {
|
||||
if !(fraction >= 0) {
|
||||
fraction = 0
|
||||
} else if fraction >= 1 {
|
||||
return AlwaysSample()
|
||||
}
|
||||
|
||||
traceIDUpperBound := uint64(fraction * (1 << 63))
|
||||
return Sampler(func(p SamplingParameters) SamplingDecision {
|
||||
if p.ParentContext.IsSampled() {
|
||||
return SamplingDecision{Sample: true}
|
||||
}
|
||||
x := p.TraceID.High >> 1
|
||||
return SamplingDecision{Sample: x < traceIDUpperBound}
|
||||
})
|
||||
}
|
||||
|
||||
// AlwaysSample returns a Sampler that samples every trace.
|
||||
// Be careful about using this sampler in a production application with
|
||||
// significant traffic: a new trace will be started and exported for every
|
||||
// request.
|
||||
func AlwaysSample() Sampler {
|
||||
return func(p SamplingParameters) SamplingDecision {
|
||||
return SamplingDecision{Sample: true}
|
||||
}
|
||||
}
|
||||
|
||||
// NeverSample returns a Sampler that samples no traces.
|
||||
func NeverSample() Sampler {
|
||||
return func(p SamplingParameters) SamplingDecision {
|
||||
return SamplingDecision{Sample: false}
|
||||
}
|
||||
}
|
288
sdk/trace/span.go
Normal file
288
sdk/trace/span.go
Normal file
@ -0,0 +1,288 @@
|
||||
// Copyright 2019, OpenTelemetry Authors
|
||||
//
|
||||
// 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 trace
|
||||
|
||||
import (
|
||||
"context"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"go.opentelemetry.io/api/core"
|
||||
apievent "go.opentelemetry.io/api/event"
|
||||
apitag "go.opentelemetry.io/api/tag"
|
||||
apitrace "go.opentelemetry.io/api/trace"
|
||||
"go.opentelemetry.io/sdk/internal"
|
||||
"google.golang.org/grpc/codes"
|
||||
)
|
||||
|
||||
// span implements apitrace.Span interface.
|
||||
type span struct {
|
||||
// data contains information recorded about the span.
|
||||
//
|
||||
// It will be non-nil if we are exporting the span or recording events for it.
|
||||
// Otherwise, data is nil, and the span is simply a carrier for the
|
||||
// SpanContext, so that the trace ID is propagated.
|
||||
data *SpanData
|
||||
mu sync.Mutex // protects the contents of *data (but not the pointer value.)
|
||||
spanContext core.SpanContext
|
||||
|
||||
// lruAttributes are capped at configured limit. When the capacity is reached an oldest entry
|
||||
// is removed to create room for a new entry.
|
||||
lruAttributes *lruMap
|
||||
|
||||
// messageEvents are stored in FIFO queue capped by configured limit.
|
||||
messageEvents *evictedQueue
|
||||
|
||||
// links are stored in FIFO queue capped by configured limit.
|
||||
links *evictedQueue
|
||||
|
||||
// spanStore is the spanStore this span belongs to, if any, otherwise it is nil.
|
||||
//*spanStore
|
||||
endOnce sync.Once
|
||||
|
||||
executionTracerTaskEnd func() // ends the execution tracer span
|
||||
tracer apitrace.Tracer // tracer used to create span.
|
||||
}
|
||||
|
||||
var _ apitrace.Span = &span{}
|
||||
|
||||
func (s *span) SpanContext() core.SpanContext {
|
||||
if s == nil {
|
||||
return core.INVALID_SPAN_CONTEXT
|
||||
}
|
||||
return s.spanContext
|
||||
}
|
||||
|
||||
func (s *span) IsRecordingEvents() bool {
|
||||
if s == nil {
|
||||
return false
|
||||
}
|
||||
return s.data != nil
|
||||
}
|
||||
|
||||
func (s *span) SetStatus(status codes.Code) {
|
||||
if s == nil {
|
||||
return
|
||||
}
|
||||
if !s.IsRecordingEvents() {
|
||||
return
|
||||
}
|
||||
s.mu.Lock()
|
||||
s.data.Status = status
|
||||
s.mu.Unlock()
|
||||
}
|
||||
|
||||
func (s *span) SetAttribute(attribute core.KeyValue) {
|
||||
if !s.IsRecordingEvents() {
|
||||
return
|
||||
}
|
||||
s.copyToCappedAttributes(attribute)
|
||||
}
|
||||
|
||||
func (s *span) SetAttributes(attributes ...core.KeyValue) {
|
||||
if !s.IsRecordingEvents() {
|
||||
return
|
||||
}
|
||||
s.copyToCappedAttributes(attributes...)
|
||||
}
|
||||
|
||||
// ModifyAttribute does nothing.
|
||||
func (s *span) ModifyAttribute(mutator apitag.Mutator) {
|
||||
}
|
||||
|
||||
// ModifyAttributes does nothing.
|
||||
func (s *span) ModifyAttributes(mutators ...apitag.Mutator) {
|
||||
}
|
||||
|
||||
func (s *span) Finish() {
|
||||
if s == nil {
|
||||
return
|
||||
}
|
||||
|
||||
if s.executionTracerTaskEnd != nil {
|
||||
s.executionTracerTaskEnd()
|
||||
}
|
||||
if !s.IsRecordingEvents() {
|
||||
return
|
||||
}
|
||||
s.endOnce.Do(func() {
|
||||
exp, _ := exporters.Load().(exportersMap)
|
||||
mustExport := s.spanContext.IsSampled() && len(exp) > 0
|
||||
//if s.spanStore != nil || mustExport {
|
||||
if mustExport {
|
||||
sd := s.makeSpanData()
|
||||
sd.EndTime = internal.MonotonicEndTime(sd.StartTime)
|
||||
//if s.spanStore != nil {
|
||||
// s.spanStore.finished(s, sd)
|
||||
//}
|
||||
if mustExport {
|
||||
for e := range exp {
|
||||
e.ExportSpan(sd)
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func (s *span) Tracer() apitrace.Tracer {
|
||||
return s.tracer
|
||||
}
|
||||
|
||||
func (s *span) AddEvent(ctx context.Context, event apievent.Event) {
|
||||
if !s.IsRecordingEvents() {
|
||||
return
|
||||
}
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
s.messageEvents.add(event)
|
||||
}
|
||||
|
||||
func (s *span) Event(ctx context.Context, msg string, attrs ...core.KeyValue) {
|
||||
if !s.IsRecordingEvents() {
|
||||
return
|
||||
}
|
||||
now := time.Now()
|
||||
s.mu.Lock()
|
||||
s.messageEvents.add(event{
|
||||
msg: msg,
|
||||
attributes: attrs,
|
||||
time: now,
|
||||
})
|
||||
s.mu.Unlock()
|
||||
}
|
||||
|
||||
// makeSpanData produces a SpanData representing the current state of the span.
|
||||
// It requires that s.data is non-nil.
|
||||
func (s *span) makeSpanData() *SpanData {
|
||||
var sd SpanData
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
sd = *s.data
|
||||
if s.lruAttributes.simpleLruMap.Len() > 0 {
|
||||
sd.Attributes = s.lruAttributesToAttributeMap()
|
||||
sd.DroppedAttributeCount = s.lruAttributes.droppedCount
|
||||
}
|
||||
if len(s.messageEvents.queue) > 0 {
|
||||
sd.MessageEvents = s.interfaceArrayToMessageEventArray()
|
||||
sd.DroppedMessageEventCount = s.messageEvents.droppedCount
|
||||
}
|
||||
return &sd
|
||||
}
|
||||
|
||||
func (s *span) interfaceArrayToMessageEventArray() []event {
|
||||
messageEventArr := make([]event, 0)
|
||||
for _, value := range s.messageEvents.queue {
|
||||
messageEventArr = append(messageEventArr, value.(event))
|
||||
}
|
||||
return messageEventArr
|
||||
}
|
||||
|
||||
func (s *span) lruAttributesToAttributeMap() map[string]interface{} {
|
||||
attributes := make(map[string]interface{})
|
||||
for _, key := range s.lruAttributes.simpleLruMap.Keys() {
|
||||
value, ok := s.lruAttributes.simpleLruMap.Get(key)
|
||||
if ok {
|
||||
key := key.(core.Key)
|
||||
attributes[key.Variable.Name] = value
|
||||
}
|
||||
}
|
||||
return attributes
|
||||
}
|
||||
|
||||
func (s *span) copyToCappedAttributes(attributes ...core.KeyValue) {
|
||||
s.mu.Lock()
|
||||
defer s.mu.Unlock()
|
||||
for _, a := range attributes {
|
||||
s.lruAttributes.add(a.Key, a.Value)
|
||||
}
|
||||
}
|
||||
|
||||
func (s *span) addChild() {
|
||||
if !s.IsRecordingEvents() {
|
||||
return
|
||||
}
|
||||
s.mu.Lock()
|
||||
s.data.ChildSpanCount++
|
||||
s.mu.Unlock()
|
||||
}
|
||||
|
||||
func startSpanInternal(name string, parent core.SpanContext, remoteParent bool, o apitrace.SpanOptions) *span {
|
||||
var noParent bool
|
||||
span := &span{}
|
||||
span.spanContext = parent
|
||||
|
||||
cfg := config.Load().(*Config)
|
||||
|
||||
if parent == core.INVALID_SPAN_CONTEXT {
|
||||
span.spanContext.TraceID = cfg.IDGenerator.NewTraceID()
|
||||
noParent = true
|
||||
}
|
||||
span.spanContext.SpanID = cfg.IDGenerator.NewSpanID()
|
||||
sampler := cfg.DefaultSampler
|
||||
|
||||
// TODO: [rghetia] fix sampler
|
||||
//if !hasParent || remoteParent || o.Sampler != nil {
|
||||
if noParent || remoteParent {
|
||||
// If this span is the child of a local span and no Sampler is set in the
|
||||
// options, keep the parent's TraceOptions.
|
||||
//
|
||||
// Otherwise, consult the Sampler in the options if it is non-nil, otherwise
|
||||
// the default sampler.
|
||||
//if o.Sampler != nil {
|
||||
// sampler = o.Sampler
|
||||
//}
|
||||
sampled := sampler(SamplingParameters{
|
||||
ParentContext: parent,
|
||||
TraceID: span.spanContext.TraceID,
|
||||
SpanID: span.spanContext.SpanID,
|
||||
Name: name,
|
||||
HasRemoteParent: remoteParent}).Sample
|
||||
if sampled {
|
||||
span.spanContext.TraceOptions = core.TraceOptionSampled
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: [rghetia] restore when spanstore is added.
|
||||
// if !internal.LocalSpanStoreEnabled && !span.spanContext.IsSampled() && !o.RecordEvent {
|
||||
if !span.spanContext.IsSampled() && !o.RecordEvent {
|
||||
return span
|
||||
}
|
||||
|
||||
span.data = &SpanData{
|
||||
SpanContext: span.spanContext,
|
||||
StartTime: time.Now(),
|
||||
// TODO;[rghetia] : fix spanKind
|
||||
//SpanKind: o.SpanKind,
|
||||
Name: name,
|
||||
HasRemoteParent: remoteParent,
|
||||
}
|
||||
span.lruAttributes = newLruMap(cfg.MaxAttributesPerSpan)
|
||||
span.messageEvents = newEvictedQueue(cfg.MaxEventsPerSpan)
|
||||
span.links = newEvictedQueue(cfg.MaxLinksPerSpan)
|
||||
|
||||
if !noParent {
|
||||
span.data.ParentSpanID = parent.SpanID
|
||||
}
|
||||
// TODO: [rghetia] restore when spanstore is added.
|
||||
//if internal.LocalSpanStoreEnabled {
|
||||
// ss := spanStoreForNameCreateIfNew(name)
|
||||
// if ss != nil {
|
||||
// span.spanStore = ss
|
||||
// ss.add(span)
|
||||
// }
|
||||
//}
|
||||
|
||||
return span
|
||||
}
|
75
sdk/trace/trace.go
Normal file
75
sdk/trace/trace.go
Normal file
@ -0,0 +1,75 @@
|
||||
// Copyright 2019, OpenTelemetry Authors
|
||||
//
|
||||
// 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 trace
|
||||
|
||||
import (
|
||||
"context"
|
||||
crand "crypto/rand"
|
||||
"encoding/binary"
|
||||
"math/rand"
|
||||
"sync"
|
||||
"sync/atomic"
|
||||
|
||||
apitrace "go.opentelemetry.io/api/trace"
|
||||
)
|
||||
|
||||
var config atomic.Value // access atomically
|
||||
|
||||
func init() {
|
||||
gen := &defaultIDGenerator{}
|
||||
// initialize traceID and spanID generators.
|
||||
var rngSeed int64
|
||||
for _, p := range []interface{}{
|
||||
&rngSeed, &gen.traceIDAdd, &gen.nextSpanID, &gen.spanIDInc,
|
||||
} {
|
||||
_ = binary.Read(crand.Reader, binary.LittleEndian, p)
|
||||
}
|
||||
gen.traceIDRand = rand.New(rand.NewSource(rngSeed))
|
||||
gen.spanIDInc |= 1
|
||||
|
||||
config.Store(&Config{
|
||||
DefaultSampler: ProbabilitySampler(defaultSamplingProbability),
|
||||
IDGenerator: gen,
|
||||
MaxAttributesPerSpan: DefaultMaxAttributesPerSpan,
|
||||
MaxEventsPerSpan: DefaultMaxEventsPerSpan,
|
||||
MaxLinksPerSpan: DefaultMaxLinksPerSpan,
|
||||
})
|
||||
}
|
||||
|
||||
var tr *tracer
|
||||
var registerOnce sync.Once
|
||||
|
||||
// Register registers tracer implementation as default Tracer.
|
||||
// It creates single instance of tracer and registers it once.
|
||||
// Recommended use is to call Register in main() of an
|
||||
// application before calling any tracing api.
|
||||
func Register() apitrace.Tracer {
|
||||
registerOnce.Do(func() {
|
||||
tr = &tracer{}
|
||||
apitrace.SetGlobalTracer(tr)
|
||||
})
|
||||
return tr
|
||||
}
|
||||
|
||||
type contextKey struct{}
|
||||
|
||||
func fromContext(ctx context.Context) *span {
|
||||
s, _ := ctx.Value(contextKey{}).(*span)
|
||||
return s
|
||||
}
|
||||
|
||||
func newContext(parent context.Context, s *span) context.Context {
|
||||
return context.WithValue(parent, contextKey{}, s)
|
||||
}
|
32
sdk/trace/trace_go11.go
Normal file
32
sdk/trace/trace_go11.go
Normal file
@ -0,0 +1,32 @@
|
||||
// Copyright 2019, OpenTelemetry Authors
|
||||
//
|
||||
// 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.
|
||||
|
||||
// +build go1.11
|
||||
|
||||
package trace
|
||||
|
||||
import (
|
||||
"context"
|
||||
rt "runtime/trace"
|
||||
)
|
||||
|
||||
func startExecutionTracerTask(ctx context.Context, name string) (context.Context, func()) {
|
||||
if !rt.IsEnabled() {
|
||||
// Avoid additional overhead if
|
||||
// runtime/trace is not enabled.
|
||||
return ctx, func() {}
|
||||
}
|
||||
nctx, task := rt.NewTask(ctx, name)
|
||||
return nctx, task.End
|
||||
}
|
25
sdk/trace/trace_nongo11.go
Normal file
25
sdk/trace/trace_nongo11.go
Normal file
@ -0,0 +1,25 @@
|
||||
// Copyright 2019, OpenTelemetry Authors
|
||||
//
|
||||
// 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.
|
||||
|
||||
// +build !go1.11
|
||||
|
||||
package trace
|
||||
|
||||
import (
|
||||
"context"
|
||||
)
|
||||
|
||||
func startExecutionTracerTask(ctx context.Context, name string) (context.Context, func()) {
|
||||
return ctx, func() {}
|
||||
}
|
524
sdk/trace/trace_test.go
Normal file
524
sdk/trace/trace_test.go
Normal file
@ -0,0 +1,524 @@
|
||||
// Copyright 2019, OpenTelemetry Authors
|
||||
//
|
||||
// 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 trace
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"sync/atomic"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/google/go-cmp/cmp"
|
||||
"go.opentelemetry.io/api/core"
|
||||
"go.opentelemetry.io/api/key"
|
||||
apitrace "go.opentelemetry.io/api/trace"
|
||||
"google.golang.org/grpc/codes"
|
||||
)
|
||||
|
||||
var (
|
||||
tid = core.TraceID{High: 0x0102030405060708, Low: 0x0102040810203040}
|
||||
sid = uint64(0x0102040810203040)
|
||||
)
|
||||
|
||||
func init() {
|
||||
Register()
|
||||
// no random sampling, but sample children of sampled spans.
|
||||
ApplyConfig(Config{DefaultSampler: ProbabilitySampler(0)})
|
||||
}
|
||||
|
||||
type testExporter struct {
|
||||
spans []*SpanData
|
||||
}
|
||||
|
||||
func (t *testExporter) ExportSpan(s *SpanData) {
|
||||
t.spans = append(t.spans, s)
|
||||
}
|
||||
|
||||
func TestStartSpan(t *testing.T) {
|
||||
_, span := apitrace.GlobalTracer().Start(context.Background(), "StartSpan")
|
||||
defer span.Finish()
|
||||
if span == nil {
|
||||
t.Errorf("span not started")
|
||||
}
|
||||
}
|
||||
|
||||
func TestRecordingIsOff(t *testing.T) {
|
||||
_, span := apitrace.GlobalTracer().Start(context.Background(), "StartSpan")
|
||||
defer span.Finish()
|
||||
if span.IsRecordingEvents() == true {
|
||||
t.Error("new span is recording events")
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: [rghetia] enable sampling test when Sampling is working.
|
||||
|
||||
func TestStartSpanWithChildOf(t *testing.T) {
|
||||
sc1 := core.SpanContext{
|
||||
TraceID: tid,
|
||||
SpanID: sid,
|
||||
TraceOptions: 0x0,
|
||||
}
|
||||
_, s1 := apitrace.GlobalTracer().Start(context.Background(), "span1-unsampled-parent1", apitrace.ChildOf(sc1))
|
||||
if err := checkChild(sc1, s1); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
_, s2 := apitrace.GlobalTracer().Start(context.Background(), "span2-unsampled-parent1", apitrace.ChildOf(sc1))
|
||||
if err := checkChild(sc1, s2); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
sc2 := core.SpanContext{
|
||||
TraceID: tid,
|
||||
SpanID: sid,
|
||||
TraceOptions: 0x1,
|
||||
//Tracestate: testTracestate,
|
||||
}
|
||||
_, s3 := apitrace.GlobalTracer().Start(context.Background(), "span3-sampled-parent2", apitrace.ChildOf(sc2))
|
||||
if err := checkChild(sc2, s3); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
ctx, s4 := apitrace.GlobalTracer().Start(context.Background(), "span4-sampled-parent2", apitrace.ChildOf(sc2))
|
||||
if err := checkChild(sc2, s4); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
|
||||
s4Sc := s4.SpanContext()
|
||||
_, s5 := apitrace.GlobalTracer().Start(ctx, "span5-implicit-childof-span4")
|
||||
if err := checkChild(s4Sc, s5); err != nil {
|
||||
t.Error(err)
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: [rghetia] Equivalent of SpanKind Test.
|
||||
|
||||
func TestSetSpanAttributes(t *testing.T) {
|
||||
span := startSpan()
|
||||
span.SetAttribute(key.New("key1").String("value1"))
|
||||
got, err := endSpan(span)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
want := &SpanData{
|
||||
SpanContext: core.SpanContext{
|
||||
TraceID: tid,
|
||||
TraceOptions: 0x1,
|
||||
},
|
||||
ParentSpanID: sid,
|
||||
Name: "span0",
|
||||
Attributes: map[string]interface{}{"key1": core.Value{Type: core.STRING, String: "value1"}},
|
||||
HasRemoteParent: true,
|
||||
}
|
||||
if diff := cmp.Diff(got, want); diff != "" {
|
||||
t.Errorf("SetSpanAttributes: -got +want %s", diff)
|
||||
}
|
||||
}
|
||||
|
||||
func TestSetSpanAttributesOverLimit(t *testing.T) {
|
||||
cfg := Config{MaxAttributesPerSpan: 2}
|
||||
ApplyConfig(cfg)
|
||||
|
||||
span := startSpan()
|
||||
span.SetAttribute(key.New("key1").String("value1"))
|
||||
span.SetAttribute(key.New("key2").String("value2"))
|
||||
span.SetAttribute(key.New("key1").String("value3")) // Replace key1.
|
||||
span.SetAttribute(key.New("key4").String("value4")) // Remove key2 and add key4
|
||||
got, err := endSpan(span)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
want := &SpanData{
|
||||
SpanContext: core.SpanContext{
|
||||
TraceID: tid,
|
||||
TraceOptions: 0x1,
|
||||
},
|
||||
ParentSpanID: sid,
|
||||
Name: "span0",
|
||||
Attributes: map[string]interface{}{
|
||||
"key1": core.Value{Type: core.STRING, String: "value3"},
|
||||
"key4": core.Value{Type: core.STRING, String: "value4"}},
|
||||
HasRemoteParent: true,
|
||||
DroppedAttributeCount: 1,
|
||||
}
|
||||
if diff := cmp.Diff(got, want); diff != "" {
|
||||
t.Errorf("SetSpanAttributesOverLimit: -got +want %s", diff)
|
||||
}
|
||||
}
|
||||
|
||||
func TestEvents(t *testing.T) {
|
||||
span := startSpan()
|
||||
k1v1 := key.New("key1").String("value1")
|
||||
k2v2 := key.New("key2").String("value2")
|
||||
k3v3 := key.New("key3").String("value3")
|
||||
|
||||
span.Event(context.Background(), "foo", key.New("key1").String("value1"))
|
||||
span.Event(context.Background(), "bar",
|
||||
key.New("key2").String("value2"),
|
||||
key.New("key3").String("value3"),
|
||||
)
|
||||
got, err := endSpan(span)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
for i := range got.MessageEvents {
|
||||
if !checkTime(&got.MessageEvents[i].time) {
|
||||
t.Error("exporting span: expected nonzero event Time")
|
||||
}
|
||||
}
|
||||
|
||||
want := &SpanData{
|
||||
SpanContext: core.SpanContext{
|
||||
TraceID: tid,
|
||||
TraceOptions: 0x1,
|
||||
},
|
||||
ParentSpanID: sid,
|
||||
Name: "span0",
|
||||
HasRemoteParent: true,
|
||||
MessageEvents: []event{
|
||||
{msg: "foo", attributes: []core.KeyValue{k1v1}},
|
||||
{msg: "bar", attributes: []core.KeyValue{k2v2, k3v3}},
|
||||
},
|
||||
}
|
||||
if diff := cmp.Diff(got, want, cmp.AllowUnexported(event{})); diff != "" {
|
||||
t.Errorf("Message Events: -got +want %s", diff)
|
||||
}
|
||||
}
|
||||
|
||||
func TestEventsOverLimit(t *testing.T) {
|
||||
cfg := Config{MaxEventsPerSpan: 2}
|
||||
ApplyConfig(cfg)
|
||||
span := startSpan()
|
||||
k1v1 := key.New("key1").String("value1")
|
||||
k2v2 := key.New("key2").String("value2")
|
||||
k3v3 := key.New("key3").String("value3")
|
||||
|
||||
span.Event(context.Background(), "fooDrop", key.New("key1").String("value1"))
|
||||
span.Event(context.Background(), "barDrop",
|
||||
key.New("key2").String("value2"),
|
||||
key.New("key3").String("value3"),
|
||||
)
|
||||
span.Event(context.Background(), "foo", key.New("key1").String("value1"))
|
||||
span.Event(context.Background(), "bar",
|
||||
key.New("key2").String("value2"),
|
||||
key.New("key3").String("value3"),
|
||||
)
|
||||
got, err := endSpan(span)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
for i := range got.MessageEvents {
|
||||
if !checkTime(&got.MessageEvents[i].time) {
|
||||
t.Error("exporting span: expected nonzero event Time")
|
||||
}
|
||||
}
|
||||
|
||||
want := &SpanData{
|
||||
SpanContext: core.SpanContext{
|
||||
TraceID: tid,
|
||||
TraceOptions: 0x1,
|
||||
},
|
||||
ParentSpanID: sid,
|
||||
Name: "span0",
|
||||
MessageEvents: []event{
|
||||
{msg: "foo", attributes: []core.KeyValue{k1v1}},
|
||||
{msg: "bar", attributes: []core.KeyValue{k2v2, k3v3}},
|
||||
},
|
||||
DroppedMessageEventCount: 2,
|
||||
HasRemoteParent: true,
|
||||
}
|
||||
if diff := cmp.Diff(got, want, cmp.AllowUnexported(event{})); diff != "" {
|
||||
t.Errorf("Message Event over limit: -got +want %s", diff)
|
||||
}
|
||||
}
|
||||
|
||||
func TestSetSpanName(t *testing.T) {
|
||||
want := "SpanName-1"
|
||||
_, span := apitrace.GlobalTracer().Start(context.Background(), want,
|
||||
apitrace.ChildOf(core.SpanContext{
|
||||
TraceID: tid,
|
||||
SpanID: sid,
|
||||
TraceOptions: 1,
|
||||
}),
|
||||
)
|
||||
got, err := endSpan(span)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if got.Name != want {
|
||||
t.Errorf("span.Name: got %q; want %q", got.Name, want)
|
||||
}
|
||||
}
|
||||
|
||||
func TestSetSpanStatus(t *testing.T) {
|
||||
span := startSpan()
|
||||
span.SetStatus(codes.Canceled)
|
||||
got, err := endSpan(span)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
want := &SpanData{
|
||||
SpanContext: core.SpanContext{
|
||||
TraceID: tid,
|
||||
TraceOptions: 0x1,
|
||||
},
|
||||
ParentSpanID: sid,
|
||||
Name: "span0",
|
||||
Status: codes.Canceled,
|
||||
HasRemoteParent: true,
|
||||
}
|
||||
if diff := cmp.Diff(got, want); diff != "" {
|
||||
t.Errorf("SetSpanStatus: -got +want %s", diff)
|
||||
}
|
||||
}
|
||||
|
||||
func TestUnregisterExporter(t *testing.T) {
|
||||
var te testExporter
|
||||
RegisterExporter(&te)
|
||||
UnregisterExporter(&te)
|
||||
|
||||
ctx := startSpan()
|
||||
_, _ = endSpan(ctx)
|
||||
if len(te.spans) != 0 {
|
||||
t.Error("unregistered Exporter was called")
|
||||
}
|
||||
}
|
||||
|
||||
func remoteSpanContext() core.SpanContext {
|
||||
return core.SpanContext{
|
||||
TraceID: tid,
|
||||
SpanID: sid,
|
||||
TraceOptions: 1,
|
||||
}
|
||||
}
|
||||
|
||||
// checkChild is test utility function that tests that c has fields set appropriately,
|
||||
// given that it is a child span of p.
|
||||
func checkChild(p core.SpanContext, apiSpan apitrace.Span) error {
|
||||
s := apiSpan.(*span)
|
||||
if s == nil {
|
||||
return fmt.Errorf("got nil child span, want non-nil")
|
||||
}
|
||||
if got, want := s.spanContext.TraceIDString(), p.TraceIDString(); got != want {
|
||||
return fmt.Errorf("got child trace ID %s, want %s", got, want)
|
||||
}
|
||||
if childID, parentID := s.spanContext.SpanIDString(), p.SpanIDString(); childID == parentID {
|
||||
return fmt.Errorf("got child span ID %s, parent span ID %s; want unequal IDs", childID, parentID)
|
||||
}
|
||||
if got, want := s.spanContext.TraceOptions, p.TraceOptions; got != want {
|
||||
return fmt.Errorf("got child trace options %d, want %d", got, want)
|
||||
}
|
||||
// TODO [rgheita] : Fix tracestate test
|
||||
//if got, want := c.spanContext.Tracestate, p.Tracestate; got != want {
|
||||
// return fmt.Errorf("got child tracestate %v, want %v", got, want)
|
||||
//}
|
||||
return nil
|
||||
}
|
||||
|
||||
// startSpan is a test utility func that starts a span with ChildOf option.
|
||||
// remote span context contains traceoption with sampled bit set. This allows
|
||||
// the span to be automatically sampled.
|
||||
func startSpan() apitrace.Span {
|
||||
_, span := apitrace.GlobalTracer().Start(
|
||||
context.Background(),
|
||||
"span0",
|
||||
apitrace.ChildOf(remoteSpanContext()),
|
||||
apitrace.WithRecordEvents(),
|
||||
)
|
||||
return span
|
||||
}
|
||||
|
||||
// endSpan is a test utility function that ends the span in the context and
|
||||
// returns the exported SpanData.
|
||||
// It requires that span be sampled using one of these methods
|
||||
// 1. Passing parent span context using ChildOf option
|
||||
// 2. Use WithSampler(AlwaysSample())
|
||||
// 3. Configuring AlwaysSample() as default sampler
|
||||
//
|
||||
// It also does some basic tests on the span.
|
||||
// It also clears spanID in the SpanData to make the comparison easier.
|
||||
func endSpan(span apitrace.Span) (*SpanData, error) {
|
||||
|
||||
if !span.IsRecordingEvents() {
|
||||
return nil, fmt.Errorf("IsRecordingEvents: got false, want true")
|
||||
}
|
||||
if !span.SpanContext().IsSampled() {
|
||||
return nil, fmt.Errorf("IsSampled: got false, want true")
|
||||
}
|
||||
var te testExporter
|
||||
RegisterExporter(&te)
|
||||
span.Finish()
|
||||
UnregisterExporter(&te)
|
||||
if len(te.spans) != 1 {
|
||||
return nil, fmt.Errorf("got exported spans %#v, want one span", te.spans)
|
||||
}
|
||||
got := te.spans[0]
|
||||
if got.SpanContext.SpanID == 0 {
|
||||
return nil, fmt.Errorf("exporting span: expected nonzero SpanID")
|
||||
}
|
||||
got.SpanContext.SpanID = 0
|
||||
if !checkTime(&got.StartTime) {
|
||||
return nil, fmt.Errorf("exporting span: expected nonzero StartTime")
|
||||
}
|
||||
if !checkTime(&got.EndTime) {
|
||||
return nil, fmt.Errorf("exporting span: expected nonzero EndTime")
|
||||
}
|
||||
return got, nil
|
||||
}
|
||||
|
||||
// checkTime checks that a nonzero time was set in x, then clears it.
|
||||
func checkTime(x *time.Time) bool {
|
||||
if x.IsZero() {
|
||||
return false
|
||||
}
|
||||
*x = time.Time{}
|
||||
return true
|
||||
}
|
||||
|
||||
type exporter map[string]*SpanData
|
||||
|
||||
func (e exporter) ExportSpan(s *SpanData) {
|
||||
e[s.Name] = s
|
||||
}
|
||||
|
||||
func TestEndSpanTwice(t *testing.T) {
|
||||
spans := make(exporter)
|
||||
RegisterExporter(&spans)
|
||||
defer UnregisterExporter(&spans)
|
||||
span := startSpan()
|
||||
span.Finish()
|
||||
span.Finish()
|
||||
UnregisterExporter(&spans)
|
||||
if len(spans) != 1 {
|
||||
t.Fatalf("expected only a single span, got %#v", spans)
|
||||
}
|
||||
}
|
||||
|
||||
func TestStartSpanAfterEnd(t *testing.T) {
|
||||
spans := make(exporter)
|
||||
RegisterExporter(&spans)
|
||||
defer UnregisterExporter(&spans)
|
||||
ctx, span0 := apitrace.GlobalTracer().Start(context.Background(), "parent", apitrace.ChildOf(remoteSpanContext()))
|
||||
ctx1, span1 := apitrace.GlobalTracer().Start(ctx, "span-1")
|
||||
span1.Finish()
|
||||
// Start a new span with the context containing span-1
|
||||
// even though span-1 is ended, we still add this as a new child of span-1
|
||||
_, span2 := apitrace.GlobalTracer().Start(ctx1, "span-2")
|
||||
span2.Finish()
|
||||
span0.Finish()
|
||||
UnregisterExporter(&spans)
|
||||
if got, want := len(spans), 3; got != want {
|
||||
t.Fatalf("len(%#v) = %d; want %d", spans, got, want)
|
||||
}
|
||||
if got, want := spans["span-1"].SpanContext.TraceID, spans["parent"].SpanContext.TraceID; got != want {
|
||||
t.Errorf("span-1.TraceID=%q; want %q", got, want)
|
||||
}
|
||||
if got, want := spans["span-2"].SpanContext.TraceID, spans["parent"].SpanContext.TraceID; got != want {
|
||||
t.Errorf("span-2.TraceID=%q; want %q", got, want)
|
||||
}
|
||||
if got, want := spans["span-1"].ParentSpanID, spans["parent"].SpanContext.SpanID; got != want {
|
||||
t.Errorf("span-1.ParentSpanID=%q; want %q (parent.SpanID)", got, want)
|
||||
}
|
||||
if got, want := spans["span-2"].ParentSpanID, spans["span-1"].SpanContext.SpanID; got != want {
|
||||
t.Errorf("span-2.ParentSpanID=%q; want %q (span1.SpanID)", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
func TestChildSpanCount(t *testing.T) {
|
||||
ApplyConfig(Config{DefaultSampler: AlwaysSample()})
|
||||
spans := make(exporter)
|
||||
RegisterExporter(&spans)
|
||||
defer UnregisterExporter(&spans)
|
||||
ctx, span0 := apitrace.GlobalTracer().Start(context.Background(), "parent")
|
||||
ctx1, span1 := apitrace.GlobalTracer().Start(ctx, "span-1")
|
||||
_, span2 := apitrace.GlobalTracer().Start(ctx1, "span-2")
|
||||
span2.Finish()
|
||||
span1.Finish()
|
||||
|
||||
_, span3 := apitrace.GlobalTracer().Start(ctx, "span-3")
|
||||
span3.Finish()
|
||||
span0.Finish()
|
||||
UnregisterExporter(&spans)
|
||||
if got, want := len(spans), 4; got != want {
|
||||
t.Fatalf("len(%#v) = %d; want %d", spans, got, want)
|
||||
}
|
||||
if got, want := spans["span-3"].ChildSpanCount, 0; got != want {
|
||||
t.Errorf("span-3.ChildSpanCount=%q; want %q", got, want)
|
||||
}
|
||||
if got, want := spans["span-2"].ChildSpanCount, 0; got != want {
|
||||
t.Errorf("span-2.ChildSpanCount=%q; want %q", got, want)
|
||||
}
|
||||
if got, want := spans["span-1"].ChildSpanCount, 1; got != want {
|
||||
t.Errorf("span-1.ChildSpanCount=%q; want %q", got, want)
|
||||
}
|
||||
if got, want := spans["parent"].ChildSpanCount, 2; got != want {
|
||||
t.Errorf("parent.ChildSpanCount=%q; want %q", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
func TestNilSpanFinish(t *testing.T) {
|
||||
var span *span
|
||||
span.Finish()
|
||||
}
|
||||
|
||||
func TestExecutionTracerTaskEnd(t *testing.T) {
|
||||
var n uint64
|
||||
ApplyConfig(Config{DefaultSampler: NeverSample()})
|
||||
executionTracerTaskEnd := func() {
|
||||
atomic.AddUint64(&n, 1)
|
||||
}
|
||||
|
||||
var spans []*span
|
||||
_, apiSpan := apitrace.GlobalTracer().Start(context.Background(), "foo")
|
||||
s := apiSpan.(*span)
|
||||
|
||||
s.executionTracerTaskEnd = executionTracerTaskEnd
|
||||
spans = append(spans, s) // never sample
|
||||
|
||||
_, apiSpan = apitrace.GlobalTracer().Start(
|
||||
context.Background(),
|
||||
"foo",
|
||||
apitrace.ChildOf(
|
||||
core.SpanContext{
|
||||
TraceID: core.TraceID{High: 0x0102030405060708, Low: 0x090a0b0c0d0e0f},
|
||||
SpanID: uint64(0x0001020304050607),
|
||||
TraceOptions: 0,
|
||||
},
|
||||
),
|
||||
)
|
||||
s = apiSpan.(*span)
|
||||
s.executionTracerTaskEnd = executionTracerTaskEnd
|
||||
spans = append(spans, s) // parent not sampled
|
||||
|
||||
ApplyConfig(Config{DefaultSampler: AlwaysSample()})
|
||||
_, apiSpan = apitrace.GlobalTracer().Start(context.Background(), "foo")
|
||||
s = apiSpan.(*span)
|
||||
s.executionTracerTaskEnd = executionTracerTaskEnd
|
||||
spans = append(spans, s) // always sample
|
||||
|
||||
for _, span := range spans {
|
||||
span.Finish()
|
||||
}
|
||||
if got, want := n, uint64(len(spans)); got != want {
|
||||
t.Fatalf("Execution tracer task ended for %v spans; want %v", got, want)
|
||||
}
|
||||
}
|
98
sdk/trace/tracer.go
Normal file
98
sdk/trace/tracer.go
Normal file
@ -0,0 +1,98 @@
|
||||
// Copyright 2019, OpenTelemetry Authors
|
||||
//
|
||||
// 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 trace
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"go.opentelemetry.io/api/core"
|
||||
apitrace "go.opentelemetry.io/api/trace"
|
||||
)
|
||||
|
||||
type tracer struct {
|
||||
name string
|
||||
component string
|
||||
resources []core.KeyValue
|
||||
}
|
||||
|
||||
var _ apitrace.Tracer = &tracer{}
|
||||
|
||||
func (tr *tracer) Start(ctx context.Context, name string, o ...apitrace.SpanOption) (context.Context, apitrace.Span) {
|
||||
var opts apitrace.SpanOptions
|
||||
var parent core.SpanContext
|
||||
var remoteParent bool
|
||||
|
||||
//TODO [rghetia] : Add new option for parent. If parent is configured then use that parent.
|
||||
for _, op := range o {
|
||||
op(&opts)
|
||||
}
|
||||
|
||||
// TODO: [rghetia] ChildOfRelationship is used to indicate that the parent is remote
|
||||
// and its context is received as part of a request. There are two possibilities
|
||||
// 1. Remote is trusted. So continue using same trace.
|
||||
// tracer.Start(ctx, "some name", ChildOf(remote_span_context))
|
||||
// 2. Remote is not trusted. In this case create a root span and then add the remote as link
|
||||
// span := tracer.Start(ctx, "some name")
|
||||
// span.Link(remote_span_context, ChildOfRelationship)
|
||||
if opts.Reference.SpanContext != core.INVALID_SPAN_CONTEXT &&
|
||||
opts.Reference.RelationshipType == apitrace.ChildOfRelationship {
|
||||
parent = opts.Reference.SpanContext
|
||||
remoteParent = true
|
||||
} else {
|
||||
if p := fromContext(ctx); p != nil {
|
||||
p.addChild()
|
||||
parent = p.spanContext
|
||||
}
|
||||
}
|
||||
|
||||
span := startSpanInternal(name, parent, remoteParent, opts)
|
||||
span.tracer = tr
|
||||
|
||||
ctx, end := startExecutionTracerTask(ctx, name)
|
||||
span.executionTracerTaskEnd = end
|
||||
return newContext(ctx, span), span
|
||||
}
|
||||
|
||||
func (tr *tracer) WithSpan(ctx context.Context, name string, body func(ctx context.Context) error) error {
|
||||
ctx, span := tr.Start(ctx, name)
|
||||
defer span.Finish()
|
||||
|
||||
if err := body(ctx); err != nil {
|
||||
// TODO: set event with boolean attribute for error.
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (tr *tracer) WithService(name string) apitrace.Tracer {
|
||||
tr.name = name
|
||||
return tr
|
||||
}
|
||||
|
||||
// WithResources does nothing and returns noop implementation of apitrace.Tracer.
|
||||
func (tr *tracer) WithResources(res ...core.KeyValue) apitrace.Tracer {
|
||||
tr.resources = res
|
||||
return tr
|
||||
}
|
||||
|
||||
// WithComponent does nothing and returns noop implementation of apitrace.Tracer.
|
||||
func (tr *tracer) WithComponent(component string) apitrace.Tracer {
|
||||
tr.component = component
|
||||
return tr
|
||||
}
|
||||
|
||||
func (tr *tracer) Inject(ctx context.Context, span apitrace.Span, injector apitrace.Injector) {
|
||||
injector.Inject(span.SpanContext(), nil)
|
||||
}
|
Loading…
Reference in New Issue
Block a user