mirror of
https://github.com/open-telemetry/opentelemetry-go.git
synced 2025-01-18 03:22:12 +02:00
log: Implement Value and KeyValue types (#4949)
* Implement `Value` and `KeyValue` * Add tests for `Value` and `KeyValue` --------- Co-authored-by: Robert Pająk <pellared@hotmail.com>
This commit is contained in:
parent
f793a0575d
commit
7b3382e4dc
@ -3,6 +3,8 @@ module go.opentelemetry.io/otel/log
|
||||
go 1.20
|
||||
|
||||
require (
|
||||
github.com/go-logr/logr v1.4.1
|
||||
github.com/go-logr/stdr v1.2.2
|
||||
github.com/stretchr/testify v1.8.4
|
||||
go.opentelemetry.io/otel v1.23.1
|
||||
)
|
||||
@ -10,6 +12,8 @@ require (
|
||||
require (
|
||||
github.com/davecgh/go-spew v1.1.1 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||
go.opentelemetry.io/otel/metric v1.23.1 // indirect
|
||||
go.opentelemetry.io/otel/trace v1.23.1 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
)
|
||||
|
||||
|
@ -1,5 +1,10 @@
|
||||
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
|
||||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
|
||||
github.com/go-logr/logr v1.4.1 h1:pKouT5E8xu9zeFC39JXRDukb6JFQPXM5p5I91188VAQ=
|
||||
github.com/go-logr/logr v1.4.1/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
|
||||
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
|
||||
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
|
||||
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
|
||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
|
||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
|
306
log/keyvalue.go
306
log/keyvalue.go
@ -16,6 +16,18 @@
|
||||
|
||||
package log // import "go.opentelemetry.io/otel/log"
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"math"
|
||||
"unsafe"
|
||||
|
||||
"go.opentelemetry.io/otel/internal/global"
|
||||
)
|
||||
|
||||
// errKind is logged when a Value is decoded to an incompatible type.
|
||||
var errKind = errors.New("invalid Kind")
|
||||
|
||||
// Kind is the kind of a [Value].
|
||||
type Kind int
|
||||
|
||||
@ -32,70 +44,286 @@ const (
|
||||
)
|
||||
|
||||
// A Value represents a structured log value.
|
||||
type Value struct{} // TODO (#4914): implement.
|
||||
type Value struct {
|
||||
// Ensure forward compatibility by explicitly making this not comparable.
|
||||
noCmp [0]func() //nolint: unused // This is indeed used.
|
||||
|
||||
// num holds the value for Int64, Float64, and Bool. It holds the length
|
||||
// for String, Bytes, Slice, Map.
|
||||
num uint64
|
||||
// any holds either the KindBool, KindInt64, KindFloat64, stringptr,
|
||||
// bytesptr, sliceptr, or mapptr. If KindBool, KindInt64, or KindFloat64
|
||||
// then the value of Value is in num as described above. Otherwise, it
|
||||
// contains the value wrapped in the appropriate type.
|
||||
any any
|
||||
}
|
||||
|
||||
type (
|
||||
// sliceptr represents a value in Value.any for KindString Values.
|
||||
stringptr *byte
|
||||
// bytesptr represents a value in Value.any for KindBytes Values.
|
||||
bytesptr *byte
|
||||
// sliceptr represents a value in Value.any for KindSlice Values.
|
||||
sliceptr *Value
|
||||
// mapptr represents a value in Value.any for KindMap Values.
|
||||
mapptr *KeyValue
|
||||
)
|
||||
|
||||
// StringValue returns a new [Value] for a string.
|
||||
func StringValue(v string) Value { return Value{} } // TODO (#4914): implement.
|
||||
func StringValue(v string) Value {
|
||||
return Value{
|
||||
num: uint64(len(v)),
|
||||
any: stringptr(unsafe.StringData(v)),
|
||||
}
|
||||
}
|
||||
|
||||
// IntValue returns a [Value] for an int.
|
||||
func IntValue(v int) Value { return Value{} } // TODO (#4914): implement.
|
||||
func IntValue(v int) Value { return Int64Value(int64(v)) }
|
||||
|
||||
// Int64Value returns a [Value] for an int64.
|
||||
func Int64Value(v int64) Value { return Value{} } // TODO (#4914): implement.
|
||||
func Int64Value(v int64) Value {
|
||||
return Value{num: uint64(v), any: KindInt64}
|
||||
}
|
||||
|
||||
// Float64Value returns a [Value] for a float64.
|
||||
func Float64Value(v float64) Value { return Value{} } // TODO (#4914): implement.
|
||||
func Float64Value(v float64) Value {
|
||||
return Value{num: math.Float64bits(v), any: KindFloat64}
|
||||
}
|
||||
|
||||
// BoolValue returns a [Value] for a bool.
|
||||
func BoolValue(v bool) Value { //nolint:revive // Not a control flag.
|
||||
// TODO (#4914): implement.
|
||||
return Value{}
|
||||
var n uint64
|
||||
if v {
|
||||
n = 1
|
||||
}
|
||||
return Value{num: n, any: KindBool}
|
||||
}
|
||||
|
||||
// BytesValue returns a [Value] for a byte slice. The passed slice must not be
|
||||
// changed after it is passed.
|
||||
func BytesValue(v []byte) Value { return Value{} } // TODO (#4914): implement.
|
||||
func BytesValue(v []byte) Value {
|
||||
return Value{
|
||||
num: uint64(len(v)),
|
||||
any: bytesptr(unsafe.SliceData(v)),
|
||||
}
|
||||
}
|
||||
|
||||
// SliceValue returns a [Value] for a slice of [Value]. The passed slice must
|
||||
// not be changed after it is passed.
|
||||
func SliceValue(vs ...Value) Value { return Value{} } // TODO (#4914): implement.
|
||||
func SliceValue(vs ...Value) Value {
|
||||
return Value{
|
||||
num: uint64(len(vs)),
|
||||
any: sliceptr(unsafe.SliceData(vs)),
|
||||
}
|
||||
}
|
||||
|
||||
// MapValue returns a new [Value] for a slice of key-value pairs. The passed
|
||||
// slice must not be changed after it is passed.
|
||||
func MapValue(kvs ...KeyValue) Value { return Value{} } // TODO (#4914): implement.
|
||||
func MapValue(kvs ...KeyValue) Value {
|
||||
return Value{
|
||||
num: uint64(len(kvs)),
|
||||
any: mapptr(unsafe.SliceData(kvs)),
|
||||
}
|
||||
}
|
||||
|
||||
// AsAny returns the value held by v as an any.
|
||||
func (v Value) AsAny() any { return nil } // TODO (#4914): implement
|
||||
func (v Value) AsAny() any {
|
||||
switch v.Kind() {
|
||||
case KindMap:
|
||||
return v.asMap()
|
||||
case KindSlice:
|
||||
return v.asSlice()
|
||||
case KindInt64:
|
||||
return v.asInt64()
|
||||
case KindFloat64:
|
||||
return v.asFloat64()
|
||||
case KindString:
|
||||
return v.asString()
|
||||
case KindBool:
|
||||
return v.asBool()
|
||||
case KindBytes:
|
||||
return v.asBytes()
|
||||
case KindEmpty:
|
||||
return nil
|
||||
default:
|
||||
global.Error(errKind, "AsAny", "Kind", v.Kind())
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// AsString returns the value held by v as a string.
|
||||
func (v Value) AsString() string { return "" } // TODO (#4914): implement
|
||||
func (v Value) AsString() string {
|
||||
if sp, ok := v.any.(stringptr); ok {
|
||||
return unsafe.String(sp, v.num)
|
||||
}
|
||||
global.Error(errKind, "AsString", "Kind", v.Kind())
|
||||
return ""
|
||||
}
|
||||
|
||||
// asString returns the value held by v as a string. It will panic if the Value
|
||||
// is not KindString.
|
||||
func (v Value) asString() string {
|
||||
return unsafe.String(v.any.(stringptr), v.num)
|
||||
}
|
||||
|
||||
// AsInt64 returns the value held by v as an int64.
|
||||
func (v Value) AsInt64() int64 { return 0 } // TODO (#4914): implement
|
||||
func (v Value) AsInt64() int64 {
|
||||
if v.Kind() != KindInt64 {
|
||||
global.Error(errKind, "AsInt64", "Kind", v.Kind())
|
||||
return 0
|
||||
}
|
||||
return v.asInt64()
|
||||
}
|
||||
|
||||
// asInt64 returns the value held by v as an int64. If v is not of KindInt64,
|
||||
// this will return garbage.
|
||||
func (v Value) asInt64() int64 { return int64(v.num) }
|
||||
|
||||
// AsBool returns the value held by v as a bool.
|
||||
func (v Value) AsBool() bool { return false } // TODO (#4914): implement
|
||||
func (v Value) AsBool() bool {
|
||||
if v.Kind() != KindBool {
|
||||
global.Error(errKind, "AsBool", "Kind", v.Kind())
|
||||
return false
|
||||
}
|
||||
return v.asBool()
|
||||
}
|
||||
|
||||
// asBool returns the value held by v as a bool. If v is not of KindBool, this
|
||||
// will return garbage.
|
||||
func (v Value) asBool() bool { return v.num == 1 }
|
||||
|
||||
// AsFloat64 returns the value held by v as a float64.
|
||||
func (v Value) AsFloat64() float64 { return 0 } // TODO (#4914): implement
|
||||
func (v Value) AsFloat64() float64 {
|
||||
if v.Kind() != KindFloat64 {
|
||||
global.Error(errKind, "AsFloat64", "Kind", v.Kind())
|
||||
return 0
|
||||
}
|
||||
return v.asFloat64()
|
||||
}
|
||||
|
||||
// asFloat64 returns the value held by v as a float64. If v is not of
|
||||
// KindFloat64, this will return garbage.
|
||||
func (v Value) asFloat64() float64 { return math.Float64frombits(v.num) }
|
||||
|
||||
// AsBytes returns the value held by v as a []byte.
|
||||
func (v Value) AsBytes() []byte { return nil } // TODO (#4914): implement
|
||||
func (v Value) AsBytes() []byte {
|
||||
if sp, ok := v.any.(bytesptr); ok {
|
||||
return unsafe.Slice((*byte)(sp), v.num)
|
||||
}
|
||||
global.Error(errKind, "AsBytes", "Kind", v.Kind())
|
||||
return nil
|
||||
}
|
||||
|
||||
// asBytes returns the value held by v as a []byte. It will panic if the Value
|
||||
// is not KindBytes.
|
||||
func (v Value) asBytes() []byte {
|
||||
return unsafe.Slice((*byte)(v.any.(bytesptr)), v.num)
|
||||
}
|
||||
|
||||
// AsSlice returns the value held by v as a []Value.
|
||||
func (v Value) AsSlice() []Value { return nil } // TODO (#4914): implement
|
||||
func (v Value) AsSlice() []Value {
|
||||
if sp, ok := v.any.(sliceptr); ok {
|
||||
return unsafe.Slice((*Value)(sp), v.num)
|
||||
}
|
||||
global.Error(errKind, "AsSlice", "Kind", v.Kind())
|
||||
return nil
|
||||
}
|
||||
|
||||
// asSlice returns the value held by v as a []Value. It will panic if the Value
|
||||
// is not KindSlice.
|
||||
func (v Value) asSlice() []Value {
|
||||
return unsafe.Slice((*Value)(v.any.(sliceptr)), v.num)
|
||||
}
|
||||
|
||||
// AsMap returns the value held by v as a []KeyValue.
|
||||
func (v Value) AsMap() []KeyValue { return nil } // TODO (#4914): implement
|
||||
func (v Value) AsMap() []KeyValue {
|
||||
if sp, ok := v.any.(mapptr); ok {
|
||||
return unsafe.Slice((*KeyValue)(sp), v.num)
|
||||
}
|
||||
global.Error(errKind, "AsMap", "Kind", v.Kind())
|
||||
return nil
|
||||
}
|
||||
|
||||
// asMap returns the value held by v as a []KeyValue. It will panic if the
|
||||
// Value is not KindMap.
|
||||
func (v Value) asMap() []KeyValue {
|
||||
return unsafe.Slice((*KeyValue)(v.any.(mapptr)), v.num)
|
||||
}
|
||||
|
||||
// Kind returns the Kind of v.
|
||||
func (v Value) Kind() Kind { return KindEmpty } // TODO (#4914): implement.
|
||||
func (v Value) Kind() Kind {
|
||||
switch x := v.any.(type) {
|
||||
case Kind:
|
||||
return x
|
||||
case stringptr:
|
||||
return KindString
|
||||
case bytesptr:
|
||||
return KindBytes
|
||||
case sliceptr:
|
||||
return KindSlice
|
||||
case mapptr:
|
||||
return KindMap
|
||||
default:
|
||||
return KindEmpty
|
||||
}
|
||||
}
|
||||
|
||||
// Empty returns if v does not hold any value.
|
||||
func (v Value) Empty() bool { return false } // TODO (#4914): implement
|
||||
func (v Value) Empty() bool { return v.Kind() == KindEmpty }
|
||||
|
||||
// Equal returns if v is equal to w.
|
||||
func (v Value) Equal(w Value) bool { return false } // TODO (#4914): implement
|
||||
func (v Value) Equal(w Value) bool {
|
||||
k1 := v.Kind()
|
||||
k2 := w.Kind()
|
||||
if k1 != k2 {
|
||||
return false
|
||||
}
|
||||
switch k1 {
|
||||
case KindInt64, KindBool:
|
||||
return v.num == w.num
|
||||
case KindString:
|
||||
return v.asString() == w.asString()
|
||||
case KindFloat64:
|
||||
return v.asFloat64() == w.asFloat64()
|
||||
case KindSlice:
|
||||
// TODO: replace with slices.EqualFunc when Go 1.20 support dropped.
|
||||
return sliceEqualFunc(v.asSlice(), w.asSlice(), Value.Equal)
|
||||
case KindMap:
|
||||
// TODO: replace with slices.EqualFunc when Go 1.20 support dropped.
|
||||
return sliceEqualFunc(v.asMap(), w.asMap(), KeyValue.Equal)
|
||||
case KindBytes:
|
||||
return bytes.Equal(v.asBytes(), w.asBytes())
|
||||
case KindEmpty:
|
||||
return true
|
||||
default:
|
||||
global.Error(errKind, "Equal", "Kind", k1)
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
// sliceEqualFunc reports whether two slices are equal using an equality
|
||||
// function on each pair of elements. If the lengths are different,
|
||||
// sliceEqualFunc returns false. Otherwise, the elements are compared in
|
||||
// increasing index order, and the comparison stops at the first index for
|
||||
// which eq returns false.
|
||||
//
|
||||
// This is based on [EqualFunc]. It was added to provide backwards
|
||||
// compatibility for Go 1.20. When Go 1.20 is no longer supported it can be
|
||||
// removed.
|
||||
//
|
||||
// EqualFunc: https://pkg.go.dev/slices#EqualFunc
|
||||
func sliceEqualFunc[T any](s1 []T, s2 []T, eq func(T, T) bool) bool {
|
||||
if len(s1) != len(s2) {
|
||||
return false
|
||||
}
|
||||
for i, v1 := range s1 {
|
||||
v2 := s2[i]
|
||||
if !eq(v1, v2) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// An KeyValue is a key-value pair used to represent a log attribute (a
|
||||
// superset of [go.opentelemetry.io/otel/attribute.KeyValue]) and map item.
|
||||
@ -105,28 +333,46 @@ type KeyValue struct {
|
||||
}
|
||||
|
||||
// Equal returns if a is equal to b.
|
||||
func (a KeyValue) Equal(b KeyValue) bool { return false } // TODO (#4914): implement
|
||||
func (a KeyValue) Equal(b KeyValue) bool {
|
||||
return a.Key == b.Key && a.Value.Equal(b.Value)
|
||||
}
|
||||
|
||||
// String returns an KeyValue for a string value.
|
||||
func String(key, value string) KeyValue { return KeyValue{} } // TODO (#4914): implement
|
||||
func String(key, value string) KeyValue {
|
||||
return KeyValue{key, StringValue(value)}
|
||||
}
|
||||
|
||||
// Int64 returns an KeyValue for an int64 value.
|
||||
func Int64(key string, value int64) KeyValue { return KeyValue{} } // TODO (#4914): implement
|
||||
func Int64(key string, value int64) KeyValue {
|
||||
return KeyValue{key, Int64Value(value)}
|
||||
}
|
||||
|
||||
// Int returns an KeyValue for an int value.
|
||||
func Int(key string, value int) KeyValue { return KeyValue{} } // TODO (#4914): implement
|
||||
func Int(key string, value int) KeyValue {
|
||||
return KeyValue{key, IntValue(value)}
|
||||
}
|
||||
|
||||
// Float64 returns an KeyValue for a float64 value.
|
||||
func Float64(key string, v float64) KeyValue { return KeyValue{} } // TODO (#4914): implement
|
||||
func Float64(key string, value float64) KeyValue {
|
||||
return KeyValue{key, Float64Value(value)}
|
||||
}
|
||||
|
||||
// Bool returns an KeyValue for a bool value.
|
||||
func Bool(key string, v bool) KeyValue { return KeyValue{} } // TODO (#4914): implement
|
||||
func Bool(key string, value bool) KeyValue {
|
||||
return KeyValue{key, BoolValue(value)}
|
||||
}
|
||||
|
||||
// Bytes returns an KeyValue for a []byte value.
|
||||
func Bytes(key string, v []byte) KeyValue { return KeyValue{} } // TODO (#4914): implement
|
||||
func Bytes(key string, value []byte) KeyValue {
|
||||
return KeyValue{key, BytesValue(value)}
|
||||
}
|
||||
|
||||
// Slice returns an KeyValue for a []Value value.
|
||||
func Slice(key string, args ...Value) KeyValue { return KeyValue{} } // TODO (#4914): implement
|
||||
func Slice(key string, value ...Value) KeyValue {
|
||||
return KeyValue{key, SliceValue(value...)}
|
||||
}
|
||||
|
||||
// Map returns an KeyValue for a map value.
|
||||
func Map(key string, args ...KeyValue) KeyValue { return KeyValue{} } // TODO (#4914): implement
|
||||
func Map(key string, value ...KeyValue) KeyValue {
|
||||
return KeyValue{key, MapValue(value...)}
|
||||
}
|
||||
|
302
log/keyvalue_test.go
Normal file
302
log/keyvalue_test.go
Normal file
@ -0,0 +1,302 @@
|
||||
// Copyright The 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.
|
||||
|
||||
// Copyright 2022 The Go Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style
|
||||
// license that can be found in the LICENSE file.
|
||||
|
||||
package log_test
|
||||
|
||||
import (
|
||||
golog "log"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/go-logr/logr"
|
||||
"github.com/go-logr/logr/testr"
|
||||
"github.com/go-logr/stdr"
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
|
||||
"go.opentelemetry.io/otel/internal/global"
|
||||
"go.opentelemetry.io/otel/log"
|
||||
)
|
||||
|
||||
func TestKind(t *testing.T) {
|
||||
testCases := []struct {
|
||||
kind log.Kind
|
||||
str string
|
||||
value int
|
||||
}{
|
||||
{log.KindBool, "Bool", 1},
|
||||
{log.KindBytes, "Bytes", 5},
|
||||
{log.KindEmpty, "Empty", 0},
|
||||
{log.KindFloat64, "Float64", 2},
|
||||
{log.KindInt64, "Int64", 3},
|
||||
{log.KindSlice, "Slice", 6},
|
||||
{log.KindMap, "Map", 7},
|
||||
{log.KindString, "String", 4},
|
||||
}
|
||||
for _, tc := range testCases {
|
||||
t.Run(tc.str, func(t *testing.T) {
|
||||
assert.Equal(t, tc.value, int(tc.kind), "Kind value")
|
||||
assert.Equal(t, tc.str, tc.kind.String(), "Kind string")
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestValueEqual(t *testing.T) {
|
||||
vals := []log.Value{
|
||||
{},
|
||||
log.Int64Value(1),
|
||||
log.Int64Value(2),
|
||||
log.Float64Value(3.5),
|
||||
log.Float64Value(3.7),
|
||||
log.BoolValue(true),
|
||||
log.BoolValue(false),
|
||||
log.StringValue("hi"),
|
||||
log.StringValue("bye"),
|
||||
log.BytesValue([]byte{1, 3, 5}),
|
||||
log.SliceValue(log.StringValue("foo")),
|
||||
log.SliceValue(log.IntValue(3), log.StringValue("foo")),
|
||||
log.MapValue(log.Bool("b", true), log.Int("i", 3)),
|
||||
log.MapValue(
|
||||
log.Slice("l", log.IntValue(3), log.StringValue("foo")),
|
||||
log.Bytes("b", []byte{3, 5, 7}),
|
||||
),
|
||||
}
|
||||
for i, v1 := range vals {
|
||||
for j, v2 := range vals {
|
||||
assert.Equal(t, i == j, v1.Equal(v2), "%v.Equal(%v)", v1, v2)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestEmpty(t *testing.T) {
|
||||
v := log.Value{}
|
||||
t.Run("Value.Empty", func(t *testing.T) {
|
||||
assert.True(t, v.Empty())
|
||||
})
|
||||
t.Run("Value.AsAny", func(t *testing.T) {
|
||||
assert.Nil(t, v.AsAny())
|
||||
})
|
||||
|
||||
t.Run("Bytes", func(t *testing.T) {
|
||||
assert.Nil(t, log.Bytes("b", nil).Value.AsBytes())
|
||||
})
|
||||
t.Run("Slice", func(t *testing.T) {
|
||||
assert.Nil(t, log.Slice("s").Value.AsSlice())
|
||||
})
|
||||
t.Run("Map", func(t *testing.T) {
|
||||
assert.Nil(t, log.Map("m").Value.AsMap())
|
||||
})
|
||||
}
|
||||
|
||||
func TestEmptyGroupsPreserved(t *testing.T) {
|
||||
t.Run("Map", func(t *testing.T) {
|
||||
assert.Equal(t, []log.KeyValue{
|
||||
log.Int("a", 1),
|
||||
log.Map("g1", log.Map("g2")),
|
||||
log.Map("g3", log.Map("g4", log.Int("b", 2))),
|
||||
}, log.MapValue(
|
||||
log.Int("a", 1),
|
||||
log.Map("g1", log.Map("g2")),
|
||||
log.Map("g3", log.Map("g4", log.Int("b", 2))),
|
||||
).AsMap())
|
||||
})
|
||||
|
||||
t.Run("Slice", func(t *testing.T) {
|
||||
assert.Equal(t, []log.Value{{}}, log.SliceValue(log.Value{}).AsSlice())
|
||||
})
|
||||
}
|
||||
|
||||
func TestBool(t *testing.T) {
|
||||
const key, val = "key", true
|
||||
kv := log.Bool(key, val)
|
||||
testKV(t, key, val, kv)
|
||||
|
||||
v, k := kv.Value, log.KindBool
|
||||
t.Run("AsBool", func(t *testing.T) {
|
||||
assert.Equal(t, val, kv.Value.AsBool(), "AsBool")
|
||||
})
|
||||
t.Run("AsFloat64", testErrKind(v.AsFloat64, "AsFloat64", k))
|
||||
t.Run("AsInt64", testErrKind(v.AsInt64, "AsInt64", k))
|
||||
t.Run("AsString", testErrKind(v.AsString, "AsString", k))
|
||||
t.Run("AsBytes", testErrKind(v.AsBytes, "AsBytes", k))
|
||||
t.Run("AsSlice", testErrKind(v.AsSlice, "AsSlice", k))
|
||||
t.Run("AsMap", testErrKind(v.AsMap, "AsMap", k))
|
||||
}
|
||||
|
||||
func TestFloat64(t *testing.T) {
|
||||
const key, val = "key", 3.0
|
||||
kv := log.Float64(key, val)
|
||||
testKV(t, key, val, kv)
|
||||
|
||||
v, k := kv.Value, log.KindFloat64
|
||||
t.Run("AsBool", testErrKind(v.AsBool, "AsBool", k))
|
||||
t.Run("AsFloat64", func(t *testing.T) {
|
||||
assert.Equal(t, val, v.AsFloat64(), "AsFloat64")
|
||||
})
|
||||
t.Run("AsInt64", testErrKind(v.AsInt64, "AsInt64", k))
|
||||
t.Run("AsString", testErrKind(v.AsString, "AsString", k))
|
||||
t.Run("AsBytes", testErrKind(v.AsBytes, "AsBytes", k))
|
||||
t.Run("AsSlice", testErrKind(v.AsSlice, "AsSlice", k))
|
||||
t.Run("AsMap", testErrKind(v.AsMap, "AsMap", k))
|
||||
}
|
||||
|
||||
func TestInt(t *testing.T) {
|
||||
const key, val = "key", 1
|
||||
kv := log.Int(key, val)
|
||||
testKV[int64](t, key, val, kv)
|
||||
|
||||
v, k := kv.Value, log.KindInt64
|
||||
t.Run("AsBool", testErrKind(v.AsBool, "AsBool", k))
|
||||
t.Run("AsFloat64", testErrKind(v.AsFloat64, "AsFloat64", k))
|
||||
t.Run("AsInt64", func(t *testing.T) {
|
||||
assert.Equal(t, int64(val), v.AsInt64(), "AsInt64")
|
||||
})
|
||||
t.Run("AsString", testErrKind(v.AsString, "AsString", k))
|
||||
t.Run("AsBytes", testErrKind(v.AsBytes, "AsBytes", k))
|
||||
t.Run("AsSlice", testErrKind(v.AsSlice, "AsSlice", k))
|
||||
t.Run("AsMap", testErrKind(v.AsMap, "AsMap", k))
|
||||
}
|
||||
|
||||
func TestInt64(t *testing.T) {
|
||||
const key, val = "key", 1
|
||||
kv := log.Int64(key, val)
|
||||
testKV[int64](t, key, val, kv)
|
||||
|
||||
v, k := kv.Value, log.KindInt64
|
||||
t.Run("AsBool", testErrKind(v.AsBool, "AsBool", k))
|
||||
t.Run("AsFloat64", testErrKind(v.AsFloat64, "AsFloat64", k))
|
||||
t.Run("AsInt64", func(t *testing.T) {
|
||||
assert.Equal(t, int64(val), v.AsInt64(), "AsInt64")
|
||||
})
|
||||
t.Run("AsString", testErrKind(v.AsString, "AsString", k))
|
||||
t.Run("AsBytes", testErrKind(v.AsBytes, "AsBytes", k))
|
||||
t.Run("AsSlice", testErrKind(v.AsSlice, "AsSlice", k))
|
||||
t.Run("AsMap", testErrKind(v.AsMap, "AsMap", k))
|
||||
}
|
||||
|
||||
func TestString(t *testing.T) {
|
||||
const key, val = "key", "test string value"
|
||||
kv := log.String(key, val)
|
||||
testKV(t, key, val, kv)
|
||||
|
||||
v, k := kv.Value, log.KindString
|
||||
t.Run("AsBool", testErrKind(v.AsBool, "AsBool", k))
|
||||
t.Run("AsFloat64", testErrKind(v.AsFloat64, "AsFloat64", k))
|
||||
t.Run("AsInt64", testErrKind(v.AsInt64, "AsInt64", k))
|
||||
t.Run("AsString", func(t *testing.T) {
|
||||
assert.Equal(t, val, v.AsString(), "AsString")
|
||||
})
|
||||
t.Run("AsBytes", testErrKind(v.AsBytes, "AsBytes", k))
|
||||
t.Run("AsSlice", testErrKind(v.AsSlice, "AsSlice", k))
|
||||
t.Run("AsMap", testErrKind(v.AsMap, "AsMap", k))
|
||||
}
|
||||
|
||||
func TestBytes(t *testing.T) {
|
||||
const key = "key"
|
||||
val := []byte{3, 2, 1}
|
||||
kv := log.Bytes(key, val)
|
||||
testKV(t, key, val, kv)
|
||||
|
||||
v, k := kv.Value, log.KindBytes
|
||||
t.Run("AsBool", testErrKind(v.AsBool, "AsBool", k))
|
||||
t.Run("AsFloat64", testErrKind(v.AsFloat64, "AsFloat64", k))
|
||||
t.Run("AsInt64", testErrKind(v.AsInt64, "AsInt64", k))
|
||||
t.Run("AsString", testErrKind(v.AsString, "AsString", k))
|
||||
t.Run("AsBytes", func(t *testing.T) {
|
||||
assert.Equal(t, val, v.AsBytes(), "AsBytes")
|
||||
})
|
||||
t.Run("AsSlice", testErrKind(v.AsSlice, "AsSlice", k))
|
||||
t.Run("AsMap", testErrKind(v.AsMap, "AsMap", k))
|
||||
}
|
||||
|
||||
func TestSlice(t *testing.T) {
|
||||
const key = "key"
|
||||
val := []log.Value{log.IntValue(3), log.StringValue("foo")}
|
||||
kv := log.Slice(key, val...)
|
||||
testKV(t, key, val, kv)
|
||||
|
||||
v, k := kv.Value, log.KindSlice
|
||||
t.Run("AsBool", testErrKind(v.AsBool, "AsBool", k))
|
||||
t.Run("AsFloat64", testErrKind(v.AsFloat64, "AsFloat64", k))
|
||||
t.Run("AsInt64", testErrKind(v.AsInt64, "AsInt64", k))
|
||||
t.Run("AsString", testErrKind(v.AsString, "AsString", k))
|
||||
t.Run("AsBytes", testErrKind(v.AsBytes, "AsBytes", k))
|
||||
t.Run("AsSlice", func(t *testing.T) {
|
||||
assert.Equal(t, val, v.AsSlice(), "AsSlice")
|
||||
})
|
||||
t.Run("AsMap", testErrKind(v.AsMap, "AsMap", k))
|
||||
}
|
||||
|
||||
func TestMap(t *testing.T) {
|
||||
const key = "key"
|
||||
val := []log.KeyValue{
|
||||
log.Slice("l", log.IntValue(3), log.StringValue("foo")),
|
||||
log.Bytes("b", []byte{3, 5, 7}),
|
||||
}
|
||||
kv := log.Map(key, val...)
|
||||
testKV(t, key, val, kv)
|
||||
|
||||
v, k := kv.Value, log.KindMap
|
||||
t.Run("AsBool", testErrKind(v.AsBool, "AsBool", k))
|
||||
t.Run("AsFloat64", testErrKind(v.AsFloat64, "AsFloat64", k))
|
||||
t.Run("AsInt64", testErrKind(v.AsInt64, "AsInt64", k))
|
||||
t.Run("AsString", testErrKind(v.AsString, "AsString", k))
|
||||
t.Run("AsBytes", testErrKind(v.AsBytes, "AsBytes", k))
|
||||
t.Run("AsSlice", testErrKind(v.AsSlice, "AsSlice", k))
|
||||
t.Run("AsMap", func(t *testing.T) {
|
||||
assert.Equal(t, val, v.AsMap(), "AsMap")
|
||||
})
|
||||
}
|
||||
|
||||
type logSink struct {
|
||||
logr.LogSink
|
||||
|
||||
err error
|
||||
msg string
|
||||
keysAndValues []interface{}
|
||||
}
|
||||
|
||||
func (l *logSink) Error(err error, msg string, keysAndValues ...interface{}) {
|
||||
l.err, l.msg, l.keysAndValues = err, msg, keysAndValues
|
||||
l.LogSink.Error(err, msg, keysAndValues)
|
||||
}
|
||||
|
||||
var stdLogger = stdr.New(golog.New(os.Stderr, "", golog.LstdFlags|golog.Lshortfile))
|
||||
|
||||
func testErrKind[T any](f func() T, msg string, k log.Kind) func(*testing.T) {
|
||||
return func(t *testing.T) {
|
||||
l := &logSink{LogSink: testr.New(t).GetSink()}
|
||||
global.SetLogger(logr.New(l))
|
||||
t.Cleanup(func() { global.SetLogger(stdLogger) })
|
||||
|
||||
assert.Zero(t, f())
|
||||
|
||||
assert.ErrorContains(t, l.err, "invalid Kind")
|
||||
assert.Equal(t, msg, l.msg)
|
||||
require.Len(t, l.keysAndValues, 2, "logged attributes")
|
||||
assert.Equal(t, l.keysAndValues[1], k)
|
||||
}
|
||||
}
|
||||
|
||||
func testKV[T any](t *testing.T, key string, val T, kv log.KeyValue) {
|
||||
t.Helper()
|
||||
|
||||
assert.Equal(t, key, kv.Key, "incorrect key")
|
||||
assert.False(t, kv.Value.Empty(), "value empty")
|
||||
assert.Equal(t, kv.Value.AsAny(), T(val), "AsAny wrong value")
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user