Netty41ConnectionSpanTest.groovy 7.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230
  1. /*
  2. * Copyright The OpenTelemetry Authors
  3. * SPDX-License-Identifier: Apache-2.0
  4. */
  5. import io.netty.bootstrap.Bootstrap
  6. import io.netty.buffer.Unpooled
  7. import io.netty.channel.ChannelInitializer
  8. import io.netty.channel.ChannelPipeline
  9. import io.netty.channel.EventLoopGroup
  10. import io.netty.channel.nio.NioEventLoopGroup
  11. import io.netty.channel.socket.SocketChannel
  12. import io.netty.channel.socket.nio.NioSocketChannel
  13. import io.netty.handler.codec.http.DefaultFullHttpRequest
  14. import io.netty.handler.codec.http.HttpClientCodec
  15. import io.netty.handler.codec.http.HttpHeaderNames
  16. import io.netty.handler.codec.http.HttpMethod
  17. import io.netty.handler.codec.http.HttpVersion
  18. import io.opentelemetry.instrumentation.api.instrumenter.network.internal.NetworkAttributes
  19. import io.opentelemetry.instrumentation.api.internal.SemconvStability
  20. import io.opentelemetry.instrumentation.netty.v4_1.ClientHandler
  21. import io.opentelemetry.instrumentation.test.AgentTestTrait
  22. import io.opentelemetry.instrumentation.test.InstrumentationSpecification
  23. import io.opentelemetry.instrumentation.test.utils.PortUtils
  24. import io.opentelemetry.instrumentation.testing.junit.http.HttpClientTestServer
  25. import io.opentelemetry.semconv.trace.attributes.SemanticAttributes
  26. import spock.lang.Shared
  27. import java.util.concurrent.CompletableFuture
  28. import java.util.concurrent.TimeUnit
  29. import static io.opentelemetry.api.trace.SpanKind.CLIENT
  30. import static io.opentelemetry.api.trace.SpanKind.INTERNAL
  31. import static io.opentelemetry.api.trace.SpanKind.SERVER
  32. import static io.opentelemetry.api.trace.StatusCode.ERROR
  33. import static io.opentelemetry.semconv.trace.attributes.SemanticAttributes.NetTransportValues.IP_TCP
  34. class Netty41ConnectionSpanTest extends InstrumentationSpecification implements AgentTestTrait {
  35. @Shared
  36. private HttpClientTestServer server
  37. @Shared
  38. private EventLoopGroup eventLoopGroup = new NioEventLoopGroup()
  39. @Shared
  40. private Bootstrap bootstrap = buildBootstrap()
  41. def setupSpec() {
  42. server = new HttpClientTestServer(openTelemetry)
  43. server.start()
  44. }
  45. def cleanupSpec() {
  46. eventLoopGroup.shutdownGracefully()
  47. server.stop()
  48. }
  49. Bootstrap buildBootstrap() {
  50. Bootstrap bootstrap = new Bootstrap()
  51. bootstrap.group(eventLoopGroup)
  52. .channel(NioSocketChannel)
  53. .handler(new ChannelInitializer<SocketChannel>() {
  54. @Override
  55. protected void initChannel(SocketChannel socketChannel) throws Exception {
  56. ChannelPipeline pipeline = socketChannel.pipeline()
  57. pipeline.addLast(new HttpClientCodec())
  58. }
  59. })
  60. return bootstrap
  61. }
  62. DefaultFullHttpRequest buildRequest(String method, URI uri, Map<String, String> headers) {
  63. def request = new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.valueOf(method), uri.path, Unpooled.EMPTY_BUFFER)
  64. request.headers().set(HttpHeaderNames.HOST, uri.host + ":" + uri.port)
  65. headers.each { k, v -> request.headers().set(k, v) }
  66. return request
  67. }
  68. int sendRequest(DefaultFullHttpRequest request, URI uri) {
  69. def channel = bootstrap.connect(uri.host, uri.port).sync().channel()
  70. def result = new CompletableFuture<Integer>()
  71. channel.pipeline().addLast(new ClientHandler(result))
  72. channel.writeAndFlush(request).get()
  73. return result.get(20, TimeUnit.SECONDS)
  74. }
  75. def "test successful request"() {
  76. when:
  77. def uri = URI.create("http://localhost:${server.httpPort()}/success")
  78. def request = buildRequest("GET", uri, [:])
  79. def responseCode = runWithSpan("parent") {
  80. sendRequest(request, uri)
  81. }
  82. then:
  83. responseCode == 200
  84. assertTraces(1) {
  85. trace(0, 5) {
  86. def list = Arrays.asList("RESOLVE", "CONNECT")
  87. spans.subList(1, 3).sort(Comparator.comparing { item -> list.indexOf(item.name) })
  88. span(0) {
  89. name "parent"
  90. kind INTERNAL
  91. hasNoParent()
  92. }
  93. span(1) {
  94. name "RESOLVE"
  95. kind INTERNAL
  96. childOf span(0)
  97. if (SemconvStability.emitOldHttpSemconv()) {
  98. attributes {
  99. "$SemanticAttributes.NET_TRANSPORT" IP_TCP
  100. "$SemanticAttributes.NET_PEER_NAME" uri.host
  101. "$SemanticAttributes.NET_PEER_PORT" uri.port
  102. }
  103. }
  104. if (SemconvStability.emitStableHttpSemconv()) {
  105. attributes {
  106. "$NetworkAttributes.SERVER_ADDRESS" uri.host
  107. "$NetworkAttributes.SERVER_PORT" uri.port
  108. }
  109. }
  110. }
  111. span(2) {
  112. name "CONNECT"
  113. kind INTERNAL
  114. childOf(span(0))
  115. if (SemconvStability.emitOldHttpSemconv()) {
  116. attributes {
  117. "$SemanticAttributes.NET_TRANSPORT" IP_TCP
  118. "$SemanticAttributes.NET_PEER_NAME" uri.host
  119. "$SemanticAttributes.NET_PEER_PORT" uri.port
  120. "$SemanticAttributes.NET_SOCK_PEER_ADDR" "127.0.0.1"
  121. }
  122. }
  123. if (SemconvStability.emitStableHttpSemconv()) {
  124. attributes {
  125. "$NetworkAttributes.NETWORK_TRANSPORT" "tcp"
  126. "$NetworkAttributes.NETWORK_TYPE" "ipv4"
  127. "$NetworkAttributes.SERVER_ADDRESS" uri.host
  128. "$NetworkAttributes.SERVER_PORT" uri.port
  129. "$NetworkAttributes.SERVER_SOCKET_ADDRESS" "127.0.0.1"
  130. }
  131. }
  132. }
  133. span(3) {
  134. name "GET"
  135. kind CLIENT
  136. childOf(span(0))
  137. }
  138. span(4) {
  139. name "test-http-server"
  140. kind SERVER
  141. childOf(span(3))
  142. }
  143. }
  144. }
  145. }
  146. def "test failing request"() {
  147. when:
  148. URI uri = URI.create("http://localhost:${PortUtils.UNUSABLE_PORT}")
  149. def request = buildRequest("GET", uri, [:])
  150. runWithSpan("parent") {
  151. sendRequest(request, uri)
  152. }
  153. then:
  154. def thrownException = thrown(Exception)
  155. and:
  156. assertTraces(1) {
  157. trace(0, 3) {
  158. def list = Arrays.asList("RESOLVE", "CONNECT")
  159. spans.subList(1, 3).sort(Comparator.comparing { item -> list.indexOf(item.name) })
  160. span(0) {
  161. name "parent"
  162. kind INTERNAL
  163. hasNoParent()
  164. status ERROR
  165. errorEvent(thrownException.class, thrownException.message)
  166. }
  167. span(1) {
  168. name "RESOLVE"
  169. kind INTERNAL
  170. childOf span(0)
  171. if (SemconvStability.emitOldHttpSemconv()) {
  172. attributes {
  173. "$SemanticAttributes.NET_TRANSPORT" IP_TCP
  174. "$SemanticAttributes.NET_PEER_NAME" uri.host
  175. "$SemanticAttributes.NET_PEER_PORT" uri.port
  176. }
  177. }
  178. if (SemconvStability.emitStableHttpSemconv()) {
  179. attributes {
  180. "$NetworkAttributes.SERVER_ADDRESS" uri.host
  181. "$NetworkAttributes.SERVER_PORT" uri.port
  182. }
  183. }
  184. }
  185. span(2) {
  186. name "CONNECT"
  187. kind INTERNAL
  188. childOf span(0)
  189. status ERROR
  190. errorEvent(thrownException.class, thrownException.message)
  191. if (SemconvStability.emitOldHttpSemconv()) {
  192. attributes {
  193. "$SemanticAttributes.NET_TRANSPORT" IP_TCP
  194. "$SemanticAttributes.NET_PEER_NAME" uri.host
  195. "$SemanticAttributes.NET_PEER_PORT" uri.port
  196. "$SemanticAttributes.NET_SOCK_PEER_ADDR" { it == "127.0.0.1" || it == null }
  197. }
  198. }
  199. if (SemconvStability.emitStableHttpSemconv()) {
  200. attributes {
  201. "$NetworkAttributes.NETWORK_TRANSPORT" "tcp"
  202. "$NetworkAttributes.NETWORK_TYPE" { it == "ipv4" || it == null }
  203. "$NetworkAttributes.SERVER_ADDRESS" uri.host
  204. "$NetworkAttributes.SERVER_PORT" uri.port
  205. "$NetworkAttributes.SERVER_SOCKET_ADDRESS" { it == "127.0.0.1" || it == null }
  206. }
  207. }
  208. }
  209. }
  210. }
  211. }
  212. }