4
4
"bytes"
5
5
"encoding/json"
6
6
"fmt"
7
+ "math/big"
7
8
"reflect"
8
9
"strconv"
9
10
"strings"
@@ -285,11 +286,20 @@ func formatLogfmtValue(value interface{}, term bool) string {
285
286
return "nil"
286
287
}
287
288
288
- if t , ok := value .(time.Time ); ok {
289
+ switch v := value .(type ) {
290
+ case time.Time :
289
291
// Performance optimization: No need for escaping since the provided
290
292
// timeFormat doesn't have any escape characters, and escaping is
291
293
// expensive.
292
- return t .Format (timeFormat )
294
+ return v .Format (timeFormat )
295
+
296
+ case * big.Int :
297
+ // Big ints get consumed by the Stringer clause so we need to handle
298
+ // them earlier on.
299
+ if v == nil {
300
+ return "<nil>"
301
+ }
302
+ return formatLogfmtBigInt (v )
293
303
}
294
304
if term {
295
305
if s , ok := value .(TerminalStringer ); ok {
@@ -305,15 +315,106 @@ func formatLogfmtValue(value interface{}, term bool) string {
305
315
return strconv .FormatFloat (float64 (v ), floatFormat , 3 , 64 )
306
316
case float64 :
307
317
return strconv .FormatFloat (v , floatFormat , 3 , 64 )
308
- case int , int8 , int16 , int32 , int64 , uint , uint8 , uint16 , uint32 , uint64 :
318
+ case int8 , uint8 :
309
319
return fmt .Sprintf ("%d" , value )
320
+ case int :
321
+ return FormatLogfmtInt64 (int64 (v ))
322
+ case int16 :
323
+ return FormatLogfmtInt64 (int64 (v ))
324
+ case int32 :
325
+ return FormatLogfmtInt64 (int64 (v ))
326
+ case int64 :
327
+ return FormatLogfmtInt64 (v )
328
+ case uint :
329
+ return FormatLogfmtUint64 (uint64 (v ))
330
+ case uint16 :
331
+ return FormatLogfmtUint64 (uint64 (v ))
332
+ case uint32 :
333
+ return FormatLogfmtUint64 (uint64 (v ))
334
+ case uint64 :
335
+ return FormatLogfmtUint64 (v )
310
336
case string :
311
337
return escapeString (v )
312
338
default :
313
339
return escapeString (fmt .Sprintf ("%+v" , value ))
314
340
}
315
341
}
316
342
343
+
344
+ // FormatLogfmtInt64 formats a potentially big number in a friendlier split format.
345
+ func FormatLogfmtInt64 (n int64 ) string {
346
+ if n < 0 {
347
+ return formatLogfmtUint64 (uint64 (- n ), true )
348
+ }
349
+ return formatLogfmtUint64 (uint64 (n ), false )
350
+ }
351
+
352
+ // FormatLogfmtUint64 formats a potentially big number in a friendlier split format.
353
+ func FormatLogfmtUint64 (n uint64 ) string {
354
+ return formatLogfmtUint64 (n , false )
355
+ }
356
+
357
+ func formatLogfmtUint64 (n uint64 , neg bool ) string {
358
+ // Small numbers are fine as is
359
+ if n < 100000 {
360
+ if neg {
361
+ return strconv .Itoa (- int (n ))
362
+ } else {
363
+ return strconv .Itoa (int (n ))
364
+ }
365
+ }
366
+ // Large numbers should be split
367
+ const maxLength = 26
368
+
369
+ var (
370
+ out = make ([]byte , maxLength )
371
+ i = maxLength - 1
372
+ comma = 0
373
+ )
374
+ for ; n > 0 ; i -- {
375
+ if comma == 3 {
376
+ comma = 0
377
+ out [i ] = ','
378
+ } else {
379
+ comma ++
380
+ out [i ] = '0' + byte (n % 10 )
381
+ n /= 10
382
+ }
383
+ }
384
+ if neg {
385
+ out [i ] = '-'
386
+ i --
387
+ }
388
+ return string (out [i + 1 :])
389
+ }
390
+
391
+ var big1000 = big .NewInt (1000 )
392
+
393
+ // formatLogfmtBigInt formats a potentially gigantic number in a friendlier split
394
+ // format.
395
+ func formatLogfmtBigInt (n * big.Int ) string {
396
+ // Most number don't need fancy handling, just downcast
397
+ if n .IsUint64 () {
398
+ return FormatLogfmtUint64 (n .Uint64 ())
399
+ }
400
+ if n .IsInt64 () {
401
+ return FormatLogfmtInt64 (n .Int64 ())
402
+ }
403
+ // Ok, huge number needs huge effort
404
+ groups := make ([]string , 0 , 8 ) // random initial size to cover most cases
405
+ for n .Cmp (big1000 ) >= 0 {
406
+ _ , mod := n .DivMod (n , big1000 , nil )
407
+ groups = append (groups , fmt .Sprintf ("%03d" , mod ))
408
+ }
409
+ groups = append (groups , n .String ())
410
+
411
+ last := len (groups ) - 1
412
+ for i := 0 ; i < len (groups )/ 2 ; i ++ {
413
+ groups [i ], groups [last - i ] = groups [last - i ], groups [i ]
414
+ }
415
+ return strings .Join (groups , "," )
416
+ }
417
+
317
418
// escapeString checks if the provided string needs escaping/quoting, and
318
419
// calls strconv.Quote if needed
319
420
func escapeString (s string ) string {
0 commit comments