fix(log): toString float32 precision loss and convert uint use `FormatUint` (#2461)

* fix(log): toString float32 precision loss

* convert uint to string
pull/2464/head
Jesse 2 years ago committed by GitHub
parent 4bd1fde7ef
commit a911f8f9ee
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 36
      contrib/log/aliyun/aliyun.go
  2. 59
      contrib/log/aliyun/aliyun_test.go
  3. 36
      contrib/log/tencent/tencent.go
  4. 44
      contrib/log/tencent/tencent_test.go
  5. 4
      log/level.go
  6. 6
      log/level_test.go

@ -16,6 +16,7 @@ import (
// Logger see more detail https://github.com/aliyun/aliyun-log-go-sdk // Logger see more detail https://github.com/aliyun/aliyun-log-go-sdk
type Logger interface { type Logger interface {
log.Logger log.Logger
GetProducer() *producer.Producer GetProducer() *producer.Producer
Close() error Close() error
} }
@ -81,22 +82,16 @@ func (a *aliyunLog) Close() error {
} }
func (a *aliyunLog) Log(level log.Level, keyvals ...interface{}) error { func (a *aliyunLog) Log(level log.Level, keyvals ...interface{}) error {
buf := level.String() contents := make([]*sls.LogContent, 0, len(keyvals)/2+1)
levelTitle := "level"
contents := make([]*sls.LogContent, 0)
contents = append(contents, &sls.LogContent{ contents = append(contents, &sls.LogContent{
Key: &levelTitle, Key: newString(level.Key()),
Value: &buf, Value: newString(level.String()),
}) })
for i := 0; i < len(keyvals); i += 2 { for i := 0; i < len(keyvals); i += 2 {
key := toString(keyvals[i])
value := toString(keyvals[i+1])
contents = append(contents, &sls.LogContent{ contents = append(contents, &sls.LogContent{
Key: &key, Key: newString(toString(keyvals[i])),
Value: &value, Value: newString(toString(keyvals[i+1])),
}) })
} }
@ -104,9 +99,7 @@ func (a *aliyunLog) Log(level log.Level, keyvals ...interface{}) error {
Time: proto.Uint32(uint32(time.Now().Unix())), Time: proto.Uint32(uint32(time.Now().Unix())),
Contents: contents, Contents: contents,
} }
return a.producer.SendLog(a.opts.project, a.opts.logstore, "", "", logInst)
err := a.producer.SendLog(a.opts.project, a.opts.logstore, "", "", logInst)
return err
} }
// NewAliyunLog new a aliyun logger with options. // NewAliyunLog new a aliyun logger with options.
@ -128,6 +121,11 @@ func NewAliyunLog(options ...Option) Logger {
} }
} }
// newString string convert to *string
func newString(s string) *string {
return &s
}
// toString convert any type to string // toString convert any type to string
func toString(v interface{}) string { func toString(v interface{}) string {
var key string var key string
@ -138,23 +136,23 @@ func toString(v interface{}) string {
case float64: case float64:
key = strconv.FormatFloat(v, 'f', -1, 64) key = strconv.FormatFloat(v, 'f', -1, 64)
case float32: case float32:
key = strconv.FormatFloat(float64(v), 'f', -1, 64) key = strconv.FormatFloat(float64(v), 'f', -1, 32)
case int: case int:
key = strconv.Itoa(v) key = strconv.Itoa(v)
case uint: case uint:
key = strconv.Itoa(int(v)) key = strconv.FormatUint(uint64(v), 10)
case int8: case int8:
key = strconv.Itoa(int(v)) key = strconv.Itoa(int(v))
case uint8: case uint8:
key = strconv.Itoa(int(v)) key = strconv.FormatUint(uint64(v), 10)
case int16: case int16:
key = strconv.Itoa(int(v)) key = strconv.Itoa(int(v))
case uint16: case uint16:
key = strconv.Itoa(int(v)) key = strconv.FormatUint(uint64(v), 10)
case int32: case int32:
key = strconv.Itoa(int(v)) key = strconv.Itoa(int(v))
case uint32: case uint32:
key = strconv.Itoa(int(v)) key = strconv.FormatUint(uint64(v), 10)
case int64: case int64:
key = strconv.FormatInt(v, 10) key = strconv.FormatInt(v, 10)
case uint64: case uint64:

@ -2,6 +2,7 @@ package aliyun
import ( import (
"math" "math"
"reflect"
"testing" "testing"
"github.com/go-kratos/kratos/v2/log" "github.com/go-kratos/kratos/v2/log"
@ -99,34 +100,44 @@ func TestLog(t *testing.T) {
} }
} }
func TestNewString(t *testing.T) {
ptr := newString("")
if kind := reflect.TypeOf(ptr).Kind(); kind != reflect.Ptr {
t.Errorf("want type: %v, got type: %v", reflect.Ptr, kind)
}
}
func TestToString(t *testing.T) { func TestToString(t *testing.T) {
tests := []struct { tests := []struct {
in interface{} name string
out string in interface{}
out string
}{ }{
{math.MaxFloat64, "17976931348623157000000000000000000000000000000000000" + {"float64", 6.66, "6.66"},
"000000000000000000000000000000000000000000000000000000000000000000000000000" + {"max float64", math.MaxFloat64, "179769313486231570000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"}, //nolint:lll
"000000000000000000000000000000000000000000000000000000000000000000000000000" + {"float32", float32(6.66), "6.66"},
"000000000000000000000000000000000000000000000000000000000000000000000000000" + {"max float32", math.MaxFloat32, "340282346638528860000000000000000000000"},
"0000000000000000000000000000000"}, {"int", int(math.MaxInt64), "9223372036854775807"},
{math.MaxFloat32, "340282346638528860000000000000000000000"}, {"uint", uint(math.MaxUint64), "18446744073709551615"},
{1<<((32<<(^uint(0)>>63))-1) - 1, "9223372036854775807"}, {"int8", math.MaxInt8, "127"},
{uint(1<<(32<<(^uint(0)>>63)) - 1), "-1"}, {"uint8", math.MaxUint8, "255"},
{math.MaxInt8, "127"}, {"int16", math.MaxInt16, "32767"},
{math.MaxUint8, "255"}, {"uint16", math.MaxUint16, "65535"},
{math.MaxInt16, "32767"}, {"int32", math.MaxInt32, "2147483647"},
{math.MaxUint16, "65535"}, {"uint32", math.MaxUint32, "4294967295"},
{math.MaxInt32, "2147483647"}, {"int64", math.MaxInt64, "9223372036854775807"},
{math.MaxUint32, "4294967295"}, {"uint64", uint64(math.MaxUint64), "18446744073709551615"},
{math.MaxInt64, "9223372036854775807"}, {"string", "abc", "abc"},
{uint64(math.MaxUint64), "18446744073709551615"}, {"bool", false, "false"},
{"abc", "abc"}, {"[]byte", []byte("abc"), "abc"},
{false, "false"}, {"struct", struct{ Name string }{}, `{"Name":""}`},
{[]byte("abc"), "abc"},
} }
for _, test := range tests { for _, test := range tests {
if toString(test.in) != test.out { t.Run(test.name, func(t *testing.T) {
t.Fatalf("want: %s, got: %s", test.out, toString(test.in)) out := toString(test.in)
} if test.out != out {
t.Fatalf("want: %s, got: %s", test.out, out)
}
})
} }
} }

@ -14,8 +14,8 @@ import (
type Logger interface { type Logger interface {
log.Logger log.Logger
GetProducer() *cls.AsyncProducerClient
GetProducer() *cls.AsyncProducerClient
Close() error Close() error
} }
@ -66,25 +66,20 @@ func WithAccessSecret(as string) Option {
type Option func(cls *options) type Option func(cls *options)
func (log *tencentLog) Close() error { func (log *tencentLog) Close() error {
err := log.producer.Close(5000) return log.producer.Close(5000)
return err
} }
func (log *tencentLog) Log(level log.Level, keyvals ...interface{}) error { func (log *tencentLog) Log(level log.Level, keyvals ...interface{}) error {
buf := level.String() contents := make([]*cls.Log_Content, 0, len(keyvals)/2+1)
levelTitle := "level"
contents := make([]*cls.Log_Content, 0)
contents = append(contents, &cls.Log_Content{ contents = append(contents, &cls.Log_Content{
Key: &levelTitle, Key: newString(level.Key()),
Value: &buf, Value: newString(level.String()),
}) })
for i := 0; i < len(keyvals); i += 2 { for i := 0; i < len(keyvals); i += 2 {
key := toString(keyvals[i])
value := toString(keyvals[i+1])
contents = append(contents, &cls.Log_Content{ contents = append(contents, &cls.Log_Content{
Key: &key, Key: newString(toString(keyvals[i])),
Value: &value, Value: newString(toString(keyvals[i+1])),
}) })
} }
@ -92,8 +87,7 @@ func (log *tencentLog) Log(level log.Level, keyvals ...interface{}) error {
Time: proto.Int64(time.Now().Unix()), Time: proto.Int64(time.Now().Unix()),
Contents: contents, Contents: contents,
} }
err := log.producer.SendLog(log.opts.topicID, logInst, nil) return log.producer.SendLog(log.opts.topicID, logInst, nil)
return err
} }
func NewLogger(options ...Option) (Logger, error) { func NewLogger(options ...Option) (Logger, error) {
@ -115,6 +109,10 @@ func NewLogger(options ...Option) (Logger, error) {
}, nil }, nil
} }
func newString(s string) *string {
return &s
}
// toString convert any type to string // toString convert any type to string
func toString(v interface{}) string { func toString(v interface{}) string {
var key string var key string
@ -125,23 +123,23 @@ func toString(v interface{}) string {
case float64: case float64:
key = strconv.FormatFloat(v, 'f', -1, 64) key = strconv.FormatFloat(v, 'f', -1, 64)
case float32: case float32:
key = strconv.FormatFloat(float64(v), 'f', -1, 64) key = strconv.FormatFloat(float64(v), 'f', -1, 32)
case int: case int:
key = strconv.Itoa(v) key = strconv.Itoa(v)
case uint: case uint:
key = strconv.Itoa(int(v)) key = strconv.FormatUint(uint64(v), 10)
case int8: case int8:
key = strconv.Itoa(int(v)) key = strconv.Itoa(int(v))
case uint8: case uint8:
key = strconv.Itoa(int(v)) key = strconv.FormatUint(uint64(v), 10)
case int16: case int16:
key = strconv.Itoa(int(v)) key = strconv.Itoa(int(v))
case uint16: case uint16:
key = strconv.Itoa(int(v)) key = strconv.FormatUint(uint64(v), 10)
case int32: case int32:
key = strconv.Itoa(int(v)) key = strconv.Itoa(int(v))
case uint32: case uint32:
key = strconv.Itoa(int(v)) key = strconv.FormatUint(uint64(v), 10)
case int64: case int64:
key = strconv.FormatInt(v, 10) key = strconv.FormatInt(v, 10)
case uint64: case uint64:

@ -1,6 +1,8 @@
package tencent package tencent
import ( import (
"math"
"reflect"
"testing" "testing"
"github.com/go-kratos/kratos/v2/log" "github.com/go-kratos/kratos/v2/log"
@ -101,3 +103,45 @@ func TestLog(t *testing.T) {
t.Errorf("Log() returns error:%v", err) t.Errorf("Log() returns error:%v", err)
} }
} }
func TestNewString(t *testing.T) {
ptr := newString("")
if kind := reflect.TypeOf(ptr).Kind(); kind != reflect.Ptr {
t.Errorf("want type: %v, got type: %v", reflect.Ptr, kind)
}
}
func TestToString(t *testing.T) {
tests := []struct {
name string
in interface{}
out string
}{
{"float64", 6.66, "6.66"},
{"max float64", math.MaxFloat64, "179769313486231570000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"}, //nolint:lll
{"float32", float32(6.66), "6.66"},
{"max float32", math.MaxFloat32, "340282346638528860000000000000000000000"},
{"int", int(math.MaxInt64), "9223372036854775807"},
{"uint", uint(math.MaxUint64), "18446744073709551615"},
{"int8", math.MaxInt8, "127"},
{"uint8", math.MaxUint8, "255"},
{"int16", math.MaxInt16, "32767"},
{"uint16", math.MaxUint16, "65535"},
{"int32", math.MaxInt32, "2147483647"},
{"uint32", math.MaxUint32, "4294967295"},
{"int64", math.MaxInt64, "9223372036854775807"},
{"uint64", uint64(math.MaxUint64), "18446744073709551615"},
{"string", "abc", "abc"},
{"bool", false, "false"},
{"[]byte", []byte("abc"), "abc"},
{"struct", struct{ Name string }{}, `{"Name":""}`},
}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
out := toString(test.in)
if test.out != out {
t.Fatalf("want: %s, got: %s", test.out, out)
}
})
}
}

@ -21,6 +21,10 @@ const (
LevelFatal LevelFatal
) )
func (l Level) Key() string {
return LevelKey
}
func (l Level) String() string { func (l Level) String() string {
switch l { switch l {
case LevelDebug: case LevelDebug:

@ -2,6 +2,12 @@ package log
import "testing" import "testing"
func TestLevel_Key(t *testing.T) {
if LevelInfo.Key() != LevelKey {
t.Errorf("want: %s, got: %s", LevelKey, LevelInfo.Key())
}
}
func TestLevel_String(t *testing.T) { func TestLevel_String(t *testing.T) {
tests := []struct { tests := []struct {
name string name string

Loading…
Cancel
Save