From 554d90f811fc43d3ef2bebe63ebb19df2eb74c77 Mon Sep 17 00:00:00 2001 From: Kaushal Agrawal Date: Wed, 1 Feb 2023 16:46:04 +0530 Subject: [PATCH 1/7] Add servlet 2.2 instrumentation --- instrumentation/build.gradle.kts | 1 + .../servlet/servlet-2.2/build.gradle.kts | 49 +++ .../Servlet2AndFilterInstrumentation.java | 226 +++++++++++ .../Servlet2InstrumentationModule.java | 51 +++ .../Servlet2InstrumentationName.java | 24 ++ .../servlet/v2_2/nowrapping/Utils.java | 124 ++++++ .../ServletInputStreamInstrumentation.java | 360 ++++++++++++++++++ .../ServletRequestInstrumentation.java | 284 ++++++++++++++ .../v2_2/nowrapping/request/Utils.java | 78 ++++ .../ServletOutputStreamInstrumentation.java | 230 +++++++++++ .../ServletResponseInstrumentation.java | 194 ++++++++++ .../Servlet30AndFilterInstrumentation.java | 2 + .../Servlet30InstrumentationModule.java | 1 + .../Servlet30InstrumentationName.java | 8 +- .../servlet/v3_0/nowrapping/Utils.java | 6 +- .../async/Servlet30AsyncInstrumentation.java | 1 + .../ServletInputStreamInstrumentation.java | 16 +- .../ServletRequestInstrumentation.java | 11 +- .../v3_0/nowrapping/request/Utils.java | 8 +- .../ServletOutputStreamInstrumentation.java | 6 +- .../ServletResponseInstrumentation.java | 2 + settings.gradle.kts | 2 + 22 files changed, 1657 insertions(+), 27 deletions(-) create mode 100644 instrumentation/servlet/servlet-2.2/build.gradle.kts create mode 100644 instrumentation/servlet/servlet-2.2/src/main/java/io/opentelemetry/javaagent/instrumentation/hypertrace/servlet/v2_2/nowrapping/Servlet2AndFilterInstrumentation.java create mode 100644 instrumentation/servlet/servlet-2.2/src/main/java/io/opentelemetry/javaagent/instrumentation/hypertrace/servlet/v2_2/nowrapping/Servlet2InstrumentationModule.java create mode 100644 instrumentation/servlet/servlet-2.2/src/main/java/io/opentelemetry/javaagent/instrumentation/hypertrace/servlet/v2_2/nowrapping/Servlet2InstrumentationName.java create mode 100644 instrumentation/servlet/servlet-2.2/src/main/java/io/opentelemetry/javaagent/instrumentation/hypertrace/servlet/v2_2/nowrapping/Utils.java create mode 100644 instrumentation/servlet/servlet-2.2/src/main/java/io/opentelemetry/javaagent/instrumentation/hypertrace/servlet/v2_2/nowrapping/request/ServletInputStreamInstrumentation.java create mode 100644 instrumentation/servlet/servlet-2.2/src/main/java/io/opentelemetry/javaagent/instrumentation/hypertrace/servlet/v2_2/nowrapping/request/ServletRequestInstrumentation.java create mode 100644 instrumentation/servlet/servlet-2.2/src/main/java/io/opentelemetry/javaagent/instrumentation/hypertrace/servlet/v2_2/nowrapping/request/Utils.java create mode 100644 instrumentation/servlet/servlet-2.2/src/main/java/io/opentelemetry/javaagent/instrumentation/hypertrace/servlet/v2_2/nowrapping/response/ServletOutputStreamInstrumentation.java create mode 100644 instrumentation/servlet/servlet-2.2/src/main/java/io/opentelemetry/javaagent/instrumentation/hypertrace/servlet/v2_2/nowrapping/response/ServletResponseInstrumentation.java diff --git a/instrumentation/build.gradle.kts b/instrumentation/build.gradle.kts index 065d5736b..e722ffe74 100644 --- a/instrumentation/build.gradle.kts +++ b/instrumentation/build.gradle.kts @@ -6,6 +6,7 @@ plugins { dependencies{ implementation(project(":instrumentation:servlet:servlet-rw")) implementation(project(":instrumentation:servlet:servlet-3.0")) + implementation(project(":instrumentation:servlet:servlet-2.2")) implementation(project(":instrumentation:spark-2.3")) implementation(project(":instrumentation:grpc-1.6")) implementation(project(":instrumentation:grpc-shaded-netty-1.9")) diff --git a/instrumentation/servlet/servlet-2.2/build.gradle.kts b/instrumentation/servlet/servlet-2.2/build.gradle.kts new file mode 100644 index 000000000..f5e1af80e --- /dev/null +++ b/instrumentation/servlet/servlet-2.2/build.gradle.kts @@ -0,0 +1,49 @@ +plugins { + `java-library` + id("net.bytebuddy.byte-buddy") + id("io.opentelemetry.instrumentation.auto-instrumentation") + muzzle +} + +muzzle { + pass { + group = "javax.servlet" + module = "servlet-api" + versions = "[2.2, 3.0)" + assertInverse = true + } + + fail { + group = "javax.servlet" + module = "javax.servlet-api" + versions = "[3.0,)" + } +} + +afterEvaluate{ + io.opentelemetry.instrumentation.gradle.bytebuddy.ByteBuddyPluginConfigurator( + project, + sourceSets.main.get(), + io.opentelemetry.javaagent.tooling.muzzle.generation.MuzzleCodeGenerationPlugin::class.java.name, + project(":javaagent-tooling").configurations["instrumentationMuzzle"] + configurations.runtimeClasspath + ).configure() +} + +val versions: Map by extra + +dependencies { + implementation("io.opentelemetry.javaagent.instrumentation:opentelemetry-javaagent-servlet-common:${versions["opentelemetry_java_agent"]}") + implementation("io.opentelemetry.javaagent.instrumentation:opentelemetry-javaagent-servlet-2.2:${versions["opentelemetry_java_agent"]}") // Servlet2Accessor + compileOnly("io.opentelemetry.javaagent:opentelemetry-javaagent-bootstrap:${versions["opentelemetry_java_agent"]}") + testRuntimeOnly("io.opentelemetry.javaagent.instrumentation:opentelemetry-javaagent-servlet-common-bootstrap:${versions["opentelemetry_java_agent"]}") + muzzleBootstrap("io.opentelemetry.javaagent.instrumentation:opentelemetry-javaagent-servlet-common-bootstrap:${versions["opentelemetry_java_agent"]}") + compileOnly("javax.servlet:servlet-api:2.2") + +// testInstrumentation(project(":instrumentation:servlet:servlet-3.0:javaagent")) +// +// testLibrary("org.eclipse.jetty:jetty-server:7.0.0.v20091005") +// testLibrary("org.eclipse.jetty:jetty-servlet:7.0.0.v20091005") +// +// latestDepTestLibrary("org.eclipse.jetty:jetty-server:7.+") // see servlet-3.0 module +// latestDepTestLibrary("org.eclipse.jetty:jetty-servlet:7.+") // see servlet-3.0 module +} diff --git a/instrumentation/servlet/servlet-2.2/src/main/java/io/opentelemetry/javaagent/instrumentation/hypertrace/servlet/v2_2/nowrapping/Servlet2AndFilterInstrumentation.java b/instrumentation/servlet/servlet-2.2/src/main/java/io/opentelemetry/javaagent/instrumentation/hypertrace/servlet/v2_2/nowrapping/Servlet2AndFilterInstrumentation.java new file mode 100644 index 000000000..1662ea29b --- /dev/null +++ b/instrumentation/servlet/servlet-2.2/src/main/java/io/opentelemetry/javaagent/instrumentation/hypertrace/servlet/v2_2/nowrapping/Servlet2AndFilterInstrumentation.java @@ -0,0 +1,226 @@ +/* + * Copyright The Hypertrace Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.opentelemetry.javaagent.instrumentation.hypertrace.servlet.v2_2.nowrapping; + +import static io.opentelemetry.javaagent.extension.matcher.AgentElementMatchers.hasClassesNamed; +import static io.opentelemetry.javaagent.extension.matcher.AgentElementMatchers.hasSuperType; +import static net.bytebuddy.matcher.ElementMatchers.isPublic; +import static net.bytebuddy.matcher.ElementMatchers.named; +import static net.bytebuddy.matcher.ElementMatchers.takesArgument; + +import io.opentelemetry.api.common.AttributeKey; +import io.opentelemetry.api.trace.Span; +import io.opentelemetry.instrumentation.api.field.VirtualField; +import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation; +import io.opentelemetry.javaagent.extension.instrumentation.TypeTransformer; +import io.opentelemetry.javaagent.instrumentation.api.Java8BytecodeBridge; +import java.io.BufferedReader; +import java.io.PrintWriter; +import java.util.Collections; +import java.util.Enumeration; +import java.util.HashMap; +import java.util.Map; +import javax.servlet.ServletInputStream; +import javax.servlet.ServletOutputStream; +import javax.servlet.ServletRequest; +import javax.servlet.ServletResponse; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import net.bytebuddy.asm.Advice; +import net.bytebuddy.description.type.TypeDescription; +import net.bytebuddy.matcher.ElementMatcher; +import org.hypertrace.agent.core.config.InstrumentationConfig; +import org.hypertrace.agent.core.instrumentation.HypertraceCallDepthThreadLocalMap; +import org.hypertrace.agent.core.instrumentation.HypertraceEvaluationException; +import org.hypertrace.agent.core.instrumentation.HypertraceSemanticAttributes; +import org.hypertrace.agent.core.instrumentation.SpanAndObjectPair; +import org.hypertrace.agent.core.instrumentation.buffer.*; +import org.hypertrace.agent.core.instrumentation.utils.ContentTypeUtils; +import org.hypertrace.agent.filter.FilterRegistry; + +public class Servlet2AndFilterInstrumentation implements TypeInstrumentation { + + @Override + public ElementMatcher classLoaderOptimization() { + return hasClassesNamed("javax.servlet.Servlet"); + } + + @Override + public ElementMatcher typeMatcher() { + return hasSuperType(named("javax.servlet.Servlet")); + } + + @Override + public void transform(TypeTransformer transformer) { + transformer.applyAdviceToMethod( + named("service") + .and(takesArgument(0, named("javax.servlet.ServletRequest"))) + .and(takesArgument(1, named("javax.servlet.ServletResponse"))) + .and(isPublic()), + Servlet2AndFilterInstrumentation.class.getName() + "$ServletAdvice"); + } + + @SuppressWarnings("unused") + public static class ServletAdvice { + + @Advice.OnMethodEnter(suppress = Throwable.class, skipOn = Advice.OnNonDefaultValue.class) + public static boolean start( + @Advice.Argument(value = 0) ServletRequest request, + @Advice.Argument(value = 1) ServletResponse response, + @Advice.Local("currentSpan") Span currentSpan) { + System.out.println("start Enter javax.servlet.Servlet.service"); + int callDepth = + HypertraceCallDepthThreadLocalMap.incrementCallDepth(Servlet2InstrumentationName.class); + if (callDepth > 0) { + System.out.println("end1 Enter javax.servlet.Servlet.service"); + return false; + } + if (!(request instanceof HttpServletRequest) || !(response instanceof HttpServletResponse)) { + System.out.println("end2 Enter javax.servlet.Servlet.service"); + return false; + } + + HttpServletRequest httpRequest = (HttpServletRequest) request; + HttpServletResponse httpResponse = (HttpServletResponse) response; + currentSpan = Java8BytecodeBridge.currentSpan(); + + InstrumentationConfig instrumentationConfig = InstrumentationConfig.ConfigProvider.get(); + + Utils.addSessionId(currentSpan, httpRequest); + + // set request headers + Enumeration headerNames = httpRequest.getHeaderNames(); + Map headers = new HashMap<>(); + while (headerNames.hasMoreElements()) { + String headerName; + try { + headerName = (String) headerNames.nextElement(); + } catch (Exception e) { + continue; + } + String headerValue = httpRequest.getHeader(headerName); + AttributeKey attributeKey = + HypertraceSemanticAttributes.httpRequestHeader(headerName); + + if (instrumentationConfig.httpHeaders().request()) { + currentSpan.setAttribute(attributeKey, headerValue); + } + headers.put(attributeKey.getKey(), headerValue); + } + + if (FilterRegistry.getFilter().evaluateRequestHeaders(currentSpan, headers)) { + httpResponse.setStatus(403); + // skip execution of the user code + System.out.println("end3 Enter javax.servlet.Servlet.service"); + return true; + } + + if (instrumentationConfig.httpBody().request() + && ContentTypeUtils.shouldCapture(httpRequest.getContentType())) { + // The HttpServletRequest instrumentation uses this to + // enable the instrumentation + VirtualField.find(HttpServletRequest.class, SpanAndObjectPair.class) + .set( + httpRequest, + new SpanAndObjectPair(currentSpan, Collections.unmodifiableMap(headers))); + } + System.out.println("end Enter javax.servlet.Servlet.service"); + return false; + } + + @Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class) + public static void exit( + @Advice.Argument(0) ServletRequest request, + @Advice.Argument(1) ServletResponse response, + @Advice.Thrown(readOnly = false) Throwable throwable, + @Advice.Local("currentSpan") Span currentSpan) { + System.out.println("start Exit javax.servlet.Servlet.service"); + int callDepth = + HypertraceCallDepthThreadLocalMap.decrementCallDepth(Servlet2InstrumentationName.class); + if (callDepth > 0) { + System.out.println("end1 Exit javax.servlet.Servlet.service"); + return; + } + // we are in the most outermost level of Servlet instrumentation + + if (!(request instanceof HttpServletRequest) || !(response instanceof HttpServletResponse)) { + System.out.println("end2 Exit javax.servlet.Servlet.service"); + return; + } + + HttpServletResponse httpResponse = (HttpServletResponse) response; + HttpServletRequest httpRequest = (HttpServletRequest) request; + InstrumentationConfig instrumentationConfig = InstrumentationConfig.ConfigProvider.get(); + + try { + // response context to capture body and clear the context + VirtualField responseContextStore = + VirtualField.find(HttpServletResponse.class, SpanAndObjectPair.class); + VirtualField outputStreamContextStore = + VirtualField.find(ServletOutputStream.class, BoundedByteArrayOutputStream.class); + VirtualField writerContextStore = + VirtualField.find(PrintWriter.class, BoundedCharArrayWriter.class); + + // request context to clear body buffer + VirtualField requestContextStore = + VirtualField.find(HttpServletRequest.class, SpanAndObjectPair.class); + VirtualField inputStreamContextStore = + VirtualField.find(ServletInputStream.class, ByteBufferSpanPair.class); + VirtualField readerContextStore = + VirtualField.find(BufferedReader.class, CharBufferSpanPair.class); + VirtualField urlEncodedMapContextStore = + VirtualField.find(HttpServletRequest.class, StringMapSpanPair.class); + + // capture response body + // TODO: capture response headers + // TODO: capture body based on response content type + if (instrumentationConfig.httpBody().response()) { + Utils.captureResponseBody( + currentSpan, + httpResponse, + responseContextStore, + outputStreamContextStore, + writerContextStore); + } + + // remove request body buffers from context stores, otherwise they might get reused + if (instrumentationConfig.httpBody().request() + && ContentTypeUtils.shouldCapture(httpRequest.getContentType())) { + Utils.resetRequestBodyBuffers( + httpRequest, + requestContextStore, + inputStreamContextStore, + readerContextStore, + urlEncodedMapContextStore); + } + } finally { + Throwable tmp = throwable; + while (tmp != null) { // loop in case our exception is nested (eg. springframework) + if (tmp instanceof HypertraceEvaluationException) { + httpResponse.setStatus(403); + // bytebuddy treats the reassignment of this variable to null as an instruction to + // suppress this exception, which is what we want + throwable = null; + break; + } + tmp = tmp.getCause(); + } + } + System.out.println("end Exit javax.servlet.Servlet.service"); + } + } +} diff --git a/instrumentation/servlet/servlet-2.2/src/main/java/io/opentelemetry/javaagent/instrumentation/hypertrace/servlet/v2_2/nowrapping/Servlet2InstrumentationModule.java b/instrumentation/servlet/servlet-2.2/src/main/java/io/opentelemetry/javaagent/instrumentation/hypertrace/servlet/v2_2/nowrapping/Servlet2InstrumentationModule.java new file mode 100644 index 000000000..efb8603da --- /dev/null +++ b/instrumentation/servlet/servlet-2.2/src/main/java/io/opentelemetry/javaagent/instrumentation/hypertrace/servlet/v2_2/nowrapping/Servlet2InstrumentationModule.java @@ -0,0 +1,51 @@ +/* + * Copyright The Hypertrace Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.opentelemetry.javaagent.instrumentation.hypertrace.servlet.v2_2.nowrapping; + +import com.google.auto.service.AutoService; +import io.opentelemetry.javaagent.extension.instrumentation.InstrumentationModule; +import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation; +import io.opentelemetry.javaagent.instrumentation.hypertrace.servlet.v2_2.nowrapping.request.ServletInputStreamInstrumentation; +import io.opentelemetry.javaagent.instrumentation.hypertrace.servlet.v2_2.nowrapping.request.ServletRequestInstrumentation; +import io.opentelemetry.javaagent.instrumentation.hypertrace.servlet.v2_2.nowrapping.response.ServletOutputStreamInstrumentation; +import io.opentelemetry.javaagent.instrumentation.hypertrace.servlet.v2_2.nowrapping.response.ServletResponseInstrumentation; +import java.util.Arrays; +import java.util.List; + +@SuppressWarnings("unused") +@AutoService(InstrumentationModule.class) +public class Servlet2InstrumentationModule extends InstrumentationModule { + + public Servlet2InstrumentationModule() { + super(Servlet2InstrumentationName.PRIMARY, Servlet2InstrumentationName.OTHER); + } + + @Override + public int order() { + return 1; + } + + @Override + public List typeInstrumentations() { + return Arrays.asList( + new Servlet2AndFilterInstrumentation(), + new ServletRequestInstrumentation(), + new ServletInputStreamInstrumentation(), + new ServletResponseInstrumentation(), + new ServletOutputStreamInstrumentation()); + } +} diff --git a/instrumentation/servlet/servlet-2.2/src/main/java/io/opentelemetry/javaagent/instrumentation/hypertrace/servlet/v2_2/nowrapping/Servlet2InstrumentationName.java b/instrumentation/servlet/servlet-2.2/src/main/java/io/opentelemetry/javaagent/instrumentation/hypertrace/servlet/v2_2/nowrapping/Servlet2InstrumentationName.java new file mode 100644 index 000000000..07fb923cb --- /dev/null +++ b/instrumentation/servlet/servlet-2.2/src/main/java/io/opentelemetry/javaagent/instrumentation/hypertrace/servlet/v2_2/nowrapping/Servlet2InstrumentationName.java @@ -0,0 +1,24 @@ +/* + * Copyright The Hypertrace Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.opentelemetry.javaagent.instrumentation.hypertrace.servlet.v2_2.nowrapping; + +public class Servlet2InstrumentationName { + public static final String PRIMARY = "servlet"; + public static final String[] OTHER = { + "servlet-2", "ht", "servlet-ht", "servlet-2-ht", "servlet-2-no-wrapping", "servlet-no-wrapping" + }; +} diff --git a/instrumentation/servlet/servlet-2.2/src/main/java/io/opentelemetry/javaagent/instrumentation/hypertrace/servlet/v2_2/nowrapping/Utils.java b/instrumentation/servlet/servlet-2.2/src/main/java/io/opentelemetry/javaagent/instrumentation/hypertrace/servlet/v2_2/nowrapping/Utils.java new file mode 100644 index 000000000..fd6baad07 --- /dev/null +++ b/instrumentation/servlet/servlet-2.2/src/main/java/io/opentelemetry/javaagent/instrumentation/hypertrace/servlet/v2_2/nowrapping/Utils.java @@ -0,0 +1,124 @@ +/* + * Copyright The Hypertrace Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.opentelemetry.javaagent.instrumentation.hypertrace.servlet.v2_2.nowrapping; + +import io.opentelemetry.api.trace.Span; +import io.opentelemetry.instrumentation.api.field.VirtualField; +import java.io.BufferedReader; +import java.io.PrintWriter; +import java.io.UnsupportedEncodingException; +import java.util.Map; +import java.util.Objects; +import javax.servlet.ServletInputStream; +import javax.servlet.ServletOutputStream; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import javax.servlet.http.HttpSession; +import org.hypertrace.agent.core.instrumentation.HypertraceSemanticAttributes; +import org.hypertrace.agent.core.instrumentation.SpanAndObjectPair; +import org.hypertrace.agent.core.instrumentation.buffer.*; + +public class Utils { + + private Utils() {} + + public static void addSessionId(Span span, HttpServletRequest httpRequest) { + if (httpRequest.isRequestedSessionIdValid()) { + HttpSession session = httpRequest.getSession(); + if (session != null && !Objects.equals(session.getId(), "")) { + span.setAttribute(HypertraceSemanticAttributes.HTTP_REQUEST_SESSION_ID, session.getId()); + } + } + } + + public static void captureResponseBody( + Span span, + HttpServletResponse httpServletResponse, + VirtualField responseContextStore, + VirtualField streamContextStore, + VirtualField writerContextStore) { + + SpanAndObjectPair responseStreamWriterHolder = responseContextStore.get(httpServletResponse); + if (responseStreamWriterHolder == null) { + return; + } + responseContextStore.set(httpServletResponse, null); + + if (responseStreamWriterHolder.getAssociatedObject() instanceof ServletOutputStream) { + ServletOutputStream servletOutputStream = + (ServletOutputStream) responseStreamWriterHolder.getAssociatedObject(); + BoundedByteArrayOutputStream buffer = streamContextStore.get(servletOutputStream); + if (buffer != null) { + try { + span.setAttribute( + HypertraceSemanticAttributes.HTTP_RESPONSE_BODY, + buffer.toStringWithSuppliedCharset()); + } catch (UnsupportedEncodingException e) { + // should not happen + } + streamContextStore.set(servletOutputStream, null); + } + } else if (responseStreamWriterHolder.getAssociatedObject() instanceof PrintWriter) { + PrintWriter printWriter = (PrintWriter) responseStreamWriterHolder.getAssociatedObject(); + BoundedCharArrayWriter buffer = writerContextStore.get(printWriter); + if (buffer != null) { + span.setAttribute(HypertraceSemanticAttributes.HTTP_RESPONSE_BODY, buffer.toString()); + writerContextStore.set(printWriter, null); + } + } + } + + public static void resetRequestBodyBuffers( + HttpServletRequest httpServletRequest, + VirtualField requestContextStore, + VirtualField streamContextStore, + VirtualField bufferedReaderContextStore, + VirtualField urlEncodedMapContextStore) { + + SpanAndObjectPair requestStreamReaderHolder = requestContextStore.get(httpServletRequest); + if (requestStreamReaderHolder == null) { + return; + } + requestContextStore.set(httpServletRequest, null); + + if (requestStreamReaderHolder.getAssociatedObject() instanceof ServletInputStream) { + ServletInputStream servletInputStream = + (ServletInputStream) requestStreamReaderHolder.getAssociatedObject(); + ByteBufferSpanPair byteBufferSpanPair = streamContextStore.get(servletInputStream); + if (byteBufferSpanPair != null) { + // capture body explicitly e.g. Jackson does not call ServletInputStream$read() until -1 is + // returned. It does not even call ServletInputStream#available() + byteBufferSpanPair.captureBody(HypertraceSemanticAttributes.HTTP_REQUEST_BODY); + streamContextStore.set(servletInputStream, null); + } + } else if (requestStreamReaderHolder.getAssociatedObject() instanceof BufferedReader) { + BufferedReader bufferedReader = + (BufferedReader) requestStreamReaderHolder.getAssociatedObject(); + CharBufferSpanPair charBufferSpanPair = bufferedReaderContextStore.get(bufferedReader); + if (charBufferSpanPair != null) { + charBufferSpanPair.captureBody(HypertraceSemanticAttributes.HTTP_REQUEST_BODY); + bufferedReaderContextStore.set(bufferedReader, null); + } + } else if (requestStreamReaderHolder.getAssociatedObject() instanceof Map) { + StringMapSpanPair stringMapSpanPair = urlEncodedMapContextStore.get(httpServletRequest); + if (stringMapSpanPair != null) { + stringMapSpanPair.captureBody(HypertraceSemanticAttributes.HTTP_REQUEST_BODY); + urlEncodedMapContextStore.set(httpServletRequest, null); + } + } + } +} diff --git a/instrumentation/servlet/servlet-2.2/src/main/java/io/opentelemetry/javaagent/instrumentation/hypertrace/servlet/v2_2/nowrapping/request/ServletInputStreamInstrumentation.java b/instrumentation/servlet/servlet-2.2/src/main/java/io/opentelemetry/javaagent/instrumentation/hypertrace/servlet/v2_2/nowrapping/request/ServletInputStreamInstrumentation.java new file mode 100644 index 000000000..046a21190 --- /dev/null +++ b/instrumentation/servlet/servlet-2.2/src/main/java/io/opentelemetry/javaagent/instrumentation/hypertrace/servlet/v2_2/nowrapping/request/ServletInputStreamInstrumentation.java @@ -0,0 +1,360 @@ +/* + * Copyright The Hypertrace Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.opentelemetry.javaagent.instrumentation.hypertrace.servlet.v2_2.nowrapping.request; + +import static io.opentelemetry.javaagent.extension.matcher.AgentElementMatchers.hasSuperType; +import static net.bytebuddy.matcher.ElementMatchers.is; +import static net.bytebuddy.matcher.ElementMatchers.isPublic; +import static net.bytebuddy.matcher.ElementMatchers.named; +import static net.bytebuddy.matcher.ElementMatchers.takesArgument; +import static net.bytebuddy.matcher.ElementMatchers.takesArguments; + +import io.opentelemetry.instrumentation.api.field.VirtualField; +import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation; +import io.opentelemetry.javaagent.extension.instrumentation.TypeTransformer; +import java.io.IOException; +import javax.servlet.ServletInputStream; +import net.bytebuddy.asm.Advice; +import net.bytebuddy.description.type.TypeDescription; +import net.bytebuddy.matcher.ElementMatcher; +import org.hypertrace.agent.core.instrumentation.HypertraceCallDepthThreadLocalMap; +import org.hypertrace.agent.core.instrumentation.HypertraceEvaluationException; +import org.hypertrace.agent.core.instrumentation.HypertraceSemanticAttributes; +import org.hypertrace.agent.core.instrumentation.buffer.ByteBufferSpanPair; + +public class ServletInputStreamInstrumentation implements TypeInstrumentation { + + @Override + public ElementMatcher typeMatcher() { + return hasSuperType(named("javax.servlet.ServletInputStream")); + } + + @Override + public void transform(TypeTransformer transformer) { + transformer.applyAdviceToMethod( + named("read").and(takesArguments(0)).and(isPublic()), + ServletInputStreamInstrumentation.class.getName() + "$InputStream_ReadNoArgs"); + transformer.applyAdviceToMethod( + named("read") + .and(takesArguments(1)) + .and(takesArgument(0, is(byte[].class))) + .and(isPublic()), + ServletInputStreamInstrumentation.class.getName() + "$InputStream_ReadByteArray"); + transformer.applyAdviceToMethod( + named("read") + .and(takesArguments(3)) + .and(takesArgument(0, is(byte[].class))) + .and(takesArgument(1, is(int.class))) + .and(takesArgument(2, is(int.class))) + .and(isPublic()), + ServletInputStreamInstrumentation.class.getName() + "$InputStream_ReadByteArrayOffset"); + transformer.applyAdviceToMethod( + named("readAllBytes").and(takesArguments(0)).and(isPublic()), + ServletInputStreamInstrumentation.class.getName() + "$InputStream_ReadAllBytes"); + // TODO: readNBytes(int len) is not transformed + transformer.applyAdviceToMethod( + named("readNBytes") + .and(takesArguments(0)) + .and(takesArgument(0, is(byte[].class))) + .and(takesArgument(1, is(int.class))) + .and(takesArgument(2, is(int.class))) + .and(isPublic()), + ServletInputStreamInstrumentation.class.getName() + "$InputStream_ReadNBytes"); + + // ServletInputStream methods + transformer.applyAdviceToMethod( + named("readLine") + .and(takesArguments(3)) + .and(takesArgument(0, is(byte[].class))) + .and(takesArgument(1, is(int.class))) + .and(takesArgument(2, is(int.class))) + .and(isPublic()), + ServletInputStreamInstrumentation.class.getName() + "$InputStream_ReadByteArrayOffset"); + } + + @SuppressWarnings("unused") + static class InputStream_ReadNoArgs { + + @Advice.OnMethodEnter(suppress = Throwable.class) + public static ByteBufferSpanPair enter(@Advice.This ServletInputStream thizz) { + System.out.println("start Enter javax.servlet.ServletInputStream.ReadNoArgs"); + ByteBufferSpanPair bufferSpanPair = + VirtualField.find(ServletInputStream.class, ByteBufferSpanPair.class).get(thizz); + if (bufferSpanPair == null) { + System.out.println("end1 Enter javax.servlet.ServletInputStream.ReadNoArgs"); + return null; + } + + HypertraceCallDepthThreadLocalMap.incrementCallDepth(ServletInputStream.class); + System.out.println("end Enter javax.servlet.ServletInputStream.ReadNoArgs"); + return bufferSpanPair; + } + + @Advice.OnMethodExit(onThrowable = Throwable.class) + public static void exit( + @Advice.Return int read, @Advice.Enter ByteBufferSpanPair bufferSpanPair) { + System.out.println("start Exit javax.servlet.ServletInputStream.ReadNoArgs"); + try { + if (bufferSpanPair == null) { + System.out.println("end1 Exit javax.servlet.ServletInputStream.ReadNoArgs"); + return; + } + int callDepth = + HypertraceCallDepthThreadLocalMap.decrementCallDepth(ServletInputStream.class); + if (callDepth > 0) { + System.out.println("end2 Exit javax.servlet.ServletInputStream.ReadNoArgs"); + return; + } + + if (read == -1) { + bufferSpanPair.captureBody(HypertraceSemanticAttributes.HTTP_REQUEST_BODY); + } else { + bufferSpanPair.writeToBuffer((byte) read); + } + } catch (Throwable t) { + if (t instanceof HypertraceEvaluationException) { + throw t; + } else { + // ignore + } + } + System.out.println("end Exit javax.servlet.ServletInputStream.ReadNoArgs"); + } + } + + @SuppressWarnings("unused") + public static class InputStream_ReadByteArray { + + @Advice.OnMethodEnter(suppress = Throwable.class) + public static ByteBufferSpanPair enter(@Advice.This ServletInputStream thizz) { + System.out.println("start Enter javax.servlet.ServletInputStream.ReadByteArray"); + ByteBufferSpanPair bufferSpanPair = + VirtualField.find(ServletInputStream.class, ByteBufferSpanPair.class).get(thizz); + if (bufferSpanPair == null) { + System.out.println("end1 Enter javax.servlet.ServletInputStream.ReadByteArray"); + return null; + } + + HypertraceCallDepthThreadLocalMap.incrementCallDepth(ServletInputStream.class); + System.out.println("end Enter javax.servlet.ServletInputStream.ReadByteArray"); + return bufferSpanPair; + } + + @Advice.OnMethodExit(onThrowable = Throwable.class) + public static void exit( + @Advice.This ServletInputStream thizz, + @Advice.Return int read, + @Advice.Argument(0) byte[] b, + @Advice.Enter ByteBufferSpanPair bufferSpanPair) + throws Throwable { + System.out.println("start Exit javax.servlet.ServletInputStream.ReadByteArray"); + try { + if (bufferSpanPair == null) { + System.out.println("end1 Exit javax.servlet.ServletInputStream.ReadByteArray"); + return; + } + int callDepth = + HypertraceCallDepthThreadLocalMap.decrementCallDepth(ServletInputStream.class); + if (callDepth > 0) { + System.out.println("end2 Exit javax.servlet.ServletInputStream.ReadByteArray"); + return; + } + + if (read == -1) { + bufferSpanPair.captureBody(HypertraceSemanticAttributes.HTTP_REQUEST_BODY); + } else { + bufferSpanPair.writeToBuffer(b, 0, read); + if (thizz.available() == 0) { + bufferSpanPair.captureBody(HypertraceSemanticAttributes.HTTP_REQUEST_BODY); + } + } + } catch (Throwable t) { + if (t instanceof HypertraceEvaluationException) { + throw t; + } else { + // ignore + } + } + System.out.println("end Exit javax.servlet.ServletInputStream.ReadByteArray"); + } + } + + @SuppressWarnings("unused") + public static class InputStream_ReadByteArrayOffset { + + @Advice.OnMethodEnter(suppress = Throwable.class) + public static ByteBufferSpanPair enter(@Advice.This ServletInputStream thizz) { + System.out.println("start Enter javax.servlet.ServletInputStream.ReadByteArrayOffset"); + ByteBufferSpanPair bufferSpanPair = + VirtualField.find(ServletInputStream.class, ByteBufferSpanPair.class).get(thizz); + if (bufferSpanPair == null) { + System.out.println("end1 Enter javax.servlet.ServletInputStream.ReadByteArrayOffset"); + return null; + } + + HypertraceCallDepthThreadLocalMap.incrementCallDepth(ServletInputStream.class); + System.out.println("end Enter javax.servlet.ServletInputStream.ReadByteArrayOffset"); + return bufferSpanPair; + } + + @Advice.OnMethodExit(onThrowable = Throwable.class) + public static void exit( + @Advice.This ServletInputStream thizz, + @Advice.Return int read, + @Advice.Argument(0) byte[] b, + @Advice.Argument(1) int off, + @Advice.Argument(2) int len, + @Advice.Enter ByteBufferSpanPair bufferSpanPair) + throws Throwable { + System.out.println("start Exit javax.servlet.ServletInputStream.ReadByteArrayOffset"); + try { + + if (bufferSpanPair == null) { + System.out.println("end1 Exit javax.servlet.ServletInputStream.ReadByteArrayOffset"); + return; + } + int callDepth = + HypertraceCallDepthThreadLocalMap.decrementCallDepth(ServletInputStream.class); + if (callDepth > 0) { + System.out.println("end2 Exit javax.servlet.ServletInputStream.ReadByteArrayOffset"); + return; + } + + if (read == -1) { + bufferSpanPair.captureBody(HypertraceSemanticAttributes.HTTP_REQUEST_BODY); + } else { + bufferSpanPair.writeToBuffer(b, off, read); + if (thizz.available() == 0) { + bufferSpanPair.captureBody(HypertraceSemanticAttributes.HTTP_REQUEST_BODY); + } + } + } catch (Throwable t) { + if (t instanceof HypertraceEvaluationException) { + throw t; + } else { + // ignore + } + } + System.out.println("end Exit javax.servlet.ServletInputStream.ReadByteArrayOffset"); + } + } + + @SuppressWarnings("unused") + public static class InputStream_ReadAllBytes { + + @Advice.OnMethodEnter(suppress = Throwable.class) + public static ByteBufferSpanPair enter(@Advice.This ServletInputStream thizz) { + System.out.println("start Enter javax.servlet.ServletInputStream.ReadAllBytes"); + ByteBufferSpanPair bufferSpanPair = + VirtualField.find(ServletInputStream.class, ByteBufferSpanPair.class).get(thizz); + if (bufferSpanPair == null) { + System.out.println("end1 Enter javax.servlet.ServletInputStream.ReadAllBytes"); + return null; + } + + HypertraceCallDepthThreadLocalMap.incrementCallDepth(ServletInputStream.class); + System.out.println("end Enter javax.servlet.ServletInputStream.ReadAllBytes"); + return bufferSpanPair; + } + + @Advice.OnMethodExit(onThrowable = Throwable.class) + public static void exit( + @Advice.Return byte[] b, @Advice.Enter ByteBufferSpanPair bufferSpanPair) + throws IOException { + System.out.println("start Exit javax.servlet.ServletInputStream.ReadAllBytes"); + try { + if (bufferSpanPair == null) { + System.out.println("end1 Exit javax.servlet.ServletInputStream.ReadAllBytes"); + return; + } + int callDepth = + HypertraceCallDepthThreadLocalMap.decrementCallDepth(ServletInputStream.class); + if (callDepth > 0) { + System.out.println("end2 Exit javax.servlet.ServletInputStream.ReadAllBytes"); + return; + } + bufferSpanPair.writeToBuffer(b); + bufferSpanPair.captureBody(HypertraceSemanticAttributes.HTTP_REQUEST_BODY); + } catch (Throwable t) { + if (t instanceof HypertraceEvaluationException) { + throw t; + } else { + // ignore + } + } + System.out.println("end Exit javax.servlet.ServletInputStream.ReadAllBytes"); + } + } + + @SuppressWarnings("unused") + public static class InputStream_ReadNBytes { + + @Advice.OnMethodEnter(suppress = Throwable.class) + public static ByteBufferSpanPair enter(@Advice.This ServletInputStream thizz) { + System.out.println("start Enter javax.servlet.ServletInputStream.ReadNBytes"); + ByteBufferSpanPair bufferSpanPair = + VirtualField.find(ServletInputStream.class, ByteBufferSpanPair.class).get(thizz); + if (bufferSpanPair == null) { + System.out.println("end1 Enter javax.servlet.ServletInputStream.ReadNBytes"); + return null; + } + + HypertraceCallDepthThreadLocalMap.incrementCallDepth(ServletInputStream.class); + System.out.println("end Enter javax.servlet.ServletInputStream.ReadNBytes"); + return bufferSpanPair; + } + + @Advice.OnMethodExit(onThrowable = Throwable.class) + public static void exit( + @Advice.This ServletInputStream thizz, + @Advice.Return int read, + @Advice.Argument(0) byte[] b, + @Advice.Argument(1) int off, + @Advice.Enter ByteBufferSpanPair bufferSpanPair) + throws Throwable { + System.out.println("start Exit javax.servlet.ServletInputStream.ReadNBytes"); + try { + if (bufferSpanPair == null) { + System.out.println("end1 Exit javax.servlet.ServletInputStream.ReadNBytes"); + return; + } + int callDepth = + HypertraceCallDepthThreadLocalMap.decrementCallDepth(ServletInputStream.class); + if (callDepth > 0) { + System.out.println("end2 Exit javax.servlet.ServletInputStream.ReadNBytes"); + return; + } + + if (read == -1) { + bufferSpanPair.captureBody(HypertraceSemanticAttributes.HTTP_REQUEST_BODY); + } else { + bufferSpanPair.writeToBuffer(b, off, read); + if (thizz.available() == 0) { + bufferSpanPair.captureBody(HypertraceSemanticAttributes.HTTP_REQUEST_BODY); + } + } + } catch (Throwable t) { + if (t instanceof HypertraceEvaluationException) { + throw t; + } else { + // ignore + } + } + System.out.println("end Exit javax.servlet.ServletInputStream.ReadNBytes"); + } + } +} diff --git a/instrumentation/servlet/servlet-2.2/src/main/java/io/opentelemetry/javaagent/instrumentation/hypertrace/servlet/v2_2/nowrapping/request/ServletRequestInstrumentation.java b/instrumentation/servlet/servlet-2.2/src/main/java/io/opentelemetry/javaagent/instrumentation/hypertrace/servlet/v2_2/nowrapping/request/ServletRequestInstrumentation.java new file mode 100644 index 000000000..987d3ba90 --- /dev/null +++ b/instrumentation/servlet/servlet-2.2/src/main/java/io/opentelemetry/javaagent/instrumentation/hypertrace/servlet/v2_2/nowrapping/request/ServletRequestInstrumentation.java @@ -0,0 +1,284 @@ +/* + * Copyright The Hypertrace Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.opentelemetry.javaagent.instrumentation.hypertrace.servlet.v2_2.nowrapping.request; + +import static io.opentelemetry.javaagent.extension.matcher.AgentElementMatchers.hasSuperType; +import static net.bytebuddy.matcher.ElementMatchers.*; + +import io.opentelemetry.instrumentation.api.field.VirtualField; +import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation; +import io.opentelemetry.javaagent.extension.instrumentation.TypeTransformer; +import java.io.BufferedReader; +import java.util.HashMap; +import java.util.Map; +import javax.servlet.ServletInputStream; +import javax.servlet.ServletRequest; +import javax.servlet.http.HttpServletRequest; +import net.bytebuddy.asm.Advice; +import net.bytebuddy.description.type.TypeDescription; +import net.bytebuddy.matcher.ElementMatcher; +import org.hypertrace.agent.core.instrumentation.HypertraceCallDepthThreadLocalMap; +import org.hypertrace.agent.core.instrumentation.SpanAndObjectPair; +import org.hypertrace.agent.core.instrumentation.buffer.ByteBufferSpanPair; +import org.hypertrace.agent.core.instrumentation.buffer.CharBufferSpanPair; +import org.hypertrace.agent.core.instrumentation.buffer.StringMapSpanPair; + +public class ServletRequestInstrumentation implements TypeInstrumentation { + + @Override + public ElementMatcher typeMatcher() { + return hasSuperType(named("javax.servlet.ServletRequest")); + } + + @Override + public void transform(TypeTransformer transformer) { + transformer.applyAdviceToMethod( + named("getInputStream") + .and(takesArguments(0)) + .and(returns(named("javax.servlet.ServletInputStream"))) + .and(isPublic()), + ServletRequestInstrumentation.class.getName() + "$ServletRequest_getInputStream_advice"); + transformer.applyAdviceToMethod( + named("getReader") + .and(takesArguments(0)) + // .and(returns(BufferedReader.class)) + .and(isPublic()), + ServletRequestInstrumentation.class.getName() + "$ServletRequest_getReader_advice"); + transformer.applyAdviceToMethod( + named("getParameter") + .and(takesArguments(1)) + .and(takesArgument(0, is(String.class))) + .and(returns(String.class)) + .and(isPublic()), + ServletRequestInstrumentation.class.getName() + "$ServletRequest_getParameter_advice"); + } + + @SuppressWarnings("unused") + static class ServletRequest_getInputStream_advice { + + @Advice.OnMethodEnter(suppress = Throwable.class) + public static SpanAndObjectPair enter(@Advice.This ServletRequest servletRequest) { + System.out.println("start Enter javax.servlet.ServletRequest.getInputStream"); + HttpServletRequest httpServletRequest = (HttpServletRequest) servletRequest; + // span is added in servlet/filter instrumentation if data capture is enabled + SpanAndObjectPair requestBufferWrapper = + VirtualField.find(HttpServletRequest.class, SpanAndObjectPair.class) + .get(httpServletRequest); + if (requestBufferWrapper == null) { + System.out.println("end1 Enter javax.servlet.ServletRequest.getInputStream"); + return null; + } + + // the getReader method might call getInputStream + HypertraceCallDepthThreadLocalMap.incrementCallDepth(ServletRequest.class); + System.out.println("end Enter javax.servlet.ServletRequest.getInputStream"); + return requestBufferWrapper; + } + + @Advice.OnMethodExit(suppress = Throwable.class, onThrowable = Throwable.class) + public static void exit( + @Advice.This ServletRequest servletRequest, + @Advice.Return ServletInputStream servletInputStream, + @Advice.Thrown Throwable throwable, + @Advice.Enter SpanAndObjectPair spanAndObjectPair) { + System.out.println("start Exit javax.servlet.ServletRequest.getInputStream"); + if (spanAndObjectPair == null) { + System.out.println("end1 Exit javax.servlet.ServletRequest.getInputStream"); + return; + } + + int callDepth = HypertraceCallDepthThreadLocalMap.decrementCallDepth(ServletRequest.class); + if (callDepth > 0) { + System.out.println("end2 Exit javax.servlet.ServletRequest.getInputStream"); + return; + } + + if (!(servletRequest instanceof HttpServletRequest) || throwable != null) { + System.out.println("end3 Exit javax.servlet.ServletRequest.getInputStream"); + return; + } + HttpServletRequest httpServletRequest = (HttpServletRequest) servletRequest; + + VirtualField contextStore = + VirtualField.find(ServletInputStream.class, ByteBufferSpanPair.class); + if (contextStore.get(servletInputStream) != null) { + // getInputStream() can be called multiple times + System.out.println("end4 Exit javax.servlet.ServletRequest.getInputStream"); + return; + } + + ByteBufferSpanPair bufferSpanPair = + Utils.createRequestByteBufferSpanPair( + httpServletRequest, spanAndObjectPair.getSpan(), spanAndObjectPair.getHeaders()); + contextStore.set(servletInputStream, bufferSpanPair); + spanAndObjectPair.setAssociatedObject(servletInputStream); + System.out.println("end Exit javax.servlet.ServletRequest.getInputStream"); + } + } + + @SuppressWarnings("unused") + static class ServletRequest_getReader_advice { + + @Advice.OnMethodEnter(suppress = Throwable.class) + public static SpanAndObjectPair enter(@Advice.This ServletRequest servletRequest) { + System.out.println("start Enter javax.servlet.ServletRequest.getReader"); + HttpServletRequest httpServletRequest = (HttpServletRequest) servletRequest; + SpanAndObjectPair spanAndObjectPair = + VirtualField.find(HttpServletRequest.class, SpanAndObjectPair.class) + .get(httpServletRequest); + if (spanAndObjectPair == null) { + System.out.println("end1 Enter javax.servlet.ServletRequest.getReader"); + return null; + } + + HypertraceCallDepthThreadLocalMap.incrementCallDepth(ServletRequest.class); + System.out.println("end Enter javax.servlet.ServletRequest.getReader"); + return spanAndObjectPair; + } + + @Advice.OnMethodExit(suppress = Throwable.class, onThrowable = Throwable.class) + public static void exit( + @Advice.This ServletRequest servletRequest, + @Advice.Return BufferedReader reader, + @Advice.Thrown Throwable throwable, + @Advice.Enter SpanAndObjectPair spanAndObjectPair) { + System.out.println("start Exit javax.servlet.ServletRequest.getReader"); + if (spanAndObjectPair == null) { + System.out.println("end1 Exit javax.servlet.ServletRequest.getReader"); + return; + } + + int callDepth = HypertraceCallDepthThreadLocalMap.decrementCallDepth(ServletRequest.class); + if (callDepth > 0) { + System.out.println("end2 Exit javax.servlet.ServletRequest.getReader"); + return; + } + + if (!(servletRequest instanceof HttpServletRequest) || throwable != null) { + System.out.println("end3 Exit javax.servlet.ServletRequest.getReader"); + return; + } + HttpServletRequest httpServletRequest = (HttpServletRequest) servletRequest; + + VirtualField contextStore = + VirtualField.find(BufferedReader.class, CharBufferSpanPair.class); + if (contextStore.get(reader) != null) { + // getReader() can be called multiple times + System.out.println("end4 Exit javax.servlet.ServletRequest.getReader"); + return; + } + + CharBufferSpanPair bufferSpanPair = + Utils.createRequestCharBufferSpanPair( + httpServletRequest, spanAndObjectPair.getSpan(), spanAndObjectPair.getHeaders()); + contextStore.set(reader, bufferSpanPair); + spanAndObjectPair.setAssociatedObject(reader); + System.out.println("end Exit javax.servlet.ServletRequest.getReader"); + } + } + + /** Provides instrumentation template for ServletRequest.getParameter() method. */ + @SuppressWarnings("unused") + static class ServletRequest_getParameter_advice { + + /** + * Instrumentation template for ServletRequest.getParameter() entry point. + * + * @param servletRequest servletRequest + * @return a (possibly null) SpanAndObjectPair, which will be passed to the method exit + * instrumentation + */ + @Advice.OnMethodEnter(suppress = Throwable.class) + public static SpanAndObjectPair enter(@Advice.This ServletRequest servletRequest) { + System.out.println("start Enter javax.servlet.ServletRequest.getParameter"); + HttpServletRequest httpServletRequest = (HttpServletRequest) servletRequest; + SpanAndObjectPair spanAndObjectPair = + VirtualField.find(HttpServletRequest.class, SpanAndObjectPair.class) + .get(httpServletRequest); + if (spanAndObjectPair == null) { + System.out.println("end1 Enter javax.servlet.ServletRequest.getParameter"); + return null; + } + + HypertraceCallDepthThreadLocalMap.incrementCallDepth(ServletRequest.class); + System.out.println("end Enter javax.servlet.ServletRequest.getParameter"); + return spanAndObjectPair; + } + + /** + * Instrumentation template for ServletRequest.getParameter() exit point(s). + * + * @param servletRequest the ServletRequest instance + * @param returnValue the value that is being returned by getParameter() + * @param paramName the argument that was passed to getParameter() + * @param throwable the Throwable object, if exiting method because of a 'throw' + * @param spanAndObjectPair the value returned by the getParameter() method entry + * instrumentation + */ + @Advice.OnMethodExit(suppress = Throwable.class, onThrowable = Throwable.class) + public static void exit( + @Advice.This ServletRequest servletRequest, + @Advice.Return String returnValue, + @Advice.Argument(0) String paramName, + @Advice.Thrown Throwable throwable, + @Advice.Enter SpanAndObjectPair spanAndObjectPair) { + System.out.println("start Exit javax.servlet.ServletRequest.getParameter"); + if (spanAndObjectPair == null) { + System.out.println("end1 Exit javax.servlet.ServletRequest.getParameter"); + return; + } + + int callDepth = HypertraceCallDepthThreadLocalMap.decrementCallDepth(ServletRequest.class); + if (callDepth > 0) { + System.out.println("end2 Exit javax.servlet.ServletRequest.getParameter"); + return; + } + + if (returnValue == null) { + System.out.println("end3 Exit javax.servlet.ServletRequest.getParameter"); + return; + } + + if (!(servletRequest instanceof HttpServletRequest) || throwable != null) { + System.out.println("end4 Exit javax.servlet.ServletRequest.getParameter"); + return; + } + HttpServletRequest httpServletRequest = (HttpServletRequest) servletRequest; + + Map stringMap; + + VirtualField contextStore = + VirtualField.find(HttpServletRequest.class, StringMapSpanPair.class); + + StringMapSpanPair stringMapSpanPair = contextStore.get(httpServletRequest); + + if (stringMapSpanPair != null) { + stringMap = stringMapSpanPair.stringMap; + } else { + stringMap = new HashMap<>(); + stringMapSpanPair = + Utils.createStringMapSpanPair( + stringMap, spanAndObjectPair.getSpan(), spanAndObjectPair.getHeaders()); + contextStore.set(httpServletRequest, stringMapSpanPair); + } + + stringMap.put(paramName, returnValue); + spanAndObjectPair.setAssociatedObject(stringMap); + System.out.println("end Exit javax.servlet.ServletRequest.getParameter"); + } + } +} diff --git a/instrumentation/servlet/servlet-2.2/src/main/java/io/opentelemetry/javaagent/instrumentation/hypertrace/servlet/v2_2/nowrapping/request/Utils.java b/instrumentation/servlet/servlet-2.2/src/main/java/io/opentelemetry/javaagent/instrumentation/hypertrace/servlet/v2_2/nowrapping/request/Utils.java new file mode 100644 index 000000000..76dfc2399 --- /dev/null +++ b/instrumentation/servlet/servlet-2.2/src/main/java/io/opentelemetry/javaagent/instrumentation/hypertrace/servlet/v2_2/nowrapping/request/Utils.java @@ -0,0 +1,78 @@ +/* + * Copyright The Hypertrace Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.opentelemetry.javaagent.instrumentation.hypertrace.servlet.v2_2.nowrapping.request; + +import io.opentelemetry.api.trace.Span; +import java.nio.charset.Charset; +import java.util.Map; +import javax.servlet.http.HttpServletRequest; +import org.hypertrace.agent.core.instrumentation.buffer.BoundedBuffersFactory; +import org.hypertrace.agent.core.instrumentation.buffer.ByteBufferSpanPair; +import org.hypertrace.agent.core.instrumentation.buffer.CharBufferSpanPair; +import org.hypertrace.agent.core.instrumentation.buffer.StringMapSpanPair; +import org.hypertrace.agent.core.instrumentation.utils.ContentLengthUtils; +import org.hypertrace.agent.core.instrumentation.utils.ContentTypeCharsetUtils; +import org.hypertrace.agent.filter.FilterRegistry; +import org.hypertrace.agent.filter.api.Filter; + +public class Utils { + + private static final Filter filter = FilterRegistry.getFilter(); + + private Utils() {} + + public static ByteBufferSpanPair createRequestByteBufferSpanPair( + HttpServletRequest httpServletRequest, Span span, Map headers) { + String charsetStr = httpServletRequest.getCharacterEncoding(); + Charset charset = ContentTypeCharsetUtils.toCharset(charsetStr); + int contentLength = httpServletRequest.getContentLength(); + if (contentLength < 0) { + contentLength = ContentLengthUtils.DEFAULT; + } + return new ByteBufferSpanPair( + span, + BoundedBuffersFactory.createStream(contentLength, charset), + filter::evaluateRequestBody, + headers); + } + + public static CharBufferSpanPair createRequestCharBufferSpanPair( + HttpServletRequest httpServletRequest, Span span, Map headers) { + int contentLength = httpServletRequest.getContentLength(); + if (contentLength < 0) { + contentLength = ContentLengthUtils.DEFAULT; + } + return new CharBufferSpanPair( + span, + BoundedBuffersFactory.createWriter(contentLength), + filter::evaluateRequestBody, + headers); + } + + /** + * Create a StringMapSpanPair. + * + * @param stringMap stringMap + * @param span span + * @param headers headers + * @return StringMapSpanPair + */ + public static StringMapSpanPair createStringMapSpanPair( + Map stringMap, Span span, Map headers) { + return new StringMapSpanPair(span, stringMap, headers); + } +} diff --git a/instrumentation/servlet/servlet-2.2/src/main/java/io/opentelemetry/javaagent/instrumentation/hypertrace/servlet/v2_2/nowrapping/response/ServletOutputStreamInstrumentation.java b/instrumentation/servlet/servlet-2.2/src/main/java/io/opentelemetry/javaagent/instrumentation/hypertrace/servlet/v2_2/nowrapping/response/ServletOutputStreamInstrumentation.java new file mode 100644 index 000000000..813e164f9 --- /dev/null +++ b/instrumentation/servlet/servlet-2.2/src/main/java/io/opentelemetry/javaagent/instrumentation/hypertrace/servlet/v2_2/nowrapping/response/ServletOutputStreamInstrumentation.java @@ -0,0 +1,230 @@ +/* + * Copyright The Hypertrace Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.opentelemetry.javaagent.instrumentation.hypertrace.servlet.v2_2.nowrapping.response; + +import static io.opentelemetry.javaagent.extension.matcher.AgentElementMatchers.hasSuperType; +import static net.bytebuddy.matcher.ElementMatchers.is; +import static net.bytebuddy.matcher.ElementMatchers.isPublic; +import static net.bytebuddy.matcher.ElementMatchers.named; +import static net.bytebuddy.matcher.ElementMatchers.takesArgument; +import static net.bytebuddy.matcher.ElementMatchers.takesArguments; + +import io.opentelemetry.instrumentation.api.field.VirtualField; +import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation; +import io.opentelemetry.javaagent.extension.instrumentation.TypeTransformer; +import java.io.IOException; +import javax.servlet.ServletOutputStream; +import net.bytebuddy.asm.Advice; +import net.bytebuddy.description.type.TypeDescription; +import net.bytebuddy.matcher.ElementMatcher; +import org.hypertrace.agent.core.instrumentation.HypertraceCallDepthThreadLocalMap; +import org.hypertrace.agent.core.instrumentation.buffer.BoundedByteArrayOutputStream; + +public class ServletOutputStreamInstrumentation implements TypeInstrumentation { + + @Override + public ElementMatcher typeMatcher() { + return hasSuperType(named("javax.servlet.ServletOutputStream")); + } + + @Override + public void transform(TypeTransformer transformer) { + transformer.applyAdviceToMethod( + named("print") + .and(takesArguments(1)) + .and(takesArgument(0, is(String.class))) + .and(isPublic()), + ServletOutputStreamInstrumentation.class.getName() + "$ServletOutputStream_print"); + // other print methods call print or write on the OutputStream + + // OutputStream methods + transformer.applyAdviceToMethod( + named("write").and(takesArguments(1)).and(takesArgument(0, is(int.class))).and(isPublic()), + ServletOutputStreamInstrumentation.class.getName() + "$OutputStream_write"); + transformer.applyAdviceToMethod( + named("write") + .and(takesArguments(1)) + .and(takesArgument(0, is(byte[].class))) + .and(isPublic()), + ServletOutputStreamInstrumentation.class.getName() + "$OutputStream_writeByteArr"); + transformer.applyAdviceToMethod( + named("write") + .and(takesArguments(3)) + .and(takesArgument(0, is(byte[].class))) + .and(takesArgument(1, is(int.class))) + .and(takesArgument(2, is(int.class))) + .and(isPublic()), + ServletOutputStreamInstrumentation.class.getName() + "$OutputStream_writeByteArrOffset"); + + // close is not called on Tomcat (tested with Spring Boot) + // transformer.applyAdviceToMethod( + // named("close").and(takesArguments(0)) + // .and(isPublic()), + // ServletOutputStreamInstrumentation.class.getName() + "$OutputStream_close"); + } + + @SuppressWarnings("unused") + static class OutputStream_write { + + @Advice.OnMethodEnter(suppress = Throwable.class) + public static BoundedByteArrayOutputStream enter( + @Advice.This ServletOutputStream thizz, @Advice.Argument(0) int b) { + System.out.println("start Enter javax.servlet.ServletOutputStream.write"); + BoundedByteArrayOutputStream buffer = + VirtualField.find(ServletOutputStream.class, BoundedByteArrayOutputStream.class) + .get(thizz); + if (buffer == null) { + System.out.println("end1 Enter javax.servlet.ServletOutputStream.write"); + return null; + } + int callDepth = + HypertraceCallDepthThreadLocalMap.incrementCallDepth(ServletOutputStream.class); + if (callDepth > 0) { + System.out.println("end2 Enter javax.servlet.ServletOutputStream.write"); + return buffer; + } + + buffer.write(b); + System.out.println("end Enter javax.servlet.ServletOutputStream.write"); + return buffer; + } + + @Advice.OnMethodExit(suppress = Throwable.class, onThrowable = Throwable.class) + public static void exit(@Advice.Enter BoundedByteArrayOutputStream buffer) { + System.out.println("start Exit javax.servlet.ServletOutputStream.write"); + if (buffer != null) { + HypertraceCallDepthThreadLocalMap.decrementCallDepth(ServletOutputStream.class); + } + System.out.println("end Exit javax.servlet.ServletOutputStream.write"); + } + } + + @SuppressWarnings("unused") + static class OutputStream_writeByteArr { + + @Advice.OnMethodEnter(suppress = Throwable.class) + public static BoundedByteArrayOutputStream enter( + @Advice.This ServletOutputStream thizz, @Advice.Argument(0) byte[] b) throws IOException { + System.out.println("start Enter javax.servlet.ServletOutputStream.writeByteArr"); + + BoundedByteArrayOutputStream buffer = + VirtualField.find(ServletOutputStream.class, BoundedByteArrayOutputStream.class) + .get(thizz); + if (buffer == null) { + System.out.println("end1 Enter javax.servlet.ServletOutputStream.writeByteArr"); + return null; + } + int callDepth = + HypertraceCallDepthThreadLocalMap.incrementCallDepth(ServletOutputStream.class); + if (callDepth > 0) { + System.out.println("end2 Enter javax.servlet.ServletOutputStream.writeByteArr"); + return buffer; + } + + buffer.write(b); + System.out.println("end Enter javax.servlet.ServletOutputStream.writeByteArr"); + return buffer; + } + + @Advice.OnMethodExit(suppress = Throwable.class, onThrowable = Throwable.class) + public static void exit(@Advice.Enter BoundedByteArrayOutputStream buffer) { + System.out.println("start Exit javax.servlet.ServletOutputStream.writeByteArr"); + if (buffer != null) { + HypertraceCallDepthThreadLocalMap.decrementCallDepth(ServletOutputStream.class); + } + System.out.println("end Exit javax.servlet.ServletOutputStream.writeByteArr"); + } + } + + @SuppressWarnings("unused") + static class OutputStream_writeByteArrOffset { + + @Advice.OnMethodEnter(suppress = Throwable.class) + public static BoundedByteArrayOutputStream enter( + @Advice.This ServletOutputStream thizz, + @Advice.Argument(0) byte[] b, + @Advice.Argument(1) int off, + @Advice.Argument(2) int len) { + System.out.println("start Enter javax.servlet.ServletOutputStream.writeByteArrOffset"); + + BoundedByteArrayOutputStream buffer = + VirtualField.find(ServletOutputStream.class, BoundedByteArrayOutputStream.class) + .get(thizz); + if (buffer == null) { + System.out.println("end1 Enter javax.servlet.ServletOutputStream.writeByteArrOffset"); + return null; + } + int callDepth = + HypertraceCallDepthThreadLocalMap.incrementCallDepth(ServletOutputStream.class); + if (callDepth > 0) { + System.out.println("end2 Enter javax.servlet.ServletOutputStream.writeByteArrOffset"); + return buffer; + } + + buffer.write(b, off, len); + System.out.println("end Enter javax.servlet.ServletOutputStream.writeByteArrOffset"); + return buffer; + } + + @Advice.OnMethodExit(suppress = Throwable.class, onThrowable = Throwable.class) + public static void exit(@Advice.Enter BoundedByteArrayOutputStream buffer) { + System.out.println("start Exit javax.servlet.ServletOutputStream.writeByteArrOffset"); + if (buffer != null) { + HypertraceCallDepthThreadLocalMap.decrementCallDepth(ServletOutputStream.class); + } + System.out.println("end Exit javax.servlet.ServletOutputStream.writeByteArrOffset"); + } + } + + @SuppressWarnings("unused") + static class ServletOutputStream_print { + + @Advice.OnMethodEnter(suppress = Throwable.class) + public static BoundedByteArrayOutputStream enter( + @Advice.This ServletOutputStream thizz, @Advice.Argument(0) String s) throws IOException { + System.out.println("start Enter javax.servlet.ServletOutputStream.print"); + + BoundedByteArrayOutputStream buffer = + VirtualField.find(ServletOutputStream.class, BoundedByteArrayOutputStream.class) + .get(thizz); + if (buffer == null) { + System.out.println("end1 Enter javax.servlet.ServletOutputStream.print"); + return null; + } + int callDepth = + HypertraceCallDepthThreadLocalMap.incrementCallDepth(ServletOutputStream.class); + if (callDepth > 0) { + System.out.println("end2 Enter javax.servlet.ServletOutputStream.print"); + return buffer; + } + + String bodyPart = s == null ? "null" : s; + buffer.write(bodyPart.getBytes()); + System.out.println("end Enter javax.servlet.ServletOutputStream.print"); + return buffer; + } + + @Advice.OnMethodExit(suppress = Throwable.class, onThrowable = Throwable.class) + public static void exit(@Advice.Enter BoundedByteArrayOutputStream buffer) { + System.out.println("start Exit javax.servlet.ServletOutputStream.print"); + if (buffer != null) { + HypertraceCallDepthThreadLocalMap.decrementCallDepth(ServletOutputStream.class); + } + System.out.println("end Exit javax.servlet.ServletOutputStream.print"); + } + } +} diff --git a/instrumentation/servlet/servlet-2.2/src/main/java/io/opentelemetry/javaagent/instrumentation/hypertrace/servlet/v2_2/nowrapping/response/ServletResponseInstrumentation.java b/instrumentation/servlet/servlet-2.2/src/main/java/io/opentelemetry/javaagent/instrumentation/hypertrace/servlet/v2_2/nowrapping/response/ServletResponseInstrumentation.java new file mode 100644 index 000000000..0d7699fcc --- /dev/null +++ b/instrumentation/servlet/servlet-2.2/src/main/java/io/opentelemetry/javaagent/instrumentation/hypertrace/servlet/v2_2/nowrapping/response/ServletResponseInstrumentation.java @@ -0,0 +1,194 @@ +/* + * Copyright The Hypertrace Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.opentelemetry.javaagent.instrumentation.hypertrace.servlet.v2_2.nowrapping.response; + +import static io.opentelemetry.javaagent.extension.matcher.AgentElementMatchers.hasSuperType; +import static net.bytebuddy.matcher.ElementMatchers.isPublic; +import static net.bytebuddy.matcher.ElementMatchers.named; +import static net.bytebuddy.matcher.ElementMatchers.returns; +import static net.bytebuddy.matcher.ElementMatchers.takesArguments; + +import io.opentelemetry.instrumentation.api.field.VirtualField; +import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation; +import io.opentelemetry.javaagent.extension.instrumentation.TypeTransformer; +import java.io.PrintWriter; +import java.nio.charset.Charset; +import javax.servlet.ServletOutputStream; +import javax.servlet.ServletResponse; +import javax.servlet.http.HttpServletResponse; +import net.bytebuddy.asm.Advice; +import net.bytebuddy.description.type.TypeDescription; +import net.bytebuddy.matcher.ElementMatcher; +import org.hypertrace.agent.core.config.InstrumentationConfig; +import org.hypertrace.agent.core.instrumentation.HypertraceCallDepthThreadLocalMap; +import org.hypertrace.agent.core.instrumentation.SpanAndObjectPair; +import org.hypertrace.agent.core.instrumentation.buffer.BoundedBuffersFactory; +import org.hypertrace.agent.core.instrumentation.buffer.BoundedByteArrayOutputStream; +import org.hypertrace.agent.core.instrumentation.buffer.BoundedCharArrayWriter; +import org.hypertrace.agent.core.instrumentation.utils.ContentTypeCharsetUtils; + +public class ServletResponseInstrumentation implements TypeInstrumentation { + + @Override + public ElementMatcher typeMatcher() { + return hasSuperType(named("javax.servlet.ServletResponse")); + } + + @Override + public void transform(TypeTransformer transformer) { + transformer.applyAdviceToMethod( + named("getOutputStream") + .and(takesArguments(0)) + .and(returns(named("javax.servlet.ServletOutputStream"))) + .and(isPublic()), + ServletResponseInstrumentation.class.getName() + "$ServletResponse_getOutputStream"); + transformer.applyAdviceToMethod( + named("getWriter").and(takesArguments(0)).and(returns(PrintWriter.class)).and(isPublic()), + ServletResponseInstrumentation.class.getName() + "$ServletResponse_getWriter_advice"); + } + + @SuppressWarnings("unused") + static class ServletResponse_getOutputStream { + + @Advice.OnMethodEnter(suppress = Throwable.class) + public static HttpServletResponse enter(@Advice.This ServletResponse servletResponse) { + System.out.println("start Enter javax.servlet.ServletResponse.getOutputStream"); + + if (!(servletResponse instanceof HttpServletResponse)) { + System.out.println("end1 Enter javax.servlet.ServletResponse.getOutputStream"); + return null; + } + // ignore wrappers, the filter/servlet instrumentation gets the captured body from context + // store + // by using response as a key and the filter/servlet instrumentation runs early when wrappers + // are not used. + HttpServletResponse httpServletResponse = (HttpServletResponse) servletResponse; + // the getReader method might call getInputStream + HypertraceCallDepthThreadLocalMap.incrementCallDepth(ServletResponse.class); + System.out.println("end Enter javax.servlet.ServletResponse.getOutputStream"); + return httpServletResponse; + } + + @Advice.OnMethodExit(suppress = Throwable.class, onThrowable = Throwable.class) + public static void exit( + @Advice.Enter HttpServletResponse httpServletResponse, + @Advice.Thrown Throwable throwable, + @Advice.Return ServletOutputStream servletOutputStream) { + System.out.println("start Exit javax.servlet.ServletResponse.getOutputStream"); + + if (httpServletResponse == null) { + System.out.println("end1 Exit javax.servlet.ServletResponse.getOutputStream"); + return; + } + + int callDepth = HypertraceCallDepthThreadLocalMap.decrementCallDepth(ServletResponse.class); + if (callDepth > 0) { + System.out.println("end2 Exit javax.servlet.ServletResponse.getOutputStream"); + return; + } + if (throwable != null) { + System.out.println("end3 Exit javax.servlet.ServletResponse.getOutputStream"); + return; + } + + VirtualField contextStore = + VirtualField.find(ServletOutputStream.class, BoundedByteArrayOutputStream.class); + if (contextStore.get(servletOutputStream) != null) { + // getOutputStream() can be called multiple times + System.out.println("end4 Exit javax.servlet.ServletResponse.getOutputStream"); + return; + } + + // do not capture if data capture is disabled or not supported content type + InstrumentationConfig instrumentationConfig = InstrumentationConfig.ConfigProvider.get(); + // TODO: check response contentType before capturing response body + if (instrumentationConfig.httpBody().response()) { + String charsetStr = httpServletResponse.getCharacterEncoding(); + Charset charset = ContentTypeCharsetUtils.toCharset(charsetStr); + BoundedByteArrayOutputStream buffer = BoundedBuffersFactory.createStream(charset); + contextStore.set(servletOutputStream, buffer); + SpanAndObjectPair spanAndObjectPair = new SpanAndObjectPair(null, null); + spanAndObjectPair.setAssociatedObject(servletOutputStream); + VirtualField.find(HttpServletResponse.class, SpanAndObjectPair.class) + .set(httpServletResponse, spanAndObjectPair); + } + System.out.println("end Exit javax.servlet.ServletResponse.getOutputStream"); + } + } + + @SuppressWarnings("unused") + static class ServletResponse_getWriter_advice { + + @Advice.OnMethodEnter(suppress = Throwable.class) + public static HttpServletResponse enter(@Advice.This ServletResponse servletResponse) { + System.out.println("start Enter javax.servlet.ServletResponse.getWriter"); + + if (!(servletResponse instanceof HttpServletResponse)) { + System.out.println("end1 Enter javax.servlet.ServletResponse.getWriter"); + return null; + } + HttpServletResponse httpServletResponse = (HttpServletResponse) servletResponse; + // the getWriter method might call getInputStream + HypertraceCallDepthThreadLocalMap.incrementCallDepth(ServletResponse.class); + System.out.println("end Enter javax.servlet.ServletResponse.getWriter"); + return httpServletResponse; + } + + @Advice.OnMethodExit(suppress = Throwable.class, onThrowable = Throwable.class) + public static void exit( + @Advice.Enter HttpServletResponse httpServletResponse, + @Advice.Thrown Throwable throwable, + @Advice.Return PrintWriter printWriter) { + System.out.println("start Exit javax.servlet.ServletResponse.getWriter"); + if (httpServletResponse == null) { + System.out.println("end1 Exit javax.servlet.ServletResponse.getWriter"); + return; + } + + int callDepth = HypertraceCallDepthThreadLocalMap.decrementCallDepth(ServletResponse.class); + if (callDepth > 0) { + System.out.println("end2 Exit javax.servlet.ServletResponse.getWriter"); + return; + } + if (throwable != null) { + System.out.println("end3 Exit javax.servlet.ServletResponse.getWriter"); + return; + } + + VirtualField contextStore = + VirtualField.find(PrintWriter.class, BoundedCharArrayWriter.class); + if (contextStore.get(printWriter) != null) { + // getWriter() can be called multiple times + System.out.println("end4 Exit javax.servlet.ServletResponse.getWriter"); + return; + } + + // do not capture if data capture is disabled or not supported content type + InstrumentationConfig instrumentationConfig = InstrumentationConfig.ConfigProvider.get(); + // TODO: check response contentType before capturing the response body + if (instrumentationConfig.httpBody().response()) { + BoundedCharArrayWriter writer = BoundedBuffersFactory.createWriter(); + contextStore.set(printWriter, writer); + SpanAndObjectPair spanAndObjectPair = new SpanAndObjectPair(null, null); + spanAndObjectPair.setAssociatedObject(printWriter); + VirtualField.find(HttpServletResponse.class, SpanAndObjectPair.class) + .set(httpServletResponse, spanAndObjectPair); + } + System.out.println("end Exit javax.servlet.ServletResponse.getWriter"); + } + } +} diff --git a/instrumentation/servlet/servlet-3.0/src/main/java/io/opentelemetry/javaagent/instrumentation/hypertrace/servlet/v3_0/nowrapping/Servlet30AndFilterInstrumentation.java b/instrumentation/servlet/servlet-3.0/src/main/java/io/opentelemetry/javaagent/instrumentation/hypertrace/servlet/v3_0/nowrapping/Servlet30AndFilterInstrumentation.java index b76f135d1..b4d92a875 100644 --- a/instrumentation/servlet/servlet-3.0/src/main/java/io/opentelemetry/javaagent/instrumentation/hypertrace/servlet/v3_0/nowrapping/Servlet30AndFilterInstrumentation.java +++ b/instrumentation/servlet/servlet-3.0/src/main/java/io/opentelemetry/javaagent/instrumentation/hypertrace/servlet/v3_0/nowrapping/Servlet30AndFilterInstrumentation.java @@ -57,6 +57,7 @@ public class Servlet30AndFilterInstrumentation implements TypeInstrumentation { @Override public ElementMatcher classLoaderOptimization() { + // TODO: reconcile with OTEL, it uses "javax.servlet.Servlet" instead return hasClassesNamed("javax.servlet.Filter"); } @@ -75,6 +76,7 @@ public void transform(TypeTransformer transformer) { Servlet30AndFilterInstrumentation.class.getName() + "$ServletAdvice"); } + @SuppressWarnings("unused") public static class ServletAdvice { @Advice.OnMethodEnter(suppress = Throwable.class, skipOn = Advice.OnNonDefaultValue.class) diff --git a/instrumentation/servlet/servlet-3.0/src/main/java/io/opentelemetry/javaagent/instrumentation/hypertrace/servlet/v3_0/nowrapping/Servlet30InstrumentationModule.java b/instrumentation/servlet/servlet-3.0/src/main/java/io/opentelemetry/javaagent/instrumentation/hypertrace/servlet/v3_0/nowrapping/Servlet30InstrumentationModule.java index 45e5d6225..181973b1d 100644 --- a/instrumentation/servlet/servlet-3.0/src/main/java/io/opentelemetry/javaagent/instrumentation/hypertrace/servlet/v3_0/nowrapping/Servlet30InstrumentationModule.java +++ b/instrumentation/servlet/servlet-3.0/src/main/java/io/opentelemetry/javaagent/instrumentation/hypertrace/servlet/v3_0/nowrapping/Servlet30InstrumentationModule.java @@ -27,6 +27,7 @@ import java.util.Arrays; import java.util.List; +@SuppressWarnings("unused") @AutoService(InstrumentationModule.class) public class Servlet30InstrumentationModule extends InstrumentationModule { diff --git a/instrumentation/servlet/servlet-3.0/src/main/java/io/opentelemetry/javaagent/instrumentation/hypertrace/servlet/v3_0/nowrapping/Servlet30InstrumentationName.java b/instrumentation/servlet/servlet-3.0/src/main/java/io/opentelemetry/javaagent/instrumentation/hypertrace/servlet/v3_0/nowrapping/Servlet30InstrumentationName.java index 4c26b166f..779f7e836 100644 --- a/instrumentation/servlet/servlet-3.0/src/main/java/io/opentelemetry/javaagent/instrumentation/hypertrace/servlet/v3_0/nowrapping/Servlet30InstrumentationName.java +++ b/instrumentation/servlet/servlet-3.0/src/main/java/io/opentelemetry/javaagent/instrumentation/hypertrace/servlet/v3_0/nowrapping/Servlet30InstrumentationName.java @@ -19,12 +19,6 @@ public class Servlet30InstrumentationName { public static final String PRIMARY = "servlet"; public static final String[] OTHER = { - "servlet-3", - "ht", - "servlet-ht", - "servlet-3-ht", - "servlet-3-no-wrapping", - "servlet-no-wrapping", - "servlet-no-wrapping" + "servlet-3", "ht", "servlet-ht", "servlet-3-ht", "servlet-3-no-wrapping", "servlet-no-wrapping" }; } diff --git a/instrumentation/servlet/servlet-3.0/src/main/java/io/opentelemetry/javaagent/instrumentation/hypertrace/servlet/v3_0/nowrapping/Utils.java b/instrumentation/servlet/servlet-3.0/src/main/java/io/opentelemetry/javaagent/instrumentation/hypertrace/servlet/v3_0/nowrapping/Utils.java index 3c271ca11..e0fa66553 100644 --- a/instrumentation/servlet/servlet-3.0/src/main/java/io/opentelemetry/javaagent/instrumentation/hypertrace/servlet/v3_0/nowrapping/Utils.java +++ b/instrumentation/servlet/servlet-3.0/src/main/java/io/opentelemetry/javaagent/instrumentation/hypertrace/servlet/v3_0/nowrapping/Utils.java @@ -22,6 +22,7 @@ import java.io.PrintWriter; import java.io.UnsupportedEncodingException; import java.util.Map; +import java.util.Objects; import javax.servlet.ServletInputStream; import javax.servlet.ServletOutputStream; import javax.servlet.http.HttpServletRequest; @@ -38,7 +39,7 @@ private Utils() {} public static void addSessionId(Span span, HttpServletRequest httpRequest) { if (httpRequest.isRequestedSessionIdValid()) { HttpSession session = httpRequest.getSession(); - if (session != null && session.getId() != "") { + if (session != null && !Objects.equals(session.getId(), "")) { span.setAttribute(HypertraceSemanticAttributes.HTTP_REQUEST_SESSION_ID, session.getId()); } } @@ -100,8 +101,7 @@ public static void resetRequestBodyBuffers( ByteBufferSpanPair byteBufferSpanPair = streamContextStore.get(servletInputStream); if (byteBufferSpanPair != null) { // capture body explicitly e.g. Jackson does not call ServletInputStream$read() until -1 is - // returned - // it does not even call ServletInputStream#available() + // returned. It does not even call ServletInputStream#available() byteBufferSpanPair.captureBody(HypertraceSemanticAttributes.HTTP_REQUEST_BODY); streamContextStore.set(servletInputStream, null); } diff --git a/instrumentation/servlet/servlet-3.0/src/main/java/io/opentelemetry/javaagent/instrumentation/hypertrace/servlet/v3_0/nowrapping/async/Servlet30AsyncInstrumentation.java b/instrumentation/servlet/servlet-3.0/src/main/java/io/opentelemetry/javaagent/instrumentation/hypertrace/servlet/v3_0/nowrapping/async/Servlet30AsyncInstrumentation.java index 3c106f281..016f6fee6 100644 --- a/instrumentation/servlet/servlet-3.0/src/main/java/io/opentelemetry/javaagent/instrumentation/hypertrace/servlet/v3_0/nowrapping/async/Servlet30AsyncInstrumentation.java +++ b/instrumentation/servlet/servlet-3.0/src/main/java/io/opentelemetry/javaagent/instrumentation/hypertrace/servlet/v3_0/nowrapping/async/Servlet30AsyncInstrumentation.java @@ -63,6 +63,7 @@ public void transform(TypeTransformer transformer) { Servlet30AsyncInstrumentation.class.getName() + "$StartAsyncAdvice"); } + @SuppressWarnings("unused") static final class StartAsyncAdvice { @Advice.OnMethodEnter(suppress = Throwable.class) diff --git a/instrumentation/servlet/servlet-3.0/src/main/java/io/opentelemetry/javaagent/instrumentation/hypertrace/servlet/v3_0/nowrapping/request/ServletInputStreamInstrumentation.java b/instrumentation/servlet/servlet-3.0/src/main/java/io/opentelemetry/javaagent/instrumentation/hypertrace/servlet/v3_0/nowrapping/request/ServletInputStreamInstrumentation.java index 2b7d9ebb9..246c9579c 100644 --- a/instrumentation/servlet/servlet-3.0/src/main/java/io/opentelemetry/javaagent/instrumentation/hypertrace/servlet/v3_0/nowrapping/request/ServletInputStreamInstrumentation.java +++ b/instrumentation/servlet/servlet-3.0/src/main/java/io/opentelemetry/javaagent/instrumentation/hypertrace/servlet/v3_0/nowrapping/request/ServletInputStreamInstrumentation.java @@ -65,6 +65,7 @@ public void transform(TypeTransformer transformer) { transformer.applyAdviceToMethod( named("readAllBytes").and(takesArguments(0)).and(isPublic()), ServletInputStreamInstrumentation.class.getName() + "$InputStream_ReadAllBytes"); + // TODO: readNBytes(int len) is not transformed transformer.applyAdviceToMethod( named("readNBytes") .and(takesArguments(0)) @@ -85,6 +86,7 @@ public void transform(TypeTransformer transformer) { ServletInputStreamInstrumentation.class.getName() + "$InputStream_ReadByteArrayOffset"); } + @SuppressWarnings("unused") static class InputStream_ReadNoArgs { @Advice.OnMethodEnter(suppress = Throwable.class) @@ -127,6 +129,7 @@ public static void exit( } } + @SuppressWarnings("unused") public static class InputStream_ReadByteArray { @Advice.OnMethodEnter(suppress = Throwable.class) @@ -145,7 +148,7 @@ public static ByteBufferSpanPair enter(@Advice.This ServletInputStream thizz) { public static void exit( @Advice.This ServletInputStream thizz, @Advice.Return int read, - @Advice.Argument(0) byte b[], + @Advice.Argument(0) byte[] b, @Advice.Enter ByteBufferSpanPair bufferSpanPair) throws Throwable { try { @@ -176,6 +179,7 @@ public static void exit( } } + @SuppressWarnings("unused") public static class InputStream_ReadByteArrayOffset { @Advice.OnMethodEnter(suppress = Throwable.class) @@ -194,7 +198,7 @@ public static ByteBufferSpanPair enter(@Advice.This ServletInputStream thizz) { public static void exit( @Advice.This ServletInputStream thizz, @Advice.Return int read, - @Advice.Argument(0) byte b[], + @Advice.Argument(0) byte[] b, @Advice.Argument(1) int off, @Advice.Argument(2) int len, @Advice.Enter ByteBufferSpanPair bufferSpanPair) @@ -228,6 +232,7 @@ public static void exit( } } + @SuppressWarnings("unused") public static class InputStream_ReadAllBytes { @Advice.OnMethodEnter(suppress = Throwable.class) @@ -267,6 +272,7 @@ public static void exit( } } + @SuppressWarnings("unused") public static class InputStream_ReadNBytes { @Advice.OnMethodEnter(suppress = Throwable.class) @@ -287,7 +293,6 @@ public static void exit( @Advice.Return int read, @Advice.Argument(0) byte[] b, @Advice.Argument(1) int off, - @Advice.Argument(2) int len, @Advice.Enter ByteBufferSpanPair bufferSpanPair) throws Throwable { try { @@ -317,9 +322,4 @@ public static void exit( } } } - - private static final String HANDLER_NAME = - "io.opentelemetry.javaagent.bootstrap.ExceptionLogger".replace('.', '/'); - private static final String LOGGER_NAME = "org.slf4j.Logger".replace('.', '/'); - private static final String LOG_FACTORY_NAME = "org.slf4j.LoggerFactory".replace('.', '/'); } diff --git a/instrumentation/servlet/servlet-3.0/src/main/java/io/opentelemetry/javaagent/instrumentation/hypertrace/servlet/v3_0/nowrapping/request/ServletRequestInstrumentation.java b/instrumentation/servlet/servlet-3.0/src/main/java/io/opentelemetry/javaagent/instrumentation/hypertrace/servlet/v3_0/nowrapping/request/ServletRequestInstrumentation.java index b701924e3..01ac6da8f 100644 --- a/instrumentation/servlet/servlet-3.0/src/main/java/io/opentelemetry/javaagent/instrumentation/hypertrace/servlet/v3_0/nowrapping/request/ServletRequestInstrumentation.java +++ b/instrumentation/servlet/servlet-3.0/src/main/java/io/opentelemetry/javaagent/instrumentation/hypertrace/servlet/v3_0/nowrapping/request/ServletRequestInstrumentation.java @@ -67,6 +67,7 @@ public void transform(TypeTransformer transformer) { ServletRequestInstrumentation.class.getName() + "$ServletRequest_getParameter_advice"); } + @SuppressWarnings("unused") static class ServletRequest_getInputStream_advice { @Advice.OnMethodEnter(suppress = Throwable.class) @@ -121,6 +122,7 @@ public static void exit( } } + @SuppressWarnings("unused") static class ServletRequest_getReader_advice { @Advice.OnMethodEnter(suppress = Throwable.class) @@ -174,12 +176,13 @@ public static void exit( } /** Provides instrumentation template for ServletRequest.getParameter() method. */ + @SuppressWarnings("unused") static class ServletRequest_getParameter_advice { /** * Instrumentation template for ServletRequest.getParameter() entry point. * - * @param servletRequest + * @param servletRequest servletRequest * @return a (possibly null) SpanAndObjectPair, which will be passed to the method exit * instrumentation */ @@ -202,7 +205,7 @@ public static SpanAndObjectPair enter(@Advice.This ServletRequest servletRequest * * @param servletRequest the ServletRequest instance * @param returnValue the value that is being returned by getParameter() - * @param parmName the argument that was passed to getParameter() + * @param paramName the argument that was passed to getParameter() * @param throwable the Throwable object, if exiting method because of a 'throw' * @param spanAndObjectPair the value returned by the getParameter() method entry * instrumentation @@ -211,7 +214,7 @@ public static SpanAndObjectPair enter(@Advice.This ServletRequest servletRequest public static void exit( @Advice.This ServletRequest servletRequest, @Advice.Return String returnValue, - @Advice.Argument(0) String parmName, + @Advice.Argument(0) String paramName, @Advice.Thrown Throwable throwable, @Advice.Enter SpanAndObjectPair spanAndObjectPair) { if (spanAndObjectPair == null) { @@ -249,7 +252,7 @@ public static void exit( contextStore.set(httpServletRequest, stringMapSpanPair); } - stringMap.put(parmName, returnValue); + stringMap.put(paramName, returnValue); spanAndObjectPair.setAssociatedObject(stringMap); } } diff --git a/instrumentation/servlet/servlet-3.0/src/main/java/io/opentelemetry/javaagent/instrumentation/hypertrace/servlet/v3_0/nowrapping/request/Utils.java b/instrumentation/servlet/servlet-3.0/src/main/java/io/opentelemetry/javaagent/instrumentation/hypertrace/servlet/v3_0/nowrapping/request/Utils.java index 27d412c9b..0276ae540 100644 --- a/instrumentation/servlet/servlet-3.0/src/main/java/io/opentelemetry/javaagent/instrumentation/hypertrace/servlet/v3_0/nowrapping/request/Utils.java +++ b/instrumentation/servlet/servlet-3.0/src/main/java/io/opentelemetry/javaagent/instrumentation/hypertrace/servlet/v3_0/nowrapping/request/Utils.java @@ -66,10 +66,10 @@ public static CharBufferSpanPair createRequestCharBufferSpanPair( /** * Create a StringMapSpanPair. * - * @param stringMap - * @param span - * @param headers - * @return + * @param stringMap stringMap + * @param span span + * @param headers headers + * @return StringMapSpanPair */ public static StringMapSpanPair createStringMapSpanPair( Map stringMap, Span span, Map headers) { diff --git a/instrumentation/servlet/servlet-3.0/src/main/java/io/opentelemetry/javaagent/instrumentation/hypertrace/servlet/v3_0/nowrapping/response/ServletOutputStreamInstrumentation.java b/instrumentation/servlet/servlet-3.0/src/main/java/io/opentelemetry/javaagent/instrumentation/hypertrace/servlet/v3_0/nowrapping/response/ServletOutputStreamInstrumentation.java index b23f390e8..577e05d4d 100644 --- a/instrumentation/servlet/servlet-3.0/src/main/java/io/opentelemetry/javaagent/instrumentation/hypertrace/servlet/v3_0/nowrapping/response/ServletOutputStreamInstrumentation.java +++ b/instrumentation/servlet/servlet-3.0/src/main/java/io/opentelemetry/javaagent/instrumentation/hypertrace/servlet/v3_0/nowrapping/response/ServletOutputStreamInstrumentation.java @@ -77,6 +77,7 @@ public void transform(TypeTransformer transformer) { // ServletOutputStreamInstrumentation.class.getName() + "$OutputStream_close"); } + @SuppressWarnings("unused") static class OutputStream_write { @Advice.OnMethodEnter(suppress = Throwable.class) @@ -106,6 +107,7 @@ public static void exit(@Advice.Enter BoundedByteArrayOutputStream buffer) { } } + @SuppressWarnings("unused") static class OutputStream_writeByteArr { @Advice.OnMethodEnter(suppress = Throwable.class) @@ -136,12 +138,13 @@ public static void exit(@Advice.Enter BoundedByteArrayOutputStream buffer) { } } + @SuppressWarnings("unused") static class OutputStream_writeByteArrOffset { @Advice.OnMethodEnter(suppress = Throwable.class) public static BoundedByteArrayOutputStream enter( @Advice.This ServletOutputStream thizz, - @Advice.Argument(0) byte b[], + @Advice.Argument(0) byte[] b, @Advice.Argument(1) int off, @Advice.Argument(2) int len) { @@ -169,6 +172,7 @@ public static void exit(@Advice.Enter BoundedByteArrayOutputStream buffer) { } } + @SuppressWarnings("unused") static class ServletOutputStream_print { @Advice.OnMethodEnter(suppress = Throwable.class) diff --git a/instrumentation/servlet/servlet-3.0/src/main/java/io/opentelemetry/javaagent/instrumentation/hypertrace/servlet/v3_0/nowrapping/response/ServletResponseInstrumentation.java b/instrumentation/servlet/servlet-3.0/src/main/java/io/opentelemetry/javaagent/instrumentation/hypertrace/servlet/v3_0/nowrapping/response/ServletResponseInstrumentation.java index 79d2671ba..f310e9a87 100644 --- a/instrumentation/servlet/servlet-3.0/src/main/java/io/opentelemetry/javaagent/instrumentation/hypertrace/servlet/v3_0/nowrapping/response/ServletResponseInstrumentation.java +++ b/instrumentation/servlet/servlet-3.0/src/main/java/io/opentelemetry/javaagent/instrumentation/hypertrace/servlet/v3_0/nowrapping/response/ServletResponseInstrumentation.java @@ -63,6 +63,7 @@ public void transform(TypeTransformer transformer) { ServletResponseInstrumentation.class.getName() + "$ServletResponse_getWriter_advice"); } + @SuppressWarnings("unused") static class ServletResponse_getOutputStream { @Advice.OnMethodEnter(suppress = Throwable.class) @@ -127,6 +128,7 @@ public static void exit( } } + @SuppressWarnings("unused") static class ServletResponse_getWriter_advice { @Advice.OnMethodEnter(suppress = Throwable.class) diff --git a/settings.gradle.kts b/settings.gradle.kts index 2edeb428b..dde827cb1 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -58,6 +58,8 @@ include("instrumentation:servlet:servlet-3.0") findProject(":instrumentation:servlet:servlet-3.0")?.name = "servlet-3.0" include("instrumentation:servlet:servlet-rw") findProject(":instrumentation:servlet:servlet-rw")?.name = "servlet-rw" +include("instrumentation:servlet:servlet-2.2") +findProject(":instrumentation:servlet:servlet-2.2")?.name = "servlet-2.2" include("instrumentation:struts-2.3") findProject(":instrumentation:struts-2.3")?.name = "struts-2.3" include("instrumentation:undertow:undertow-common") From 4a71a2dc44e313352d7d6167b93941b075d3abbd Mon Sep 17 00:00:00 2001 From: Kaushal Agrawal Date: Fri, 3 Feb 2023 11:50:37 +0530 Subject: [PATCH 2/7] remove old warnings and add httpServletResponseInstrumentation --- .../hypertrace/agent/filter/api/Filter.java | 1 + .../HttpServletResponseInstrumentation.java | 164 ++++++++++++++++++ .../ServletResponseInstrumentation.java | 14 +- .../core/config/InstrumentationConfig.java | 10 +- .../HypertraceEvaluationException.java | 2 - .../HypertraceSemanticAttributes.java | 12 +- .../buffer/StringMapSpanPair.java | 2 +- .../utils/ContentTypeUtils.java | 10 +- 8 files changed, 190 insertions(+), 25 deletions(-) create mode 100644 instrumentation/servlet/servlet-2.2/src/main/java/io/opentelemetry/javaagent/instrumentation/hypertrace/servlet/v2_2/nowrapping/response/HttpServletResponseInstrumentation.java diff --git a/filter-api/src/main/java/org/hypertrace/agent/filter/api/Filter.java b/filter-api/src/main/java/org/hypertrace/agent/filter/api/Filter.java index 74dfaeee7..57071075f 100644 --- a/filter-api/src/main/java/org/hypertrace/agent/filter/api/Filter.java +++ b/filter-api/src/main/java/org/hypertrace/agent/filter/api/Filter.java @@ -29,6 +29,7 @@ public interface Filter { /** * Evaluate the execution. * + * @param span span * @param headers are used for blocking evaluation. * @return filter result */ diff --git a/instrumentation/servlet/servlet-2.2/src/main/java/io/opentelemetry/javaagent/instrumentation/hypertrace/servlet/v2_2/nowrapping/response/HttpServletResponseInstrumentation.java b/instrumentation/servlet/servlet-2.2/src/main/java/io/opentelemetry/javaagent/instrumentation/hypertrace/servlet/v2_2/nowrapping/response/HttpServletResponseInstrumentation.java new file mode 100644 index 000000000..45155d713 --- /dev/null +++ b/instrumentation/servlet/servlet-2.2/src/main/java/io/opentelemetry/javaagent/instrumentation/hypertrace/servlet/v2_2/nowrapping/response/HttpServletResponseInstrumentation.java @@ -0,0 +1,164 @@ +/* + * Copyright The Hypertrace Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.opentelemetry.javaagent.instrumentation.hypertrace.servlet.v2_2.nowrapping.response; + +import static io.opentelemetry.javaagent.extension.matcher.AgentElementMatchers.hasSuperType; +import static net.bytebuddy.matcher.ElementMatchers.*; + +import io.opentelemetry.api.common.AttributeKey; +import io.opentelemetry.api.trace.Span; +import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation; +import io.opentelemetry.javaagent.extension.instrumentation.TypeTransformer; +import io.opentelemetry.javaagent.instrumentation.api.Java8BytecodeBridge; +import io.opentelemetry.sdk.trace.ReadableSpan; +import javax.servlet.http.HttpServletResponse; +import net.bytebuddy.asm.Advice; +import net.bytebuddy.description.type.TypeDescription; +import net.bytebuddy.matcher.ElementMatcher; +import org.hypertrace.agent.core.instrumentation.HypertraceSemanticAttributes; + +public class HttpServletResponseInstrumentation implements TypeInstrumentation { + + @Override + public ElementMatcher typeMatcher() { + return hasSuperType(named("javax.servlet.http.HttpServletResponse")); + } + /* + * public void setStatus(int sc); + * public void setStatus(int sc, String sm); + */ + @Override + public void transform(TypeTransformer transformer) { + transformer.applyAdviceToMethod( + named("setHeader") + .and(takesArguments(2)) + .and(takesArgument(0, is(String.class))) + .and(takesArgument(1, is(String.class))) + .and(returns(void.class)) + .and(isPublic()), + HttpServletResponseInstrumentation.class.getName() + "$HttpServletResponse_setHeader"); + transformer.applyAdviceToMethod( + named("addHeader") + .and(takesArguments(2)) + .and(takesArgument(0, is(String.class))) + .and(takesArgument(1, is(String.class))) + .and(returns(void.class)) + .and(isPublic()), + HttpServletResponseInstrumentation.class.getName() + "$HttpServletResponse_addHeader"); + for (String methodName : new String[] {"setDateHeader", "addDateHeader"}) { + transformer.applyAdviceToMethod( + named(methodName) + .and(takesArguments(2)) + .and(takesArgument(0, is(String.class))) + .and(takesArgument(1, is(long.class))) + .and(returns(void.class)) + .and(isPublic()), + HttpServletResponseInstrumentation.class.getName() + + "$HttpServletResponse_setDateHeader"); + } + for (String methodName : new String[] {"setIntHeader", "addIntHeader"}) { + transformer.applyAdviceToMethod( + named(methodName) + .and(takesArguments(2)) + .and(takesArgument(0, is(String.class))) + .and(takesArgument(1, is(int.class))) + .and(returns(void.class)) + .and(isPublic()), + HttpServletResponseInstrumentation.class.getName() + "$HttpServletResponse_setIntHeader"); + } + } + + @SuppressWarnings("unused") + static class HttpServletResponse_setHeader { + @Advice.OnMethodEnter(suppress = Throwable.class) + public static void enter( + @Advice.This HttpServletResponse httpServletResponse, + @Advice.Argument(value = 0) String headerName, + @Advice.Argument(value = 1) String headerValue) { + System.out.printf( + "start Enter javax.servlet.http.HttpServletResponse.setHeader (%s, %s)\n", + headerName, headerValue); + Span currentSpan = Java8BytecodeBridge.currentSpan(); + AttributeKey attributeKey = + HypertraceSemanticAttributes.httpResponseHeader(headerName); + currentSpan.setAttribute(attributeKey, headerValue); + System.out.println("end Enter javax.servlet.http.HttpServletResponse.setHeader"); + } + } + + @SuppressWarnings("unused") + static class HttpServletResponse_addHeader { + @Advice.OnMethodEnter(suppress = Throwable.class) + public static void enter( + @Advice.This HttpServletResponse httpServletResponse, + @Advice.Argument(value = 0) String headerName, + @Advice.Argument(value = 1) String headerValue) { + System.out.printf( + "start Enter javax.servlet.http.HttpServletResponse.addHeader (%s, %s)\n", + headerName, headerValue); + Span currentSpan = Java8BytecodeBridge.currentSpan(); + if (!(currentSpan instanceof ReadableSpan)) { + return; + } + AttributeKey attributeKey = + HypertraceSemanticAttributes.httpResponseHeader(headerName); + String oldHeaderValue = ((ReadableSpan) currentSpan).getAttribute(attributeKey); + if (oldHeaderValue == null) { + currentSpan.setAttribute(attributeKey, headerValue); + } else { + currentSpan.setAttribute(attributeKey, String.join(",", oldHeaderValue, headerValue)); + } + System.out.println("end Enter javax.servlet.http.HttpServletResponse.addHeader"); + } + } + + @SuppressWarnings("unused") + static class HttpServletResponse_setDateHeader { + @Advice.OnMethodEnter(suppress = Throwable.class) + public static void enter( + @Advice.This HttpServletResponse httpServletResponse, + @Advice.Argument(value = 0) String headerName, + @Advice.Argument(value = 1) long headerValue) { + System.out.printf( + "start Enter javax.servlet.http.HttpServletResponse.setDateHeader (%s, %d)\n", + headerName, headerValue); + Span currentSpan = Java8BytecodeBridge.currentSpan(); + AttributeKey attributeKey = + HypertraceSemanticAttributes.httpResponseHeaderLong(headerName); + currentSpan.setAttribute(attributeKey, headerValue); + System.out.println("end Enter javax.servlet.http.HttpServletResponse.setDateHeader"); + } + } + + @SuppressWarnings("unused") + static class HttpServletResponse_setIntHeader { + @Advice.OnMethodEnter(suppress = Throwable.class) + public static void enter( + @Advice.This HttpServletResponse httpServletResponse, + @Advice.Argument(value = 0) String headerName, + @Advice.Argument(value = 1) int headerValue) { + System.out.printf( + "start Enter javax.servlet.http.HttpServletResponse.setIntHeader (%s, %d)\n", + headerName, headerValue); + Span currentSpan = Java8BytecodeBridge.currentSpan(); + AttributeKey attributeKey = + HypertraceSemanticAttributes.httpResponseHeaderLong(headerName); + currentSpan.setAttribute(attributeKey, headerValue); + System.out.println("end Enter javax.servlet.http.HttpServletResponse.setIntHeader"); + } + } +} diff --git a/instrumentation/servlet/servlet-2.2/src/main/java/io/opentelemetry/javaagent/instrumentation/hypertrace/servlet/v2_2/nowrapping/response/ServletResponseInstrumentation.java b/instrumentation/servlet/servlet-2.2/src/main/java/io/opentelemetry/javaagent/instrumentation/hypertrace/servlet/v2_2/nowrapping/response/ServletResponseInstrumentation.java index 0d7699fcc..e5c259ca4 100644 --- a/instrumentation/servlet/servlet-2.2/src/main/java/io/opentelemetry/javaagent/instrumentation/hypertrace/servlet/v2_2/nowrapping/response/ServletResponseInstrumentation.java +++ b/instrumentation/servlet/servlet-2.2/src/main/java/io/opentelemetry/javaagent/instrumentation/hypertrace/servlet/v2_2/nowrapping/response/ServletResponseInstrumentation.java @@ -17,10 +17,7 @@ package io.opentelemetry.javaagent.instrumentation.hypertrace.servlet.v2_2.nowrapping.response; import static io.opentelemetry.javaagent.extension.matcher.AgentElementMatchers.hasSuperType; -import static net.bytebuddy.matcher.ElementMatchers.isPublic; -import static net.bytebuddy.matcher.ElementMatchers.named; -import static net.bytebuddy.matcher.ElementMatchers.returns; -import static net.bytebuddy.matcher.ElementMatchers.takesArguments; +import static net.bytebuddy.matcher.ElementMatchers.*; import io.opentelemetry.instrumentation.api.field.VirtualField; import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation; @@ -73,9 +70,8 @@ public static HttpServletResponse enter(@Advice.This ServletResponse servletResp return null; } // ignore wrappers, the filter/servlet instrumentation gets the captured body from context - // store - // by using response as a key and the filter/servlet instrumentation runs early when wrappers - // are not used. + // store by using response as a key and the filter/servlet instrumentation runs early when + // wrappers are not used. HttpServletResponse httpServletResponse = (HttpServletResponse) servletResponse; // the getReader method might call getInputStream HypertraceCallDepthThreadLocalMap.incrementCallDepth(ServletResponse.class); @@ -115,7 +111,7 @@ public static void exit( // do not capture if data capture is disabled or not supported content type InstrumentationConfig instrumentationConfig = InstrumentationConfig.ConfigProvider.get(); - // TODO: check response contentType before capturing response body + // TODO: capture body based on response content type if (instrumentationConfig.httpBody().response()) { String charsetStr = httpServletResponse.getCharacterEncoding(); Charset charset = ContentTypeCharsetUtils.toCharset(charsetStr); @@ -179,7 +175,7 @@ public static void exit( // do not capture if data capture is disabled or not supported content type InstrumentationConfig instrumentationConfig = InstrumentationConfig.ConfigProvider.get(); - // TODO: check response contentType before capturing the response body + // TODO: capture body based on response content type if (instrumentationConfig.httpBody().response()) { BoundedCharArrayWriter writer = BoundedBuffersFactory.createWriter(); contextStore.set(printWriter, writer); diff --git a/javaagent-core/src/main/java/org/hypertrace/agent/core/config/InstrumentationConfig.java b/javaagent-core/src/main/java/org/hypertrace/agent/core/config/InstrumentationConfig.java index 9aec3fe57..cc83effd4 100644 --- a/javaagent-core/src/main/java/org/hypertrace/agent/core/config/InstrumentationConfig.java +++ b/javaagent-core/src/main/java/org/hypertrace/agent/core/config/InstrumentationConfig.java @@ -24,19 +24,19 @@ /** Instrumentation config holds configuration for the instrumentation. */ public interface InstrumentationConfig { - /** Maximum capture body size in bytes. */ + /** @return Maximum capture body size in bytes. */ int maxBodySizeBytes(); - /** Data capture for HTTP headers. */ + /** @return Data capture for HTTP headers. */ Message httpHeaders(); - /** Data capture for HTTP body. */ + /** @return Data capture for HTTP body. */ Message httpBody(); - /** Data capture for RPC metadata. */ + /** @return Data capture for RPC metadata. */ Message rpcMetadata(); - /** Data capture for RPC body */ + /** @return Data capture for RPC body */ Message rpcBody(); /** Message holds data capture configuration for various entities. */ diff --git a/javaagent-core/src/main/java/org/hypertrace/agent/core/instrumentation/HypertraceEvaluationException.java b/javaagent-core/src/main/java/org/hypertrace/agent/core/instrumentation/HypertraceEvaluationException.java index 5bba0275a..f6c55a73c 100644 --- a/javaagent-core/src/main/java/org/hypertrace/agent/core/instrumentation/HypertraceEvaluationException.java +++ b/javaagent-core/src/main/java/org/hypertrace/agent/core/instrumentation/HypertraceEvaluationException.java @@ -21,8 +21,6 @@ * opportunity for middleware to look at the contents of an HTTP request and determine if it should * be processed before handing it off to application code for execution. * - *

- * *

Framework-specific instrumentation of middlewares (e.g. {@code javax.servlet.Filter}) or * implementations of framework-specific middlewares (e.g. {@code * io.netty.channel.ChannelInboundHandlerAdapter} and {@code io.grpc.ServerInterceptor}) should diff --git a/javaagent-core/src/main/java/org/hypertrace/agent/core/instrumentation/HypertraceSemanticAttributes.java b/javaagent-core/src/main/java/org/hypertrace/agent/core/instrumentation/HypertraceSemanticAttributes.java index 5e94c8cd7..66cdd12bf 100644 --- a/javaagent-core/src/main/java/org/hypertrace/agent/core/instrumentation/HypertraceSemanticAttributes.java +++ b/javaagent-core/src/main/java/org/hypertrace/agent/core/instrumentation/HypertraceSemanticAttributes.java @@ -33,10 +33,18 @@ public static AttributeKey httpRequestHeader(String header) { return AttributeKey.stringKey("http.request.header." + header.toLowerCase()); } + public static AttributeKey httpRequestHeaderLong(String header) { + return AttributeKey.longKey("http.request.header." + header.toLowerCase()); + } + public static AttributeKey httpResponseHeader(String header) { return AttributeKey.stringKey("http.response.header." + header.toLowerCase()); } + public static AttributeKey httpResponseHeaderLong(String header) { + return AttributeKey.longKey("http.response.header." + header.toLowerCase()); + } + public static final AttributeKey HTTP_REQUEST_BODY = AttributeKey.stringKey("http.request.body"); public static final AttributeKey HTTP_RESPONSE_BODY = @@ -50,11 +58,11 @@ public static AttributeKey httpResponseHeader(String header) { public static final AttributeKey RPC_RESPONSE_BODY = AttributeKey.stringKey("rpc.response.body"); - public static final AttributeKey rpcRequestMetadata(String key) { + public static AttributeKey rpcRequestMetadata(String key) { return AttributeKey.stringKey("rpc.request.metadata." + key.toLowerCase()); } - public static final AttributeKey rpcResponseMetadata(String key) { + public static AttributeKey rpcResponseMetadata(String key) { return AttributeKey.stringKey("rpc.response.metadata." + key.toLowerCase()); } } diff --git a/javaagent-core/src/main/java/org/hypertrace/agent/core/instrumentation/buffer/StringMapSpanPair.java b/javaagent-core/src/main/java/org/hypertrace/agent/core/instrumentation/buffer/StringMapSpanPair.java index 911050e40..32976ba53 100644 --- a/javaagent-core/src/main/java/org/hypertrace/agent/core/instrumentation/buffer/StringMapSpanPair.java +++ b/javaagent-core/src/main/java/org/hypertrace/agent/core/instrumentation/buffer/StringMapSpanPair.java @@ -41,7 +41,7 @@ public StringMapSpanPair(Span span, Map stringMap, Map attributeKey) { if (!mapCaptured) { diff --git a/javaagent-core/src/main/java/org/hypertrace/agent/core/instrumentation/utils/ContentTypeUtils.java b/javaagent-core/src/main/java/org/hypertrace/agent/core/instrumentation/utils/ContentTypeUtils.java index aefc36d9a..0e5840fc0 100644 --- a/javaagent-core/src/main/java/org/hypertrace/agent/core/instrumentation/utils/ContentTypeUtils.java +++ b/javaagent-core/src/main/java/org/hypertrace/agent/core/instrumentation/utils/ContentTypeUtils.java @@ -64,10 +64,8 @@ private static synchronized ContentTypeUtils getInstanceSync() { } /** - * Determines if the content type should be captured by the agent. - * - * @param contentType - * @return + * @param contentType content type to be checked + * @return true if the content type should be captured by the agent. */ private boolean shouldCapture_(String contentType) { if (contentType == null) { @@ -121,8 +119,8 @@ public static String parseCharset(String contentType) { /** * Converts an arbitrary object into JSON format. * - * @param obj - * @return + * @param obj an arbitrary object + * @return a string representation of the obj in JSON format */ public static String convertToJSONString(Object obj) { try { From d7eecf69d89302090926ca518d603d7aaf10b0f6 Mon Sep 17 00:00:00 2001 From: Kaushal Agrawal Date: Wed, 15 Mar 2023 20:20:56 +0530 Subject: [PATCH 3/7] merge instrumentation files --- instrumentation/build.gradle.kts | 2 +- .../NettyChannelPipelineInstrumentation.java | 2 + .../servlet/servlet-2.2/build.gradle.kts | 3 +- .../Servlet2AndFilterInstrumentation.java | 7 +- .../Servlet2InstrumentationModule.java | 2 +- .../HttpServletResponseInstrumentation.java | 164 ------------------ .../ServletResponseInstrumentation.java | 150 +++++++++++++++- .../HttpRequestHandleInstrumentation.java | 3 + .../vertx/HttpRequestInstrumentation.java | 41 ++++- .../vertx/HttpResponseInstrumentation.java | 3 + .../HypertraceSemanticAttributes.java | 2 + javaagent/build.gradle.kts | 1 + settings.gradle.kts | 4 +- 13 files changed, 206 insertions(+), 178 deletions(-) delete mode 100644 instrumentation/servlet/servlet-2.2/src/main/java/io/opentelemetry/javaagent/instrumentation/hypertrace/servlet/v2_2/nowrapping/response/HttpServletResponseInstrumentation.java diff --git a/instrumentation/build.gradle.kts b/instrumentation/build.gradle.kts index e722ffe74..a48a2e2f0 100644 --- a/instrumentation/build.gradle.kts +++ b/instrumentation/build.gradle.kts @@ -5,8 +5,8 @@ plugins { dependencies{ implementation(project(":instrumentation:servlet:servlet-rw")) - implementation(project(":instrumentation:servlet:servlet-3.0")) implementation(project(":instrumentation:servlet:servlet-2.2")) + implementation(project(":instrumentation:servlet:servlet-3.0")) implementation(project(":instrumentation:spark-2.3")) implementation(project(":instrumentation:grpc-1.6")) implementation(project(":instrumentation:grpc-shaded-netty-1.9")) diff --git a/instrumentation/netty/netty-4.1/src/main/java/io/opentelemetry/javaagent/instrumentation/hypertrace/netty/v4_1/NettyChannelPipelineInstrumentation.java b/instrumentation/netty/netty-4.1/src/main/java/io/opentelemetry/javaagent/instrumentation/hypertrace/netty/v4_1/NettyChannelPipelineInstrumentation.java index bd6561046..7b2a73e3a 100644 --- a/instrumentation/netty/netty-4.1/src/main/java/io/opentelemetry/javaagent/instrumentation/hypertrace/netty/v4_1/NettyChannelPipelineInstrumentation.java +++ b/instrumentation/netty/netty-4.1/src/main/java/io/opentelemetry/javaagent/instrumentation/hypertrace/netty/v4_1/NettyChannelPipelineInstrumentation.java @@ -169,3 +169,5 @@ public static void addHandler( } } } + + diff --git a/instrumentation/servlet/servlet-2.2/build.gradle.kts b/instrumentation/servlet/servlet-2.2/build.gradle.kts index f5e1af80e..f5c7cb164 100644 --- a/instrumentation/servlet/servlet-2.2/build.gradle.kts +++ b/instrumentation/servlet/servlet-2.2/build.gradle.kts @@ -16,7 +16,7 @@ muzzle { fail { group = "javax.servlet" module = "javax.servlet-api" - versions = "[3.0,)" + versions = "(,)" } } @@ -33,7 +33,6 @@ val versions: Map by extra dependencies { implementation("io.opentelemetry.javaagent.instrumentation:opentelemetry-javaagent-servlet-common:${versions["opentelemetry_java_agent"]}") - implementation("io.opentelemetry.javaagent.instrumentation:opentelemetry-javaagent-servlet-2.2:${versions["opentelemetry_java_agent"]}") // Servlet2Accessor compileOnly("io.opentelemetry.javaagent:opentelemetry-javaagent-bootstrap:${versions["opentelemetry_java_agent"]}") testRuntimeOnly("io.opentelemetry.javaagent.instrumentation:opentelemetry-javaagent-servlet-common-bootstrap:${versions["opentelemetry_java_agent"]}") muzzleBootstrap("io.opentelemetry.javaagent.instrumentation:opentelemetry-javaagent-servlet-common-bootstrap:${versions["opentelemetry_java_agent"]}") diff --git a/instrumentation/servlet/servlet-2.2/src/main/java/io/opentelemetry/javaagent/instrumentation/hypertrace/servlet/v2_2/nowrapping/Servlet2AndFilterInstrumentation.java b/instrumentation/servlet/servlet-2.2/src/main/java/io/opentelemetry/javaagent/instrumentation/hypertrace/servlet/v2_2/nowrapping/Servlet2AndFilterInstrumentation.java index 1662ea29b..a0c7e53f7 100644 --- a/instrumentation/servlet/servlet-2.2/src/main/java/io/opentelemetry/javaagent/instrumentation/hypertrace/servlet/v2_2/nowrapping/Servlet2AndFilterInstrumentation.java +++ b/instrumentation/servlet/servlet-2.2/src/main/java/io/opentelemetry/javaagent/instrumentation/hypertrace/servlet/v2_2/nowrapping/Servlet2AndFilterInstrumentation.java @@ -20,6 +20,7 @@ import static io.opentelemetry.javaagent.extension.matcher.AgentElementMatchers.hasSuperType; import static net.bytebuddy.matcher.ElementMatchers.isPublic; import static net.bytebuddy.matcher.ElementMatchers.named; +import static net.bytebuddy.matcher.ElementMatchers.namedOneOf; import static net.bytebuddy.matcher.ElementMatchers.takesArgument; import io.opentelemetry.api.common.AttributeKey; @@ -53,7 +54,6 @@ import org.hypertrace.agent.filter.FilterRegistry; public class Servlet2AndFilterInstrumentation implements TypeInstrumentation { - @Override public ElementMatcher classLoaderOptimization() { return hasClassesNamed("javax.servlet.Servlet"); @@ -61,13 +61,14 @@ public ElementMatcher classLoaderOptimization() { @Override public ElementMatcher typeMatcher() { - return hasSuperType(named("javax.servlet.Servlet")); + return hasSuperType(namedOneOf("javax.servlet.Filter", "javax.servlet.Servlet")); } @Override public void transform(TypeTransformer transformer) { + // print version with Implmentation Version transformer.applyAdviceToMethod( - named("service") + namedOneOf("doFilter", "service") .and(takesArgument(0, named("javax.servlet.ServletRequest"))) .and(takesArgument(1, named("javax.servlet.ServletResponse"))) .and(isPublic()), diff --git a/instrumentation/servlet/servlet-2.2/src/main/java/io/opentelemetry/javaagent/instrumentation/hypertrace/servlet/v2_2/nowrapping/Servlet2InstrumentationModule.java b/instrumentation/servlet/servlet-2.2/src/main/java/io/opentelemetry/javaagent/instrumentation/hypertrace/servlet/v2_2/nowrapping/Servlet2InstrumentationModule.java index efb8603da..2e9f64458 100644 --- a/instrumentation/servlet/servlet-2.2/src/main/java/io/opentelemetry/javaagent/instrumentation/hypertrace/servlet/v2_2/nowrapping/Servlet2InstrumentationModule.java +++ b/instrumentation/servlet/servlet-2.2/src/main/java/io/opentelemetry/javaagent/instrumentation/hypertrace/servlet/v2_2/nowrapping/Servlet2InstrumentationModule.java @@ -36,7 +36,7 @@ public Servlet2InstrumentationModule() { @Override public int order() { - return 1; + return -1; } @Override diff --git a/instrumentation/servlet/servlet-2.2/src/main/java/io/opentelemetry/javaagent/instrumentation/hypertrace/servlet/v2_2/nowrapping/response/HttpServletResponseInstrumentation.java b/instrumentation/servlet/servlet-2.2/src/main/java/io/opentelemetry/javaagent/instrumentation/hypertrace/servlet/v2_2/nowrapping/response/HttpServletResponseInstrumentation.java deleted file mode 100644 index 45155d713..000000000 --- a/instrumentation/servlet/servlet-2.2/src/main/java/io/opentelemetry/javaagent/instrumentation/hypertrace/servlet/v2_2/nowrapping/response/HttpServletResponseInstrumentation.java +++ /dev/null @@ -1,164 +0,0 @@ -/* - * Copyright The Hypertrace Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package io.opentelemetry.javaagent.instrumentation.hypertrace.servlet.v2_2.nowrapping.response; - -import static io.opentelemetry.javaagent.extension.matcher.AgentElementMatchers.hasSuperType; -import static net.bytebuddy.matcher.ElementMatchers.*; - -import io.opentelemetry.api.common.AttributeKey; -import io.opentelemetry.api.trace.Span; -import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation; -import io.opentelemetry.javaagent.extension.instrumentation.TypeTransformer; -import io.opentelemetry.javaagent.instrumentation.api.Java8BytecodeBridge; -import io.opentelemetry.sdk.trace.ReadableSpan; -import javax.servlet.http.HttpServletResponse; -import net.bytebuddy.asm.Advice; -import net.bytebuddy.description.type.TypeDescription; -import net.bytebuddy.matcher.ElementMatcher; -import org.hypertrace.agent.core.instrumentation.HypertraceSemanticAttributes; - -public class HttpServletResponseInstrumentation implements TypeInstrumentation { - - @Override - public ElementMatcher typeMatcher() { - return hasSuperType(named("javax.servlet.http.HttpServletResponse")); - } - /* - * public void setStatus(int sc); - * public void setStatus(int sc, String sm); - */ - @Override - public void transform(TypeTransformer transformer) { - transformer.applyAdviceToMethod( - named("setHeader") - .and(takesArguments(2)) - .and(takesArgument(0, is(String.class))) - .and(takesArgument(1, is(String.class))) - .and(returns(void.class)) - .and(isPublic()), - HttpServletResponseInstrumentation.class.getName() + "$HttpServletResponse_setHeader"); - transformer.applyAdviceToMethod( - named("addHeader") - .and(takesArguments(2)) - .and(takesArgument(0, is(String.class))) - .and(takesArgument(1, is(String.class))) - .and(returns(void.class)) - .and(isPublic()), - HttpServletResponseInstrumentation.class.getName() + "$HttpServletResponse_addHeader"); - for (String methodName : new String[] {"setDateHeader", "addDateHeader"}) { - transformer.applyAdviceToMethod( - named(methodName) - .and(takesArguments(2)) - .and(takesArgument(0, is(String.class))) - .and(takesArgument(1, is(long.class))) - .and(returns(void.class)) - .and(isPublic()), - HttpServletResponseInstrumentation.class.getName() - + "$HttpServletResponse_setDateHeader"); - } - for (String methodName : new String[] {"setIntHeader", "addIntHeader"}) { - transformer.applyAdviceToMethod( - named(methodName) - .and(takesArguments(2)) - .and(takesArgument(0, is(String.class))) - .and(takesArgument(1, is(int.class))) - .and(returns(void.class)) - .and(isPublic()), - HttpServletResponseInstrumentation.class.getName() + "$HttpServletResponse_setIntHeader"); - } - } - - @SuppressWarnings("unused") - static class HttpServletResponse_setHeader { - @Advice.OnMethodEnter(suppress = Throwable.class) - public static void enter( - @Advice.This HttpServletResponse httpServletResponse, - @Advice.Argument(value = 0) String headerName, - @Advice.Argument(value = 1) String headerValue) { - System.out.printf( - "start Enter javax.servlet.http.HttpServletResponse.setHeader (%s, %s)\n", - headerName, headerValue); - Span currentSpan = Java8BytecodeBridge.currentSpan(); - AttributeKey attributeKey = - HypertraceSemanticAttributes.httpResponseHeader(headerName); - currentSpan.setAttribute(attributeKey, headerValue); - System.out.println("end Enter javax.servlet.http.HttpServletResponse.setHeader"); - } - } - - @SuppressWarnings("unused") - static class HttpServletResponse_addHeader { - @Advice.OnMethodEnter(suppress = Throwable.class) - public static void enter( - @Advice.This HttpServletResponse httpServletResponse, - @Advice.Argument(value = 0) String headerName, - @Advice.Argument(value = 1) String headerValue) { - System.out.printf( - "start Enter javax.servlet.http.HttpServletResponse.addHeader (%s, %s)\n", - headerName, headerValue); - Span currentSpan = Java8BytecodeBridge.currentSpan(); - if (!(currentSpan instanceof ReadableSpan)) { - return; - } - AttributeKey attributeKey = - HypertraceSemanticAttributes.httpResponseHeader(headerName); - String oldHeaderValue = ((ReadableSpan) currentSpan).getAttribute(attributeKey); - if (oldHeaderValue == null) { - currentSpan.setAttribute(attributeKey, headerValue); - } else { - currentSpan.setAttribute(attributeKey, String.join(",", oldHeaderValue, headerValue)); - } - System.out.println("end Enter javax.servlet.http.HttpServletResponse.addHeader"); - } - } - - @SuppressWarnings("unused") - static class HttpServletResponse_setDateHeader { - @Advice.OnMethodEnter(suppress = Throwable.class) - public static void enter( - @Advice.This HttpServletResponse httpServletResponse, - @Advice.Argument(value = 0) String headerName, - @Advice.Argument(value = 1) long headerValue) { - System.out.printf( - "start Enter javax.servlet.http.HttpServletResponse.setDateHeader (%s, %d)\n", - headerName, headerValue); - Span currentSpan = Java8BytecodeBridge.currentSpan(); - AttributeKey attributeKey = - HypertraceSemanticAttributes.httpResponseHeaderLong(headerName); - currentSpan.setAttribute(attributeKey, headerValue); - System.out.println("end Enter javax.servlet.http.HttpServletResponse.setDateHeader"); - } - } - - @SuppressWarnings("unused") - static class HttpServletResponse_setIntHeader { - @Advice.OnMethodEnter(suppress = Throwable.class) - public static void enter( - @Advice.This HttpServletResponse httpServletResponse, - @Advice.Argument(value = 0) String headerName, - @Advice.Argument(value = 1) int headerValue) { - System.out.printf( - "start Enter javax.servlet.http.HttpServletResponse.setIntHeader (%s, %d)\n", - headerName, headerValue); - Span currentSpan = Java8BytecodeBridge.currentSpan(); - AttributeKey attributeKey = - HypertraceSemanticAttributes.httpResponseHeaderLong(headerName); - currentSpan.setAttribute(attributeKey, headerValue); - System.out.println("end Enter javax.servlet.http.HttpServletResponse.setIntHeader"); - } - } -} diff --git a/instrumentation/servlet/servlet-2.2/src/main/java/io/opentelemetry/javaagent/instrumentation/hypertrace/servlet/v2_2/nowrapping/response/ServletResponseInstrumentation.java b/instrumentation/servlet/servlet-2.2/src/main/java/io/opentelemetry/javaagent/instrumentation/hypertrace/servlet/v2_2/nowrapping/response/ServletResponseInstrumentation.java index e5c259ca4..bb05e1ab4 100644 --- a/instrumentation/servlet/servlet-2.2/src/main/java/io/opentelemetry/javaagent/instrumentation/hypertrace/servlet/v2_2/nowrapping/response/ServletResponseInstrumentation.java +++ b/instrumentation/servlet/servlet-2.2/src/main/java/io/opentelemetry/javaagent/instrumentation/hypertrace/servlet/v2_2/nowrapping/response/ServletResponseInstrumentation.java @@ -19,9 +19,13 @@ import static io.opentelemetry.javaagent.extension.matcher.AgentElementMatchers.hasSuperType; import static net.bytebuddy.matcher.ElementMatchers.*; +import io.opentelemetry.api.common.AttributeKey; +import io.opentelemetry.api.trace.Span; import io.opentelemetry.instrumentation.api.field.VirtualField; import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation; import io.opentelemetry.javaagent.extension.instrumentation.TypeTransformer; +import io.opentelemetry.javaagent.instrumentation.api.Java8BytecodeBridge; +import io.opentelemetry.sdk.trace.ReadableSpan; import java.io.PrintWriter; import java.nio.charset.Charset; import javax.servlet.ServletOutputStream; @@ -32,6 +36,7 @@ import net.bytebuddy.matcher.ElementMatcher; import org.hypertrace.agent.core.config.InstrumentationConfig; import org.hypertrace.agent.core.instrumentation.HypertraceCallDepthThreadLocalMap; +import org.hypertrace.agent.core.instrumentation.HypertraceSemanticAttributes; import org.hypertrace.agent.core.instrumentation.SpanAndObjectPair; import org.hypertrace.agent.core.instrumentation.buffer.BoundedBuffersFactory; import org.hypertrace.agent.core.instrumentation.buffer.BoundedByteArrayOutputStream; @@ -42,7 +47,7 @@ public class ServletResponseInstrumentation implements TypeInstrumentation { @Override public ElementMatcher typeMatcher() { - return hasSuperType(named("javax.servlet.ServletResponse")); + return hasSuperType(named("javax.servlet.http.HttpServletResponse")); } @Override @@ -55,7 +60,146 @@ public void transform(TypeTransformer transformer) { ServletResponseInstrumentation.class.getName() + "$ServletResponse_getOutputStream"); transformer.applyAdviceToMethod( named("getWriter").and(takesArguments(0)).and(returns(PrintWriter.class)).and(isPublic()), - ServletResponseInstrumentation.class.getName() + "$ServletResponse_getWriter_advice"); + ServletResponseInstrumentation.class.getName() + "$ServletResponse_getWriter"); + transformer.applyAdviceToMethod( + named("setContentType").and(takesArgument(0, String.class)).and(isPublic()), + ServletResponseInstrumentation.class.getName() + "$ServletResponse_setContentType"); + transformer.applyAdviceToMethod( + named("setContentLength").and(takesArgument(0, int.class)).and(isPublic()), + ServletResponseInstrumentation.class.getName() + "$ServletResponse_setContentLength"); + transformer.applyAdviceToMethod( + named("setCharacterEncoding").and(takesArgument(0, String.class)).and(isPublic()), + ServletResponseInstrumentation.class.getName() + "$ServletResponse_setCharacterEncoding"); + transformer.applyAdviceToMethod( + named("setStatus").and(takesArgument(0, int.class)).and(isPublic()), + ServletResponseInstrumentation.class.getName() + "$HttpServletResponse_setStatus"); + transformer.applyAdviceToMethod( + named("setHeader").and(takesArguments(2)).and(isPublic()), + ServletResponseInstrumentation.class.getName() + "$HttpServletResponse_setHeader"); + transformer.applyAdviceToMethod( + named("addHeader").and(takesArguments(2)).and(isPublic()), + ServletResponseInstrumentation.class.getName() + "$HttpServletResponse_addHeader"); + for (String methodName : new String[] {"setDateHeader", "addDateHeader"}) { + transformer.applyAdviceToMethod( + named(methodName).and(takesArguments(2)).and(isPublic()), + ServletResponseInstrumentation.class.getName() + "$HttpServletResponse_setDateHeader"); + } + for (String methodName : new String[] {"setIntHeader", "addIntHeader"}) { + transformer.applyAdviceToMethod( + named(methodName).and(takesArguments(2)).and(isPublic()), + ServletResponseInstrumentation.class.getName() + "$HttpServletResponse_setIntHeader"); + } + } + + static class ServletResponse_setContentType { + @Advice.OnMethodExit(suppress = Throwable.class) + public static void exit(@Advice.Argument(0) String type) { + System.out.println("inside javax.servlet.ServletResponse.setContentType [" + type + "]"); + } + } + + static class ServletResponse_setContentLength { + @Advice.OnMethodExit(suppress = Throwable.class) + public static void exit(@Advice.Argument(0) int len) { + System.out.println("inside javax.servlet.ServletResponse.setContentLength [" + len + "]"); + } + } + + static class ServletResponse_setCharacterEncoding { + @Advice.OnMethodExit(suppress = Throwable.class) + public static void exit(@Advice.Argument(0) String charset) { + System.out.println( + "inside javax.servlet.ServletResponse.setCharacterEncoding [" + charset + "]"); + } + } + + @SuppressWarnings("unused") + static class HttpServletResponse_setStatus { + @Advice.OnMethodExit(suppress = Throwable.class) + public static void exit(@Advice.Argument(value = 0) int status) { + System.out.println( + "inside javax.servlet.http.HttpServletResponse.setStatus [" + status + "]"); + } + } + + @SuppressWarnings("unused") + static class HttpServletResponse_setHeader { + @Advice.OnMethodExit(suppress = Throwable.class) + public static void exit( + @Advice.Argument(value = 0) String headerName, + @Advice.Argument(value = 1) String headerValue) { + System.out.println( + "inside javax.servlet.http.HttpServletResponse.setHeader [" + + headerName + + ", " + + headerValue + + "]"); + Java8BytecodeBridge.currentSpan() + .setAttribute(HypertraceSemanticAttributes.httpResponseHeader(headerName), headerValue); + } + } + + @SuppressWarnings("unused") + static class HttpServletResponse_addHeader { + @Advice.OnMethodExit(suppress = Throwable.class) + public static void exit( + @Advice.Argument(value = 0) String headerName, + @Advice.Argument(value = 1) String headerValue) { + System.out.println( + "inside javax.servlet.http.HttpServletResponse.addHeader [" + + headerName + + ", " + + headerValue + + "]"); + Span currentSpan = Java8BytecodeBridge.currentSpan(); + if (!(currentSpan instanceof ReadableSpan)) { + return; + } + AttributeKey attributeKey = + HypertraceSemanticAttributes.httpResponseHeader(headerName); + String oldHeaderValue = ((ReadableSpan) currentSpan).getAttribute(attributeKey); + if (oldHeaderValue == null) { + currentSpan.setAttribute(attributeKey, headerValue); + } else { + currentSpan.setAttribute(attributeKey, String.join(",", oldHeaderValue, headerValue)); + } + } + } + + @SuppressWarnings("unused") + static class HttpServletResponse_setDateHeader { + @Advice.OnMethodExit(suppress = Throwable.class) + public static void exit( + @Advice.Argument(value = 0) String headerName, + @Advice.Argument(value = 1) long headerValue) { + System.out.println( + "inside javax.servlet.http.HttpServletResponse.setDateHeader [" + + headerName + + ", " + + headerValue + + "]"); + Java8BytecodeBridge.currentSpan() + .setAttribute( + HypertraceSemanticAttributes.httpResponseHeaderLong(headerName), headerValue); + } + } + + @SuppressWarnings("unused") + static class HttpServletResponse_setIntHeader { + @Advice.OnMethodExit(suppress = Throwable.class) + public static void exit( + @Advice.Argument(value = 0) String headerName, + @Advice.Argument(value = 1) int headerValue) { + System.out.println( + "inside javax.servlet.http.HttpServletResponse.setIntHeader [" + + headerName + + ", " + + headerValue + + "]"); + Java8BytecodeBridge.currentSpan() + .setAttribute( + HypertraceSemanticAttributes.httpResponseHeaderLong(headerName), headerValue); + } } @SuppressWarnings("unused") @@ -127,7 +271,7 @@ public static void exit( } @SuppressWarnings("unused") - static class ServletResponse_getWriter_advice { + static class ServletResponse_getWriter { @Advice.OnMethodEnter(suppress = Throwable.class) public static HttpServletResponse enter(@Advice.This ServletResponse servletResponse) { diff --git a/instrumentation/vertx/vertx-web-3.0/src/main/java/io/opentelemetry/javaagent/instrumentation/hypertrace/vertx/HttpRequestHandleInstrumentation.java b/instrumentation/vertx/vertx-web-3.0/src/main/java/io/opentelemetry/javaagent/instrumentation/hypertrace/vertx/HttpRequestHandleInstrumentation.java index 415d0e112..4b6093821 100644 --- a/instrumentation/vertx/vertx-web-3.0/src/main/java/io/opentelemetry/javaagent/instrumentation/hypertrace/vertx/HttpRequestHandleInstrumentation.java +++ b/instrumentation/vertx/vertx-web-3.0/src/main/java/io/opentelemetry/javaagent/instrumentation/hypertrace/vertx/HttpRequestHandleInstrumentation.java @@ -60,9 +60,11 @@ public static class HandleResponseAdvice { @Advice.OnMethodEnter(suppress = Throwable.class) public static void handleResponseEnter( @Advice.This HttpClientRequest request, @Advice.Argument(0) HttpClientResponse response) { + System.out.println("start Enter io.vertx.core.http.HttpClientRequest.handleResponse"); Contexts contexts = VirtualField.find(HttpClientRequest.class, Contexts.class).get(request); if (contexts == null) { + System.out.println("end1 Enter io.vertx.core.http.HttpClientRequest.handleResponse"); return; } Span span = Span.fromContext(contexts.context); @@ -87,6 +89,7 @@ public static void handleResponseEnter( && ContentTypeUtils.shouldCapture(contentType)) { VirtualField.find(HttpClientResponse.class, Span.class).set(response, span); } + System.out.println("end Enter io.vertx.core.http.HttpClientRequest.handleResponse"); } } } diff --git a/instrumentation/vertx/vertx-web-3.0/src/main/java/io/opentelemetry/javaagent/instrumentation/hypertrace/vertx/HttpRequestInstrumentation.java b/instrumentation/vertx/vertx-web-3.0/src/main/java/io/opentelemetry/javaagent/instrumentation/hypertrace/vertx/HttpRequestInstrumentation.java index c070384e7..8b2eb5c04 100644 --- a/instrumentation/vertx/vertx-web-3.0/src/main/java/io/opentelemetry/javaagent/instrumentation/hypertrace/vertx/HttpRequestInstrumentation.java +++ b/instrumentation/vertx/vertx-web-3.0/src/main/java/io/opentelemetry/javaagent/instrumentation/hypertrace/vertx/HttpRequestInstrumentation.java @@ -87,14 +87,16 @@ public void transform(TypeTransformer transformer) { public static class EndRequestAdvice { @Advice.OnMethodEnter(suppress = Throwable.class) public static void enter(@Advice.This HttpClientRequest request) { - + System.out.println("start Enter io.vertx.core.http.HttpClientRequest.EndRequestAdvice"); int callDepth = HypertraceCallDepthThreadLocalMap.incrementCallDepth(HttpClientRequest.class); if (callDepth > 0) { + System.out.println("end1 Enter io.vertx.core.http.HttpClientRequest.EndRequestAdvice"); return; } Contexts contexts = VirtualField.find(HttpClientRequest.class, Contexts.class).get(request); if (contexts == null) { + System.out.println("end2 Enter io.vertx.core.http.HttpClientRequest.EndRequestAdvice"); return; } Span span = Span.fromContext(contexts.context); @@ -104,10 +106,12 @@ public static void enter(@Advice.This HttpClientRequest request) { if (buffer != null) { span.setAttribute(HypertraceSemanticAttributes.HTTP_REQUEST_BODY, buffer.toString()); } + System.out.println("end Enter io.vertx.core.http.HttpClientRequest.EndRequestAdvice"); } @Advice.OnMethodExit(suppress = Throwable.class, onThrowable = Throwable.class) public static void exit() { + System.out.println("start/end Exit io.vertx.core.http.HttpClientRequest.EndRequestAdvice"); HypertraceCallDepthThreadLocalMap.decrementCallDepth(HttpClientRequest.class); } } @@ -117,14 +121,19 @@ public static class EndRequestAdvice_string { public static void enter( @Advice.This HttpClientRequest request, @Advice.Argument(0) String chunk) throws IOException { - + System.out.println( + "start Enter io.vertx.core.http.HttpClientRequest.EndRequestAdvice_string"); int callDepth = HypertraceCallDepthThreadLocalMap.incrementCallDepth(HttpClientRequest.class); if (callDepth > 0) { + System.out.println( + "end1 Enter io.vertx.core.http.HttpClientRequest.EndRequestAdvice_string"); return; } Contexts contexts = VirtualField.find(HttpClientRequest.class, Contexts.class).get(request); if (contexts == null) { + System.out.println( + "end2 Enter io.vertx.core.http.HttpClientRequest.EndRequestAdvice_string"); return; } Span span = Span.fromContext(contexts.context); @@ -142,10 +151,13 @@ public static void enter( span.setAttribute(HypertraceSemanticAttributes.HTTP_REQUEST_BODY, buffer.toString()); } } + System.out.println("end Enter io.vertx.core.http.HttpClientRequest.EndRequestAdvice_string"); } @Advice.OnMethodExit(suppress = Throwable.class, onThrowable = Throwable.class) public static void exit() { + System.out.println( + "start/end Exit io.vertx.core.http.HttpClientRequest.EndRequestAdvice_string"); HypertraceCallDepthThreadLocalMap.decrementCallDepth(HttpClientRequest.class); } } @@ -155,14 +167,20 @@ public static class EndRequestAdvice_buffer { public static void enter( @Advice.This HttpClientRequest request, @Advice.Argument(0) Buffer chunk) throws IOException { + System.out.println( + "start Enter io.vertx.core.http.HttpClientRequest.EndRequestAdvice_buffer"); int callDepth = HypertraceCallDepthThreadLocalMap.incrementCallDepth(HttpClientRequest.class); if (callDepth > 0) { + System.out.println( + "end1 Enter io.vertx.core.http.HttpClientRequest.EndRequestAdvice_buffer"); return; } Contexts contexts = VirtualField.find(HttpClientRequest.class, Contexts.class).get(request); if (contexts == null) { + System.out.println( + "end2 Enter io.vertx.core.http.HttpClientRequest.EndRequestAdvice_buffer"); return; } Span span = Span.fromContext(contexts.context); @@ -183,10 +201,13 @@ public static void enter( span.setAttribute(HypertraceSemanticAttributes.HTTP_REQUEST_BODY, buffer.toString()); } } + System.out.println("end Enter io.vertx.core.http.HttpClientRequest.EndRequestAdvice_buffer"); } @Advice.OnMethodExit(suppress = Throwable.class, onThrowable = Throwable.class) public static void exit() { + System.out.println( + "start/end Exit io.vertx.core.http.HttpClientRequest.EndRequestAdvice_buffer"); HypertraceCallDepthThreadLocalMap.decrementCallDepth(HttpClientRequest.class); } } @@ -196,9 +217,13 @@ public static class WriteRequestAdvice_string { public static void enter( @Advice.This HttpClientRequest request, @Advice.Argument(0) String chunk) throws IOException { + System.out.println( + "start Enter io.vertx.core.http.HttpClientRequest.WriteRequestAdvice_string"); int callDepth = HypertraceCallDepthThreadLocalMap.incrementCallDepth(HttpClientRequest.class); if (callDepth > 0) { + System.out.println( + "end1 Enter io.vertx.core.http.HttpClientRequest.WriteRequestAdvice_string"); return; } @@ -216,10 +241,14 @@ public static void enter( } buffer.write(chunk); } + System.out.println( + "end Enter io.vertx.core.http.HttpClientRequest.WriteRequestAdvice_string"); } @Advice.OnMethodExit(suppress = Throwable.class, onThrowable = Throwable.class) public static void exit() { + System.out.println( + "start/end Exit io.vertx.core.http.HttpClientRequest.WriteRequestAdvice_string"); HypertraceCallDepthThreadLocalMap.decrementCallDepth(HttpClientRequest.class); } } @@ -229,9 +258,13 @@ public static class WriteRequestAdvice_buffer { public static void enter( @Advice.This HttpClientRequest request, @Advice.Argument(0) Buffer chunk) throws IOException { + System.out.println( + "start Enter io.vertx.core.http.HttpClientRequest.WriteRequestAdvice_buffer"); int callDepth = HypertraceCallDepthThreadLocalMap.incrementCallDepth(HttpClientRequest.class); if (callDepth > 0) { + System.out.println( + "end1 Enter io.vertx.core.http.HttpClientRequest.WriteRequestAdvice_buffer"); return; } @@ -249,10 +282,14 @@ public static void enter( } buffer.write(chunk.toString(StandardCharsets.UTF_8.name())); } + System.out.println( + "end Enter io.vertx.core.http.HttpClientRequest.WriteRequestAdvice_buffer"); } @Advice.OnMethodExit(suppress = Throwable.class, onThrowable = Throwable.class) public static void exit() { + System.out.println( + "start/end Exit io.vertx.core.http.HttpClientRequest.WriteRequestAdvice_buffer"); HypertraceCallDepthThreadLocalMap.decrementCallDepth(HttpClientRequest.class); } } diff --git a/instrumentation/vertx/vertx-web-3.0/src/main/java/io/opentelemetry/javaagent/instrumentation/hypertrace/vertx/HttpResponseInstrumentation.java b/instrumentation/vertx/vertx-web-3.0/src/main/java/io/opentelemetry/javaagent/instrumentation/hypertrace/vertx/HttpResponseInstrumentation.java index fac690dcc..c684d329b 100644 --- a/instrumentation/vertx/vertx-web-3.0/src/main/java/io/opentelemetry/javaagent/instrumentation/hypertrace/vertx/HttpResponseInstrumentation.java +++ b/instrumentation/vertx/vertx-web-3.0/src/main/java/io/opentelemetry/javaagent/instrumentation/hypertrace/vertx/HttpResponseInstrumentation.java @@ -57,15 +57,18 @@ public static class ResponseBodyHandler { public static void handleResponseEnter( @Advice.This HttpClientResponse response, @Advice.Argument(value = 0, readOnly = false) Handler handler) { + System.out.println("start Enter io.vertx.core.http.HttpClientResponse.bodyHandler"); Span span = VirtualField.find(HttpClientResponse.class, Span.class).get(response); if (span == null) { // request not traced - e.g. wrong content type + System.out.println("end1 Enter io.vertx.core.http.HttpClientResponse.bodyHandler"); return; } VirtualField.find(HttpClientResponse.class, Span.class).set(response, null); handler = new ResponseBodyWrappingHandler(handler, span); + System.out.println("end Enter io.vertx.core.http.HttpClientResponse.bodyHandler"); } } } diff --git a/javaagent-core/src/main/java/org/hypertrace/agent/core/instrumentation/HypertraceSemanticAttributes.java b/javaagent-core/src/main/java/org/hypertrace/agent/core/instrumentation/HypertraceSemanticAttributes.java index 66cdd12bf..d5f2ba020 100644 --- a/javaagent-core/src/main/java/org/hypertrace/agent/core/instrumentation/HypertraceSemanticAttributes.java +++ b/javaagent-core/src/main/java/org/hypertrace/agent/core/instrumentation/HypertraceSemanticAttributes.java @@ -52,6 +52,8 @@ public static AttributeKey httpResponseHeaderLong(String header) { public static final AttributeKey HTTP_REQUEST_SESSION_ID = AttributeKey.stringKey("http.request.session_id"); + public static final AttributeKey HTTP_RESPONSE_CONTENT_TYPE = + AttributeKey.stringKey("http.response.header.content-type"); public static final AttributeKey RPC_REQUEST_BODY = AttributeKey.stringKey("rpc.request.body"); diff --git a/javaagent/build.gradle.kts b/javaagent/build.gradle.kts index c2febd5a3..f276b9ca2 100644 --- a/javaagent/build.gradle.kts +++ b/javaagent/build.gradle.kts @@ -13,6 +13,7 @@ dependencies { // https://dl.bintray.com/open-telemetry/maven/ implementation("io.opentelemetry.javaagent", "opentelemetry-javaagent", version = "${versions["opentelemetry_java_agent_all"]}") implementation(project(":filter-api")) + implementation("io.opentelemetry:opentelemetry-sdk-trace:1.13.0") } base.archivesBaseName = "hypertrace-agent" diff --git a/settings.gradle.kts b/settings.gradle.kts index dde827cb1..63d28c81e 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -54,12 +54,12 @@ include("instrumentation:micronaut-1.0") findProject(":instrumentation:micronaut-1.0")?.name = "micronaut-1.0" include("instrumentation:micronaut-3.0") findProject(":instrumentation:micronaut-3.0")?.name = "micronaut-3.0" +include("instrumentation:servlet:servlet-2.2") +findProject(":instrumentation:servlet:servlet-2.2")?.name = "servlet-2.2" include("instrumentation:servlet:servlet-3.0") findProject(":instrumentation:servlet:servlet-3.0")?.name = "servlet-3.0" include("instrumentation:servlet:servlet-rw") findProject(":instrumentation:servlet:servlet-rw")?.name = "servlet-rw" -include("instrumentation:servlet:servlet-2.2") -findProject(":instrumentation:servlet:servlet-2.2")?.name = "servlet-2.2" include("instrumentation:struts-2.3") findProject(":instrumentation:struts-2.3")?.name = "struts-2.3" include("instrumentation:undertow:undertow-common") From 20edca1a1560b95525fb9c78a9fd363416305ba1 Mon Sep 17 00:00:00 2001 From: Kaushal Agrawal Date: Mon, 20 Mar 2023 20:12:28 +0530 Subject: [PATCH 4/7] remove comments and move from draft to ready --- .../NettyChannelPipelineInstrumentation.java | 2 - .../Servlet2AndFilterInstrumentation.java | 11 +- .../Servlet2InstrumentationModule.java | 2 +- .../ServletInputStreamInstrumentation.java | 35 ------ .../ServletRequestInstrumentation.java | 27 ----- .../ServletOutputStreamInstrumentation.java | 31 +----- .../ServletResponseInstrumentation.java | 105 ++++-------------- .../HttpRequestHandleInstrumentation.java | 3 - .../vertx/HttpRequestInstrumentation.java | 39 ------- .../vertx/HttpResponseInstrumentation.java | 3 - .../HypertraceSemanticAttributes.java | 6 +- 11 files changed, 30 insertions(+), 234 deletions(-) diff --git a/instrumentation/netty/netty-4.1/src/main/java/io/opentelemetry/javaagent/instrumentation/hypertrace/netty/v4_1/NettyChannelPipelineInstrumentation.java b/instrumentation/netty/netty-4.1/src/main/java/io/opentelemetry/javaagent/instrumentation/hypertrace/netty/v4_1/NettyChannelPipelineInstrumentation.java index 7b2a73e3a..bd6561046 100644 --- a/instrumentation/netty/netty-4.1/src/main/java/io/opentelemetry/javaagent/instrumentation/hypertrace/netty/v4_1/NettyChannelPipelineInstrumentation.java +++ b/instrumentation/netty/netty-4.1/src/main/java/io/opentelemetry/javaagent/instrumentation/hypertrace/netty/v4_1/NettyChannelPipelineInstrumentation.java @@ -169,5 +169,3 @@ public static void addHandler( } } } - - diff --git a/instrumentation/servlet/servlet-2.2/src/main/java/io/opentelemetry/javaagent/instrumentation/hypertrace/servlet/v2_2/nowrapping/Servlet2AndFilterInstrumentation.java b/instrumentation/servlet/servlet-2.2/src/main/java/io/opentelemetry/javaagent/instrumentation/hypertrace/servlet/v2_2/nowrapping/Servlet2AndFilterInstrumentation.java index a0c7e53f7..477386844 100644 --- a/instrumentation/servlet/servlet-2.2/src/main/java/io/opentelemetry/javaagent/instrumentation/hypertrace/servlet/v2_2/nowrapping/Servlet2AndFilterInstrumentation.java +++ b/instrumentation/servlet/servlet-2.2/src/main/java/io/opentelemetry/javaagent/instrumentation/hypertrace/servlet/v2_2/nowrapping/Servlet2AndFilterInstrumentation.java @@ -83,15 +83,12 @@ public static boolean start( @Advice.Argument(value = 0) ServletRequest request, @Advice.Argument(value = 1) ServletResponse response, @Advice.Local("currentSpan") Span currentSpan) { - System.out.println("start Enter javax.servlet.Servlet.service"); int callDepth = HypertraceCallDepthThreadLocalMap.incrementCallDepth(Servlet2InstrumentationName.class); if (callDepth > 0) { - System.out.println("end1 Enter javax.servlet.Servlet.service"); return false; } if (!(request instanceof HttpServletRequest) || !(response instanceof HttpServletResponse)) { - System.out.println("end2 Enter javax.servlet.Servlet.service"); return false; } @@ -126,7 +123,6 @@ public static boolean start( if (FilterRegistry.getFilter().evaluateRequestHeaders(currentSpan, headers)) { httpResponse.setStatus(403); // skip execution of the user code - System.out.println("end3 Enter javax.servlet.Servlet.service"); return true; } @@ -139,7 +135,6 @@ public static boolean start( httpRequest, new SpanAndObjectPair(currentSpan, Collections.unmodifiableMap(headers))); } - System.out.println("end Enter javax.servlet.Servlet.service"); return false; } @@ -149,17 +144,14 @@ public static void exit( @Advice.Argument(1) ServletResponse response, @Advice.Thrown(readOnly = false) Throwable throwable, @Advice.Local("currentSpan") Span currentSpan) { - System.out.println("start Exit javax.servlet.Servlet.service"); int callDepth = HypertraceCallDepthThreadLocalMap.decrementCallDepth(Servlet2InstrumentationName.class); if (callDepth > 0) { - System.out.println("end1 Exit javax.servlet.Servlet.service"); return; } // we are in the most outermost level of Servlet instrumentation if (!(request instanceof HttpServletRequest) || !(response instanceof HttpServletResponse)) { - System.out.println("end2 Exit javax.servlet.Servlet.service"); return; } @@ -188,7 +180,7 @@ public static void exit( // capture response body // TODO: capture response headers - // TODO: capture body based on response content type + // FIXME: capture body based on response content type if (instrumentationConfig.httpBody().response()) { Utils.captureResponseBody( currentSpan, @@ -221,7 +213,6 @@ public static void exit( tmp = tmp.getCause(); } } - System.out.println("end Exit javax.servlet.Servlet.service"); } } } diff --git a/instrumentation/servlet/servlet-2.2/src/main/java/io/opentelemetry/javaagent/instrumentation/hypertrace/servlet/v2_2/nowrapping/Servlet2InstrumentationModule.java b/instrumentation/servlet/servlet-2.2/src/main/java/io/opentelemetry/javaagent/instrumentation/hypertrace/servlet/v2_2/nowrapping/Servlet2InstrumentationModule.java index 2e9f64458..efb8603da 100644 --- a/instrumentation/servlet/servlet-2.2/src/main/java/io/opentelemetry/javaagent/instrumentation/hypertrace/servlet/v2_2/nowrapping/Servlet2InstrumentationModule.java +++ b/instrumentation/servlet/servlet-2.2/src/main/java/io/opentelemetry/javaagent/instrumentation/hypertrace/servlet/v2_2/nowrapping/Servlet2InstrumentationModule.java @@ -36,7 +36,7 @@ public Servlet2InstrumentationModule() { @Override public int order() { - return -1; + return 1; } @Override diff --git a/instrumentation/servlet/servlet-2.2/src/main/java/io/opentelemetry/javaagent/instrumentation/hypertrace/servlet/v2_2/nowrapping/request/ServletInputStreamInstrumentation.java b/instrumentation/servlet/servlet-2.2/src/main/java/io/opentelemetry/javaagent/instrumentation/hypertrace/servlet/v2_2/nowrapping/request/ServletInputStreamInstrumentation.java index 046a21190..1238d57df 100644 --- a/instrumentation/servlet/servlet-2.2/src/main/java/io/opentelemetry/javaagent/instrumentation/hypertrace/servlet/v2_2/nowrapping/request/ServletInputStreamInstrumentation.java +++ b/instrumentation/servlet/servlet-2.2/src/main/java/io/opentelemetry/javaagent/instrumentation/hypertrace/servlet/v2_2/nowrapping/request/ServletInputStreamInstrumentation.java @@ -91,32 +91,26 @@ static class InputStream_ReadNoArgs { @Advice.OnMethodEnter(suppress = Throwable.class) public static ByteBufferSpanPair enter(@Advice.This ServletInputStream thizz) { - System.out.println("start Enter javax.servlet.ServletInputStream.ReadNoArgs"); ByteBufferSpanPair bufferSpanPair = VirtualField.find(ServletInputStream.class, ByteBufferSpanPair.class).get(thizz); if (bufferSpanPair == null) { - System.out.println("end1 Enter javax.servlet.ServletInputStream.ReadNoArgs"); return null; } HypertraceCallDepthThreadLocalMap.incrementCallDepth(ServletInputStream.class); - System.out.println("end Enter javax.servlet.ServletInputStream.ReadNoArgs"); return bufferSpanPair; } @Advice.OnMethodExit(onThrowable = Throwable.class) public static void exit( @Advice.Return int read, @Advice.Enter ByteBufferSpanPair bufferSpanPair) { - System.out.println("start Exit javax.servlet.ServletInputStream.ReadNoArgs"); try { if (bufferSpanPair == null) { - System.out.println("end1 Exit javax.servlet.ServletInputStream.ReadNoArgs"); return; } int callDepth = HypertraceCallDepthThreadLocalMap.decrementCallDepth(ServletInputStream.class); if (callDepth > 0) { - System.out.println("end2 Exit javax.servlet.ServletInputStream.ReadNoArgs"); return; } @@ -132,7 +126,6 @@ public static void exit( // ignore } } - System.out.println("end Exit javax.servlet.ServletInputStream.ReadNoArgs"); } } @@ -141,16 +134,13 @@ public static class InputStream_ReadByteArray { @Advice.OnMethodEnter(suppress = Throwable.class) public static ByteBufferSpanPair enter(@Advice.This ServletInputStream thizz) { - System.out.println("start Enter javax.servlet.ServletInputStream.ReadByteArray"); ByteBufferSpanPair bufferSpanPair = VirtualField.find(ServletInputStream.class, ByteBufferSpanPair.class).get(thizz); if (bufferSpanPair == null) { - System.out.println("end1 Enter javax.servlet.ServletInputStream.ReadByteArray"); return null; } HypertraceCallDepthThreadLocalMap.incrementCallDepth(ServletInputStream.class); - System.out.println("end Enter javax.servlet.ServletInputStream.ReadByteArray"); return bufferSpanPair; } @@ -161,16 +151,13 @@ public static void exit( @Advice.Argument(0) byte[] b, @Advice.Enter ByteBufferSpanPair bufferSpanPair) throws Throwable { - System.out.println("start Exit javax.servlet.ServletInputStream.ReadByteArray"); try { if (bufferSpanPair == null) { - System.out.println("end1 Exit javax.servlet.ServletInputStream.ReadByteArray"); return; } int callDepth = HypertraceCallDepthThreadLocalMap.decrementCallDepth(ServletInputStream.class); if (callDepth > 0) { - System.out.println("end2 Exit javax.servlet.ServletInputStream.ReadByteArray"); return; } @@ -189,7 +176,6 @@ public static void exit( // ignore } } - System.out.println("end Exit javax.servlet.ServletInputStream.ReadByteArray"); } } @@ -198,16 +184,13 @@ public static class InputStream_ReadByteArrayOffset { @Advice.OnMethodEnter(suppress = Throwable.class) public static ByteBufferSpanPair enter(@Advice.This ServletInputStream thizz) { - System.out.println("start Enter javax.servlet.ServletInputStream.ReadByteArrayOffset"); ByteBufferSpanPair bufferSpanPair = VirtualField.find(ServletInputStream.class, ByteBufferSpanPair.class).get(thizz); if (bufferSpanPair == null) { - System.out.println("end1 Enter javax.servlet.ServletInputStream.ReadByteArrayOffset"); return null; } HypertraceCallDepthThreadLocalMap.incrementCallDepth(ServletInputStream.class); - System.out.println("end Enter javax.servlet.ServletInputStream.ReadByteArrayOffset"); return bufferSpanPair; } @@ -220,17 +203,14 @@ public static void exit( @Advice.Argument(2) int len, @Advice.Enter ByteBufferSpanPair bufferSpanPair) throws Throwable { - System.out.println("start Exit javax.servlet.ServletInputStream.ReadByteArrayOffset"); try { if (bufferSpanPair == null) { - System.out.println("end1 Exit javax.servlet.ServletInputStream.ReadByteArrayOffset"); return; } int callDepth = HypertraceCallDepthThreadLocalMap.decrementCallDepth(ServletInputStream.class); if (callDepth > 0) { - System.out.println("end2 Exit javax.servlet.ServletInputStream.ReadByteArrayOffset"); return; } @@ -249,7 +229,6 @@ public static void exit( // ignore } } - System.out.println("end Exit javax.servlet.ServletInputStream.ReadByteArrayOffset"); } } @@ -258,16 +237,13 @@ public static class InputStream_ReadAllBytes { @Advice.OnMethodEnter(suppress = Throwable.class) public static ByteBufferSpanPair enter(@Advice.This ServletInputStream thizz) { - System.out.println("start Enter javax.servlet.ServletInputStream.ReadAllBytes"); ByteBufferSpanPair bufferSpanPair = VirtualField.find(ServletInputStream.class, ByteBufferSpanPair.class).get(thizz); if (bufferSpanPair == null) { - System.out.println("end1 Enter javax.servlet.ServletInputStream.ReadAllBytes"); return null; } HypertraceCallDepthThreadLocalMap.incrementCallDepth(ServletInputStream.class); - System.out.println("end Enter javax.servlet.ServletInputStream.ReadAllBytes"); return bufferSpanPair; } @@ -275,16 +251,13 @@ public static ByteBufferSpanPair enter(@Advice.This ServletInputStream thizz) { public static void exit( @Advice.Return byte[] b, @Advice.Enter ByteBufferSpanPair bufferSpanPair) throws IOException { - System.out.println("start Exit javax.servlet.ServletInputStream.ReadAllBytes"); try { if (bufferSpanPair == null) { - System.out.println("end1 Exit javax.servlet.ServletInputStream.ReadAllBytes"); return; } int callDepth = HypertraceCallDepthThreadLocalMap.decrementCallDepth(ServletInputStream.class); if (callDepth > 0) { - System.out.println("end2 Exit javax.servlet.ServletInputStream.ReadAllBytes"); return; } bufferSpanPair.writeToBuffer(b); @@ -296,7 +269,6 @@ public static void exit( // ignore } } - System.out.println("end Exit javax.servlet.ServletInputStream.ReadAllBytes"); } } @@ -305,16 +277,13 @@ public static class InputStream_ReadNBytes { @Advice.OnMethodEnter(suppress = Throwable.class) public static ByteBufferSpanPair enter(@Advice.This ServletInputStream thizz) { - System.out.println("start Enter javax.servlet.ServletInputStream.ReadNBytes"); ByteBufferSpanPair bufferSpanPair = VirtualField.find(ServletInputStream.class, ByteBufferSpanPair.class).get(thizz); if (bufferSpanPair == null) { - System.out.println("end1 Enter javax.servlet.ServletInputStream.ReadNBytes"); return null; } HypertraceCallDepthThreadLocalMap.incrementCallDepth(ServletInputStream.class); - System.out.println("end Enter javax.servlet.ServletInputStream.ReadNBytes"); return bufferSpanPair; } @@ -326,16 +295,13 @@ public static void exit( @Advice.Argument(1) int off, @Advice.Enter ByteBufferSpanPair bufferSpanPair) throws Throwable { - System.out.println("start Exit javax.servlet.ServletInputStream.ReadNBytes"); try { if (bufferSpanPair == null) { - System.out.println("end1 Exit javax.servlet.ServletInputStream.ReadNBytes"); return; } int callDepth = HypertraceCallDepthThreadLocalMap.decrementCallDepth(ServletInputStream.class); if (callDepth > 0) { - System.out.println("end2 Exit javax.servlet.ServletInputStream.ReadNBytes"); return; } @@ -354,7 +320,6 @@ public static void exit( // ignore } } - System.out.println("end Exit javax.servlet.ServletInputStream.ReadNBytes"); } } } diff --git a/instrumentation/servlet/servlet-2.2/src/main/java/io/opentelemetry/javaagent/instrumentation/hypertrace/servlet/v2_2/nowrapping/request/ServletRequestInstrumentation.java b/instrumentation/servlet/servlet-2.2/src/main/java/io/opentelemetry/javaagent/instrumentation/hypertrace/servlet/v2_2/nowrapping/request/ServletRequestInstrumentation.java index 987d3ba90..fde0b65c4 100644 --- a/instrumentation/servlet/servlet-2.2/src/main/java/io/opentelemetry/javaagent/instrumentation/hypertrace/servlet/v2_2/nowrapping/request/ServletRequestInstrumentation.java +++ b/instrumentation/servlet/servlet-2.2/src/main/java/io/opentelemetry/javaagent/instrumentation/hypertrace/servlet/v2_2/nowrapping/request/ServletRequestInstrumentation.java @@ -72,20 +72,17 @@ static class ServletRequest_getInputStream_advice { @Advice.OnMethodEnter(suppress = Throwable.class) public static SpanAndObjectPair enter(@Advice.This ServletRequest servletRequest) { - System.out.println("start Enter javax.servlet.ServletRequest.getInputStream"); HttpServletRequest httpServletRequest = (HttpServletRequest) servletRequest; // span is added in servlet/filter instrumentation if data capture is enabled SpanAndObjectPair requestBufferWrapper = VirtualField.find(HttpServletRequest.class, SpanAndObjectPair.class) .get(httpServletRequest); if (requestBufferWrapper == null) { - System.out.println("end1 Enter javax.servlet.ServletRequest.getInputStream"); return null; } // the getReader method might call getInputStream HypertraceCallDepthThreadLocalMap.incrementCallDepth(ServletRequest.class); - System.out.println("end Enter javax.servlet.ServletRequest.getInputStream"); return requestBufferWrapper; } @@ -95,20 +92,16 @@ public static void exit( @Advice.Return ServletInputStream servletInputStream, @Advice.Thrown Throwable throwable, @Advice.Enter SpanAndObjectPair spanAndObjectPair) { - System.out.println("start Exit javax.servlet.ServletRequest.getInputStream"); if (spanAndObjectPair == null) { - System.out.println("end1 Exit javax.servlet.ServletRequest.getInputStream"); return; } int callDepth = HypertraceCallDepthThreadLocalMap.decrementCallDepth(ServletRequest.class); if (callDepth > 0) { - System.out.println("end2 Exit javax.servlet.ServletRequest.getInputStream"); return; } if (!(servletRequest instanceof HttpServletRequest) || throwable != null) { - System.out.println("end3 Exit javax.servlet.ServletRequest.getInputStream"); return; } HttpServletRequest httpServletRequest = (HttpServletRequest) servletRequest; @@ -117,7 +110,6 @@ public static void exit( VirtualField.find(ServletInputStream.class, ByteBufferSpanPair.class); if (contextStore.get(servletInputStream) != null) { // getInputStream() can be called multiple times - System.out.println("end4 Exit javax.servlet.ServletRequest.getInputStream"); return; } @@ -126,7 +118,6 @@ public static void exit( httpServletRequest, spanAndObjectPair.getSpan(), spanAndObjectPair.getHeaders()); contextStore.set(servletInputStream, bufferSpanPair); spanAndObjectPair.setAssociatedObject(servletInputStream); - System.out.println("end Exit javax.servlet.ServletRequest.getInputStream"); } } @@ -135,18 +126,15 @@ static class ServletRequest_getReader_advice { @Advice.OnMethodEnter(suppress = Throwable.class) public static SpanAndObjectPair enter(@Advice.This ServletRequest servletRequest) { - System.out.println("start Enter javax.servlet.ServletRequest.getReader"); HttpServletRequest httpServletRequest = (HttpServletRequest) servletRequest; SpanAndObjectPair spanAndObjectPair = VirtualField.find(HttpServletRequest.class, SpanAndObjectPair.class) .get(httpServletRequest); if (spanAndObjectPair == null) { - System.out.println("end1 Enter javax.servlet.ServletRequest.getReader"); return null; } HypertraceCallDepthThreadLocalMap.incrementCallDepth(ServletRequest.class); - System.out.println("end Enter javax.servlet.ServletRequest.getReader"); return spanAndObjectPair; } @@ -156,20 +144,16 @@ public static void exit( @Advice.Return BufferedReader reader, @Advice.Thrown Throwable throwable, @Advice.Enter SpanAndObjectPair spanAndObjectPair) { - System.out.println("start Exit javax.servlet.ServletRequest.getReader"); if (spanAndObjectPair == null) { - System.out.println("end1 Exit javax.servlet.ServletRequest.getReader"); return; } int callDepth = HypertraceCallDepthThreadLocalMap.decrementCallDepth(ServletRequest.class); if (callDepth > 0) { - System.out.println("end2 Exit javax.servlet.ServletRequest.getReader"); return; } if (!(servletRequest instanceof HttpServletRequest) || throwable != null) { - System.out.println("end3 Exit javax.servlet.ServletRequest.getReader"); return; } HttpServletRequest httpServletRequest = (HttpServletRequest) servletRequest; @@ -178,7 +162,6 @@ public static void exit( VirtualField.find(BufferedReader.class, CharBufferSpanPair.class); if (contextStore.get(reader) != null) { // getReader() can be called multiple times - System.out.println("end4 Exit javax.servlet.ServletRequest.getReader"); return; } @@ -187,7 +170,6 @@ public static void exit( httpServletRequest, spanAndObjectPair.getSpan(), spanAndObjectPair.getHeaders()); contextStore.set(reader, bufferSpanPair); spanAndObjectPair.setAssociatedObject(reader); - System.out.println("end Exit javax.servlet.ServletRequest.getReader"); } } @@ -204,18 +186,15 @@ static class ServletRequest_getParameter_advice { */ @Advice.OnMethodEnter(suppress = Throwable.class) public static SpanAndObjectPair enter(@Advice.This ServletRequest servletRequest) { - System.out.println("start Enter javax.servlet.ServletRequest.getParameter"); HttpServletRequest httpServletRequest = (HttpServletRequest) servletRequest; SpanAndObjectPair spanAndObjectPair = VirtualField.find(HttpServletRequest.class, SpanAndObjectPair.class) .get(httpServletRequest); if (spanAndObjectPair == null) { - System.out.println("end1 Enter javax.servlet.ServletRequest.getParameter"); return null; } HypertraceCallDepthThreadLocalMap.incrementCallDepth(ServletRequest.class); - System.out.println("end Enter javax.servlet.ServletRequest.getParameter"); return spanAndObjectPair; } @@ -236,25 +215,20 @@ public static void exit( @Advice.Argument(0) String paramName, @Advice.Thrown Throwable throwable, @Advice.Enter SpanAndObjectPair spanAndObjectPair) { - System.out.println("start Exit javax.servlet.ServletRequest.getParameter"); if (spanAndObjectPair == null) { - System.out.println("end1 Exit javax.servlet.ServletRequest.getParameter"); return; } int callDepth = HypertraceCallDepthThreadLocalMap.decrementCallDepth(ServletRequest.class); if (callDepth > 0) { - System.out.println("end2 Exit javax.servlet.ServletRequest.getParameter"); return; } if (returnValue == null) { - System.out.println("end3 Exit javax.servlet.ServletRequest.getParameter"); return; } if (!(servletRequest instanceof HttpServletRequest) || throwable != null) { - System.out.println("end4 Exit javax.servlet.ServletRequest.getParameter"); return; } HttpServletRequest httpServletRequest = (HttpServletRequest) servletRequest; @@ -278,7 +252,6 @@ public static void exit( stringMap.put(paramName, returnValue); spanAndObjectPair.setAssociatedObject(stringMap); - System.out.println("end Exit javax.servlet.ServletRequest.getParameter"); } } } diff --git a/instrumentation/servlet/servlet-2.2/src/main/java/io/opentelemetry/javaagent/instrumentation/hypertrace/servlet/v2_2/nowrapping/response/ServletOutputStreamInstrumentation.java b/instrumentation/servlet/servlet-2.2/src/main/java/io/opentelemetry/javaagent/instrumentation/hypertrace/servlet/v2_2/nowrapping/response/ServletOutputStreamInstrumentation.java index 813e164f9..4c9b43446 100644 --- a/instrumentation/servlet/servlet-2.2/src/main/java/io/opentelemetry/javaagent/instrumentation/hypertrace/servlet/v2_2/nowrapping/response/ServletOutputStreamInstrumentation.java +++ b/instrumentation/servlet/servlet-2.2/src/main/java/io/opentelemetry/javaagent/instrumentation/hypertrace/servlet/v2_2/nowrapping/response/ServletOutputStreamInstrumentation.java @@ -70,11 +70,8 @@ public void transform(TypeTransformer transformer) { .and(isPublic()), ServletOutputStreamInstrumentation.class.getName() + "$OutputStream_writeByteArrOffset"); - // close is not called on Tomcat (tested with Spring Boot) - // transformer.applyAdviceToMethod( - // named("close").and(takesArguments(0)) - // .and(isPublic()), - // ServletOutputStreamInstrumentation.class.getName() + "$OutputStream_close"); + // close() is not instrumented due to some issue with Tomcat + // refer the comment in servlet-3.0 code } @SuppressWarnings("unused") @@ -83,33 +80,27 @@ static class OutputStream_write { @Advice.OnMethodEnter(suppress = Throwable.class) public static BoundedByteArrayOutputStream enter( @Advice.This ServletOutputStream thizz, @Advice.Argument(0) int b) { - System.out.println("start Enter javax.servlet.ServletOutputStream.write"); BoundedByteArrayOutputStream buffer = VirtualField.find(ServletOutputStream.class, BoundedByteArrayOutputStream.class) .get(thizz); if (buffer == null) { - System.out.println("end1 Enter javax.servlet.ServletOutputStream.write"); return null; } int callDepth = HypertraceCallDepthThreadLocalMap.incrementCallDepth(ServletOutputStream.class); if (callDepth > 0) { - System.out.println("end2 Enter javax.servlet.ServletOutputStream.write"); return buffer; } buffer.write(b); - System.out.println("end Enter javax.servlet.ServletOutputStream.write"); return buffer; } @Advice.OnMethodExit(suppress = Throwable.class, onThrowable = Throwable.class) public static void exit(@Advice.Enter BoundedByteArrayOutputStream buffer) { - System.out.println("start Exit javax.servlet.ServletOutputStream.write"); if (buffer != null) { HypertraceCallDepthThreadLocalMap.decrementCallDepth(ServletOutputStream.class); } - System.out.println("end Exit javax.servlet.ServletOutputStream.write"); } } @@ -119,34 +110,28 @@ static class OutputStream_writeByteArr { @Advice.OnMethodEnter(suppress = Throwable.class) public static BoundedByteArrayOutputStream enter( @Advice.This ServletOutputStream thizz, @Advice.Argument(0) byte[] b) throws IOException { - System.out.println("start Enter javax.servlet.ServletOutputStream.writeByteArr"); BoundedByteArrayOutputStream buffer = VirtualField.find(ServletOutputStream.class, BoundedByteArrayOutputStream.class) .get(thizz); if (buffer == null) { - System.out.println("end1 Enter javax.servlet.ServletOutputStream.writeByteArr"); return null; } int callDepth = HypertraceCallDepthThreadLocalMap.incrementCallDepth(ServletOutputStream.class); if (callDepth > 0) { - System.out.println("end2 Enter javax.servlet.ServletOutputStream.writeByteArr"); return buffer; } buffer.write(b); - System.out.println("end Enter javax.servlet.ServletOutputStream.writeByteArr"); return buffer; } @Advice.OnMethodExit(suppress = Throwable.class, onThrowable = Throwable.class) public static void exit(@Advice.Enter BoundedByteArrayOutputStream buffer) { - System.out.println("start Exit javax.servlet.ServletOutputStream.writeByteArr"); if (buffer != null) { HypertraceCallDepthThreadLocalMap.decrementCallDepth(ServletOutputStream.class); } - System.out.println("end Exit javax.servlet.ServletOutputStream.writeByteArr"); } } @@ -159,34 +144,28 @@ public static BoundedByteArrayOutputStream enter( @Advice.Argument(0) byte[] b, @Advice.Argument(1) int off, @Advice.Argument(2) int len) { - System.out.println("start Enter javax.servlet.ServletOutputStream.writeByteArrOffset"); BoundedByteArrayOutputStream buffer = VirtualField.find(ServletOutputStream.class, BoundedByteArrayOutputStream.class) .get(thizz); if (buffer == null) { - System.out.println("end1 Enter javax.servlet.ServletOutputStream.writeByteArrOffset"); return null; } int callDepth = HypertraceCallDepthThreadLocalMap.incrementCallDepth(ServletOutputStream.class); if (callDepth > 0) { - System.out.println("end2 Enter javax.servlet.ServletOutputStream.writeByteArrOffset"); return buffer; } buffer.write(b, off, len); - System.out.println("end Enter javax.servlet.ServletOutputStream.writeByteArrOffset"); return buffer; } @Advice.OnMethodExit(suppress = Throwable.class, onThrowable = Throwable.class) public static void exit(@Advice.Enter BoundedByteArrayOutputStream buffer) { - System.out.println("start Exit javax.servlet.ServletOutputStream.writeByteArrOffset"); if (buffer != null) { HypertraceCallDepthThreadLocalMap.decrementCallDepth(ServletOutputStream.class); } - System.out.println("end Exit javax.servlet.ServletOutputStream.writeByteArrOffset"); } } @@ -196,35 +175,29 @@ static class ServletOutputStream_print { @Advice.OnMethodEnter(suppress = Throwable.class) public static BoundedByteArrayOutputStream enter( @Advice.This ServletOutputStream thizz, @Advice.Argument(0) String s) throws IOException { - System.out.println("start Enter javax.servlet.ServletOutputStream.print"); BoundedByteArrayOutputStream buffer = VirtualField.find(ServletOutputStream.class, BoundedByteArrayOutputStream.class) .get(thizz); if (buffer == null) { - System.out.println("end1 Enter javax.servlet.ServletOutputStream.print"); return null; } int callDepth = HypertraceCallDepthThreadLocalMap.incrementCallDepth(ServletOutputStream.class); if (callDepth > 0) { - System.out.println("end2 Enter javax.servlet.ServletOutputStream.print"); return buffer; } String bodyPart = s == null ? "null" : s; buffer.write(bodyPart.getBytes()); - System.out.println("end Enter javax.servlet.ServletOutputStream.print"); return buffer; } @Advice.OnMethodExit(suppress = Throwable.class, onThrowable = Throwable.class) public static void exit(@Advice.Enter BoundedByteArrayOutputStream buffer) { - System.out.println("start Exit javax.servlet.ServletOutputStream.print"); if (buffer != null) { HypertraceCallDepthThreadLocalMap.decrementCallDepth(ServletOutputStream.class); } - System.out.println("end Exit javax.servlet.ServletOutputStream.print"); } } } diff --git a/instrumentation/servlet/servlet-2.2/src/main/java/io/opentelemetry/javaagent/instrumentation/hypertrace/servlet/v2_2/nowrapping/response/ServletResponseInstrumentation.java b/instrumentation/servlet/servlet-2.2/src/main/java/io/opentelemetry/javaagent/instrumentation/hypertrace/servlet/v2_2/nowrapping/response/ServletResponseInstrumentation.java index bb05e1ab4..897ccdcae 100644 --- a/instrumentation/servlet/servlet-2.2/src/main/java/io/opentelemetry/javaagent/instrumentation/hypertrace/servlet/v2_2/nowrapping/response/ServletResponseInstrumentation.java +++ b/instrumentation/servlet/servlet-2.2/src/main/java/io/opentelemetry/javaagent/instrumentation/hypertrace/servlet/v2_2/nowrapping/response/ServletResponseInstrumentation.java @@ -67,12 +67,6 @@ public void transform(TypeTransformer transformer) { transformer.applyAdviceToMethod( named("setContentLength").and(takesArgument(0, int.class)).and(isPublic()), ServletResponseInstrumentation.class.getName() + "$ServletResponse_setContentLength"); - transformer.applyAdviceToMethod( - named("setCharacterEncoding").and(takesArgument(0, String.class)).and(isPublic()), - ServletResponseInstrumentation.class.getName() + "$ServletResponse_setCharacterEncoding"); - transformer.applyAdviceToMethod( - named("setStatus").and(takesArgument(0, int.class)).and(isPublic()), - ServletResponseInstrumentation.class.getName() + "$HttpServletResponse_setStatus"); transformer.applyAdviceToMethod( named("setHeader").and(takesArguments(2)).and(isPublic()), ServletResponseInstrumentation.class.getName() + "$HttpServletResponse_setHeader"); @@ -91,49 +85,31 @@ public void transform(TypeTransformer transformer) { } } + @SuppressWarnings("unused") static class ServletResponse_setContentType { - @Advice.OnMethodExit(suppress = Throwable.class) - public static void exit(@Advice.Argument(0) String type) { - System.out.println("inside javax.servlet.ServletResponse.setContentType [" + type + "]"); - } - } - - static class ServletResponse_setContentLength { - @Advice.OnMethodExit(suppress = Throwable.class) - public static void exit(@Advice.Argument(0) int len) { - System.out.println("inside javax.servlet.ServletResponse.setContentLength [" + len + "]"); - } - } - - static class ServletResponse_setCharacterEncoding { - @Advice.OnMethodExit(suppress = Throwable.class) - public static void exit(@Advice.Argument(0) String charset) { - System.out.println( - "inside javax.servlet.ServletResponse.setCharacterEncoding [" + charset + "]"); + @Advice.OnMethodEnter(suppress = Throwable.class) + public static void enter(@Advice.Argument(0) String type) { + Java8BytecodeBridge.currentSpan() + .setAttribute(HypertraceSemanticAttributes.HTTP_RESPONSE_CONTENT_TYPE, type); } } @SuppressWarnings("unused") - static class HttpServletResponse_setStatus { - @Advice.OnMethodExit(suppress = Throwable.class) - public static void exit(@Advice.Argument(value = 0) int status) { - System.out.println( - "inside javax.servlet.http.HttpServletResponse.setStatus [" + status + "]"); + static class ServletResponse_setContentLength { + @Advice.OnMethodEnter(suppress = Throwable.class) + public static void enter(@Advice.Argument(0) int len) { + Java8BytecodeBridge.currentSpan() + .setAttribute(HypertraceSemanticAttributes.HTTP_RESPONSE_CONTENT_LENGTH, len); } } @SuppressWarnings("unused") static class HttpServletResponse_setHeader { - @Advice.OnMethodExit(suppress = Throwable.class) - public static void exit( + @Advice.OnMethodEnter(suppress = Throwable.class) + public static void enter( @Advice.Argument(value = 0) String headerName, @Advice.Argument(value = 1) String headerValue) { - System.out.println( - "inside javax.servlet.http.HttpServletResponse.setHeader [" - + headerName - + ", " - + headerValue - + "]"); + Java8BytecodeBridge.currentSpan() .setAttribute(HypertraceSemanticAttributes.httpResponseHeader(headerName), headerValue); } @@ -141,16 +117,11 @@ public static void exit( @SuppressWarnings("unused") static class HttpServletResponse_addHeader { - @Advice.OnMethodExit(suppress = Throwable.class) - public static void exit( + @Advice.OnMethodEnter(suppress = Throwable.class) + public static void enter( @Advice.Argument(value = 0) String headerName, @Advice.Argument(value = 1) String headerValue) { - System.out.println( - "inside javax.servlet.http.HttpServletResponse.addHeader [" - + headerName - + ", " - + headerValue - + "]"); + Span currentSpan = Java8BytecodeBridge.currentSpan(); if (!(currentSpan instanceof ReadableSpan)) { return; @@ -168,16 +139,11 @@ public static void exit( @SuppressWarnings("unused") static class HttpServletResponse_setDateHeader { - @Advice.OnMethodExit(suppress = Throwable.class) - public static void exit( + @Advice.OnMethodEnter(suppress = Throwable.class) + public static void enter( @Advice.Argument(value = 0) String headerName, @Advice.Argument(value = 1) long headerValue) { - System.out.println( - "inside javax.servlet.http.HttpServletResponse.setDateHeader [" - + headerName - + ", " - + headerValue - + "]"); + Java8BytecodeBridge.currentSpan() .setAttribute( HypertraceSemanticAttributes.httpResponseHeaderLong(headerName), headerValue); @@ -186,16 +152,11 @@ public static void exit( @SuppressWarnings("unused") static class HttpServletResponse_setIntHeader { - @Advice.OnMethodExit(suppress = Throwable.class) - public static void exit( + @Advice.OnMethodEnter(suppress = Throwable.class) + public static void enter( @Advice.Argument(value = 0) String headerName, @Advice.Argument(value = 1) int headerValue) { - System.out.println( - "inside javax.servlet.http.HttpServletResponse.setIntHeader [" - + headerName - + ", " - + headerValue - + "]"); + Java8BytecodeBridge.currentSpan() .setAttribute( HypertraceSemanticAttributes.httpResponseHeaderLong(headerName), headerValue); @@ -207,10 +168,8 @@ static class ServletResponse_getOutputStream { @Advice.OnMethodEnter(suppress = Throwable.class) public static HttpServletResponse enter(@Advice.This ServletResponse servletResponse) { - System.out.println("start Enter javax.servlet.ServletResponse.getOutputStream"); if (!(servletResponse instanceof HttpServletResponse)) { - System.out.println("end1 Enter javax.servlet.ServletResponse.getOutputStream"); return null; } // ignore wrappers, the filter/servlet instrumentation gets the captured body from context @@ -219,7 +178,6 @@ public static HttpServletResponse enter(@Advice.This ServletResponse servletResp HttpServletResponse httpServletResponse = (HttpServletResponse) servletResponse; // the getReader method might call getInputStream HypertraceCallDepthThreadLocalMap.incrementCallDepth(ServletResponse.class); - System.out.println("end Enter javax.servlet.ServletResponse.getOutputStream"); return httpServletResponse; } @@ -228,20 +186,16 @@ public static void exit( @Advice.Enter HttpServletResponse httpServletResponse, @Advice.Thrown Throwable throwable, @Advice.Return ServletOutputStream servletOutputStream) { - System.out.println("start Exit javax.servlet.ServletResponse.getOutputStream"); if (httpServletResponse == null) { - System.out.println("end1 Exit javax.servlet.ServletResponse.getOutputStream"); return; } int callDepth = HypertraceCallDepthThreadLocalMap.decrementCallDepth(ServletResponse.class); if (callDepth > 0) { - System.out.println("end2 Exit javax.servlet.ServletResponse.getOutputStream"); return; } if (throwable != null) { - System.out.println("end3 Exit javax.servlet.ServletResponse.getOutputStream"); return; } @@ -249,13 +203,12 @@ public static void exit( VirtualField.find(ServletOutputStream.class, BoundedByteArrayOutputStream.class); if (contextStore.get(servletOutputStream) != null) { // getOutputStream() can be called multiple times - System.out.println("end4 Exit javax.servlet.ServletResponse.getOutputStream"); return; } // do not capture if data capture is disabled or not supported content type InstrumentationConfig instrumentationConfig = InstrumentationConfig.ConfigProvider.get(); - // TODO: capture body based on response content type + // FIXME: capture body based on response content type if (instrumentationConfig.httpBody().response()) { String charsetStr = httpServletResponse.getCharacterEncoding(); Charset charset = ContentTypeCharsetUtils.toCharset(charsetStr); @@ -266,7 +219,6 @@ public static void exit( VirtualField.find(HttpServletResponse.class, SpanAndObjectPair.class) .set(httpServletResponse, spanAndObjectPair); } - System.out.println("end Exit javax.servlet.ServletResponse.getOutputStream"); } } @@ -275,16 +227,13 @@ static class ServletResponse_getWriter { @Advice.OnMethodEnter(suppress = Throwable.class) public static HttpServletResponse enter(@Advice.This ServletResponse servletResponse) { - System.out.println("start Enter javax.servlet.ServletResponse.getWriter"); if (!(servletResponse instanceof HttpServletResponse)) { - System.out.println("end1 Enter javax.servlet.ServletResponse.getWriter"); return null; } HttpServletResponse httpServletResponse = (HttpServletResponse) servletResponse; // the getWriter method might call getInputStream HypertraceCallDepthThreadLocalMap.incrementCallDepth(ServletResponse.class); - System.out.println("end Enter javax.servlet.ServletResponse.getWriter"); return httpServletResponse; } @@ -293,19 +242,15 @@ public static void exit( @Advice.Enter HttpServletResponse httpServletResponse, @Advice.Thrown Throwable throwable, @Advice.Return PrintWriter printWriter) { - System.out.println("start Exit javax.servlet.ServletResponse.getWriter"); if (httpServletResponse == null) { - System.out.println("end1 Exit javax.servlet.ServletResponse.getWriter"); return; } int callDepth = HypertraceCallDepthThreadLocalMap.decrementCallDepth(ServletResponse.class); if (callDepth > 0) { - System.out.println("end2 Exit javax.servlet.ServletResponse.getWriter"); return; } if (throwable != null) { - System.out.println("end3 Exit javax.servlet.ServletResponse.getWriter"); return; } @@ -313,13 +258,12 @@ public static void exit( VirtualField.find(PrintWriter.class, BoundedCharArrayWriter.class); if (contextStore.get(printWriter) != null) { // getWriter() can be called multiple times - System.out.println("end4 Exit javax.servlet.ServletResponse.getWriter"); return; } // do not capture if data capture is disabled or not supported content type InstrumentationConfig instrumentationConfig = InstrumentationConfig.ConfigProvider.get(); - // TODO: capture body based on response content type + // FIXME: capture body based on response content type if (instrumentationConfig.httpBody().response()) { BoundedCharArrayWriter writer = BoundedBuffersFactory.createWriter(); contextStore.set(printWriter, writer); @@ -328,7 +272,6 @@ public static void exit( VirtualField.find(HttpServletResponse.class, SpanAndObjectPair.class) .set(httpServletResponse, spanAndObjectPair); } - System.out.println("end Exit javax.servlet.ServletResponse.getWriter"); } } } diff --git a/instrumentation/vertx/vertx-web-3.0/src/main/java/io/opentelemetry/javaagent/instrumentation/hypertrace/vertx/HttpRequestHandleInstrumentation.java b/instrumentation/vertx/vertx-web-3.0/src/main/java/io/opentelemetry/javaagent/instrumentation/hypertrace/vertx/HttpRequestHandleInstrumentation.java index 4b6093821..415d0e112 100644 --- a/instrumentation/vertx/vertx-web-3.0/src/main/java/io/opentelemetry/javaagent/instrumentation/hypertrace/vertx/HttpRequestHandleInstrumentation.java +++ b/instrumentation/vertx/vertx-web-3.0/src/main/java/io/opentelemetry/javaagent/instrumentation/hypertrace/vertx/HttpRequestHandleInstrumentation.java @@ -60,11 +60,9 @@ public static class HandleResponseAdvice { @Advice.OnMethodEnter(suppress = Throwable.class) public static void handleResponseEnter( @Advice.This HttpClientRequest request, @Advice.Argument(0) HttpClientResponse response) { - System.out.println("start Enter io.vertx.core.http.HttpClientRequest.handleResponse"); Contexts contexts = VirtualField.find(HttpClientRequest.class, Contexts.class).get(request); if (contexts == null) { - System.out.println("end1 Enter io.vertx.core.http.HttpClientRequest.handleResponse"); return; } Span span = Span.fromContext(contexts.context); @@ -89,7 +87,6 @@ public static void handleResponseEnter( && ContentTypeUtils.shouldCapture(contentType)) { VirtualField.find(HttpClientResponse.class, Span.class).set(response, span); } - System.out.println("end Enter io.vertx.core.http.HttpClientRequest.handleResponse"); } } } diff --git a/instrumentation/vertx/vertx-web-3.0/src/main/java/io/opentelemetry/javaagent/instrumentation/hypertrace/vertx/HttpRequestInstrumentation.java b/instrumentation/vertx/vertx-web-3.0/src/main/java/io/opentelemetry/javaagent/instrumentation/hypertrace/vertx/HttpRequestInstrumentation.java index 8b2eb5c04..15c7c74db 100644 --- a/instrumentation/vertx/vertx-web-3.0/src/main/java/io/opentelemetry/javaagent/instrumentation/hypertrace/vertx/HttpRequestInstrumentation.java +++ b/instrumentation/vertx/vertx-web-3.0/src/main/java/io/opentelemetry/javaagent/instrumentation/hypertrace/vertx/HttpRequestInstrumentation.java @@ -87,16 +87,13 @@ public void transform(TypeTransformer transformer) { public static class EndRequestAdvice { @Advice.OnMethodEnter(suppress = Throwable.class) public static void enter(@Advice.This HttpClientRequest request) { - System.out.println("start Enter io.vertx.core.http.HttpClientRequest.EndRequestAdvice"); int callDepth = HypertraceCallDepthThreadLocalMap.incrementCallDepth(HttpClientRequest.class); if (callDepth > 0) { - System.out.println("end1 Enter io.vertx.core.http.HttpClientRequest.EndRequestAdvice"); return; } Contexts contexts = VirtualField.find(HttpClientRequest.class, Contexts.class).get(request); if (contexts == null) { - System.out.println("end2 Enter io.vertx.core.http.HttpClientRequest.EndRequestAdvice"); return; } Span span = Span.fromContext(contexts.context); @@ -106,12 +103,10 @@ public static void enter(@Advice.This HttpClientRequest request) { if (buffer != null) { span.setAttribute(HypertraceSemanticAttributes.HTTP_REQUEST_BODY, buffer.toString()); } - System.out.println("end Enter io.vertx.core.http.HttpClientRequest.EndRequestAdvice"); } @Advice.OnMethodExit(suppress = Throwable.class, onThrowable = Throwable.class) public static void exit() { - System.out.println("start/end Exit io.vertx.core.http.HttpClientRequest.EndRequestAdvice"); HypertraceCallDepthThreadLocalMap.decrementCallDepth(HttpClientRequest.class); } } @@ -121,19 +116,13 @@ public static class EndRequestAdvice_string { public static void enter( @Advice.This HttpClientRequest request, @Advice.Argument(0) String chunk) throws IOException { - System.out.println( - "start Enter io.vertx.core.http.HttpClientRequest.EndRequestAdvice_string"); int callDepth = HypertraceCallDepthThreadLocalMap.incrementCallDepth(HttpClientRequest.class); if (callDepth > 0) { - System.out.println( - "end1 Enter io.vertx.core.http.HttpClientRequest.EndRequestAdvice_string"); return; } Contexts contexts = VirtualField.find(HttpClientRequest.class, Contexts.class).get(request); if (contexts == null) { - System.out.println( - "end2 Enter io.vertx.core.http.HttpClientRequest.EndRequestAdvice_string"); return; } Span span = Span.fromContext(contexts.context); @@ -151,13 +140,10 @@ public static void enter( span.setAttribute(HypertraceSemanticAttributes.HTTP_REQUEST_BODY, buffer.toString()); } } - System.out.println("end Enter io.vertx.core.http.HttpClientRequest.EndRequestAdvice_string"); } @Advice.OnMethodExit(suppress = Throwable.class, onThrowable = Throwable.class) public static void exit() { - System.out.println( - "start/end Exit io.vertx.core.http.HttpClientRequest.EndRequestAdvice_string"); HypertraceCallDepthThreadLocalMap.decrementCallDepth(HttpClientRequest.class); } } @@ -167,20 +153,14 @@ public static class EndRequestAdvice_buffer { public static void enter( @Advice.This HttpClientRequest request, @Advice.Argument(0) Buffer chunk) throws IOException { - System.out.println( - "start Enter io.vertx.core.http.HttpClientRequest.EndRequestAdvice_buffer"); int callDepth = HypertraceCallDepthThreadLocalMap.incrementCallDepth(HttpClientRequest.class); if (callDepth > 0) { - System.out.println( - "end1 Enter io.vertx.core.http.HttpClientRequest.EndRequestAdvice_buffer"); return; } Contexts contexts = VirtualField.find(HttpClientRequest.class, Contexts.class).get(request); if (contexts == null) { - System.out.println( - "end2 Enter io.vertx.core.http.HttpClientRequest.EndRequestAdvice_buffer"); return; } Span span = Span.fromContext(contexts.context); @@ -201,13 +181,10 @@ public static void enter( span.setAttribute(HypertraceSemanticAttributes.HTTP_REQUEST_BODY, buffer.toString()); } } - System.out.println("end Enter io.vertx.core.http.HttpClientRequest.EndRequestAdvice_buffer"); } @Advice.OnMethodExit(suppress = Throwable.class, onThrowable = Throwable.class) public static void exit() { - System.out.println( - "start/end Exit io.vertx.core.http.HttpClientRequest.EndRequestAdvice_buffer"); HypertraceCallDepthThreadLocalMap.decrementCallDepth(HttpClientRequest.class); } } @@ -217,13 +194,9 @@ public static class WriteRequestAdvice_string { public static void enter( @Advice.This HttpClientRequest request, @Advice.Argument(0) String chunk) throws IOException { - System.out.println( - "start Enter io.vertx.core.http.HttpClientRequest.WriteRequestAdvice_string"); int callDepth = HypertraceCallDepthThreadLocalMap.incrementCallDepth(HttpClientRequest.class); if (callDepth > 0) { - System.out.println( - "end1 Enter io.vertx.core.http.HttpClientRequest.WriteRequestAdvice_string"); return; } @@ -241,14 +214,10 @@ public static void enter( } buffer.write(chunk); } - System.out.println( - "end Enter io.vertx.core.http.HttpClientRequest.WriteRequestAdvice_string"); } @Advice.OnMethodExit(suppress = Throwable.class, onThrowable = Throwable.class) public static void exit() { - System.out.println( - "start/end Exit io.vertx.core.http.HttpClientRequest.WriteRequestAdvice_string"); HypertraceCallDepthThreadLocalMap.decrementCallDepth(HttpClientRequest.class); } } @@ -258,13 +227,9 @@ public static class WriteRequestAdvice_buffer { public static void enter( @Advice.This HttpClientRequest request, @Advice.Argument(0) Buffer chunk) throws IOException { - System.out.println( - "start Enter io.vertx.core.http.HttpClientRequest.WriteRequestAdvice_buffer"); int callDepth = HypertraceCallDepthThreadLocalMap.incrementCallDepth(HttpClientRequest.class); if (callDepth > 0) { - System.out.println( - "end1 Enter io.vertx.core.http.HttpClientRequest.WriteRequestAdvice_buffer"); return; } @@ -282,14 +247,10 @@ public static void enter( } buffer.write(chunk.toString(StandardCharsets.UTF_8.name())); } - System.out.println( - "end Enter io.vertx.core.http.HttpClientRequest.WriteRequestAdvice_buffer"); } @Advice.OnMethodExit(suppress = Throwable.class, onThrowable = Throwable.class) public static void exit() { - System.out.println( - "start/end Exit io.vertx.core.http.HttpClientRequest.WriteRequestAdvice_buffer"); HypertraceCallDepthThreadLocalMap.decrementCallDepth(HttpClientRequest.class); } } diff --git a/instrumentation/vertx/vertx-web-3.0/src/main/java/io/opentelemetry/javaagent/instrumentation/hypertrace/vertx/HttpResponseInstrumentation.java b/instrumentation/vertx/vertx-web-3.0/src/main/java/io/opentelemetry/javaagent/instrumentation/hypertrace/vertx/HttpResponseInstrumentation.java index c684d329b..fac690dcc 100644 --- a/instrumentation/vertx/vertx-web-3.0/src/main/java/io/opentelemetry/javaagent/instrumentation/hypertrace/vertx/HttpResponseInstrumentation.java +++ b/instrumentation/vertx/vertx-web-3.0/src/main/java/io/opentelemetry/javaagent/instrumentation/hypertrace/vertx/HttpResponseInstrumentation.java @@ -57,18 +57,15 @@ public static class ResponseBodyHandler { public static void handleResponseEnter( @Advice.This HttpClientResponse response, @Advice.Argument(value = 0, readOnly = false) Handler handler) { - System.out.println("start Enter io.vertx.core.http.HttpClientResponse.bodyHandler"); Span span = VirtualField.find(HttpClientResponse.class, Span.class).get(response); if (span == null) { // request not traced - e.g. wrong content type - System.out.println("end1 Enter io.vertx.core.http.HttpClientResponse.bodyHandler"); return; } VirtualField.find(HttpClientResponse.class, Span.class).set(response, null); handler = new ResponseBodyWrappingHandler(handler, span); - System.out.println("end Enter io.vertx.core.http.HttpClientResponse.bodyHandler"); } } } diff --git a/javaagent-core/src/main/java/org/hypertrace/agent/core/instrumentation/HypertraceSemanticAttributes.java b/javaagent-core/src/main/java/org/hypertrace/agent/core/instrumentation/HypertraceSemanticAttributes.java index d5f2ba020..57b5a5232 100644 --- a/javaagent-core/src/main/java/org/hypertrace/agent/core/instrumentation/HypertraceSemanticAttributes.java +++ b/javaagent-core/src/main/java/org/hypertrace/agent/core/instrumentation/HypertraceSemanticAttributes.java @@ -33,10 +33,6 @@ public static AttributeKey httpRequestHeader(String header) { return AttributeKey.stringKey("http.request.header." + header.toLowerCase()); } - public static AttributeKey httpRequestHeaderLong(String header) { - return AttributeKey.longKey("http.request.header." + header.toLowerCase()); - } - public static AttributeKey httpResponseHeader(String header) { return AttributeKey.stringKey("http.response.header." + header.toLowerCase()); } @@ -54,6 +50,8 @@ public static AttributeKey httpResponseHeaderLong(String header) { AttributeKey.stringKey("http.request.session_id"); public static final AttributeKey HTTP_RESPONSE_CONTENT_TYPE = AttributeKey.stringKey("http.response.header.content-type"); + public static final AttributeKey HTTP_RESPONSE_CONTENT_LENGTH = + AttributeKey.longKey("http.response.header.content-length"); public static final AttributeKey RPC_REQUEST_BODY = AttributeKey.stringKey("rpc.request.body"); From d0bd73e1051ad9436d11ad836d85f78f64c2c3d5 Mon Sep 17 00:00:00 2001 From: Kaushal Agrawal Date: Thu, 23 Mar 2023 18:21:59 +0530 Subject: [PATCH 5/7] PR comments --- .../servlet/servlet-2.2/build.gradle.kts | 10 +--- .../Servlet2AndFilterInstrumentation.java | 12 ++--- .../ServletInputStreamInstrumentation.java | 53 ++++++++++++++++++- .../ServletRequestInstrumentation.java | 5 +- .../ServletOutputStreamInstrumentation.java | 3 -- .../servlet/servlet-3.0/build.gradle.kts | 3 +- .../ServletInputStreamInstrumentation.java | 2 +- .../ServletRequestInstrumentation.java | 7 +-- .../v3_0/nowrapping/request/Utils.java | 10 ++-- .../buffer/StringMapSpanPair.java | 4 +- javaagent/build.gradle.kts | 1 - 11 files changed, 70 insertions(+), 40 deletions(-) diff --git a/instrumentation/servlet/servlet-2.2/build.gradle.kts b/instrumentation/servlet/servlet-2.2/build.gradle.kts index f5c7cb164..d663dd430 100644 --- a/instrumentation/servlet/servlet-2.2/build.gradle.kts +++ b/instrumentation/servlet/servlet-2.2/build.gradle.kts @@ -16,7 +16,7 @@ muzzle { fail { group = "javax.servlet" module = "javax.servlet-api" - versions = "(,)" + versions = "[3.0,)" } } @@ -37,12 +37,4 @@ dependencies { testRuntimeOnly("io.opentelemetry.javaagent.instrumentation:opentelemetry-javaagent-servlet-common-bootstrap:${versions["opentelemetry_java_agent"]}") muzzleBootstrap("io.opentelemetry.javaagent.instrumentation:opentelemetry-javaagent-servlet-common-bootstrap:${versions["opentelemetry_java_agent"]}") compileOnly("javax.servlet:servlet-api:2.2") - -// testInstrumentation(project(":instrumentation:servlet:servlet-3.0:javaagent")) -// -// testLibrary("org.eclipse.jetty:jetty-server:7.0.0.v20091005") -// testLibrary("org.eclipse.jetty:jetty-servlet:7.0.0.v20091005") -// -// latestDepTestLibrary("org.eclipse.jetty:jetty-server:7.+") // see servlet-3.0 module -// latestDepTestLibrary("org.eclipse.jetty:jetty-servlet:7.+") // see servlet-3.0 module } diff --git a/instrumentation/servlet/servlet-2.2/src/main/java/io/opentelemetry/javaagent/instrumentation/hypertrace/servlet/v2_2/nowrapping/Servlet2AndFilterInstrumentation.java b/instrumentation/servlet/servlet-2.2/src/main/java/io/opentelemetry/javaagent/instrumentation/hypertrace/servlet/v2_2/nowrapping/Servlet2AndFilterInstrumentation.java index 477386844..d0077ffc4 100644 --- a/instrumentation/servlet/servlet-2.2/src/main/java/io/opentelemetry/javaagent/instrumentation/hypertrace/servlet/v2_2/nowrapping/Servlet2AndFilterInstrumentation.java +++ b/instrumentation/servlet/servlet-2.2/src/main/java/io/opentelemetry/javaagent/instrumentation/hypertrace/servlet/v2_2/nowrapping/Servlet2AndFilterInstrumentation.java @@ -66,7 +66,6 @@ public ElementMatcher typeMatcher() { @Override public void transform(TypeTransformer transformer) { - // print version with Implmentation Version transformer.applyAdviceToMethod( namedOneOf("doFilter", "service") .and(takesArgument(0, named("javax.servlet.ServletRequest"))) @@ -79,17 +78,17 @@ public void transform(TypeTransformer transformer) { public static class ServletAdvice { @Advice.OnMethodEnter(suppress = Throwable.class, skipOn = Advice.OnNonDefaultValue.class) - public static boolean start( + public static void start( @Advice.Argument(value = 0) ServletRequest request, @Advice.Argument(value = 1) ServletResponse response, @Advice.Local("currentSpan") Span currentSpan) { int callDepth = HypertraceCallDepthThreadLocalMap.incrementCallDepth(Servlet2InstrumentationName.class); if (callDepth > 0) { - return false; + return; } if (!(request instanceof HttpServletRequest) || !(response instanceof HttpServletResponse)) { - return false; + return; } HttpServletRequest httpRequest = (HttpServletRequest) request; @@ -123,7 +122,7 @@ public static boolean start( if (FilterRegistry.getFilter().evaluateRequestHeaders(currentSpan, headers)) { httpResponse.setStatus(403); // skip execution of the user code - return true; + return; } if (instrumentationConfig.httpBody().request() @@ -135,7 +134,7 @@ public static boolean start( httpRequest, new SpanAndObjectPair(currentSpan, Collections.unmodifiableMap(headers))); } - return false; + return; } @Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class) @@ -179,7 +178,6 @@ public static void exit( VirtualField.find(HttpServletRequest.class, StringMapSpanPair.class); // capture response body - // TODO: capture response headers // FIXME: capture body based on response content type if (instrumentationConfig.httpBody().response()) { Utils.captureResponseBody( diff --git a/instrumentation/servlet/servlet-2.2/src/main/java/io/opentelemetry/javaagent/instrumentation/hypertrace/servlet/v2_2/nowrapping/request/ServletInputStreamInstrumentation.java b/instrumentation/servlet/servlet-2.2/src/main/java/io/opentelemetry/javaagent/instrumentation/hypertrace/servlet/v2_2/nowrapping/request/ServletInputStreamInstrumentation.java index 1238d57df..2f40bc8ef 100644 --- a/instrumentation/servlet/servlet-2.2/src/main/java/io/opentelemetry/javaagent/instrumentation/hypertrace/servlet/v2_2/nowrapping/request/ServletInputStreamInstrumentation.java +++ b/instrumentation/servlet/servlet-2.2/src/main/java/io/opentelemetry/javaagent/instrumentation/hypertrace/servlet/v2_2/nowrapping/request/ServletInputStreamInstrumentation.java @@ -65,15 +65,20 @@ public void transform(TypeTransformer transformer) { transformer.applyAdviceToMethod( named("readAllBytes").and(takesArguments(0)).and(isPublic()), ServletInputStreamInstrumentation.class.getName() + "$InputStream_ReadAllBytes"); - // TODO: readNBytes(int len) is not transformed transformer.applyAdviceToMethod( named("readNBytes") - .and(takesArguments(0)) + .and(takesArguments(3)) .and(takesArgument(0, is(byte[].class))) .and(takesArgument(1, is(int.class))) .and(takesArgument(2, is(int.class))) .and(isPublic()), ServletInputStreamInstrumentation.class.getName() + "$InputStream_ReadNBytes"); + transformer.applyAdviceToMethod( + named("readNBytes") + .and(takesArguments(1)) + .and(takesArgument(0, is(int.class))) + .and(isPublic()), + ServletInputStreamInstrumentation.class.getName() + "$InputStream_ReadNBytesLength"); // ServletInputStream methods transformer.applyAdviceToMethod( @@ -322,4 +327,48 @@ public static void exit( } } } + + @SuppressWarnings("unused") + public static class InputStream_ReadNBytesLength { + + @Advice.OnMethodEnter(suppress = Throwable.class) + public static ByteBufferSpanPair enter(@Advice.This ServletInputStream thizz) { + ByteBufferSpanPair bufferSpanPair = + VirtualField.find(ServletInputStream.class, ByteBufferSpanPair.class).get(thizz); + if (bufferSpanPair == null) { + return null; + } + + HypertraceCallDepthThreadLocalMap.incrementCallDepth(ServletInputStream.class); + return bufferSpanPair; + } + + @Advice.OnMethodExit(onThrowable = Throwable.class) + public static void exit( + @Advice.This ServletInputStream thizz, + @Advice.Return byte[] b, + @Advice.Enter ByteBufferSpanPair bufferSpanPair) + throws IOException { + try { + if (bufferSpanPair == null) { + return; + } + int callDepth = + HypertraceCallDepthThreadLocalMap.decrementCallDepth(ServletInputStream.class); + if (callDepth > 0) { + return; + } + bufferSpanPair.writeToBuffer(b); + if (thizz.available() == 0) { + bufferSpanPair.captureBody(HypertraceSemanticAttributes.HTTP_REQUEST_BODY); + } + } catch (Throwable t) { + if (t instanceof HypertraceEvaluationException) { + throw t; + } else { + // ignore + } + } + } + } } diff --git a/instrumentation/servlet/servlet-2.2/src/main/java/io/opentelemetry/javaagent/instrumentation/hypertrace/servlet/v2_2/nowrapping/request/ServletRequestInstrumentation.java b/instrumentation/servlet/servlet-2.2/src/main/java/io/opentelemetry/javaagent/instrumentation/hypertrace/servlet/v2_2/nowrapping/request/ServletRequestInstrumentation.java index fde0b65c4..6793b1818 100644 --- a/instrumentation/servlet/servlet-2.2/src/main/java/io/opentelemetry/javaagent/instrumentation/hypertrace/servlet/v2_2/nowrapping/request/ServletRequestInstrumentation.java +++ b/instrumentation/servlet/servlet-2.2/src/main/java/io/opentelemetry/javaagent/instrumentation/hypertrace/servlet/v2_2/nowrapping/request/ServletRequestInstrumentation.java @@ -53,10 +53,7 @@ public void transform(TypeTransformer transformer) { .and(isPublic()), ServletRequestInstrumentation.class.getName() + "$ServletRequest_getInputStream_advice"); transformer.applyAdviceToMethod( - named("getReader") - .and(takesArguments(0)) - // .and(returns(BufferedReader.class)) - .and(isPublic()), + named("getReader").and(takesArguments(0)).and(isPublic()), ServletRequestInstrumentation.class.getName() + "$ServletRequest_getReader_advice"); transformer.applyAdviceToMethod( named("getParameter") diff --git a/instrumentation/servlet/servlet-2.2/src/main/java/io/opentelemetry/javaagent/instrumentation/hypertrace/servlet/v2_2/nowrapping/response/ServletOutputStreamInstrumentation.java b/instrumentation/servlet/servlet-2.2/src/main/java/io/opentelemetry/javaagent/instrumentation/hypertrace/servlet/v2_2/nowrapping/response/ServletOutputStreamInstrumentation.java index 4c9b43446..e856b1127 100644 --- a/instrumentation/servlet/servlet-2.2/src/main/java/io/opentelemetry/javaagent/instrumentation/hypertrace/servlet/v2_2/nowrapping/response/ServletOutputStreamInstrumentation.java +++ b/instrumentation/servlet/servlet-2.2/src/main/java/io/opentelemetry/javaagent/instrumentation/hypertrace/servlet/v2_2/nowrapping/response/ServletOutputStreamInstrumentation.java @@ -69,9 +69,6 @@ public void transform(TypeTransformer transformer) { .and(takesArgument(2, is(int.class))) .and(isPublic()), ServletOutputStreamInstrumentation.class.getName() + "$OutputStream_writeByteArrOffset"); - - // close() is not instrumented due to some issue with Tomcat - // refer the comment in servlet-3.0 code } @SuppressWarnings("unused") diff --git a/instrumentation/servlet/servlet-3.0/build.gradle.kts b/instrumentation/servlet/servlet-3.0/build.gradle.kts index 0720a6445..4086a4b78 100644 --- a/instrumentation/servlet/servlet-3.0/build.gradle.kts +++ b/instrumentation/servlet/servlet-3.0/build.gradle.kts @@ -9,7 +9,8 @@ muzzle { pass { group = "javax.servlet" module = "javax.servlet-api" - versions = "[3.0.0,)" + versions = "[3.0,)" + assertInverse = true } // fail on all old servlet-api. This groupId was changed in 3.x to javax.servlet-api fail { diff --git a/instrumentation/servlet/servlet-3.0/src/main/java/io/opentelemetry/javaagent/instrumentation/hypertrace/servlet/v3_0/nowrapping/request/ServletInputStreamInstrumentation.java b/instrumentation/servlet/servlet-3.0/src/main/java/io/opentelemetry/javaagent/instrumentation/hypertrace/servlet/v3_0/nowrapping/request/ServletInputStreamInstrumentation.java index 246c9579c..0bcbc30b2 100644 --- a/instrumentation/servlet/servlet-3.0/src/main/java/io/opentelemetry/javaagent/instrumentation/hypertrace/servlet/v3_0/nowrapping/request/ServletInputStreamInstrumentation.java +++ b/instrumentation/servlet/servlet-3.0/src/main/java/io/opentelemetry/javaagent/instrumentation/hypertrace/servlet/v3_0/nowrapping/request/ServletInputStreamInstrumentation.java @@ -68,7 +68,7 @@ public void transform(TypeTransformer transformer) { // TODO: readNBytes(int len) is not transformed transformer.applyAdviceToMethod( named("readNBytes") - .and(takesArguments(0)) + .and(takesArguments(3)) .and(takesArgument(0, is(byte[].class))) .and(takesArgument(1, is(int.class))) .and(takesArgument(2, is(int.class))) diff --git a/instrumentation/servlet/servlet-3.0/src/main/java/io/opentelemetry/javaagent/instrumentation/hypertrace/servlet/v3_0/nowrapping/request/ServletRequestInstrumentation.java b/instrumentation/servlet/servlet-3.0/src/main/java/io/opentelemetry/javaagent/instrumentation/hypertrace/servlet/v3_0/nowrapping/request/ServletRequestInstrumentation.java index 01ac6da8f..e5e3b7ed1 100644 --- a/instrumentation/servlet/servlet-3.0/src/main/java/io/opentelemetry/javaagent/instrumentation/hypertrace/servlet/v3_0/nowrapping/request/ServletRequestInstrumentation.java +++ b/instrumentation/servlet/servlet-3.0/src/main/java/io/opentelemetry/javaagent/instrumentation/hypertrace/servlet/v3_0/nowrapping/request/ServletRequestInstrumentation.java @@ -53,10 +53,7 @@ public void transform(TypeTransformer transformer) { .and(isPublic()), ServletRequestInstrumentation.class.getName() + "$ServletRequest_getInputStream_advice"); transformer.applyAdviceToMethod( - named("getReader") - .and(takesArguments(0)) - // .and(returns(BufferedReader.class)) - .and(isPublic()), + named("getReader").and(takesArguments(0)).and(isPublic()), ServletRequestInstrumentation.class.getName() + "$ServletRequest_getReader_advice"); transformer.applyAdviceToMethod( named("getParameter") @@ -182,7 +179,7 @@ static class ServletRequest_getParameter_advice { /** * Instrumentation template for ServletRequest.getParameter() entry point. * - * @param servletRequest servletRequest + * @param servletRequest the ServletRequest instance * @return a (possibly null) SpanAndObjectPair, which will be passed to the method exit * instrumentation */ diff --git a/instrumentation/servlet/servlet-3.0/src/main/java/io/opentelemetry/javaagent/instrumentation/hypertrace/servlet/v3_0/nowrapping/request/Utils.java b/instrumentation/servlet/servlet-3.0/src/main/java/io/opentelemetry/javaagent/instrumentation/hypertrace/servlet/v3_0/nowrapping/request/Utils.java index 0276ae540..0b23786b1 100644 --- a/instrumentation/servlet/servlet-3.0/src/main/java/io/opentelemetry/javaagent/instrumentation/hypertrace/servlet/v3_0/nowrapping/request/Utils.java +++ b/instrumentation/servlet/servlet-3.0/src/main/java/io/opentelemetry/javaagent/instrumentation/hypertrace/servlet/v3_0/nowrapping/request/Utils.java @@ -64,12 +64,12 @@ public static CharBufferSpanPair createRequestCharBufferSpanPair( } /** - * Create a StringMapSpanPair. + * Create a StringMapSpanPair with provided values. * - * @param stringMap stringMap - * @param span span - * @param headers headers - * @return StringMapSpanPair + * @param stringMap the stringMap instance + * @param span the span instance + * @param headers the headers instance + * @return a StringMapSpanPair created with values of the above instances */ public static StringMapSpanPair createStringMapSpanPair( Map stringMap, Span span, Map headers) { diff --git a/javaagent-core/src/main/java/org/hypertrace/agent/core/instrumentation/buffer/StringMapSpanPair.java b/javaagent-core/src/main/java/org/hypertrace/agent/core/instrumentation/buffer/StringMapSpanPair.java index 32976ba53..768557d09 100644 --- a/javaagent-core/src/main/java/org/hypertrace/agent/core/instrumentation/buffer/StringMapSpanPair.java +++ b/javaagent-core/src/main/java/org/hypertrace/agent/core/instrumentation/buffer/StringMapSpanPair.java @@ -39,9 +39,9 @@ public StringMapSpanPair(Span span, Map stringMap, Map attributeKey) { if (!mapCaptured) { diff --git a/javaagent/build.gradle.kts b/javaagent/build.gradle.kts index f276b9ca2..c2febd5a3 100644 --- a/javaagent/build.gradle.kts +++ b/javaagent/build.gradle.kts @@ -13,7 +13,6 @@ dependencies { // https://dl.bintray.com/open-telemetry/maven/ implementation("io.opentelemetry.javaagent", "opentelemetry-javaagent", version = "${versions["opentelemetry_java_agent_all"]}") implementation(project(":filter-api")) - implementation("io.opentelemetry:opentelemetry-sdk-trace:1.13.0") } base.archivesBaseName = "hypertrace-agent" From 16a5ca36922ce52887ac473cb7aa7e27e879818e Mon Sep 17 00:00:00 2001 From: Kaushal Agrawal Date: Fri, 24 Mar 2023 01:52:36 +0530 Subject: [PATCH 6/7] fixed muzzle error, added tests WIP --- .../servlet/servlet-2.2/build.gradle.kts | 19 +- .../Servlet2InstrumentationModule.java | 9 + .../Servlet2InstrumentationTest.java | 364 ++++++++++++++++++ .../servlet/v2_2/nowrapping/TestServlets.java | 223 +++++++++++ ...eamContextAccessInstrumentationModule.java | 93 +++++ ...ServletInputStreamInstrumentationTest.java | 96 +++++ ...eamContextAccessInstrumentationModule.java | 92 +++++ ...ervletOutputStreamInstrumentationTest.java | 93 +++++ .../java/org/DelegatingBufferedReader.java | 37 ++ .../test/java/org/DelegatingPrintWriter.java | 41 ++ .../org/DelegatingServletInputStream.java | 50 +++ .../org/DelegatingServletOutputStream.java | 55 +++ .../java/org/ServletStreamContextAccess.java | 31 ++ .../test/java/org/TestServletInputStream.java | 35 ++ .../java/org/TestServletOutputStream.java | 28 ++ ...sion.instrumentation.InstrumentationModule | 2 + .../Servlet30InstrumentationModule.java | 8 + .../v3_0/nowrapping/request/Utils.java | 6 +- 18 files changed, 1274 insertions(+), 8 deletions(-) create mode 100644 instrumentation/servlet/servlet-2.2/src/test/java/io/opentelemetry/javaagent/instrumentation/hypertrace/servlet/v2_2/nowrapping/Servlet2InstrumentationTest.java create mode 100644 instrumentation/servlet/servlet-2.2/src/test/java/io/opentelemetry/javaagent/instrumentation/hypertrace/servlet/v2_2/nowrapping/TestServlets.java create mode 100644 instrumentation/servlet/servlet-2.2/src/test/java/io/opentelemetry/javaagent/instrumentation/hypertrace/servlet/v2_2/nowrapping/request/ServletInputStreamContextAccessInstrumentationModule.java create mode 100644 instrumentation/servlet/servlet-2.2/src/test/java/io/opentelemetry/javaagent/instrumentation/hypertrace/servlet/v2_2/nowrapping/request/ServletInputStreamInstrumentationTest.java create mode 100644 instrumentation/servlet/servlet-2.2/src/test/java/io/opentelemetry/javaagent/instrumentation/hypertrace/servlet/v2_2/nowrapping/response/ServletOutputStreamContextAccessInstrumentationModule.java create mode 100644 instrumentation/servlet/servlet-2.2/src/test/java/io/opentelemetry/javaagent/instrumentation/hypertrace/servlet/v2_2/nowrapping/response/ServletOutputStreamInstrumentationTest.java create mode 100644 instrumentation/servlet/servlet-2.2/src/test/java/org/DelegatingBufferedReader.java create mode 100644 instrumentation/servlet/servlet-2.2/src/test/java/org/DelegatingPrintWriter.java create mode 100644 instrumentation/servlet/servlet-2.2/src/test/java/org/DelegatingServletInputStream.java create mode 100644 instrumentation/servlet/servlet-2.2/src/test/java/org/DelegatingServletOutputStream.java create mode 100644 instrumentation/servlet/servlet-2.2/src/test/java/org/ServletStreamContextAccess.java create mode 100644 instrumentation/servlet/servlet-2.2/src/test/java/org/TestServletInputStream.java create mode 100644 instrumentation/servlet/servlet-2.2/src/test/java/org/TestServletOutputStream.java create mode 100644 instrumentation/servlet/servlet-2.2/src/test/resources/META-INF/services/io.opentelemetry.javaagent.extension.instrumentation.InstrumentationModule diff --git a/instrumentation/servlet/servlet-2.2/build.gradle.kts b/instrumentation/servlet/servlet-2.2/build.gradle.kts index d663dd430..deb4f9a28 100644 --- a/instrumentation/servlet/servlet-2.2/build.gradle.kts +++ b/instrumentation/servlet/servlet-2.2/build.gradle.kts @@ -9,14 +9,12 @@ muzzle { pass { group = "javax.servlet" module = "servlet-api" - versions = "[2.2, 3.0)" - assertInverse = true + versions = "[2.2,3.0)" } - fail { group = "javax.servlet" module = "javax.servlet-api" - versions = "[3.0,)" + versions = "(,)" } } @@ -33,8 +31,19 @@ val versions: Map by extra dependencies { implementation("io.opentelemetry.javaagent.instrumentation:opentelemetry-javaagent-servlet-common:${versions["opentelemetry_java_agent"]}") + implementation("io.opentelemetry.javaagent.instrumentation:opentelemetry-javaagent-servlet-2.2:${versions["opentelemetry_java_agent"]}") // Servlet2Accessor + testImplementation("io.opentelemetry.javaagent.instrumentation:opentelemetry-javaagent-servlet-2.2:${versions["opentelemetry_java_agent"]}") + testImplementation("io.opentelemetry.javaagent:opentelemetry-muzzle:${versions["opentelemetry_java_agent"]}") compileOnly("io.opentelemetry.javaagent:opentelemetry-javaagent-bootstrap:${versions["opentelemetry_java_agent"]}") + compileOnly("javax.servlet:servlet-api:2.2") testRuntimeOnly("io.opentelemetry.javaagent.instrumentation:opentelemetry-javaagent-servlet-common-bootstrap:${versions["opentelemetry_java_agent"]}") muzzleBootstrap("io.opentelemetry.javaagent.instrumentation:opentelemetry-javaagent-servlet-common-bootstrap:${versions["opentelemetry_java_agent"]}") - compileOnly("javax.servlet:servlet-api:2.2") + + testImplementation(project(":instrumentation:servlet:servlet-rw")) + testImplementation(testFixtures(project(":testing-common")) as ProjectDependency) { + exclude(group = "org.eclipse.jetty", module = "jetty-server") + } + testImplementation("org.eclipse.jetty:jetty-server:8.1.22.v20160922") + testImplementation("org.eclipse.jetty:jetty-servlet:8.1.22.v20160922") + testImplementation("io.opentelemetry.instrumentation:opentelemetry-instrumentation-api-semconv:${versions["opentelemetry_semconv"]}") } diff --git a/instrumentation/servlet/servlet-2.2/src/main/java/io/opentelemetry/javaagent/instrumentation/hypertrace/servlet/v2_2/nowrapping/Servlet2InstrumentationModule.java b/instrumentation/servlet/servlet-2.2/src/main/java/io/opentelemetry/javaagent/instrumentation/hypertrace/servlet/v2_2/nowrapping/Servlet2InstrumentationModule.java index efb8603da..4914b7208 100644 --- a/instrumentation/servlet/servlet-2.2/src/main/java/io/opentelemetry/javaagent/instrumentation/hypertrace/servlet/v2_2/nowrapping/Servlet2InstrumentationModule.java +++ b/instrumentation/servlet/servlet-2.2/src/main/java/io/opentelemetry/javaagent/instrumentation/hypertrace/servlet/v2_2/nowrapping/Servlet2InstrumentationModule.java @@ -16,6 +16,9 @@ package io.opentelemetry.javaagent.instrumentation.hypertrace.servlet.v2_2.nowrapping; +import static io.opentelemetry.javaagent.extension.matcher.AgentElementMatchers.hasClassesNamed; +import static net.bytebuddy.matcher.ElementMatchers.not; + import com.google.auto.service.AutoService; import io.opentelemetry.javaagent.extension.instrumentation.InstrumentationModule; import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation; @@ -25,6 +28,7 @@ import io.opentelemetry.javaagent.instrumentation.hypertrace.servlet.v2_2.nowrapping.response.ServletResponseInstrumentation; import java.util.Arrays; import java.util.List; +import net.bytebuddy.matcher.ElementMatcher; @SuppressWarnings("unused") @AutoService(InstrumentationModule.class) @@ -39,6 +43,11 @@ public int order() { return 1; } + @Override + public ElementMatcher.Junction classLoaderMatcher() { + return not(hasClassesNamed("javax.servlet.HttpConstraintElement")); + } + @Override public List typeInstrumentations() { return Arrays.asList( diff --git a/instrumentation/servlet/servlet-2.2/src/test/java/io/opentelemetry/javaagent/instrumentation/hypertrace/servlet/v2_2/nowrapping/Servlet2InstrumentationTest.java b/instrumentation/servlet/servlet-2.2/src/test/java/io/opentelemetry/javaagent/instrumentation/hypertrace/servlet/v2_2/nowrapping/Servlet2InstrumentationTest.java new file mode 100644 index 000000000..c8572d041 --- /dev/null +++ b/instrumentation/servlet/servlet-2.2/src/test/java/io/opentelemetry/javaagent/instrumentation/hypertrace/servlet/v2_2/nowrapping/Servlet2InstrumentationTest.java @@ -0,0 +1,364 @@ +/* + * Copyright The Hypertrace Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.opentelemetry.javaagent.instrumentation.hypertrace.servlet.v2_2.nowrapping; + +import io.opentelemetry.javaagent.instrumentation.hypertrace.servlet.v2_2.nowrapping.TestServlets.*; +import io.opentelemetry.sdk.trace.data.SpanData; +import okhttp3.*; +import org.eclipse.jetty.server.Server; +import org.eclipse.jetty.servlet.ServletContextHandler; +import org.hypertrace.agent.core.instrumentation.HypertraceEvaluationException; +import org.hypertrace.agent.core.instrumentation.HypertraceSemanticAttributes; +import org.hypertrace.agent.testing.AbstractInstrumenterTest; +import org.junit.jupiter.api.AfterAll; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +import javax.servlet.*; +import java.io.IOException; +import java.util.EnumSet; +import java.util.List; + +public class Servlet2InstrumentationTest extends AbstractInstrumenterTest { + private static final String REQUEST_BODY = "hello"; + private static final String REQUEST_HEADER = "requestheader"; + private static final String REQUEST_HEADER_VALUE = "requestvalue"; + + private static final Server server = new Server(0); + private static int serverPort; + + /* + * Filter that mimics the spring framework. It will catch and wrap our blocking exception + */ + public static class WrapExceptionFilter implements Servlet { + @Override + public void service(ServletRequest request, ServletResponse response) + throws ServletException, IOException { + System.out.print("hello from service"); + try { + chain.doFilter(request, response); + } catch (Throwable t) { + if (t instanceof HypertraceEvaluationException) { + throw new RuntimeException("wrapped exception", t); + } + throw t; + } + } + + @Override + public void init(FilterConfig arg0) throws ServletException {} + + @Override + public void destroy() {} + } + + @BeforeAll + public static void startServer() throws Exception { + ServletContextHandler handler = new ServletContextHandler(); + + handler.addServlet(GetHello.class, "/hello"); + handler.addServlet(EchoStream_single_byte.class, "/echo_stream_single_byte"); + handler.addServlet(EchoStream_arr.class, "/echo_stream_arr"); + handler.addFilter( + WrapExceptionFilter.class, "/echo_stream_arr", EnumSet.of(DispatcherType.REQUEST)); + handler.addServlet(EchoStream_arr_offset.class, "/echo_stream_arr_offset"); + handler.addServlet(EchoStream_readLine_print.class, "/echo_stream_readLine_print"); + handler.addServlet(EchoWriter_single_char.class, "/echo_writer_single_char"); + handler.addServlet(TestServlets.EchoWriter_arr.class, "/echo_writer_arr"); + handler.addServlet(TestServlets.EchoWriter_arr_offset.class, "/echo_writer_arr_offset"); + handler.addServlet(TestServlets.EchoWriter_readLine_write.class, "/echo_writer_readLine_write"); + handler.addServlet(TestServlets.EchoWriter_readLines.class, "/echo_writer_readLines"); + handler.addServlet( + TestServlets.EchoWriter_readLine_print_str.class, "/echo_writer_readLine_print_str"); + handler.addServlet( + TestServlets.EchoWriter_readLine_print_arr.class, "/echo_writer_readLine_print_arr"); + handler.addServlet(TestServlets.Forward_to_post.class, "/forward_to_echo"); + handler.addServlet(EchoStream_read_large_array.class, "/echo_stream_read_large_array"); + handler.addServlet(EchoReader_read_large_array.class, "/echo_reader_read_large_array"); + server.setHandler(handler); + server.start(); + serverPort = server.getConnectors()[0].getLocalPort(); + } + + @AfterAll + public static void stopServer() throws Exception { + server.stop(); + } + + @Test + public void forward_to_post() throws Exception { + postJson(String.format("http://localhost:%d/forward_to_echo", serverPort)); + } + + @Test + public void postJson_stream_single_byte() throws Exception { + postJson(String.format("http://localhost:%d/echo_stream_single_byte", serverPort)); + } + + @Test + public void postJson_stream_read_large_array() throws Exception { + postJson(String.format("http://localhost:%d/echo_stream_read_large_array", serverPort)); + } + + @Test + public void postJson_reader_read_large_array() throws Exception { + postJson(String.format("http://localhost:%d/echo_reader_read_large_array", serverPort)); + } + + @Test + public void postJson_stream_arr() throws Exception { + postJson(String.format("http://localhost:%d/echo_stream_arr", serverPort)); + } + + @Test + public void postJson_stream_arr_offset() throws Exception { + postJson(String.format("http://localhost:%d/echo_stream_arr_offset", serverPort)); + } + + @Test + public void postJson_stream_readLine_print() throws Exception { + postJson(String.format("http://localhost:%d/echo_stream_readLine_print", serverPort)); + } + + @Test + public void postJson_writer_single_char() throws Exception { + postJson(String.format("http://localhost:%d/echo_writer_single_char", serverPort)); + } + + @Test + public void postJson_writer_arr() throws Exception { + postJson(String.format("http://localhost:%d/echo_writer_arr", serverPort)); + } + + @Test + public void postJson_writer_arr_offset() throws Exception { + postJson(String.format("http://localhost:%d/echo_writer_arr_offset", serverPort)); + } + + @Test + public void postJson_writer_readLine_write() throws Exception { + postJson(String.format("http://localhost:%d/echo_writer_readLine_write", serverPort)); + } + + @Test + public void postJson_writer_readLine_print_str() throws Exception { + postJson(String.format("http://localhost:%d/echo_writer_readLine_print_str", serverPort)); + } + + @Test + public void postJson_writer_readLines() throws Exception { + postJson(String.format("http://localhost:%d/echo_writer_readLines", serverPort)); + } + + @Test + public void postJson_writer_readLine_print_arr() throws Exception { + postJson(String.format("http://localhost:%d/echo_writer_readLine_print_arr", serverPort)); + } + + @Test + public void portUrlEncoded() throws Exception { + FormBody formBody = new FormBody.Builder().add("key1", "value1").add("key2", "value2").build(); + Request request = + new Request.Builder() + .url(String.format("http://localhost:%d/echo_stream_single_byte", serverPort)) + .post(formBody) + .header(REQUEST_HEADER, REQUEST_HEADER_VALUE) + .build(); + try (Response response = httpClient.newCall(request).execute()) { + Assertions.assertEquals(200, response.code()); + } + + TEST_WRITER.waitForTraces(1); + List> traces = TEST_WRITER.getTraces(); + Assertions.assertEquals(1, traces.size()); + List spans = traces.get(0); + Assertions.assertEquals(1, spans.size()); + SpanData spanData = spans.get(0); + Assertions.assertEquals( + REQUEST_HEADER_VALUE, + spanData + .getAttributes() + .get(HypertraceSemanticAttributes.httpRequestHeader(REQUEST_HEADER))); + Assertions.assertEquals( + TestServlets.RESPONSE_HEADER_VALUE, + spanData + .getAttributes() + .get(HypertraceSemanticAttributes.httpResponseHeader(TestServlets.RESPONSE_HEADER))); + Assertions.assertEquals( + "key1=value1&key2=value2", + spanData.getAttributes().get(HypertraceSemanticAttributes.HTTP_REQUEST_BODY)); + Assertions.assertEquals( + TestServlets.RESPONSE_BODY, + spanData.getAttributes().get(HypertraceSemanticAttributes.HTTP_RESPONSE_BODY)); + } + + @Test + public void getHello() throws Exception { + Request request = + new Request.Builder() + .url(String.format("http://localhost:%d/hello", serverPort)) + .get() + .header(REQUEST_HEADER, REQUEST_HEADER_VALUE) + .build(); + try (Response response = httpClient.newCall(request).execute()) { + Assertions.assertEquals(204, response.code()); + } + + TEST_WRITER.waitForTraces(1); + List> traces = TEST_WRITER.getTraces(); + Assertions.assertEquals(1, traces.size()); + List spans = traces.get(0); + Assertions.assertEquals(1, spans.size()); + SpanData spanData = spans.get(0); + Assertions.assertEquals( + REQUEST_HEADER_VALUE, + spanData + .getAttributes() + .get(HypertraceSemanticAttributes.httpRequestHeader(REQUEST_HEADER))); + Assertions.assertEquals( + TestServlets.RESPONSE_HEADER_VALUE, + spanData + .getAttributes() + .get(HypertraceSemanticAttributes.httpResponseHeader(TestServlets.RESPONSE_HEADER))); + Assertions.assertNull( + spanData.getAttributes().get(HypertraceSemanticAttributes.HTTP_REQUEST_BODY)); + Assertions.assertNull( + spanData.getAttributes().get(HypertraceSemanticAttributes.HTTP_RESPONSE_BODY)); + } + + @Test + public void block() throws Exception { + Request request = + new Request.Builder() + .url(String.format("http://localhost:%d/hello", serverPort)) + .get() + .header("mockblock", "true") + .build(); + try (Response response = httpClient.newCall(request).execute()) { + Assertions.assertEquals(403, response.code()); + } + + TEST_WRITER.waitForTraces(1); + List> traces = TEST_WRITER.getTraces(); + Assertions.assertEquals(1, traces.size()); + List spans = traces.get(0); + Assertions.assertEquals(1, spans.size()); + SpanData spanData = spans.get(0); + Assertions.assertNull( + spanData + .getAttributes() + .get(HypertraceSemanticAttributes.httpResponseHeader(TestServlets.RESPONSE_HEADER))); + Assertions.assertNull( + spanData.getAttributes().get(HypertraceSemanticAttributes.HTTP_REQUEST_BODY)); + Assertions.assertNull( + spanData.getAttributes().get(HypertraceSemanticAttributes.HTTP_RESPONSE_BODY)); + } + + @Test + public void blockBody() throws Exception { + FormBody formBody = new FormBody.Builder().add("block", "true").build(); + Request request = + new Request.Builder() + .url(String.format("http://localhost:%d/echo_stream_single_byte", serverPort)) + .post(formBody) + .header(REQUEST_HEADER, REQUEST_HEADER_VALUE) + .build(); + try (Response response = httpClient.newCall(request).execute()) { + Assertions.assertEquals(403, response.code()); + } + + TEST_WRITER.waitForTraces(1); + List> traces = TEST_WRITER.getTraces(); + Assertions.assertEquals(1, traces.size()); + List spans = traces.get(0); + Assertions.assertEquals(1, spans.size()); + SpanData spanData = spans.get(0); + Assertions.assertNull( + spanData + .getAttributes() + .get(HypertraceSemanticAttributes.httpResponseHeader(TestServlets.RESPONSE_HEADER))); + Assertions.assertEquals( + "block=true", spanData.getAttributes().get(HypertraceSemanticAttributes.HTTP_REQUEST_BODY)); + Assertions.assertNull( + spanData.getAttributes().get(HypertraceSemanticAttributes.HTTP_RESPONSE_BODY)); + } + + @Test + public void blockBodyWrappedException() throws Exception { + FormBody formBody = new FormBody.Builder().add("block", "true").build(); + Request request = + new Request.Builder() + .url(String.format("http://localhost:%d/echo_stream_arr", serverPort)) + .post(formBody) + .header(REQUEST_HEADER, REQUEST_HEADER_VALUE) + .build(); + try (Response response = httpClient.newCall(request).execute()) { + Assertions.assertEquals(403, response.code()); + } + + TEST_WRITER.waitForTraces(1); + List> traces = TEST_WRITER.getTraces(); + Assertions.assertEquals(1, traces.size()); + List spans = traces.get(0); + Assertions.assertEquals(1, spans.size()); + SpanData spanData = spans.get(0); + Assertions.assertNull( + spanData + .getAttributes() + .get(HypertraceSemanticAttributes.httpResponseHeader(TestServlets.RESPONSE_HEADER))); + Assertions.assertEquals( + "block=true", spanData.getAttributes().get(HypertraceSemanticAttributes.HTTP_REQUEST_BODY)); + Assertions.assertNull( + spanData.getAttributes().get(HypertraceSemanticAttributes.HTTP_RESPONSE_BODY)); + } + + public void postJson(String url) throws Exception { + Request request = + new Request.Builder() + .url(url) + .post(RequestBody.create(REQUEST_BODY, MediaType.get("application/json"))) + .header(REQUEST_HEADER, REQUEST_HEADER_VALUE) + .build(); + try (Response response = httpClient.newCall(request).execute()) { + Assertions.assertEquals(200, response.code()); + Assertions.assertEquals(TestServlets.RESPONSE_BODY, response.body().string()); + } + + TEST_WRITER.waitForTraces(1); + List> traces = TEST_WRITER.getTraces(); + Assertions.assertEquals(1, traces.size()); + List spans = traces.get(0); + Assertions.assertEquals(1, spans.size()); + SpanData spanData = spans.get(0); + Assertions.assertEquals( + REQUEST_HEADER_VALUE, + spanData + .getAttributes() + .get(HypertraceSemanticAttributes.httpRequestHeader(REQUEST_HEADER))); + Assertions.assertEquals( + TestServlets.RESPONSE_HEADER_VALUE, + spanData + .getAttributes() + .get(HypertraceSemanticAttributes.httpResponseHeader(TestServlets.RESPONSE_HEADER))); + Assertions.assertEquals( + REQUEST_BODY, spanData.getAttributes().get(HypertraceSemanticAttributes.HTTP_REQUEST_BODY)); + Assertions.assertEquals( + TestServlets.RESPONSE_BODY, + spanData.getAttributes().get(HypertraceSemanticAttributes.HTTP_RESPONSE_BODY)); + } +} diff --git a/instrumentation/servlet/servlet-2.2/src/test/java/io/opentelemetry/javaagent/instrumentation/hypertrace/servlet/v2_2/nowrapping/TestServlets.java b/instrumentation/servlet/servlet-2.2/src/test/java/io/opentelemetry/javaagent/instrumentation/hypertrace/servlet/v2_2/nowrapping/TestServlets.java new file mode 100644 index 000000000..74fd47ec0 --- /dev/null +++ b/instrumentation/servlet/servlet-2.2/src/test/java/io/opentelemetry/javaagent/instrumentation/hypertrace/servlet/v2_2/nowrapping/TestServlets.java @@ -0,0 +1,223 @@ +/* + * Copyright The Hypertrace Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.opentelemetry.javaagent.instrumentation.hypertrace.servlet.v2_2.nowrapping; + +import javax.servlet.ServletException; +import javax.servlet.http.HttpServlet; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; +import java.util.stream.Stream; + +public class TestServlets { + + public static final String RESPONSE_BODY = "{\"key\": \"val\"}"; + + public static final String RESPONSE_HEADER = "responseheader"; + public static final String RESPONSE_HEADER_VALUE = "responsevalue"; + + public static class GetHello extends HttpServlet { + @Override + protected void service(HttpServletRequest req, HttpServletResponse resp) throws IOException { + while (req.getInputStream().read() != -1) {} + resp.setStatus(204); + resp.setHeader(RESPONSE_HEADER, RESPONSE_HEADER_VALUE); + resp.getWriter().write("hello"); + } + } + + public static class EchoStream_single_byte extends HttpServlet { + @Override + protected void service(HttpServletRequest req, HttpServletResponse resp) throws IOException { + while (req.getInputStream().read() != -1) {} + resp.setStatus(200); + resp.setContentType("application/json"); + resp.setHeader(RESPONSE_HEADER, RESPONSE_HEADER_VALUE); + + byte[] response_bodyBytes = RESPONSE_BODY.getBytes(); + for (int i = 0; i < RESPONSE_BODY.length(); i++) { + resp.getOutputStream().write(response_bodyBytes[i]); + } + } + } + + public static class EchoStream_arr extends HttpServlet { + @Override + protected void service(HttpServletRequest req, HttpServletResponse resp) throws IOException { + while (req.getInputStream().read(new byte[2]) != -1) {} + resp.setStatus(200); + resp.setContentType("application/json"); + resp.setHeader(RESPONSE_HEADER, RESPONSE_HEADER_VALUE); + resp.getOutputStream().write(RESPONSE_BODY.getBytes()); + } + } + + public static class EchoStream_arr_offset extends HttpServlet { + @Override + protected void service(HttpServletRequest req, HttpServletResponse resp) throws IOException { + while (req.getInputStream().read(new byte[12], 3, 2) != -1) {} + resp.setStatus(200); + resp.setContentType("application/json"); + resp.setHeader(RESPONSE_HEADER, RESPONSE_HEADER_VALUE); + + byte[] responseBytes = RESPONSE_BODY.getBytes(); + resp.getOutputStream().write(responseBytes, 0, 2); + resp.getOutputStream().write(responseBytes, 2, 1); + resp.getOutputStream().write(responseBytes, 3, responseBytes.length - 3); + } + } + + public static class EchoStream_readLine_print extends HttpServlet { + @Override + protected void service(HttpServletRequest req, HttpServletResponse resp) throws IOException { + while (req.getInputStream().readLine(new byte[14], 3, 3) != -1) {} + resp.setStatus(200); + resp.setContentType("application/json"); + resp.setHeader(RESPONSE_HEADER, RESPONSE_HEADER_VALUE); + resp.getOutputStream().print(RESPONSE_BODY); + } + } + + public static class EchoWriter_single_char extends HttpServlet { + @Override + protected void service(HttpServletRequest req, HttpServletResponse resp) throws IOException { + while (req.getReader().read() != -1) {} + + resp.setStatus(200); + resp.setContentType("application/json"); + resp.setHeader(RESPONSE_HEADER, RESPONSE_HEADER_VALUE); + + for (int i = 0; i < RESPONSE_BODY.length(); i++) + resp.getWriter().write(RESPONSE_BODY.charAt(i)); + } + } + + public static class EchoWriter_arr extends HttpServlet { + @Override + protected void service(HttpServletRequest req, HttpServletResponse resp) throws IOException { + while (req.getReader().read(new char[2]) != -1) {} + + resp.setStatus(200); + resp.setContentType("application/json"); + resp.setHeader(RESPONSE_HEADER, RESPONSE_HEADER_VALUE); + resp.getWriter().write(RESPONSE_BODY.toCharArray()); + } + } + + public static class EchoWriter_arr_offset extends HttpServlet { + @Override + protected void service(HttpServletRequest req, HttpServletResponse resp) throws IOException { + while (req.getReader().read(new char[12], 3, 2) != -1) {} + + resp.setStatus(200); + resp.setContentType("application/json"); + resp.setHeader(RESPONSE_HEADER, RESPONSE_HEADER_VALUE); + + char[] chars = RESPONSE_BODY.toCharArray(); + resp.getWriter().write(chars, 0, 2); + resp.getWriter().write(chars, 2, 2); + resp.getWriter().write(chars, 4, chars.length - 4); + } + } + + public static class EchoWriter_readLine_write extends HttpServlet { + @Override + protected void service(HttpServletRequest req, HttpServletResponse resp) throws IOException { + while (req.getReader().readLine() != null) {} + + resp.setStatus(200); + resp.setContentType("application/json"); + resp.setHeader(RESPONSE_HEADER, RESPONSE_HEADER_VALUE); + + resp.getWriter().write(RESPONSE_BODY); + } + } + + public static class EchoWriter_readLines extends HttpServlet { + @Override + protected void service(HttpServletRequest req, HttpServletResponse resp) throws IOException { + Stream lines = req.getReader().lines(); + lines.forEach(s -> {}); + + resp.setStatus(200); + resp.setContentType("application/json"); + resp.setHeader(RESPONSE_HEADER, RESPONSE_HEADER_VALUE); + + resp.getWriter().write(RESPONSE_BODY); + } + } + + public static class EchoWriter_readLine_print_str extends HttpServlet { + @Override + protected void service(HttpServletRequest req, HttpServletResponse resp) throws IOException { + while (req.getReader().readLine() != null) {} + + resp.setStatus(200); + resp.setContentType("application/json"); + resp.setHeader(RESPONSE_HEADER, RESPONSE_HEADER_VALUE); + + resp.getWriter().print(RESPONSE_BODY); + } + } + + public static class EchoWriter_readLine_print_arr extends HttpServlet { + @Override + protected void service(HttpServletRequest req, HttpServletResponse resp) throws IOException { + while (req.getReader().readLine() != null) {} + + resp.setStatus(200); + resp.setContentType("application/json"); + resp.setHeader(RESPONSE_HEADER, RESPONSE_HEADER_VALUE); + + resp.getWriter().print(RESPONSE_BODY.toCharArray()); + } + } + + public static class Forward_to_post extends HttpServlet { + @Override + protected void service(HttpServletRequest req, HttpServletResponse resp) + throws IOException, ServletException { + req.getRequestDispatcher("/echo_stream_single_byte").forward(req, resp); + } + } + + public static class EchoStream_read_large_array extends HttpServlet { + @Override + protected void service(HttpServletRequest req, HttpServletResponse resp) throws IOException { + req.getInputStream().read(new byte[1000], 0, 1000); + + resp.setStatus(200); + resp.setContentType("application/json"); + resp.setHeader(RESPONSE_HEADER, RESPONSE_HEADER_VALUE); + + resp.getWriter().print(RESPONSE_BODY.toCharArray()); + } + } + + public static class EchoReader_read_large_array extends HttpServlet { + @Override + protected void service(HttpServletRequest req, HttpServletResponse resp) throws IOException { + req.getReader().read(new char[1000], 0, 1000); + + resp.setStatus(200); + resp.setContentType("application/json"); + resp.setHeader(RESPONSE_HEADER, RESPONSE_HEADER_VALUE); + + resp.getWriter().print(RESPONSE_BODY.toCharArray()); + } + } +} diff --git a/instrumentation/servlet/servlet-2.2/src/test/java/io/opentelemetry/javaagent/instrumentation/hypertrace/servlet/v2_2/nowrapping/request/ServletInputStreamContextAccessInstrumentationModule.java b/instrumentation/servlet/servlet-2.2/src/test/java/io/opentelemetry/javaagent/instrumentation/hypertrace/servlet/v2_2/nowrapping/request/ServletInputStreamContextAccessInstrumentationModule.java new file mode 100644 index 000000000..82f857ab6 --- /dev/null +++ b/instrumentation/servlet/servlet-2.2/src/test/java/io/opentelemetry/javaagent/instrumentation/hypertrace/servlet/v2_2/nowrapping/request/ServletInputStreamContextAccessInstrumentationModule.java @@ -0,0 +1,93 @@ +/* + * Copyright The Hypertrace Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.opentelemetry.javaagent.instrumentation.hypertrace.servlet.v2_2.nowrapping.request; + +import static net.bytebuddy.matcher.ElementMatchers.isPublic; +import static net.bytebuddy.matcher.ElementMatchers.named; +import static net.bytebuddy.matcher.ElementMatchers.takesArguments; + +import io.opentelemetry.instrumentation.api.field.VirtualField; +import io.opentelemetry.javaagent.extension.instrumentation.InstrumentationModule; +import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation; +import io.opentelemetry.javaagent.extension.instrumentation.TypeTransformer; +import io.opentelemetry.javaagent.tooling.muzzle.InstrumentationModuleMuzzle; +import io.opentelemetry.javaagent.tooling.muzzle.VirtualFieldMappingsBuilder; +import io.opentelemetry.javaagent.tooling.muzzle.references.ClassRef; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import javax.servlet.ServletInputStream; +import net.bytebuddy.asm.Advice; +import net.bytebuddy.description.type.TypeDescription; +import net.bytebuddy.matcher.ElementMatcher; +import org.hypertrace.agent.core.instrumentation.buffer.ByteBufferSpanPair; + +// SPI explicitly added in META-INF/services/... +public class ServletInputStreamContextAccessInstrumentationModule extends InstrumentationModule + implements InstrumentationModuleMuzzle { + + public ServletInputStreamContextAccessInstrumentationModule() { + super("test-servlet-input-stream"); + } + + @Override + public List typeInstrumentations() { + return Collections.singletonList(new InputStreamTriggerInstrumentation()); + } + + @Override + public Map getMuzzleReferences() { + return Collections.emptyMap(); + } + + @Override + public void registerMuzzleVirtualFields(VirtualFieldMappingsBuilder builder) { + builder.register("javax.servlet.ServletInputStream", ByteBufferSpanPair.class.getName()); + } + + @Override + public List getMuzzleHelperClassNames() { + return Collections.emptyList(); + } + + static final class InputStreamTriggerInstrumentation implements TypeInstrumentation { + + @Override + public ElementMatcher typeMatcher() { + return named("org.ServletStreamContextAccess"); + } + + @Override + public void transform(TypeTransformer transformer) { + transformer.applyAdviceToMethod( + named("addToInputStreamContext").and(takesArguments(2)).and(isPublic()), + ServletInputStreamContextAccessInstrumentationModule.class.getName() + "$TestAdvice"); + } + } + + static class TestAdvice { + + @Advice.OnMethodEnter(suppress = Throwable.class) + public static void enter( + @Advice.Argument(0) ServletInputStream servletInputStream, + @Advice.Argument(1) ByteBufferSpanPair metadata) { + VirtualField contextStore = + VirtualField.find(ServletInputStream.class, ByteBufferSpanPair.class); + contextStore.set(servletInputStream, metadata); + } + } +} diff --git a/instrumentation/servlet/servlet-2.2/src/test/java/io/opentelemetry/javaagent/instrumentation/hypertrace/servlet/v2_2/nowrapping/request/ServletInputStreamInstrumentationTest.java b/instrumentation/servlet/servlet-2.2/src/test/java/io/opentelemetry/javaagent/instrumentation/hypertrace/servlet/v2_2/nowrapping/request/ServletInputStreamInstrumentationTest.java new file mode 100644 index 000000000..a487500f2 --- /dev/null +++ b/instrumentation/servlet/servlet-2.2/src/test/java/io/opentelemetry/javaagent/instrumentation/hypertrace/servlet/v2_2/nowrapping/request/ServletInputStreamInstrumentationTest.java @@ -0,0 +1,96 @@ +/* + * Copyright The Hypertrace Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.opentelemetry.javaagent.instrumentation.hypertrace.servlet.v2_2.nowrapping.request; + +import io.opentelemetry.api.trace.Span; +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.util.Collections; +import java.util.Map; +import javax.servlet.ServletInputStream; +import org.ServletStreamContextAccess; +import org.TestServletInputStream; +import org.hypertrace.agent.core.TriFunction; +import org.hypertrace.agent.core.instrumentation.buffer.BoundedBuffersFactory; +import org.hypertrace.agent.core.instrumentation.buffer.BoundedByteArrayOutputStream; +import org.hypertrace.agent.core.instrumentation.buffer.ByteBufferSpanPair; +import org.hypertrace.agent.testing.AbstractInstrumenterTest; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +public class ServletInputStreamInstrumentationTest extends AbstractInstrumenterTest { + + private static final String TEST_SPAN_NAME = "foo"; + private static final String BODY = "boobar"; + + @Test + public void read() throws IOException { + Span span = TEST_TRACER.spanBuilder(TEST_SPAN_NAME).startSpan(); + + ServletInputStream servletInputStream = + new TestServletInputStream(new ByteArrayInputStream(BODY.getBytes())); + + BoundedByteArrayOutputStream buffer = + BoundedBuffersFactory.createStream(StandardCharsets.UTF_8); + ByteBufferSpanPair bufferSpanPair = + new ByteBufferSpanPair(span, buffer, NOOP_FILTER, Collections.emptyMap()); + ServletStreamContextAccess.addToInputStreamContext(servletInputStream, bufferSpanPair); + + while (servletInputStream.read() != -1) {} + Assertions.assertEquals(BODY, buffer.toStringWithSuppliedCharset()); + } + + @Test + public void read_callDepth_is_cleared() throws IOException { + Span span = TEST_TRACER.spanBuilder(TEST_SPAN_NAME).startSpan(); + + ServletInputStream servletInputStream = + new TestServletInputStream(new ByteArrayInputStream(BODY.getBytes())); + servletInputStream.read(); + + BoundedByteArrayOutputStream buffer = + BoundedBuffersFactory.createStream(StandardCharsets.UTF_8); + ByteBufferSpanPair bufferSpanPair = + new ByteBufferSpanPair(span, buffer, NOOP_FILTER, Collections.emptyMap()); + ServletStreamContextAccess.addToInputStreamContext(servletInputStream, bufferSpanPair); + + while (servletInputStream.read() != -1) {} + Assertions.assertEquals(BODY.substring(1), buffer.toStringWithSuppliedCharset()); + } + + @Test + public void read_call_depth_read_calls_read() throws IOException { + Span span = TEST_TRACER.spanBuilder(TEST_SPAN_NAME).startSpan(); + + ServletInputStream servletInputStream = + new TestServletInputStream(new ByteArrayInputStream(BODY.getBytes())); + servletInputStream.read(new byte[2]); + + BoundedByteArrayOutputStream buffer = + BoundedBuffersFactory.createStream(StandardCharsets.UTF_8); + ByteBufferSpanPair bufferSpanPair = + new ByteBufferSpanPair(span, buffer, NOOP_FILTER, Collections.emptyMap()); + ServletStreamContextAccess.addToInputStreamContext(servletInputStream, bufferSpanPair); + + servletInputStream.read(new byte[BODY.length()]); + Assertions.assertEquals(BODY.substring(2), buffer.toStringWithSuppliedCharset()); + } + + private static final TriFunction, Boolean> NOOP_FILTER = + (span, body, headers) -> false; +} diff --git a/instrumentation/servlet/servlet-2.2/src/test/java/io/opentelemetry/javaagent/instrumentation/hypertrace/servlet/v2_2/nowrapping/response/ServletOutputStreamContextAccessInstrumentationModule.java b/instrumentation/servlet/servlet-2.2/src/test/java/io/opentelemetry/javaagent/instrumentation/hypertrace/servlet/v2_2/nowrapping/response/ServletOutputStreamContextAccessInstrumentationModule.java new file mode 100644 index 000000000..5fbce83e3 --- /dev/null +++ b/instrumentation/servlet/servlet-2.2/src/test/java/io/opentelemetry/javaagent/instrumentation/hypertrace/servlet/v2_2/nowrapping/response/ServletOutputStreamContextAccessInstrumentationModule.java @@ -0,0 +1,92 @@ +/* + * Copyright The Hypertrace Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.opentelemetry.javaagent.instrumentation.hypertrace.servlet.v2_2.nowrapping.response; + +import static net.bytebuddy.matcher.ElementMatchers.isPublic; +import static net.bytebuddy.matcher.ElementMatchers.named; +import static net.bytebuddy.matcher.ElementMatchers.takesArguments; + +import io.opentelemetry.instrumentation.api.field.VirtualField; +import io.opentelemetry.javaagent.extension.instrumentation.InstrumentationModule; +import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation; +import io.opentelemetry.javaagent.extension.instrumentation.TypeTransformer; +import io.opentelemetry.javaagent.tooling.muzzle.InstrumentationModuleMuzzle; +import io.opentelemetry.javaagent.tooling.muzzle.VirtualFieldMappingsBuilder; +import io.opentelemetry.javaagent.tooling.muzzle.references.ClassRef; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import javax.servlet.ServletOutputStream; +import net.bytebuddy.asm.Advice; +import net.bytebuddy.description.type.TypeDescription; +import net.bytebuddy.matcher.ElementMatcher; +import org.hypertrace.agent.core.instrumentation.buffer.BoundedByteArrayOutputStream; + +public class ServletOutputStreamContextAccessInstrumentationModule extends InstrumentationModule + implements InstrumentationModuleMuzzle { + + public ServletOutputStreamContextAccessInstrumentationModule() { + super("test-servlet-output-stream"); + } + + @Override + public List typeInstrumentations() { + return Collections.singletonList(new OutputStreamTriggerInstrumentation()); + } + + @Override + public Map getMuzzleReferences() { + return Collections.emptyMap(); + } + + @Override + public void registerMuzzleVirtualFields(VirtualFieldMappingsBuilder builder) { + builder.register( + "javax.servlet.ServletOutputStream", BoundedByteArrayOutputStream.class.getName()); + } + + @Override + public List getMuzzleHelperClassNames() { + return Collections.emptyList(); + } + + static final class OutputStreamTriggerInstrumentation implements TypeInstrumentation { + + @Override + public ElementMatcher typeMatcher() { + return named("org.ServletStreamContextAccess"); + } + + @Override + public void transform(TypeTransformer transformer) { + transformer.applyAdviceToMethod( + named("addToOutputStreamContext").and(takesArguments(2)).and(isPublic()), + ServletOutputStreamContextAccessInstrumentationModule.class.getName() + "$TestAdvice"); + } + } + + static class TestAdvice { + @Advice.OnMethodEnter(suppress = Throwable.class) + public static void enter( + @Advice.Argument(0) ServletOutputStream servletOutputStream, + @Advice.Argument(1) BoundedByteArrayOutputStream buffer) { + VirtualField contextStore = + VirtualField.find(ServletOutputStream.class, BoundedByteArrayOutputStream.class); + contextStore.set(servletOutputStream, buffer); + } + } +} diff --git a/instrumentation/servlet/servlet-2.2/src/test/java/io/opentelemetry/javaagent/instrumentation/hypertrace/servlet/v2_2/nowrapping/response/ServletOutputStreamInstrumentationTest.java b/instrumentation/servlet/servlet-2.2/src/test/java/io/opentelemetry/javaagent/instrumentation/hypertrace/servlet/v2_2/nowrapping/response/ServletOutputStreamInstrumentationTest.java new file mode 100644 index 000000000..67baf0036 --- /dev/null +++ b/instrumentation/servlet/servlet-2.2/src/test/java/io/opentelemetry/javaagent/instrumentation/hypertrace/servlet/v2_2/nowrapping/response/ServletOutputStreamInstrumentationTest.java @@ -0,0 +1,93 @@ +/* + * Copyright The Hypertrace Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package io.opentelemetry.javaagent.instrumentation.hypertrace.servlet.v2_2.nowrapping.response; + +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import javax.servlet.ServletOutputStream; +import org.ServletStreamContextAccess; +import org.TestServletOutputStream; +import org.hypertrace.agent.core.instrumentation.buffer.BoundedBuffersFactory; +import org.hypertrace.agent.core.instrumentation.buffer.BoundedByteArrayOutputStream; +import org.hypertrace.agent.testing.AbstractInstrumenterTest; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +public class ServletOutputStreamInstrumentationTest extends AbstractInstrumenterTest { + + private static final String BODY = "boobar"; + + @Test + public void write_single_byte() throws IOException { + ServletOutputStream servletOutputStream = new TestServletOutputStream(); + BoundedByteArrayOutputStream buffer = + BoundedBuffersFactory.createStream(StandardCharsets.UTF_8); + ServletStreamContextAccess.addToOutputStreamContext(servletOutputStream, buffer); + + byte[] bytes = BODY.getBytes(); + for (int i = 0; i < BODY.length(); i++) { + servletOutputStream.write(bytes[i]); + } + Assertions.assertEquals(BODY, buffer.toStringWithSuppliedCharset()); + } + + @Test + public void write_arr() throws IOException { + ServletOutputStream servletOutputStream = new TestServletOutputStream(); + BoundedByteArrayOutputStream buffer = + BoundedBuffersFactory.createStream(StandardCharsets.UTF_8); + ServletStreamContextAccess.addToOutputStreamContext(servletOutputStream, buffer); + + servletOutputStream.write(BODY.getBytes()); + Assertions.assertEquals(BODY, buffer.toStringWithSuppliedCharset()); + } + + @Test + public void write_arr_offset() throws IOException { + ServletOutputStream servletOutputStream = new TestServletOutputStream(); + BoundedByteArrayOutputStream buffer = + BoundedBuffersFactory.createStream(StandardCharsets.UTF_8); + ServletStreamContextAccess.addToOutputStreamContext(servletOutputStream, buffer); + + byte[] bytes = BODY.getBytes(); + servletOutputStream.write(bytes, 0, 2); + servletOutputStream.write(bytes, 2, bytes.length - 2); + Assertions.assertEquals(BODY, buffer.toStringWithSuppliedCharset()); + } + + @Test + public void print_str() throws IOException { + ServletOutputStream servletOutputStream = new TestServletOutputStream(); + BoundedByteArrayOutputStream buffer = + BoundedBuffersFactory.createStream(StandardCharsets.UTF_8); + ServletStreamContextAccess.addToOutputStreamContext(servletOutputStream, buffer); + + servletOutputStream.print(BODY); + Assertions.assertEquals(BODY, buffer.toStringWithSuppliedCharset()); + } + + @Test + public void println_str() throws IOException { + ServletOutputStream servletOutputStream = new TestServletOutputStream(); + BoundedByteArrayOutputStream buffer = + BoundedBuffersFactory.createStream(StandardCharsets.UTF_8); + ServletStreamContextAccess.addToOutputStreamContext(servletOutputStream, buffer); + + servletOutputStream.println(BODY); + Assertions.assertEquals(BODY + "\r\n", buffer.toStringWithSuppliedCharset()); + } +} diff --git a/instrumentation/servlet/servlet-2.2/src/test/java/org/DelegatingBufferedReader.java b/instrumentation/servlet/servlet-2.2/src/test/java/org/DelegatingBufferedReader.java new file mode 100644 index 000000000..9756e8525 --- /dev/null +++ b/instrumentation/servlet/servlet-2.2/src/test/java/org/DelegatingBufferedReader.java @@ -0,0 +1,37 @@ +/* + * Copyright The Hypertrace Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.Reader; +import org.jetbrains.annotations.NotNull; + +public class DelegatingBufferedReader extends BufferedReader { + + private final Reader delegate; + + public DelegatingBufferedReader(Reader delegate) { + super(delegate); + this.delegate = delegate; + } + + @Override + public int read(@NotNull char[] cbuf) throws IOException { + return delegate.read(cbuf); + } +} diff --git a/instrumentation/servlet/servlet-2.2/src/test/java/org/DelegatingPrintWriter.java b/instrumentation/servlet/servlet-2.2/src/test/java/org/DelegatingPrintWriter.java new file mode 100644 index 000000000..544338420 --- /dev/null +++ b/instrumentation/servlet/servlet-2.2/src/test/java/org/DelegatingPrintWriter.java @@ -0,0 +1,41 @@ +/* + * Copyright The Hypertrace Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org; + +import java.io.IOException; +import java.io.PrintWriter; +import java.io.Writer; +import org.jetbrains.annotations.NotNull; + +public class DelegatingPrintWriter extends PrintWriter { + + private final Writer delegate; + + public DelegatingPrintWriter(Writer delegate) { + super(delegate); + this.delegate = delegate; + } + + @Override + public void write(@NotNull char[] buf) { + try { + this.delegate.write(buf); + } catch (IOException e) { + e.printStackTrace(); + } + } +} diff --git a/instrumentation/servlet/servlet-2.2/src/test/java/org/DelegatingServletInputStream.java b/instrumentation/servlet/servlet-2.2/src/test/java/org/DelegatingServletInputStream.java new file mode 100644 index 000000000..6e5955860 --- /dev/null +++ b/instrumentation/servlet/servlet-2.2/src/test/java/org/DelegatingServletInputStream.java @@ -0,0 +1,50 @@ +/* + * Copyright The Hypertrace Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org; + +import java.io.IOException; +import javax.servlet.ServletInputStream; +import org.jetbrains.annotations.NotNull; + +public class DelegatingServletInputStream extends ServletInputStream { + + private final ServletInputStream wrapped; + + public DelegatingServletInputStream(ServletInputStream wrapped) { + this.wrapped = wrapped; + } + + @Override + public int read() throws IOException { + return wrapped.read(); + } + + @Override + public int read(@NotNull byte[] b) throws IOException { + return wrapped.read(b); + } + + @Override + public int read(@NotNull byte[] b, int off, int len) throws IOException { + return wrapped.read(b, off, len); + } + + @Override + public int readLine(byte[] b, int off, int len) throws IOException { + return wrapped.readLine(b, off, len); + } +} diff --git a/instrumentation/servlet/servlet-2.2/src/test/java/org/DelegatingServletOutputStream.java b/instrumentation/servlet/servlet-2.2/src/test/java/org/DelegatingServletOutputStream.java new file mode 100644 index 000000000..21e3e5115 --- /dev/null +++ b/instrumentation/servlet/servlet-2.2/src/test/java/org/DelegatingServletOutputStream.java @@ -0,0 +1,55 @@ +/* + * Copyright The Hypertrace Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org; + +import java.io.IOException; +import javax.servlet.ServletOutputStream; +import org.jetbrains.annotations.NotNull; + +public class DelegatingServletOutputStream extends ServletOutputStream { + + private final ServletOutputStream delegate; + + public DelegatingServletOutputStream(ServletOutputStream delegate) { + this.delegate = delegate; + } + + @Override + public void write(int b) throws IOException { + this.delegate.write(b); + } + + @Override + public void write(@NotNull byte[] b) throws IOException { + this.delegate.write(b); + } + + @Override + public void write(@NotNull byte[] b, int off, int len) throws IOException { + this.delegate.write(b, off, len); + } + + @Override + public void flush() throws IOException { + this.delegate.flush(); + } + + @Override + public void close() throws IOException { + this.delegate.close(); + } +} diff --git a/instrumentation/servlet/servlet-2.2/src/test/java/org/ServletStreamContextAccess.java b/instrumentation/servlet/servlet-2.2/src/test/java/org/ServletStreamContextAccess.java new file mode 100644 index 000000000..58c532e43 --- /dev/null +++ b/instrumentation/servlet/servlet-2.2/src/test/java/org/ServletStreamContextAccess.java @@ -0,0 +1,31 @@ +/* + * Copyright The Hypertrace Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org; + +import javax.servlet.ServletInputStream; +import javax.servlet.ServletOutputStream; +import org.hypertrace.agent.core.instrumentation.buffer.BoundedByteArrayOutputStream; +import org.hypertrace.agent.core.instrumentation.buffer.ByteBufferSpanPair; + +public class ServletStreamContextAccess { + + public static void addToInputStreamContext( + ServletInputStream servletInputStream, ByteBufferSpanPair buffer) {} + + public static void addToOutputStreamContext( + ServletOutputStream servletOutputStream, BoundedByteArrayOutputStream buffer) {} +} diff --git a/instrumentation/servlet/servlet-2.2/src/test/java/org/TestServletInputStream.java b/instrumentation/servlet/servlet-2.2/src/test/java/org/TestServletInputStream.java new file mode 100644 index 000000000..650994b14 --- /dev/null +++ b/instrumentation/servlet/servlet-2.2/src/test/java/org/TestServletInputStream.java @@ -0,0 +1,35 @@ +/* + * Copyright The Hypertrace Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org; + +import java.io.IOException; +import java.io.InputStream; +import javax.servlet.ServletInputStream; + +public class TestServletInputStream extends ServletInputStream { + + private final InputStream wrapped; + + public TestServletInputStream(InputStream wrapped) { + this.wrapped = wrapped; + } + + @Override + public int read() throws IOException { + return wrapped.read(); + } +} diff --git a/instrumentation/servlet/servlet-2.2/src/test/java/org/TestServletOutputStream.java b/instrumentation/servlet/servlet-2.2/src/test/java/org/TestServletOutputStream.java new file mode 100644 index 000000000..813643bd0 --- /dev/null +++ b/instrumentation/servlet/servlet-2.2/src/test/java/org/TestServletOutputStream.java @@ -0,0 +1,28 @@ +/* + * Copyright The Hypertrace Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org; + +import java.io.IOException; +import javax.servlet.ServletOutputStream; + +public class TestServletOutputStream extends ServletOutputStream { + + @Override + public void write(int b) throws IOException { + // noop + } +} diff --git a/instrumentation/servlet/servlet-2.2/src/test/resources/META-INF/services/io.opentelemetry.javaagent.extension.instrumentation.InstrumentationModule b/instrumentation/servlet/servlet-2.2/src/test/resources/META-INF/services/io.opentelemetry.javaagent.extension.instrumentation.InstrumentationModule new file mode 100644 index 000000000..8a5d00f68 --- /dev/null +++ b/instrumentation/servlet/servlet-2.2/src/test/resources/META-INF/services/io.opentelemetry.javaagent.extension.instrumentation.InstrumentationModule @@ -0,0 +1,2 @@ +io.opentelemetry.javaagent.instrumentation.hypertrace.servlet.v2_2.nowrapping.request.ServletInputStreamContextAccessInstrumentationModule +io.opentelemetry.javaagent.instrumentation.hypertrace.servlet.v2_2.nowrapping.response.ServletOutputStreamContextAccessInstrumentationModule diff --git a/instrumentation/servlet/servlet-3.0/src/main/java/io/opentelemetry/javaagent/instrumentation/hypertrace/servlet/v3_0/nowrapping/Servlet30InstrumentationModule.java b/instrumentation/servlet/servlet-3.0/src/main/java/io/opentelemetry/javaagent/instrumentation/hypertrace/servlet/v3_0/nowrapping/Servlet30InstrumentationModule.java index 181973b1d..c3bfc3c94 100644 --- a/instrumentation/servlet/servlet-3.0/src/main/java/io/opentelemetry/javaagent/instrumentation/hypertrace/servlet/v3_0/nowrapping/Servlet30InstrumentationModule.java +++ b/instrumentation/servlet/servlet-3.0/src/main/java/io/opentelemetry/javaagent/instrumentation/hypertrace/servlet/v3_0/nowrapping/Servlet30InstrumentationModule.java @@ -16,6 +16,8 @@ package io.opentelemetry.javaagent.instrumentation.hypertrace.servlet.v3_0.nowrapping; +import static io.opentelemetry.javaagent.extension.matcher.AgentElementMatchers.hasClassesNamed; + import com.google.auto.service.AutoService; import io.opentelemetry.javaagent.extension.instrumentation.InstrumentationModule; import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation; @@ -26,6 +28,7 @@ import io.opentelemetry.javaagent.instrumentation.hypertrace.servlet.v3_0.nowrapping.response.ServletResponseInstrumentation; import java.util.Arrays; import java.util.List; +import net.bytebuddy.matcher.ElementMatcher; @SuppressWarnings("unused") @AutoService(InstrumentationModule.class) @@ -40,6 +43,11 @@ public int order() { return 1; } + @Override + public ElementMatcher.Junction classLoaderMatcher() { + return hasClassesNamed("javax.servlet.HttpConstraintElement"); + } + @Override public List typeInstrumentations() { return Arrays.asList( diff --git a/instrumentation/servlet/servlet-3.0/src/main/java/io/opentelemetry/javaagent/instrumentation/hypertrace/servlet/v3_0/nowrapping/request/Utils.java b/instrumentation/servlet/servlet-3.0/src/main/java/io/opentelemetry/javaagent/instrumentation/hypertrace/servlet/v3_0/nowrapping/request/Utils.java index 0b23786b1..1d8312fc7 100644 --- a/instrumentation/servlet/servlet-3.0/src/main/java/io/opentelemetry/javaagent/instrumentation/hypertrace/servlet/v3_0/nowrapping/request/Utils.java +++ b/instrumentation/servlet/servlet-3.0/src/main/java/io/opentelemetry/javaagent/instrumentation/hypertrace/servlet/v3_0/nowrapping/request/Utils.java @@ -66,10 +66,10 @@ public static CharBufferSpanPair createRequestCharBufferSpanPair( /** * Create a StringMapSpanPair with provided values. * - * @param stringMap the stringMap instance + * @param stringMap a map of key-value pairs of x-www-form-urlencoded content-type body * @param span the span instance - * @param headers the headers instance - * @return a StringMapSpanPair created with values of the above instances + * @param headers map of headers + * @return a StringMapSpanPair instance created with values of the above instances */ public static StringMapSpanPair createStringMapSpanPair( Map stringMap, Span span, Map headers) { From 4aaada510b8934d145e5d69faecb37cf756b235e Mon Sep 17 00:00:00 2001 From: Kaushal Agrawal Date: Fri, 24 Mar 2023 14:00:08 +0530 Subject: [PATCH 7/7] fix jetty version --- .../servlet/servlet-2.2/build.gradle.kts | 4 +- .../Servlet2InstrumentationTest.java | 32 +++--- .../servlet/v2_2/nowrapping/TestServlets.java | 4 +- .../src/test/java/org/WrappingFilter.java | 99 +++++++++++++++++++ 4 files changed, 121 insertions(+), 18 deletions(-) create mode 100644 instrumentation/servlet/servlet-2.2/src/test/java/org/WrappingFilter.java diff --git a/instrumentation/servlet/servlet-2.2/build.gradle.kts b/instrumentation/servlet/servlet-2.2/build.gradle.kts index deb4f9a28..6e776bff3 100644 --- a/instrumentation/servlet/servlet-2.2/build.gradle.kts +++ b/instrumentation/servlet/servlet-2.2/build.gradle.kts @@ -43,7 +43,7 @@ dependencies { testImplementation(testFixtures(project(":testing-common")) as ProjectDependency) { exclude(group = "org.eclipse.jetty", module = "jetty-server") } - testImplementation("org.eclipse.jetty:jetty-server:8.1.22.v20160922") - testImplementation("org.eclipse.jetty:jetty-servlet:8.1.22.v20160922") + testImplementation("org.eclipse.jetty:jetty-server:7.0.0.v20091005") + testImplementation("org.eclipse.jetty:jetty-servlet:7.0.0.v20091005") testImplementation("io.opentelemetry.instrumentation:opentelemetry-instrumentation-api-semconv:${versions["opentelemetry_semconv"]}") } diff --git a/instrumentation/servlet/servlet-2.2/src/test/java/io/opentelemetry/javaagent/instrumentation/hypertrace/servlet/v2_2/nowrapping/Servlet2InstrumentationTest.java b/instrumentation/servlet/servlet-2.2/src/test/java/io/opentelemetry/javaagent/instrumentation/hypertrace/servlet/v2_2/nowrapping/Servlet2InstrumentationTest.java index c8572d041..a64d358c7 100644 --- a/instrumentation/servlet/servlet-2.2/src/test/java/io/opentelemetry/javaagent/instrumentation/hypertrace/servlet/v2_2/nowrapping/Servlet2InstrumentationTest.java +++ b/instrumentation/servlet/servlet-2.2/src/test/java/io/opentelemetry/javaagent/instrumentation/hypertrace/servlet/v2_2/nowrapping/Servlet2InstrumentationTest.java @@ -16,9 +16,10 @@ package io.opentelemetry.javaagent.instrumentation.hypertrace.servlet.v2_2.nowrapping; -import io.opentelemetry.javaagent.instrumentation.hypertrace.servlet.v2_2.nowrapping.TestServlets.*; import io.opentelemetry.sdk.trace.data.SpanData; import okhttp3.*; +import org.WrappingFilter; +import org.eclipse.jetty.server.DispatcherType; import org.eclipse.jetty.server.Server; import org.eclipse.jetty.servlet.ServletContextHandler; import org.hypertrace.agent.core.instrumentation.HypertraceEvaluationException; @@ -31,7 +32,6 @@ import javax.servlet.*; import java.io.IOException; -import java.util.EnumSet; import java.util.List; public class Servlet2InstrumentationTest extends AbstractInstrumenterTest { @@ -45,11 +45,11 @@ public class Servlet2InstrumentationTest extends AbstractInstrumenterTest { /* * Filter that mimics the spring framework. It will catch and wrap our blocking exception */ - public static class WrapExceptionFilter implements Servlet { + public static class WrapExceptionFilter implements Filter { @Override - public void service(ServletRequest request, ServletResponse response) + public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws ServletException, IOException { - System.out.print("hello from service"); + System.out.print("hello from filter"); try { chain.doFilter(request, response); } catch (Throwable t) { @@ -71,14 +71,16 @@ public void destroy() {} public static void startServer() throws Exception { ServletContextHandler handler = new ServletContextHandler(); - handler.addServlet(GetHello.class, "/hello"); - handler.addServlet(EchoStream_single_byte.class, "/echo_stream_single_byte"); - handler.addServlet(EchoStream_arr.class, "/echo_stream_arr"); + handler.addFilter(WrappingFilter.class, "/*", DispatcherType.REQUEST.ordinal()); + + handler.addServlet(TestServlets.GetHello.class, "/hello"); + handler.addServlet(TestServlets.EchoStream_single_byte.class, "/echo_stream_single_byte"); + handler.addServlet(TestServlets.EchoStream_arr.class, "/echo_stream_arr"); handler.addFilter( - WrapExceptionFilter.class, "/echo_stream_arr", EnumSet.of(DispatcherType.REQUEST)); - handler.addServlet(EchoStream_arr_offset.class, "/echo_stream_arr_offset"); - handler.addServlet(EchoStream_readLine_print.class, "/echo_stream_readLine_print"); - handler.addServlet(EchoWriter_single_char.class, "/echo_writer_single_char"); + WrapExceptionFilter.class, "/echo_stream_arr", DispatcherType.REQUEST.ordinal()); + handler.addServlet(TestServlets.EchoStream_arr_offset.class, "/echo_stream_arr_offset"); + handler.addServlet(TestServlets.EchoStream_readLine_print.class, "/echo_stream_readLine_print"); + handler.addServlet(TestServlets.EchoWriter_single_char.class, "/echo_writer_single_char"); handler.addServlet(TestServlets.EchoWriter_arr.class, "/echo_writer_arr"); handler.addServlet(TestServlets.EchoWriter_arr_offset.class, "/echo_writer_arr_offset"); handler.addServlet(TestServlets.EchoWriter_readLine_write.class, "/echo_writer_readLine_write"); @@ -88,8 +90,10 @@ public static void startServer() throws Exception { handler.addServlet( TestServlets.EchoWriter_readLine_print_arr.class, "/echo_writer_readLine_print_arr"); handler.addServlet(TestServlets.Forward_to_post.class, "/forward_to_echo"); - handler.addServlet(EchoStream_read_large_array.class, "/echo_stream_read_large_array"); - handler.addServlet(EchoReader_read_large_array.class, "/echo_reader_read_large_array"); + handler.addServlet( + TestServlets.EchoStream_read_large_array.class, "/echo_stream_read_large_array"); + handler.addServlet( + TestServlets.EchoReader_read_large_array.class, "/echo_reader_read_large_array"); server.setHandler(handler); server.start(); serverPort = server.getConnectors()[0].getLocalPort(); diff --git a/instrumentation/servlet/servlet-2.2/src/test/java/io/opentelemetry/javaagent/instrumentation/hypertrace/servlet/v2_2/nowrapping/TestServlets.java b/instrumentation/servlet/servlet-2.2/src/test/java/io/opentelemetry/javaagent/instrumentation/hypertrace/servlet/v2_2/nowrapping/TestServlets.java index 74fd47ec0..d3e9ff9b7 100644 --- a/instrumentation/servlet/servlet-2.2/src/test/java/io/opentelemetry/javaagent/instrumentation/hypertrace/servlet/v2_2/nowrapping/TestServlets.java +++ b/instrumentation/servlet/servlet-2.2/src/test/java/io/opentelemetry/javaagent/instrumentation/hypertrace/servlet/v2_2/nowrapping/TestServlets.java @@ -16,12 +16,12 @@ package io.opentelemetry.javaagent.instrumentation.hypertrace.servlet.v2_2.nowrapping; +import java.io.IOException; +import java.util.stream.Stream; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; -import java.io.IOException; -import java.util.stream.Stream; public class TestServlets { diff --git a/instrumentation/servlet/servlet-2.2/src/test/java/org/WrappingFilter.java b/instrumentation/servlet/servlet-2.2/src/test/java/org/WrappingFilter.java new file mode 100644 index 000000000..73293d8f3 --- /dev/null +++ b/instrumentation/servlet/servlet-2.2/src/test/java/org/WrappingFilter.java @@ -0,0 +1,99 @@ +/* + * Copyright The Hypertrace Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org; + +import javax.servlet.*; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletRequestWrapper; +import javax.servlet.http.HttpServletResponse; +import javax.servlet.http.HttpServletResponseWrapper; +import java.io.BufferedReader; +import java.io.IOException; +import java.io.PrintWriter; + +public class WrappingFilter implements Filter { + + @Override + public void init(FilterConfig filterConfig) {} + + @Override + public void destroy() {} + + @Override + public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) + throws IOException, ServletException { + + HttpServletRequest httpServletRequest = (HttpServletRequest) request; + HttpServletResponse httpServletResponse = (HttpServletResponse) response; + + ReqWrapper reqWrapper = new ReqWrapper(httpServletRequest); + RespWrapper respWrapper = new RespWrapper(httpServletResponse); + chain.doFilter(reqWrapper, respWrapper); + } + + static class ReqWrapper extends HttpServletRequestWrapper { + + private ServletInputStream servletInputStream; + private BufferedReader bufferedReader; + + public ReqWrapper(HttpServletRequest request) { + super(request); + } + + @Override + public ServletInputStream getInputStream() throws IOException { + if (servletInputStream == null) { + servletInputStream = new DelegatingServletInputStream(super.getInputStream()); + } + return servletInputStream; + } + + @Override + public BufferedReader getReader() throws IOException { + if (bufferedReader == null) { + bufferedReader = new DelegatingBufferedReader(super.getReader()); + } + return bufferedReader; + } + } + + static class RespWrapper extends HttpServletResponseWrapper { + + private ServletOutputStream servletOutputStream; + private PrintWriter printWriter; + + public RespWrapper(HttpServletResponse response) { + super(response); + } + + @Override + public ServletOutputStream getOutputStream() throws IOException { + if (servletOutputStream == null) { + servletOutputStream = new DelegatingServletOutputStream(super.getOutputStream()); + } + return servletOutputStream; + } + + @Override + public PrintWriter getWriter() throws IOException { + if (printWriter == null) { + printWriter = new DelegatingPrintWriter(super.getWriter()); + } + return printWriter; + } + } +}