Repository: open-telemetry/opentelemetry-go (go)

Issue:
Suggestion: core.Key should be a type alias instead of struct
Making [`core.Key`](https://github.com/open-telemetry/opentelemetry-go/blob/e0eccc9a57e0cae76c1c5b0dde747bb79a8f1300/api/core/key.go#L8-L10) a type alias for `string` instead of a whole struct would (arguably!) make it easier to use, while still allowing us to add methods on `Key`.

Pull request:
change key to a string alias and KeyValue constructors
fixes #209 

Patch:
diff --git a/api/core/key.go b/api/core/key.go
index e6c78f3a..8a31e798 100644
--- a/api/core/key.go
+++ b/api/core/key.go
@@ -5,9 +5,7 @@ import (
 	"unsafe"
 )
 
-type Key struct {
-	Name string
-}
+type Key string
 
 type KeyValue struct {
 	Key   Key
@@ -147,7 +145,7 @@ func (k Key) Uint(v uint) KeyValue {
 }
 
 func (k Key) Defined() bool {
-	return len(k.Name) != 0
+	return len(k) != 0
 }
 
 // TODO make this a lazy one-time conversion.
diff --git a/api/distributedcontext/context.go b/api/distributedcontext/context.go
index 131e0aa2..e37a537d 100644
--- a/api/distributedcontext/context.go
+++ b/api/distributedcontext/context.go
@@ -48,7 +48,7 @@ func Do(ctx context.Context, f func(ctx context.Context)) {
 	m := FromContext(ctx)
 	keyvals := make([]string, 0, 2*len(m.m))
 	for k, v := range m.m {
-		keyvals = append(keyvals, k.Name, v.value.Emit())
+		keyvals = append(keyvals, string(k), v.value.Emit())
 	}
 	pprof.Do(ctx, pprof.Labels(keyvals...), f)
 }
diff --git a/api/key/key.go b/api/key/key.go
index 0dc4665b..55e82696 100644
--- a/api/key/key.go
+++ b/api/key/key.go
@@ -5,7 +5,49 @@ import (
 )
 
 func New(name string) core.Key {
-	return core.Key{
-		Name: name,
-	}
+	return core.Key(name)
+}
+
+func Bool(k string, v bool) core.KeyValue {
+	return New(k).Bool(v)
+}
+
+func Int64(k string, v int64) core.KeyValue {
+	return New(k).Int64(v)
+}
+
+func Uint64(k string, v uint64) core.KeyValue {
+	return New(k).Uint64(v)
+}
+
+func Float64(k string, v float64) core.KeyValue {
+	return New(k).Float64(v)
+}
+
+func Int32(k string, v int32) core.KeyValue {
+	return New(k).Int32(v)
+}
+
+func Uint32(k string, v uint32) core.KeyValue {
+	return New(k).Uint32(v)
+}
+
+func Float32(k string, v float32) core.KeyValue {
+	return New(k).Float32(v)
+}
+
+func String(k, v string) core.KeyValue {
+	return New(k).String(v)
+}
+
+func Bytes(k string, v []byte) core.KeyValue {
+	return New(k).Bytes(v)
+}
+
+func Int(k string, v int) core.KeyValue {
+	return New(k).Int(v)
+}
+
+func Uint(k string, v uint) core.KeyValue {
+	return New(k).Uint(v)
 }
diff --git a/experimental/bridge/opentracing/bridge.go b/experimental/bridge/opentracing/bridge.go
index 19456062..d8d7e4fb 100644
--- a/experimental/bridge/opentracing/bridge.go
+++ b/experimental/bridge/opentracing/bridge.go
@@ -432,9 +432,7 @@ func otTagToOtelCoreKeyValue(k string, v interface{}) otelcore.KeyValue {
 }
 
 func otTagToOtelCoreKey(k string) otelcore.Key {
-	return otelcore.Key{
-		Name: k,
-	}
+	return otelcore.Key(k)
 }
 
 type bridgeReference struct {
diff --git a/experimental/streaming/exporter/reader/format/format.go b/experimental/streaming/exporter/reader/format/format.go
index 25e34e11..1b344f0a 100644
--- a/experimental/streaming/exporter/reader/format/format.go
+++ b/experimental/streaming/exporter/reader/format/format.go
@@ -41,7 +41,7 @@ func AppendEvent(buf *strings.Builder, data reader.Event) {
 				return true
 			}
 			buf.WriteString(" ")
-			buf.WriteString(kv.Key.Name)
+			buf.WriteString(string(kv.Key))
 			buf.WriteString("=")
 			buf.WriteString(kv.Value.Emit())
 			return true
@@ -81,7 +81,7 @@ func AppendEvent(buf *strings.Builder, data reader.Event) {
 		buf.WriteString(" (")
 		data.Attributes.Foreach(func(kv core.KeyValue) bool {
 			buf.WriteString(" ")
-			buf.WriteString(kv.Key.Name)
+			buf.WriteString(string(kv.Key))
 			buf.WriteString("=")
 			buf.WriteString(kv.Value.Emit())
 			return true
@@ -150,7 +150,7 @@ func formatMetricLabels(buf *strings.Builder, l distributedcontext.Map) {
 			buf.WriteString(",")
 		}
 		i++
-		buf.WriteString(kv.Key.Name)
+		buf.WriteString(string(kv.Key))
 		buf.WriteString("=")
 		buf.WriteString(kv.Value.Emit())
 		return true
diff --git a/exporter/trace/jaeger/jaeger.go b/exporter/trace/jaeger/jaeger.go
index 04e82040..616331a0 100644
--- a/exporter/trace/jaeger/jaeger.go
+++ b/exporter/trace/jaeger/jaeger.go
@@ -227,25 +227,25 @@ func coreAttributeToTag(kv core.KeyValue) *gen.Tag {
 	switch kv.Value.Type {
 	case core.STRING:
 		tag = &gen.Tag{
-			Key:   kv.Key.Name,
+			Key:   string(kv.Key),
 			VStr:  &kv.Value.String,
 			VType: gen.TagType_STRING,
 		}
 	case core.BOOL:
 		tag = &gen.Tag{
-			Key:   kv.Key.Name,
+			Key:   string(kv.Key),
 			VBool: &kv.Value.Bool,
 			VType: gen.TagType_BOOL,
 		}
 	case core.INT32, core.INT64:
 		tag = &gen.Tag{
-			Key:   kv.Key.Name,
+			Key:   string(kv.Key),
 			VLong: &kv.Value.Int64,
 			VType: gen.TagType_LONG,
 		}
 	case core.FLOAT32, core.FLOAT64:
 		tag = &gen.Tag{
-			Key:     kv.Key.Name,
+			Key:     string(kv.Key),
 			VDouble: &kv.Value.Float64,
 			VType:   gen.TagType_DOUBLE,
 		}
diff --git a/exporter/trace/stackdriver/trace_proto.go b/exporter/trace/stackdriver/trace_proto.go
index 51b523da..db97bcc9 100644
--- a/exporter/trace/stackdriver/trace_proto.go
+++ b/exporter/trace/stackdriver/trace_proto.go
@@ -170,7 +170,7 @@ func copyAttributes(out **tracepb.Span_Attributes, in []core.KeyValue) {
 		if av == nil {
 			continue
 		}
-		switch kv.Key.Name {
+		switch kv.Key {
 		case PathAttribute:
 			(*out).AttributeMap[labelHTTPPath] = av
 		case HostAttribute:
@@ -182,11 +182,11 @@ func copyAttributes(out **tracepb.Span_Attributes, in []core.KeyValue) {
 		case StatusCodeAttribute:
 			(*out).AttributeMap[labelHTTPStatusCode] = av
 		default:
-			if len(kv.Key.Name) > 128 {
+			if len(kv.Key) > 128 {
 				dropped++
 				continue
 			}
-			(*out).AttributeMap[kv.Key.Name] = av
+			(*out).AttributeMap[string(kv.Key)] = av
 		}
 	}
 	(*out).DroppedAttributesCount = dropped
diff --git a/plugin/othttp/server.go b/plugin/othttp/server.go
index 6e5d3073..d3781887 100644
--- a/plugin/othttp/server.go
+++ b/plugin/othttp/server.go
@@ -35,17 +35,17 @@ const (
 
 // Attribute keys that HTTPHandler could write out.
 const (
-	HostKeyName       = "http.host"        // the http host (http.Request.Host)
-	MethodKeyName     = "http.method"      // the http method (http.Request.Method)
-	PathKeyName       = "http.path"        // the http path (http.Request.URL.Path)
-	URLKeyName        = "http.url"         // the http url (http.Request.URL.String())
-	UserAgentKeyName  = "http.user_agent"  // the http user agent (http.Request.UserAgent())
-	RouteKeyName      = "http.route"       // the http route (ex: /users/:id)
-	StatusCodeKeyName = "http.status_code" // if set, the http status
-	ReadBytesKeyName  = "http.read_bytes"  // if anything was read from the request body, the total number of bytes read
-	ReadErrorKeyName  = "http.read_error"  // If an error occurred while reading a request, the string of the error (io.EOF is not recorded)
-	WroteBytesKeyName = "http.wrote_bytes" // if anything was written to the response writer, the total number of bytes written
-	WriteErrorKeyName = "http.write_error" // if an error occurred while writing a reply, the string of the error (io.EOF is not recorded)
+	HostKeyName       core.Key = "http.host"        // the http host (http.Request.Host)
+	MethodKeyName     core.Key = "http.method"      // the http method (http.Request.Method)
+	PathKeyName       core.Key = "http.path"        // the http path (http.Request.URL.Path)
+	URLKeyName        core.Key = "http.url"         // the http url (http.Request.URL.String())
+	UserAgentKeyName  core.Key = "http.user_agent"  // the http user agent (http.Request.UserAgent())
+	RouteKeyName      core.Key = "http.route"       // the http route (ex: /users/:id)
+	StatusCodeKeyName core.Key = "http.status_code" // if set, the http status
+	ReadBytesKeyName  core.Key = "http.read_bytes"  // if anything was read from the request body, the total number of bytes read
+	ReadErrorKeyName  core.Key = "http.read_error"  // If an error occurred while reading a request, the string of the error (io.EOF is not recorded)
+	WroteBytesKeyName core.Key = "http.wrote_bytes" // if anything was written to the response writer, the total number of bytes written
+	WriteErrorKeyName core.Key = "http.write_error" // if an error occurred while writing a reply, the string of the error (io.EOF is not recorded)
 )
 
 // HTTPHandler provides http middleware that corresponds to the http.Handler interface
@@ -151,7 +151,7 @@ func (h *HTTPHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
 	if h.readEvent {
 		readRecordFunc = func(n int) {
 			span.AddEvent(ctx, "read", core.KeyValue{
-				Key: core.Key{Name: ReadBytesKeyName},
+				Key: ReadBytesKeyName,
 				Value: core.Value{
 					Type:  core.INT64,
 					Int64: int64(n),
@@ -165,7 +165,7 @@ func (h *HTTPHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
 	if h.writeEvent {
 		writeRecordFunc = func(n int) {
 			span.AddEvent(ctx, "write", core.KeyValue{
-				Key: core.Key{Name: WroteBytesKeyName},
+				Key: WroteBytesKeyName,
 				Value: core.Value{
 					Type:  core.INT64,
 					Int64: int64(n),
@@ -189,31 +189,31 @@ func setBeforeServeAttributes(span trace.Span, host, method, path, url, uagent s
 	// are available to be mutated by the handler if needed.
 	span.SetAttributes(
 		core.KeyValue{
-			Key: core.Key{Name: HostKeyName},
+			Key: HostKeyName,
 			Value: core.Value{
 				Type:   core.STRING,
 				String: host,
 			}},
 		core.KeyValue{
-			Key: core.Key{Name: MethodKeyName},
+			Key: MethodKeyName,
 			Value: core.Value{
 				Type:   core.STRING,
 				String: method,
 			}},
 		core.KeyValue{
-			Key: core.Key{Name: PathKeyName},
+			Key: PathKeyName,
 			Value: core.Value{
 				Type:   core.STRING,
 				String: path,
 			}},
 		core.KeyValue{
-			Key: core.Key{Name: URLKeyName},
+			Key: URLKeyName,
 			Value: core.Value{
 				Type:   core.STRING,
 				String: url,
 			}},
 		core.KeyValue{
-			Key: core.Key{Name: UserAgentKeyName},
+			Key: UserAgentKeyName,
 			Value: core.Value{
 				Type:   core.STRING,
 				String: uagent,
@@ -228,7 +228,7 @@ func setAfterServeAttributes(span trace.Span, read, wrote, statusCode int64, rer
 	if read > 0 {
 		kv = append(kv,
 			core.KeyValue{
-				Key: core.Key{Name: ReadBytesKeyName},
+				Key: ReadBytesKeyName,
 				Value: core.Value{
 					Type:  core.INT64,
 					Int64: read,
@@ -238,7 +238,7 @@ func setAfterServeAttributes(span trace.Span, read, wrote, statusCode int64, rer
 	if rerr != nil && rerr != io.EOF {
 		kv = append(kv,
 			core.KeyValue{
-				Key: core.Key{Name: ReadErrorKeyName},
+				Key: ReadErrorKeyName,
 				Value: core.Value{
 					Type:   core.STRING,
 					String: rerr.Error(),
@@ -248,7 +248,7 @@ func setAfterServeAttributes(span trace.Span, read, wrote, statusCode int64, rer
 	if wrote > 0 {
 		kv = append(kv,
 			core.KeyValue{
-				Key: core.Key{Name: WroteBytesKeyName},
+				Key: WroteBytesKeyName,
 				Value: core.Value{
 					Type:  core.INT64,
 					Int64: wrote,
@@ -258,7 +258,7 @@ func setAfterServeAttributes(span trace.Span, read, wrote, statusCode int64, rer
 	if statusCode > 0 {
 		kv = append(kv,
 			core.KeyValue{
-				Key: core.Key{Name: StatusCodeKeyName},
+				Key: StatusCodeKeyName,
 				Value: core.Value{
 					Type:  core.INT64,
 					Int64: statusCode,
@@ -268,7 +268,7 @@ func setAfterServeAttributes(span trace.Span, read, wrote, statusCode int64, rer
 	if werr != nil && werr != io.EOF {
 		kv = append(kv,
 			core.KeyValue{
-				Key: core.Key{Name: WriteErrorKeyName},
+				Key: WriteErrorKeyName,
 				Value: core.Value{
 					Type:   core.STRING,
 					String: werr.Error(),
@@ -284,7 +284,7 @@ func WithRouteTag(route string, h http.Handler) http.Handler {
 		//TODO: Why doesn't tag.Upsert work?
 		span.SetAttribute(
 			core.KeyValue{
-				Key: core.Key{Name: RouteKeyName},
+				Key: RouteKeyName,
 				Value: core.Value{
 					Type:   core.STRING,
 					String: route,


Test patch:
diff --git a/api/core/key_test.go b/api/core/key_test.go
index a0be400b..68000469 100644
--- a/api/core/key_test.go
+++ b/api/core/key_test.go
@@ -26,7 +26,7 @@ func TestBool(t *testing.T) {
 	} {
 		t.Run(testcase.name, func(t *testing.T) {
 			//proto: func (k core.Key) core.Bool(v bool) KeyValue {}
-			have := core.Key{}.Bool(testcase.v)
+			have := core.Key("").Bool(testcase.v)
 			if diff := cmp.Diff(testcase.want, have.Value); diff != "" {
 				t.Fatal(diff)
 			}
@@ -51,7 +51,7 @@ func TestInt64(t *testing.T) {
 	} {
 		t.Run(testcase.name, func(t *testing.T) {
 			//proto: func (k core.Key) Int64(v int64) KeyValue {
-			have := core.Key{}.Int64(testcase.v)
+			have := core.Key("").Int64(testcase.v)
 			if diff := cmp.Diff(testcase.want, have.Value); diff != "" {
 				t.Fatal(diff)
 			}
@@ -76,7 +76,7 @@ func TestUint64(t *testing.T) {
 	} {
 		t.Run(testcase.name, func(t *testing.T) {
 			//proto: func (k core.Key) Uint64(v uint64) KeyValue {
-			have := core.Key{}.Uint64(testcase.v)
+			have := core.Key("").Uint64(testcase.v)
 			if diff := cmp.Diff(testcase.want, have.Value); diff != "" {
 				t.Fatal(diff)
 			}
@@ -101,7 +101,7 @@ func TestFloat64(t *testing.T) {
 	} {
 		t.Run(testcase.name, func(t *testing.T) {
 			//proto: func (k core.Key) Float64(v float64) KeyValue {
-			have := core.Key{}.Float64(testcase.v)
+			have := core.Key("").Float64(testcase.v)
 			if diff := cmp.Diff(testcase.want, have.Value); diff != "" {
 				t.Fatal(diff)
 			}
@@ -126,7 +126,7 @@ func TestInt32(t *testing.T) {
 	} {
 		t.Run(testcase.name, func(t *testing.T) {
 			//proto: func (k core.Key) Int32(v int32) KeyValue {
-			have := core.Key{}.Int32(testcase.v)
+			have := core.Key("").Int32(testcase.v)
 			if diff := cmp.Diff(testcase.want, have.Value); diff != "" {
 				t.Fatal(diff)
 			}
@@ -151,7 +151,7 @@ func TestUint32(t *testing.T) {
 	} {
 		t.Run(testcase.name, func(t *testing.T) {
 			//proto: func (k core.Key) Uint32(v uint32) KeyValue {
-			have := core.Key{}.Uint32(testcase.v)
+			have := core.Key("").Uint32(testcase.v)
 			if diff := cmp.Diff(testcase.want, have.Value); diff != "" {
 				t.Fatal(diff)
 			}
@@ -176,7 +176,7 @@ func TestFloat32(t *testing.T) {
 	} {
 		t.Run(testcase.name, func(t *testing.T) {
 			//proto: func (k core.Key) Float32(v float32) KeyValue {
-			have := core.Key{}.Float32(testcase.v)
+			have := core.Key("").Float32(testcase.v)
 			if diff := cmp.Diff(testcase.want, have.Value); diff != "" {
 				t.Fatal(diff)
 			}
@@ -201,7 +201,7 @@ func TestString(t *testing.T) {
 	} {
 		t.Run(testcase.name, func(t *testing.T) {
 			//proto: func (k core.Key) String(v string) KeyValue {
-			have := core.Key{}.String(testcase.v)
+			have := core.Key("").String(testcase.v)
 			if diff := cmp.Diff(testcase.want, have.Value); diff != "" {
 				t.Fatal(diff)
 			}
@@ -226,7 +226,7 @@ func TestBytes(t *testing.T) {
 	} {
 		t.Run(testcase.name, func(t *testing.T) {
 			//proto: func (k core.Key) Bytes(v []byte) KeyValue {
-			have := core.Key{}.Bytes(testcase.v)
+			have := core.Key("").Bytes(testcase.v)
 			if diff := cmp.Diff(testcase.want, have.Value); diff != "" {
 				t.Fatal(diff)
 			}
@@ -257,7 +257,7 @@ func TestInt(t *testing.T) {
 	} {
 		t.Run(testcase.name, func(t *testing.T) {
 			//proto: func (k core.Key) Int(v int) KeyValue {
-			have := core.Key{}.Int(testcase.v)
+			have := core.Key("").Int(testcase.v)
 			if diff := cmp.Diff(testcase.want, have.Value); diff != "" {
 				t.Fatal(diff)
 			}
@@ -288,7 +288,7 @@ func TestUint(t *testing.T) {
 	} {
 		t.Run(testcase.name, func(t *testing.T) {
 			//proto: func (k core.Key) Uint(v uint) KeyValue {
-			have := core.Key{}.Uint(testcase.v)
+			have := core.Key("").Uint(testcase.v)
 			if diff := cmp.Diff(testcase.want, have.Value); diff != "" {
 				t.Fatal(diff)
 			}
@@ -304,14 +304,12 @@ func TestDefined(t *testing.T) {
 	}{
 		{
 			name: "Key.Defined() returns true when len(v.Name) != 0",
-			k: core.Key{
-				Name: "foo",
-			},
+			k:    core.Key("foo"),
 			want: true,
 		},
 		{
 			name: "Key.Defined() returns false when len(v.Name) == 0",
-			k:    core.Key{},
+			k:    core.Key(""),
 			want: false,
 		},
 	} {
diff --git a/api/key/key_test.go b/api/key/key_test.go
new file mode 100644
index 00000000..3a3ca01c
--- /dev/null
+++ b/api/key/key_test.go
@@ -0,0 +1,155 @@
+package key_test
+
+import (
+	"testing"
+	"unsafe"
+
+	"github.com/google/go-cmp/cmp"
+
+	"go.opentelemetry.io/api/core"
+	"go.opentelemetry.io/api/key"
+)
+
+func TestKeyValueConstructors(t *testing.T) {
+
+	tt := []struct {
+		name     string
+		actual   core.KeyValue
+		expected core.KeyValue
+	}{
+		{
+			name:   "Bool",
+			actual: key.Bool("k1", true),
+			expected: core.KeyValue{
+				Key: "k1",
+				Value: core.Value{
+					Type: core.BOOL,
+					Bool: true,
+				},
+			},
+		},
+		{
+			name:   "Int64",
+			actual: key.Int64("k1", 123),
+			expected: core.KeyValue{
+				Key: "k1",
+				Value: core.Value{
+					Type:  core.INT64,
+					Int64: 123,
+				},
+			},
+		},
+		{
+			name:   "Uint64",
+			actual: key.Uint64("k1", 1),
+			expected: core.KeyValue{
+				Key: "k1",
+				Value: core.Value{
+					Type:   core.UINT64,
+					Uint64: 1,
+				},
+			},
+		},
+		{
+			name:   "Float64",
+			actual: key.Float64("k1", 123.5),
+			expected: core.KeyValue{
+				Key: "k1",
+				Value: core.Value{
+					Type:    core.FLOAT64,
+					Float64: 123.5,
+				},
+			},
+		},
+		{
+			name:   "Int32",
+			actual: key.Int32("k1", 123),
+			expected: core.KeyValue{
+				Key: "k1",
+				Value: core.Value{
+					Type:  core.INT32,
+					Int64: 123,
+				},
+			},
+		},
+		{
+			name:   "Uint32",
+			actual: key.Uint32("k1", 123),
+			expected: core.KeyValue{
+				Key: "k1",
+				Value: core.Value{
+					Type:   core.UINT32,
+					Uint64: 123,
+				},
+			},
+		},
+		{
+			name:   "Float32",
+			actual: key.Float32("k1", 123.5),
+			expected: core.KeyValue{
+				Key: "k1",
+				Value: core.Value{
+					Type:    core.FLOAT32,
+					Float64: 123.5,
+				},
+			},
+		},
+		{
+			name:   "Bytes",
+			actual: key.Bytes("k1", []byte("v1")),
+			expected: core.KeyValue{
+				Key: "k1",
+				Value: core.Value{
+					Type:  core.BYTES,
+					Bytes: []byte("v1"),
+				},
+			},
+		},
+		{
+			name:   "Int",
+			actual: key.Int("k1", 123),
+			expected: core.KeyValue{
+				Key: "k1",
+				Value: core.Value{
+					Int64: 123,
+					Type:  IntType(123),
+				},
+			},
+		},
+		{
+			name:   "Uint",
+			actual: key.Uint("k1", 123),
+			expected: core.KeyValue{
+				Key: "k1",
+				Value: core.Value{
+					Uint64: 123,
+					Type:   UintType(123),
+				},
+			},
+		},
+	}
+
+	for _, test := range tt {
+		t.Run(test.name, func(t *testing.T) {
+			if diff := cmp.Diff(test.actual, test.expected); diff != "" {
+				t.Fatal(diff)
+			}
+		})
+	}
+}
+
+// IntType returns the core.ValueType depending on system int byte-size
+func IntType(v int) core.ValueType {
+	if unsafe.Sizeof(v) == 4 {
+		return core.INT32
+	}
+	return core.INT64
+}
+
+// UintType returns the core.ValueType depending on system uint byte-size
+func UintType(v uint) core.ValueType {
+	if unsafe.Sizeof(v) == 4 {
+		return core.UINT32
+	}
+	return core.UINT64
+}
diff --git a/api/metric/api_test.go b/api/metric/api_test.go
index afda76d2..1c998c08 100644
--- a/api/metric/api_test.go
+++ b/api/metric/api_test.go
@@ -20,6 +20,7 @@ import (
 	"testing"
 
 	"go.opentelemetry.io/api/core"
+	"go.opentelemetry.io/api/key"
 	"go.opentelemetry.io/api/unit"
 )
 
@@ -44,14 +45,14 @@ func TestCounterOptions(t *testing.T) {
 		{
 			name: "keys keys keys",
 			opts: []CounterOptionApplier{
-				WithKeys(key("foo"), key("foo2")),
-				WithKeys(key("bar"), key("bar2")),
-				WithKeys(key("baz"), key("baz2")),
+				WithKeys(key.New("foo"), key.New("foo2")),
+				WithKeys(key.New("bar"), key.New("bar2")),
+				WithKeys(key.New("baz"), key.New("baz2")),
 			},
 			keys: []core.Key{
-				key("foo"), key("foo2"),
-				key("bar"), key("bar2"),
-				key("baz"), key("baz2"),
+				key.New("foo"), key.New("foo2"),
+				key.New("bar"), key.New("bar2"),
+				key.New("baz"), key.New("baz2"),
 			},
 			desc: "",
 			unit: "",
@@ -163,14 +164,14 @@ func TestGaugeOptions(t *testing.T) {
 		{
 			name: "keys keys keys",
 			opts: []GaugeOptionApplier{
-				WithKeys(key("foo"), key("foo2")),
-				WithKeys(key("bar"), key("bar2")),
-				WithKeys(key("baz"), key("baz2")),
+				WithKeys(key.New("foo"), key.New("foo2")),
+				WithKeys(key.New("bar"), key.New("bar2")),
+				WithKeys(key.New("baz"), key.New("baz2")),
 			},
 			keys: []core.Key{
-				key("foo"), key("foo2"),
-				key("bar"), key("bar2"),
-				key("baz"), key("baz2"),
+				key.New("foo"), key.New("foo2"),
+				key.New("bar"), key.New("bar2"),
+				key.New("baz"), key.New("baz2"),
 			},
 			desc: "",
 			unit: "",
@@ -282,14 +283,14 @@ func TestMeasureOptions(t *testing.T) {
 		{
 			name: "keys keys keys",
 			opts: []MeasureOptionApplier{
-				WithKeys(key("foo"), key("foo2")),
-				WithKeys(key("bar"), key("bar2")),
-				WithKeys(key("baz"), key("baz2")),
+				WithKeys(key.New("foo"), key.New("foo2")),
+				WithKeys(key.New("bar"), key.New("bar2")),
+				WithKeys(key.New("baz"), key.New("baz2")),
 			},
 			keys: []core.Key{
-				key("foo"), key("foo2"),
-				key("bar"), key("bar2"),
-				key("baz"), key("baz2"),
+				key.New("foo"), key.New("foo2"),
+				key.New("bar"), key.New("bar2"),
+				key.New("baz"), key.New("baz2"),
 			},
 			desc: "",
 			unit: "",
@@ -401,14 +402,14 @@ func TestObserverOptions(t *testing.T) {
 		{
 			name: "keys keys keys",
 			opts: []GaugeOptionApplier{
-				WithKeys(key("foo"), key("foo2")),
-				WithKeys(key("bar"), key("bar2")),
-				WithKeys(key("baz"), key("baz2")),
+				WithKeys(key.New("foo"), key.New("foo2")),
+				WithKeys(key.New("bar"), key.New("bar2")),
+				WithKeys(key.New("baz"), key.New("baz2")),
 			},
 			keys: []core.Key{
-				key("foo"), key("foo2"),
-				key("bar"), key("bar2"),
-				key("baz"), key("baz2"),
+				key.New("foo"), key.New("foo2"),
+				key.New("bar"), key.New("bar2"),
+				key.New("baz"), key.New("baz2"),
 			},
 			desc: "",
 			unit: "",
@@ -499,12 +500,6 @@ func TestObserverOptions(t *testing.T) {
 	}
 }
 
-func key(name string) core.Key {
-	return core.Key{
-		Name: name,
-	}
-}
-
 type descriptor struct {
 	name string
 	keys []core.Key
@@ -527,8 +522,8 @@ func checkDescriptor(t *testing.T, e descriptor, d *Descriptor) {
 		minLen = len(d.Keys())
 	}
 	for i := 0; i < minLen; i++ {
-		if e.keys[i].Name != d.Keys()[i].Name {
-			t.Errorf("Expected key %q, got %q", e.keys[i].Name, d.Keys()[i].Name)
+		if e.keys[i] != d.Keys()[i] {
+			t.Errorf("Expected key %q, got %q", e.keys[i], d.Keys()[i])
 		}
 	}
 	if e.desc != d.Description() {
diff --git a/exporter/trace/jaeger/jaeger_test.go b/exporter/trace/jaeger/jaeger_test.go
index 82c3abc7..9e301f45 100644
--- a/exporter/trace/jaeger/jaeger_test.go
+++ b/exporter/trace/jaeger/jaeger_test.go
@@ -70,11 +70,11 @@ func Test_spanDataToThrift(t *testing.T) {
 				},
 				Attributes: []core.KeyValue{
 					{
-						Key:   core.Key{Name: "key"},
+						Key:   core.Key("key"),
 						Value: core.Value{Type: core.STRING, String: keyValue},
 					},
 					{
-						Key:   core.Key{Name: "double"},
+						Key:   core.Key("double"),
 						Value: core.Value{Type: core.FLOAT64, Float64: doubleValue},
 					},
 				},
diff --git a/exporter/trace/stdout/stdout_test.go b/exporter/trace/stdout/stdout_test.go
index 384f36c0..3a85fd10 100644
--- a/exporter/trace/stdout/stdout_test.go
+++ b/exporter/trace/stdout/stdout_test.go
@@ -54,11 +54,11 @@ func TestExporter_ExportSpan(t *testing.T) {
 		EndTime:   now,
 		Attributes: []core.KeyValue{
 			{
-				Key:   core.Key{Name: "key"},
+				Key:   core.Key("key"),
 				Value: core.Value{Type: core.STRING, String: keyValue},
 			},
 			{
-				Key:   core.Key{Name: "double"},
+				Key:   core.Key("double"),
 				Value: core.Value{Type: core.FLOAT64, Float64: doubleValue},
 			},
 		},
@@ -79,11 +79,11 @@ func TestExporter_ExportSpan(t *testing.T) {
 		`"EndTime":` + string(expectedSerializedNow) + "," +
 		`"Attributes":[` +
 		`{` +
-		`"Key":{"Name":"key"},` +
+		`"Key":"key",` +
 		`"Value":{"Type":8,"Bool":false,"Int64":0,"Uint64":0,"Float64":0,"String":"value","Bytes":null}` +
 		`},` +
 		`{` +
-		`"Key":{"Name":"double"},` +
+		`"Key":"double",` +
 		`"Value":{"Type":7,"Bool":false,"Int64":0,"Uint64":0,"Float64":123.456,"String":"","Bytes":null}` +
 		`}` +
 		`],` +
diff --git a/plugin/othttp/server_example_test.go b/plugin/othttp/server_example_test.go
index 278125b3..c30c3da6 100644
--- a/plugin/othttp/server_example_test.go
+++ b/plugin/othttp/server_example_test.go
@@ -70,7 +70,7 @@ func ExampleNewHandler() {
 		default:
 			span := trace.CurrentSpan(ctx)
 			span.SetAttribute(
-				core.KeyValue{Key: core.Key{Name: "name"},
+				core.KeyValue{Key: "name",
 					Value: core.Value{Type: core.STRING, String: pp[1]},
 				},
 			)
diff --git a/sdk/trace/trace_test.go b/sdk/trace/trace_test.go
index f0e3ce77..3c251225 100644
--- a/sdk/trace/trace_test.go
+++ b/sdk/trace/trace_test.go
@@ -229,7 +229,7 @@ func TestSetSpanAttributes(t *testing.T) {
 		ParentSpanID: sid,
 		Name:         "span0",
 		Attributes: []core.KeyValue{{
-			Key:   core.Key{Name: "key1"},
+			Key:   core.Key("key1"),
 			Value: core.Value{Type: core.STRING, String: "value1"},
 		}},
 		HasRemoteParent: true,
@@ -262,11 +262,11 @@ func TestSetSpanAttributesOverLimit(t *testing.T) {
 		Name:         "span0",
 		Attributes: []core.KeyValue{
 			{
-				Key:   core.Key{Name: "key1"},
+				Key:   core.Key("key1"),
 				Value: core.Value{Type: core.STRING, String: "value3"},
 			},
 			{
-				Key:   core.Key{Name: "key4"},
+				Key:   core.Key("key4"),
 				Value: core.Value{Type: core.STRING, String: "value4"},
 			},
 		},