Skip to content

Commit

Permalink
Handle bytes
Browse files Browse the repository at this point in the history
  • Loading branch information
pellared committed Jan 19, 2024
1 parent 0380851 commit 9c272a9
Show file tree
Hide file tree
Showing 3 changed files with 62 additions and 19 deletions.
2 changes: 2 additions & 0 deletions log/internal/writer_logger.go
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,8 @@ func (l *writerLogger) appendValue(v log.Value) {
l.write(strconv.FormatFloat(v.Float64(), 'g', -1, 64)) // strconv.FormatFloat allocates memory.
case log.KindBool:
l.write(strconv.FormatBool(v.Bool()))
case log.KindBytes:
l.write(fmt.Sprint(v.Bytes()))
case log.KindGroup:
l.write(fmt.Sprint(v.Group()))
case log.KindEmpty:
Expand Down
46 changes: 40 additions & 6 deletions log/value.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
package log // import "go.opentelemetry.io/otel/log"

import (
"bytes"
"fmt"
"math"
"strconv"
Expand All @@ -19,17 +20,16 @@ import (
type Value struct {
_ [0]func() // disallow ==
// num holds the value for Kinds Int64, Uint64, Float64, and Bool,
// the string length for KindString.
// the length for Kinds String, Bytes.
num uint64
// If any is of type Kind, then the value is in num as described above.
// If any is of type stringptr, then the Kind is String and the string value
// consists of the length in num and the pointer in any.
// (This implies that key-value pairs cannot store values of type stringptr.)
// Otherwise (if is of type stringptr, bytesptr or groupptr) then it contains the value.
any any
}

type (
stringptr *byte // used in Value.any when the Value is a string
bytesptr *byte // used in Value.any when the Value is a []byte
groupptr *KeyValue // used in Value.any when the Value is a []Attr
)

Expand All @@ -44,6 +44,7 @@ const (
KindInt64
KindString
KindUint64
KindBytes
KindGroup
)

Expand All @@ -54,6 +55,7 @@ var kindStrings = []string{
"Int64",
"String",
"Uint64",
"Bytes",
"Group",
}

Expand All @@ -73,6 +75,8 @@ func (v Value) Kind() Kind {
return x
case stringptr:
return KindString
case bytesptr:
return KindBytes
case groupptr:
return KindGroup
default:
Expand Down Expand Up @@ -114,6 +118,12 @@ func BoolValue(v bool) Value { //nolint:revive // We are passing bool as this is
return Value{num: u, any: KindBool}
}

// BytesValue returns a [Value] for bytes.
// The caller must not subsequently mutate the argument slice.
func BytesValue(v []byte) Value {
return Value{num: uint64(len(v)), any: bytesptr(unsafe.SliceData(v))}
}

// GroupValue returns a new [Value] for a list of key-value pairs.
// The caller must not subsequently mutate the argument slice.
func GroupValue(as ...KeyValue) Value {
Expand Down Expand Up @@ -158,6 +168,8 @@ func (v Value) Any() any {
return v.str()
case KindBool:
return v.bool()
case KindBytes:
return v.bytes()
case KindEmpty:
return nil
default:
Expand Down Expand Up @@ -225,7 +237,20 @@ func (v Value) float() float64 {
return math.Float64frombits(v.num)
}

// Group returns v's value as a []Attr.
// Group returns v's value as a []byte.
// It panics if v's [Kind] is not [KindBytes].
func (v Value) Bytes() []byte {
if sp, ok := v.any.(bytesptr); ok {
return unsafe.Slice((*byte)(sp), v.num)
}
panic("Bytes: bad kind")
}

func (v Value) bytes() []byte {
return unsafe.Slice((*byte)(v.any.(bytesptr)), v.num)
}

// Group returns v's value as a []KeyValue.
// It panics if v's [Kind] is not [KindGroup].
func (v Value) Group() []KeyValue {
if sp, ok := v.any.(groupptr); ok {
Expand Down Expand Up @@ -259,8 +284,10 @@ func (v Value) Equal(w Value) bool {
return v.float() == w.float()
case KindGroup:
return sliceEqualFunc(v.group(), w.group(), KeyValue.Equal)
case KindBytes:
return bytes.Equal(v.bytes(), w.bytes())
case KindEmpty:
return k1 == k2
return true
default:
panic(fmt.Sprintf("bad kind: %s", k1))
}
Expand Down Expand Up @@ -291,6 +318,8 @@ func (v Value) append(dst []byte) []byte {
return strconv.AppendFloat(dst, v.float(), 'g', -1, 64)
case KindBool:
return strconv.AppendBool(dst, v.bool())
case KindBytes:
return fmt.Append(dst, v.bytes())
case KindGroup:
return fmt.Append(dst, v.group())
case KindEmpty:
Expand Down Expand Up @@ -337,6 +366,11 @@ func Bool(key string, v bool) KeyValue {
return KeyValue{key, BoolValue(v)}
}

// Bytes returns an KeyValue for a bytes.
func Bytes(key string, v []byte) KeyValue {
return KeyValue{key, BytesValue(v)}
}

// Group returns an KeyValue for a Group [Value].
//
// Use Group to collect several key-value pairs under a single
Expand Down
33 changes: 20 additions & 13 deletions log/value_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ package log
import (
"fmt"
"testing"
"time"
"unsafe"

"github.com/stretchr/testify/assert"
Expand All @@ -26,7 +25,9 @@ func TestValueEqual(t *testing.T) {
Float64Value(3.7),
BoolValue(true),
BoolValue(false),
GroupValue(Bool("b", true), Int("i", 3)),
StringValue("hi"),
BytesValue([]byte{1, 3, 5}),
GroupValue(Bool("b", true), Int("i", 3), Bytes("b", []byte{3, 5, 7})),
}
for i, v1 := range vals {
for j, v2 := range vals {
Expand All @@ -49,7 +50,9 @@ func TestValueString(t *testing.T) {
{Float64Value(.15), "0.15"},
{BoolValue(true), "true"},
{StringValue("foo"), "foo"},
{BytesValue([]byte{2, 4, 6}), "[2 4 6]"},
{GroupValue(Int("a", 1), Bool("b", true)), "[a=1 b=true]"},
{Value{}, "<nil>"},
} {
got := test.v.String()
assert.Equal(t, test.want, got)
Expand All @@ -63,51 +66,53 @@ func TestValueNoAlloc(t *testing.T) {
u uint64
f float64
b bool
by []byte
s string
x any
tm time.Time
)
bytes := []byte{1, 3, 4}
a := int(testing.AllocsPerRun(5, func() {
i = Int64Value(1).Int64()
u = Uint64Value(1).Uint64()
f = Float64Value(1).Float64()
b = BoolValue(true).Bool()
by = BytesValue(bytes).Bytes()
s = StringValue("foo").String()
}))
assert.Zero(t, a)
_ = i
_ = u
_ = f
_ = b
_ = by
_ = s
_ = x
_ = tm
}

func TestKeyValueNoAlloc(t *testing.T) {
// Assign values just to make sure the compiler doesn't optimize away the statements.
var (
i int64
u uint64
f float64
b bool
s string
x any
i int64
u uint64
f float64
b bool
by []byte
s string
)
bytes := []byte{1, 3, 4}
a := int(testing.AllocsPerRun(5, func() {
i = Int64("key", 1).Value.Int64()
u = Uint64("key", 1).Value.Uint64()
f = Float64("key", 1).Value.Float64()
b = Bool("key", true).Value.Bool()
by = Bytes("key", bytes).Value.Bytes()
s = String("key", "foo").Value.String()
}))
assert.Zero(t, a)
_ = i
_ = u
_ = f
_ = b
_ = by
_ = s
_ = x
}

func TestValueAny(t *testing.T) {
Expand All @@ -121,7 +126,9 @@ func TestValueAny(t *testing.T) {
{uint64(2), Uint64Value(2)},
{int64(11), Int64Value(11)},
{1.5, Float64Value(1.5)},
{[]byte{1, 2, 3}, BytesValue([]byte{1, 2, 3})},
{[]KeyValue{Int("i", 3)}, GroupValue(Int("i", 3))},
{nil, Value{}},
} {
got := test.in.Any()
assert.Equal(t, test.want, got)
Expand Down

0 comments on commit 9c272a9

Please sign in to comment.