Repository: open-telemetry/opentelemetry-go
Pull request #1889 (2021-05-07 15:43:14)

Add schema URL support to Tracer
This adds support for schema URL to the Tracer according to the specification:
https://github.com/open-telemetry/opentelemetry-specification/pull/1666
(Link to replaced by the link to the spec after that PR is merged)

For the future: once the proto is updated we will need to populate the
schema_url field in the messages.

tigrannajaryan: @Aneurysm9 @MrAlias I am looking for your feedback to understand if this is the right direction.
codecov[bot]: # [Codecov](https://codecov.io/gh/open-telemetry/opentelemetry-go/pull/1889?src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=open-telemetry) Report
> Merging [#1889](https://codecov.io/gh/open-telemetry/opentelemetry-go/pull/1889?src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=open-telemetry) (25bd614) into [main](https://codecov.io/gh/open-telemetry/opentelemetry-go/commit/cbcd4b1a3dc6ff839e06749c83294260aa671e6f?el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=open-telemetry) (cbcd4b1) will **increase** coverage by `0.2%`.
> The diff coverage is `85.7%`.

[![Impacted file tree graph](https://codecov.io/gh/open-telemetry/opentelemetry-go/pull/1889/graphs/tree.svg?width=650&height=150&src=pr&token=8efTmh4kvf&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=open-telemetry)](https://codecov.io/gh/open-telemetry/opentelemetry-go/pull/1889?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=open-telemetry)

```diff
@@           Coverage Diff           @@
##            main   #1889     +/-   ##
=======================================
+ Coverage   79.0%   79.2%   +0.2%     
=======================================
  Files        139     139             
  Lines       7477    7464     -13     
=======================================
+ Hits        5909    5914      +5     
+ Misses      1322    1304     -18     
  Partials     246     246             
```


| [Impacted Files](https://codecov.io/gh/open-telemetry/opentelemetry-go/pull/1889?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=open-telemetry) | Coverage Δ | |
|---|---|---|
| [trace/config.go](https://codecov.io/gh/open-telemetry/opentelemetry-go/pull/1889/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=open-telemetry#diff-dHJhY2UvY29uZmlnLmdv) | `61.1% <66.6%> (+0.5%)` | :arrow_up: |
| [exporters/otlp/internal/transform/span.go](https://codecov.io/gh/open-telemetry/opentelemetry-go/pull/1889/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=open-telemetry#diff-ZXhwb3J0ZXJzL290bHAvaW50ZXJuYWwvdHJhbnNmb3JtL3NwYW4uZ28=) | `98.1% <100.0%> (+<0.1%)` | :arrow_up: |
| [sdk/trace/provider.go](https://codecov.io/gh/open-telemetry/opentelemetry-go/pull/1889/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=open-telemetry#diff-c2RrL3RyYWNlL3Byb3ZpZGVyLmdv) | `83.9% <100.0%> (+0.1%)` | :arrow_up: |
| [exporters/trace/jaeger/uploader.go](https://codecov.io/gh/open-telemetry/opentelemetry-go/pull/1889/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=open-telemetry#diff-ZXhwb3J0ZXJzL3RyYWNlL2phZWdlci91cGxvYWRlci5nbw==) | `50.6% <0.0%> (-0.1%)` | :arrow_down: |
| [sdk/export/metric/exportkind\_string.go](https://codecov.io/gh/open-telemetry/opentelemetry-go/pull/1889/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=open-telemetry#diff-c2RrL2V4cG9ydC9tZXRyaWMvZXhwb3J0a2luZF9zdHJpbmcuZ28=) | `0.0% <0.0%> (ø)` | |
| [metric/number/kind\_string.go](https://codecov.io/gh/open-telemetry/opentelemetry-go/pull/1889/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=open-telemetry#diff-bWV0cmljL251bWJlci9raW5kX3N0cmluZy5nbw==) | `33.3% <0.0%> (+16.6%)` | :arrow_up: |
| [attribute/type\_string.go](https://codecov.io/gh/open-telemetry/opentelemetry-go/pull/1889/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=open-telemetry#diff-YXR0cmlidXRlL3R5cGVfc3RyaW5nLmdv) | `33.3% <0.0%> (+23.3%)` | :arrow_up: |
| [metric/instrumentkind\_string.go](https://codecov.io/gh/open-telemetry/opentelemetry-go/pull/1889/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=open-telemetry#diff-bWV0cmljL2luc3RydW1lbnRraW5kX3N0cmluZy5nbw==) | `33.3% <0.0%> (+23.3%)` | :arrow_up: |

tigrannajaryan: @MrAlias @Aneurysm9 this is ready for review.

Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 77cf87b1..0078775b 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -25,6 +25,7 @@ This project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.htm
 - The `SpanStub` type and its associated functions were added to the `go.opentelemetry.io/otel/sdk/trace/tracetest` package.
   This type can be used as a testing replacement for the `SpanSnapshot` that was removed from the `go.opentelemetry.io/otel/sdk/trace` package. (#1873)
 - Adds support for scheme in `OTEL_EXPORTER_OTLP_ENDPOINT` according to the spec. (#1886)
+- Adds `trace.WithSchemaURL` option for configuring the tracer with a Schema URL. (#1889)
 - An example of using OpenTelemetry Go as a trace context forwarder. (#1912)
 - `ParseTraceState` is added to the `go.opentelemetry.io/otel/trace` package.
   It can be used to decode a `TraceState` from a `tracestate` header string value. (#1937)
diff --git a/exporters/otlp/internal/transform/span.go b/exporters/otlp/internal/transform/span.go
index bcf540e4..7bcdeeed 100644
--- a/exporters/otlp/internal/transform/span.go
+++ b/exporters/otlp/internal/transform/span.go
@@ -61,6 +61,8 @@ func Spans(sdl []tracesdk.ReadOnlySpan) []*tracepb.ResourceSpans {
 				InstrumentationLibrary: instrumentationLibrary(sd.InstrumentationLibrary()),
 				Spans:                  []*tracepb.Span{},
 			}
+			// TODO: set schema_url field of ils when it is available in the proto.
+			_ = sd.InstrumentationLibrary().SchemaURL
 		}
 		ils.Spans = append(ils.Spans, span(sd))
 		ilsm[iKey] = ils
diff --git a/sdk/instrumentation/library.go b/sdk/instrumentation/library.go
index c897c04d..5e54ee48 100644
--- a/sdk/instrumentation/library.go
+++ b/sdk/instrumentation/library.go
@@ -32,4 +32,6 @@ type Library struct {
 	Name string
 	// Version is the version of the instrumentation library.
 	Version string
+	// SchemaURL of the telemetry emitted by the library.
+	SchemaURL string
 }
diff --git a/sdk/trace/provider.go b/sdk/trace/provider.go
index bc4ba5db..57edf789 100644
--- a/sdk/trace/provider.go
+++ b/sdk/trace/provider.go
@@ -115,8 +115,9 @@ func (p *TracerProvider) Tracer(name string, opts ...trace.TracerOption) trace.T
 		name = defaultTracerName
 	}
 	il := instrumentation.Library{
-		Name:    name,
-		Version: c.InstrumentationVersion(),
+		Name:      name,
+		Version:   c.InstrumentationVersion(),
+		SchemaURL: c.SchemaURL(),
 	}
 	t, ok := p.namedTracer[il]
 	if !ok {
diff --git a/trace/config.go b/trace/config.go
index 4485fb9b..e93511d6 100644
--- a/trace/config.go
+++ b/trace/config.go
@@ -23,6 +23,8 @@ import (
 // TracerConfig is a group of options for a Tracer.
 type TracerConfig struct {
 	instrumentationVersion string
+	// Schema URL of the telemetry emitted by the Tracer.
+	schemaURL string
 }
 
 // InstrumentationVersion returns the version of the library providing instrumentation.
@@ -30,6 +32,11 @@ func (t *TracerConfig) InstrumentationVersion() string {
 	return t.instrumentationVersion
 }
 
+// SchemaURL returns the Schema URL of the telemetry emitted by the Tracer.
+func (t *TracerConfig) SchemaURL() string {
+	return t.schemaURL
+}
+
 // NewTracerConfig applies all the options to a returned TracerConfig.
 func NewTracerConfig(options ...TracerOption) *TracerConfig {
 	config := new(TracerConfig)
@@ -252,3 +259,10 @@ func WithInstrumentationVersion(version string) TracerOption {
 		cfg.instrumentationVersion = version
 	})
 }
+
+// WithSchemaURL sets the schema URL for the Tracer.
+func WithSchemaURL(schemaURL string) TracerOption {
+	return tracerOptionFunc(func(cfg *TracerConfig) {
+		cfg.schemaURL = schemaURL
+	})
+}


Test patch:
diff --git a/exporters/otlp/internal/transform/span_test.go b/exporters/otlp/internal/transform/span_test.go
index 9fec4f44..74ed36b8 100644
--- a/exporters/otlp/internal/transform/span_test.go
+++ b/exporters/otlp/internal/transform/span_test.go
@@ -263,8 +263,9 @@ func TestSpanData(t *testing.T) {
 		DroppedLinks:      3,
 		Resource:          resource.NewWithAttributes(attribute.String("rk1", "rv1"), attribute.Int64("rk2", 5)),
 		InstrumentationLibrary: instrumentation.Library{
-			Name:    "go.opentelemetry.io/test/otel",
-			Version: "v0.0.1",
+			Name:      "go.opentelemetry.io/test/otel",
+			Version:   "v0.0.1",
+			SchemaURL: "https://opentelemetry.io/schemas/1.2.0",
 		},
 	}
 
@@ -295,6 +296,7 @@ func TestSpanData(t *testing.T) {
 	assert.Equal(t, got[0].GetResource(), Resource(spanData.Resource))
 	ilSpans := got[0].GetInstrumentationLibrarySpans()
 	require.Len(t, ilSpans, 1)
+	// TODO: Add SchemaURL field checking once the field is added to the proto.
 	assert.Equal(t, ilSpans[0].GetInstrumentationLibrary(), instrumentationLibrary(spanData.InstrumentationLibrary))
 	require.Len(t, ilSpans[0].Spans, 1)
 	actualSpan := ilSpans[0].Spans[0]
diff --git a/exporters/stdout/example_test.go b/exporters/stdout/example_test.go
index 82da5665..f38a572b 100644
--- a/exporters/stdout/example_test.go
+++ b/exporters/stdout/example_test.go
@@ -35,6 +35,7 @@ var (
 	tracer = otel.GetTracerProvider().Tracer(
 		instrumentationName,
 		trace.WithInstrumentationVersion(instrumentationVersion),
+		trace.WithSchemaURL("https://opentelemetry.io/schemas/1.2.0"),
 	)
 
 	meter = global.GetMeterProvider().Meter(
diff --git a/exporters/stdout/trace_test.go b/exporters/stdout/trace_test.go
index f9cccef9..fea48d65 100644
--- a/exporters/stdout/trace_test.go
+++ b/exporters/stdout/trace_test.go
@@ -170,7 +170,8 @@ func TestExporter_ExportSpan(t *testing.T) {
 		],
 		"InstrumentationLibrary": {
 			"Name": "",
-			"Version": ""
+			"Version": "",
+			"SchemaURL": ""
 		}
 	}
 ]
diff --git a/sdk/trace/provider_test.go b/sdk/trace/provider_test.go
index 6a55b50b..e2fce31d 100644
--- a/sdk/trace/provider_test.go
+++ b/sdk/trace/provider_test.go
@@ -20,6 +20,8 @@ import (
 	"testing"
 
 	"github.com/stretchr/testify/assert"
+
+	"go.opentelemetry.io/otel/trace"
 )
 
 type basicSpanProcesor struct {
@@ -82,3 +84,13 @@ func TestFailedProcessorShutdownInUnregister(t *testing.T) {
 	err := stp.Shutdown(context.Background())
 	assert.NoError(t, err)
 }
+
+func TestSchemaURL(t *testing.T) {
+	stp := NewTracerProvider()
+	schemaURL := "https://opentelemetry.io/schemas/1.2.0"
+	tracerIface := stp.Tracer("tracername", trace.WithSchemaURL(schemaURL))
+
+	// Verify that the SchemaURL of the constructed Tracer is correctly populated.
+	tracerStruct := tracerIface.(*tracer)
+	assert.EqualValues(t, schemaURL, tracerStruct.instrumentationLibrary.SchemaURL)
+}
diff --git a/sdk/trace/trace_test.go b/sdk/trace/trace_test.go
index b18a9d77..cf055c57 100644
--- a/sdk/trace/trace_test.go
+++ b/sdk/trace/trace_test.go
@@ -1284,7 +1284,7 @@ func TestWithResource(t *testing.T) {
 	}
 }
 
-func TestWithInstrumentationVersion(t *testing.T) {
+func TestWithInstrumentationVersionAndSchema(t *testing.T) {
 	te := NewTestExporter()
 	tp := NewTracerProvider(WithSyncer(te), WithResource(resource.Empty()))
 
@@ -1293,6 +1293,7 @@ func TestWithInstrumentationVersion(t *testing.T) {
 	_, span := tp.Tracer(
 		"WithInstrumentationVersion",
 		trace.WithInstrumentationVersion("v0.1.0"),
+		trace.WithSchemaURL("https://opentelemetry.io/schemas/1.2.0"),
 	).Start(ctx, "span0")
 	got, err := endSpan(te, span)
 	if err != nil {
@@ -1308,8 +1309,9 @@ func TestWithInstrumentationVersion(t *testing.T) {
 		name:     "span0",
 		spanKind: trace.SpanKindInternal,
 		instrumentationLibrary: instrumentation.Library{
-			Name:    "WithInstrumentationVersion",
-			Version: "v0.1.0",
+			Name:      "WithInstrumentationVersion",
+			Version:   "v0.1.0",
+			SchemaURL: "https://opentelemetry.io/schemas/1.2.0",
 		},
 	}
 	if diff := cmpDiff(got, want); diff != "" {
diff --git a/trace/config_test.go b/trace/config_test.go
index 074aa303..3b8c9b18 100644
--- a/trace/config_test.go
+++ b/trace/config_test.go
@@ -177,6 +177,7 @@ func TestNewSpanConfig(t *testing.T) {
 func TestTracerConfig(t *testing.T) {
 	v1 := "semver:0.0.1"
 	v2 := "semver:1.0.0"
+	schemaURL := "https://opentelemetry.io/schemas/1.2.0"
 	tests := []struct {
 		options  []TracerOption
 		expected *TracerConfig
@@ -204,6 +205,14 @@ func TestTracerConfig(t *testing.T) {
 				instrumentationVersion: v2,
 			},
 		},
+		{
+			[]TracerOption{
+				WithSchemaURL(schemaURL),
+			},
+			&TracerConfig{
+				schemaURL: schemaURL,
+			},
+		},
 	}
 	for _, test := range tests {
 		config := NewTracerConfig(test.options...)