diff --git a/.fossa.yml b/.fossa.yml index 12ec5017e1f7..e9d0d9657605 100644 --- a/.fossa.yml +++ b/.fossa.yml @@ -1024,6 +1024,12 @@ targets: - type: gradle path: ./ target: ':instrumentation:struts:struts-7.0:javaagent' + - type: gradle + path: ./ + target: ':instrumentation:thrift:thrift-0.9.1:javaagent' + - type: gradle + path: ./ + target: ':instrumentation:thrift:thrift-common:javaagent' - type: gradle path: ./ target: ':instrumentation:tomcat:tomcat-10.0:javaagent' diff --git a/instrumentation/thrift/thrift-0.9.1/javaagent/build.gradle.kts b/instrumentation/thrift/thrift-0.9.1/javaagent/build.gradle.kts new file mode 100644 index 000000000000..e65e513463ee --- /dev/null +++ b/instrumentation/thrift/thrift-0.9.1/javaagent/build.gradle.kts @@ -0,0 +1,60 @@ +plugins { + id("otel.javaagent-instrumentation") +} + +muzzle { + pass { + group.set("org.apache.thrift") + module.set("libthrift") + versions.set("[0.9.1,)") + assertInverse.set(true) + } +} + +dependencies { + library("org.apache.thrift:libthrift:0.9.1") + testLibrary("org.apache.thrift:libthrift:0.12.0") + implementation(project(":instrumentation:thrift:thrift-common:javaagent")) +} + +val generatedThriftDir = layout.buildDirectory.dir("generated/thrift/test") + +val generateThriftSources by tasks.registering(Exec::class) { + val thriftFilePath = + layout.projectDirectory.file("src/test/resources/ThriftService.thrift").asFile.absolutePath + val outputDirPath = generatedThriftDir.get().asFile.also { it.mkdirs() }.absolutePath + inputs.file(thriftFilePath) + outputs.dir(outputDirPath) + + standardOutput = System.out + executable = "docker" + args = listOf( + "run", + "--rm", + "--platform=linux/amd64", + "-v", "$thriftFilePath:/thrift/input/ThriftService.thrift:ro", + "-v", "$outputDirPath:/thrift/output", + "thrift:0.12.0", + "thrift", + "--gen", + "java", + "-out", + "/thrift/output", + "/thrift/input/ThriftService.thrift") +} + +sourceSets { + test { + java { + srcDir(generatedThriftDir) + } + } +} + +tasks.compileTestJava { + dependsOn(generateThriftSources) +} + +tasks.named("checkstyleTest") { + exclude("**/thrift/**") +} diff --git a/instrumentation/thrift/thrift-0.9.1/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/thrift/v0_9_1/AbstractProtocolWrapper.java b/instrumentation/thrift/thrift-0.9.1/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/thrift/v0_9_1/AbstractProtocolWrapper.java new file mode 100644 index 000000000000..66de3af38961 --- /dev/null +++ b/instrumentation/thrift/thrift-0.9.1/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/thrift/v0_9_1/AbstractProtocolWrapper.java @@ -0,0 +1,41 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +// Includes work from: +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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.thrift.v0_9_1; + +import org.apache.thrift.protocol.TProtocol; +import org.apache.thrift.protocol.TProtocolDecorator; + +/** + * Note that the 8888th field of record is reserved for transporting trace header. Because Thrift + * doesn't support to transport metadata. + */ +public abstract class AbstractProtocolWrapper extends TProtocolDecorator { + public static final String OT_MAGIC_FIELD = "OT_MAGIC_FIELD"; + public static final short OT_MAGIC_FIELD_ID = 8888; + + public AbstractProtocolWrapper(TProtocol protocol) { + super(protocol); + } +} diff --git a/instrumentation/thrift/thrift-0.9.1/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/thrift/v0_9_1/AsyncMethodCallbackWrapper.java b/instrumentation/thrift/thrift-0.9.1/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/thrift/v0_9_1/AsyncMethodCallbackWrapper.java new file mode 100644 index 000000000000..cac2a854b3bd --- /dev/null +++ b/instrumentation/thrift/thrift-0.9.1/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/thrift/v0_9_1/AsyncMethodCallbackWrapper.java @@ -0,0 +1,67 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.instrumentation.thrift.v0_9_1; + +import static io.opentelemetry.javaagent.instrumentation.thrift.v0_9_1.ThriftSingletons.clientInstrumenter; +import static io.opentelemetry.javaagent.instrumentation.thrift.v0_9_1.ThriftSingletons.serverInstrumenter; + +import io.opentelemetry.context.Context; +import io.opentelemetry.instrumentation.thrift.common.RequestScopeContext; +import io.opentelemetry.instrumentation.thrift.common.ThriftRequest; +import org.apache.thrift.async.AsyncMethodCallback; + +public final class AsyncMethodCallbackWrapper implements AsyncMethodCallback { + private final AsyncMethodCallback delegate; + private RequestScopeContext requestScopeContext; + private final boolean isServer; + + public AsyncMethodCallbackWrapper(AsyncMethodCallback methodCallback, boolean isServer) { + this.delegate = methodCallback; + this.isServer = isServer; + } + + public void setRequestScopeContext(RequestScopeContext requestScopeContext) { + this.requestScopeContext = requestScopeContext; + } + + @Override + public void onComplete(T t) { + try { + if (this.requestScopeContext == null) { + return; + } + this.requestScopeContext.close(); + Context context = this.requestScopeContext.getContext(); + ThriftRequest request = this.requestScopeContext.getRequest(); + if (isServer) { + serverInstrumenter().end(context, request, 0, null); + } else { + clientInstrumenter().end(context, request, 0, null); + } + } finally { + this.delegate.onComplete(t); + } + } + + @Override + public void onError(Exception e) { + try { + if (this.requestScopeContext == null) { + return; + } + this.requestScopeContext.close(); + Context context = this.requestScopeContext.getContext(); + ThriftRequest request = this.requestScopeContext.getRequest(); + if (isServer) { + serverInstrumenter().end(context, request, 1, e); + } else { + clientInstrumenter().end(context, request, 1, e); + } + } finally { + this.delegate.onError(e); + } + } +} diff --git a/instrumentation/thrift/thrift-0.9.1/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/thrift/v0_9_1/ThriftSingletons.java b/instrumentation/thrift/thrift-0.9.1/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/thrift/v0_9_1/ThriftSingletons.java new file mode 100644 index 000000000000..c22dd1b56304 --- /dev/null +++ b/instrumentation/thrift/thrift-0.9.1/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/thrift/v0_9_1/ThriftSingletons.java @@ -0,0 +1,29 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.instrumentation.thrift.v0_9_1; + +import io.opentelemetry.instrumentation.api.instrumenter.Instrumenter; +import io.opentelemetry.instrumentation.thrift.common.ThriftInstrumenterFactory; +import io.opentelemetry.instrumentation.thrift.common.ThriftRequest; + +public final class ThriftSingletons { + private static final String INSTRUMENTATION_NAME = "io.opentelemetry.thrift-0.9.1"; + + private static final Instrumenter CLIENT_INSTRUMENTER = + ThriftInstrumenterFactory.clientInstrumenter(INSTRUMENTATION_NAME); + private static final Instrumenter SERVER_INSTRUMENTER = + ThriftInstrumenterFactory.serverInstrumenter(INSTRUMENTATION_NAME); + + public static Instrumenter clientInstrumenter() { + return CLIENT_INSTRUMENTER; + } + + public static Instrumenter serverInstrumenter() { + return SERVER_INSTRUMENTER; + } + + private ThriftSingletons() {} +} diff --git a/instrumentation/thrift/thrift-0.9.1/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/thrift/v0_9_1/client/ClientOutProtocolWrapper.java b/instrumentation/thrift/thrift-0.9.1/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/thrift/v0_9_1/client/ClientOutProtocolWrapper.java new file mode 100644 index 000000000000..1e192818ed10 --- /dev/null +++ b/instrumentation/thrift/thrift-0.9.1/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/thrift/v0_9_1/client/ClientOutProtocolWrapper.java @@ -0,0 +1,148 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.instrumentation.thrift.v0_9_1.client; + +import static io.opentelemetry.javaagent.instrumentation.thrift.v0_9_1.ThriftSingletons.clientInstrumenter; + +import io.opentelemetry.context.Context; +import io.opentelemetry.instrumentation.thrift.common.RequestScopeContext; +import io.opentelemetry.instrumentation.thrift.common.SocketAccessor; +import io.opentelemetry.instrumentation.thrift.common.ThriftRequest; +import io.opentelemetry.javaagent.instrumentation.thrift.v0_9_1.AbstractProtocolWrapper; +import java.net.Socket; +import java.util.HashMap; +import java.util.Map; +import java.util.Set; +import org.apache.thrift.TException; +import org.apache.thrift.protocol.TField; +import org.apache.thrift.protocol.TMap; +import org.apache.thrift.protocol.TMessage; +import org.apache.thrift.protocol.TMessageType; +import org.apache.thrift.protocol.TProtocol; +import org.apache.thrift.protocol.TType; +import org.apache.thrift.transport.TTransport; + +public final class ClientOutProtocolWrapper extends AbstractProtocolWrapper { + public static final String ONE_WAY_METHOD_NAME_PREFIX = "recv_"; + private volatile RequestScopeContext requestScopeContext; + public TTransport transport; + private boolean injected = true; + private String methodName; + private final Set voidMethodNames; + private String serviceName; + private byte type = -1; + private byte originType; + + public ClientOutProtocolWrapper( + TProtocol protocol, String serviceName, Set voidMethodNames) { + super(protocol); + this.serviceName = serviceName; + this.voidMethodNames = voidMethodNames; + } + + @Override + public void writeMessageBegin(TMessage message) throws TException { + this.injected = false; + this.methodName = message.name; + this.originType = message.type; + // Compatible with version 0.9.1 and 0.9.2 asynchronous logic + if (message.type == TMessageType.ONEWAY || this.type == -1) { + this.type = message.type; + } + if (!this.isOneway()) { + if (this.voidMethodNames != null + && this.voidMethodNames.contains(this.methodName) + && !this.voidMethodNames.contains(ONE_WAY_METHOD_NAME_PREFIX + this.methodName)) { + this.type = TMessageType.ONEWAY; + } + } + try { + if (this.requestScopeContext == null) { + Socket socket = SocketAccessor.getSocket(super.getTransport()); + if (socket == null) { + socket = SocketAccessor.getSocket(this.transport); + } + ThriftRequest request = + ThriftRequest.create(this.serviceName, this.methodName, socket, new HashMap<>()); + Context parentContext = Context.current(); + if (!clientInstrumenter().shouldStart(parentContext, request)) { + return; + } + Context context = clientInstrumenter().start(parentContext, request); + this.requestScopeContext = RequestScopeContext.create(request, null, context); + } + } finally { + if (this.isOneway() && message.type != TMessageType.ONEWAY) { + // In Thrift 0.9.1 and 0.9.2 versions, the type of the TMessage for oneway requests is still + // TMessageType.CALL. + // This causes issues with the server-side instrumentation logic. Here, we are simply + // correcting the actual request type. + // Since it is a oneway request, the client does not need to handle the response, + // and the server does not use this type for any specific logic processing. + // Therefore, it has no impact on either the client or the server. + TMessage onewayMessage = new TMessage(message.name, TMessageType.ONEWAY, message.seqid); + super.writeMessageBegin(onewayMessage); + } else { + super.writeMessageBegin(message); + } + } + } + + @Override + public void writeFieldStop() throws TException { + try { + if (!this.injected && this.requestScopeContext != null) { + ThriftRequest request = this.requestScopeContext.getRequest(); + this.writeHeader(request.getHeader()); + } + } finally { + this.injected = true; + super.writeFieldStop(); + } + } + + public void writeHeader(Map header) throws TException { + super.writeFieldBegin(new TField(OT_MAGIC_FIELD, TType.MAP, OT_MAGIC_FIELD_ID)); + super.writeMapBegin(new TMap(TType.STRING, TType.STRING, header.size())); + + Set> entries = header.entrySet(); + for (Map.Entry entry : entries) { + super.writeString(entry.getKey()); + super.writeString(entry.getValue()); + } + + super.writeMapEnd(); + super.writeFieldEnd(); + } + + public boolean isOneway() { + return this.type == TMessageType.ONEWAY; + } + + public boolean isChangeToOneway() { + return this.type != this.originType; + } + + public void updateTransport(TTransport transport) { + this.transport = transport; + } + + public RequestScopeContext getRequestScopeContext() { + return requestScopeContext; + } + + public void setRequestScopeContext(RequestScopeContext requestScopeContext) { + this.requestScopeContext = requestScopeContext; + } + + public void setServiceName(String serviceName) { + this.serviceName = serviceName; + } + + public void setType(byte type) { + this.type = type; + } +} diff --git a/instrumentation/thrift/thrift-0.9.1/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/thrift/v0_9_1/client/ClientProtocolFactoryWrapper.java b/instrumentation/thrift/thrift-0.9.1/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/thrift/v0_9_1/client/ClientProtocolFactoryWrapper.java new file mode 100644 index 000000000000..346be082ff0c --- /dev/null +++ b/instrumentation/thrift/thrift-0.9.1/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/thrift/v0_9_1/client/ClientProtocolFactoryWrapper.java @@ -0,0 +1,40 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.instrumentation.thrift.v0_9_1.client; + +import org.apache.thrift.protocol.TProtocol; +import org.apache.thrift.protocol.TProtocolFactory; +import org.apache.thrift.transport.TTransport; + +public final class ClientProtocolFactoryWrapper implements TProtocolFactory { + private final TProtocolFactory delegate; + private final TTransport transport; + private final String serviceName; + + @Override + public TProtocol getProtocol(TTransport transport) { + TProtocol protocol = this.delegate.getProtocol(transport); + if (protocol instanceof ClientOutProtocolWrapper) { + if (transport != null) { + ((ClientOutProtocolWrapper) protocol).updateTransport(this.transport); + } + ((ClientOutProtocolWrapper) protocol).setServiceName(this.serviceName); + return protocol; + } + protocol = new ClientOutProtocolWrapper(protocol, this.serviceName, null); + if (transport != null) { + ((ClientOutProtocolWrapper) protocol).updateTransport(this.transport); + } + return protocol; + } + + public ClientProtocolFactoryWrapper( + TProtocolFactory protocolFactory, TTransport transport, String serviceName) { + this.delegate = protocolFactory; + this.transport = transport; + this.serviceName = serviceName; + } +} diff --git a/instrumentation/thrift/thrift-0.9.1/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/thrift/v0_9_1/client/ThriftAsyncClientInstrumentation.java b/instrumentation/thrift/thrift-0.9.1/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/thrift/v0_9_1/client/ThriftAsyncClientInstrumentation.java new file mode 100644 index 000000000000..0684aa58e8f4 --- /dev/null +++ b/instrumentation/thrift/thrift-0.9.1/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/thrift/v0_9_1/client/ThriftAsyncClientInstrumentation.java @@ -0,0 +1,58 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.instrumentation.thrift.v0_9_1.client; + +import static io.opentelemetry.javaagent.extension.matcher.AgentElementMatchers.extendsClass; +import static io.opentelemetry.javaagent.extension.matcher.AgentElementMatchers.hasClassesNamed; +import static net.bytebuddy.matcher.ElementMatchers.isConstructor; +import static net.bytebuddy.matcher.ElementMatchers.named; +import static net.bytebuddy.matcher.ElementMatchers.takesArgument; + +import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation; +import io.opentelemetry.javaagent.extension.instrumentation.TypeTransformer; +import net.bytebuddy.asm.Advice; +import net.bytebuddy.asm.Advice.AssignReturned; +import net.bytebuddy.asm.Advice.AssignReturned.ToArguments.ToArgument; +import net.bytebuddy.description.type.TypeDescription; +import net.bytebuddy.matcher.ElementMatcher; +import org.apache.thrift.protocol.TProtocolFactory; +import org.apache.thrift.transport.TTransport; + +class ThriftAsyncClientInstrumentation implements TypeInstrumentation { + @Override + public ElementMatcher typeMatcher() { + return extendsClass(named("org.apache.thrift.async.TAsyncClient")); + } + + @Override + public ElementMatcher classLoaderOptimization() { + return hasClassesNamed("org.apache.thrift.async.TAsyncClient"); + } + + @Override + public void transform(TypeTransformer transformer) { + transformer.applyAdviceToMethod( + isConstructor() + .and(takesArgument(0, named("org.apache.thrift.protocol.TProtocolFactory"))) + .and(takesArgument(2, named("org.apache.thrift.transport.TNonblockingTransport"))), + getClass().getName() + "$ConstructorAdvice"); + } + + @SuppressWarnings("unused") + public static class ConstructorAdvice { + @AssignReturned.ToArguments(@ToArgument(0)) + @Advice.OnMethodEnter(suppress = Throwable.class, inline = false) + public static TProtocolFactory onEnter( + @Advice.Origin("#t") String serviceName, + @Advice.Argument(0) TProtocolFactory factory, + @Advice.Argument(2) TTransport transport) { + if (factory instanceof ClientProtocolFactoryWrapper) { + return factory; + } + return new ClientProtocolFactoryWrapper(factory, transport, serviceName); + } + } +} diff --git a/instrumentation/thrift/thrift-0.9.1/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/thrift/v0_9_1/client/ThriftAsyncMethodCallInstrumentation.java b/instrumentation/thrift/thrift-0.9.1/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/thrift/v0_9_1/client/ThriftAsyncMethodCallInstrumentation.java new file mode 100644 index 000000000000..0cfb09588ea5 --- /dev/null +++ b/instrumentation/thrift/thrift-0.9.1/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/thrift/v0_9_1/client/ThriftAsyncMethodCallInstrumentation.java @@ -0,0 +1,65 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.instrumentation.thrift.v0_9_1.client; + +import static io.opentelemetry.instrumentation.thrift.common.client.VirtualFields.ASYNC_METHOD_CALLBACK; +import static net.bytebuddy.matcher.ElementMatchers.isConstructor; +import static net.bytebuddy.matcher.ElementMatchers.named; +import static net.bytebuddy.matcher.ElementMatchers.takesArgument; + +import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation; +import io.opentelemetry.javaagent.extension.instrumentation.TypeTransformer; +import io.opentelemetry.javaagent.instrumentation.thrift.v0_9_1.AsyncMethodCallbackWrapper; +import net.bytebuddy.asm.Advice; +import net.bytebuddy.asm.Advice.AssignReturned; +import net.bytebuddy.asm.Advice.AssignReturned.ToArguments.ToArgument; +import net.bytebuddy.description.type.TypeDescription; +import net.bytebuddy.matcher.ElementMatcher; +import org.apache.thrift.async.AsyncMethodCallback; +import org.apache.thrift.async.TAsyncMethodCall; + +class ThriftAsyncMethodCallInstrumentation implements TypeInstrumentation { + + @Override + public ElementMatcher typeMatcher() { + return named("org.apache.thrift.async.TAsyncMethodCall"); + } + + @Override + public void transform(TypeTransformer transformer) { + transformer.applyAdviceToMethod( + isConstructor().and(takesArgument(3, named("org.apache.thrift.async.AsyncMethodCallback"))), + getClass().getName() + "$ConstructorAdvice"); + + transformer.applyAdviceToMethod( + named("prepareMethodCall"), getClass().getName() + "$MethodCallAdvice"); + } + + @SuppressWarnings("unused") + public static class ConstructorAdvice { + @AssignReturned.ToArguments(@ToArgument(3)) + @Advice.OnMethodEnter(suppress = Throwable.class, inline = false) + public static AsyncMethodCallback onEnter( + @Advice.Argument(3) AsyncMethodCallback callback) { + if (callback instanceof AsyncMethodCallbackWrapper) { + return callback; + } + return new AsyncMethodCallbackWrapper<>(callback, false); + } + } + + @SuppressWarnings("unused") + public static class MethodCallAdvice { + @Advice.OnMethodEnter(suppress = Throwable.class, inline = false) + public static void methodEnter( + @Advice.This TAsyncMethodCall thiz, + @Advice.FieldValue("callback") AsyncMethodCallback callback) { + if (callback instanceof AsyncMethodCallbackWrapper) { + ASYNC_METHOD_CALLBACK.set(thiz, callback); + } + } + } +} diff --git a/instrumentation/thrift/thrift-0.9.1/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/thrift/v0_9_1/client/ThriftAsyncWriteArgsInstrumentation.java b/instrumentation/thrift/thrift-0.9.1/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/thrift/v0_9_1/client/ThriftAsyncWriteArgsInstrumentation.java new file mode 100644 index 000000000000..1b05a448667e --- /dev/null +++ b/instrumentation/thrift/thrift-0.9.1/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/thrift/v0_9_1/client/ThriftAsyncWriteArgsInstrumentation.java @@ -0,0 +1,83 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.instrumentation.thrift.v0_9_1.client; + +import static io.opentelemetry.instrumentation.thrift.common.client.VirtualFields.ASYNC_METHOD_CALLBACK; +import static io.opentelemetry.javaagent.extension.matcher.AgentElementMatchers.extendsClass; +import static io.opentelemetry.javaagent.instrumentation.thrift.v0_9_1.ThriftSingletons.clientInstrumenter; +import static net.bytebuddy.matcher.ElementMatchers.named; + +import io.opentelemetry.context.Context; +import io.opentelemetry.instrumentation.thrift.common.RequestScopeContext; +import io.opentelemetry.instrumentation.thrift.common.client.MethodAccessor; +import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation; +import io.opentelemetry.javaagent.extension.instrumentation.TypeTransformer; +import io.opentelemetry.javaagent.instrumentation.thrift.v0_9_1.AsyncMethodCallbackWrapper; +import java.util.Set; +import net.bytebuddy.asm.Advice; +import net.bytebuddy.description.type.TypeDescription; +import net.bytebuddy.matcher.ElementMatcher; +import org.apache.thrift.async.AsyncMethodCallback; +import org.apache.thrift.async.TAsyncMethodCall; +import org.apache.thrift.protocol.TMessageType; +import org.apache.thrift.protocol.TProtocol; + +class ThriftAsyncWriteArgsInstrumentation implements TypeInstrumentation { + + @Override + public ElementMatcher typeMatcher() { + return extendsClass(named("org.apache.thrift.async.TAsyncMethodCall")); + } + + @Override + public void transform(TypeTransformer transformer) { + transformer.applyAdviceToMethod(named("write_args"), getClass().getName() + "$WriteArgsAdvice"); + } + + @SuppressWarnings("unused") + public static class WriteArgsAdvice { + @Advice.OnMethodEnter(suppress = Throwable.class, inline = false) + public static void methodEnter( + @Advice.Origin("#t") String serviceName, @Advice.Argument(0) TProtocol protocol) { + if (protocol instanceof ClientOutProtocolWrapper) { + Set methodNames = MethodAccessor.voidMethodNames(serviceName); + // Compatible with asynchronous oneway method + if (methodNames.contains("getResult")) { + ClientOutProtocolWrapper wrapper = (ClientOutProtocolWrapper) protocol; + wrapper.setType(TMessageType.ONEWAY); + } + } + } + + @Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class, inline = false) + public static void methodExit( + @Advice.This TAsyncMethodCall methodCall, + @Advice.Argument(0) TProtocol protocol, + @Advice.Thrown Throwable throwable) { + if (protocol instanceof ClientOutProtocolWrapper) { + ClientOutProtocolWrapper wrapper = (ClientOutProtocolWrapper) protocol; + RequestScopeContext requestScopeContext = wrapper.getRequestScopeContext(); + if (requestScopeContext == null) { + return; + } + // wrapper.isChangeToOneway() judgment logic is for compatibility with version 0.9.1 + // the return value is void but is not oneway method + if (throwable != null) { + requestScopeContext.close(); + Context context = requestScopeContext.getContext(); + clientInstrumenter().end(context, requestScopeContext.getRequest(), null, throwable); + wrapper.setRequestScopeContext(null); + return; + } + + AsyncMethodCallback callback = ASYNC_METHOD_CALLBACK.get(methodCall); + if (callback instanceof AsyncMethodCallbackWrapper) { + ((AsyncMethodCallbackWrapper) callback).setRequestScopeContext(requestScopeContext); + } + } + } + } +} diff --git a/instrumentation/thrift/thrift-0.9.1/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/thrift/v0_9_1/client/ThriftClientCommonInstrumentationModule.java b/instrumentation/thrift/thrift-0.9.1/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/thrift/v0_9_1/client/ThriftClientCommonInstrumentationModule.java new file mode 100644 index 000000000000..faf82cbe36e3 --- /dev/null +++ b/instrumentation/thrift/thrift-0.9.1/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/thrift/v0_9_1/client/ThriftClientCommonInstrumentationModule.java @@ -0,0 +1,37 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.instrumentation.thrift.v0_9_1.client; + +import static io.opentelemetry.javaagent.extension.matcher.AgentElementMatchers.hasClassesNamed; +import static java.util.Arrays.asList; + +import com.google.auto.service.AutoService; +import io.opentelemetry.javaagent.extension.instrumentation.InstrumentationModule; +import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation; +import java.util.List; +import net.bytebuddy.matcher.ElementMatcher; + +@AutoService(InstrumentationModule.class) +public class ThriftClientCommonInstrumentationModule extends InstrumentationModule { + + public ThriftClientCommonInstrumentationModule() { + super("thrift", "thrift-0.9.1", "thrift-0.9.1-client"); + } + + @Override + public ElementMatcher.Junction classLoaderMatcher() { + return hasClassesNamed("org.apache.thrift.protocol.TProtocolDecorator"); + } + + @Override + public List typeInstrumentations() { + return asList( + new ThriftClientInstrumentation(), + new ThriftAsyncClientInstrumentation(), + new ThriftAsyncMethodCallInstrumentation(), + new ThriftAsyncWriteArgsInstrumentation()); + } +} diff --git a/instrumentation/thrift/thrift-0.9.1/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/thrift/v0_9_1/client/ThriftClientInstrumentation.java b/instrumentation/thrift/thrift-0.9.1/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/thrift/v0_9_1/client/ThriftClientInstrumentation.java new file mode 100644 index 000000000000..0ca7920df1f7 --- /dev/null +++ b/instrumentation/thrift/thrift-0.9.1/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/thrift/v0_9_1/client/ThriftClientInstrumentation.java @@ -0,0 +1,136 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.instrumentation.thrift.v0_9_1.client; + +import static io.opentelemetry.javaagent.extension.matcher.AgentElementMatchers.extendsClass; +import static io.opentelemetry.javaagent.instrumentation.thrift.v0_9_1.ThriftSingletons.clientInstrumenter; +import static net.bytebuddy.matcher.ElementMatchers.isConstructor; +import static net.bytebuddy.matcher.ElementMatchers.isProtected; +import static net.bytebuddy.matcher.ElementMatchers.nameStartsWith; +import static net.bytebuddy.matcher.ElementMatchers.named; +import static net.bytebuddy.matcher.ElementMatchers.takesArguments; + +import io.opentelemetry.context.Context; +import io.opentelemetry.instrumentation.thrift.common.RequestScopeContext; +import io.opentelemetry.instrumentation.thrift.common.client.MethodAccessor; +import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation; +import io.opentelemetry.javaagent.extension.instrumentation.TypeTransformer; +import java.util.Set; +import net.bytebuddy.asm.Advice; +import net.bytebuddy.asm.Advice.AssignReturned; +import net.bytebuddy.asm.Advice.AssignReturned.ToArguments.ToArgument; +import net.bytebuddy.description.type.TypeDescription; +import net.bytebuddy.matcher.ElementMatcher; +import org.apache.thrift.protocol.TProtocol; + +class ThriftClientInstrumentation implements TypeInstrumentation { + + @Override + public ElementMatcher typeMatcher() { + return extendsClass(named("org.apache.thrift.TServiceClient")); + } + + @Override + public void transform(TypeTransformer transformer) { + transformer.applyAdviceToMethod( + isConstructor().and(takesArguments(1)), getClass().getName() + "$ConstructorOneAdvice"); + + transformer.applyAdviceToMethod( + isConstructor().and(takesArguments(2)), getClass().getName() + "$ConstructorTwoAdvice"); + + transformer.applyAdviceToMethod( + isProtected().and(nameStartsWith("sendBase")), getClass().getName() + "$ClientSendAdvice"); + + transformer.applyAdviceToMethod( + named("receiveBase"), getClass().getName() + "$ClientReceiveAdvice"); + } + + @SuppressWarnings("unused") + public static class ConstructorOneAdvice { + @AssignReturned.ToArguments(@ToArgument(0)) + @Advice.OnMethodEnter(suppress = Throwable.class, inline = false) + public static TProtocol onEnter( + @Advice.Origin("#t") String serviceName, @Advice.Argument(0) TProtocol inProtocol) { + if (inProtocol instanceof ClientOutProtocolWrapper) { + return inProtocol; + } + Set voidMethodNames = MethodAccessor.voidMethodNames(serviceName); + return new ClientOutProtocolWrapper(inProtocol, serviceName, voidMethodNames); + } + } + + @SuppressWarnings("unused") + public static class ConstructorTwoAdvice { + @AssignReturned.ToArguments({ + @ToArgument(value = 0, index = 0), + @ToArgument(value = 1, index = 1) + }) + @Advice.OnMethodEnter(suppress = Throwable.class, inline = false) + public static Object[] onEnter( + @Advice.Origin("#t") String serviceName, + @Advice.Argument(0) TProtocol inProtocol, + @Advice.Argument(1) TProtocol outProtocol) { + Set voidMethodNames = MethodAccessor.voidMethodNames(serviceName); + TProtocol inProtocolResult = inProtocol; + TProtocol outProtocolResult = outProtocol; + if (!(inProtocol instanceof ClientOutProtocolWrapper)) { + inProtocolResult = new ClientOutProtocolWrapper(inProtocol, serviceName, voidMethodNames); + } + if (!(outProtocol instanceof ClientOutProtocolWrapper)) { + outProtocolResult = new ClientOutProtocolWrapper(outProtocol, serviceName, voidMethodNames); + } + return new Object[] {inProtocolResult, outProtocolResult}; + } + } + + @SuppressWarnings("unused") + public static class ClientSendAdvice { + @Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class, inline = false) + public static void methodExit( + @Advice.FieldValue("oprot_") TProtocol outProtocol, @Advice.Thrown Throwable throwable) { + if (outProtocol instanceof ClientOutProtocolWrapper) { + ClientOutProtocolWrapper wrapper = (ClientOutProtocolWrapper) outProtocol; + RequestScopeContext requestScopeContext = wrapper.getRequestScopeContext(); + if (requestScopeContext == null) { + return; + } + + Context context = requestScopeContext.getContext(); + if (throwable != null) { + requestScopeContext.close(); + clientInstrumenter().end(context, requestScopeContext.getRequest(), null, throwable); + wrapper.setRequestScopeContext(null); + return; + } + + if (wrapper.isOneway()) { + requestScopeContext.close(); + clientInstrumenter().end(context, requestScopeContext.getRequest(), 0, null); + wrapper.setRequestScopeContext(null); + } + } + } + } + + @SuppressWarnings("unused") + public static class ClientReceiveAdvice { + @Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class, inline = false) + public static void methodExit( + @Advice.Thrown Throwable throwable, @Advice.FieldValue("oprot_") TProtocol outProtocol) { + if (outProtocol instanceof ClientOutProtocolWrapper) { + ClientOutProtocolWrapper wrapper = (ClientOutProtocolWrapper) outProtocol; + RequestScopeContext requestScopeContext = wrapper.getRequestScopeContext(); + if (requestScopeContext == null) { + return; + } + requestScopeContext.close(); + Context context = requestScopeContext.getContext(); + clientInstrumenter().end(context, requestScopeContext.getRequest(), null, throwable); + wrapper.setRequestScopeContext(null); + } + } + } +} diff --git a/instrumentation/thrift/thrift-0.9.1/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/thrift/v0_9_1/server/ServerInProtocolWrapper.java b/instrumentation/thrift/thrift-0.9.1/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/thrift/v0_9_1/server/ServerInProtocolWrapper.java new file mode 100644 index 000000000000..cfea1723bd94 --- /dev/null +++ b/instrumentation/thrift/thrift-0.9.1/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/thrift/v0_9_1/server/ServerInProtocolWrapper.java @@ -0,0 +1,145 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.instrumentation.thrift.v0_9_1.server; + +import static io.opentelemetry.javaagent.instrumentation.thrift.v0_9_1.ThriftSingletons.serverInstrumenter; + +import io.opentelemetry.context.Context; +import io.opentelemetry.context.Scope; +import io.opentelemetry.instrumentation.api.internal.Timer; +import io.opentelemetry.instrumentation.thrift.common.RequestScopeContext; +import io.opentelemetry.instrumentation.thrift.common.SocketAccessor; +import io.opentelemetry.instrumentation.thrift.common.ThriftRequest; +import io.opentelemetry.javaagent.instrumentation.thrift.v0_9_1.AbstractProtocolWrapper; +import java.net.Socket; +import java.util.HashMap; +import java.util.Map; +import org.apache.thrift.TException; +import org.apache.thrift.protocol.TField; +import org.apache.thrift.protocol.TMap; +import org.apache.thrift.protocol.TMessage; +import org.apache.thrift.protocol.TMessageType; +import org.apache.thrift.protocol.TProtocol; +import org.apache.thrift.protocol.TType; +import org.apache.thrift.transport.TTransport; + +public final class ServerInProtocolWrapper extends AbstractProtocolWrapper { + + private volatile RequestScopeContext requestScopeContext; + private String methodName; + private String serviceName; + public TTransport transport; + private byte type; + private Timer timer; + + public ServerInProtocolWrapper(TProtocol protocol) { + super(protocol); + } + + @Override + public TMessage readMessageBegin() throws TException { + TMessage message = super.readMessageBegin(); + this.methodName = message.name; + this.type = message.type; + this.timer = Timer.start(); + return message; + } + + @Override + public TField readFieldBegin() throws TException { + TField field = super.readFieldBegin(); + if (field.id == OT_MAGIC_FIELD_ID && field.type == TType.MAP) { + try { + TMap map = super.readMapBegin(); + Map header = new HashMap<>(map.size); + + for (int i = 0; i < map.size; i++) { + header.put(readString(), readString()); + } + + Socket socket = SocketAccessor.getSocket(super.getTransport()); + if (socket == null) { + // The non-blocking processing method cannot obtain the corresponding Transport with a + // socket through super.getTransport(). + // Instrumentation has been added to the invoke methods of FrameBuffer and + // AsyncFrameBuffer to actively set TNonblockingTransport. + // This serves as a compensation here. + socket = SocketAccessor.getSocket(this.transport); + } + ThriftRequest request = + ThriftRequest.create(this.serviceName, this.methodName, socket, header); + Context parentContext = Context.current(); + if (!serverInstrumenter().shouldStart(parentContext, request)) { + return field; + } + Context context = serverInstrumenter().start(parentContext, request); + this.requestScopeContext = RequestScopeContext.create(request, null, context); + } finally { + super.readMapEnd(); + super.readFieldEnd(); + } + return this.readFieldBegin(); + } + return field; + } + + @Override + public void readMessageEnd() throws TException { + super.readMessageEnd(); + if (this.requestScopeContext == null) { + Socket socket = SocketAccessor.getSocket(super.getTransport()); + if (socket == null) { + // The non-blocking processing method cannot obtain the corresponding Transport with a + // socket through super.getTransport(). + // Instrumentation has been added to the invoke methods of FrameBuffer and AsyncFrameBuffer + // to actively set TNonblockingTransport. + // This serves as a compensation here. + socket = SocketAccessor.getSocket(this.transport); + } + ThriftRequest request = + ThriftRequest.create(this.serviceName, this.methodName, socket, new HashMap<>()); + Context parentContext = Context.current(); + if (!serverInstrumenter().shouldStart(parentContext, request)) { + return; + } + Context context = serverInstrumenter().start(parentContext, request); + Scope scope = context.makeCurrent(); + this.requestScopeContext = RequestScopeContext.create(request, scope, context); + } + } + + public String getMethodName() { + return methodName; + } + + public boolean isOneway() { + return type == TMessageType.ONEWAY; + } + + public RequestScopeContext getRequestScopeContext() { + return requestScopeContext; + } + + public void setRequestScopeContext(RequestScopeContext requestScopeContext) { + this.requestScopeContext = requestScopeContext; + } + + public Timer getTimer() { + return timer; + } + + public String getServiceName() { + return serviceName; + } + + public void setServiceName(String serviceName) { + this.serviceName = serviceName; + } + + public void setTransport(TTransport transport) { + this.transport = transport; + } +} diff --git a/instrumentation/thrift/thrift-0.9.1/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/thrift/v0_9_1/server/ServerProtocolFactoryWrapper.java b/instrumentation/thrift/thrift-0.9.1/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/thrift/v0_9_1/server/ServerProtocolFactoryWrapper.java new file mode 100644 index 000000000000..b860eea52397 --- /dev/null +++ b/instrumentation/thrift/thrift-0.9.1/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/thrift/v0_9_1/server/ServerProtocolFactoryWrapper.java @@ -0,0 +1,28 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.instrumentation.thrift.v0_9_1.server; + +import org.apache.thrift.protocol.TProtocol; +import org.apache.thrift.protocol.TProtocolFactory; +import org.apache.thrift.transport.TTransport; + +@SuppressWarnings({"serial"}) +public final class ServerProtocolFactoryWrapper implements TProtocolFactory { + public TProtocolFactory delegate; + + @Override + public TProtocol getProtocol(TTransport transport) { + TProtocol protocol = delegate.getProtocol(transport); + if (protocol instanceof ServerInProtocolWrapper) { + return protocol; + } + return new ServerInProtocolWrapper(protocol); + } + + public ServerProtocolFactoryWrapper(TProtocolFactory protocolFactory) { + this.delegate = protocolFactory; + } +} diff --git a/instrumentation/thrift/thrift-0.9.1/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/thrift/v0_9_1/server/ThriftAsyncProcessInstrumentation.java b/instrumentation/thrift/thrift-0.9.1/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/thrift/v0_9_1/server/ThriftAsyncProcessInstrumentation.java new file mode 100644 index 000000000000..1784b3226717 --- /dev/null +++ b/instrumentation/thrift/thrift-0.9.1/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/thrift/v0_9_1/server/ThriftAsyncProcessInstrumentation.java @@ -0,0 +1,67 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.instrumentation.thrift.v0_9_1.server; + +import static io.opentelemetry.javaagent.extension.matcher.AgentElementMatchers.extendsClass; +import static io.opentelemetry.javaagent.extension.matcher.AgentElementMatchers.hasClassesNamed; +import static net.bytebuddy.matcher.ElementMatchers.named; + +import io.opentelemetry.instrumentation.thrift.common.RequestScopeContext; +import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation; +import io.opentelemetry.javaagent.extension.instrumentation.TypeTransformer; +import io.opentelemetry.javaagent.instrumentation.thrift.v0_9_1.AsyncMethodCallbackWrapper; +import net.bytebuddy.asm.Advice; +import net.bytebuddy.asm.Advice.AssignReturned; +import net.bytebuddy.description.type.TypeDescription; +import net.bytebuddy.matcher.ElementMatcher; +import org.apache.thrift.async.AsyncMethodCallback; +import org.apache.thrift.protocol.TProtocol; +import org.apache.thrift.server.AbstractNonblockingServer; + +class ThriftAsyncProcessInstrumentation implements TypeInstrumentation { + + @Override + public ElementMatcher typeMatcher() { + return extendsClass(named("org.apache.thrift.AsyncProcessFunction")); + } + + @Override + public ElementMatcher classLoaderOptimization() { + return hasClassesNamed("org.apache.thrift.AsyncProcessFunction"); + } + + @Override + public void transform(TypeTransformer transformer) { + transformer.applyAdviceToMethod( + named("getResultHandler"), getClass().getName() + "$GetResultHandlerAdvice"); + } + + @SuppressWarnings("unused") + public static class GetResultHandlerAdvice { + + @AssignReturned.ToReturned + @Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class, inline = false) + public static AsyncMethodCallback methodExit( + @Advice.Argument(0) AbstractNonblockingServer.AsyncFrameBuffer fb, + @Advice.Return AsyncMethodCallback callback) { + TProtocol inProtocol = fb.getInputProtocol(); + if (inProtocol instanceof ServerInProtocolWrapper) { + ServerInProtocolWrapper wrapper = (ServerInProtocolWrapper) inProtocol; + RequestScopeContext requestScopeContext = wrapper.getRequestScopeContext(); + if (requestScopeContext == null) { + return callback; + } + + AsyncMethodCallbackWrapper callbackWrapper = + new AsyncMethodCallbackWrapper<>(callback, true); + callbackWrapper.setRequestScopeContext(requestScopeContext); + return callbackWrapper; + } + + return callback; + } + } +} diff --git a/instrumentation/thrift/thrift-0.9.1/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/thrift/v0_9_1/server/ThriftBaseAsyncProcessorInstrumentation.java b/instrumentation/thrift/thrift-0.9.1/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/thrift/v0_9_1/server/ThriftBaseAsyncProcessorInstrumentation.java new file mode 100644 index 000000000000..c06cbd6d2c2e --- /dev/null +++ b/instrumentation/thrift/thrift-0.9.1/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/thrift/v0_9_1/server/ThriftBaseAsyncProcessorInstrumentation.java @@ -0,0 +1,73 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.instrumentation.thrift.v0_9_1.server; + +import static io.opentelemetry.javaagent.instrumentation.thrift.v0_9_1.ThriftSingletons.serverInstrumenter; +import static net.bytebuddy.matcher.ElementMatchers.named; +import static net.bytebuddy.matcher.ElementMatchers.takesArguments; + +import io.opentelemetry.context.Context; +import io.opentelemetry.instrumentation.thrift.common.RequestScopeContext; +import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation; +import io.opentelemetry.javaagent.extension.instrumentation.TypeTransformer; +import net.bytebuddy.asm.Advice; +import net.bytebuddy.description.type.TypeDescription; +import net.bytebuddy.matcher.ElementMatcher; +import org.apache.thrift.protocol.TProtocol; +import org.apache.thrift.server.AbstractNonblockingServer; + +class ThriftBaseAsyncProcessorInstrumentation implements TypeInstrumentation { + + @Override + public ElementMatcher typeMatcher() { + return named("org.apache.thrift.TBaseAsyncProcessor"); + } + + @Override + public void transform(TypeTransformer transformer) { + transformer.applyAdviceToMethod( + named("process").and(takesArguments(1)), getClass().getName() + "$ProcessAdvice"); + } + + @SuppressWarnings("unused") + public static class ProcessAdvice { + + @Advice.OnMethodEnter(suppress = Throwable.class, inline = false) + public static void methodEnter( + @Advice.Argument(0) AbstractNonblockingServer.AsyncFrameBuffer fb, + @Advice.FieldValue("iface") Object iface) { + String serviceName = iface.getClass().getName(); + TProtocol inProtocol = fb.getInputProtocol(); + if (inProtocol instanceof ServerInProtocolWrapper) { + ServerInProtocolWrapper wrapper = (ServerInProtocolWrapper) inProtocol; + wrapper.setServiceName(serviceName); + } + } + + @Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class, inline = false) + public static void methodExit( + @Advice.Argument(0) AbstractNonblockingServer.AsyncFrameBuffer fb, + @Advice.Thrown Throwable throwable) { + TProtocol inProtocol = fb.getInputProtocol(); + if (inProtocol instanceof ServerInProtocolWrapper) { + ServerInProtocolWrapper wrapper = (ServerInProtocolWrapper) inProtocol; + if (throwable == null && !wrapper.isOneway()) { + return; + } + + RequestScopeContext requestScopeContext = wrapper.getRequestScopeContext(); + if (requestScopeContext == null) { + return; + } + + requestScopeContext.close(); + Context context = requestScopeContext.getContext(); + serverInstrumenter().end(context, requestScopeContext.getRequest(), null, throwable); + wrapper.setRequestScopeContext(null); + } + } + } +} diff --git a/instrumentation/thrift/thrift-0.9.1/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/thrift/v0_9_1/server/ThriftBaseProcessorInstrumentation.java b/instrumentation/thrift/thrift-0.9.1/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/thrift/v0_9_1/server/ThriftBaseProcessorInstrumentation.java new file mode 100644 index 000000000000..e1abea22e636 --- /dev/null +++ b/instrumentation/thrift/thrift-0.9.1/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/thrift/v0_9_1/server/ThriftBaseProcessorInstrumentation.java @@ -0,0 +1,77 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.instrumentation.thrift.v0_9_1.server; + +import static io.opentelemetry.javaagent.instrumentation.thrift.v0_9_1.ThriftSingletons.serverInstrumenter; +import static net.bytebuddy.matcher.ElementMatchers.named; + +import io.opentelemetry.context.Context; +import io.opentelemetry.instrumentation.thrift.common.RequestScopeContext; +import io.opentelemetry.instrumentation.thrift.common.client.MethodAccessor; +import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation; +import io.opentelemetry.javaagent.extension.instrumentation.TypeTransformer; +import java.lang.reflect.Field; +import net.bytebuddy.asm.Advice; +import net.bytebuddy.description.type.TypeDescription; +import net.bytebuddy.matcher.ElementMatcher; +import org.apache.thrift.protocol.TProtocol; +import org.apache.thrift.protocol.TProtocolDecorator; + +class ThriftBaseProcessorInstrumentation implements TypeInstrumentation { + + @Override + public ElementMatcher typeMatcher() { + return named("org.apache.thrift.TBaseProcessor"); + } + + @Override + public void transform(TypeTransformer transformer) { + transformer.applyAdviceToMethod(named("process"), getClass().getName() + "$ProcessAdvice"); + } + + @SuppressWarnings("unused") + public static class ProcessAdvice { + + @Advice.OnMethodEnter(suppress = Throwable.class, inline = false) + public static void methodEnter( + @Advice.Argument(0) TProtocol inProtocol, @Advice.FieldValue("iface") Object iface) + throws IllegalAccessException { + String serviceName = iface.getClass().getName(); + if (inProtocol instanceof ServerInProtocolWrapper) { + ServerInProtocolWrapper wrapper = (ServerInProtocolWrapper) inProtocol; + wrapper.setServiceName(serviceName); + } else if (inProtocol instanceof TProtocolDecorator) { + // TMultiplexedProcessor compatible + Field field = MethodAccessor.getConcreteProtocolField(TProtocolDecorator.class); + Object obj = field.get(inProtocol); + if (obj instanceof ServerInProtocolWrapper) { + ServerInProtocolWrapper wrapper = (ServerInProtocolWrapper) obj; + wrapper.setServiceName(serviceName); + } + } + } + + @Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class, inline = false) + public static void methodExit( + @Advice.Argument(0) TProtocol inProtocol, @Advice.Thrown Throwable throwable) { + if (inProtocol instanceof ServerInProtocolWrapper) { + ServerInProtocolWrapper wrapper = (ServerInProtocolWrapper) inProtocol; + String methodName = wrapper.getMethodName(); + if (methodName == null || methodName.isEmpty()) { + return; + } + RequestScopeContext requestScopeContext = wrapper.getRequestScopeContext(); + if (requestScopeContext == null) { + return; + } + requestScopeContext.close(); + Context context = requestScopeContext.getContext(); + serverInstrumenter().end(context, requestScopeContext.getRequest(), null, throwable); + wrapper.setRequestScopeContext(null); + } + } + } +} diff --git a/instrumentation/thrift/thrift-0.9.1/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/thrift/v0_9_1/server/ThriftFrameBufferInstrumentation.java b/instrumentation/thrift/thrift-0.9.1/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/thrift/v0_9_1/server/ThriftFrameBufferInstrumentation.java new file mode 100644 index 000000000000..50fc25637554 --- /dev/null +++ b/instrumentation/thrift/thrift-0.9.1/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/thrift/v0_9_1/server/ThriftFrameBufferInstrumentation.java @@ -0,0 +1,47 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.instrumentation.thrift.v0_9_1.server; + +import static net.bytebuddy.matcher.ElementMatchers.named; +import static net.bytebuddy.matcher.ElementMatchers.namedOneOf; + +import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation; +import io.opentelemetry.javaagent.extension.instrumentation.TypeTransformer; +import net.bytebuddy.asm.Advice; +import net.bytebuddy.description.type.TypeDescription; +import net.bytebuddy.matcher.ElementMatcher; +import org.apache.thrift.protocol.TProtocol; +import org.apache.thrift.transport.TNonblockingTransport; + +class ThriftFrameBufferInstrumentation implements TypeInstrumentation { + + @Override + public ElementMatcher typeMatcher() { + return namedOneOf( + "org.apache.thrift.server.AbstractNonblockingServer$FrameBuffer", + "org.apache.thrift.server.AbstractNonblockingServer$AsyncFrameBuffer"); + } + + @Override + public void transform(TypeTransformer transformer) { + transformer.applyAdviceToMethod( + named("invoke"), getClass().getName() + "$FrameBufferConstructorAdvice"); + } + + @SuppressWarnings("unused") + public static class FrameBufferConstructorAdvice { + + @Advice.OnMethodEnter(suppress = Throwable.class, inline = false) + public static void methodEnter( + @Advice.FieldValue("inProt_") TProtocol inProtocol, + @Advice.FieldValue("trans_") TNonblockingTransport transport) { + if (inProtocol instanceof ServerInProtocolWrapper) { + ServerInProtocolWrapper wrapper = (ServerInProtocolWrapper) inProtocol; + wrapper.setTransport(transport); + } + } + } +} diff --git a/instrumentation/thrift/thrift-0.9.1/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/thrift/v0_9_1/server/ThriftMultiplexedProcessorInstrumentation.java b/instrumentation/thrift/thrift-0.9.1/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/thrift/v0_9_1/server/ThriftMultiplexedProcessorInstrumentation.java new file mode 100644 index 000000000000..4f9177ca0f93 --- /dev/null +++ b/instrumentation/thrift/thrift-0.9.1/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/thrift/v0_9_1/server/ThriftMultiplexedProcessorInstrumentation.java @@ -0,0 +1,82 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.instrumentation.thrift.v0_9_1.server; + +import static io.opentelemetry.javaagent.instrumentation.thrift.v0_9_1.ThriftSingletons.serverInstrumenter; +import static net.bytebuddy.matcher.ElementMatchers.named; + +import io.opentelemetry.context.Context; +import io.opentelemetry.instrumentation.api.internal.InstrumenterUtil; +import io.opentelemetry.instrumentation.api.internal.Timer; +import io.opentelemetry.instrumentation.thrift.common.RequestScopeContext; +import io.opentelemetry.instrumentation.thrift.common.SocketAccessor; +import io.opentelemetry.instrumentation.thrift.common.ThriftRequest; +import io.opentelemetry.javaagent.bootstrap.Java8BytecodeBridge; +import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation; +import io.opentelemetry.javaagent.extension.instrumentation.TypeTransformer; +import java.net.Socket; +import java.util.HashMap; +import net.bytebuddy.asm.Advice; +import net.bytebuddy.description.type.TypeDescription; +import net.bytebuddy.matcher.ElementMatcher; +import org.apache.thrift.protocol.TProtocol; + +class ThriftMultiplexedProcessorInstrumentation implements TypeInstrumentation { + + @Override + public ElementMatcher typeMatcher() { + return named("org.apache.thrift.TMultiplexedProcessor"); + } + + @Override + public void transform(TypeTransformer transformer) { + transformer.applyAdviceToMethod(named("process"), getClass().getName() + "$ProcessAdvice"); + } + + @SuppressWarnings("unused") + public static class ProcessAdvice { + + @Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class, inline = false) + public static void methodExit( + @Advice.Argument(0) TProtocol inProtocol, @Advice.Thrown Throwable throwable) { + if (inProtocol instanceof ServerInProtocolWrapper) { + ServerInProtocolWrapper wrapper = (ServerInProtocolWrapper) inProtocol; + String methodName = wrapper.getMethodName(); + if (methodName == null || methodName.isEmpty()) { + return; + } + + RequestScopeContext requestScopeContext = wrapper.getRequestScopeContext(); + if (requestScopeContext == null) { + if (throwable != null) { + Timer timer = wrapper.getTimer(); + Socket socket = SocketAccessor.getSocket(wrapper.getTransport()); + ThriftRequest request = + ThriftRequest.create( + wrapper.getServiceName(), wrapper.getMethodName(), socket, new HashMap<>()); + Context parentContext = Java8BytecodeBridge.currentContext(); + if (serverInstrumenter().shouldStart(parentContext, request)) { + InstrumenterUtil.startAndEnd( + serverInstrumenter(), + parentContext, + request, + null, + throwable, + timer.startTime(), + timer.now()); + wrapper.setRequestScopeContext(null); + } + } + return; + } + + requestScopeContext.close(); + Context context = requestScopeContext.getContext(); + serverInstrumenter().end(context, requestScopeContext.getRequest(), null, throwable); + } + } + } +} diff --git a/instrumentation/thrift/thrift-0.9.1/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/thrift/v0_9_1/server/ThriftServerInstrumentation.java b/instrumentation/thrift/thrift-0.9.1/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/thrift/v0_9_1/server/ThriftServerInstrumentation.java new file mode 100644 index 000000000000..f3df3d3e6f97 --- /dev/null +++ b/instrumentation/thrift/thrift-0.9.1/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/thrift/v0_9_1/server/ThriftServerInstrumentation.java @@ -0,0 +1,47 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.instrumentation.thrift.v0_9_1.server; + +import static net.bytebuddy.matcher.ElementMatchers.isConstructor; +import static net.bytebuddy.matcher.ElementMatchers.named; +import static net.bytebuddy.matcher.ElementMatchers.takesArguments; + +import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation; +import io.opentelemetry.javaagent.extension.instrumentation.TypeTransformer; +import net.bytebuddy.asm.Advice; +import net.bytebuddy.asm.Advice.AssignReturned; +import net.bytebuddy.asm.Advice.AssignReturned.ToFields.ToField; +import net.bytebuddy.description.type.TypeDescription; +import net.bytebuddy.matcher.ElementMatcher; +import org.apache.thrift.protocol.TProtocolFactory; + +class ThriftServerInstrumentation implements TypeInstrumentation { + + @Override + public ElementMatcher typeMatcher() { + return named("org.apache.thrift.server.TServer"); + } + + @Override + public void transform(TypeTransformer transformer) { + transformer.applyAdviceToMethod( + isConstructor().and(takesArguments(1)), getClass().getName() + "$ServerConstructorAdvice"); + } + + @SuppressWarnings("unused") + public static class ServerConstructorAdvice { + + @AssignReturned.ToFields(@ToField("inputProtocolFactory_")) + @Advice.OnMethodExit(suppress = Throwable.class, inline = false) + public static TProtocolFactory onExit( + @Advice.FieldValue("inputProtocolFactory_") TProtocolFactory factory) { + if (factory instanceof ServerProtocolFactoryWrapper) { + return factory; + } + return new ServerProtocolFactoryWrapper(factory); + } + } +} diff --git a/instrumentation/thrift/thrift-0.9.1/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/thrift/v0_9_1/server/ThriftServerInstrumentationModule.java b/instrumentation/thrift/thrift-0.9.1/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/thrift/v0_9_1/server/ThriftServerInstrumentationModule.java new file mode 100644 index 000000000000..ef1586926d7d --- /dev/null +++ b/instrumentation/thrift/thrift-0.9.1/javaagent/src/main/java/io/opentelemetry/javaagent/instrumentation/thrift/v0_9_1/server/ThriftServerInstrumentationModule.java @@ -0,0 +1,39 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.instrumentation.thrift.v0_9_1.server; + +import static io.opentelemetry.javaagent.extension.matcher.AgentElementMatchers.hasClassesNamed; +import static java.util.Arrays.asList; + +import com.google.auto.service.AutoService; +import io.opentelemetry.javaagent.extension.instrumentation.InstrumentationModule; +import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation; +import java.util.List; +import net.bytebuddy.matcher.ElementMatcher; + +@AutoService(InstrumentationModule.class) +public class ThriftServerInstrumentationModule extends InstrumentationModule { + + public ThriftServerInstrumentationModule() { + super("thrift", "thrift-0.9.1", "thrift-0.9.1-server"); + } + + @Override + public ElementMatcher.Junction classLoaderMatcher() { + return hasClassesNamed("org.apache.thrift.protocol.TProtocolDecorator"); + } + + @Override + public List typeInstrumentations() { + return asList( + new ThriftServerInstrumentation(), + new ThriftAsyncProcessInstrumentation(), + new ThriftFrameBufferInstrumentation(), + new ThriftBaseProcessorInstrumentation(), + new ThriftMultiplexedProcessorInstrumentation(), + new ThriftBaseAsyncProcessorInstrumentation()); + } +} diff --git a/instrumentation/thrift/thrift-0.9.1/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/thrift/v0_9_1/NoReturnTest.java b/instrumentation/thrift/thrift-0.9.1/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/thrift/v0_9_1/NoReturnTest.java new file mode 100644 index 000000000000..b932b926b891 --- /dev/null +++ b/instrumentation/thrift/thrift-0.9.1/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/thrift/v0_9_1/NoReturnTest.java @@ -0,0 +1,404 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.instrumentation.thrift.v0_9_1; + +import static org.assertj.core.api.Assertions.assertThat; + +import io.opentelemetry.javaagent.instrumentation.thrift.v0_9_1.thrift.ThriftService; +import java.io.IOException; +import org.apache.thrift.TException; +import org.apache.thrift.async.AsyncMethodCallback; +import org.apache.thrift.async.TAsyncClientManager; +import org.apache.thrift.protocol.TBinaryProtocol; +import org.apache.thrift.protocol.TMultiplexedProtocol; +import org.apache.thrift.protocol.TProtocol; +import org.apache.thrift.protocol.TProtocolFactory; +import org.apache.thrift.transport.TFramedTransport; +import org.apache.thrift.transport.TNonblockingSocket; +import org.apache.thrift.transport.TNonblockingTransport; +import org.apache.thrift.transport.TSocket; +import org.apache.thrift.transport.TTransport; +import org.apache.thrift.transport.TTransportException; +import org.junit.jupiter.api.Test; + +public class NoReturnTest extends ThriftBaseTest { + + @Test + public void syncClientSyncSimpleServerNoReturn() throws TException { + int port = super.getPort(); + this.startSyncSimpleServer(port); + this.syncClientNoReturn(port); + this.waitAndAssertTracesClientSyncServerSync(port, "noReturn", 1); + } + + @Test + public void syncClientSyncSimpleServerNoReturnParallel() + throws InterruptedException, TTransportException { + int port = super.getPort(); + this.startSyncSimpleServer(port); + int threadCount = 5; + runParallel(threadCount, () -> this.syncClientNoReturn(port)); + this.waitAndAssertTracesClientSyncServerSync(port, "noReturn", threadCount); + } + + @Test + public void syncClientSyncThreadPoolServerNoReturn() throws TException, InterruptedException { + int port = super.getPort(); + this.startSyncThreadPoolServer(port); + this.syncClientNoReturn(port); + this.waitAndAssertTracesClientSyncServerSync(port, "noReturn", 1); + } + + @Test + public void syncClientSyncThreadPoolServerNoReturnParallel() + throws InterruptedException, TTransportException { + int port = super.getPort(); + this.startSyncThreadPoolServer(port); + int threadCount = 5; + runParallel(threadCount, () -> this.syncClientNoReturn(port)); + this.waitAndAssertTracesClientSyncServerSync(port, "noReturn", threadCount); + } + + @Test + public void syncClientMutiSyncSimpleServerNoReturn() throws TException { + int port = super.getPort(); + this.startMultiSimpleServer(port); + this.syncClientMultiNoReturn(port); + this.waitAndAssertTracesClientSyncServerSync(port, "noReturn", "syncHelloWorld:noReturn", 1); + } + + @Test + public void syncClientMutiSyncSimpleServerNoReturnParallel() + throws InterruptedException, TTransportException { + int port = super.getPort(); + this.startMultiSimpleServer(port); + int threadCount = 5; + runParallel(threadCount, () -> this.syncClientMultiNoReturn(port)); + this.waitAndAssertTracesClientSyncServerSync( + port, "noReturn", "syncHelloWorld:noReturn", threadCount); + } + + @Test + public void syncFramedClientAsyncThreadedSelectorServerNoReturn() throws TException { + int port = super.getPort(); + this.startAsyncThreadedSelectorServer(port); + this.syncFramedClientNoReturn(port); + this.waitAndAssertTracesClientSyncServerAsync(port, "noReturn", 1); + } + + @Test + public void syncFramedClientAsyncThreadedSelectorServerNoReturnParallel() + throws InterruptedException, TTransportException { + int port = super.getPort(); + this.startAsyncThreadedSelectorServer(port); + int threadCount = 5; + runParallel(threadCount, () -> this.syncFramedClientNoReturn(port)); + this.waitAndAssertTracesClientSyncServerAsync(port, "noReturn", threadCount); + } + + @Test + public void syncFramedClientAsyncMutiThreadedSelectorServerNoReturn() throws TException { + int port = super.getPort(); + this.startMultiThreadedSelectorServer(port); + this.syncFramedClientMultiNoReturn(port); + this.waitAndAssertTracesClientSyncServerSync(port, "noReturn", "syncHelloWorld:noReturn", 1); + } + + @Test + public void syncFramedClientAsyncMutiThreadedSelectorServerNoReturnParallel() + throws InterruptedException, TTransportException { + int port = super.getPort(); + this.startMultiThreadedSelectorServer(port); + int threadCount = 5; + runParallel(threadCount, () -> this.syncFramedClientMultiNoReturn(port)); + this.waitAndAssertTracesClientSyncServerSync( + port, "noReturn", "syncHelloWorld:noReturn", threadCount); + } + + @Test + public void syncFramedClientSyncThreadedSelectorServerNoReturn() throws TException { + int port = super.getPort(); + this.startSyncThreadedSelectorServer(port); + this.syncFramedClientNoReturn(port); + this.waitAndAssertTracesClientSyncServerSync(port, "noReturn", 1); + } + + @Test + public void syncFramedClientSyncThreadedSelectorServerNoReturnParallel() + throws InterruptedException, TTransportException { + int port = super.getPort(); + this.startSyncThreadedSelectorServer(port); + int threadCount = 5; + runParallel(threadCount, () -> this.syncFramedClientNoReturn(port)); + this.waitAndAssertTracesClientSyncServerSync(port, "noReturn", threadCount); + } + + @Test + public void nonBlockClientAsyncThreadedSelectorServerNoReturn() throws TException, IOException { + int port = super.getPort(); + this.startAsyncThreadedSelectorServer(port); + this.nonBlockClientNoReturn(port); + this.waitAndAssertTracesClientAsyncServerAsync(port, "noReturn", 1); + } + + @Test + public void nonBlockClientAsyncThreadedSelectorServerNoReturnParallel() + throws InterruptedException, TTransportException { + int port = super.getPort(); + this.startAsyncThreadedSelectorServer(port); + int threadCount = 5; + runParallel(threadCount, () -> this.nonBlockClientNoReturn(port)); + this.waitAndAssertTracesClientAsyncServerAsync(port, "noReturn", threadCount); + } + + @Test + public void nonBlockClientSyncThreadedSelectorServerNoReturn() throws TException, IOException { + int port = super.getPort(); + this.startSyncThreadedSelectorServer(port); + this.nonBlockClientNoReturn(port); + this.waitAndAssertTracesClientAsyncServerSync(port, "noReturn", 1); + } + + @Test + public void nonBlockClientSyncThreadedSelectorServerNoReturnParallel() + throws InterruptedException, TTransportException { + int port = super.getPort(); + this.startSyncThreadedSelectorServer(port); + int threadCount = 5; + runParallel(threadCount, () -> this.nonBlockClientNoReturn(port)); + this.waitAndAssertTracesClientAsyncServerSync(port, "noReturn", threadCount); + } + + @Test + public void syncFramedClientSyncNonblockingServerNoReturn() throws TException { + int port = super.getPort(); + this.startSyncNonblockingServer(port); + this.syncFramedClientNoReturn(port); + this.waitAndAssertTracesClientSyncServerSync(port, "noReturn", 1); + } + + @Test + public void syncFramedClientSyncNonblockingServerNoReturnParallel() + throws InterruptedException, TTransportException { + int port = super.getPort(); + this.startSyncNonblockingServer(port); + int threadCount = 5; + runParallel(threadCount, () -> this.syncFramedClientNoReturn(port)); + this.waitAndAssertTracesClientSyncServerSync(port, "noReturn", threadCount); + } + + @Test + public void syncFramedClientSyncHsHaServerNoReturn() throws TException { + int port = super.getPort(); + this.startSyncHsHaServer(port); + this.syncFramedClientNoReturn(port); + this.waitAndAssertTracesClientSyncServerSync(port, "noReturn", 1); + } + + @Test + public void syncFramedClientSyncHsHaServerNoReturnParallel() + throws InterruptedException, TTransportException { + int port = super.getPort(); + this.startSyncHsHaServer(port); + int threadCount = 5; + runParallel(threadCount, () -> this.syncFramedClientNoReturn(port)); + this.waitAndAssertTracesClientSyncServerSync(port, "noReturn", threadCount); + } + + @Test + public void syncFramedClientAsyncNonblockingServerNoReturn() throws TException { + int port = super.getPort(); + this.startAsyncNonblockingServer(port); + this.syncFramedClientNoReturn(port); + this.waitAndAssertTracesClientSyncServerAsync(port, "noReturn", 1); + } + + @Test + public void syncFramedClientAsyncNonblockingServerNoReturnParallel() + throws InterruptedException, TTransportException { + int port = super.getPort(); + this.startAsyncNonblockingServer(port); + int threadCount = 5; + runParallel(threadCount, () -> this.syncFramedClientNoReturn(port)); + this.waitAndAssertTracesClientSyncServerAsync(port, "noReturn", threadCount); + } + + @Test + public void syncFramedClientAsyncHsHaServerNoReturn() throws TException { + int port = super.getPort(); + this.startAsyncHsHaServer(port); + this.syncFramedClientNoReturn(port); + this.waitAndAssertTracesClientSyncServerAsync(port, "noReturn", 1); + } + + @Test + public void syncFramedClientAsyncHsHaServerNoReturnParallel() + throws InterruptedException, TTransportException { + int port = super.getPort(); + this.startAsyncHsHaServer(port); + int threadCount = 5; + runParallel(threadCount, () -> this.syncFramedClientNoReturn(port)); + this.waitAndAssertTracesClientSyncServerAsync(port, "noReturn", threadCount); + } + + @Test + public void nonBlockClientSyncNonblockingServerNoReturn() throws TException, IOException { + int port = super.getPort(); + this.startSyncNonblockingServer(port); + this.nonBlockClientNoReturn(port); + this.waitAndAssertTracesClientAsyncServerSync(port, "noReturn", 1); + } + + @Test + public void nonBlockClientSyncNonblockingServerNoReturnParallel() + throws InterruptedException, TTransportException { + int port = super.getPort(); + this.startSyncNonblockingServer(port); + int threadCount = 5; + runParallel(threadCount, () -> this.nonBlockClientNoReturn(port)); + this.waitAndAssertTracesClientAsyncServerSync(port, "noReturn", threadCount); + } + + @Test + public void nonBlockClientSyncHsHaServerNoReturn() throws TException, IOException { + int port = super.getPort(); + this.startSyncHsHaServer(port); + this.nonBlockClientNoReturn(port); + this.waitAndAssertTracesClientAsyncServerSync(port, "noReturn", 1); + } + + @Test + public void nonBlockClientSyncHsHaServerNoReturnParallel() + throws InterruptedException, TTransportException { + int port = super.getPort(); + this.startSyncHsHaServer(port); + int threadCount = 5; + runParallel(threadCount, () -> this.nonBlockClientNoReturn(port)); + this.waitAndAssertTracesClientAsyncServerSync(port, "noReturn", threadCount); + } + + @Test + public void nonBlockClientAsyncNonblockingServerNoReturn() throws TException, IOException { + int port = super.getPort(); + this.startAsyncNonblockingServer(port); + this.nonBlockClientNoReturn(port); + this.waitAndAssertTracesClientAsyncServerAsync(port, "noReturn", 1); + } + + @Test + public void nonBlockClientAsyncNonblockingServerNoReturnParallel() + throws InterruptedException, TTransportException { + int port = super.getPort(); + this.startAsyncNonblockingServer(port); + int threadCount = 5; + runParallel(threadCount, () -> this.nonBlockClientNoReturn(port)); + this.waitAndAssertTracesClientAsyncServerAsync(port, "noReturn", threadCount); + } + + @Test + public void nonBlockClientAsyncHsHaServerNoReturn() throws TException, IOException { + int port = super.getPort(); + this.startAsyncHsHaServer(port); + this.nonBlockClientNoReturn(port); + this.waitAndAssertTracesClientAsyncServerAsync(port, "noReturn", 1); + } + + @Test + public void nonBlockClientAsyncHsHaServerNoReturnParallel() + throws InterruptedException, TTransportException { + int port = super.getPort(); + this.startAsyncHsHaServer(port); + int threadCount = 5; + runParallel(threadCount, () -> this.nonBlockClientNoReturn(port)); + this.waitAndAssertTracesClientAsyncServerAsync(port, "noReturn", threadCount); + } + + public void syncClientNoReturn(int port) throws TException { + TTransport transport = null; + try { + transport = new TSocket("localhost", port); + transport.open(); + TProtocol protocol = new TBinaryProtocol(transport); + ThriftService.Client client = new ThriftService.Client(protocol); + this.testing().runWithSpan("parent", () -> client.noReturn(1)); + } finally { + if (transport != null) { + transport.close(); + } + } + } + + public void syncClientMultiNoReturn(int port) throws TException { + TTransport transport = null; + try { + transport = new TSocket("localhost", port); + transport.open(); + TProtocol protocol = new TBinaryProtocol(transport); + TMultiplexedProtocol multiplexedProtocol = + new TMultiplexedProtocol(protocol, "syncHelloWorld"); + ThriftService.Client client = new ThriftService.Client(multiplexedProtocol); + this.testing().runWithSpan("parent", () -> client.noReturn(1)); + } finally { + if (transport != null) { + transport.close(); + } + } + } + + public void nonBlockClientNoReturn(int port) throws TException, IOException { + TNonblockingTransport transport = new TNonblockingSocket("localhost", port); + TAsyncClientManager clientManager = new TAsyncClientManager(); + TProtocolFactory protocolFactory = new TBinaryProtocol.Factory(); + ThriftService.AsyncClient.Factory factory = + new ThriftService.AsyncClient.Factory(clientManager, protocolFactory); + ThriftService.AsyncClient asyClient = factory.getAsyncClient(transport); + AsyncMethodCallback callback = + new AsyncMethodCallback() { + @Override + public void onComplete(Void s) {} + + @Override + public void onError(Exception e) { + assertThat(e.getMessage()).isEqualTo("Read call frame size failed"); + } + }; + this.testing().runWithSpan("parent", () -> asyClient.noReturn(1, callback)); + } + + public void syncFramedClientNoReturn(int port) throws TException { + TTransport transport = null; + try { + transport = new TSocket("localhost", port); + TFramedTransport framedTransport = new TFramedTransport(transport); + framedTransport.open(); + TProtocol protocol = new TBinaryProtocol(framedTransport); + ThriftService.Client client = new ThriftService.Client(protocol); + this.testing().runWithSpan("parent", () -> client.noReturn(1)); + } finally { + if (transport != null) { + transport.close(); + } + } + } + + public void syncFramedClientMultiNoReturn(int port) throws TException { + TTransport transport = null; + try { + transport = new TSocket("localhost", port); + TFramedTransport framedTransport = new TFramedTransport(transport); + framedTransport.open(); + TProtocol protocol = new TBinaryProtocol(framedTransport); + TMultiplexedProtocol multiplexedProtocol = + new TMultiplexedProtocol(protocol, "syncHelloWorld"); + ThriftService.Client client = new ThriftService.Client(multiplexedProtocol); + this.testing().runWithSpan("parent", () -> client.noReturn(1)); + } finally { + if (transport != null) { + transport.close(); + } + } + } +} diff --git a/instrumentation/thrift/thrift-0.9.1/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/thrift/v0_9_1/OneWayErrorTest.java b/instrumentation/thrift/thrift-0.9.1/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/thrift/v0_9_1/OneWayErrorTest.java new file mode 100644 index 000000000000..5ebe5a853d0b --- /dev/null +++ b/instrumentation/thrift/thrift-0.9.1/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/thrift/v0_9_1/OneWayErrorTest.java @@ -0,0 +1,409 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.instrumentation.thrift.v0_9_1; + +import static org.assertj.core.api.Assertions.assertThat; + +import io.opentelemetry.javaagent.instrumentation.thrift.v0_9_1.thrift.ThriftService; +import java.io.IOException; +import org.apache.thrift.TException; +import org.apache.thrift.async.AsyncMethodCallback; +import org.apache.thrift.async.TAsyncClientManager; +import org.apache.thrift.protocol.TBinaryProtocol; +import org.apache.thrift.protocol.TMultiplexedProtocol; +import org.apache.thrift.protocol.TProtocol; +import org.apache.thrift.protocol.TProtocolFactory; +import org.apache.thrift.transport.TFramedTransport; +import org.apache.thrift.transport.TNonblockingSocket; +import org.apache.thrift.transport.TNonblockingTransport; +import org.apache.thrift.transport.TSocket; +import org.apache.thrift.transport.TTransport; +import org.apache.thrift.transport.TTransportException; +import org.junit.jupiter.api.Test; + +public class OneWayErrorTest extends ThriftBaseTest { + + @Test + public void syncClientSyncSimpleServerOneWayWithError() throws TException { + int port = super.getPort(); + this.startSyncSimpleServer(port); + this.syncClientOneWayWithError(port); + this.waitAndAssertTracesClientSyncServerSyncOnewayError(port, "oneWayWithError", 1); + } + + @Test + public void syncClientSyncSimpleServerOneWayWithErrorParallel() + throws InterruptedException, TTransportException { + int port = super.getPort(); + this.startSyncSimpleServer(port); + int threadCount = 5; + runParallel(threadCount, () -> this.syncClientOneWayWithError(port)); + this.waitAndAssertTracesClientSyncServerSyncOnewayError(port, "oneWayWithError", threadCount); + } + + @Test + public void syncClientSyncThreadPoolServerOneWayWithError() + throws TException, InterruptedException { + int port = super.getPort(); + this.startSyncThreadPoolServer(port); + this.syncClientOneWayWithError(port); + this.waitAndAssertTracesClientSyncServerSyncOnewayError(port, "oneWayWithError", 1); + } + + @Test + public void syncClientSyncThreadPoolServerOneWayWithErrorParallel() + throws InterruptedException, TTransportException { + int port = super.getPort(); + this.startSyncThreadPoolServer(port); + int threadCount = 5; + runParallel(threadCount, () -> this.syncClientOneWayWithError(port)); + this.waitAndAssertTracesClientSyncServerSyncOnewayError(port, "oneWayWithError", threadCount); + } + + @Test + public void syncClientMutiSyncSimpleServerOneWayWithError() throws TException { + int port = super.getPort(); + this.startMultiSimpleServer(port); + this.syncClientMultiOneWayWithError(port); + this.waitAndAssertTracesClientSyncServerSyncOnewayError( + port, "oneWayWithError", "syncHelloWorld:oneWayWithError", 1); + } + + @Test + public void syncClientMutiSyncSimpleServerOneWayWithErrorParallel() + throws InterruptedException, TTransportException { + int port = super.getPort(); + this.startMultiSimpleServer(port); + int threadCount = 5; + runParallel(threadCount, () -> this.syncClientMultiOneWayWithError(port)); + this.waitAndAssertTracesClientSyncServerSyncOnewayError( + port, "oneWayWithError", "syncHelloWorld:oneWayWithError", threadCount); + } + + @Test + public void syncFramedClientAsyncThreadedSelectorServerOneWayWithError() throws TException { + int port = super.getPort(); + this.startAsyncThreadedSelectorServer(port); + this.syncFramedClientOneWayWithError(port); + this.waitAndAssertTracesClientSyncServerAsyncError(port, "oneWayWithError", 1); + } + + @Test + public void syncFramedClientAsyncThreadedSelectorServerOneWayWithErrorParallel() + throws InterruptedException, TTransportException { + int port = super.getPort(); + this.startAsyncThreadedSelectorServer(port); + int threadCount = 5; + runParallel(threadCount, () -> this.syncFramedClientOneWayWithError(port)); + this.waitAndAssertTracesClientSyncServerAsyncError(port, "oneWayWithError", threadCount); + } + + @Test + public void syncFramedClientAsyncMutiThreadedSelectorServerOneWayWithError() throws TException { + int port = super.getPort(); + this.startMultiThreadedSelectorServer(port); + this.syncFramedClientMultiOneWayWithError(port); + this.waitAndAssertTracesClientSyncServerSyncOnewayError( + port, "oneWayWithError", "syncHelloWorld:oneWayWithError", 1); + } + + @Test + public void syncFramedClientAsyncMutiThreadedSelectorServerOneWayWithErrorParallel() + throws InterruptedException, TTransportException { + int port = super.getPort(); + this.startMultiThreadedSelectorServer(port); + int threadCount = 5; + runParallel(threadCount, () -> this.syncFramedClientMultiOneWayWithError(port)); + this.waitAndAssertTracesClientSyncServerSyncOnewayError( + port, "oneWayWithError", "syncHelloWorld:oneWayWithError", threadCount); + } + + @Test + public void syncFramedClientSyncThreadedSelectorServerOneWayWithError() throws TException { + int port = super.getPort(); + this.startSyncThreadedSelectorServer(port); + this.syncFramedClientOneWayWithError(port); + this.waitAndAssertTracesClientSyncServerSyncOnewayError(port, "oneWayWithError", 1); + } + + @Test + public void syncFramedClientSyncThreadedSelectorServerOneWayWithErrorParallel() + throws InterruptedException, TTransportException { + int port = super.getPort(); + this.startSyncThreadedSelectorServer(port); + int threadCount = 5; + runParallel(threadCount, () -> this.syncFramedClientOneWayWithError(port)); + this.waitAndAssertTracesClientSyncServerSyncOnewayError(port, "oneWayWithError", threadCount); + } + + @Test + public void nonBlockClientAsyncThreadedSelectorServerOneWayWithError() + throws TException, IOException { + int port = super.getPort(); + this.startAsyncThreadedSelectorServer(port); + this.nonBlockClientOneWayWithError(port); + this.waitAndAssertTracesClientAsyncServerAsyncError(port, "oneWayWithError", 1); + } + + @Test + public void nonBlockClientAsyncThreadedSelectorServerOneWayWithErrorParallel() + throws InterruptedException, TTransportException { + int port = super.getPort(); + this.startAsyncThreadedSelectorServer(port); + int threadCount = 5; + runParallel(threadCount, () -> this.nonBlockClientOneWayWithError(port)); + this.waitAndAssertTracesClientAsyncServerAsyncError(port, "oneWayWithError", threadCount); + } + + @Test + public void nonBlockClientSyncThreadedSelectorServerOneWayWithError() + throws TException, IOException { + int port = super.getPort(); + this.startSyncThreadedSelectorServer(port); + this.nonBlockClientOneWayWithError(port); + this.waitAndAssertTracesClientAsyncServerSyncOnewayError(port, "oneWayWithError", 1); + } + + @Test + public void nonBlockClientSyncThreadedSelectorServerOneWayWithErrorParallel() + throws InterruptedException, TTransportException { + int port = super.getPort(); + this.startSyncThreadedSelectorServer(port); + int threadCount = 5; + runParallel(threadCount, () -> this.nonBlockClientOneWayWithError(port)); + this.waitAndAssertTracesClientAsyncServerSyncOnewayError(port, "oneWayWithError", threadCount); + } + + @Test + public void syncFramedClientSyncNonblockingServerOneWayWithError() throws TException { + int port = super.getPort(); + this.startSyncNonblockingServer(port); + this.syncFramedClientOneWayWithError(port); + this.waitAndAssertTracesClientSyncServerSyncOnewayError(port, "oneWayWithError", 1); + } + + @Test + public void syncFramedClientSyncNonblockingServerOneWayWithErrorParallel() + throws InterruptedException, TTransportException { + int port = super.getPort(); + this.startSyncNonblockingServer(port); + int threadCount = 5; + runParallel(threadCount, () -> this.syncFramedClientOneWayWithError(port)); + this.waitAndAssertTracesClientSyncServerSyncOnewayError(port, "oneWayWithError", threadCount); + } + + @Test + public void syncFramedClientSyncHsHaServerOneWayWithError() throws TException { + int port = super.getPort(); + this.startSyncHsHaServer(port); + this.syncFramedClientOneWayWithError(port); + this.waitAndAssertTracesClientSyncServerSyncOnewayError(port, "oneWayWithError", 1); + } + + @Test + public void syncFramedClientSyncHsHaServerOneWayWithErrorParallel() + throws InterruptedException, TTransportException { + int port = super.getPort(); + this.startSyncHsHaServer(port); + int threadCount = 5; + runParallel(threadCount, () -> this.syncFramedClientOneWayWithError(port)); + this.waitAndAssertTracesClientSyncServerSyncOnewayError(port, "oneWayWithError", threadCount); + } + + @Test + public void syncFramedClientAsyncNonblockingServerOneWayWithError() throws TException { + int port = super.getPort(); + this.startAsyncNonblockingServer(port); + this.syncFramedClientOneWayWithError(port); + this.waitAndAssertTracesClientSyncServerAsyncError(port, "oneWayWithError", 1); + } + + @Test + public void syncFramedClientAsyncNonblockingServerOneWayWithErrorParallel() + throws InterruptedException, TTransportException { + int port = super.getPort(); + this.startAsyncNonblockingServer(port); + int threadCount = 5; + runParallel(threadCount, () -> this.syncFramedClientOneWayWithError(port)); + this.waitAndAssertTracesClientSyncServerAsyncError(port, "oneWayWithError", threadCount); + } + + @Test + public void syncFramedClientAsyncHsHaServerOneWayWithError() throws TException { + int port = super.getPort(); + this.startAsyncHsHaServer(port); + this.syncFramedClientOneWayWithError(port); + this.waitAndAssertTracesClientSyncServerAsyncError(port, "oneWayWithError", 1); + } + + @Test + public void syncFramedClientAsyncHsHaServerOneWayWithErrorParallel() + throws InterruptedException, TTransportException { + int port = super.getPort(); + this.startAsyncHsHaServer(port); + int threadCount = 5; + runParallel(threadCount, () -> this.syncFramedClientOneWayWithError(port)); + this.waitAndAssertTracesClientSyncServerAsyncError(port, "oneWayWithError", threadCount); + } + + @Test + public void nonBlockClientSyncNonblockingServerOneWayWithError() throws TException, IOException { + int port = super.getPort(); + this.startSyncNonblockingServer(port); + this.nonBlockClientOneWayWithError(port); + this.waitAndAssertTracesClientAsyncServerSyncOnewayError(port, "oneWayWithError", 1); + } + + @Test + public void nonBlockClientSyncNonblockingServerOneWayWithErrorParallel() + throws InterruptedException, TTransportException { + int port = super.getPort(); + this.startSyncNonblockingServer(port); + int threadCount = 5; + runParallel(threadCount, () -> this.nonBlockClientOneWayWithError(port)); + this.waitAndAssertTracesClientAsyncServerSyncOnewayError(port, "oneWayWithError", threadCount); + } + + @Test + public void nonBlockClientSyncHsHaServerOneWayWithError() throws TException, IOException { + int port = super.getPort(); + this.startSyncHsHaServer(port); + this.nonBlockClientOneWayWithError(port); + this.waitAndAssertTracesClientAsyncServerSyncOnewayError(port, "oneWayWithError", 1); + } + + @Test + public void nonBlockClientSyncHsHaServerOneWayWithErrorParallel() + throws InterruptedException, TTransportException { + int port = super.getPort(); + this.startSyncHsHaServer(port); + int threadCount = 5; + runParallel(threadCount, () -> this.nonBlockClientOneWayWithError(port)); + this.waitAndAssertTracesClientAsyncServerSyncOnewayError(port, "oneWayWithError", threadCount); + } + + @Test + public void nonBlockClientAsyncNonblockingServerOneWayWithError() throws TException, IOException { + int port = super.getPort(); + this.startAsyncNonblockingServer(port); + this.nonBlockClientOneWayWithError(port); + this.waitAndAssertTracesClientAsyncServerAsyncError(port, "oneWayWithError", 1); + } + + @Test + public void nonBlockClientAsyncNonblockingServerOneWayWithErrorParallel() + throws InterruptedException, TTransportException { + int port = super.getPort(); + this.startAsyncNonblockingServer(port); + int threadCount = 5; + runParallel(threadCount, () -> this.nonBlockClientOneWayWithError(port)); + this.waitAndAssertTracesClientAsyncServerAsyncError(port, "oneWayWithError", threadCount); + } + + @Test + public void nonBlockClientAsyncHsHaServerOneWayWithError() throws TException, IOException { + int port = super.getPort(); + this.startAsyncHsHaServer(port); + this.nonBlockClientOneWayWithError(port); + this.waitAndAssertTracesClientAsyncServerAsyncError(port, "oneWayWithError", 1); + } + + @Test + public void nonBlockClientAsyncHsHaServerOneWayWithErrorParallel() + throws InterruptedException, TTransportException { + int port = super.getPort(); + this.startAsyncHsHaServer(port); + int threadCount = 5; + runParallel(threadCount, () -> this.nonBlockClientOneWayWithError(port)); + this.waitAndAssertTracesClientAsyncServerAsyncError(port, "oneWayWithError", threadCount); + } + + public void syncClientOneWayWithError(int port) throws TException { + TTransport transport = null; + try { + transport = new TSocket("localhost", port); + transport.open(); + TProtocol protocol = new TBinaryProtocol(transport); + ThriftService.Client client = new ThriftService.Client(protocol); + this.testing().runWithSpan("parent", () -> client.oneWayWithError()); + } finally { + if (transport != null) { + transport.close(); + } + } + } + + public void syncClientMultiOneWayWithError(int port) throws TException { + TTransport transport = null; + try { + transport = new TSocket("localhost", port); + transport.open(); + TProtocol protocol = new TBinaryProtocol(transport); + TMultiplexedProtocol multiplexedProtocol = + new TMultiplexedProtocol(protocol, "syncHelloWorld"); + ThriftService.Client client = new ThriftService.Client(multiplexedProtocol); + this.testing().runWithSpan("parent", () -> client.oneWayWithError()); + } finally { + if (transport != null) { + transport.close(); + } + } + } + + public void nonBlockClientOneWayWithError(int port) throws TException, IOException { + TNonblockingTransport transport = new TNonblockingSocket("localhost", port); + TAsyncClientManager clientManager = new TAsyncClientManager(); + TProtocolFactory protocolFactory = new TBinaryProtocol.Factory(); + ThriftService.AsyncClient.Factory factory = + new ThriftService.AsyncClient.Factory(clientManager, protocolFactory); + ThriftService.AsyncClient asyClient = factory.getAsyncClient(transport); + AsyncMethodCallback callback = + new AsyncMethodCallback() { + @Override + public void onComplete(Void no) {} + + @Override + public void onError(Exception e) { + assertThat(e.getMessage()).isEqualTo("Read call frame size failed"); + } + }; + this.testing().runWithSpan("parent", () -> asyClient.oneWayWithError(callback)); + } + + public void syncFramedClientOneWayWithError(int port) throws TException { + TTransport transport = null; + try { + transport = new TSocket("localhost", port); + TFramedTransport framedTransport = new TFramedTransport(transport); + framedTransport.open(); + TProtocol protocol = new TBinaryProtocol(framedTransport); + ThriftService.Client client = new ThriftService.Client(protocol); + this.testing().runWithSpan("parent", () -> client.oneWayWithError()); + } finally { + if (transport != null) { + transport.close(); + } + } + } + + public void syncFramedClientMultiOneWayWithError(int port) throws TException { + TTransport transport = null; + try { + transport = new TSocket("localhost", port); + TFramedTransport framedTransport = new TFramedTransport(transport); + framedTransport.open(); + TProtocol protocol = new TBinaryProtocol(framedTransport); + TMultiplexedProtocol multiplexedProtocol = + new TMultiplexedProtocol(protocol, "syncHelloWorld"); + ThriftService.Client client = new ThriftService.Client(multiplexedProtocol); + this.testing().runWithSpan("parent", () -> client.oneWayWithError()); + } finally { + if (transport != null) { + transport.close(); + } + } + } +} diff --git a/instrumentation/thrift/thrift-0.9.1/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/thrift/v0_9_1/OneWayTest.java b/instrumentation/thrift/thrift-0.9.1/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/thrift/v0_9_1/OneWayTest.java new file mode 100644 index 000000000000..08f174c8b894 --- /dev/null +++ b/instrumentation/thrift/thrift-0.9.1/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/thrift/v0_9_1/OneWayTest.java @@ -0,0 +1,404 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.instrumentation.thrift.v0_9_1; + +import static org.assertj.core.api.Assertions.assertThat; + +import io.opentelemetry.javaagent.instrumentation.thrift.v0_9_1.thrift.ThriftService; +import java.io.IOException; +import org.apache.thrift.TException; +import org.apache.thrift.async.AsyncMethodCallback; +import org.apache.thrift.async.TAsyncClientManager; +import org.apache.thrift.protocol.TBinaryProtocol; +import org.apache.thrift.protocol.TMultiplexedProtocol; +import org.apache.thrift.protocol.TProtocol; +import org.apache.thrift.protocol.TProtocolFactory; +import org.apache.thrift.transport.TFramedTransport; +import org.apache.thrift.transport.TNonblockingSocket; +import org.apache.thrift.transport.TNonblockingTransport; +import org.apache.thrift.transport.TSocket; +import org.apache.thrift.transport.TTransport; +import org.apache.thrift.transport.TTransportException; +import org.junit.jupiter.api.Test; + +public class OneWayTest extends ThriftBaseTest { + + @Test + public void syncClientSyncSimpleServerOneWay() throws TException { + int port = super.getPort(); + this.startSyncSimpleServer(port); + this.syncClientOneWay(port); + this.waitAndAssertTracesClientSyncServerSync(port, "oneWay", 1); + } + + @Test + public void syncClientSyncSimpleServerOneWayParallel() + throws InterruptedException, TTransportException { + int port = super.getPort(); + this.startSyncSimpleServer(port); + int threadCount = 5; + runParallel(threadCount, () -> this.syncClientOneWay(port)); + this.waitAndAssertTracesClientSyncServerSync(port, "oneWay", threadCount); + } + + @Test + public void syncClientSyncThreadPoolServerOneWay() throws TException, InterruptedException { + int port = super.getPort(); + this.startSyncThreadPoolServer(port); + this.syncClientOneWay(port); + this.waitAndAssertTracesClientSyncServerSync(port, "oneWay", 1); + } + + @Test + public void syncClientSyncThreadPoolServerOneWayParallel() + throws InterruptedException, TTransportException { + int port = super.getPort(); + this.startSyncThreadPoolServer(port); + int threadCount = 5; + runParallel(threadCount, () -> this.syncClientOneWay(port)); + this.waitAndAssertTracesClientSyncServerSync(port, "oneWay", threadCount); + } + + @Test + public void syncClientMutiSyncSimpleServerOneWay() throws TException { + int port = super.getPort(); + this.startMultiSimpleServer(port); + this.syncClientMultiOneWay(port); + this.waitAndAssertTracesClientSyncServerSync(port, "oneWay", "syncHelloWorld:oneWay", 1); + } + + @Test + public void syncClientMutiSyncSimpleServerOneWayParallel() + throws InterruptedException, TTransportException { + int port = super.getPort(); + this.startMultiSimpleServer(port); + int threadCount = 5; + runParallel(threadCount, () -> this.syncClientMultiOneWay(port)); + this.waitAndAssertTracesClientSyncServerSync( + port, "oneWay", "syncHelloWorld:oneWay", threadCount); + } + + @Test + public void syncFramedClientAsyncThreadedSelectorServerOneWay() throws TException { + int port = super.getPort(); + this.startAsyncThreadedSelectorServer(port); + this.syncFramedClientOneWay(port); + this.waitAndAssertTracesClientSyncServerAsync(port, "oneWay", 1); + } + + @Test + public void syncFramedClientAsyncThreadedSelectorServerOneWayParallel() + throws InterruptedException, TTransportException { + int port = super.getPort(); + this.startAsyncThreadedSelectorServer(port); + int threadCount = 5; + runParallel(threadCount, () -> this.syncFramedClientOneWay(port)); + this.waitAndAssertTracesClientSyncServerAsync(port, "oneWay", threadCount); + } + + @Test + public void syncFramedClientAsyncMutiThreadedSelectorServerOneWay() throws TException { + int port = super.getPort(); + this.startMultiThreadedSelectorServer(port); + this.syncFramedClientMultiOneWay(port); + this.waitAndAssertTracesClientSyncServerSync(port, "oneWay", "syncHelloWorld:oneWay", 1); + } + + @Test + public void syncFramedClientAsyncMutiThreadedSelectorServerOneWayParallel() + throws InterruptedException, TTransportException { + int port = super.getPort(); + this.startMultiThreadedSelectorServer(port); + int threadCount = 5; + runParallel(threadCount, () -> this.syncFramedClientMultiOneWay(port)); + this.waitAndAssertTracesClientSyncServerSync( + port, "oneWay", "syncHelloWorld:oneWay", threadCount); + } + + @Test + public void syncFramedClientSyncThreadedSelectorServerOneWay() throws TException { + int port = super.getPort(); + this.startSyncThreadedSelectorServer(port); + this.syncFramedClientOneWay(port); + this.waitAndAssertTracesClientSyncServerSync(port, "oneWay", 1); + } + + @Test + public void syncFramedClientSyncThreadedSelectorServerOneWayParallel() + throws InterruptedException, TTransportException { + int port = super.getPort(); + this.startSyncThreadedSelectorServer(port); + int threadCount = 5; + runParallel(threadCount, () -> this.syncFramedClientOneWay(port)); + this.waitAndAssertTracesClientSyncServerSync(port, "oneWay", threadCount); + } + + @Test + public void nonBlockClientAsyncThreadedSelectorServerOneWay() throws TException, IOException { + int port = super.getPort(); + this.startAsyncThreadedSelectorServer(port); + this.nonBlockClientOneWay(port); + this.waitAndAssertTracesClientAsyncServerAsync(port, "oneWay", 1); + } + + @Test + public void nonBlockClientAsyncThreadedSelectorServerOneWayParallel() + throws InterruptedException, TTransportException { + int port = super.getPort(); + this.startAsyncThreadedSelectorServer(port); + int threadCount = 5; + runParallel(threadCount, () -> this.nonBlockClientOneWay(port)); + this.waitAndAssertTracesClientAsyncServerAsync(port, "oneWay", threadCount); + } + + @Test + public void nonBlockClientSyncThreadedSelectorServerOneWay() throws TException, IOException { + int port = super.getPort(); + this.startSyncThreadedSelectorServer(port); + this.nonBlockClientOneWay(port); + this.waitAndAssertTracesClientAsyncServerSync(port, "oneWay", 1); + } + + @Test + public void nonBlockClientSyncThreadedSelectorServerOneWayParallel() + throws InterruptedException, TTransportException { + int port = super.getPort(); + this.startSyncThreadedSelectorServer(port); + int threadCount = 5; + runParallel(threadCount, () -> this.nonBlockClientOneWay(port)); + this.waitAndAssertTracesClientAsyncServerSync(port, "oneWay", threadCount); + } + + @Test + public void syncFramedClientSyncNonblockingServerOneWay() throws TException { + int port = super.getPort(); + this.startSyncNonblockingServer(port); + this.syncFramedClientOneWay(port); + this.waitAndAssertTracesClientSyncServerSync(port, "oneWay", 1); + } + + @Test + public void syncFramedClientSyncNonblockingServerOneWayParallel() + throws InterruptedException, TTransportException { + int port = super.getPort(); + this.startSyncNonblockingServer(port); + int threadCount = 5; + runParallel(threadCount, () -> this.syncFramedClientOneWay(port)); + this.waitAndAssertTracesClientSyncServerSync(port, "oneWay", threadCount); + } + + @Test + public void syncFramedClientSyncHsHaServerOneWay() throws TException { + int port = super.getPort(); + this.startSyncHsHaServer(port); + this.syncFramedClientOneWay(port); + this.waitAndAssertTracesClientSyncServerSync(port, "oneWay", 1); + } + + @Test + public void syncFramedClientSyncHsHaServerOneWayParallel() + throws InterruptedException, TTransportException { + int port = super.getPort(); + this.startSyncHsHaServer(port); + int threadCount = 5; + runParallel(threadCount, () -> this.syncFramedClientOneWay(port)); + this.waitAndAssertTracesClientSyncServerSync(port, "oneWay", threadCount); + } + + @Test + public void syncFramedClientAsyncNonblockingServerOneWay() throws TException { + int port = super.getPort(); + this.startAsyncNonblockingServer(port); + this.syncFramedClientOneWay(port); + this.waitAndAssertTracesClientSyncServerAsync(port, "oneWay", 1); + } + + @Test + public void syncFramedClientAsyncNonblockingServerOneWayParallel() + throws InterruptedException, TTransportException { + int port = super.getPort(); + this.startAsyncNonblockingServer(port); + int threadCount = 5; + runParallel(threadCount, () -> this.syncFramedClientOneWay(port)); + this.waitAndAssertTracesClientSyncServerAsync(port, "oneWay", threadCount); + } + + @Test + public void syncFramedClientAsyncHsHaServerOneWay() throws TException { + int port = super.getPort(); + this.startAsyncHsHaServer(port); + this.syncFramedClientOneWay(port); + this.waitAndAssertTracesClientSyncServerAsync(port, "oneWay", 1); + } + + @Test + public void syncFramedClientAsyncHsHaServerOneWayParallel() + throws InterruptedException, TTransportException { + int port = super.getPort(); + this.startAsyncHsHaServer(port); + int threadCount = 5; + runParallel(threadCount, () -> this.syncFramedClientOneWay(port)); + this.waitAndAssertTracesClientSyncServerAsync(port, "oneWay", threadCount); + } + + @Test + public void nonBlockClientSyncNonblockingServerOneWay() throws TException, IOException { + int port = super.getPort(); + this.startSyncNonblockingServer(port); + this.nonBlockClientOneWay(port); + this.waitAndAssertTracesClientAsyncServerSync(port, "oneWay", 1); + } + + @Test + public void nonBlockClientSyncNonblockingServerOneWayParallel() + throws InterruptedException, TTransportException { + int port = super.getPort(); + this.startSyncNonblockingServer(port); + int threadCount = 5; + runParallel(threadCount, () -> this.nonBlockClientOneWay(port)); + this.waitAndAssertTracesClientAsyncServerSync(port, "oneWay", threadCount); + } + + @Test + public void nonBlockClientSyncHsHaServerOneWay() throws TException, IOException { + int port = super.getPort(); + this.startSyncHsHaServer(port); + this.nonBlockClientOneWay(port); + this.waitAndAssertTracesClientAsyncServerSync(port, "oneWay", 1); + } + + @Test + public void nonBlockClientSyncHsHaServerOneWayParallel() + throws InterruptedException, TTransportException { + int port = super.getPort(); + this.startSyncHsHaServer(port); + int threadCount = 5; + runParallel(threadCount, () -> this.nonBlockClientOneWay(port)); + this.waitAndAssertTracesClientAsyncServerSync(port, "oneWay", threadCount); + } + + @Test + public void nonBlockClientAsyncNonblockingServerOneWay() throws TException, IOException { + int port = super.getPort(); + this.startAsyncNonblockingServer(port); + this.nonBlockClientOneWay(port); + this.waitAndAssertTracesClientAsyncServerAsync(port, "oneWay", 1); + } + + @Test + public void nonBlockClientAsyncNonblockingServerOneWayParallel() + throws InterruptedException, TTransportException { + int port = super.getPort(); + this.startAsyncNonblockingServer(port); + int threadCount = 5; + runParallel(threadCount, () -> this.nonBlockClientOneWay(port)); + this.waitAndAssertTracesClientAsyncServerAsync(port, "oneWay", threadCount); + } + + @Test + public void nonBlockClientAsyncHsHaServerOneWay() throws TException, IOException { + int port = super.getPort(); + this.startAsyncHsHaServer(port); + this.nonBlockClientOneWay(port); + this.waitAndAssertTracesClientAsyncServerAsync(port, "oneWay", 1); + } + + @Test + public void nonBlockClientAsyncHsHaServerOneWayParallel() + throws InterruptedException, TTransportException { + int port = super.getPort(); + this.startAsyncHsHaServer(port); + int threadCount = 5; + runParallel(threadCount, () -> this.nonBlockClientOneWay(port)); + this.waitAndAssertTracesClientAsyncServerAsync(port, "oneWay", threadCount); + } + + public void syncClientOneWay(int port) throws TException { + TTransport transport = null; + try { + transport = new TSocket("localhost", port); + transport.open(); + TProtocol protocol = new TBinaryProtocol(transport); + ThriftService.Client client = new ThriftService.Client(protocol); + this.testing().runWithSpan("parent", () -> client.oneWay()); + } finally { + if (transport != null) { + transport.close(); + } + } + } + + public void syncClientMultiOneWay(int port) throws TException { + TTransport transport = null; + try { + transport = new TSocket("localhost", port); + transport.open(); + TProtocol protocol = new TBinaryProtocol(transport); + TMultiplexedProtocol multiplexedProtocol = + new TMultiplexedProtocol(protocol, "syncHelloWorld"); + ThriftService.Client client = new ThriftService.Client(multiplexedProtocol); + this.testing().runWithSpan("parent", () -> client.oneWay()); + } finally { + if (transport != null) { + transport.close(); + } + } + } + + public void nonBlockClientOneWay(int port) throws TException, IOException { + TNonblockingTransport transport = new TNonblockingSocket("localhost", port); + TAsyncClientManager clientManager = new TAsyncClientManager(); + TProtocolFactory protocolFactory = new TBinaryProtocol.Factory(); + ThriftService.AsyncClient.Factory factory = + new ThriftService.AsyncClient.Factory(clientManager, protocolFactory); + ThriftService.AsyncClient asyClient = factory.getAsyncClient(transport); + AsyncMethodCallback callback = + new AsyncMethodCallback() { + @Override + public void onComplete(Void result) {} + + @Override + public void onError(Exception e) { + assertThat(e.getMessage()).isEqualTo("Read call frame size failed"); + } + }; + this.testing().runWithSpan("parent", () -> asyClient.oneWay(callback)); + } + + public void syncFramedClientOneWay(int port) throws TException { + TTransport transport = null; + try { + transport = new TSocket("localhost", port); + TFramedTransport framedTransport = new TFramedTransport(transport); + framedTransport.open(); + TProtocol protocol = new TBinaryProtocol(framedTransport); + ThriftService.Client client = new ThriftService.Client(protocol); + this.testing().runWithSpan("parent", () -> client.oneWay()); + } finally { + if (transport != null) { + transport.close(); + } + } + } + + public void syncFramedClientMultiOneWay(int port) throws TException { + TTransport transport = null; + try { + transport = new TSocket("localhost", port); + TFramedTransport framedTransport = new TFramedTransport(transport); + framedTransport.open(); + TProtocol protocol = new TBinaryProtocol(framedTransport); + TMultiplexedProtocol multiplexedProtocol = + new TMultiplexedProtocol(protocol, "syncHelloWorld"); + ThriftService.Client client = new ThriftService.Client(multiplexedProtocol); + this.testing().runWithSpan("parent", () -> client.oneWay()); + } finally { + if (transport != null) { + transport.close(); + } + } + } +} diff --git a/instrumentation/thrift/thrift-0.9.1/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/thrift/v0_9_1/SayHelloTest.java b/instrumentation/thrift/thrift-0.9.1/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/thrift/v0_9_1/SayHelloTest.java new file mode 100644 index 000000000000..724a73ce7d4d --- /dev/null +++ b/instrumentation/thrift/thrift-0.9.1/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/thrift/v0_9_1/SayHelloTest.java @@ -0,0 +1,410 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.instrumentation.thrift.v0_9_1; + +import static org.assertj.core.api.Assertions.assertThat; + +import io.opentelemetry.javaagent.instrumentation.thrift.v0_9_1.thrift.ThriftService; +import java.io.IOException; +import org.apache.thrift.TException; +import org.apache.thrift.async.AsyncMethodCallback; +import org.apache.thrift.async.TAsyncClientManager; +import org.apache.thrift.protocol.TBinaryProtocol; +import org.apache.thrift.protocol.TMultiplexedProtocol; +import org.apache.thrift.protocol.TProtocol; +import org.apache.thrift.protocol.TProtocolFactory; +import org.apache.thrift.transport.TFramedTransport; +import org.apache.thrift.transport.TNonblockingSocket; +import org.apache.thrift.transport.TNonblockingTransport; +import org.apache.thrift.transport.TSocket; +import org.apache.thrift.transport.TTransport; +import org.apache.thrift.transport.TTransportException; +import org.junit.jupiter.api.Test; + +public class SayHelloTest extends ThriftBaseTest { + + @Test + public void syncClientSyncSimpleServerSayHello() throws TException { + int port = super.getPort(); + this.startSyncSimpleServer(port); + this.syncClientSayHello(port); + this.waitAndAssertTracesClientSyncServerSync(port, "sayHello", 1); + } + + @Test + public void syncClientSyncSimpleServerSayHelloParallel() + throws InterruptedException, TTransportException { + int port = super.getPort(); + this.startSyncSimpleServer(port); + int threadCount = 5; + runParallel(threadCount, () -> this.syncClientSayHello(port)); + this.waitAndAssertTracesClientSyncServerSync(port, "sayHello", threadCount); + } + + @Test + public void syncClientSyncThreadPoolServerSayHello() throws TException, InterruptedException { + int port = super.getPort(); + this.startSyncThreadPoolServer(port); + this.syncClientSayHello(port); + this.waitAndAssertTracesClientSyncServerSync(port, "sayHello", 1); + } + + @Test + public void syncClientSyncThreadPoolServerSayHelloParallel() + throws InterruptedException, TTransportException { + int port = super.getPort(); + this.startSyncThreadPoolServer(port); + int threadCount = 5; + runParallel(threadCount, () -> this.syncClientSayHello(port)); + this.waitAndAssertTracesClientSyncServerSync(port, "sayHello", threadCount); + } + + @Test + public void syncClientMutiSyncSimpleServerSayHello() throws TException { + int port = super.getPort(); + this.startMultiSimpleServer(port); + this.syncClientMultiSayHello(port); + this.waitAndAssertTracesClientSyncServerSync(port, "sayHello", "syncHelloWorld:sayHello", 1); + } + + @Test + public void syncClientMutiSyncSimpleServerSayHelloParallel() + throws InterruptedException, TTransportException { + int port = super.getPort(); + this.startMultiSimpleServer(port); + int threadCount = 5; + runParallel(threadCount, () -> this.syncClientMultiSayHello(port)); + this.waitAndAssertTracesClientSyncServerSync( + port, "sayHello", "syncHelloWorld:sayHello", threadCount); + } + + @Test + public void syncFramedClientAsyncThreadedSelectorServerSayHello() throws TException { + int port = super.getPort(); + this.startAsyncThreadedSelectorServer(port); + this.syncFramedClientSayHello(port); + this.waitAndAssertTracesClientSyncServerAsync(port, "sayHello", 1); + } + + @Test + public void syncFramedClientAsyncThreadedSelectorServerSayHelloParallel() + throws InterruptedException, TTransportException { + int port = super.getPort(); + this.startAsyncThreadedSelectorServer(port); + int threadCount = 5; + runParallel(threadCount, () -> this.syncFramedClientSayHello(port)); + this.waitAndAssertTracesClientSyncServerAsync(port, "sayHello", threadCount); + } + + @Test + public void syncFramedClientAsyncMutiThreadedSelectorServerSayHello() throws TException { + int port = super.getPort(); + this.startMultiThreadedSelectorServer(port); + this.syncFramedClientMultiSayHello(port); + this.waitAndAssertTracesClientSyncServerSync(port, "sayHello", "syncHelloWorld:sayHello", 1); + } + + @Test + public void syncFramedClientAsyncMutiThreadedSelectorServerSayHelloParallel() + throws InterruptedException, TTransportException { + int port = super.getPort(); + this.startMultiThreadedSelectorServer(port); + int threadCount = 5; + runParallel(threadCount, () -> this.syncFramedClientMultiSayHello(port)); + this.waitAndAssertTracesClientSyncServerSync( + port, "sayHello", "syncHelloWorld:sayHello", threadCount); + } + + @Test + public void syncFramedClientSyncThreadedSelectorServerSayHello() throws TException { + int port = super.getPort(); + this.startSyncThreadedSelectorServer(port); + this.syncFramedClientSayHello(port); + this.waitAndAssertTracesClientSyncServerSync(port, "sayHello", 1); + } + + @Test + public void syncFramedClientSyncThreadedSelectorServerSayHelloParallel() + throws InterruptedException, TTransportException { + int port = super.getPort(); + this.startSyncThreadedSelectorServer(port); + int threadCount = 5; + runParallel(threadCount, () -> this.syncFramedClientSayHello(port)); + this.waitAndAssertTracesClientSyncServerSync(port, "sayHello", threadCount); + } + + @Test + public void nonBlockClientAsyncThreadedSelectorServerSayHello() throws TException, IOException { + int port = super.getPort(); + this.startAsyncThreadedSelectorServer(port); + this.nonBlockClientSayHello(port); + this.waitAndAssertTracesClientAsyncServerAsync(port, "sayHello", 1); + } + + @Test + public void nonBlockClientAsyncThreadedSelectorServerSayHelloParallel() + throws InterruptedException, TTransportException { + int port = super.getPort(); + this.startAsyncThreadedSelectorServer(port); + int threadCount = 5; + runParallel(threadCount, () -> this.nonBlockClientSayHello(port)); + this.waitAndAssertTracesClientAsyncServerAsync(port, "sayHello", threadCount); + } + + @Test + public void nonBlockClientSyncThreadedSelectorServerSayHello() throws TException, IOException { + int port = super.getPort(); + this.startSyncThreadedSelectorServer(port); + this.nonBlockClientSayHello(port); + this.waitAndAssertTracesClientAsyncServerSync(port, "sayHello", 1); + } + + @Test + public void nonBlockClientSyncThreadedSelectorServerSayHelloParallel() + throws InterruptedException, TTransportException { + int port = super.getPort(); + this.startSyncThreadedSelectorServer(port); + int threadCount = 5; + runParallel(threadCount, () -> this.nonBlockClientSayHello(port)); + this.waitAndAssertTracesClientAsyncServerSync(port, "sayHello", threadCount); + } + + @Test + public void syncFramedClientSyncNonblockingServerSayHello() throws TException { + int port = super.getPort(); + this.startSyncNonblockingServer(port); + this.syncFramedClientSayHello(port); + this.waitAndAssertTracesClientSyncServerSync(port, "sayHello", 1); + } + + @Test + public void syncFramedClientSyncNonblockingServerSayHelloParallel() + throws InterruptedException, TTransportException { + int port = super.getPort(); + this.startSyncNonblockingServer(port); + int threadCount = 5; + runParallel(threadCount, () -> this.syncFramedClientSayHello(port)); + this.waitAndAssertTracesClientSyncServerSync(port, "sayHello", threadCount); + } + + @Test + public void syncFramedClientSyncHsHaServerSayHello() throws TException { + int port = super.getPort(); + this.startSyncHsHaServer(port); + this.syncFramedClientSayHello(port); + this.waitAndAssertTracesClientSyncServerSync(port, "sayHello", 1); + } + + @Test + public void syncFramedClientSyncHsHaServerSayHelloParallel() + throws InterruptedException, TTransportException { + int port = super.getPort(); + this.startSyncHsHaServer(port); + int threadCount = 5; + runParallel(threadCount, () -> this.syncFramedClientSayHello(port)); + this.waitAndAssertTracesClientSyncServerSync(port, "sayHello", threadCount); + } + + @Test + public void syncFramedClientAsyncNonblockingServerSayHello() throws TException { + int port = super.getPort(); + this.startAsyncNonblockingServer(port); + this.syncFramedClientSayHello(port); + this.waitAndAssertTracesClientSyncServerAsync(port, "sayHello", 1); + } + + @Test + public void syncFramedClientAsyncNonblockingServerSayHelloParallel() + throws InterruptedException, TTransportException { + int port = super.getPort(); + this.startAsyncNonblockingServer(port); + int threadCount = 5; + runParallel(threadCount, () -> this.syncFramedClientSayHello(port)); + this.waitAndAssertTracesClientSyncServerAsync(port, "sayHello", threadCount); + } + + @Test + public void syncFramedClientAsyncHsHaServerSayHello() throws TException { + int port = super.getPort(); + this.startAsyncHsHaServer(port); + this.syncFramedClientSayHello(port); + this.waitAndAssertTracesClientSyncServerAsync(port, "sayHello", 1); + } + + @Test + public void syncFramedClientAsyncHsHaServerSayHelloParallel() + throws InterruptedException, TTransportException { + int port = super.getPort(); + this.startAsyncHsHaServer(port); + int threadCount = 5; + runParallel(threadCount, () -> this.syncFramedClientSayHello(port)); + this.waitAndAssertTracesClientSyncServerAsync(port, "sayHello", threadCount); + } + + @Test + public void nonBlockClientSyncNonblockingServerSayHello() throws TException, IOException { + int port = super.getPort(); + this.startSyncNonblockingServer(port); + this.nonBlockClientSayHello(port); + this.waitAndAssertTracesClientAsyncServerSync(port, "sayHello", 1); + } + + @Test + public void nonBlockClientSyncNonblockingServerSayHelloParallel() + throws InterruptedException, TTransportException { + int port = super.getPort(); + this.startSyncNonblockingServer(port); + int threadCount = 5; + runParallel(threadCount, () -> this.nonBlockClientSayHello(port)); + this.waitAndAssertTracesClientAsyncServerSync(port, "sayHello", threadCount); + } + + @Test + public void nonBlockClientSyncHsHaServerSayHello() throws TException, IOException { + int port = super.getPort(); + this.startSyncHsHaServer(port); + this.nonBlockClientSayHello(port); + this.waitAndAssertTracesClientAsyncServerSync(port, "sayHello", 1); + } + + @Test + public void nonBlockClientSyncHsHaServerSayHelloParallel() + throws InterruptedException, TTransportException { + int port = super.getPort(); + this.startSyncHsHaServer(port); + int threadCount = 5; + runParallel(threadCount, () -> this.nonBlockClientSayHello(port)); + this.waitAndAssertTracesClientAsyncServerSync(port, "sayHello", threadCount); + } + + @Test + public void nonBlockClientAsyncNonblockingServerSayHello() throws TException, IOException { + int port = super.getPort(); + this.startAsyncNonblockingServer(port); + this.nonBlockClientSayHello(port); + this.waitAndAssertTracesClientAsyncServerAsync(port, "sayHello", 1); + } + + @Test + public void nonBlockClientAsyncNonblockingServerSayHelloParallel() + throws InterruptedException, TTransportException { + int port = super.getPort(); + this.startAsyncNonblockingServer(port); + int threadCount = 5; + runParallel(threadCount, () -> this.nonBlockClientSayHello(port)); + this.waitAndAssertTracesClientAsyncServerAsync(port, "sayHello", threadCount); + } + + @Test + public void nonBlockClientAsyncHsHaServerSayHello() throws TException, IOException { + int port = super.getPort(); + this.startAsyncHsHaServer(port); + this.nonBlockClientSayHello(port); + this.waitAndAssertTracesClientAsyncServerAsync(port, "sayHello", 1); + } + + @Test + public void nonBlockClientAsyncHsHaServerSayHelloParallel() + throws InterruptedException, TTransportException { + int port = super.getPort(); + this.startAsyncHsHaServer(port); + int threadCount = 5; + runParallel(threadCount, () -> this.nonBlockClientSayHello(port)); + this.waitAndAssertTracesClientAsyncServerAsync(port, "sayHello", threadCount); + } + + public void syncClientSayHello(int port) throws TException { + TTransport transport = null; + try { + transport = new TSocket("localhost", port); + transport.open(); + TProtocol protocol = new TBinaryProtocol(transport); + ThriftService.Client client = new ThriftService.Client(protocol); + String response = this.testing().runWithSpan("parent", () -> client.sayHello("US", "Bob")); + assertThat(response).isEqualTo("Hello USs' Bob"); + } finally { + if (transport != null) { + transport.close(); + } + } + } + + public void syncClientMultiSayHello(int port) throws TException { + TTransport transport = null; + try { + transport = new TSocket("localhost", port); + transport.open(); + TProtocol protocol = new TBinaryProtocol(transport); + TMultiplexedProtocol multiplexedProtocol = + new TMultiplexedProtocol(protocol, "syncHelloWorld"); + ThriftService.Client client = new ThriftService.Client(multiplexedProtocol); + String response = this.testing().runWithSpan("parent", () -> client.sayHello("US", "Bob")); + assertThat(response).isEqualTo("Hello USs' Bob"); + } finally { + if (transport != null) { + transport.close(); + } + } + } + + public void nonBlockClientSayHello(int port) throws TException, IOException { + TNonblockingTransport transport = new TNonblockingSocket("localhost", port); + TAsyncClientManager clientManager = new TAsyncClientManager(); + TProtocolFactory protocolFactory = new TBinaryProtocol.Factory(); + ThriftService.AsyncClient.Factory factory = + new ThriftService.AsyncClient.Factory(clientManager, protocolFactory); + ThriftService.AsyncClient asyClient = factory.getAsyncClient(transport); + AsyncMethodCallback callback = + new AsyncMethodCallback() { + @Override + public void onComplete(String result) { + assertThat(result).isEqualTo("Hello USs' Bob"); + } + + @Override + public void onError(Exception e) { + assertThat(e.getMessage()).isEqualTo("Read call frame size failed"); + } + }; + this.testing().runWithSpan("parent", () -> asyClient.sayHello("US", "Bob", callback)); + } + + public void syncFramedClientSayHello(int port) throws TException { + TTransport transport = null; + try { + transport = new TSocket("localhost", port); + TFramedTransport framedTransport = new TFramedTransport(transport); + framedTransport.open(); + TProtocol protocol = new TBinaryProtocol(framedTransport); + ThriftService.Client client = new ThriftService.Client(protocol); + String response = this.testing().runWithSpan("parent", () -> client.sayHello("US", "Bob")); + assertThat(response).isEqualTo("Hello USs' Bob"); + } finally { + if (transport != null) { + transport.close(); + } + } + } + + public void syncFramedClientMultiSayHello(int port) throws TException { + TTransport transport = null; + try { + transport = new TSocket("localhost", port); + TFramedTransport framedTransport = new TFramedTransport(transport); + framedTransport.open(); + TProtocol protocol = new TBinaryProtocol(framedTransport); + TMultiplexedProtocol multiplexedProtocol = + new TMultiplexedProtocol(protocol, "syncHelloWorld"); + ThriftService.Client client = new ThriftService.Client(multiplexedProtocol); + String response = this.testing().runWithSpan("parent", () -> client.sayHello("US", "Bob")); + assertThat(response).isEqualTo("Hello USs' Bob"); + } finally { + if (transport != null) { + transport.close(); + } + } + } +} diff --git a/instrumentation/thrift/thrift-0.9.1/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/thrift/v0_9_1/ThriftBaseTest.java b/instrumentation/thrift/thrift-0.9.1/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/thrift/v0_9_1/ThriftBaseTest.java new file mode 100644 index 000000000000..82a598710a52 --- /dev/null +++ b/instrumentation/thrift/thrift-0.9.1/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/thrift/v0_9_1/ThriftBaseTest.java @@ -0,0 +1,708 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.instrumentation.thrift.v0_9_1; + +import static io.opentelemetry.api.common.AttributeKey.stringKey; +import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.equalTo; +import static io.opentelemetry.sdk.testing.assertj.OpenTelemetryAssertions.satisfies; +import static java.util.concurrent.TimeUnit.SECONDS; +import static org.assertj.core.api.Assertions.assertThat; + +import io.opentelemetry.api.common.AttributeKey; +import io.opentelemetry.api.trace.SpanKind; +import io.opentelemetry.instrumentation.testing.junit.AgentInstrumentationExtension; +import io.opentelemetry.instrumentation.testing.junit.InstrumentationExtension; +import io.opentelemetry.javaagent.instrumentation.thrift.v0_9_1.server.ThriftServiceAsyncImpl; +import io.opentelemetry.javaagent.instrumentation.thrift.v0_9_1.server.ThriftServiceImpl; +import io.opentelemetry.javaagent.instrumentation.thrift.v0_9_1.thrift.ThriftService; +import io.opentelemetry.sdk.testing.assertj.SpanDataAssert; +import io.opentelemetry.sdk.testing.assertj.TraceAssert; +import io.opentelemetry.sdk.trace.data.StatusData; +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStreamReader; +import java.nio.charset.Charset; +import java.util.Random; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.function.Consumer; +import org.apache.thrift.TMultiplexedProcessor; +import org.apache.thrift.protocol.TBinaryProtocol; +import org.apache.thrift.server.THsHaServer; +import org.apache.thrift.server.TNonblockingServer; +import org.apache.thrift.server.TServer; +import org.apache.thrift.server.TSimpleServer; +import org.apache.thrift.server.TThreadPoolServer; +import org.apache.thrift.server.TThreadedSelectorServer; +import org.apache.thrift.transport.TFramedTransport; +import org.apache.thrift.transport.TNonblockingServerSocket; +import org.apache.thrift.transport.TNonblockingServerTransport; +import org.apache.thrift.transport.TServerSocket; +import org.apache.thrift.transport.TServerTransport; +import org.apache.thrift.transport.TTransportException; +import org.assertj.core.api.AbstractAssert; +import org.assertj.core.api.Assertions; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.extension.RegisterExtension; +import org.testcontainers.shaded.com.google.common.base.VerifyException; + +public abstract class ThriftBaseTest { + public TServer server; + public int port = 13100; + + private static final String ASYNC_CLIENT = + "io.opentelemetry.javaagent.instrumentation.thrift.v0_9_1.thrift.ThriftService$AsyncClient"; + private static final String SYNC_CLIENT = + "io.opentelemetry.javaagent.instrumentation.thrift.v0_9_1.thrift.ThriftService$Client"; + private static final String ASYNC_SERVER = + "io.opentelemetry.javaagent.instrumentation.thrift.v0_9_1.server.ThriftServiceAsyncImpl"; + private static final String SYNC_SERVER = + "io.opentelemetry.javaagent.instrumentation.thrift.v0_9_1.server.ThriftServiceImpl"; + private static final String PEER_ADDR = "127.0.0.1"; + + private static final String TRANSPORT_EXCEPTION = + "org.apache.thrift.transport.TTransportException"; + private static final String VERIFY_EXCEPTION = "org.apache.thrift.transport.TTransportException"; + private static final String IO_EXCEPTION = "java.io.IOException"; + + private static final AttributeKey RPC_METHOD = AttributeKey.stringKey("rpc.method"); + private static final AttributeKey RPC_SERVICE = AttributeKey.stringKey("rpc.service"); + private static final AttributeKey RPC_SYSTEM = AttributeKey.stringKey("rpc.system"); + + private static final String EXCEPTION_EVENT_NAME = "exception"; + private static final AttributeKey EXCEPTION_MESSAGE = + AttributeKey.stringKey("exception.message"); + private static final AttributeKey EXCEPTION_TYPE = + AttributeKey.stringKey("exception.type"); + + public static final AttributeKey NETWORK_PEER_ADDRESS = + AttributeKey.stringKey("network.peer.address"); + public static final AttributeKey NETWORK_PEER_PORT = + AttributeKey.longKey("network.peer.port"); + + @RegisterExtension + protected static InstrumentationExtension testing = AgentInstrumentationExtension.create(); + + protected InstrumentationExtension testing() { + return testing; + } + + @BeforeEach + public void before() { + ++this.port; + this.testing().clearData(); + } + + @AfterEach + public void after() { + this.stopServer(); + } + + public int getPort() { + Random random = new Random(); + int newPort = this.port + random.nextInt(2000); + while (portNotRelease(newPort)) { + newPort = this.port + random.nextInt(2000); + } + return newPort; + } + + @FunctionalInterface + protected interface CheckedRunnable { + void run() throws Exception; + } + + protected void runParallel(int threadCount, CheckedRunnable action) throws InterruptedException { + AtomicInteger failCount = new AtomicInteger(0); + CountDownLatch latch = new CountDownLatch(threadCount); + for (int i = 0; i < threadCount; ++i) { + new Thread( + () -> { + try { + action.run(); + } catch (Exception e) { + failCount.incrementAndGet(); + Assertions.fail("parallel test failed: " + e.getMessage()); + } finally { + latch.countDown(); + } + }) + .start(); + } + latch.await(10L, SECONDS); + assertThat(failCount.get()).isEqualTo(0); + } + + public static boolean portNotRelease(int port) { + Process process = null; + String pid = null; + try { + process = Runtime.getRuntime().exec("lsof -ti:" + port); + BufferedReader reader = + new BufferedReader( + new InputStreamReader(process.getInputStream(), Charset.defaultCharset())); + pid = reader.readLine(); + } catch (IOException e) { + throw new VerifyException(e); + } + return pid != null && !pid.isEmpty(); + } + + public void startSyncSimpleServer(int port) throws TTransportException { + ThriftServiceImpl impl = new ThriftServiceImpl(); + ThriftService.Processor processor = + new ThriftService.Processor(impl); + TServerTransport serverTransport = new TServerSocket(port); + this.server = new TSimpleServer(new TServer.Args(serverTransport).processor(processor)); + new Thread(() -> this.server.serve()).start(); + } + + public void startMultiSimpleServer(int port) throws TTransportException { + ThriftServiceImpl syncImpl = new ThriftServiceImpl(); + ThriftService.Processor syncProcessor = + new ThriftService.Processor(syncImpl); + ThriftServiceAsyncImpl asyncImpl = new ThriftServiceAsyncImpl(); + ThriftService.AsyncProcessor asyncProcessor = + new ThriftService.AsyncProcessor(asyncImpl); + TMultiplexedProcessor multiplexedProcessor = new TMultiplexedProcessor(); + multiplexedProcessor.registerProcessor("syncHelloWorld", syncProcessor); + multiplexedProcessor.registerProcessor("asyncHelloWorld", asyncProcessor); + TServerTransport serverTransport = new TServerSocket(port); + this.server = + new TSimpleServer(new TServer.Args(serverTransport).processor(multiplexedProcessor)); + new Thread(() -> this.server.serve()).start(); + } + + public void startMultiThreadedSelectorServer(int port) throws TTransportException { + ThriftServiceImpl syncImpl = new ThriftServiceImpl(); + ThriftService.Processor syncProcessor = + new ThriftService.Processor(syncImpl); + ThriftServiceAsyncImpl asyncImpl = new ThriftServiceAsyncImpl(); + ThriftService.AsyncProcessor asyncProcessor = + new ThriftService.AsyncProcessor(asyncImpl); + TMultiplexedProcessor multiplexedProcessor = new TMultiplexedProcessor(); + multiplexedProcessor.registerProcessor("syncHelloWorld", syncProcessor); + multiplexedProcessor.registerProcessor("asyncHelloWorld", asyncProcessor); + TNonblockingServerTransport transport = new TNonblockingServerSocket(port); + TThreadedSelectorServer.Args serverArgs = + new TThreadedSelectorServer.Args(transport) + .selectorThreads(5) + .workerThreads(10) + .acceptQueueSizePerThread(20) + .processor(multiplexedProcessor); + this.server = new TThreadedSelectorServer(serverArgs); + new Thread(() -> this.server.serve()).start(); + } + + public void startSyncThreadedSelectorServer(int port) throws TTransportException { + ThriftServiceImpl impl = new ThriftServiceImpl(); + ThriftService.Processor processor = + new ThriftService.Processor(impl); + TNonblockingServerTransport transport = new TNonblockingServerSocket(port); + TThreadedSelectorServer.Args serverArgs = + new TThreadedSelectorServer.Args(transport) + .selectorThreads(5) + .workerThreads(10) + .acceptQueueSizePerThread(20) + .processor(processor); + this.server = new TThreadedSelectorServer(serverArgs); + new Thread(() -> this.server.serve()).start(); + } + + public void startAsyncThreadedSelectorServer(int port) throws TTransportException { + ThriftServiceAsyncImpl impl = new ThriftServiceAsyncImpl(); + ThriftService.AsyncProcessor processor = + new ThriftService.AsyncProcessor(impl); + TNonblockingServerTransport transport = new TNonblockingServerSocket(port); + TThreadedSelectorServer.Args serverArgs = + new TThreadedSelectorServer.Args(transport) + .selectorThreads(5) + .workerThreads(10) + .acceptQueueSizePerThread(20) + .processor(processor); + this.server = new TThreadedSelectorServer(serverArgs); + new Thread(() -> this.server.serve()).start(); + } + + public void startSyncNonblockingServer(int port) throws TTransportException { + ThriftServiceImpl impl = new ThriftServiceImpl(); + ThriftService.Processor processor = + new ThriftService.Processor(impl); + TNonblockingServerTransport transport = new TNonblockingServerSocket(port); + TNonblockingServer.Args serverArgs = + new TNonblockingServer.Args(transport).processor(processor); + this.server = new TNonblockingServer(serverArgs); + new Thread(() -> this.server.serve()).start(); + } + + public void startAsyncNonblockingServer(int port) throws TTransportException { + ThriftServiceAsyncImpl impl = new ThriftServiceAsyncImpl(); + ThriftService.AsyncProcessor processor = + new ThriftService.AsyncProcessor(impl); + TNonblockingServerTransport transport = new TNonblockingServerSocket(port); + TNonblockingServer.Args serverArgs = + new TNonblockingServer.Args(transport).processor(processor); + this.server = new TNonblockingServer(serverArgs); + new Thread(() -> this.server.serve()).start(); + } + + public void startSyncThreadPoolServer(int port) throws TTransportException { + ThriftServiceImpl impl = new ThriftServiceImpl(); + ThriftService.Processor processor = + new ThriftService.Processor(impl); + TServerSocket transport = new TServerSocket(port); + ExecutorService executor = Executors.newFixedThreadPool(5); + TThreadPoolServer.Args serverArgs = + new TThreadPoolServer.Args(transport).executorService(executor).processor(processor); + TServer server = new TThreadPoolServer(serverArgs); + new Thread(() -> server.serve()).start(); + } + + public void startSyncHsHaServer(int port) throws TTransportException { + ThriftServiceImpl impl = new ThriftServiceImpl(); + ThriftService.Processor processor = + new ThriftService.Processor(impl); + TNonblockingServerTransport transport = new TNonblockingServerSocket(port); + TBinaryProtocol.Factory protocolFactory = new TBinaryProtocol.Factory(); + TFramedTransport.Factory transportFactory = new TFramedTransport.Factory(); + THsHaServer.Args serverArgs = + new THsHaServer.Args(transport) + .processor(processor) + .protocolFactory(protocolFactory) + .transportFactory(transportFactory); + this.server = new THsHaServer(serverArgs); + new Thread(() -> this.server.serve()).start(); + } + + public void startAsyncHsHaServer(int port) throws TTransportException { + ThriftServiceAsyncImpl impl = new ThriftServiceAsyncImpl(); + ThriftService.AsyncProcessor processor = + new ThriftService.AsyncProcessor(impl); + TNonblockingServerTransport transport = new TNonblockingServerSocket(port); + TBinaryProtocol.Factory protocolFactory = new TBinaryProtocol.Factory(); + TFramedTransport.Factory transportFactory = new TFramedTransport.Factory(); + THsHaServer.Args serverArgs = + new THsHaServer.Args(transport) + .processor(processor) + .protocolFactory(protocolFactory) + .transportFactory(transportFactory); + this.server = new THsHaServer(serverArgs); + new Thread(() -> this.server.serve()).start(); + } + + public void stopServer() { + if (this.server != null) { + this.server.stop(); + } + } + + public void waitAndAssertTracesClientSyncServerSync(int peerPort, String method, int count) { + this.baseWaitAndAssertTraces( + method, + method, + count, + SYNC_CLIENT, + StatusData.unset(), + peerPort, + PEER_ADDR, + null, + null, + SYNC_SERVER, + StatusData.unset(), + null, + null); + } + + public void waitAndAssertTracesClientSyncServerSync( + int peerPort, String clientMethod, String serverMethod, int count) { + this.baseWaitAndAssertTraces( + clientMethod, + serverMethod, + count, + SYNC_CLIENT, + StatusData.unset(), + peerPort, + PEER_ADDR, + null, + null, + SYNC_SERVER, + StatusData.unset(), + null, + null); + } + + public void waitAndAssertTracesClientSyncServerSyncWithError( + int peerPort, String method, int count) { + this.baseWaitAndAssertTraces( + method, + method, + count, + SYNC_CLIENT, + StatusData.error(), + peerPort, + PEER_ADDR, + new Object[] {null, "Internal error processing " + method}, + TRANSPORT_EXCEPTION, + SYNC_SERVER, + StatusData.error(), + new Object[] {"fail"}, + VERIFY_EXCEPTION); + } + + public void waitAndAssertTracesClientSyncServerSyncWithError( + int peerPort, String clientMethod, String serverMethod, int count) { + this.baseWaitAndAssertTraces( + clientMethod, + serverMethod, + count, + SYNC_CLIENT, + StatusData.error(), + peerPort, + PEER_ADDR, + new Object[] {null, "Internal error processing " + clientMethod}, + TRANSPORT_EXCEPTION, + SYNC_SERVER, + StatusData.error(), + new Object[] {"fail"}, + VERIFY_EXCEPTION); + } + + public void waitAndAssertTracesClientSyncServerSyncOnewayError( + int peerPort, String method, int count) { + this.baseWaitAndAssertTraces( + method, + method, + count, + SYNC_CLIENT, + StatusData.unset(), + peerPort, + PEER_ADDR, + null, + null, + SYNC_SERVER, + StatusData.error(), + new Object[] {"fail"}, + VERIFY_EXCEPTION); + } + + public void waitAndAssertTracesClientSyncServerSyncOnewayError( + int peerPort, String clientMethod, String serverMethod, int count) { + this.baseWaitAndAssertTraces( + clientMethod, + serverMethod, + count, + SYNC_CLIENT, + StatusData.unset(), + peerPort, + PEER_ADDR, + null, + null, + SYNC_SERVER, + StatusData.error(), + new Object[] {"fail"}, + VERIFY_EXCEPTION); + } + + public void waitAndAssertTracesClientAsyncServerAsync(int peerPort, String method, int count) { + this.baseWaitAndAssertTraces( + method, + method, + count, + ASYNC_CLIENT, + StatusData.unset(), + peerPort, + PEER_ADDR, + null, + null, + ASYNC_SERVER, + StatusData.unset(), + null, + null); + } + + public void waitAndAssertTracesClientAsyncServerAsyncError( + int peerPort, String method, int count) { + this.baseWaitAndAssertTraces( + method, + method, + count, + ASYNC_CLIENT, + StatusData.unset(), + peerPort, + PEER_ADDR, + null, + null, + ASYNC_SERVER, + StatusData.error(), + new Object[] {"fail"}, + VERIFY_EXCEPTION); + } + + public void waitAndAssertTracesClientAsyncServerAsyncWithError( + int peerPort, String method, int count) { + this.baseWaitAndAssertTraces( + method, + method, + count, + ASYNC_CLIENT, + StatusData.error(), + peerPort, + PEER_ADDR, + new Object[] {"Read call frame size failed", "fail"}, + IO_EXCEPTION, + ASYNC_SERVER, + StatusData.error(), + new Object[] {"Read call frame size failed", "fail"}, + VERIFY_EXCEPTION); + } + + public void waitAndAssertTracesClientAsyncServerSync(int peerPort, String method, int count) { + this.baseWaitAndAssertTraces( + method, + method, + count, + ASYNC_CLIENT, + StatusData.unset(), + peerPort, + PEER_ADDR, + null, + null, + SYNC_SERVER, + StatusData.unset(), + null, + null); + } + + public void waitAndAssertTracesClientAsyncServerSyncWithError( + int peerPort, String method, int count) { + this.baseWaitAndAssertTraces( + method, + method, + count, + ASYNC_CLIENT, + StatusData.error(), + peerPort, + PEER_ADDR, + new Object[] {"Read call frame size failed", "Internal error processing " + method}, + IO_EXCEPTION, + SYNC_SERVER, + StatusData.error(), + new Object[] {"fail"}, + VERIFY_EXCEPTION); + } + + public void waitAndAssertTracesClientAsyncServerSyncOnewayError( + int peerPort, String method, int count) { + this.baseWaitAndAssertTraces( + method, + method, + count, + ASYNC_CLIENT, + StatusData.unset(), + peerPort, + PEER_ADDR, + null, + null, + SYNC_SERVER, + StatusData.error(), + new Object[] {"fail"}, + VERIFY_EXCEPTION); + } + + @SuppressWarnings({"rawtypes", "unchecked"}) // 测试代码 + public void waitAndAssertTracesClientSyncServerAsync(int peerPort, String method, int count) { + this.baseWaitAndAssertTraces( + method, + method, + count, + SYNC_CLIENT, + StatusData.unset(), + peerPort, + PEER_ADDR, + null, + null, + ASYNC_SERVER, + StatusData.unset(), + null, + null); + } + + @SuppressWarnings({"rawtypes", "unchecked"}) // 测试代码 + public void waitAndAssertTracesClientSyncServerAsyncError( + int peerPort, String method, int count) { + this.baseWaitAndAssertTraces( + method, + method, + count, + SYNC_CLIENT, + StatusData.unset(), + peerPort, + PEER_ADDR, + null, + null, + ASYNC_SERVER, + StatusData.error(), + new Object[] {"fail"}, + VERIFY_EXCEPTION); + } + + @SuppressWarnings({"rawtypes", "unchecked"}) + public void waitAndAssertTracesClientSyncServerAsyncWithError( + int peerPort, String method, int count) { + this.baseWaitAndAssertTraces( + method, + method, + count, + SYNC_CLIENT, + StatusData.error(), + peerPort, + PEER_ADDR, + new Object[] {null, "fail"}, + TRANSPORT_EXCEPTION, + ASYNC_SERVER, + StatusData.error(), + new Object[] {"fail"}, + VERIFY_EXCEPTION); + } + + @SuppressWarnings({"rawtypes", "unchecked"}) + private void baseWaitAndAssertTraces( + String clientMethod, + String serverMethod, + int count, + String clientClass, + StatusData clientStatus, + int peerPort, + String peerAddr, + Object[] clientErrMsg, + String clientErrorType, + String serverClass, + StatusData serverStatus, + Object[] serverErrMsg, + String serverErrorType) { + Consumer[] consumers = new Consumer[count]; + Consumer traceAssertConsumer; + if (serverClass == null) { + traceAssertConsumer = + trace -> + trace.hasSpansSatisfyingExactly( + span -> span.hasName("parent").hasKind(SpanKind.INTERNAL).hasNoParent(), + clientSpanDataAssertConsumer( + clientMethod, + clientClass, + clientStatus, + trace, + peerPort, + peerAddr, + clientErrMsg, + clientErrorType)); + } else { + traceAssertConsumer = + trace -> + trace.hasSpansSatisfyingExactly( + span -> span.hasName("parent").hasKind(SpanKind.INTERNAL).hasNoParent(), + clientSpanDataAssertConsumer( + clientMethod, + clientClass, + clientStatus, + trace, + peerPort, + peerAddr, + clientErrMsg, + clientErrorType), + serverSpanDataAssertConsumer( + serverMethod, + serverClass, + serverStatus, + trace, + serverErrMsg, + serverErrorType)); + } + + for (int i = 0; i < count; ++i) { + consumers[i] = traceAssertConsumer; + } + this.testing().waitAndAssertTraces(consumers); + } + + @SuppressWarnings({"ReturnValueIgnored"}) + private static Consumer clientSpanDataAssertConsumer( + String clientMethod, + String clientClass, + StatusData statusData, + TraceAssert trace, + int peerPort, + String peerAddr, + Object[] errMsg, + String errorType) { + Consumer consumer = + span -> + span.hasName(clientMethod) + .hasKind(SpanKind.CLIENT) + .hasParent(trace.getSpan(0)) + .hasStatus(statusData) + .hasAttributesSatisfying( + equalTo(NETWORK_PEER_PORT, peerPort), + equalTo(NETWORK_PEER_ADDRESS, peerAddr), + equalTo(RPC_SYSTEM, "apache_thrift"), + equalTo(RPC_SERVICE, clientClass), + equalTo(RPC_METHOD, clientMethod)); + if (statusData == StatusData.error()) { + consumer = + consumer.andThen( + span -> + span.hasEventsSatisfyingExactly( + event -> + event + .hasName(EXCEPTION_EVENT_NAME) + .hasAttributesSatisfyingExactly( + satisfies(EXCEPTION_MESSAGE, val -> val.isIn(errMsg)), + satisfies( + stringKey("exception.stacktrace"), AbstractAssert::isNotNull), + equalTo(EXCEPTION_TYPE, errorType)))); + } + return consumer; + } + + @SuppressWarnings({"ReturnValueIgnored"}) + private static Consumer serverSpanDataAssertConsumer( + String serverMethod, + String serverClass, + StatusData statusData, + TraceAssert trace, + Object[] errMsg, + String errorType) { + Consumer consumer = + span -> + span.hasName(serverMethod) + .hasKind(SpanKind.SERVER) + .hasParent(trace.getSpan(1)) + .hasStatus(statusData) + .hasAttributesSatisfying( + equalTo(NETWORK_PEER_ADDRESS, "127.0.0.1"), + equalTo(RPC_SYSTEM, "apache_thrift"), + equalTo(RPC_SERVICE, serverClass), + equalTo(RPC_METHOD, serverMethod)); + if (statusData == StatusData.error()) { + consumer = + consumer.andThen( + span -> + span.hasEventsSatisfyingExactly( + event -> + event + .hasName(EXCEPTION_EVENT_NAME) + .hasAttributesSatisfyingExactly( + satisfies(EXCEPTION_MESSAGE, val -> val.isIn(errMsg)), + satisfies( + stringKey("exception.stacktrace"), AbstractAssert::isNotNull), + equalTo(EXCEPTION_TYPE, errorType)))); + } + return consumer; + } +} diff --git a/instrumentation/thrift/thrift-0.9.1/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/thrift/v0_9_1/WithErrorTest.java b/instrumentation/thrift/thrift-0.9.1/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/thrift/v0_9_1/WithErrorTest.java new file mode 100644 index 000000000000..a2bb6e507483 --- /dev/null +++ b/instrumentation/thrift/thrift-0.9.1/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/thrift/v0_9_1/WithErrorTest.java @@ -0,0 +1,432 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.instrumentation.thrift.v0_9_1; + +import static org.assertj.core.api.Assertions.assertThat; + +import io.opentelemetry.javaagent.instrumentation.thrift.v0_9_1.thrift.ThriftService; +import java.io.IOException; +import org.apache.thrift.TException; +import org.apache.thrift.async.AsyncMethodCallback; +import org.apache.thrift.async.TAsyncClientManager; +import org.apache.thrift.protocol.TBinaryProtocol; +import org.apache.thrift.protocol.TMultiplexedProtocol; +import org.apache.thrift.protocol.TProtocol; +import org.apache.thrift.protocol.TProtocolFactory; +import org.apache.thrift.transport.TFramedTransport; +import org.apache.thrift.transport.TNonblockingSocket; +import org.apache.thrift.transport.TNonblockingTransport; +import org.apache.thrift.transport.TSocket; +import org.apache.thrift.transport.TTransport; +import org.apache.thrift.transport.TTransportException; +import org.junit.jupiter.api.Test; + +public class WithErrorTest extends ThriftBaseTest { + + @Test + public void syncClientSyncSimpleServerWithError() throws TException { + int port = super.getPort(); + this.startSyncSimpleServer(port); + this.syncClientWithError(port); + this.waitAndAssertTracesClientSyncServerSyncWithError(port, "withError", 1); + } + + @Test + public void syncClientSyncSimpleServerWithErrorParallel() + throws InterruptedException, TTransportException { + int port = super.getPort(); + this.startSyncSimpleServer(port); + int threadCount = 5; + runParallel(threadCount, () -> this.syncClientWithError(port)); + this.waitAndAssertTracesClientSyncServerSyncWithError(port, "withError", threadCount); + } + + @Test + public void syncClientSyncThreadPoolServerWithError() throws TException, InterruptedException { + int port = super.getPort(); + this.startSyncThreadPoolServer(port); + this.syncClientWithError(port); + this.waitAndAssertTracesClientSyncServerSyncWithError(port, "withError", 1); + } + + @Test + public void syncClientSyncThreadPoolServerWithErrorParallel() + throws InterruptedException, TTransportException { + int port = super.getPort(); + this.startSyncThreadPoolServer(port); + int threadCount = 5; + runParallel(threadCount, () -> this.syncClientWithError(port)); + this.waitAndAssertTracesClientSyncServerSyncWithError(port, "withError", threadCount); + } + + @Test + public void syncClientMutiSyncSimpleServerWithError() throws TException { + int port = super.getPort(); + this.startMultiSimpleServer(port); + this.syncClientMultiWithError(port); + this.waitAndAssertTracesClientSyncServerSyncWithError( + port, "withError", "syncHelloWorld:withError", 1); + } + + @Test + public void syncClientMutiSyncSimpleServerWithErrorParallel() + throws InterruptedException, TTransportException { + int port = super.getPort(); + this.startMultiSimpleServer(port); + int threadCount = 5; + runParallel(threadCount, () -> this.syncClientMultiWithError(port)); + this.waitAndAssertTracesClientSyncServerSyncWithError( + port, "withError", "syncHelloWorld:withError", threadCount); + } + + @Test + public void syncFramedClientAsyncThreadedSelectorServerWithError() throws TException { + int port = super.getPort(); + this.startAsyncThreadedSelectorServer(port); + this.syncFramedClientWithError(port); + this.waitAndAssertTracesClientSyncServerAsyncWithError(port, "withError", 1); + } + + @Test + public void syncFramedClientAsyncThreadedSelectorServerWithErrorParallel() + throws InterruptedException, TTransportException { + int port = super.getPort(); + this.startAsyncThreadedSelectorServer(port); + int threadCount = 5; + runParallel(threadCount, () -> this.syncFramedClientWithError(port)); + this.waitAndAssertTracesClientSyncServerAsyncWithError(port, "withError", threadCount); + } + + @Test + public void syncFramedClientAsyncMutiThreadedSelectorServerWithError() throws TException { + int port = super.getPort(); + this.startMultiThreadedSelectorServer(port); + this.syncFramedClientMultiWithError(port); + this.waitAndAssertTracesClientSyncServerSyncWithError( + port, "withError", "syncHelloWorld:withError", 1); + } + + @Test + public void syncFramedClientAsyncMutiThreadedSelectorServerWithErrorParallel() + throws InterruptedException, TTransportException { + int port = super.getPort(); + this.startMultiThreadedSelectorServer(port); + int threadCount = 5; + runParallel(threadCount, () -> this.syncFramedClientMultiWithError(port)); + this.waitAndAssertTracesClientSyncServerSyncWithError( + port, "withError", "syncHelloWorld:withError", threadCount); + } + + @Test + public void syncFramedClientSyncThreadedSelectorServerWithError() throws TException { + int port = super.getPort(); + this.startSyncThreadedSelectorServer(port); + this.syncFramedClientWithError(port); + this.waitAndAssertTracesClientSyncServerSyncWithError(port, "withError", 1); + } + + @Test + public void syncFramedClientSyncThreadedSelectorServerWithErrorParallel() + throws InterruptedException, TTransportException { + int port = super.getPort(); + this.startSyncThreadedSelectorServer(port); + int threadCount = 5; + runParallel(threadCount, () -> this.syncFramedClientWithError(port)); + this.waitAndAssertTracesClientSyncServerSyncWithError(port, "withError", threadCount); + } + + @Test + public void nonBlockClientAsyncThreadedSelectorServerWithError() throws TException, IOException { + int port = super.getPort(); + this.startAsyncThreadedSelectorServer(port); + this.nonBlockClientWithError(port); + this.waitAndAssertTracesClientAsyncServerAsyncWithError(port, "withError", 1); + } + + @Test + public void nonBlockClientAsyncThreadedSelectorServerWithErrorParallel() + throws InterruptedException, TTransportException { + int port = super.getPort(); + this.startAsyncThreadedSelectorServer(port); + int threadCount = 5; + runParallel(threadCount, () -> this.nonBlockClientWithError(port)); + this.waitAndAssertTracesClientAsyncServerAsyncWithError(port, "withError", threadCount); + } + + @Test + public void nonBlockClientSyncThreadedSelectorServerWithError() throws TException, IOException { + int port = super.getPort(); + this.startSyncThreadedSelectorServer(port); + this.nonBlockClientWithError(port); + this.waitAndAssertTracesClientAsyncServerSyncWithError(port, "withError", 1); + } + + @Test + public void nonBlockClientSyncThreadedSelectorServerWithErrorParallel() + throws InterruptedException, TTransportException { + int port = super.getPort(); + this.startSyncThreadedSelectorServer(port); + int threadCount = 5; + runParallel(threadCount, () -> this.nonBlockClientWithError(port)); + this.waitAndAssertTracesClientAsyncServerSyncWithError(port, "withError", threadCount); + } + + @Test + public void syncFramedClientSyncNonblockingServerWithError() throws TException { + int port = super.getPort(); + this.startSyncNonblockingServer(port); + this.syncFramedClientWithError(port); + this.waitAndAssertTracesClientSyncServerSyncWithError(port, "withError", 1); + } + + @Test + public void syncFramedClientSyncNonblockingServerWithErrorParallel() + throws InterruptedException, TTransportException { + int port = super.getPort(); + this.startSyncNonblockingServer(port); + int threadCount = 5; + runParallel(threadCount, () -> this.syncFramedClientWithError(port)); + this.waitAndAssertTracesClientSyncServerSyncWithError(port, "withError", threadCount); + } + + @Test + public void syncFramedClientSyncHsHaServerWithError() throws TException { + int port = super.getPort(); + this.startSyncHsHaServer(port); + this.syncFramedClientWithError(port); + this.waitAndAssertTracesClientSyncServerSyncWithError(port, "withError", 1); + } + + @Test + public void syncFramedClientSyncHsHaServerWithErrorParallel() + throws InterruptedException, TTransportException { + int port = super.getPort(); + this.startSyncHsHaServer(port); + int threadCount = 5; + runParallel(threadCount, () -> this.syncFramedClientWithError(port)); + this.waitAndAssertTracesClientSyncServerSyncWithError(port, "withError", threadCount); + } + + @Test + public void syncFramedClientAsyncNonblockingServerWithError() throws TException { + int port = super.getPort(); + this.startAsyncNonblockingServer(port); + this.syncFramedClientWithError(port); + this.waitAndAssertTracesClientSyncServerAsyncWithError(port, "withError", 1); + } + + @Test + public void syncFramedClientAsyncNonblockingServerWithErrorParallel() + throws InterruptedException, TTransportException { + int port = super.getPort(); + this.startAsyncNonblockingServer(port); + int threadCount = 5; + runParallel(threadCount, () -> this.syncFramedClientWithError(port)); + this.waitAndAssertTracesClientSyncServerAsyncWithError(port, "withError", threadCount); + } + + @Test + public void syncFramedClientAsyncHsHaServerWithError() throws TException { + int port = super.getPort(); + this.startAsyncHsHaServer(port); + this.syncFramedClientWithError(port); + this.waitAndAssertTracesClientSyncServerAsyncWithError(port, "withError", 1); + } + + @Test + public void syncFramedClientAsyncHsHaServerWithErrorParallel() + throws InterruptedException, TTransportException { + int port = super.getPort(); + this.startAsyncHsHaServer(port); + int threadCount = 5; + runParallel(threadCount, () -> this.syncFramedClientWithError(port)); + this.waitAndAssertTracesClientSyncServerAsyncWithError(port, "withError", threadCount); + } + + @Test + public void nonBlockClientSyncNonblockingServerWithError() throws TException, IOException { + int port = super.getPort(); + this.startSyncNonblockingServer(port); + this.nonBlockClientWithError(port); + this.waitAndAssertTracesClientAsyncServerSyncWithError(port, "withError", 1); + } + + @Test + public void nonBlockClientSyncNonblockingServerWithErrorParallel() + throws InterruptedException, TTransportException { + int port = super.getPort(); + this.startSyncNonblockingServer(port); + int threadCount = 5; + runParallel(threadCount, () -> this.nonBlockClientWithError(port)); + this.waitAndAssertTracesClientAsyncServerSyncWithError(port, "withError", threadCount); + } + + @Test + public void nonBlockClientSyncHsHaServerWithError() throws TException, IOException { + int port = super.getPort(); + this.startSyncHsHaServer(port); + this.nonBlockClientWithError(port); + this.waitAndAssertTracesClientAsyncServerSyncWithError(port, "withError", 1); + } + + @Test + public void nonBlockClientSyncHsHaServerWithErrorParallel() + throws InterruptedException, TTransportException { + int port = super.getPort(); + this.startSyncHsHaServer(port); + int threadCount = 5; + runParallel(threadCount, () -> this.nonBlockClientWithError(port)); + this.waitAndAssertTracesClientAsyncServerSyncWithError(port, "withError", threadCount); + } + + @Test + public void nonBlockClientAsyncNonblockingServerWithError() throws TException, IOException { + int port = super.getPort(); + this.startAsyncNonblockingServer(port); + this.nonBlockClientWithError(port); + this.waitAndAssertTracesClientAsyncServerAsyncWithError(port, "withError", 1); + } + + @Test + public void nonBlockClientAsyncNonblockingServerWithErrorParallel() + throws InterruptedException, TTransportException { + int port = super.getPort(); + this.startAsyncNonblockingServer(port); + int threadCount = 5; + runParallel(threadCount, () -> this.nonBlockClientWithError(port)); + this.waitAndAssertTracesClientAsyncServerAsyncWithError(port, "withError", threadCount); + } + + @Test + public void nonBlockClientAsyncHsHaServerWithError() throws TException, IOException { + int port = super.getPort(); + this.startAsyncHsHaServer(port); + this.nonBlockClientWithError(port); + this.waitAndAssertTracesClientAsyncServerAsyncWithError(port, "withError", 1); + } + + @Test + public void nonBlockClientAsyncHsHaServerWithErrorParallel() + throws InterruptedException, TTransportException { + int port = super.getPort(); + this.startAsyncHsHaServer(port); + int threadCount = 5; + runParallel(threadCount, () -> this.nonBlockClientWithError(port)); + this.waitAndAssertTracesClientAsyncServerAsyncWithError(port, "withError", threadCount); + } + + public void syncClientWithError(int port) throws TException { + TTransport transport = null; + try { + transport = new TSocket("localhost", port); + transport.open(); + TProtocol protocol = new TBinaryProtocol(transport); + ThriftService.Client client = new ThriftService.Client(protocol); + Exception error = null; + try { + this.testing().runWithSpan("parent", () -> client.withError()); + } catch (Exception e) { + error = e; + } + assertThat(error).isNotNull(); + } finally { + if (transport != null) { + transport.close(); + } + } + } + + public void syncClientMultiWithError(int port) throws TException { + TTransport transport = null; + try { + transport = new TSocket("localhost", port); + transport.open(); + TProtocol protocol = new TBinaryProtocol(transport); + TMultiplexedProtocol multiplexedProtocol = + new TMultiplexedProtocol(protocol, "syncHelloWorld"); + ThriftService.Client client = new ThriftService.Client(multiplexedProtocol); + Exception error = null; + try { + this.testing().runWithSpan("parent", () -> client.withError()); + } catch (Exception e) { + error = e; + } + assertThat(error).isNotNull(); + } finally { + if (transport != null) { + transport.close(); + } + } + } + + public void nonBlockClientWithError(int port) throws TException, IOException { + TNonblockingTransport transport = new TNonblockingSocket("localhost", port); + TAsyncClientManager clientManager = new TAsyncClientManager(); + TProtocolFactory protocolFactory = new TBinaryProtocol.Factory(); + ThriftService.AsyncClient.Factory factory = + new ThriftService.AsyncClient.Factory(clientManager, protocolFactory); + ThriftService.AsyncClient asyClient = factory.getAsyncClient(transport); + AsyncMethodCallback callback = + new AsyncMethodCallback() { + @Override + public void onComplete(String result) { + assertThat(result).isEqualTo("Hello USs' Bob"); + } + + @Override + public void onError(Exception e) { + assertThat(e.getMessage()).isEqualTo("Read call frame size failed"); + } + }; + this.testing().runWithSpan("parent", () -> asyClient.withError(callback)); + } + + public void syncFramedClientWithError(int port) throws TException { + TTransport transport = null; + try { + transport = new TSocket("localhost", port); + TFramedTransport framedTransport = new TFramedTransport(transport); + framedTransport.open(); + TProtocol protocol = new TBinaryProtocol(framedTransport); + ThriftService.Client client = new ThriftService.Client(protocol); + Exception error = null; + try { + this.testing().runWithSpan("parent", () -> client.withError()); + } catch (Exception e) { + error = e; + } + assertThat(error).isNotNull(); + } finally { + if (transport != null) { + transport.close(); + } + } + } + + public void syncFramedClientMultiWithError(int port) throws TException { + TTransport transport = null; + try { + transport = new TSocket("localhost", port); + TFramedTransport framedTransport = new TFramedTransport(transport); + framedTransport.open(); + TProtocol protocol = new TBinaryProtocol(framedTransport); + TMultiplexedProtocol multiplexedProtocol = + new TMultiplexedProtocol(protocol, "syncHelloWorld"); + ThriftService.Client client = new ThriftService.Client(multiplexedProtocol); + Exception error = null; + try { + this.testing().runWithSpan("parent", () -> client.withError()); + } catch (Exception e) { + error = e; + } + assertThat(error).isNotNull(); + } finally { + if (transport != null) { + transport.close(); + } + } + } +} diff --git a/instrumentation/thrift/thrift-0.9.1/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/thrift/v0_9_1/server/ThriftServiceAsyncImpl.java b/instrumentation/thrift/thrift-0.9.1/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/thrift/v0_9_1/server/ThriftServiceAsyncImpl.java new file mode 100644 index 000000000000..dfaaef1f9f98 --- /dev/null +++ b/instrumentation/thrift/thrift-0.9.1/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/thrift/v0_9_1/server/ThriftServiceAsyncImpl.java @@ -0,0 +1,40 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.instrumentation.thrift.v0_9_1.server; + +import io.opentelemetry.javaagent.instrumentation.thrift.v0_9_1.thrift.ThriftService; +import org.apache.thrift.TException; +import org.apache.thrift.async.AsyncMethodCallback; +import org.apache.thrift.transport.TTransportException; + +@SuppressWarnings({"cast", "rawtypes", "serial", "unchecked", "unused", "all"}) +public class ThriftServiceAsyncImpl implements ThriftService.AsyncIface { + public ThriftServiceAsyncImpl() {} + + @Override + public void sayHello(String zone, String name, AsyncMethodCallback resultHandler) + throws TException { + resultHandler.onComplete("Hello " + zone + "s' " + name); + } + + @Override + public void withError(AsyncMethodCallback resultHandler) throws TException { + throw new TTransportException("fail"); + } + + @Override + public void noReturn(int delay, AsyncMethodCallback resultHandler) throws TException { + resultHandler.onComplete(null); + } + + @Override + public void oneWay(AsyncMethodCallback resultHandler) throws TException {} + + @Override + public void oneWayWithError(AsyncMethodCallback resultHandler) throws TException { + throw new TTransportException("fail"); + } +} diff --git a/instrumentation/thrift/thrift-0.9.1/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/thrift/v0_9_1/server/ThriftServiceImpl.java b/instrumentation/thrift/thrift-0.9.1/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/thrift/v0_9_1/server/ThriftServiceImpl.java new file mode 100644 index 000000000000..047fc1085ec2 --- /dev/null +++ b/instrumentation/thrift/thrift-0.9.1/javaagent/src/test/java/io/opentelemetry/javaagent/instrumentation/thrift/v0_9_1/server/ThriftServiceImpl.java @@ -0,0 +1,37 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.javaagent.instrumentation.thrift.v0_9_1.server; + +import io.opentelemetry.javaagent.instrumentation.thrift.v0_9_1.thrift.ThriftService; +import org.apache.thrift.TException; +import org.apache.thrift.transport.TTransportException; + +@SuppressWarnings({"cast", "rawtypes", "serial", "unchecked", "unused", "all"}) +public class ThriftServiceImpl implements ThriftService.Iface { + + public ThriftServiceImpl() {} + + @Override + public String sayHello(String zone, String name) { + return "Hello " + zone + "s' " + name; + } + + @Override + public String withError() throws TException { + throw new TTransportException("fail"); + } + + @Override + public void noReturn(int delay) throws TException {} + + @Override + public void oneWay() {} + + @Override + public void oneWayWithError() throws TException { + throw new TTransportException("fail"); + } +} diff --git a/instrumentation/thrift/thrift-0.9.1/javaagent/src/test/resources/ThriftService.thrift b/instrumentation/thrift/thrift-0.9.1/javaagent/src/test/resources/ThriftService.thrift new file mode 100644 index 000000000000..ecc437991962 --- /dev/null +++ b/instrumentation/thrift/thrift-0.9.1/javaagent/src/test/resources/ThriftService.thrift @@ -0,0 +1,9 @@ +namespace java io.opentelemetry.javaagent.instrumentation.thrift.v0_9_1.thrift + +service ThriftService { + string sayHello(1:string zone,2:string name); + string withError(); + void noReturn(1:i32 delay); + oneway void oneWay(); + oneway void oneWayWithError(); +} diff --git a/instrumentation/thrift/thrift-common/javaagent/build.gradle.kts b/instrumentation/thrift/thrift-common/javaagent/build.gradle.kts new file mode 100644 index 000000000000..44fee0f36f1d --- /dev/null +++ b/instrumentation/thrift/thrift-common/javaagent/build.gradle.kts @@ -0,0 +1,10 @@ +plugins { + id("otel.library-instrumentation") +} + +dependencies { + library("org.apache.thrift:libthrift:0.7.0") + compileOnly("javax.annotation:javax.annotation-api:1.3.2") + compileOnly("com.google.auto.value:auto-value-annotations") + annotationProcessor("com.google.auto.value:auto-value") +} diff --git a/instrumentation/thrift/thrift-common/javaagent/src/main/java/io/opentelemetry/instrumentation/thrift/common/RequestScopeContext.java b/instrumentation/thrift/thrift-common/javaagent/src/main/java/io/opentelemetry/instrumentation/thrift/common/RequestScopeContext.java new file mode 100644 index 000000000000..e7574c9be8ae --- /dev/null +++ b/instrumentation/thrift/thrift-common/javaagent/src/main/java/io/opentelemetry/instrumentation/thrift/common/RequestScopeContext.java @@ -0,0 +1,33 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.instrumentation.thrift.common; + +import com.google.auto.value.AutoValue; +import io.opentelemetry.context.Context; +import io.opentelemetry.context.Scope; +import javax.annotation.Nullable; + +@AutoValue +public abstract class RequestScopeContext { + + public static RequestScopeContext create( + ThriftRequest request, @Nullable Scope scope, Context context) { + return new AutoValue_RequestScopeContext(request, scope, context); + } + + public abstract ThriftRequest getRequest(); + + @Nullable + public abstract Scope getScope(); + + public abstract Context getContext(); + + public void close() { + if (getScope() != null) { + getScope().close(); + } + } +} diff --git a/instrumentation/thrift/thrift-common/javaagent/src/main/java/io/opentelemetry/instrumentation/thrift/common/SocketAccessor.java b/instrumentation/thrift/thrift-common/javaagent/src/main/java/io/opentelemetry/instrumentation/thrift/common/SocketAccessor.java new file mode 100644 index 000000000000..9858047bdd77 --- /dev/null +++ b/instrumentation/thrift/thrift-common/javaagent/src/main/java/io/opentelemetry/instrumentation/thrift/common/SocketAccessor.java @@ -0,0 +1,86 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.instrumentation.thrift.common; + +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.net.Socket; +import org.apache.thrift.transport.TNonblockingSocket; +import org.apache.thrift.transport.TSaslClientTransport; +import org.apache.thrift.transport.TSocket; +import org.apache.thrift.transport.TTransport; + +public class SocketAccessor { + private static final String TRANSPORT = "transport_"; + private static final String UNDERLYING = "underlying"; + private static final String GET_INNER_TRANSPORT = "getInnerTransport"; + + private static final Class LAYERED_TRANSPORT = + getClass("org.apache.thrift.transport.layered.TLayeredTransport"); + private static final Class FRAMED_TRANSPORT = + getClass("org.apache.thrift.transport.TFramedTransport"); + private static final Class FAST_FRAMED_TRANSPORT = + getClass("org.apache.thrift.transport.TFastFramedTransport"); + + private SocketAccessor() {} + + public static Socket getSocket(TTransport transport) { + if (transport == null) { + return null; + } + try { + if (transport instanceof TSocket) { + return ((TSocket) transport).getSocket(); + } + if (transport instanceof TNonblockingSocket) { + return ((TNonblockingSocket) transport).getSocketChannel().socket(); + } + if (transport instanceof TSaslClientTransport) { + return getSocket(((TSaslClientTransport) transport).getUnderlyingTransport()); + } + Class thisClass = transport.getClass(); + Class superClass = thisClass.getSuperclass(); + Class layeredTransport = LAYERED_TRANSPORT; + if (superClass != null && superClass == layeredTransport) { + Method parentMethod = superClass.getMethod(GET_INNER_TRANSPORT); + Object result = parentMethod.invoke(transport); + if (result != null && result instanceof TTransport) { + return getSocket((TTransport) result); + } + } + + if (thisClass == FRAMED_TRANSPORT) { + return getInnerTransportSocket(thisClass, TRANSPORT, transport); + } + if (thisClass == FAST_FRAMED_TRANSPORT) { + return getInnerTransportSocket(thisClass, UNDERLYING, transport); + } + } catch (Throwable e) { + return null; + } + return null; + } + + public static Class getClass(String className) { + try { + return Class.forName(className); + } catch (Throwable e) { + return null; + } + } + + private static Socket getInnerTransportSocket( + Class thisClass, String targetField, TTransport transport) + throws NoSuchFieldException, IllegalAccessException { + Field field = thisClass.getDeclaredField(targetField); + field.setAccessible(true); + Object fieldTransport = field.get(transport); + if (fieldTransport instanceof TTransport) { + return getSocket((TTransport) fieldTransport); + } + return null; + } +} diff --git a/instrumentation/thrift/thrift-common/javaagent/src/main/java/io/opentelemetry/instrumentation/thrift/common/ThriftHeaderGetter.java b/instrumentation/thrift/thrift-common/javaagent/src/main/java/io/opentelemetry/instrumentation/thrift/common/ThriftHeaderGetter.java new file mode 100644 index 000000000000..5fd37a2cf9eb --- /dev/null +++ b/instrumentation/thrift/thrift-common/javaagent/src/main/java/io/opentelemetry/instrumentation/thrift/common/ThriftHeaderGetter.java @@ -0,0 +1,32 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.instrumentation.thrift.common; + +import static java.util.Collections.emptyList; + +import io.opentelemetry.context.propagation.TextMapGetter; +import javax.annotation.Nullable; + +public enum ThriftHeaderGetter implements TextMapGetter { + INSTANCE; + + @Override + public Iterable keys(ThriftRequest request) { + if (request == null) { + return emptyList(); + } + return request.getHeader().keySet(); + } + + @Override + @Nullable + public String get(@Nullable ThriftRequest request, String key) { + if (request == null) { + return null; + } + return request.getHeader().get(key); + } +} diff --git a/instrumentation/thrift/thrift-common/javaagent/src/main/java/io/opentelemetry/instrumentation/thrift/common/ThriftHeaderSetter.java b/instrumentation/thrift/thrift-common/javaagent/src/main/java/io/opentelemetry/instrumentation/thrift/common/ThriftHeaderSetter.java new file mode 100644 index 000000000000..ecc6fd90d59b --- /dev/null +++ b/instrumentation/thrift/thrift-common/javaagent/src/main/java/io/opentelemetry/instrumentation/thrift/common/ThriftHeaderSetter.java @@ -0,0 +1,23 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.instrumentation.thrift.common; + +import io.opentelemetry.context.propagation.TextMapSetter; +import java.util.Map; +import javax.annotation.Nullable; + +public enum ThriftHeaderSetter implements TextMapSetter { + INSTANCE; + + @Override + public void set(@Nullable ThriftRequest request, String key, String value) { + if (request == null) { + return; + } + Map header = request.getHeader(); + header.put(key, value); + } +} diff --git a/instrumentation/thrift/thrift-common/javaagent/src/main/java/io/opentelemetry/instrumentation/thrift/common/ThriftInstrumenterFactory.java b/instrumentation/thrift/thrift-common/javaagent/src/main/java/io/opentelemetry/instrumentation/thrift/common/ThriftInstrumenterFactory.java new file mode 100644 index 000000000000..8ce3b785b192 --- /dev/null +++ b/instrumentation/thrift/thrift-common/javaagent/src/main/java/io/opentelemetry/instrumentation/thrift/common/ThriftInstrumenterFactory.java @@ -0,0 +1,49 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.instrumentation.thrift.common; + +import io.opentelemetry.api.GlobalOpenTelemetry; +import io.opentelemetry.instrumentation.api.incubator.semconv.rpc.RpcClientAttributesExtractor; +import io.opentelemetry.instrumentation.api.incubator.semconv.rpc.RpcClientMetrics; +import io.opentelemetry.instrumentation.api.incubator.semconv.rpc.RpcServerAttributesExtractor; +import io.opentelemetry.instrumentation.api.incubator.semconv.rpc.RpcServerMetrics; +import io.opentelemetry.instrumentation.api.instrumenter.Instrumenter; +import io.opentelemetry.instrumentation.api.semconv.network.NetworkAttributesExtractor; +import io.opentelemetry.instrumentation.thrift.common.client.ThriftClientNetworkAttributesGetter; +import io.opentelemetry.instrumentation.thrift.common.server.ThriftServerNetworkAttributesGetter; + +public final class ThriftInstrumenterFactory { + + public static Instrumenter clientInstrumenter( + String instrumentationName) { + ThriftClientNetworkAttributesGetter netClientAttributesGetter = + new ThriftClientNetworkAttributesGetter(); + ThriftRpcAttributesGetter rpcAttributesGetter = ThriftRpcAttributesGetter.INSTANCE; + return Instrumenter.builder( + GlobalOpenTelemetry.get(), instrumentationName, new ThriftSpanNameExtractor()) + .setSpanStatusExtractor(ThriftSpanStatusExtractor.INSTANCE) + .addAttributesExtractor(RpcClientAttributesExtractor.create(rpcAttributesGetter)) + .addAttributesExtractor(NetworkAttributesExtractor.create(netClientAttributesGetter)) + .addOperationMetrics(RpcClientMetrics.get()) + .buildClientInstrumenter(ThriftHeaderSetter.INSTANCE); + } + + public static Instrumenter serverInstrumenter( + String instrumentationName) { + ThriftServerNetworkAttributesGetter netServerAttributesGetter = + new ThriftServerNetworkAttributesGetter(); + ThriftRpcAttributesGetter rpcAttributesGetter = ThriftRpcAttributesGetter.INSTANCE; + return Instrumenter.builder( + GlobalOpenTelemetry.get(), instrumentationName, new ThriftSpanNameExtractor()) + .setSpanStatusExtractor(ThriftSpanStatusExtractor.INSTANCE) + .addAttributesExtractor(RpcServerAttributesExtractor.create(rpcAttributesGetter)) + .addAttributesExtractor(NetworkAttributesExtractor.create(netServerAttributesGetter)) + .addOperationMetrics(RpcServerMetrics.get()) + .buildServerInstrumenter(ThriftHeaderGetter.INSTANCE); + } + + private ThriftInstrumenterFactory() {} +} diff --git a/instrumentation/thrift/thrift-common/javaagent/src/main/java/io/opentelemetry/instrumentation/thrift/common/ThriftRequest.java b/instrumentation/thrift/thrift-common/javaagent/src/main/java/io/opentelemetry/instrumentation/thrift/common/ThriftRequest.java new file mode 100644 index 000000000000..e5d3fffa9379 --- /dev/null +++ b/instrumentation/thrift/thrift-common/javaagent/src/main/java/io/opentelemetry/instrumentation/thrift/common/ThriftRequest.java @@ -0,0 +1,33 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.instrumentation.thrift.common; + +import com.google.auto.value.AutoValue; +import java.net.Socket; +import java.util.Map; +import javax.annotation.Nullable; + +@AutoValue +public abstract class ThriftRequest { + + public static ThriftRequest create( + @Nullable String serviceName, + String methodName, + @Nullable Socket socket, + Map header) { + return new AutoValue_ThriftRequest(serviceName, methodName, socket, header); + } + + @Nullable + public abstract String getServiceName(); + + public abstract String getMethodName(); + + @Nullable + public abstract Socket getSocket(); + + public abstract Map getHeader(); +} diff --git a/instrumentation/thrift/thrift-common/javaagent/src/main/java/io/opentelemetry/instrumentation/thrift/common/ThriftRpcAttributesGetter.java b/instrumentation/thrift/thrift-common/javaagent/src/main/java/io/opentelemetry/instrumentation/thrift/common/ThriftRpcAttributesGetter.java new file mode 100644 index 000000000000..dad72b390841 --- /dev/null +++ b/instrumentation/thrift/thrift-common/javaagent/src/main/java/io/opentelemetry/instrumentation/thrift/common/ThriftRpcAttributesGetter.java @@ -0,0 +1,30 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.instrumentation.thrift.common; + +import io.opentelemetry.instrumentation.api.incubator.semconv.rpc.RpcAttributesGetter; +import javax.annotation.Nullable; + +public enum ThriftRpcAttributesGetter implements RpcAttributesGetter { + INSTANCE; + + @Override + public String getSystem(ThriftRequest request) { + return "apache_thrift"; + } + + @Override + @Nullable + public String getService(ThriftRequest request) { + return request.getServiceName(); + } + + @Override + @Nullable + public String getMethod(ThriftRequest request) { + return request.getMethodName(); + } +} diff --git a/instrumentation/thrift/thrift-common/javaagent/src/main/java/io/opentelemetry/instrumentation/thrift/common/ThriftSpanNameExtractor.java b/instrumentation/thrift/thrift-common/javaagent/src/main/java/io/opentelemetry/instrumentation/thrift/common/ThriftSpanNameExtractor.java new file mode 100644 index 000000000000..e1dcdc78365d --- /dev/null +++ b/instrumentation/thrift/thrift-common/javaagent/src/main/java/io/opentelemetry/instrumentation/thrift/common/ThriftSpanNameExtractor.java @@ -0,0 +1,18 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.instrumentation.thrift.common; + +import io.opentelemetry.instrumentation.api.instrumenter.SpanNameExtractor; + +public final class ThriftSpanNameExtractor implements SpanNameExtractor { + @Override + public String extract(ThriftRequest request) { + if (request.getMethodName() == null) { + return "Thrift request"; + } + return request.getMethodName(); + } +} diff --git a/instrumentation/thrift/thrift-common/javaagent/src/main/java/io/opentelemetry/instrumentation/thrift/common/ThriftSpanStatusExtractor.java b/instrumentation/thrift/thrift-common/javaagent/src/main/java/io/opentelemetry/instrumentation/thrift/common/ThriftSpanStatusExtractor.java new file mode 100644 index 000000000000..4b2972ee5116 --- /dev/null +++ b/instrumentation/thrift/thrift-common/javaagent/src/main/java/io/opentelemetry/instrumentation/thrift/common/ThriftSpanStatusExtractor.java @@ -0,0 +1,28 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.instrumentation.thrift.common; + +import io.opentelemetry.api.trace.StatusCode; +import io.opentelemetry.instrumentation.api.instrumenter.SpanStatusBuilder; +import io.opentelemetry.instrumentation.api.instrumenter.SpanStatusExtractor; +import javax.annotation.Nullable; + +public final class ThriftSpanStatusExtractor + implements SpanStatusExtractor { + + public static final ThriftSpanStatusExtractor INSTANCE = new ThriftSpanStatusExtractor(); + + @Override + public void extract( + SpanStatusBuilder spanStatusBuilder, + ThriftRequest request, + @Nullable Integer status, + @Nullable Throwable error) { + if ((status != null && status > 0) || error != null) { + spanStatusBuilder.setStatus(StatusCode.ERROR); + } + } +} diff --git a/instrumentation/thrift/thrift-common/javaagent/src/main/java/io/opentelemetry/instrumentation/thrift/common/client/MethodAccessor.java b/instrumentation/thrift/thrift-common/javaagent/src/main/java/io/opentelemetry/instrumentation/thrift/common/client/MethodAccessor.java new file mode 100644 index 000000000000..ae774b535ac9 --- /dev/null +++ b/instrumentation/thrift/thrift-common/javaagent/src/main/java/io/opentelemetry/instrumentation/thrift/common/client/MethodAccessor.java @@ -0,0 +1,57 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.instrumentation.thrift.common.client; + +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; + +public class MethodAccessor { + + private static Field concreteProtocolField; + private static final Map> SERVICE_NAME_METHOD_NAMES = + new ConcurrentHashMap<>(); + + private MethodAccessor() {} + + public static Set voidMethodNames(String serviceName) { + Set exit = SERVICE_NAME_METHOD_NAMES.get(serviceName); + if (exit != null) { + return exit; + } + Set methodNames = new HashSet<>(); + try { + Class clazz = Class.forName(serviceName); + Method[] declaredMethods = clazz.getDeclaredMethods(); + for (Method declaredMethod : declaredMethods) { + if (declaredMethod.getReturnType() == void.class) { + methodNames.add(declaredMethod.getName()); + } + } + } catch (ClassNotFoundException ignore) { + // ignore + } + SERVICE_NAME_METHOD_NAMES.put(serviceName, methodNames); + return methodNames; + } + + public static Field getConcreteProtocolField(Class clazz) { + if (concreteProtocolField != null) { + return concreteProtocolField; + } + try { + Field field = clazz.getDeclaredField("concreteProtocol"); + field.setAccessible(true); + concreteProtocolField = field; + } catch (NoSuchFieldException ignore) { + // ignore + } + return concreteProtocolField; + } +} diff --git a/instrumentation/thrift/thrift-common/javaagent/src/main/java/io/opentelemetry/instrumentation/thrift/common/client/ThriftClientNetworkAttributesGetter.java b/instrumentation/thrift/thrift-common/javaagent/src/main/java/io/opentelemetry/instrumentation/thrift/common/client/ThriftClientNetworkAttributesGetter.java new file mode 100644 index 000000000000..8c9c3625b3b7 --- /dev/null +++ b/instrumentation/thrift/thrift-common/javaagent/src/main/java/io/opentelemetry/instrumentation/thrift/common/client/ThriftClientNetworkAttributesGetter.java @@ -0,0 +1,32 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.instrumentation.thrift.common.client; + +import io.opentelemetry.instrumentation.api.semconv.network.NetworkAttributesGetter; +import io.opentelemetry.instrumentation.thrift.common.ThriftRequest; +import java.net.InetSocketAddress; +import java.net.Socket; +import java.net.SocketAddress; +import javax.annotation.Nullable; + +public final class ThriftClientNetworkAttributesGetter + implements NetworkAttributesGetter { + + @Nullable + @Override + public InetSocketAddress getNetworkPeerInetSocketAddress( + ThriftRequest request, @Nullable Integer integer) { + Socket socket = request.getSocket(); + if (socket == null) { + return null; + } + SocketAddress address = socket.getRemoteSocketAddress(); + if (address instanceof InetSocketAddress) { + return (InetSocketAddress) address; + } + return null; + } +} diff --git a/instrumentation/thrift/thrift-common/javaagent/src/main/java/io/opentelemetry/instrumentation/thrift/common/client/VirtualFields.java b/instrumentation/thrift/thrift-common/javaagent/src/main/java/io/opentelemetry/instrumentation/thrift/common/client/VirtualFields.java new file mode 100644 index 000000000000..469d0de3a3da --- /dev/null +++ b/instrumentation/thrift/thrift-common/javaagent/src/main/java/io/opentelemetry/instrumentation/thrift/common/client/VirtualFields.java @@ -0,0 +1,17 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.instrumentation.thrift.common.client; + +import io.opentelemetry.instrumentation.api.util.VirtualField; +import org.apache.thrift.async.AsyncMethodCallback; +import org.apache.thrift.async.TAsyncMethodCall; + +public class VirtualFields { + private VirtualFields() {} + + public static final VirtualField, AsyncMethodCallback> + ASYNC_METHOD_CALLBACK = VirtualField.find(TAsyncMethodCall.class, AsyncMethodCallback.class); +} diff --git a/instrumentation/thrift/thrift-common/javaagent/src/main/java/io/opentelemetry/instrumentation/thrift/common/server/ThriftServerNetworkAttributesGetter.java b/instrumentation/thrift/thrift-common/javaagent/src/main/java/io/opentelemetry/instrumentation/thrift/common/server/ThriftServerNetworkAttributesGetter.java new file mode 100644 index 000000000000..1fe9bb8fe147 --- /dev/null +++ b/instrumentation/thrift/thrift-common/javaagent/src/main/java/io/opentelemetry/instrumentation/thrift/common/server/ThriftServerNetworkAttributesGetter.java @@ -0,0 +1,38 @@ +/* + * Copyright The OpenTelemetry Authors + * SPDX-License-Identifier: Apache-2.0 + */ + +package io.opentelemetry.instrumentation.thrift.common.server; + +import io.opentelemetry.instrumentation.api.semconv.network.NetworkAttributesGetter; +import io.opentelemetry.instrumentation.api.semconv.network.ServerAttributesGetter; +import io.opentelemetry.instrumentation.thrift.common.ThriftRequest; +import java.net.InetSocketAddress; +import java.net.Socket; +import java.net.SocketAddress; +import javax.annotation.Nullable; + +/** + * This class is internal and is hence not for public use. Its APIs are unstable and can change at + * any time. + */ +public final class ThriftServerNetworkAttributesGetter + implements ServerAttributesGetter, + NetworkAttributesGetter { + + @Override + @Nullable + public InetSocketAddress getNetworkPeerInetSocketAddress( + ThriftRequest request, @Nullable Integer status) { + Socket socket = request.getSocket(); + if (socket == null) { + return null; + } + SocketAddress address = socket.getRemoteSocketAddress(); + if (address instanceof InetSocketAddress) { + return (InetSocketAddress) address; + } + return null; + } +} diff --git a/settings.gradle.kts b/settings.gradle.kts index ca63b0213620..8e292de48a0c 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -677,6 +677,8 @@ include(":instrumentation:spymemcached-2.12:javaagent") include(":instrumentation:struts:struts-2.3:javaagent") include(":instrumentation:struts:struts-7.0:javaagent") include(":instrumentation:tapestry-5.4:javaagent") +include(":instrumentation:thrift:thrift-0.9.1:javaagent") +include(":instrumentation:thrift:thrift-common:javaagent") include(":instrumentation:tomcat:tomcat-7.0:javaagent") include(":instrumentation:tomcat:tomcat-10.0:javaagent") include(":instrumentation:tomcat:tomcat-common:javaagent")