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