log_test.go 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210
  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. func TestLog_LevelOverrideAny(t *testing.T) {
  134. t.Cleanup(resetState)
  135. var out bytes.Buffer
  136. SetOutput(&out)
  137. SetFormat(JSONFormat)
  138. SetLevelOverride("this_one", "", DebugLevel)
  139. SetLevelOverride("time_taken_ms", "", TraceLevel)
  140. Time(time.Unix(11, 0).UTC()).Field("this_one", "11").Debug("this is logged")
  141. Time(time.Unix(12, 0).UTC()).Field("not_this", "11").Debug("this is not logged")
  142. Time(time.Unix(13, 0).UTC()).Field("this_too", "11").Info("this is also logged")
  143. Time(time.Unix(14, 0).UTC()).Field("time_taken_ms", 0).Info("this is also logged")
  144. expected := `{"time":"1970-01-01T00:00:11Z","level":"DEBUG","message":"this is logged","this_one":"11"}
  145. {"time":"1970-01-01T00:00:13Z","level":"INFO","message":"this is also logged","this_too":"11"}
  146. {"time":"1970-01-01T00:00:14Z","level":"INFO","message":"this is also logged","time_taken_ms":0}
  147. `
  148. require.Equal(t, expected, out.String())
  149. }
  150. type fakeError struct {
  151. Code int
  152. Message string
  153. }
  154. func (e fakeError) Error() string {
  155. return e.Message
  156. }
  157. func (e fakeError) Context() Context {
  158. return Context{
  159. "error": e.Message,
  160. "error_code": e.Code,
  161. }
  162. }
  163. type fakeVisitor struct {
  164. UserID string
  165. IP string
  166. contextCalled bool
  167. }
  168. func (v *fakeVisitor) Context() Context {
  169. v.contextCalled = true
  170. return Context{
  171. "user_id": v.UserID,
  172. "visitor_ip": v.IP,
  173. }
  174. }
  175. func resetState() {
  176. SetLevel(DefaultLevel)
  177. SetFormat(DefaultFormat)
  178. SetOutput(DefaultOutput)
  179. ResetLevelOverrides()
  180. }