trace_test.go 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262
  1. // Copyright 2019 The etcd Authors
  2. //
  3. // Licensed under the Apache License, Version 2.0 (the "License");
  4. // you may not use this file except in compliance with the License.
  5. // You may obtain a copy of the License at
  6. //
  7. // http://www.apache.org/licenses/LICENSE-2.0
  8. //
  9. // Unless required by applicable law or agreed to in writing, software
  10. // distributed under the License is distributed on an "AS IS" BASIS,
  11. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. // See the License for the specific language governing permissions and
  13. // limitations under the License.
  14. package traceutil
  15. import (
  16. "bytes"
  17. "context"
  18. "fmt"
  19. "io/ioutil"
  20. "os"
  21. "path/filepath"
  22. "testing"
  23. "time"
  24. "go.uber.org/zap"
  25. )
  26. func TestGet(t *testing.T) {
  27. traceForTest := &Trace{operation: "test"}
  28. tests := []struct {
  29. name string
  30. inputCtx context.Context
  31. outputTrace *Trace
  32. }{
  33. {
  34. name: "When the context does not have trace",
  35. inputCtx: context.TODO(),
  36. outputTrace: TODO(),
  37. },
  38. {
  39. name: "When the context has trace",
  40. inputCtx: context.WithValue(context.Background(), TraceKey, traceForTest),
  41. outputTrace: traceForTest,
  42. },
  43. }
  44. for _, tt := range tests {
  45. t.Run(tt.name, func(t *testing.T) {
  46. trace := Get(tt.inputCtx)
  47. if trace == nil {
  48. t.Errorf("Expected %v; Got nil", tt.outputTrace)
  49. }
  50. if trace.operation != tt.outputTrace.operation {
  51. t.Errorf("Expected %v; Got %v", tt.outputTrace, trace)
  52. }
  53. })
  54. }
  55. }
  56. func TestCreate(t *testing.T) {
  57. var (
  58. op = "Test"
  59. steps = []string{"Step1, Step2"}
  60. fields = []Field{
  61. {"traceKey1", "traceValue1"},
  62. {"traceKey2", "traceValue2"},
  63. }
  64. stepFields = []Field{
  65. {"stepKey1", "stepValue2"},
  66. {"stepKey2", "stepValue2"},
  67. }
  68. )
  69. trace := New(op, nil, fields[0], fields[1])
  70. if trace.operation != op {
  71. t.Errorf("Expected %v; Got %v", op, trace.operation)
  72. }
  73. for i, f := range trace.fields {
  74. if f.Key != fields[i].Key {
  75. t.Errorf("Expected %v; Got %v", fields[i].Key, f.Key)
  76. }
  77. if f.Value != fields[i].Value {
  78. t.Errorf("Expected %v; Got %v", fields[i].Value, f.Value)
  79. }
  80. }
  81. for i, v := range steps {
  82. trace.Step(v, stepFields[i])
  83. }
  84. for i, v := range trace.steps {
  85. if steps[i] != v.msg {
  86. t.Errorf("Expected %v; Got %v", steps[i], v.msg)
  87. }
  88. if stepFields[i].Key != v.fields[0].Key {
  89. t.Errorf("Expected %v; Got %v", stepFields[i].Key, v.fields[0].Key)
  90. }
  91. if stepFields[i].Value != v.fields[0].Value {
  92. t.Errorf("Expected %v; Got %v", stepFields[i].Value, v.fields[0].Value)
  93. }
  94. }
  95. }
  96. func TestLog(t *testing.T) {
  97. tests := []struct {
  98. name string
  99. trace *Trace
  100. fields []Field
  101. expectedMsg []string
  102. }{
  103. {
  104. name: "When dump all logs",
  105. trace: &Trace{
  106. operation: "Test",
  107. startTime: time.Now().Add(-100 * time.Millisecond),
  108. steps: []step{
  109. {time: time.Now().Add(-80 * time.Millisecond), msg: "msg1"},
  110. {time: time.Now().Add(-50 * time.Millisecond), msg: "msg2"},
  111. },
  112. },
  113. expectedMsg: []string{
  114. "msg1", "msg2",
  115. },
  116. },
  117. {
  118. name: "When trace has fields",
  119. trace: &Trace{
  120. operation: "Test",
  121. startTime: time.Now().Add(-100 * time.Millisecond),
  122. steps: []step{
  123. {
  124. time: time.Now().Add(-80 * time.Millisecond),
  125. msg: "msg1",
  126. fields: []Field{{"stepKey1", "stepValue1"}},
  127. },
  128. {
  129. time: time.Now().Add(-50 * time.Millisecond),
  130. msg: "msg2",
  131. fields: []Field{{"stepKey2", "stepValue2"}},
  132. },
  133. },
  134. },
  135. fields: []Field{
  136. {"traceKey1", "traceValue1"},
  137. {"count", 1},
  138. },
  139. expectedMsg: []string{
  140. "Test",
  141. "msg1", "msg2",
  142. "traceKey1:traceValue1", "count:1",
  143. "stepKey1:stepValue1", "stepKey2:stepValue2",
  144. },
  145. },
  146. }
  147. for _, tt := range tests {
  148. t.Run(tt.name, func(t *testing.T) {
  149. logPath := filepath.Join(os.TempDir(), fmt.Sprintf("test-log-%d", time.Now().UnixNano()))
  150. defer os.RemoveAll(logPath)
  151. lcfg := zap.NewProductionConfig()
  152. lcfg.OutputPaths = []string{logPath}
  153. lcfg.ErrorOutputPaths = []string{logPath}
  154. lg, _ := lcfg.Build()
  155. for _, f := range tt.fields {
  156. tt.trace.AddField(f)
  157. }
  158. tt.trace.lg = lg
  159. tt.trace.Log()
  160. data, err := ioutil.ReadFile(logPath)
  161. if err != nil {
  162. t.Fatal(err)
  163. }
  164. for _, msg := range tt.expectedMsg {
  165. if !bytes.Contains(data, []byte(msg)) {
  166. t.Errorf("Expected to find %v in log", msg)
  167. }
  168. }
  169. })
  170. }
  171. }
  172. func TestLogIfLong(t *testing.T) {
  173. tests := []struct {
  174. name string
  175. threshold time.Duration
  176. trace *Trace
  177. expectedMsg []string
  178. }{
  179. {
  180. name: "When the duration is smaller than threshold",
  181. threshold: time.Duration(200 * time.Millisecond),
  182. trace: &Trace{
  183. operation: "Test",
  184. startTime: time.Now().Add(-100 * time.Millisecond),
  185. steps: []step{
  186. {time: time.Now().Add(-50 * time.Millisecond), msg: "msg1"},
  187. {time: time.Now(), msg: "msg2"},
  188. },
  189. },
  190. expectedMsg: []string{},
  191. },
  192. {
  193. name: "When the duration is longer than threshold",
  194. threshold: time.Duration(50 * time.Millisecond),
  195. trace: &Trace{
  196. operation: "Test",
  197. startTime: time.Now().Add(-100 * time.Millisecond),
  198. steps: []step{
  199. {time: time.Now().Add(-50 * time.Millisecond), msg: "msg1"},
  200. {time: time.Now(), msg: "msg2"},
  201. },
  202. },
  203. expectedMsg: []string{
  204. "msg1", "msg2",
  205. },
  206. },
  207. {
  208. name: "When not all steps are longer than step threshold",
  209. threshold: time.Duration(50 * time.Millisecond),
  210. trace: &Trace{
  211. operation: "Test",
  212. startTime: time.Now().Add(-100 * time.Millisecond),
  213. steps: []step{
  214. {time: time.Now(), msg: "msg1"},
  215. {time: time.Now(), msg: "msg2"},
  216. },
  217. },
  218. expectedMsg: []string{
  219. "msg1",
  220. },
  221. },
  222. }
  223. for _, tt := range tests {
  224. t.Run(tt.name, func(t *testing.T) {
  225. logPath := filepath.Join(os.TempDir(), fmt.Sprintf("test-log-%d", time.Now().UnixNano()))
  226. defer os.RemoveAll(logPath)
  227. lcfg := zap.NewProductionConfig()
  228. lcfg.OutputPaths = []string{logPath}
  229. lcfg.ErrorOutputPaths = []string{logPath}
  230. lg, _ := lcfg.Build()
  231. tt.trace.lg = lg
  232. tt.trace.LogIfLong(tt.threshold)
  233. data, err := ioutil.ReadFile(logPath)
  234. if err != nil {
  235. t.Fatal(err)
  236. }
  237. for _, msg := range tt.expectedMsg {
  238. if !bytes.Contains(data, []byte(msg)) {
  239. t.Errorf("Expected to find %v in log", msg)
  240. }
  241. }
  242. })
  243. }
  244. }