1
0

tracer_test.go 9.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356
  1. package trace
  2. import (
  3. "context"
  4. "testing"
  5. "github.com/stretchr/testify/assert"
  6. "github.com/stretchr/testify/require"
  7. "go.opentelemetry.io/otel"
  8. "go.opentelemetry.io/otel/propagation"
  9. "go.opentelemetry.io/otel/trace"
  10. "google.golang.org/grpc/metadata"
  11. )
  12. const (
  13. traceIDStr = "4bf92f3577b34da6a3ce929d0e0e4736"
  14. spanIDStr = "00f067aa0ba902b7"
  15. )
  16. var (
  17. traceID = mustTraceIDFromHex(traceIDStr)
  18. spanID = mustSpanIDFromHex(spanIDStr)
  19. )
  20. func mustTraceIDFromHex(s string) (t trace.TraceID) {
  21. var err error
  22. t, err = trace.TraceIDFromHex(s)
  23. if err != nil {
  24. panic(err)
  25. }
  26. return
  27. }
  28. func mustSpanIDFromHex(s string) (t trace.SpanID) {
  29. var err error
  30. t, err = trace.SpanIDFromHex(s)
  31. if err != nil {
  32. panic(err)
  33. }
  34. return
  35. }
  36. func TestExtractValidTraceContext(t *testing.T) {
  37. stateStr := "key1=value1,key2=value2"
  38. state, err := trace.ParseTraceState(stateStr)
  39. require.NoError(t, err)
  40. tests := []struct {
  41. name string
  42. traceparent string
  43. tracestate string
  44. sc trace.SpanContext
  45. }{
  46. {
  47. name: "not sampled",
  48. traceparent: "00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-00",
  49. sc: trace.NewSpanContext(trace.SpanContextConfig{
  50. TraceID: traceID,
  51. SpanID: spanID,
  52. Remote: true,
  53. }),
  54. },
  55. {
  56. name: "sampled",
  57. traceparent: "00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01",
  58. sc: trace.NewSpanContext(trace.SpanContextConfig{
  59. TraceID: traceID,
  60. SpanID: spanID,
  61. TraceFlags: trace.FlagsSampled,
  62. Remote: true,
  63. }),
  64. },
  65. {
  66. name: "valid tracestate",
  67. traceparent: "00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-00",
  68. tracestate: stateStr,
  69. sc: trace.NewSpanContext(trace.SpanContextConfig{
  70. TraceID: traceID,
  71. SpanID: spanID,
  72. TraceState: state,
  73. Remote: true,
  74. }),
  75. },
  76. {
  77. name: "invalid tracestate perserves traceparent",
  78. traceparent: "00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-00",
  79. tracestate: "invalid$@#=invalid",
  80. sc: trace.NewSpanContext(trace.SpanContextConfig{
  81. TraceID: traceID,
  82. SpanID: spanID,
  83. Remote: true,
  84. }),
  85. },
  86. {
  87. name: "future version not sampled",
  88. traceparent: "02-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-00",
  89. sc: trace.NewSpanContext(trace.SpanContextConfig{
  90. TraceID: traceID,
  91. SpanID: spanID,
  92. Remote: true,
  93. }),
  94. },
  95. {
  96. name: "future version sampled",
  97. traceparent: "02-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01",
  98. sc: trace.NewSpanContext(trace.SpanContextConfig{
  99. TraceID: traceID,
  100. SpanID: spanID,
  101. TraceFlags: trace.FlagsSampled,
  102. Remote: true,
  103. }),
  104. },
  105. {
  106. name: "future version sample bit set",
  107. traceparent: "02-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-09",
  108. sc: trace.NewSpanContext(trace.SpanContextConfig{
  109. TraceID: traceID,
  110. SpanID: spanID,
  111. TraceFlags: trace.FlagsSampled,
  112. Remote: true,
  113. }),
  114. },
  115. {
  116. name: "future version sample bit not set",
  117. traceparent: "02-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-08",
  118. sc: trace.NewSpanContext(trace.SpanContextConfig{
  119. TraceID: traceID,
  120. SpanID: spanID,
  121. Remote: true,
  122. }),
  123. },
  124. {
  125. name: "future version additional data",
  126. traceparent: "02-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-00-XYZxsf09",
  127. sc: trace.NewSpanContext(trace.SpanContextConfig{
  128. TraceID: traceID,
  129. SpanID: spanID,
  130. Remote: true,
  131. }),
  132. },
  133. {
  134. name: "B3 format ending in dash",
  135. traceparent: "00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-00-",
  136. sc: trace.NewSpanContext(trace.SpanContextConfig{
  137. TraceID: traceID,
  138. SpanID: spanID,
  139. Remote: true,
  140. }),
  141. },
  142. {
  143. name: "future version B3 format ending in dash",
  144. traceparent: "03-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-00-",
  145. sc: trace.NewSpanContext(trace.SpanContextConfig{
  146. TraceID: traceID,
  147. SpanID: spanID,
  148. Remote: true,
  149. }),
  150. },
  151. }
  152. otel.SetTextMapPropagator(propagation.NewCompositeTextMapPropagator(
  153. propagation.TraceContext{}, propagation.Baggage{}))
  154. propagator := otel.GetTextMapPropagator()
  155. for _, tt := range tests {
  156. t.Run(tt.name, func(t *testing.T) {
  157. ctx := context.Background()
  158. md := metadata.MD{}
  159. md.Set("traceparent", tt.traceparent)
  160. md.Set("tracestate", tt.tracestate)
  161. _, spanCtx := Extract(ctx, propagator, &md)
  162. assert.Equal(t, tt.sc, spanCtx)
  163. })
  164. }
  165. }
  166. func TestExtractInvalidTraceContext(t *testing.T) {
  167. tests := []struct {
  168. name string
  169. header string
  170. }{
  171. {
  172. name: "wrong version length",
  173. header: "0000-00000000000000000000000000000000-0000000000000000-01",
  174. },
  175. {
  176. name: "wrong trace ID length",
  177. header: "00-ab00000000000000000000000000000000-cd00000000000000-01",
  178. },
  179. {
  180. name: "wrong span ID length",
  181. header: "00-ab000000000000000000000000000000-cd0000000000000000-01",
  182. },
  183. {
  184. name: "wrong trace flag length",
  185. header: "00-ab000000000000000000000000000000-cd00000000000000-0100",
  186. },
  187. {
  188. name: "bogus version",
  189. header: "qw-00000000000000000000000000000000-0000000000000000-01",
  190. },
  191. {
  192. name: "bogus trace ID",
  193. header: "00-qw000000000000000000000000000000-cd00000000000000-01",
  194. },
  195. {
  196. name: "bogus span ID",
  197. header: "00-ab000000000000000000000000000000-qw00000000000000-01",
  198. },
  199. {
  200. name: "bogus trace flag",
  201. header: "00-ab000000000000000000000000000000-cd00000000000000-qw",
  202. },
  203. {
  204. name: "upper case version",
  205. header: "A0-00000000000000000000000000000000-0000000000000000-01",
  206. },
  207. {
  208. name: "upper case trace ID",
  209. header: "00-AB000000000000000000000000000000-cd00000000000000-01",
  210. },
  211. {
  212. name: "upper case span ID",
  213. header: "00-ab000000000000000000000000000000-CD00000000000000-01",
  214. },
  215. {
  216. name: "upper case trace flag",
  217. header: "00-ab000000000000000000000000000000-cd00000000000000-A1",
  218. },
  219. {
  220. name: "zero trace ID and span ID",
  221. header: "00-00000000000000000000000000000000-0000000000000000-01",
  222. },
  223. {
  224. name: "trace-flag unused bits set",
  225. header: "00-ab000000000000000000000000000000-cd00000000000000-09",
  226. },
  227. {
  228. name: "missing options",
  229. header: "00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7",
  230. },
  231. {
  232. name: "empty options",
  233. header: "00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-",
  234. },
  235. }
  236. otel.SetTextMapPropagator(propagation.NewCompositeTextMapPropagator(
  237. propagation.TraceContext{}, propagation.Baggage{}))
  238. propagator := otel.GetTextMapPropagator()
  239. for _, tt := range tests {
  240. t.Run(tt.name, func(t *testing.T) {
  241. ctx := context.Background()
  242. md := metadata.MD{}
  243. md.Set("traceparent", tt.header)
  244. _, spanCtx := Extract(ctx, propagator, &md)
  245. assert.Equal(t, trace.SpanContext{}, spanCtx)
  246. })
  247. }
  248. }
  249. func TestInjectValidTraceContext(t *testing.T) {
  250. stateStr := "key1=value1,key2=value2"
  251. state, err := trace.ParseTraceState(stateStr)
  252. require.NoError(t, err)
  253. tests := []struct {
  254. name string
  255. traceparent string
  256. tracestate string
  257. sc trace.SpanContext
  258. }{
  259. {
  260. name: "not sampled",
  261. traceparent: "00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-00",
  262. sc: trace.NewSpanContext(trace.SpanContextConfig{
  263. TraceID: traceID,
  264. SpanID: spanID,
  265. Remote: true,
  266. }),
  267. },
  268. {
  269. name: "sampled",
  270. traceparent: "00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01",
  271. sc: trace.NewSpanContext(trace.SpanContextConfig{
  272. TraceID: traceID,
  273. SpanID: spanID,
  274. TraceFlags: trace.FlagsSampled,
  275. Remote: true,
  276. }),
  277. },
  278. {
  279. name: "unsupported trace flag bits dropped",
  280. traceparent: "00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-01",
  281. sc: trace.NewSpanContext(trace.SpanContextConfig{
  282. TraceID: traceID,
  283. SpanID: spanID,
  284. TraceFlags: 0xff,
  285. Remote: true,
  286. }),
  287. },
  288. {
  289. name: "with tracestate",
  290. traceparent: "00-4bf92f3577b34da6a3ce929d0e0e4736-00f067aa0ba902b7-00",
  291. tracestate: stateStr,
  292. sc: trace.NewSpanContext(trace.SpanContextConfig{
  293. TraceID: traceID,
  294. SpanID: spanID,
  295. TraceState: state,
  296. Remote: true,
  297. }),
  298. },
  299. }
  300. otel.SetTextMapPropagator(propagation.NewCompositeTextMapPropagator(
  301. propagation.TraceContext{}, propagation.Baggage{}))
  302. propagator := otel.GetTextMapPropagator()
  303. for _, tt := range tests {
  304. t.Run(tt.name, func(t *testing.T) {
  305. ctx := context.Background()
  306. ctx = trace.ContextWithRemoteSpanContext(ctx, tt.sc)
  307. want := metadata.MD{}
  308. want.Set("traceparent", tt.traceparent)
  309. if len(tt.tracestate) > 0 {
  310. want.Set("tracestate", tt.tracestate)
  311. }
  312. md := metadata.MD{}
  313. Inject(ctx, propagator, &md)
  314. assert.Equal(t, want, md)
  315. mm := &metadataSupplier{
  316. metadata: &md,
  317. }
  318. assert.NotEmpty(t, mm.Keys())
  319. })
  320. }
  321. }
  322. func TestInvalidSpanContextDropped(t *testing.T) {
  323. invalidSC := trace.SpanContext{}
  324. require.False(t, invalidSC.IsValid())
  325. ctx := trace.ContextWithRemoteSpanContext(context.Background(), invalidSC)
  326. otel.SetTextMapPropagator(propagation.NewCompositeTextMapPropagator(
  327. propagation.TraceContext{}, propagation.Baggage{}))
  328. propagator := otel.GetTextMapPropagator()
  329. md := metadata.MD{}
  330. Inject(ctx, propagator, &md)
  331. mm := &metadataSupplier{
  332. metadata: &md,
  333. }
  334. assert.Empty(t, mm.Keys())
  335. assert.Equal(t, "", mm.Get("traceparent"), "injected invalid SpanContext")
  336. }