Skip to content

Commit 021ccc1

Browse files
authored
Add attributes API (#1007)
1 parent c7d162c commit 021ccc1

File tree

5 files changed

+507
-0
lines changed

5 files changed

+507
-0
lines changed

attribute/builder_test.go

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
package attribute
2+
3+
import (
4+
"testing"
5+
)
6+
7+
func TestBuilder(t *testing.T) {
8+
k := "key"
9+
for _, testcase := range []struct {
10+
name string
11+
value Builder
12+
wantType Type
13+
wantValue interface{}
14+
}{
15+
{
16+
name: "Bool correctly returns a boolean builder",
17+
value: Bool(k, true),
18+
wantType: BOOL,
19+
wantValue: true,
20+
},
21+
{
22+
name: "Int64() correctly returns an builder",
23+
value: Int64(k, 42),
24+
wantType: INT64,
25+
wantValue: int64(42),
26+
},
27+
{
28+
name: "Int() correctly returns an builder",
29+
value: Int(k, 42),
30+
wantType: INT64,
31+
wantValue: int64(42),
32+
},
33+
{
34+
name: "Float64() correctly returns a float64 builder",
35+
value: Float64(k, 42.1),
36+
wantType: FLOAT64,
37+
wantValue: 42.1,
38+
},
39+
{
40+
name: "String() correctly returns a string builder",
41+
value: String(k, "foo"),
42+
wantType: STRING,
43+
wantValue: "foo",
44+
},
45+
} {
46+
if testcase.value.Value.Type() != testcase.wantType {
47+
t.Errorf("wrong value type, got %#v, expected %#v", testcase.value.Value.Type(), testcase.wantType)
48+
}
49+
}
50+
}
51+
52+
func TestBuilder_Valid(t *testing.T) {
53+
for _, tt := range []struct {
54+
name string
55+
builder Builder
56+
wantValid bool
57+
}{
58+
{
59+
name: "valid key and value are valid",
60+
builder: Builder{
61+
Key: "key",
62+
Value: BoolValue(true),
63+
},
64+
wantValid: true,
65+
},
66+
{
67+
name: "empty key should be invalid",
68+
builder: Builder{
69+
Key: "",
70+
Value: IntValue(42),
71+
},
72+
wantValid: false,
73+
},
74+
{
75+
name: "invalid value should be invalid",
76+
builder: Builder{
77+
Key: "key",
78+
Value: Value{},
79+
},
80+
wantValid: false,
81+
},
82+
} {
83+
valid := tt.builder.Valid()
84+
if tt.wantValid != valid {
85+
t.Errorf("expected builder valid to be %v, got %v", tt.wantValid, valid)
86+
}
87+
}
88+
}

attribute/bulider.go

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
package attribute
2+
3+
type Builder struct {
4+
Key string
5+
Value Value
6+
}
7+
8+
// String returns a Builder for a string value.
9+
func String(key, value string) Builder {
10+
return Builder{key, StringValue(value)}
11+
}
12+
13+
// Int64 returns a Builder for an int64.
14+
func Int64(key string, value int64) Builder {
15+
return Builder{key, Int64Value(value)}
16+
}
17+
18+
// Int returns a Builder for an int64.
19+
func Int(key string, value int) Builder {
20+
return Builder{key, IntValue(value)}
21+
}
22+
23+
// Float64 returns a Builder for a float64.
24+
func Float64(key string, v float64) Builder {
25+
return Builder{key, Float64Value(v)}
26+
}
27+
28+
// Bool returns a Builder for a boolean.
29+
func Bool(key string, v bool) Builder {
30+
return Builder{key, BoolValue(v)}
31+
}
32+
33+
// Valid checks for valid key and type.
34+
func (b *Builder) Valid() bool {
35+
return len(b.Key) > 0 && b.Value.Type() != INVALID
36+
}

attribute/rawhelpers.go

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
// Copied from https://github.com/open-telemetry/opentelemetry-go/blob/cc43e01c27892252aac9a8f20da28cdde957a289/attribute/rawhelpers.go
2+
// Copyright The OpenTelemetry Authors
3+
//
4+
// Licensed under the Apache License, Version 2.0 (the "License");
5+
// you may not use this file except in compliance with the License.
6+
// You may obtain a copy of the License at
7+
//
8+
// http://www.apache.org/licenses/LICENSE-2.0
9+
//
10+
// Unless required by applicable law or agreed to in writing, software
11+
// distributed under the License is distributed on an "AS IS" BASIS,
12+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
// See the License for the specific language governing permissions and
14+
// limitations under the License.
15+
16+
package attribute
17+
18+
import (
19+
"math"
20+
)
21+
22+
func boolToRaw(b bool) uint64 { // b is not a control flag.
23+
if b {
24+
return 1
25+
}
26+
return 0
27+
}
28+
29+
func rawToBool(r uint64) bool {
30+
return r != 0
31+
}
32+
33+
func int64ToRaw(i int64) uint64 {
34+
// Assumes original was a valid int64 (overflow not checked).
35+
return uint64(i) // nolint: gosec
36+
}
37+
38+
func rawToInt64(r uint64) int64 {
39+
// Assumes original was a valid int64 (overflow not checked).
40+
return int64(r) // nolint: gosec
41+
}
42+
43+
func float64ToRaw(f float64) uint64 {
44+
return math.Float64bits(f)
45+
}
46+
47+
func rawToFloat64(r uint64) float64 {
48+
return math.Float64frombits(r)
49+
}

attribute/value.go

Lines changed: 170 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,170 @@
1+
// Adapted from https://github.com/open-telemetry/opentelemetry-go/blob/cc43e01c27892252aac9a8f20da28cdde957a289/attribute/value.go
2+
//
3+
// Copyright The OpenTelemetry Authors
4+
//
5+
// Licensed under the Apache License, Version 2.0 (the "License");
6+
// you may not use this file except in compliance with the License.
7+
// You may obtain a copy of the License at
8+
//
9+
// http://www.apache.org/licenses/LICENSE-2.0
10+
//
11+
// Unless required by applicable law or agreed to in writing, software
12+
// distributed under the License is distributed on an "AS IS" BASIS,
13+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
// See the License for the specific language governing permissions and
15+
// limitations under the License.
16+
17+
package attribute
18+
19+
import (
20+
"encoding/json"
21+
"fmt"
22+
"strconv"
23+
)
24+
25+
// Type describes the type of the data Value holds.
26+
type Type int // redefines builtin Type.
27+
28+
// Value represents the value part in key-value pairs.
29+
type Value struct {
30+
vtype Type
31+
numeric uint64
32+
stringly string
33+
}
34+
35+
const (
36+
// INVALID is used for a Value with no value set.
37+
INVALID Type = iota
38+
// BOOL is a boolean Type Value.
39+
BOOL
40+
// INT64 is a 64-bit signed integral Type Value.
41+
INT64
42+
// FLOAT64 is a 64-bit floating point Type Value.
43+
FLOAT64
44+
// STRING is a string Type Value.
45+
STRING
46+
)
47+
48+
// BoolValue creates a BOOL Value.
49+
func BoolValue(v bool) Value {
50+
return Value{
51+
vtype: BOOL,
52+
numeric: boolToRaw(v),
53+
}
54+
}
55+
56+
// IntValue creates an INT64 Value.
57+
func IntValue(v int) Value {
58+
return Int64Value(int64(v))
59+
}
60+
61+
// Int64Value creates an INT64 Value.
62+
func Int64Value(v int64) Value {
63+
return Value{
64+
vtype: INT64,
65+
numeric: int64ToRaw(v),
66+
}
67+
}
68+
69+
// Float64Value creates a FLOAT64 Value.
70+
func Float64Value(v float64) Value {
71+
return Value{
72+
vtype: FLOAT64,
73+
numeric: float64ToRaw(v),
74+
}
75+
}
76+
77+
// StringValue creates a STRING Value.
78+
func StringValue(v string) Value {
79+
return Value{
80+
vtype: STRING,
81+
stringly: v,
82+
}
83+
}
84+
85+
// Type returns a type of the Value.
86+
func (v Value) Type() Type {
87+
return v.vtype
88+
}
89+
90+
// AsBool returns the bool value. Make sure that the Value's type is
91+
// BOOL.
92+
func (v Value) AsBool() bool {
93+
return rawToBool(v.numeric)
94+
}
95+
96+
// AsInt64 returns the int64 value. Make sure that the Value's type is
97+
// INT64.
98+
func (v Value) AsInt64() int64 {
99+
return rawToInt64(v.numeric)
100+
}
101+
102+
// AsFloat64 returns the float64 value. Make sure that the Value's
103+
// type is FLOAT64.
104+
func (v Value) AsFloat64() float64 {
105+
return rawToFloat64(v.numeric)
106+
}
107+
108+
// AsString returns the string value. Make sure that the Value's type
109+
// is STRING.
110+
func (v Value) AsString() string {
111+
return v.stringly
112+
}
113+
114+
type unknownValueType struct{}
115+
116+
// AsInterface returns Value's data as interface{}.
117+
func (v Value) AsInterface() interface{} {
118+
switch v.Type() {
119+
case BOOL:
120+
return v.AsBool()
121+
case INT64:
122+
return v.AsInt64()
123+
case FLOAT64:
124+
return v.AsFloat64()
125+
case STRING:
126+
return v.stringly
127+
}
128+
return unknownValueType{}
129+
}
130+
131+
// String returns a string representation of Value's data.
132+
func (v Value) String() string {
133+
switch v.Type() {
134+
case BOOL:
135+
return strconv.FormatBool(v.AsBool())
136+
case INT64:
137+
return strconv.FormatInt(v.AsInt64(), 10)
138+
case FLOAT64:
139+
return fmt.Sprint(v.AsFloat64())
140+
case STRING:
141+
return v.stringly
142+
default:
143+
return "unknown"
144+
}
145+
}
146+
147+
// MarshalJSON returns the JSON encoding of the Value.
148+
func (v Value) MarshalJSON() ([]byte, error) {
149+
var jsonVal struct {
150+
Type string
151+
Value interface{}
152+
}
153+
jsonVal.Type = v.Type().String()
154+
jsonVal.Value = v.AsInterface()
155+
return json.Marshal(jsonVal)
156+
}
157+
158+
func (t Type) String() string {
159+
switch t {
160+
case BOOL:
161+
return "bool"
162+
case INT64:
163+
return "int64"
164+
case FLOAT64:
165+
return "float64"
166+
case STRING:
167+
return "string"
168+
}
169+
return "invalid"
170+
}

0 commit comments

Comments
 (0)