richlogger_test.go 9.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357
  1. package logx
  2. import (
  3. "context"
  4. "encoding/json"
  5. "fmt"
  6. "io"
  7. "strings"
  8. "sync/atomic"
  9. "testing"
  10. "time"
  11. "github.com/stretchr/testify/assert"
  12. "go.opentelemetry.io/otel"
  13. sdktrace "go.opentelemetry.io/otel/sdk/trace"
  14. )
  15. func TestTraceLog(t *testing.T) {
  16. SetLevel(InfoLevel)
  17. w := new(mockWriter)
  18. old := writer.Swap(w)
  19. writer.lock.RLock()
  20. defer func() {
  21. writer.lock.RUnlock()
  22. writer.Store(old)
  23. }()
  24. otp := otel.GetTracerProvider()
  25. tp := sdktrace.NewTracerProvider(sdktrace.WithSampler(sdktrace.AlwaysSample()))
  26. otel.SetTracerProvider(tp)
  27. defer otel.SetTracerProvider(otp)
  28. ctx, span := tp.Tracer("trace-id").Start(context.Background(), "span-id")
  29. defer span.End()
  30. WithContext(ctx).Info(testlog)
  31. validate(t, w.String(), true, true)
  32. }
  33. func TestTraceDebug(t *testing.T) {
  34. w := new(mockWriter)
  35. old := writer.Swap(w)
  36. writer.lock.RLock()
  37. defer func() {
  38. writer.lock.RUnlock()
  39. writer.Store(old)
  40. }()
  41. otp := otel.GetTracerProvider()
  42. tp := sdktrace.NewTracerProvider(sdktrace.WithSampler(sdktrace.AlwaysSample()))
  43. otel.SetTracerProvider(tp)
  44. defer otel.SetTracerProvider(otp)
  45. ctx, span := tp.Tracer("foo").Start(context.Background(), "bar")
  46. defer span.End()
  47. l := WithContext(ctx)
  48. SetLevel(DebugLevel)
  49. l.WithDuration(time.Second).Debug(testlog)
  50. assert.True(t, strings.Contains(w.String(), traceKey))
  51. assert.True(t, strings.Contains(w.String(), spanKey))
  52. w.Reset()
  53. l.WithDuration(time.Second).Debugf(testlog)
  54. validate(t, w.String(), true, true)
  55. w.Reset()
  56. l.WithDuration(time.Second).Debugv(testlog)
  57. validate(t, w.String(), true, true)
  58. w.Reset()
  59. l.WithDuration(time.Second).Debugv(testobj)
  60. validateContentType(t, w.String(), map[string]any{}, true, true)
  61. w.Reset()
  62. l.WithDuration(time.Second).Debugw(testlog, Field("foo", "bar"))
  63. validate(t, w.String(), true, true)
  64. assert.True(t, strings.Contains(w.String(), "foo"), w.String())
  65. assert.True(t, strings.Contains(w.String(), "bar"), w.String())
  66. }
  67. func TestTraceError(t *testing.T) {
  68. w := new(mockWriter)
  69. old := writer.Swap(w)
  70. writer.lock.RLock()
  71. defer func() {
  72. writer.lock.RUnlock()
  73. writer.Store(old)
  74. }()
  75. otp := otel.GetTracerProvider()
  76. tp := sdktrace.NewTracerProvider(sdktrace.WithSampler(sdktrace.AlwaysSample()))
  77. otel.SetTracerProvider(tp)
  78. defer otel.SetTracerProvider(otp)
  79. ctx, span := tp.Tracer("trace-id").Start(context.Background(), "span-id")
  80. defer span.End()
  81. var nilCtx context.Context
  82. l := WithContext(context.Background())
  83. l = l.WithContext(nilCtx)
  84. l = l.WithContext(ctx)
  85. SetLevel(ErrorLevel)
  86. l.WithDuration(time.Second).Error(testlog)
  87. validate(t, w.String(), true, true)
  88. w.Reset()
  89. l.WithDuration(time.Second).Errorf(testlog)
  90. validate(t, w.String(), true, true)
  91. w.Reset()
  92. l.WithDuration(time.Second).Errorv(testlog)
  93. validate(t, w.String(), true, true)
  94. w.Reset()
  95. l.WithDuration(time.Second).Errorv(testobj)
  96. validateContentType(t, w.String(), map[string]any{}, true, true)
  97. w.Reset()
  98. l.WithDuration(time.Second).Errorw(testlog, Field("basket", "ball"))
  99. validate(t, w.String(), true, true)
  100. assert.True(t, strings.Contains(w.String(), "basket"), w.String())
  101. assert.True(t, strings.Contains(w.String(), "ball"), w.String())
  102. }
  103. func TestTraceInfo(t *testing.T) {
  104. w := new(mockWriter)
  105. old := writer.Swap(w)
  106. writer.lock.RLock()
  107. defer func() {
  108. writer.lock.RUnlock()
  109. writer.Store(old)
  110. }()
  111. otp := otel.GetTracerProvider()
  112. tp := sdktrace.NewTracerProvider(sdktrace.WithSampler(sdktrace.AlwaysSample()))
  113. otel.SetTracerProvider(tp)
  114. defer otel.SetTracerProvider(otp)
  115. ctx, span := tp.Tracer("trace-id").Start(context.Background(), "span-id")
  116. defer span.End()
  117. SetLevel(InfoLevel)
  118. l := WithContext(ctx)
  119. l.WithDuration(time.Second).Info(testlog)
  120. validate(t, w.String(), true, true)
  121. w.Reset()
  122. l.WithDuration(time.Second).Infof(testlog)
  123. validate(t, w.String(), true, true)
  124. w.Reset()
  125. l.WithDuration(time.Second).Infov(testlog)
  126. validate(t, w.String(), true, true)
  127. w.Reset()
  128. l.WithDuration(time.Second).Infov(testobj)
  129. validateContentType(t, w.String(), map[string]any{}, true, true)
  130. w.Reset()
  131. l.WithDuration(time.Second).Infow(testlog, Field("basket", "ball"))
  132. validate(t, w.String(), true, true)
  133. assert.True(t, strings.Contains(w.String(), "basket"), w.String())
  134. assert.True(t, strings.Contains(w.String(), "ball"), w.String())
  135. }
  136. func TestTraceInfoConsole(t *testing.T) {
  137. old := atomic.SwapUint32(&encoding, jsonEncodingType)
  138. defer atomic.StoreUint32(&encoding, old)
  139. w := new(mockWriter)
  140. o := writer.Swap(w)
  141. writer.lock.RLock()
  142. defer func() {
  143. writer.lock.RUnlock()
  144. writer.Store(o)
  145. }()
  146. otp := otel.GetTracerProvider()
  147. tp := sdktrace.NewTracerProvider(sdktrace.WithSampler(sdktrace.AlwaysSample()))
  148. otel.SetTracerProvider(tp)
  149. defer otel.SetTracerProvider(otp)
  150. ctx, span := tp.Tracer("trace-id").Start(context.Background(), "span-id")
  151. defer span.End()
  152. l := WithContext(ctx)
  153. SetLevel(InfoLevel)
  154. l.WithDuration(time.Second).Info(testlog)
  155. validate(t, w.String(), true, true)
  156. w.Reset()
  157. l.WithDuration(time.Second).Infof(testlog)
  158. validate(t, w.String(), true, true)
  159. w.Reset()
  160. l.WithDuration(time.Second).Infov(testlog)
  161. validate(t, w.String(), true, true)
  162. w.Reset()
  163. l.WithDuration(time.Second).Infov(testobj)
  164. validateContentType(t, w.String(), map[string]any{}, true, true)
  165. }
  166. func TestTraceSlow(t *testing.T) {
  167. w := new(mockWriter)
  168. old := writer.Swap(w)
  169. writer.lock.RLock()
  170. defer func() {
  171. writer.lock.RUnlock()
  172. writer.Store(old)
  173. }()
  174. otp := otel.GetTracerProvider()
  175. tp := sdktrace.NewTracerProvider(sdktrace.WithSampler(sdktrace.AlwaysSample()))
  176. otel.SetTracerProvider(tp)
  177. defer otel.SetTracerProvider(otp)
  178. ctx, span := tp.Tracer("trace-id").Start(context.Background(), "span-id")
  179. defer span.End()
  180. l := WithContext(ctx)
  181. SetLevel(InfoLevel)
  182. l.WithDuration(time.Second).Slow(testlog)
  183. assert.True(t, strings.Contains(w.String(), traceKey))
  184. assert.True(t, strings.Contains(w.String(), spanKey))
  185. w.Reset()
  186. l.WithDuration(time.Second).Slowf(testlog)
  187. validate(t, w.String(), true, true)
  188. w.Reset()
  189. l.WithDuration(time.Second).Slowv(testlog)
  190. validate(t, w.String(), true, true)
  191. w.Reset()
  192. l.WithDuration(time.Second).Slowv(testobj)
  193. validateContentType(t, w.String(), map[string]any{}, true, true)
  194. w.Reset()
  195. l.WithDuration(time.Second).Sloww(testlog, Field("basket", "ball"))
  196. validate(t, w.String(), true, true)
  197. assert.True(t, strings.Contains(w.String(), "basket"), w.String())
  198. assert.True(t, strings.Contains(w.String(), "ball"), w.String())
  199. }
  200. func TestTraceWithoutContext(t *testing.T) {
  201. w := new(mockWriter)
  202. old := writer.Swap(w)
  203. writer.lock.RLock()
  204. defer func() {
  205. writer.lock.RUnlock()
  206. writer.Store(old)
  207. }()
  208. l := WithContext(context.Background())
  209. SetLevel(InfoLevel)
  210. l.WithDuration(time.Second).Info(testlog)
  211. validate(t, w.String(), false, false)
  212. w.Reset()
  213. l.WithDuration(time.Second).Infof(testlog)
  214. validate(t, w.String(), false, false)
  215. }
  216. func TestLogWithFields(t *testing.T) {
  217. w := new(mockWriter)
  218. old := writer.Swap(w)
  219. writer.lock.RLock()
  220. defer func() {
  221. writer.lock.RUnlock()
  222. writer.Store(old)
  223. }()
  224. ctx := ContextWithFields(context.Background(), Field("foo", "bar"))
  225. l := WithContext(ctx)
  226. SetLevel(InfoLevel)
  227. l.Info(testlog)
  228. var val mockValue
  229. assert.Nil(t, json.Unmarshal([]byte(w.String()), &val))
  230. assert.Equal(t, "bar", val.Foo)
  231. }
  232. func TestLogWithCallerSkip(t *testing.T) {
  233. w := new(mockWriter)
  234. old := writer.Swap(w)
  235. writer.lock.RLock()
  236. defer func() {
  237. writer.lock.RUnlock()
  238. writer.Store(old)
  239. }()
  240. l := WithCallerSkip(1).WithCallerSkip(0)
  241. p := func(v string) {
  242. l.Info(v)
  243. }
  244. file, line := getFileLine()
  245. p(testlog)
  246. assert.True(t, w.Contains(fmt.Sprintf("%s:%d", file, line+1)))
  247. w.Reset()
  248. l = WithCallerSkip(0).WithCallerSkip(1)
  249. file, line = getFileLine()
  250. p(testlog)
  251. assert.True(t, w.Contains(fmt.Sprintf("%s:%d", file, line+1)))
  252. }
  253. func TestLoggerWithFields(t *testing.T) {
  254. w := new(mockWriter)
  255. old := writer.Swap(w)
  256. writer.lock.RLock()
  257. defer func() {
  258. writer.lock.RUnlock()
  259. writer.Store(old)
  260. }()
  261. l := WithContext(context.Background()).WithFields(Field("foo", "bar"))
  262. l.Info(testlog)
  263. var val mockValue
  264. assert.Nil(t, json.Unmarshal([]byte(w.String()), &val))
  265. assert.Equal(t, "bar", val.Foo)
  266. }
  267. func validate(t *testing.T, body string, expectedTrace, expectedSpan bool) {
  268. var val mockValue
  269. dec := json.NewDecoder(strings.NewReader(body))
  270. for {
  271. var doc mockValue
  272. err := dec.Decode(&doc)
  273. if err == io.EOF {
  274. // all done
  275. break
  276. }
  277. if err != nil {
  278. continue
  279. }
  280. val = doc
  281. }
  282. assert.Equal(t, expectedTrace, len(val.Trace) > 0, body)
  283. assert.Equal(t, expectedSpan, len(val.Span) > 0, body)
  284. }
  285. func validateContentType(t *testing.T, body string, expectedType any, expectedTrace, expectedSpan bool) {
  286. var val mockValue
  287. dec := json.NewDecoder(strings.NewReader(body))
  288. for {
  289. var doc mockValue
  290. err := dec.Decode(&doc)
  291. if err == io.EOF {
  292. // all done
  293. break
  294. }
  295. if err != nil {
  296. continue
  297. }
  298. val = doc
  299. }
  300. assert.IsType(t, expectedType, val.Content, body)
  301. assert.Equal(t, expectedTrace, len(val.Trace) > 0, body)
  302. assert.Equal(t, expectedSpan, len(val.Span) > 0, body)
  303. }
  304. type mockValue struct {
  305. Trace string `json:"trace"`
  306. Span string `json:"span"`
  307. Foo string `json:"foo"`
  308. Content any `json:"content"`
  309. }