1
0
mirror of https://github.com/open-telemetry/opentelemetry-go.git synced 2025-02-01 13:07:51 +02:00

Shrink core.Value (#256)

* shrink the value type

went down from 40 bytes to 24

* add missing license blurb

* stringify value type

* print string value types in stdout exporter

* make Value function take a pointer receiver
This commit is contained in:
Krzesimir Nowak 2019-10-30 23:01:19 +01:00 committed by rghetia
parent de6715fca3
commit 563985f5d1
13 changed files with 536 additions and 510 deletions

View File

@ -1,5 +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 core
//go:generate stringer -type=ValueType
import (
"fmt"
"unsafe"
@ -15,20 +31,10 @@ type KeyValue struct {
type ValueType int
type Value struct {
Type ValueType
Bool bool
Int64 int64
Uint64 uint64
Float64 float64
String string
// Note: this type could be made smaller by using a
// core.Number to represent four of these fields, e.g.,
// struct {
// Type ValueType
// String string
// Number Number
// }
vtype ValueType
numeric uint64
stringly string
// TODO Lazy value type?
}
const (
@ -43,116 +49,217 @@ const (
STRING
)
func Bool(v bool) Value {
return Value{
vtype: BOOL,
numeric: boolToRaw(v),
}
}
func Int64(v int64) Value {
return Value{
vtype: INT64,
numeric: int64ToRaw(v),
}
}
func Uint64(v uint64) Value {
return Value{
vtype: UINT64,
numeric: uint64ToRaw(v),
}
}
func Float64(v float64) Value {
return Value{
vtype: FLOAT64,
numeric: float64ToRaw(v),
}
}
func Int32(v int32) Value {
return Value{
vtype: INT32,
numeric: int32ToRaw(v),
}
}
func Uint32(v uint32) Value {
return Value{
vtype: UINT32,
numeric: uint32ToRaw(v),
}
}
func Float32(v float32) Value {
return Value{
vtype: FLOAT32,
numeric: float32ToRaw(v),
}
}
func String(v string) Value {
return Value{
vtype: STRING,
stringly: v,
}
}
func Int(v int) Value {
if unsafe.Sizeof(v) == 4 {
return Int32(int32(v))
}
return Int64(int64(v))
}
func Uint(v uint) Value {
if unsafe.Sizeof(v) == 4 {
return Uint32(uint32(v))
}
return Uint64(uint64(v))
}
func (k Key) Bool(v bool) KeyValue {
return KeyValue{
Key: k,
Value: Value{
Type: BOOL,
Bool: v,
},
Key: k,
Value: Bool(v),
}
}
func (k Key) Int64(v int64) KeyValue {
return KeyValue{
Key: k,
Value: Value{
Type: INT64,
Int64: v,
},
Key: k,
Value: Int64(v),
}
}
func (k Key) Uint64(v uint64) KeyValue {
return KeyValue{
Key: k,
Value: Value{
Type: UINT64,
Uint64: v,
},
Key: k,
Value: Uint64(v),
}
}
func (k Key) Float64(v float64) KeyValue {
return KeyValue{
Key: k,
Value: Value{
Type: FLOAT64,
Float64: v,
},
Key: k,
Value: Float64(v),
}
}
func (k Key) Int32(v int32) KeyValue {
return KeyValue{
Key: k,
Value: Value{
Type: INT32,
Int64: int64(v),
},
Key: k,
Value: Int32(v),
}
}
func (k Key) Uint32(v uint32) KeyValue {
return KeyValue{
Key: k,
Value: Value{
Type: UINT32,
Uint64: uint64(v),
},
Key: k,
Value: Uint32(v),
}
}
func (k Key) Float32(v float32) KeyValue {
return KeyValue{
Key: k,
Value: Value{
Type: FLOAT32,
Float64: float64(v),
},
Key: k,
Value: Float32(v),
}
}
func (k Key) String(v string) KeyValue {
return KeyValue{
Key: k,
Value: Value{
Type: STRING,
String: v,
},
Key: k,
Value: String(v),
}
}
func (k Key) Int(v int) KeyValue {
if unsafe.Sizeof(v) == 4 {
return k.Int32(int32(v))
return KeyValue{
Key: k,
Value: Int(v),
}
return k.Int64(int64(v))
}
func (k Key) Uint(v uint) KeyValue {
if unsafe.Sizeof(v) == 4 {
return k.Uint32(uint32(v))
return KeyValue{
Key: k,
Value: Uint(v),
}
return k.Uint64(uint64(v))
}
func (k Key) Defined() bool {
return len(k) != 0
}
func (v Value) Emit() string {
switch v.Type {
case BOOL:
return fmt.Sprint(v.Bool)
case INT32, INT64:
return fmt.Sprint(v.Int64)
case UINT32, UINT64:
return fmt.Sprint(v.Uint64)
case FLOAT32, FLOAT64:
return fmt.Sprint(v.Float64)
case STRING:
return v.String
}
return "unknown"
func (v *Value) Type() ValueType {
return v.vtype
}
func (v *Value) AsBool() bool {
return rawToBool(v.numeric)
}
func (v *Value) AsInt32() int32 {
return rawToInt32(v.numeric)
}
func (v *Value) AsInt64() int64 {
return rawToInt64(v.numeric)
}
func (v *Value) AsUint32() uint32 {
return rawToUint32(v.numeric)
}
func (v *Value) AsUint64() uint64 {
return rawToUint64(v.numeric)
}
func (v *Value) AsFloat32() float32 {
return rawToFloat32(v.numeric)
}
func (v *Value) AsFloat64() float64 {
return rawToFloat64(v.numeric)
}
func (v *Value) AsString() string {
return v.stringly
}
type unknownValueType struct{}
func (v *Value) AsInterface() interface{} {
switch v.Type() {
case BOOL:
return v.AsBool()
case INT32:
return v.AsInt32()
case INT64:
return v.AsInt64()
case UINT32:
return v.AsUint32()
case UINT64:
return v.AsUint64()
case FLOAT32:
return v.AsFloat32()
case FLOAT64:
return v.AsFloat64()
case STRING:
return v.stringly
}
return unknownValueType{}
}
func (v *Value) Emit() string {
if v.Type() == STRING {
return v.stringly
}
i := v.AsInterface()
if _, ok := i.(unknownValueType); ok {
return "unknown"
}
return fmt.Sprint(i)
}

View File

@ -9,265 +9,114 @@ import (
"go.opentelemetry.io/api/core"
)
func TestBool(t *testing.T) {
func TestValue(t *testing.T) {
k := core.Key("test")
bli := getBitlessInfo(42)
for _, testcase := range []struct {
name string
v bool
want core.Value
name string
value core.Value
wantType core.ValueType
wantValue interface{}
}{
{
name: "Key.Bool() correctly returns keys's internal bool value",
v: true,
want: core.Value{
Type: core.BOOL,
Bool: true,
},
name: "Key.Bool() correctly returns keys's internal bool value",
value: k.Bool(true).Value,
wantType: core.BOOL,
wantValue: true,
},
{
name: "Key.Int64() correctly returns keys's internal int64 value",
value: k.Int64(42).Value,
wantType: core.INT64,
wantValue: int64(42),
},
{
name: "Key.Uint64() correctly returns keys's internal uint64 value",
value: k.Uint64(42).Value,
wantType: core.UINT64,
wantValue: uint64(42),
},
{
name: "Key.Float64() correctly returns keys's internal float64 value",
value: k.Float64(42.1).Value,
wantType: core.FLOAT64,
wantValue: float64(42.1),
},
{
name: "Key.Int32() correctly returns keys's internal int32 value",
value: k.Int32(42).Value,
wantType: core.INT32,
wantValue: int32(42),
},
{
name: "Key.Uint32() correctly returns keys's internal uint32 value",
value: k.Uint32(42).Value,
wantType: core.UINT32,
wantValue: uint32(42),
},
{
name: "Key.Float32() correctly returns keys's internal float32 value",
value: k.Float32(42.1).Value,
wantType: core.FLOAT32,
wantValue: float32(42.1),
},
{
name: "Key.String() correctly returns keys's internal string value",
value: k.String("foo").Value,
wantType: core.STRING,
wantValue: "foo",
},
{
name: "Key.Int() correctly returns keys's internal signed integral value",
value: k.Int(bli.intValue).Value,
wantType: bli.signedType,
wantValue: bli.signedValue,
},
{
name: "Key.Uint() correctly returns keys's internal unsigned integral value",
value: k.Uint(bli.uintValue).Value,
wantType: bli.unsignedType,
wantValue: bli.unsignedValue,
},
} {
t.Run(testcase.name, func(t *testing.T) {
//proto: func (k core.Key) core.Bool(v bool) KeyValue {}
have := core.Key("").Bool(testcase.v)
if diff := cmp.Diff(testcase.want, have.Value); diff != "" {
t.Fatal(diff)
}
})
t.Logf("Running test case %s", testcase.name)
if testcase.value.Type() != testcase.wantType {
t.Errorf("wrong value type, got %#v, expected %#v", testcase.value.Type(), testcase.wantType)
}
got := testcase.value.AsInterface()
if diff := cmp.Diff(testcase.wantValue, got); diff != "" {
t.Errorf("+got, -want: %s", diff)
}
}
}
func TestInt64(t *testing.T) {
for _, testcase := range []struct {
name string
v int64
want core.Value
}{
{
name: "Key.Int64() correctly returns keys's internal int64 value",
v: int64(42),
want: core.Value{
Type: core.INT64,
Int64: int64(42),
},
},
} {
t.Run(testcase.name, func(t *testing.T) {
//proto: func (k core.Key) Int64(v int64) KeyValue {
have := core.Key("").Int64(testcase.v)
if diff := cmp.Diff(testcase.want, have.Value); diff != "" {
t.Fatal(diff)
}
})
}
type bitlessInfo struct {
intValue int
uintValue uint
signedType core.ValueType
unsignedType core.ValueType
signedValue interface{}
unsignedValue interface{}
}
func TestUint64(t *testing.T) {
for _, testcase := range []struct {
name string
v uint64
want core.Value
}{
{
name: "Key.Uint64() correctly returns keys's internal uint64 value",
v: uint64(42),
want: core.Value{
Type: core.UINT64,
Uint64: uint64(42),
},
},
} {
t.Run(testcase.name, func(t *testing.T) {
//proto: func (k core.Key) Uint64(v uint64) KeyValue {
have := core.Key("").Uint64(testcase.v)
if diff := cmp.Diff(testcase.want, have.Value); diff != "" {
t.Fatal(diff)
}
})
func getBitlessInfo(i int) bitlessInfo {
if unsafe.Sizeof(i) == 4 {
return bitlessInfo{
intValue: i,
uintValue: uint(i),
signedType: core.INT32,
unsignedType: core.UINT32,
signedValue: int32(i),
unsignedValue: uint32(i),
}
}
}
func TestFloat64(t *testing.T) {
for _, testcase := range []struct {
name string
v float64
want core.Value
}{
{
name: "Key.float64() correctly returns keys's internal floa64 value",
v: float64(42.1),
want: core.Value{
Type: core.FLOAT64,
Float64: float64(42.1),
},
},
} {
t.Run(testcase.name, func(t *testing.T) {
//proto: func (k core.Key) Float64(v float64) KeyValue {
have := core.Key("").Float64(testcase.v)
if diff := cmp.Diff(testcase.want, have.Value); diff != "" {
t.Fatal(diff)
}
})
}
}
func TestInt32(t *testing.T) {
for _, testcase := range []struct {
name string
v int32
want core.Value
}{
{
name: "Key.int32() correctly returns keys's internal int32 value",
v: int32(42),
want: core.Value{
Type: core.INT32,
Int64: int64(42),
},
},
} {
t.Run(testcase.name, func(t *testing.T) {
//proto: func (k core.Key) Int32(v int32) KeyValue {
have := core.Key("").Int32(testcase.v)
if diff := cmp.Diff(testcase.want, have.Value); diff != "" {
t.Fatal(diff)
}
})
}
}
func TestUint32(t *testing.T) {
for _, testcase := range []struct {
name string
v uint32
want core.Value
}{
{
name: "Key.uint32() correctly returns keys's internal uint32 value",
v: uint32(42),
want: core.Value{
Type: core.UINT32,
Uint64: uint64(42),
},
},
} {
t.Run(testcase.name, func(t *testing.T) {
//proto: func (k core.Key) Uint32(v uint32) KeyValue {
have := core.Key("").Uint32(testcase.v)
if diff := cmp.Diff(testcase.want, have.Value); diff != "" {
t.Fatal(diff)
}
})
}
}
func TestFloat32(t *testing.T) {
for _, testcase := range []struct {
name string
v float32
want core.Value
}{
{
name: "Key.float32() correctly returns keys's internal float32 value",
v: float32(42.0),
want: core.Value{
Type: core.FLOAT32,
Float64: float64(42.0),
},
},
} {
t.Run(testcase.name, func(t *testing.T) {
//proto: func (k core.Key) Float32(v float32) KeyValue {
have := core.Key("").Float32(testcase.v)
if diff := cmp.Diff(testcase.want, have.Value); diff != "" {
t.Fatal(diff)
}
})
}
}
func TestString(t *testing.T) {
for _, testcase := range []struct {
name string
v string
want core.Value
}{
{
name: "Key.String() correctly returns keys's internal string value",
v: "foo",
want: core.Value{
Type: core.STRING,
String: "foo",
},
},
} {
t.Run(testcase.name, func(t *testing.T) {
//proto: func (k core.Key) String(v string) KeyValue {
have := core.Key("").String(testcase.v)
if diff := cmp.Diff(testcase.want, have.Value); diff != "" {
t.Fatal(diff)
}
})
}
}
func TestInt(t *testing.T) {
WTYPE := core.INT64
if unsafe.Sizeof(int(42)) == 4 {
// switch the desired value-type depending on system int byte-size
WTYPE = core.INT32
}
for _, testcase := range []struct {
name string
v int
want core.Value
}{
{
name: "Key.Int() correctly returns keys's internal int64 value",
v: int(42),
want: core.Value{
Type: WTYPE,
Int64: int64(42),
},
},
} {
t.Run(testcase.name, func(t *testing.T) {
//proto: func (k core.Key) Int(v int) KeyValue {
have := core.Key("").Int(testcase.v)
if diff := cmp.Diff(testcase.want, have.Value); diff != "" {
t.Fatal(diff)
}
})
}
}
func TestUint(t *testing.T) {
WTYPE := core.UINT64
if unsafe.Sizeof(uint(42)) == 4 {
// switch the desired value-type depending on system int byte-size
WTYPE = core.UINT32
}
for _, testcase := range []struct {
name string
v uint
want core.Value
}{
{
name: "Key.Uint() correctly returns keys's internal uint64 value",
v: uint(42),
want: core.Value{
Type: WTYPE,
Uint64: 42,
},
},
} {
t.Run(testcase.name, func(t *testing.T) {
//proto: func (k core.Key) Uint(v uint) KeyValue {
have := core.Key("").Uint(testcase.v)
if diff := cmp.Diff(testcase.want, have.Value); diff != "" {
t.Fatal(diff)
}
})
return bitlessInfo{
intValue: i,
uintValue: uint(i),
signedType: core.INT64,
unsignedType: core.UINT64,
signedValue: int64(i),
unsignedValue: uint64(i),
}
}
@ -306,66 +155,42 @@ func TestEmit(t *testing.T) {
}{
{
name: `test Key.Emit() can emit a string representing self.BOOL`,
v: core.Value{
Type: core.BOOL,
Bool: true,
},
v: core.Bool(true),
want: "true",
},
{
name: `test Key.Emit() can emit a string representing self.INT32`,
v: core.Value{
Type: core.INT32,
Int64: 42,
},
v: core.Int32(42),
want: "42",
},
{
name: `test Key.Emit() can emit a string representing self.INT64`,
v: core.Value{
Type: core.INT64,
Int64: 42,
},
v: core.Int64(42),
want: "42",
},
{
name: `test Key.Emit() can emit a string representing self.UINT32`,
v: core.Value{
Type: core.UINT32,
Uint64: 42,
},
v: core.Uint32(42),
want: "42",
},
{
name: `test Key.Emit() can emit a string representing self.UINT64`,
v: core.Value{
Type: core.UINT64,
Uint64: 42,
},
v: core.Uint64(42),
want: "42",
},
{
name: `test Key.Emit() can emit a string representing self.FLOAT32`,
v: core.Value{
Type: core.FLOAT32,
Float64: 42.1,
},
v: core.Float32(42.1),
want: "42.1",
},
{
name: `test Key.Emit() can emit a string representing self.FLOAT64`,
v: core.Value{
Type: core.FLOAT64,
Float64: 42.1,
},
v: core.Float64(42.1),
want: "42.1",
},
{
name: `test Key.Emit() can emit a string representing self.STRING`,
v: core.Value{
Type: core.STRING,
String: "foo",
},
v: core.String("foo"),
want: "foo",
},
} {

View File

@ -18,9 +18,7 @@ package core
import (
"fmt"
"math"
"sync/atomic"
"unsafe"
)
// NumberKind describes the data type of the Number.
@ -495,39 +493,3 @@ func (n Number) compareWithZero(kind NumberKind) int {
return 0
}
}
func rawToFloat64(r uint64) float64 {
return math.Float64frombits(r)
}
func float64ToRaw(f float64) uint64 {
return math.Float64bits(f)
}
func rawToInt64(r uint64) int64 {
return int64(r)
}
func int64ToRaw(i int64) uint64 {
return uint64(i)
}
func rawToUint64(r uint64) uint64 {
return r
}
func uint64ToRaw(u uint64) uint64 {
return u
}
func rawPtrToFloat64Ptr(r *uint64) *float64 {
return (*float64)(unsafe.Pointer(r))
}
func rawPtrToInt64Ptr(r *uint64) *int64 {
return (*int64)(unsafe.Pointer(r))
}
func rawPtrToUint64Ptr(r *uint64) *uint64 {
return r
}

77
api/core/rawhelpers.go Normal file
View File

@ -0,0 +1,77 @@
package core
import (
"math"
"unsafe"
)
func boolToRaw(b bool) uint64 {
if b {
return 1
}
return 0
}
func rawToBool(r uint64) bool {
return r != 0
}
func int64ToRaw(i int64) uint64 {
return uint64(i)
}
func rawToInt64(r uint64) int64 {
return int64(r)
}
func uint64ToRaw(u uint64) uint64 {
return u
}
func rawToUint64(r uint64) uint64 {
return r
}
func float64ToRaw(f float64) uint64 {
return math.Float64bits(f)
}
func rawToFloat64(r uint64) float64 {
return math.Float64frombits(r)
}
func int32ToRaw(i int32) uint64 {
return uint64(i)
}
func rawToInt32(r uint64) int32 {
return int32(r)
}
func uint32ToRaw(u uint32) uint64 {
return uint64(u)
}
func rawToUint32(r uint64) uint32 {
return uint32(r)
}
func float32ToRaw(f float32) uint64 {
return uint32ToRaw(math.Float32bits(f))
}
func rawToFloat32(r uint64) float32 {
return math.Float32frombits(rawToUint32(r))
}
func rawPtrToFloat64Ptr(r *uint64) *float64 {
return (*float64)(unsafe.Pointer(r))
}
func rawPtrToInt64Ptr(r *uint64) *int64 {
return (*int64)(unsafe.Pointer(r))
}
func rawPtrToUint64Ptr(r *uint64) *uint64 {
return r
}

View File

@ -0,0 +1,31 @@
// Code generated by "stringer -type=ValueType"; DO NOT EDIT.
package core
import "strconv"
func _() {
// An "invalid array index" compiler error signifies that the constant values have changed.
// Re-run the stringer command to generate them again.
var x [1]struct{}
_ = x[INVALID-0]
_ = x[BOOL-1]
_ = x[INT32-2]
_ = x[INT64-3]
_ = x[UINT32-4]
_ = x[UINT64-5]
_ = x[FLOAT32-6]
_ = x[FLOAT64-7]
_ = x[STRING-8]
}
const _ValueType_name = "INVALIDBOOLINT32INT64UINT32UINT64FLOAT32FLOAT64STRING"
var _ValueType_index = [...]uint8{0, 7, 11, 16, 21, 27, 33, 40, 47, 53}
func (i ValueType) String() string {
if i < 0 || i >= ValueType(len(_ValueType_index)-1) {
return "ValueType(" + strconv.FormatInt(int64(i), 10) + ")"
}
return _ValueType_name[_ValueType_index[i]:_ValueType_index[i+1]]
}

View File

@ -70,9 +70,6 @@ func (m Map) Apply(update MapUpdate) Map {
func (m Map) Value(k core.Key) (core.Value, bool) {
entry, ok := m.m[k]
if !ok {
entry.value.Type = core.INVALID
}
return entry.value, ok
}

View File

@ -2,7 +2,6 @@ package key_test
import (
"testing"
"unsafe"
"github.com/google/go-cmp/cmp"
@ -11,7 +10,6 @@ import (
)
func TestKeyValueConstructors(t *testing.T) {
tt := []struct {
name string
actual core.KeyValue
@ -21,124 +19,81 @@ func TestKeyValueConstructors(t *testing.T) {
name: "Bool",
actual: key.Bool("k1", true),
expected: core.KeyValue{
Key: "k1",
Value: core.Value{
Type: core.BOOL,
Bool: true,
},
Key: "k1",
Value: core.Bool(true),
},
},
{
name: "Int64",
actual: key.Int64("k1", 123),
expected: core.KeyValue{
Key: "k1",
Value: core.Value{
Type: core.INT64,
Int64: 123,
},
Key: "k1",
Value: core.Int64(123),
},
},
{
name: "Uint64",
actual: key.Uint64("k1", 1),
expected: core.KeyValue{
Key: "k1",
Value: core.Value{
Type: core.UINT64,
Uint64: 1,
},
Key: "k1",
Value: core.Uint64(1),
},
},
{
name: "Float64",
actual: key.Float64("k1", 123.5),
expected: core.KeyValue{
Key: "k1",
Value: core.Value{
Type: core.FLOAT64,
Float64: 123.5,
},
Key: "k1",
Value: core.Float64(123.5),
},
},
{
name: "Int32",
actual: key.Int32("k1", 123),
expected: core.KeyValue{
Key: "k1",
Value: core.Value{
Type: core.INT32,
Int64: 123,
},
Key: "k1",
Value: core.Int32(123),
},
},
{
name: "Uint32",
actual: key.Uint32("k1", 123),
expected: core.KeyValue{
Key: "k1",
Value: core.Value{
Type: core.UINT32,
Uint64: 123,
},
Key: "k1",
Value: core.Uint32(123),
},
},
{
name: "Float32",
actual: key.Float32("k1", 123.5),
expected: core.KeyValue{
Key: "k1",
Value: core.Value{
Type: core.FLOAT32,
Float64: 123.5,
},
Key: "k1",
Value: core.Float32(123.5),
},
},
{
name: "Int",
actual: key.Int("k1", 123),
expected: core.KeyValue{
Key: "k1",
Value: core.Value{
Int64: 123,
Type: IntType(123),
},
Key: "k1",
Value: core.Int(123),
},
},
{
name: "Uint",
actual: key.Uint("k1", 123),
expected: core.KeyValue{
Key: "k1",
Value: core.Value{
Uint64: 123,
Type: UintType(123),
},
Key: "k1",
Value: core.Uint(123),
},
},
}
for _, test := range tt {
t.Run(test.name, func(t *testing.T) {
if diff := cmp.Diff(test.actual, test.expected); diff != "" {
if diff := cmp.Diff(test.actual, test.expected, cmp.AllowUnexported(core.Value{})); 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
}

View File

@ -223,29 +223,47 @@ func spanDataToThrift(data *export.SpanData) *gen.Span {
func keyValueToTag(kv core.KeyValue) *gen.Tag {
var tag *gen.Tag
switch kv.Value.Type {
switch kv.Value.Type() {
case core.STRING:
s := kv.Value.AsString()
tag = &gen.Tag{
Key: string(kv.Key),
VStr: &kv.Value.String,
VStr: &s,
VType: gen.TagType_STRING,
}
case core.BOOL:
b := kv.Value.AsBool()
tag = &gen.Tag{
Key: string(kv.Key),
VBool: &kv.Value.Bool,
VBool: &b,
VType: gen.TagType_BOOL,
}
case core.INT32, core.INT64:
case core.INT32:
i := int64(kv.Value.AsInt32())
tag = &gen.Tag{
Key: string(kv.Key),
VLong: &kv.Value.Int64,
VLong: &i,
VType: gen.TagType_LONG,
}
case core.FLOAT32, core.FLOAT64:
case core.INT64:
i := kv.Value.AsInt64()
tag = &gen.Tag{
Key: string(kv.Key),
VLong: &i,
VType: gen.TagType_LONG,
}
case core.FLOAT32:
f := float64(kv.Value.AsFloat32())
tag = &gen.Tag{
Key: string(kv.Key),
VDouble: &kv.Value.Float64,
VDouble: &f,
VType: gen.TagType_DOUBLE,
}
case core.FLOAT64:
f := kv.Value.AsFloat64()
tag = &gen.Tag{
Key: string(kv.Key),
VDouble: &f,
VType: gen.TagType_DOUBLE,
}
}

View File

@ -194,25 +194,25 @@ func copyAttributes(out **tracepb.Span_Attributes, in []core.KeyValue) {
func attributeValue(kv core.KeyValue) *tracepb.AttributeValue {
value := kv.Value
switch value.Type {
switch value.Type() {
case core.BOOL:
return &tracepb.AttributeValue{
Value: &tracepb.AttributeValue_BoolValue{BoolValue: value.Bool},
Value: &tracepb.AttributeValue_BoolValue{BoolValue: value.AsBool()},
}
case core.INT64:
return &tracepb.AttributeValue{
Value: &tracepb.AttributeValue_IntValue{IntValue: value.Int64},
Value: &tracepb.AttributeValue_IntValue{IntValue: value.AsInt64()},
}
case core.FLOAT64:
// TODO: set double value if Stackdriver Trace support it in the future.
return &tracepb.AttributeValue{
Value: &tracepb.AttributeValue_StringValue{
StringValue: trunc(strconv.FormatFloat(value.Float64, 'f', -1, 64),
StringValue: trunc(strconv.FormatFloat(value.AsFloat64(), 'f', -1, 64),
maxAttributeStringValue)},
}
case core.STRING:
return &tracepb.AttributeValue{
Value: &tracepb.AttributeValue_StringValue{StringValue: trunc(value.String, maxAttributeStringValue)},
Value: &tracepb.AttributeValue_StringValue{StringValue: trunc(value.AsString(), maxAttributeStringValue)},
}
}
return nil

View File

@ -20,7 +20,12 @@ import (
"io"
"os"
"sync"
"time"
"google.golang.org/grpc/codes"
"go.opentelemetry.io/api/core"
apitrace "go.opentelemetry.io/api/trace"
"go.opentelemetry.io/sdk/export"
"go.opentelemetry.io/sdk/trace"
)
@ -54,15 +59,72 @@ func (e *Exporter) RegisterSimpleSpanProcessor() {
})
}
type jsonValue struct {
Type string
Value interface{}
}
type jsonKeyValue struct {
Key core.Key
Value jsonValue
}
type jsonSpanData struct {
SpanContext core.SpanContext
ParentSpanID core.SpanID
SpanKind apitrace.SpanKind
Name string
StartTime time.Time
EndTime time.Time
Attributes []jsonKeyValue
MessageEvents []export.Event
Links []apitrace.Link
Status codes.Code
HasRemoteParent bool
DroppedAttributeCount int
DroppedMessageEventCount int
DroppedLinkCount int
ChildSpanCount int
}
func marshalSpanData(data *export.SpanData, pretty bool) ([]byte, error) {
jsd := jsonSpanData{
SpanContext: data.SpanContext,
ParentSpanID: data.ParentSpanID,
SpanKind: data.SpanKind,
Name: data.Name,
StartTime: data.StartTime,
EndTime: data.EndTime,
Attributes: toJSONAttributes(data.Attributes),
MessageEvents: data.MessageEvents,
Links: data.Links,
Status: data.Status,
HasRemoteParent: data.HasRemoteParent,
DroppedAttributeCount: data.DroppedAttributeCount,
DroppedMessageEventCount: data.DroppedMessageEventCount,
DroppedLinkCount: data.DroppedLinkCount,
ChildSpanCount: data.ChildSpanCount,
}
if pretty {
return json.MarshalIndent(jsd, "", "\t")
}
return json.Marshal(jsd)
}
func toJSONAttributes(attributes []core.KeyValue) []jsonKeyValue {
jsonAttrs := make([]jsonKeyValue, len(attributes))
for i := 0; i < len(attributes); i++ {
jsonAttrs[i].Key = attributes[i].Key
jsonAttrs[i].Value.Type = attributes[i].Value.Type().String()
jsonAttrs[i].Value.Value = attributes[i].Value.AsInterface()
}
return jsonAttrs
}
// ExportSpan writes a SpanData in json format to stdout.
func (e *Exporter) ExportSpan(ctx context.Context, data *export.SpanData) {
var jsonSpan []byte
var err error
if e.pretty {
jsonSpan, err = json.MarshalIndent(data, "", "\t")
} else {
jsonSpan, err = json.Marshal(data)
}
jsonSpan, err := marshalSpanData(data, e.pretty)
if err != nil {
// ignore writer failures for now
_, _ = e.outputWriter.Write([]byte("Error converting spanData to json: " + err.Error()))

View File

@ -24,6 +24,7 @@ import (
"google.golang.org/grpc/codes"
"go.opentelemetry.io/api/core"
"go.opentelemetry.io/api/key"
"go.opentelemetry.io/api/trace"
"go.opentelemetry.io/sdk/export"
)
@ -54,14 +55,8 @@ func TestExporter_ExportSpan(t *testing.T) {
StartTime: now,
EndTime: now,
Attributes: []core.KeyValue{
{
Key: core.Key("key"),
Value: core.Value{Type: core.STRING, String: keyValue},
},
{
Key: core.Key("double"),
Value: core.Value{Type: core.FLOAT64, Float64: doubleValue},
},
key.String("key", keyValue),
key.Float64("double", doubleValue),
},
SpanKind: trace.SpanKindInternal,
Status: codes.Unknown,
@ -82,11 +77,11 @@ func TestExporter_ExportSpan(t *testing.T) {
`"Attributes":[` +
`{` +
`"Key":"key",` +
`"Value":{"Type":8,"Bool":false,"Int64":0,"Uint64":0,"Float64":0,"String":"value"}` +
`"Value":{"Type":"STRING","Value":"value"}` +
`},` +
`{` +
`"Key":"double",` +
`"Value":{"Type":7,"Bool":false,"Int64":0,"Uint64":0,"Float64":123.456,"String":""}` +
`"Value":{"Type":"FLOAT64","Value":123.456}` +
`}` +
`],` +
`"MessageEvents":null,` +

View File

@ -361,7 +361,7 @@ func TestExtractValidDistributedContextFromHTTPReq(t *testing.T) {
totalDiff := ""
wantCorCtx.Foreach(func(kv core.KeyValue) bool {
val, _ := gotCorCtx.Value(kv.Key)
diff := cmp.Diff(kv, core.KeyValue{Key: kv.Key, Value: val})
diff := cmp.Diff(kv, core.KeyValue{Key: kv.Key, Value: val}, cmp.AllowUnexported(core.Value{}))
if diff != "" {
totalDiff += diff + "\n"
}
@ -446,7 +446,7 @@ func TestInjectCorrelationContextToHTTPReq(t *testing.T) {
"key6=123",
"key7=123",
"key8=123.567",
"key9=123.56700134277344",
"key9=123.567",
},
},
}

View File

@ -304,14 +304,13 @@ func TestSetSpanAttributes(t *testing.T) {
},
ParentSpanID: sid,
Name: "SpanAttribute/span0",
Attributes: []core.KeyValue{{
Key: core.Key("key1"),
Value: core.Value{Type: core.STRING, String: "value1"},
}},
Attributes: []core.KeyValue{
key.String("key1", "value1"),
},
SpanKind: "internal",
HasRemoteParent: true,
}
if diff := cmp.Diff(got, want); diff != "" {
if diff := cmpDiff(got, want); diff != "" {
t.Errorf("SetSpanAttributes: -got +want %s", diff)
}
}
@ -339,20 +338,14 @@ func TestSetSpanAttributesOverLimit(t *testing.T) {
ParentSpanID: sid,
Name: "SpanAttributesOverLimit/span0",
Attributes: []core.KeyValue{
{
Key: core.Key("key1"),
Value: core.Value{Type: core.STRING, String: "value3"},
},
{
Key: core.Key("key4"),
Value: core.Value{Type: core.STRING, String: "value4"},
},
key.String("key1", "value3"),
key.String("key4", "value4"),
},
SpanKind: "internal",
HasRemoteParent: true,
DroppedAttributeCount: 1,
}
if diff := cmp.Diff(got, want); diff != "" {
if diff := cmpDiff(got, want); diff != "" {
t.Errorf("SetSpanAttributesOverLimit: -got +want %s", diff)
}
}
@ -396,7 +389,7 @@ func TestEvents(t *testing.T) {
},
SpanKind: "internal",
}
if diff := cmp.Diff(got, want, cmp.AllowUnexported(export.Event{})); diff != "" {
if diff := cmpDiff(got, want); diff != "" {
t.Errorf("Message Events: -got +want %s", diff)
}
}
@ -447,7 +440,7 @@ func TestEventsOverLimit(t *testing.T) {
HasRemoteParent: true,
SpanKind: "internal",
}
if diff := cmp.Diff(got, want, cmp.AllowUnexported(export.Event{})); diff != "" {
if diff := cmpDiff(got, want); diff != "" {
t.Errorf("Message Event over limit: -got +want %s", diff)
}
}
@ -487,7 +480,7 @@ func TestAddLinks(t *testing.T) {
},
SpanKind: "internal",
}
if diff := cmp.Diff(got, want, cmp.AllowUnexported(export.Event{})); diff != "" {
if diff := cmpDiff(got, want); diff != "" {
t.Errorf("AddLink: -got +want %s", diff)
}
}
@ -528,7 +521,7 @@ func TestLinks(t *testing.T) {
},
SpanKind: "internal",
}
if diff := cmp.Diff(got, want, cmp.AllowUnexported(export.Event{})); diff != "" {
if diff := cmpDiff(got, want); diff != "" {
t.Errorf("Link: -got +want %s", diff)
}
}
@ -571,7 +564,7 @@ func TestLinksOverLimit(t *testing.T) {
HasRemoteParent: true,
SpanKind: "internal",
}
if diff := cmp.Diff(got, want, cmp.AllowUnexported(export.Event{})); diff != "" {
if diff := cmpDiff(got, want); diff != "" {
t.Errorf("Link over limit: -got +want %s", diff)
}
}
@ -620,11 +613,15 @@ func TestSetSpanStatus(t *testing.T) {
Status: codes.Canceled,
HasRemoteParent: true,
}
if diff := cmp.Diff(got, want); diff != "" {
if diff := cmpDiff(got, want); diff != "" {
t.Errorf("SetSpanStatus: -got +want %s", diff)
}
}
func cmpDiff(x, y interface{}) string {
return cmp.Diff(x, y, cmp.AllowUnexported(core.Value{}), cmp.AllowUnexported(export.Event{}))
}
func remoteSpanContext() core.SpanContext {
return core.SpanContext{
TraceID: tid,