Netty40ConnectionSpanTest.groovy 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189
  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.HttpHeaders
  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.test.AgentTestTrait
  21. import io.opentelemetry.instrumentation.test.InstrumentationSpecification
  22. import io.opentelemetry.instrumentation.test.utils.PortUtils
  23. import io.opentelemetry.instrumentation.testing.junit.http.HttpClientTestServer
  24. import io.opentelemetry.semconv.trace.attributes.SemanticAttributes
  25. import spock.lang.Shared
  26. import java.util.concurrent.CompletableFuture
  27. import java.util.concurrent.TimeUnit
  28. import static io.opentelemetry.api.trace.SpanKind.CLIENT
  29. import static io.opentelemetry.api.trace.SpanKind.INTERNAL
  30. import static io.opentelemetry.api.trace.SpanKind.SERVER
  31. import static io.opentelemetry.api.trace.StatusCode.ERROR
  32. import static io.opentelemetry.semconv.trace.attributes.SemanticAttributes.NetTransportValues.IP_TCP
  33. class Netty40ConnectionSpanTest extends InstrumentationSpecification implements AgentTestTrait {
  34. @Shared
  35. private HttpClientTestServer server
  36. @Shared
  37. private EventLoopGroup eventLoopGroup = new NioEventLoopGroup()
  38. @Shared
  39. private Bootstrap bootstrap = buildBootstrap()
  40. def setupSpec() {
  41. server = new HttpClientTestServer(openTelemetry)
  42. server.start()
  43. }
  44. def cleanupSpec() {
  45. eventLoopGroup.shutdownGracefully()
  46. server.stop()
  47. }
  48. Bootstrap buildBootstrap() {
  49. Bootstrap bootstrap = new Bootstrap()
  50. bootstrap.group(eventLoopGroup)
  51. .channel(NioSocketChannel)
  52. .handler(new ChannelInitializer<SocketChannel>() {
  53. @Override
  54. protected void initChannel(SocketChannel socketChannel) throws Exception {
  55. ChannelPipeline pipeline = socketChannel.pipeline()
  56. pipeline.addLast(new HttpClientCodec())
  57. }
  58. })
  59. return bootstrap
  60. }
  61. DefaultFullHttpRequest buildRequest(String method, URI uri, Map<String, String> headers) {
  62. def request = new DefaultFullHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.valueOf(method), uri.path, Unpooled.EMPTY_BUFFER)
  63. HttpHeaders.setHost(request, uri.host + ":" + uri.port)
  64. headers.each { k, v -> request.headers().set(k, v) }
  65. return request
  66. }
  67. int sendRequest(DefaultFullHttpRequest request, URI uri) {
  68. def channel = bootstrap.connect(uri.host, uri.port).sync().channel()
  69. def result = new CompletableFuture<Integer>()
  70. channel.pipeline().addLast(new ClientHandler(result))
  71. channel.writeAndFlush(request).get()
  72. return result.get(20, TimeUnit.SECONDS)
  73. }
  74. def "test successful request"() {
  75. when:
  76. def uri = URI.create("http://localhost:${server.httpPort()}/success")
  77. def request = buildRequest("GET", uri, [:])
  78. def responseCode = runWithSpan("parent") {
  79. sendRequest(request, uri)
  80. }
  81. then:
  82. responseCode == 200
  83. assertTraces(1) {
  84. trace(0, 4) {
  85. span(0) {
  86. name "parent"
  87. kind INTERNAL
  88. hasNoParent()
  89. }
  90. span(1) {
  91. name "CONNECT"
  92. kind INTERNAL
  93. childOf(span(0))
  94. if (SemconvStability.emitOldHttpSemconv()) {
  95. attributes {
  96. "$SemanticAttributes.NET_TRANSPORT" IP_TCP
  97. "$SemanticAttributes.NET_PEER_NAME" uri.host
  98. "$SemanticAttributes.NET_PEER_PORT" uri.port
  99. "$SemanticAttributes.NET_SOCK_PEER_ADDR" "127.0.0.1"
  100. }
  101. }
  102. if (SemconvStability.emitStableHttpSemconv()) {
  103. attributes {
  104. "$NetworkAttributes.NETWORK_TRANSPORT" "tcp"
  105. "$NetworkAttributes.NETWORK_TYPE" "ipv4"
  106. "$NetworkAttributes.SERVER_ADDRESS" uri.host
  107. "$NetworkAttributes.SERVER_PORT" uri.port
  108. "$NetworkAttributes.SERVER_SOCKET_ADDRESS" "127.0.0.1"
  109. }
  110. }
  111. }
  112. span(2) {
  113. name "GET"
  114. kind CLIENT
  115. childOf(span(0))
  116. }
  117. span(3) {
  118. name "test-http-server"
  119. kind SERVER
  120. childOf(span(2))
  121. }
  122. }
  123. }
  124. }
  125. def "test failing request"() {
  126. when:
  127. URI uri = URI.create("http://localhost:${PortUtils.UNUSABLE_PORT}")
  128. def request = buildRequest("GET", uri, [:])
  129. runWithSpan("parent") {
  130. sendRequest(request, uri)
  131. }
  132. then:
  133. def thrownException = thrown(Exception)
  134. and:
  135. assertTraces(1) {
  136. trace(0, 2) {
  137. span(0) {
  138. name "parent"
  139. kind INTERNAL
  140. hasNoParent()
  141. status ERROR
  142. errorEvent(thrownException.class, thrownException.message)
  143. }
  144. span(1) {
  145. name "CONNECT"
  146. kind INTERNAL
  147. childOf(span(0))
  148. status ERROR
  149. errorEvent(thrownException.class, thrownException.message)
  150. if (SemconvStability.emitOldHttpSemconv()) {
  151. attributes {
  152. "$SemanticAttributes.NET_TRANSPORT" IP_TCP
  153. "$SemanticAttributes.NET_PEER_NAME" uri.host
  154. "$SemanticAttributes.NET_PEER_PORT" uri.port
  155. "$SemanticAttributes.NET_SOCK_PEER_ADDR" { it == "127.0.0.1" || it == null }
  156. }
  157. }
  158. if (SemconvStability.emitStableHttpSemconv()) {
  159. attributes {
  160. "$NetworkAttributes.NETWORK_TRANSPORT" "tcp"
  161. "$NetworkAttributes.NETWORK_TYPE" { it == "ipv4" || it == null }
  162. "$NetworkAttributes.SERVER_ADDRESS" uri.host
  163. "$NetworkAttributes.SERVER_PORT" uri.port
  164. "$NetworkAttributes.SERVER_SOCKET_ADDRESS" { it == "127.0.0.1" || it == null }
  165. }
  166. }
  167. }
  168. }
  169. }
  170. }
  171. }