/log/log.go

https://github.com/jbardin/gotrace · Go · 119 lines · 96 code · 13 blank · 10 comment · 14 complexity · c6c0725eff08d46e81629b70851d499b MD5 · raw file

  1. // Custom logger for gotrace
  2. package log
  3. import (
  4. "bufio"
  5. "bytes"
  6. "fmt"
  7. "io"
  8. "log"
  9. "os"
  10. "reflect"
  11. "strings"
  12. "sync"
  13. "sync/atomic"
  14. "time"
  15. "unicode/utf8"
  16. )
  17. // counter to mark each call so that entry and exit points can be correlated
  18. var (
  19. counter uint64
  20. L *log.Logger
  21. setupOnce sync.Once
  22. formatSize int
  23. )
  24. // Setup our logger
  25. // return a value so this van be executed in a toplevel var statement
  26. func Setup(output, prefix string, size int) int {
  27. setupOnce.Do(func() {
  28. setup(output, prefix, size)
  29. })
  30. return 0
  31. }
  32. func setup(output, prefix string, size int) {
  33. var out io.Writer
  34. switch output {
  35. case "stdout":
  36. out = os.Stdout
  37. default:
  38. out = os.Stderr
  39. }
  40. L = log.New(out, prefix, log.Lmicroseconds)
  41. formatSize = size
  42. }
  43. // Make things a little more readable. Format as strings with %q when we can,
  44. // strip down empty slices, and don't print the internals from buffers.
  45. func formatter(i interface{}, size int) (s string) {
  46. // don't show the internal state of buffers
  47. switch i := i.(type) {
  48. case *bufio.Reader:
  49. s = "&bufio.Reader{}"
  50. case *bufio.Writer:
  51. s = "&bufio.Writer{}"
  52. case *bytes.Buffer:
  53. s = fmt.Sprintf("&bytes.Buffer{%q}", i.String())
  54. case *bytes.Reader:
  55. v := reflect.ValueOf(i)
  56. // TODO: should probably iterate to find the slice in case the name changes
  57. if b, ok := v.FieldByName("s").Interface().([]byte); ok {
  58. if len(b) > size {
  59. b = b[:size]
  60. }
  61. s = fmt.Sprintf("&bytes.Reader{%q}", b)
  62. }
  63. case *strings.Reader:
  64. v := reflect.ValueOf(i)
  65. if f, ok := v.FieldByName("s").Interface().(string); ok {
  66. s = fmt.Sprintf("&strings.Reader{%q}", f)
  67. }
  68. case []byte:
  69. // bytes slices are often empty, so trim them down
  70. b := bytes.TrimLeft(i, "\x00")
  71. if len(b) == 0 {
  72. s = "[]byte{0...}"
  73. } else if utf8.Valid(i) {
  74. s = fmt.Sprintf("[]byte{%q}", i)
  75. } else {
  76. s = fmt.Sprintf("%#v", i)
  77. }
  78. case string:
  79. s = fmt.Sprintf("%q", i)
  80. }
  81. if s == "" {
  82. s = fmt.Sprintf("%#v", i)
  83. }
  84. if len(s) > size {
  85. last := s[len(s)-1]
  86. s = s[:size] + "..." + string(last)
  87. }
  88. return s
  89. }
  90. // Format N number of arguments for logging, and limit the length of each formatted arg.
  91. func Format(args ...interface{}) string {
  92. parts := make([]string, len(args))
  93. for i, arg := range args {
  94. parts[i] = formatter(arg, formatSize)
  95. }
  96. return strings.Join(parts, ", ")
  97. }
  98. func ID() uint64 {
  99. return atomic.AddUint64(&counter, 1)
  100. }
  101. func Now() time.Time {
  102. return time.Now()
  103. }
  104. func Since(t time.Time) time.Duration {
  105. return time.Since(t)
  106. }