44 "bytes"
55 "encoding/json"
66 "fmt"
7+ "math/big"
78 "reflect"
89 "strconv"
910 "strings"
@@ -327,11 +328,20 @@ func formatLogfmtValue(value interface{}, term bool) string {
327328 return "nil"
328329 }
329330
330- if t , ok := value .(time.Time ); ok {
331+ switch v := value .(type ) {
332+ case time.Time :
331333 // Performance optimization: No need for escaping since the provided
332334 // timeFormat doesn't have any escape characters, and escaping is
333335 // expensive.
334- return t .Format (timeFormat )
336+ return v .Format (timeFormat )
337+
338+ case * big.Int :
339+ // Big ints get consumed by the Stringer clause so we need to handle
340+ // them earlier on.
341+ if v == nil {
342+ return "<nil>"
343+ }
344+ return formatLogfmtBigInt (v )
335345 }
336346 if term {
337347 if s , ok := value .(TerminalStringer ); ok {
@@ -347,15 +357,105 @@ func formatLogfmtValue(value interface{}, term bool) string {
347357 return strconv .FormatFloat (float64 (v ), floatFormat , 3 , 64 )
348358 case float64 :
349359 return strconv .FormatFloat (v , floatFormat , 3 , 64 )
350- case int , int8 , int16 , int32 , int64 , uint , uint8 , uint16 , uint32 , uint64 :
360+ case int8 , uint8 :
351361 return fmt .Sprintf ("%d" , value )
362+ case int :
363+ return FormatLogfmtInt64 (int64 (v ))
364+ case int16 :
365+ return FormatLogfmtInt64 (int64 (v ))
366+ case int32 :
367+ return FormatLogfmtInt64 (int64 (v ))
368+ case int64 :
369+ return FormatLogfmtInt64 (v )
370+ case uint :
371+ return FormatLogfmtUint64 (uint64 (v ))
372+ case uint16 :
373+ return FormatLogfmtUint64 (uint64 (v ))
374+ case uint32 :
375+ return FormatLogfmtUint64 (uint64 (v ))
376+ case uint64 :
377+ return FormatLogfmtUint64 (v )
352378 case string :
353379 return escapeString (v )
354380 default :
355381 return escapeString (fmt .Sprintf ("%+v" , value ))
356382 }
357383}
358384
385+ // FormatLogfmtInt64 formats a potentially big number in a friendlier split format.
386+ func FormatLogfmtInt64 (n int64 ) string {
387+ if n < 0 {
388+ return formatLogfmtUint64 (uint64 (- n ), true )
389+ }
390+ return formatLogfmtUint64 (uint64 (n ), false )
391+ }
392+
393+ // FormatLogfmtUint64 formats a potentially big number in a friendlier split format.
394+ func FormatLogfmtUint64 (n uint64 ) string {
395+ return formatLogfmtUint64 (n , false )
396+ }
397+
398+ func formatLogfmtUint64 (n uint64 , neg bool ) string {
399+ // Small numbers are fine as is
400+ if n < 100000 {
401+ if neg {
402+ return strconv .Itoa (- int (n ))
403+ } else {
404+ return strconv .Itoa (int (n ))
405+ }
406+ }
407+ // Large numbers should be split
408+ const maxLength = 26
409+
410+ var (
411+ out = make ([]byte , maxLength )
412+ i = maxLength - 1
413+ comma = 0
414+ )
415+ for ; n > 0 ; i -- {
416+ if comma == 3 {
417+ comma = 0
418+ out [i ] = ','
419+ } else {
420+ comma ++
421+ out [i ] = '0' + byte (n % 10 )
422+ n /= 10
423+ }
424+ }
425+ if neg {
426+ out [i ] = '-'
427+ i --
428+ }
429+ return string (out [i + 1 :])
430+ }
431+
432+ var big1000 = big .NewInt (1000 )
433+
434+ // formatLogfmtBigInt formats a potentially gigantic number in a friendlier split
435+ // format.
436+ func formatLogfmtBigInt (n * big.Int ) string {
437+ // Most number don't need fancy handling, just downcast
438+ if n .IsUint64 () {
439+ return FormatLogfmtUint64 (n .Uint64 ())
440+ }
441+ if n .IsInt64 () {
442+ return FormatLogfmtInt64 (n .Int64 ())
443+ }
444+ // Ok, huge number needs huge effort
445+ groups := make ([]string , 0 , 8 ) // random initial size to cover most cases
446+ for n .Cmp (big1000 ) >= 0 {
447+ _ , mod := n .DivMod (n , big1000 , nil )
448+ groups = append (groups , fmt .Sprintf ("%03d" , mod ))
449+ }
450+ groups = append (groups , n .String ())
451+
452+ last := len (groups ) - 1
453+ for i := 0 ; i < len (groups )/ 2 ; i ++ {
454+ groups [i ], groups [last - i ] = groups [last - i ], groups [i ]
455+ }
456+ return strings .Join (groups , "," )
457+ }
458+
359459// escapeString checks if the provided string needs escaping/quoting, and
360460// calls strconv.Quote if needed
361461func escapeString (s string ) string {
0 commit comments