log: fix formatting of big.Int (#22679)
* log: fix formatting of big.Int The implementation of formatLogfmtBigInt had two issues: it crashed when the number was actually large enough to hit the big integer case, and modified the big.Int while formatting it. * log: don't call FormatLogfmtInt64 for int16 * log: separate from decimals back, not front Co-authored-by: Péter Szilágyi <peterke@gmail.com>
This commit is contained in:
parent
3cfd0fe7a8
commit
fda93f643e
|
@ -359,11 +359,16 @@ func formatLogfmtValue(value interface{}, term bool) string {
|
||||||
return strconv.FormatFloat(float64(v), floatFormat, 3, 64)
|
return strconv.FormatFloat(float64(v), floatFormat, 3, 64)
|
||||||
case float64:
|
case float64:
|
||||||
return strconv.FormatFloat(v, floatFormat, 3, 64)
|
return strconv.FormatFloat(v, floatFormat, 3, 64)
|
||||||
case int8, uint8:
|
case int8:
|
||||||
return fmt.Sprintf("%d", value)
|
return strconv.FormatInt(int64(v), 10)
|
||||||
case int:
|
case uint8:
|
||||||
return FormatLogfmtInt64(int64(v))
|
return strconv.FormatInt(int64(v), 10)
|
||||||
case int16:
|
case int16:
|
||||||
|
return strconv.FormatInt(int64(v), 10)
|
||||||
|
case uint16:
|
||||||
|
return strconv.FormatInt(int64(v), 10)
|
||||||
|
// Larger integers get thousands separators.
|
||||||
|
case int:
|
||||||
return FormatLogfmtInt64(int64(v))
|
return FormatLogfmtInt64(int64(v))
|
||||||
case int32:
|
case int32:
|
||||||
return FormatLogfmtInt64(int64(v))
|
return FormatLogfmtInt64(int64(v))
|
||||||
|
@ -371,8 +376,6 @@ func formatLogfmtValue(value interface{}, term bool) string {
|
||||||
return FormatLogfmtInt64(v)
|
return FormatLogfmtInt64(v)
|
||||||
case uint:
|
case uint:
|
||||||
return FormatLogfmtUint64(uint64(v))
|
return FormatLogfmtUint64(uint64(v))
|
||||||
case uint16:
|
|
||||||
return FormatLogfmtUint64(uint64(v))
|
|
||||||
case uint32:
|
case uint32:
|
||||||
return FormatLogfmtUint64(uint64(v))
|
return FormatLogfmtUint64(uint64(v))
|
||||||
case uint64:
|
case uint64:
|
||||||
|
@ -384,7 +387,7 @@ func formatLogfmtValue(value interface{}, term bool) string {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// FormatLogfmtInt64 formats a potentially big number in a friendlier split format.
|
// FormatLogfmtInt64 formats n with thousand separators.
|
||||||
func FormatLogfmtInt64(n int64) string {
|
func FormatLogfmtInt64(n int64) string {
|
||||||
if n < 0 {
|
if n < 0 {
|
||||||
return formatLogfmtUint64(uint64(-n), true)
|
return formatLogfmtUint64(uint64(-n), true)
|
||||||
|
@ -392,7 +395,7 @@ func FormatLogfmtInt64(n int64) string {
|
||||||
return formatLogfmtUint64(uint64(n), false)
|
return formatLogfmtUint64(uint64(n), false)
|
||||||
}
|
}
|
||||||
|
|
||||||
// FormatLogfmtUint64 formats a potentially big number in a friendlier split format.
|
// FormatLogfmtUint64 formats n with thousand separators.
|
||||||
func FormatLogfmtUint64(n uint64) string {
|
func FormatLogfmtUint64(n uint64) string {
|
||||||
return formatLogfmtUint64(n, false)
|
return formatLogfmtUint64(n, false)
|
||||||
}
|
}
|
||||||
|
@ -431,31 +434,38 @@ func formatLogfmtUint64(n uint64, neg bool) string {
|
||||||
return string(out[i+1:])
|
return string(out[i+1:])
|
||||||
}
|
}
|
||||||
|
|
||||||
var big1000 = big.NewInt(1000)
|
// formatLogfmtBigInt formats n with thousand separators.
|
||||||
|
|
||||||
// formatLogfmtBigInt formats a potentially gigantic number in a friendlier split
|
|
||||||
// format.
|
|
||||||
func formatLogfmtBigInt(n *big.Int) string {
|
func formatLogfmtBigInt(n *big.Int) string {
|
||||||
// Most number don't need fancy handling, just downcast
|
|
||||||
if n.IsUint64() {
|
if n.IsUint64() {
|
||||||
return FormatLogfmtUint64(n.Uint64())
|
return FormatLogfmtUint64(n.Uint64())
|
||||||
}
|
}
|
||||||
if n.IsInt64() {
|
if n.IsInt64() {
|
||||||
return FormatLogfmtInt64(n.Int64())
|
return FormatLogfmtInt64(n.Int64())
|
||||||
}
|
}
|
||||||
// Ok, huge number needs huge effort
|
|
||||||
groups := make([]string, 0, 8) // random initial size to cover most cases
|
|
||||||
for n.Cmp(big1000) >= 0 {
|
|
||||||
_, mod := n.DivMod(n, big1000, nil)
|
|
||||||
groups = append(groups, fmt.Sprintf("%03d", mod))
|
|
||||||
}
|
|
||||||
groups = append(groups, n.String())
|
|
||||||
|
|
||||||
last := len(groups) - 1
|
var (
|
||||||
for i := 0; i < len(groups)/2; i++ {
|
text = n.String()
|
||||||
groups[i], groups[last-i] = groups[last-i], groups[i]
|
buf = make([]byte, len(text)+len(text)/3)
|
||||||
|
comma = 0
|
||||||
|
i = len(buf) - 1
|
||||||
|
)
|
||||||
|
for j := len(text) - 1; j >= 0; j, i = j-1, i-1 {
|
||||||
|
c := text[j]
|
||||||
|
|
||||||
|
switch {
|
||||||
|
case c == '-':
|
||||||
|
buf[i] = c
|
||||||
|
case comma == 3:
|
||||||
|
buf[i] = ','
|
||||||
|
i--
|
||||||
|
comma = 0
|
||||||
|
fallthrough
|
||||||
|
default:
|
||||||
|
buf[i] = c
|
||||||
|
comma++
|
||||||
}
|
}
|
||||||
return strings.Join(groups, ",")
|
}
|
||||||
|
return string(buf[i+1:])
|
||||||
}
|
}
|
||||||
|
|
||||||
// escapeString checks if the provided string needs escaping/quoting, and
|
// escapeString checks if the provided string needs escaping/quoting, and
|
||||||
|
|
|
@ -2,6 +2,7 @@ package log
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"math"
|
"math"
|
||||||
|
"math/big"
|
||||||
"math/rand"
|
"math/rand"
|
||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
|
@ -58,6 +59,25 @@ func TestPrettyUint64(t *testing.T) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestPrettyBigInt(t *testing.T) {
|
||||||
|
tests := []struct {
|
||||||
|
int string
|
||||||
|
s string
|
||||||
|
}{
|
||||||
|
{"111222333444555678999", "111,222,333,444,555,678,999"},
|
||||||
|
{"-111222333444555678999", "-111,222,333,444,555,678,999"},
|
||||||
|
{"11122233344455567899900", "11,122,233,344,455,567,899,900"},
|
||||||
|
{"-11122233344455567899900", "-11,122,233,344,455,567,899,900"},
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, tt := range tests {
|
||||||
|
v, _ := new(big.Int).SetString(tt.int, 10)
|
||||||
|
if have := formatLogfmtBigInt(v); have != tt.s {
|
||||||
|
t.Errorf("invalid output %s, want %s", have, tt.s)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
var sink string
|
var sink string
|
||||||
|
|
||||||
func BenchmarkPrettyInt64Logfmt(b *testing.B) {
|
func BenchmarkPrettyInt64Logfmt(b *testing.B) {
|
||||||
|
|
Loading…
Reference in New Issue