You've already forked opentelemetry-go
							
							
				mirror of
				https://github.com/open-telemetry/opentelemetry-go.git
				synced 2025-10-31 00:07:40 +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:
		| @@ -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) | ||||
| } | ||||
		Reference in New Issue
	
	Block a user