log_test.go 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189
  1. package log
  2. import (
  3. "bytes"
  4. "encoding/json"
  5. "github.com/stretchr/testify/require"
  6. "os"
  7. "testing"
  8. "time"
  9. )
  10. func TestMain(m *testing.M) {
  11. exitCode := m.Run()
  12. resetState()
  13. SetLevel(ErrorLevel) // For other modules!
  14. os.Exit(exitCode)
  15. }
  16. func TestLog_TagContextFieldFields(t *testing.T) {
  17. t.Cleanup(resetState)
  18. v := &fakeVisitor{
  19. UserID: "u_abc",
  20. IP: "1.2.3.4",
  21. }
  22. err := &fakeError{
  23. Code: 123,
  24. Message: "some error",
  25. }
  26. var out bytes.Buffer
  27. SetOutput(&out)
  28. SetFormat(JSONFormat)
  29. SetLevelOverride("tag", "stripe", DebugLevel)
  30. SetLevelOverride("number", "5", DebugLevel)
  31. Tag("mytag").
  32. Field("field2", 123).
  33. Field("field1", "value1").
  34. Time(time.Unix(123, 999000000).UTC()).
  35. Info("hi there %s", "phil")
  36. Tag("not-stripe").
  37. Debug("this message will not appear")
  38. With(v).
  39. Fields(Context{
  40. "stripe_customer_id": "acct_123",
  41. "stripe_subscription_id": "sub_123",
  42. }).
  43. Tag("stripe").
  44. Err(err).
  45. Time(time.Unix(456, 123000000).UTC()).
  46. Debug("Subscription status %s", "active")
  47. Field("number", 5).
  48. Time(time.Unix(777, 001000000).UTC()).
  49. Debug("The number 5 is an int, but the level override is a string")
  50. expected := `{"time":"1970-01-01T00:02:03.999Z","level":"INFO","message":"hi there phil","field1":"value1","field2":123,"tag":"mytag"}
  51. {"time":"1970-01-01T00:07:36.123Z","level":"DEBUG","message":"Subscription status active","error":"some error","error_code":123,"stripe_customer_id":"acct_123","stripe_subscription_id":"sub_123","tag":"stripe","user_id":"u_abc","visitor_ip":"1.2.3.4"}
  52. {"time":"1970-01-01T00:12:57Z","level":"DEBUG","message":"The number 5 is an int, but the level override is a string","number":5}
  53. `
  54. require.Equal(t, expected, out.String())
  55. }
  56. func TestLog_NoAllocIfNotPrinted(t *testing.T) {
  57. t.Cleanup(resetState)
  58. v := &fakeVisitor{
  59. UserID: "u_abc",
  60. IP: "1.2.3.4",
  61. }
  62. var out bytes.Buffer
  63. SetOutput(&out)
  64. SetFormat(JSONFormat)
  65. // Do not log, do not call contexters (because global level is INFO)
  66. v.contextCalled = false
  67. ev := With(v)
  68. ev.Debug("some message")
  69. require.False(t, v.contextCalled)
  70. require.Equal(t, "", ev.Timestamp)
  71. require.Equal(t, Level(0), ev.Level)
  72. require.Equal(t, "", ev.Message)
  73. require.Nil(t, ev.fields)
  74. // Logged because info level, contexters called
  75. v.contextCalled = false
  76. ev = With(v).Time(time.Unix(1111, 0).UTC())
  77. ev.Info("some message")
  78. require.True(t, v.contextCalled)
  79. require.NotNil(t, ev.fields)
  80. require.Equal(t, "1.2.3.4", ev.fields["visitor_ip"])
  81. // Not logged, but contexters called, because overrides exist
  82. SetLevel(DebugLevel)
  83. SetLevelOverride("tag", "overridetag", TraceLevel)
  84. v.contextCalled = false
  85. ev = Tag("sometag").Field("field", "value").With(v).Time(time.Unix(123, 0).UTC())
  86. ev.Trace("some debug message")
  87. require.True(t, v.contextCalled) // If there are overrides, we must call the context to determine the filter fields
  88. require.Equal(t, "", ev.Timestamp)
  89. require.Equal(t, Level(0), ev.Level)
  90. require.Equal(t, "", ev.Message)
  91. require.Equal(t, 4, len(ev.fields))
  92. require.Equal(t, "value", ev.fields["field"])
  93. require.Equal(t, "sometag", ev.fields["tag"])
  94. // Logged because of override tag, and contexters called
  95. v.contextCalled = false
  96. ev = Tag("overridetag").Field("field", "value").With(v).Time(time.Unix(123, 0).UTC())
  97. ev.Trace("some trace message")
  98. require.True(t, v.contextCalled)
  99. require.Equal(t, "1970-01-01T00:02:03Z", ev.Timestamp)
  100. require.Equal(t, TraceLevel, ev.Level)
  101. require.Equal(t, "some trace message", ev.Message)
  102. // Logged because of field override, and contexters called
  103. ResetLevelOverrides()
  104. SetLevelOverride("visitor_ip", "1.2.3.4", TraceLevel)
  105. v.contextCalled = false
  106. ev = With(v).Time(time.Unix(124, 0).UTC())
  107. ev.Trace("some trace message with override")
  108. require.True(t, v.contextCalled)
  109. require.Equal(t, "1970-01-01T00:02:04Z", ev.Timestamp)
  110. require.Equal(t, TraceLevel, ev.Level)
  111. require.Equal(t, "some trace message with override", ev.Message)
  112. expected := `{"time":"1970-01-01T00:18:31Z","level":"INFO","message":"some message","user_id":"u_abc","visitor_ip":"1.2.3.4"}
  113. {"time":"1970-01-01T00:02:03Z","level":"TRACE","message":"some trace message","field":"value","tag":"overridetag","user_id":"u_abc","visitor_ip":"1.2.3.4"}
  114. {"time":"1970-01-01T00:02:04Z","level":"TRACE","message":"some trace message with override","user_id":"u_abc","visitor_ip":"1.2.3.4"}
  115. `
  116. require.Equal(t, expected, out.String())
  117. }
  118. func TestLog_Timing(t *testing.T) {
  119. t.Cleanup(resetState)
  120. var out bytes.Buffer
  121. SetOutput(&out)
  122. SetFormat(JSONFormat)
  123. Timing(func() { time.Sleep(300 * time.Millisecond) }).
  124. Time(time.Unix(12, 0).UTC()).
  125. Info("A thing that takes a while")
  126. var ev struct {
  127. TimeTakenMs int64 `json:"time_taken_ms"`
  128. }
  129. require.Nil(t, json.Unmarshal(out.Bytes(), &ev))
  130. require.True(t, ev.TimeTakenMs >= 300)
  131. require.Contains(t, out.String(), `{"time":"1970-01-01T00:00:12Z","level":"INFO","message":"A thing that takes a while","time_taken_ms":`)
  132. }
  133. type fakeError struct {
  134. Code int
  135. Message string
  136. }
  137. func (e fakeError) Error() string {
  138. return e.Message
  139. }
  140. func (e fakeError) Context() Context {
  141. return Context{
  142. "error": e.Message,
  143. "error_code": e.Code,
  144. }
  145. }
  146. type fakeVisitor struct {
  147. UserID string
  148. IP string
  149. contextCalled bool
  150. }
  151. func (v *fakeVisitor) Context() Context {
  152. v.contextCalled = true
  153. return Context{
  154. "user_id": v.UserID,
  155. "visitor_ip": v.IP,
  156. }
  157. }
  158. func resetState() {
  159. SetLevel(DefaultLevel)
  160. SetFormat(DefaultFormat)
  161. SetOutput(DefaultOutput)
  162. ResetLevelOverrides()
  163. }