Merge tag 'v0.48.0' into dd-merge

This commit is contained in:
Trask Stalnaker 2020-04-11 13:19:47 -07:00
commit d24159c8d2
67 changed files with 1310 additions and 252 deletions

View File

@ -19,18 +19,18 @@ import java.io.File;
import java.io.FileNotFoundException; import java.io.FileNotFoundException;
import java.io.FileReader; import java.io.FileReader;
import java.io.IOException; import java.io.IOException;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collections; import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet; import java.util.HashSet;
import java.util.LinkedHashSet;
import java.util.List; import java.util.List;
import java.util.Map;
import java.util.Properties; import java.util.Properties;
import java.util.Set; import java.util.Set;
import java.util.SortedSet; import java.util.SortedSet;
import java.util.regex.Pattern; import java.util.regex.Pattern;
import lombok.Getter; import lombok.Getter;
import lombok.NonNull;
import lombok.ToString; import lombok.ToString;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
@ -46,6 +46,7 @@ import lombok.extern.slf4j.Slf4j;
@Slf4j @Slf4j
@ToString(includeFieldNames = true) @ToString(includeFieldNames = true)
public class Config { public class Config {
/** Config keys below */ /** Config keys below */
private static final String PREFIX = "ota."; private static final String PREFIX = "ota.";
@ -250,13 +251,14 @@ public class Config {
} }
/** /**
* @deprecated This method should only be used internally. Use the instance getter instead {@link
* #isIntegrationEnabled(SortedSet, boolean)}.
* @param integrationNames * @param integrationNames
* @param defaultEnabled * @param defaultEnabled
* @return * @return
* @deprecated This method should only be used internally. Use the instance getter instead {@link
* #isIntegrationEnabled(SortedSet, boolean)}.
*/ */
public static boolean integrationEnabled( @Deprecated
private static boolean integrationEnabled(
final SortedSet<String> integrationNames, final boolean defaultEnabled) { final SortedSet<String> integrationNames, final boolean defaultEnabled) {
// If default is enabled, we want to enable individually, // If default is enabled, we want to enable individually,
// if default is disabled, we want to disable individually. // if default is disabled, we want to disable individually.
@ -286,9 +288,10 @@ public class Config {
*/ */
public static String getSettingFromEnvironment(final String name, final String defaultValue) { public static String getSettingFromEnvironment(final String name, final String defaultValue) {
String value; String value;
final String systemPropertyName = propertyNameToSystemPropertyName(name);
// System properties and properties provided from command line have the highest precedence // System properties and properties provided from command line have the highest precedence
value = System.getProperties().getProperty(propertyNameToSystemPropertyName(name)); value = System.getProperties().getProperty(systemPropertyName);
if (null != value) { if (null != value) {
return value; return value;
} }
@ -300,7 +303,7 @@ public class Config {
} }
// If value is not defined yet, we look at properties optionally defined in a properties file // If value is not defined yet, we look at properties optionally defined in a properties file
value = propertiesFromConfigFile.getProperty(propertyNameToSystemPropertyName(name)); value = propertiesFromConfigFile.getProperty(systemPropertyName);
if (null != value) { if (null != value) {
return value; return value;
} }
@ -314,7 +317,8 @@ public class Config {
* *
* @deprecated This method should only be used internally. Use the explicit getter instead. * @deprecated This method should only be used internally. Use the explicit getter instead.
*/ */
public static List<String> getListSettingFromEnvironment( @NonNull
private static List<String> getListSettingFromEnvironment(
final String name, final String defaultValue) { final String name, final String defaultValue) {
return parseList(getSettingFromEnvironment(name, defaultValue)); return parseList(getSettingFromEnvironment(name, defaultValue));
} }
@ -326,8 +330,7 @@ public class Config {
*/ */
public static Boolean getBooleanSettingFromEnvironment( public static Boolean getBooleanSettingFromEnvironment(
final String name, final Boolean defaultValue) { final String name, final Boolean defaultValue) {
final String value = getSettingFromEnvironment(name, null); return getSettingFromEnvironmentWithLog(name, Boolean.class, defaultValue);
return value == null || value.trim().isEmpty() ? defaultValue : Boolean.valueOf(value);
} }
/** /**
@ -336,13 +339,7 @@ public class Config {
* @deprecated This method should only be used internally. Use the explicit getter instead. * @deprecated This method should only be used internally. Use the explicit getter instead.
*/ */
public static Float getFloatSettingFromEnvironment(final String name, final Float defaultValue) { public static Float getFloatSettingFromEnvironment(final String name, final Float defaultValue) {
final String value = getSettingFromEnvironment(name, null); return getSettingFromEnvironmentWithLog(name, Float.class, defaultValue);
try {
return value == null ? defaultValue : Float.valueOf(value);
} catch (final NumberFormatException e) {
log.warn("Invalid configuration for " + name, e);
return defaultValue;
}
} }
/** /**
@ -350,41 +347,20 @@ public class Config {
*/ */
private static Integer getIntegerSettingFromEnvironment( private static Integer getIntegerSettingFromEnvironment(
final String name, final Integer defaultValue) { final String name, final Integer defaultValue) {
final String value = getSettingFromEnvironment(name, null); return getSettingFromEnvironmentWithLog(name, Integer.class, defaultValue);
}
private static <T> T getSettingFromEnvironmentWithLog(
final String name, final Class<T> tClass, final T defaultValue) {
try { try {
return value == null ? defaultValue : Integer.valueOf(value); return valueOf(getSettingFromEnvironment(name, null), tClass, defaultValue);
} catch (final NumberFormatException e) { } catch (final NumberFormatException e) {
log.warn("Invalid configuration for " + name, e); log.warn("Invalid configuration for " + name, e);
return defaultValue; return defaultValue;
} }
} }
/** private static Set<Integer> getIntegerRangeSettingFromEnvironment(
* Calls {@link #getSettingFromEnvironment(String, String)} and converts the result to a set of
* strings splitting by space or comma.
*/
private static <T extends Enum<T>> Set<T> getEnumSetSettingFromEnvironment(
final String name,
final String defaultValue,
final Class<T> clazz,
final boolean emptyResultMeansUseDefault) {
final String value = getSettingFromEnvironment(name, defaultValue);
Set<T> result =
convertStringSetToEnumSet(
parseStringIntoSetOfNonEmptyStrings(value, SPLIT_BY_SPACE_OR_COMMA_REGEX), clazz);
if (emptyResultMeansUseDefault && result.isEmpty()) {
// Treat empty parsing result as no value and use default instead
result =
convertStringSetToEnumSet(
parseStringIntoSetOfNonEmptyStrings(defaultValue, SPLIT_BY_SPACE_OR_COMMA_REGEX),
clazz);
}
return result;
}
private Set<Integer> getIntegerRangeSettingFromEnvironment(
final String name, final Set<Integer> defaultValue) { final String name, final Set<Integer> defaultValue) {
final String value = getSettingFromEnvironment(name, null); final String value = getSettingFromEnvironment(name, null);
try { try {
@ -402,6 +378,7 @@ public class Config {
* @param setting The setting name, e.g. `trace.enabled` * @param setting The setting name, e.g. `trace.enabled`
* @return The public facing environment variable name * @return The public facing environment variable name
*/ */
@NonNull
private static String propertyNameToEnvironmentVariableName(final String setting) { private static String propertyNameToEnvironmentVariableName(final String setting) {
return ENV_REPLACEMENT return ENV_REPLACEMENT
.matcher(propertyNameToSystemPropertyName(setting).toUpperCase()) .matcher(propertyNameToSystemPropertyName(setting).toUpperCase())
@ -415,14 +392,39 @@ public class Config {
* @param setting The setting name, e.g. `trace.config` * @param setting The setting name, e.g. `trace.config`
* @return The public facing system property name * @return The public facing system property name
*/ */
@NonNull
private static String propertyNameToSystemPropertyName(final String setting) { private static String propertyNameToSystemPropertyName(final String setting) {
return PREFIX + setting; return PREFIX + setting;
} }
private static Map<String, String> getPropertyMapValue( /**
final Properties properties, final String name, final Map<String, String> defaultValue) { * @param value to parse by tClass::valueOf
final String value = properties.getProperty(name); * @param tClass should contain static parsing method "T valueOf(String)"
return value == null || value.trim().isEmpty() ? defaultValue : parseMap(value, name); * @param defaultValue
* @param <T>
* @return value == null || value.trim().isEmpty() ? defaultValue : tClass.valueOf(value)
* @throws NumberFormatException
*/
private static <T> T valueOf(
final String value, @NonNull final Class<T> tClass, final T defaultValue) {
if (value == null || value.trim().isEmpty()) {
log.debug("valueOf: using defaultValue '{}' for '{}' of '{}' ", defaultValue, value, tClass);
return defaultValue;
}
try {
return (T)
MethodHandles.publicLookup()
.findStatic(tClass, "valueOf", MethodType.methodType(tClass, String.class))
.invoke(value);
} catch (final NumberFormatException e) {
throw e;
} catch (final NoSuchMethodException | IllegalAccessException e) {
log.debug("Can't invoke or access 'valueOf': ", e);
throw new NumberFormatException(e.toString());
} catch (final Throwable e) {
log.debug("Can't parse: ", e);
throw new NumberFormatException(e.toString());
}
} }
private static List<String> getPropertyListValue( private static List<String> getPropertyListValue(
@ -433,32 +435,15 @@ public class Config {
private static Boolean getPropertyBooleanValue( private static Boolean getPropertyBooleanValue(
final Properties properties, final String name, final Boolean defaultValue) { final Properties properties, final String name, final Boolean defaultValue) {
final String value = properties.getProperty(name); return valueOf(properties.getProperty(name), Boolean.class, defaultValue);
return value == null || value.trim().isEmpty() ? defaultValue : Boolean.valueOf(value);
} }
private static Integer getPropertyIntegerValue( private static Integer getPropertyIntegerValue(
final Properties properties, final String name, final Integer defaultValue) { final Properties properties, final String name, final Integer defaultValue) {
final String value = properties.getProperty(name); return valueOf(properties.getProperty(name), Integer.class, defaultValue);
return value == null || value.trim().isEmpty() ? defaultValue : Integer.valueOf(value);
} }
private static <T extends Enum<T>> Set<T> getPropertySetValue( private static Set<Integer> getPropertyIntegerRangeValue(
final Properties properties, final String name, final Class<T> clazz) {
final String value = properties.getProperty(name);
if (value != null) {
final Set<T> result =
convertStringSetToEnumSet(
parseStringIntoSetOfNonEmptyStrings(value, SPLIT_BY_SPACE_OR_COMMA_REGEX), clazz);
if (!result.isEmpty()) {
return result;
}
}
// null means parent value should be used
return null;
}
private Set<Integer> getPropertyIntegerRangeValue(
final Properties properties, final String name, final Set<Integer> defaultValue) { final Properties properties, final String name, final Set<Integer> defaultValue) {
final String value = properties.getProperty(name); final String value = properties.getProperty(name);
try { try {
@ -469,38 +454,9 @@ public class Config {
} }
} }
private static Map<String, String> parseMap(final String str, final String settingName) { @NonNull
// If we ever want to have default values besides an empty map, this will need to change. private static Set<Integer> parseIntegerRangeSet(@NonNull String str, final String settingName)
if (str == null || str.trim().isEmpty()) {
return Collections.emptyMap();
}
if (!str.matches("(([^,:]+:[^,:]*,)*([^,:]+:[^,:]*),?)?")) {
log.warn(
"Invalid config for {}: '{}'. Must match 'key1:value1,key2:value2'.", settingName, str);
return Collections.emptyMap();
}
final String[] tokens = str.split(",", -1);
final Map<String, String> map = newHashMap(tokens.length);
for (final String token : tokens) {
final String[] keyValue = token.split(":", -1);
if (keyValue.length == 2) {
final String key = keyValue[0].trim();
final String value = keyValue[1].trim();
if (value.length() <= 0) {
log.warn("Ignoring empty value for key '{}' in config for {}", key, settingName);
continue;
}
map.put(key, value);
}
}
return Collections.unmodifiableMap(map);
}
private static Set<Integer> parseIntegerRangeSet(String str, final String settingName)
throws NumberFormatException { throws NumberFormatException {
assert str != null;
str = str.replaceAll("\\s", ""); str = str.replaceAll("\\s", "");
if (!str.matches("\\d{3}(?:-\\d{3})?(?:,\\d{3}(?:-\\d{3})?)*")) { if (!str.matches("\\d{3}(?:-\\d{3})?(?:,\\d{3}(?:-\\d{3})?)*")) {
log.warn( log.warn(
@ -530,10 +486,7 @@ public class Config {
return Collections.unmodifiableSet(set); return Collections.unmodifiableSet(set);
} }
private static Map<String, String> newHashMap(final int size) { @NonNull
return new HashMap<>(size + 1, 1f);
}
private static List<String> parseList(final String str) { private static List<String> parseList(final String str) {
if (str == null || str.trim().isEmpty()) { if (str == null || str.trim().isEmpty()) {
return Collections.emptyList(); return Collections.emptyList();
@ -547,34 +500,6 @@ public class Config {
return Collections.unmodifiableList(Arrays.asList(tokens)); return Collections.unmodifiableList(Arrays.asList(tokens));
} }
private static Set<String> parseStringIntoSetOfNonEmptyStrings(
final String str, final String regex) {
// Using LinkedHashSet to preserve original string order
final Set<String> result = new LinkedHashSet<>();
// Java returns single value when splitting an empty string. We do not need that value, so
// we need to throw it out.
for (final String value : str.split(regex)) {
if (!value.isEmpty()) {
result.add(value);
}
}
return Collections.unmodifiableSet(result);
}
private static <V extends Enum<V>> Set<V> convertStringSetToEnumSet(
final Set<String> input, final Class<V> clazz) {
// Using LinkedHashSet to preserve original string order
final Set<V> result = new LinkedHashSet<>();
for (final String value : input) {
try {
result.add(Enum.valueOf(clazz, value.toUpperCase()));
} catch (final IllegalArgumentException e) {
log.debug("Cannot recognize config string value: {}, {}", value, clazz);
}
}
return Collections.unmodifiableSet(result);
}
/** /**
* Loads the optional configuration properties file into the global {@link Properties} object. * Loads the optional configuration properties file into the global {@link Properties} object.
* *
@ -606,23 +531,13 @@ public class Config {
return properties; return properties;
} }
FileReader fileReader = null; try (final FileReader fileReader = new FileReader(configurationFile)) {
try {
fileReader = new FileReader(configurationFile);
properties.load(fileReader); properties.load(fileReader);
} catch (final FileNotFoundException fnf) { } catch (final FileNotFoundException fnf) {
log.error("Configuration file '{}' not found.", configurationFilePath); log.error("Configuration file '{}' not found.", configurationFilePath);
} catch (final IOException ioe) { } catch (final IOException ioe) {
log.error( log.error(
"Configuration file '{}' cannot be accessed or correctly parsed.", configurationFilePath); "Configuration file '{}' cannot be accessed or correctly parsed.", configurationFilePath);
} finally {
if (fileReader != null) {
try {
fileReader.close();
} catch (final IOException ioe) {
log.error("Configuration file '{}' was not closed correctly.", configurationFilePath);
}
}
} }
return properties; return properties;

View File

@ -1,41 +0,0 @@
/*
* Copyright 2020, OpenTelemetry Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.opentelemetry.auto.tooling;
import net.bytebuddy.agent.builder.AgentBuilder;
import net.bytebuddy.asm.TypeConstantAdjustment;
import net.bytebuddy.description.type.TypeDescription;
import net.bytebuddy.dynamic.DynamicType;
import net.bytebuddy.utility.JavaModule;
public class AgentTransformers {
private static final AgentBuilder.Transformer CONSTANT_ADJUSTER =
new AgentBuilder.Transformer() {
@Override
public DynamicType.Builder<?> transform(
final DynamicType.Builder<?> builder,
final TypeDescription typeDescription,
final ClassLoader classLoader,
final JavaModule javaModule) {
return builder.visit(TypeConstantAdjustment.INSTANCE);
}
};
public static AgentBuilder.Transformer defaultTransformers() {
return CONSTANT_ADJUSTER;
}
}

View File

@ -22,6 +22,7 @@ import static net.bytebuddy.matcher.ElementMatchers.named;
import static net.bytebuddy.matcher.ElementMatchers.not; import static net.bytebuddy.matcher.ElementMatchers.not;
import io.opentelemetry.auto.config.Config; import io.opentelemetry.auto.config.Config;
import io.opentelemetry.auto.tooling.bytebuddy.AgentTransformers;
import io.opentelemetry.auto.tooling.bytebuddy.ExceptionHandlers; import io.opentelemetry.auto.tooling.bytebuddy.ExceptionHandlers;
import io.opentelemetry.auto.tooling.context.FieldBackedProvider; import io.opentelemetry.auto.tooling.context.FieldBackedProvider;
import io.opentelemetry.auto.tooling.context.InstrumentationContextProvider; import io.opentelemetry.auto.tooling.context.InstrumentationContextProvider;
@ -122,7 +123,7 @@ public interface Instrumenter {
if (helperClassNames.length > 0) { if (helperClassNames.length > 0) {
agentBuilder = agentBuilder =
agentBuilder.transform( agentBuilder.transform(
new HelperInjector(this.getClass().getSimpleName(), helperClassNames)); new HelperInjector(getClass().getSimpleName(), helperClassNames));
} }
return agentBuilder; return agentBuilder;
} }

View File

@ -24,6 +24,7 @@ import org.gradle.api.model.ObjectFactory
import java.lang.reflect.Method import java.lang.reflect.Method
import java.security.SecureClassLoader import java.security.SecureClassLoader
import java.util.concurrent.atomic.AtomicReference import java.util.concurrent.atomic.AtomicReference
import java.util.regex.Pattern
/** /**
* muzzle task plugin which runs muzzle validation against a range of dependencies. * muzzle task plugin which runs muzzle validation against a range of dependencies.
@ -36,7 +37,8 @@ class MuzzlePlugin implements Plugin<Project> {
private static final AtomicReference<ClassLoader> TOOLING_LOADER = new AtomicReference<>() private static final AtomicReference<ClassLoader> TOOLING_LOADER = new AtomicReference<>()
static { static {
RemoteRepository central = new RemoteRepository.Builder("central", "default", "https://repo1.maven.org/maven2/").build() RemoteRepository central = new RemoteRepository.Builder("central", "default", "https://repo1.maven.org/maven2/").build()
MUZZLE_REPOS = new ArrayList<RemoteRepository>(Arrays.asList(central)) RemoteRepository typesafe = new RemoteRepository.Builder("typesafe", "default", "https://repo.typesafe.com/typesafe/releases").build()
MUZZLE_REPOS = new ArrayList<RemoteRepository>(Arrays.asList(central, typesafe))
} }
@Override @Override
@ -343,6 +345,8 @@ class MuzzlePlugin implements Plugin<Project> {
return session return session
} }
private static final Pattern GIT_SHA_PATTERN = Pattern.compile('^.*-[0-9a-f]{7,}$')
/** /**
* Filter out snapshot-type builds from versions list. * Filter out snapshot-type builds from versions list.
*/ */
@ -357,7 +361,8 @@ class MuzzlePlugin implements Plugin<Project> {
version.contains(".m") || version.contains(".m") ||
version.contains("-m") || version.contains("-m") ||
version.contains("-dev") || version.contains("-dev") ||
version.contains("public_draft") version.contains("public_draft") ||
version.matches(GIT_SHA_PATTERN)
} }
return list return list
} }

View File

@ -0,0 +1 @@
test-api-key-very-old

View File

@ -7,6 +7,7 @@ ext {
slf4j : "1.7.29", slf4j : "1.7.29",
guava : "20.0", // Last version to support Java 7 guava : "20.0", // Last version to support Java 7
okhttp : "3.12.8", // 3.12.x is last version to support Java7
spock : "1.3-groovy-$spockGroovyVer", spock : "1.3-groovy-$spockGroovyVer",
groovy : groovyVer, groovy : groovyVer,

View File

@ -133,6 +133,9 @@ repositories {
maven { maven {
url "https://adoptopenjdk.jfrog.io/adoptopenjdk/jmc-libs-snapshots" url "https://adoptopenjdk.jfrog.io/adoptopenjdk/jmc-libs-snapshots"
} }
maven {
url "https://repo.typesafe.com/typesafe/releases"
}
} }
dependencies { dependencies {

View File

@ -1,5 +1,5 @@
distributionBase=GRADLE_USER_HOME distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-6.2.2-all.zip distributionUrl=https\://services.gradle.org/distributions/gradle-6.3-all.zip
zipStoreBase=GRADLE_USER_HOME zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists zipStorePath=wrapper/dists

3
gradlew.bat vendored
View File

@ -29,6 +29,9 @@ if "%DIRNAME%" == "" set DIRNAME=.
set APP_BASE_NAME=%~n0 set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME% set APP_HOME=%DIRNAME%
@rem Resolve any "." and ".." in APP_HOME to make it shorter.
for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"

View File

@ -22,9 +22,11 @@ import akka.stream.ActorMaterializer
import io.opentelemetry.auto.bootstrap.instrumentation.decorator.HttpClientDecorator import io.opentelemetry.auto.bootstrap.instrumentation.decorator.HttpClientDecorator
import io.opentelemetry.auto.test.base.HttpClientTest import io.opentelemetry.auto.test.base.HttpClientTest
import spock.lang.Shared import spock.lang.Shared
import spock.lang.Timeout
import static io.opentelemetry.trace.Span.Kind.CLIENT import static io.opentelemetry.trace.Span.Kind.CLIENT
@Timeout(5)
class AkkaHttpClientInstrumentationTest extends HttpClientTest { class AkkaHttpClientInstrumentationTest extends HttpClientTest {
@Shared @Shared
@ -55,6 +57,12 @@ class AkkaHttpClientInstrumentationTest extends HttpClientTest {
false false
} }
@Override
boolean testRemoteConnection() {
// Not sure how to properly set timeouts...
return false
}
def "singleRequest exception trace"() { def "singleRequest exception trace"() {
when: when:
// Passing null causes NPE in singleRequest // Passing null causes NPE in singleRequest

View File

@ -15,19 +15,28 @@
*/ */
import io.opentelemetry.auto.test.base.HttpClientTest import io.opentelemetry.auto.test.base.HttpClientTest
import org.apache.http.HttpResponse import org.apache.http.HttpResponse
import org.apache.http.client.config.RequestConfig
import org.apache.http.concurrent.FutureCallback import org.apache.http.concurrent.FutureCallback
import org.apache.http.impl.nio.client.HttpAsyncClients import org.apache.http.impl.nio.client.HttpAsyncClients
import org.apache.http.message.BasicHeader import org.apache.http.message.BasicHeader
import spock.lang.AutoCleanup import spock.lang.AutoCleanup
import spock.lang.Shared import spock.lang.Shared
import spock.lang.Timeout
import java.util.concurrent.CompletableFuture import java.util.concurrent.CompletableFuture
@Timeout(5)
class ApacheHttpAsyncClientCallbackTest extends HttpClientTest { class ApacheHttpAsyncClientCallbackTest extends HttpClientTest {
@Shared
RequestConfig requestConfig = RequestConfig.custom()
.setConnectTimeout(CONNECT_TIMEOUT_MS)
.setSocketTimeout(READ_TIMEOUT_MS)
.build()
@AutoCleanup @AutoCleanup
@Shared @Shared
def client = HttpAsyncClients.createDefault() def client = HttpAsyncClients.custom().setDefaultRequestConfig(requestConfig).build()
def setupSpec() { def setupSpec() {
client.start() client.start()
@ -68,4 +77,9 @@ class ApacheHttpAsyncClientCallbackTest extends HttpClientTest {
Integer statusOnRedirectError() { Integer statusOnRedirectError() {
return 302 return 302
} }
@Override
boolean testRemoteConnection() {
false // otherwise SocketTimeoutException for https requests
}
} }

View File

@ -14,18 +14,27 @@
* limitations under the License. * limitations under the License.
*/ */
import io.opentelemetry.auto.test.base.HttpClientTest import io.opentelemetry.auto.test.base.HttpClientTest
import org.apache.http.client.config.RequestConfig
import org.apache.http.impl.nio.client.HttpAsyncClients import org.apache.http.impl.nio.client.HttpAsyncClients
import org.apache.http.message.BasicHeader import org.apache.http.message.BasicHeader
import spock.lang.AutoCleanup import spock.lang.AutoCleanup
import spock.lang.Shared import spock.lang.Shared
import spock.lang.Timeout
import java.util.concurrent.Future import java.util.concurrent.Future
@Timeout(5)
class ApacheHttpAsyncClientNullCallbackTest extends HttpClientTest { class ApacheHttpAsyncClientNullCallbackTest extends HttpClientTest {
@Shared
RequestConfig requestConfig = RequestConfig.custom()
.setConnectTimeout(CONNECT_TIMEOUT_MS)
.setSocketTimeout(READ_TIMEOUT_MS)
.build()
@AutoCleanup @AutoCleanup
@Shared @Shared
def client = HttpAsyncClients.createDefault() def client = HttpAsyncClients.custom().setDefaultRequestConfig(requestConfig).build()
def setupSpec() { def setupSpec() {
client.start() client.start()
@ -53,4 +62,9 @@ class ApacheHttpAsyncClientNullCallbackTest extends HttpClientTest {
Integer statusOnRedirectError() { Integer statusOnRedirectError() {
return 302 return 302
} }
@Override
boolean testRemoteConnection() {
false // otherwise SocketTimeoutException for https requests
}
} }

View File

@ -15,19 +15,28 @@
*/ */
import io.opentelemetry.auto.test.base.HttpClientTest import io.opentelemetry.auto.test.base.HttpClientTest
import org.apache.http.HttpResponse import org.apache.http.HttpResponse
import org.apache.http.client.config.RequestConfig
import org.apache.http.concurrent.FutureCallback import org.apache.http.concurrent.FutureCallback
import org.apache.http.impl.nio.client.HttpAsyncClients import org.apache.http.impl.nio.client.HttpAsyncClients
import org.apache.http.message.BasicHeader import org.apache.http.message.BasicHeader
import spock.lang.AutoCleanup import spock.lang.AutoCleanup
import spock.lang.Shared import spock.lang.Shared
import spock.lang.Timeout
import java.util.concurrent.CountDownLatch import java.util.concurrent.CountDownLatch
@Timeout(5)
class ApacheHttpAsyncClientTest extends HttpClientTest { class ApacheHttpAsyncClientTest extends HttpClientTest {
@Shared
RequestConfig requestConfig = RequestConfig.custom()
.setConnectTimeout(CONNECT_TIMEOUT_MS)
.setSocketTimeout(READ_TIMEOUT_MS)
.build()
@AutoCleanup @AutoCleanup
@Shared @Shared
def client = HttpAsyncClients.createDefault() def client = HttpAsyncClients.custom().setDefaultRequestConfig(requestConfig).build()
def setupSpec() { def setupSpec() {
client.start() client.start()
@ -74,4 +83,9 @@ class ApacheHttpAsyncClientTest extends HttpClientTest {
Integer statusOnRedirectError() { Integer statusOnRedirectError() {
return 302 return 302
} }
@Override
boolean testRemoteConnection() {
false // otherwise SocketTimeoutException for https requests
}
} }

View File

@ -24,11 +24,18 @@ import org.apache.commons.httpclient.methods.PostMethod
import org.apache.commons.httpclient.methods.PutMethod import org.apache.commons.httpclient.methods.PutMethod
import org.apache.commons.httpclient.methods.TraceMethod import org.apache.commons.httpclient.methods.TraceMethod
import spock.lang.Shared import spock.lang.Shared
import spock.lang.Timeout
@Timeout(5)
class CommonsHttpClientTest extends HttpClientTest { class CommonsHttpClientTest extends HttpClientTest {
@Shared @Shared
HttpClient client = new HttpClient() HttpClient client = new HttpClient()
def setupSpec() {
client.setConnectionTimeout(CONNECT_TIMEOUT_MS)
client.setTimeout(READ_TIMEOUT_MS)
}
@Override @Override
int doRequest(String method, URI uri, Map<String, String> headers, Closure callback) { int doRequest(String method, URI uri, Map<String, String> headers, Closure callback) {
HttpMethod httpMethod HttpMethod httpMethod

View File

@ -18,8 +18,12 @@ import org.apache.http.HttpResponse
import org.apache.http.client.ResponseHandler import org.apache.http.client.ResponseHandler
import org.apache.http.impl.client.DefaultHttpClient import org.apache.http.impl.client.DefaultHttpClient
import org.apache.http.message.BasicHeader import org.apache.http.message.BasicHeader
import org.apache.http.params.HttpConnectionParams
import org.apache.http.params.HttpParams
import spock.lang.Shared import spock.lang.Shared
import spock.lang.Timeout
@Timeout(5)
class ApacheHttpClientResponseHandlerTest extends HttpClientTest { class ApacheHttpClientResponseHandlerTest extends HttpClientTest {
@Shared @Shared
@ -33,6 +37,12 @@ class ApacheHttpClientResponseHandlerTest extends HttpClientTest {
} }
} }
def setupSpec() {
HttpParams httpParams = client.getParams()
HttpConnectionParams.setConnectionTimeout(httpParams, CONNECT_TIMEOUT_MS)
HttpConnectionParams.setSoTimeout(httpParams, READ_TIMEOUT_MS)
}
@Override @Override
int doRequest(String method, URI uri, Map<String, String> headers, Closure callback) { int doRequest(String method, URI uri, Map<String, String> headers, Closure callback) {
def request = new HttpUriRequest(method, uri) def request = new HttpUriRequest(method, uri)

View File

@ -20,13 +20,22 @@ import org.apache.http.HttpResponse
import org.apache.http.impl.client.DefaultHttpClient import org.apache.http.impl.client.DefaultHttpClient
import org.apache.http.message.BasicHeader import org.apache.http.message.BasicHeader
import org.apache.http.message.BasicHttpRequest import org.apache.http.message.BasicHttpRequest
import org.apache.http.params.HttpConnectionParams
import org.apache.http.params.HttpParams
import org.apache.http.protocol.BasicHttpContext import org.apache.http.protocol.BasicHttpContext
import spock.lang.Shared import spock.lang.Shared
import spock.lang.Timeout
abstract class ApacheHttpClientTest<T extends HttpRequest> extends HttpClientTest { abstract class ApacheHttpClientTest<T extends HttpRequest> extends HttpClientTest {
@Shared @Shared
def client = new DefaultHttpClient() def client = new DefaultHttpClient()
def setupSpec() {
HttpParams httpParams = client.getParams()
HttpConnectionParams.setConnectionTimeout(httpParams, CONNECT_TIMEOUT_MS)
HttpConnectionParams.setSoTimeout(httpParams, READ_TIMEOUT_MS)
}
@Override @Override
int doRequest(String method, URI uri, Map<String, String> headers, Closure callback) { int doRequest(String method, URI uri, Map<String, String> headers, Closure callback) {
def request = createRequest(method, uri) def request = createRequest(method, uri)
@ -64,6 +73,7 @@ abstract class ApacheHttpClientTest<T extends HttpRequest> extends HttpClientTes
} }
} }
@Timeout(5)
class ApacheClientHostRequest extends ApacheHttpClientTest<BasicHttpRequest> { class ApacheClientHostRequest extends ApacheHttpClientTest<BasicHttpRequest> {
@Override @Override
BasicHttpRequest createRequest(String method, URI uri) { BasicHttpRequest createRequest(String method, URI uri) {
@ -74,8 +84,14 @@ class ApacheClientHostRequest extends ApacheHttpClientTest<BasicHttpRequest> {
HttpResponse executeRequest(BasicHttpRequest request, URI uri) { HttpResponse executeRequest(BasicHttpRequest request, URI uri) {
return client.execute(new HttpHost(uri.getHost(), uri.getPort()), request) return client.execute(new HttpHost(uri.getHost(), uri.getPort()), request)
} }
@Override
boolean testRemoteConnection() {
return false
}
} }
@Timeout(5)
class ApacheClientHostRequestContext extends ApacheHttpClientTest<BasicHttpRequest> { class ApacheClientHostRequestContext extends ApacheHttpClientTest<BasicHttpRequest> {
@Override @Override
BasicHttpRequest createRequest(String method, URI uri) { BasicHttpRequest createRequest(String method, URI uri) {
@ -86,8 +102,14 @@ class ApacheClientHostRequestContext extends ApacheHttpClientTest<BasicHttpReque
HttpResponse executeRequest(BasicHttpRequest request, URI uri) { HttpResponse executeRequest(BasicHttpRequest request, URI uri) {
return client.execute(new HttpHost(uri.getHost(), uri.getPort()), request, new BasicHttpContext()) return client.execute(new HttpHost(uri.getHost(), uri.getPort()), request, new BasicHttpContext())
} }
@Override
boolean testRemoteConnection() {
return false
}
} }
@Timeout(5)
class ApacheClientHostRequestResponseHandler extends ApacheHttpClientTest<BasicHttpRequest> { class ApacheClientHostRequestResponseHandler extends ApacheHttpClientTest<BasicHttpRequest> {
@Override @Override
BasicHttpRequest createRequest(String method, URI uri) { BasicHttpRequest createRequest(String method, URI uri) {
@ -98,8 +120,14 @@ class ApacheClientHostRequestResponseHandler extends ApacheHttpClientTest<BasicH
HttpResponse executeRequest(BasicHttpRequest request, URI uri) { HttpResponse executeRequest(BasicHttpRequest request, URI uri) {
return client.execute(new HttpHost(uri.getHost(), uri.getPort()), request, { response -> response }) return client.execute(new HttpHost(uri.getHost(), uri.getPort()), request, { response -> response })
} }
@Override
boolean testRemoteConnection() {
return false
}
} }
@Timeout(5)
class ApacheClientHostRequestResponseHandlerContext extends ApacheHttpClientTest<BasicHttpRequest> { class ApacheClientHostRequestResponseHandlerContext extends ApacheHttpClientTest<BasicHttpRequest> {
@Override @Override
BasicHttpRequest createRequest(String method, URI uri) { BasicHttpRequest createRequest(String method, URI uri) {
@ -110,8 +138,14 @@ class ApacheClientHostRequestResponseHandlerContext extends ApacheHttpClientTest
HttpResponse executeRequest(BasicHttpRequest request, URI uri) { HttpResponse executeRequest(BasicHttpRequest request, URI uri) {
return client.execute(new HttpHost(uri.getHost(), uri.getPort()), request, { response -> response }, new BasicHttpContext()) return client.execute(new HttpHost(uri.getHost(), uri.getPort()), request, { response -> response }, new BasicHttpContext())
} }
@Override
boolean testRemoteConnection() {
return false
}
} }
@Timeout(5)
class ApacheClientUriRequest extends ApacheHttpClientTest<HttpUriRequest> { class ApacheClientUriRequest extends ApacheHttpClientTest<HttpUriRequest> {
@Override @Override
HttpUriRequest createRequest(String method, URI uri) { HttpUriRequest createRequest(String method, URI uri) {
@ -124,6 +158,7 @@ class ApacheClientUriRequest extends ApacheHttpClientTest<HttpUriRequest> {
} }
} }
@Timeout(5)
class ApacheClientUriRequestContext extends ApacheHttpClientTest<HttpUriRequest> { class ApacheClientUriRequestContext extends ApacheHttpClientTest<HttpUriRequest> {
@Override @Override
HttpUriRequest createRequest(String method, URI uri) { HttpUriRequest createRequest(String method, URI uri) {
@ -136,6 +171,7 @@ class ApacheClientUriRequestContext extends ApacheHttpClientTest<HttpUriRequest>
} }
} }
@Timeout(5)
class ApacheClientUriRequestResponseHandler extends ApacheHttpClientTest<HttpUriRequest> { class ApacheClientUriRequestResponseHandler extends ApacheHttpClientTest<HttpUriRequest> {
@Override @Override
HttpUriRequest createRequest(String method, URI uri) { HttpUriRequest createRequest(String method, URI uri) {
@ -148,6 +184,7 @@ class ApacheClientUriRequestResponseHandler extends ApacheHttpClientTest<HttpUri
} }
} }
@Timeout(5)
class ApacheClientUriRequestResponseHandlerContext extends ApacheHttpClientTest<HttpUriRequest> { class ApacheClientUriRequestResponseHandlerContext extends ApacheHttpClientTest<HttpUriRequest> {
@Override @Override
HttpUriRequest createRequest(String method, URI uri) { HttpUriRequest createRequest(String method, URI uri) {

View File

@ -38,6 +38,8 @@ abstract class AbstractGoogleHttpClientTest extends HttpClientTest {
GenericUrl genericUrl = new GenericUrl(uri) GenericUrl genericUrl = new GenericUrl(uri)
HttpRequest request = requestFactory.buildRequest(method, genericUrl, null) HttpRequest request = requestFactory.buildRequest(method, genericUrl, null)
request.connectTimeout = CONNECT_TIMEOUT_MS
request.readTimeout = READ_TIMEOUT_MS
request.getHeaders().putAll(headers) request.getHeaders().putAll(headers)
request.setThrowExceptionOnExecuteError(throwExceptionOnError) request.setThrowExceptionOnExecuteError(throwExceptionOnError)

View File

@ -15,7 +15,11 @@
*/ */
import com.google.api.client.http.HttpRequest import com.google.api.client.http.HttpRequest
import com.google.api.client.http.HttpResponse import com.google.api.client.http.HttpResponse
import spock.lang.Retry
import spock.lang.Timeout
@Retry(condition = { !invocation.method.name.contains('circular redirects') })
@Timeout(5)
class GoogleHttpClientAsyncTest extends AbstractGoogleHttpClientTest { class GoogleHttpClientAsyncTest extends AbstractGoogleHttpClientTest {
@Override @Override
HttpResponse executeRequest(HttpRequest request) { HttpResponse executeRequest(HttpRequest request) {

View File

@ -15,7 +15,11 @@
*/ */
import com.google.api.client.http.HttpRequest import com.google.api.client.http.HttpRequest
import com.google.api.client.http.HttpResponse import com.google.api.client.http.HttpResponse
import spock.lang.Retry
import spock.lang.Timeout
@Retry(condition = { !invocation.method.name.contains('circular redirects') })
@Timeout(5)
class GoogleHttpClientTest extends AbstractGoogleHttpClientTest { class GoogleHttpClientTest extends AbstractGoogleHttpClientTest {
@Override @Override
HttpResponse executeRequest(HttpRequest request) { HttpResponse executeRequest(HttpRequest request) {

View File

@ -14,7 +14,9 @@
* limitations under the License. * limitations under the License.
*/ */
import io.opentelemetry.auto.test.base.HttpClientTest import io.opentelemetry.auto.test.base.HttpClientTest
import spock.lang.Timeout
@Timeout(5)
class HttpUrlConnectionResponseCodeOnlyTest extends HttpClientTest { class HttpUrlConnectionResponseCodeOnlyTest extends HttpClientTest {
@Override @Override
@ -22,6 +24,8 @@ class HttpUrlConnectionResponseCodeOnlyTest extends HttpClientTest {
HttpURLConnection connection = uri.toURL().openConnection() HttpURLConnection connection = uri.toURL().openConnection()
try { try {
connection.setRequestMethod(method) connection.setRequestMethod(method)
connection.connectTimeout = CONNECT_TIMEOUT_MS
connection.readTimeout = READ_TIMEOUT_MS
headers.each { connection.setRequestProperty(it.key, it.value) } headers.each { connection.setRequestProperty(it.key, it.value) }
connection.setRequestProperty("Connection", "close") connection.setRequestProperty("Connection", "close")
return connection.getResponseCode() return connection.getResponseCode()

View File

@ -18,11 +18,13 @@ import io.opentelemetry.auto.instrumentation.api.Tags
import io.opentelemetry.auto.test.base.HttpClientTest import io.opentelemetry.auto.test.base.HttpClientTest
import spock.lang.Ignore import spock.lang.Ignore
import spock.lang.Requires import spock.lang.Requires
import spock.lang.Timeout
import sun.net.www.protocol.https.HttpsURLConnectionImpl import sun.net.www.protocol.https.HttpsURLConnectionImpl
import static io.opentelemetry.auto.test.utils.TraceUtils.runUnderTrace import static io.opentelemetry.auto.test.utils.TraceUtils.runUnderTrace
import static io.opentelemetry.trace.Span.Kind.CLIENT import static io.opentelemetry.trace.Span.Kind.CLIENT
@Timeout(5)
class HttpUrlConnectionTest extends HttpClientTest { class HttpUrlConnectionTest extends HttpClientTest {
static final RESPONSE = "Hello." static final RESPONSE = "Hello."
@ -36,6 +38,8 @@ class HttpUrlConnectionTest extends HttpClientTest {
headers.each { connection.setRequestProperty(it.key, it.value) } headers.each { connection.setRequestProperty(it.key, it.value) }
connection.setRequestProperty("Connection", "close") connection.setRequestProperty("Connection", "close")
connection.useCaches = true connection.useCaches = true
connection.connectTimeout = CONNECT_TIMEOUT_MS
connection.readTimeout = READ_TIMEOUT_MS
def parentSpan = TEST_TRACER.getCurrentSpan() def parentSpan = TEST_TRACER.getCurrentSpan()
def stream = connection.inputStream def stream = connection.inputStream
assert TEST_TRACER.getCurrentSpan() == parentSpan assert TEST_TRACER.getCurrentSpan() == parentSpan

View File

@ -14,7 +14,9 @@
* limitations under the License. * limitations under the License.
*/ */
import io.opentelemetry.auto.test.base.HttpClientTest import io.opentelemetry.auto.test.base.HttpClientTest
import spock.lang.Timeout
@Timeout(5)
class HttpUrlConnectionUseCachesFalseTest extends HttpClientTest { class HttpUrlConnectionUseCachesFalseTest extends HttpClientTest {
@Override @Override
@ -25,6 +27,8 @@ class HttpUrlConnectionUseCachesFalseTest extends HttpClientTest {
headers.each { connection.setRequestProperty(it.key, it.value) } headers.each { connection.setRequestProperty(it.key, it.value) }
connection.setRequestProperty("Connection", "close") connection.setRequestProperty("Connection", "close")
connection.useCaches = false connection.useCaches = false
connection.connectTimeout = CONNECT_TIMEOUT_MS
connection.readTimeout = READ_TIMEOUT_MS
def parentSpan = TEST_TRACER.getCurrentSpan() def parentSpan = TEST_TRACER.getCurrentSpan()
def stream = connection.inputStream def stream = connection.inputStream
assert TEST_TRACER.getCurrentSpan() == parentSpan assert TEST_TRACER.getCurrentSpan() == parentSpan

View File

@ -18,13 +18,24 @@ import org.springframework.http.HttpEntity
import org.springframework.http.HttpHeaders import org.springframework.http.HttpHeaders
import org.springframework.http.HttpMethod import org.springframework.http.HttpMethod
import org.springframework.http.ResponseEntity import org.springframework.http.ResponseEntity
import org.springframework.http.client.ClientHttpRequestFactory
import org.springframework.http.client.SimpleClientHttpRequestFactory
import org.springframework.web.client.RestTemplate import org.springframework.web.client.RestTemplate
import spock.lang.Shared import spock.lang.Shared
import spock.lang.Timeout
@Timeout(5)
class SpringRestTemplateTest extends HttpClientTest { class SpringRestTemplateTest extends HttpClientTest {
@Shared @Shared
RestTemplate restTemplate = new RestTemplate() ClientHttpRequestFactory factory = new SimpleClientHttpRequestFactory()
@Shared
RestTemplate restTemplate = new RestTemplate(factory)
def setupSpec() {
factory.connectTimeout = CONNECT_TIMEOUT_MS
factory.readTimeout = READ_TIMEOUT_MS
}
@Override @Override
int doRequest(String method, URI uri, Map<String, String> headers, Closure callback) { int doRequest(String method, URI uri, Map<String, String> headers, Closure callback) {
@ -45,4 +56,10 @@ class SpringRestTemplateTest extends HttpClientTest {
boolean testConnectionFailure() { boolean testConnectionFailure() {
false false
} }
@Override
boolean testRemoteConnection() {
// FIXME: exception wrapped in ResourceAccessException
return false
}
} }

View File

@ -19,13 +19,17 @@ import com.sun.jersey.api.client.filter.GZIPContentEncodingFilter
import com.sun.jersey.api.client.filter.LoggingFilter import com.sun.jersey.api.client.filter.LoggingFilter
import io.opentelemetry.auto.test.base.HttpClientTest import io.opentelemetry.auto.test.base.HttpClientTest
import spock.lang.Shared import spock.lang.Shared
import spock.lang.Timeout
@Timeout(5)
class JaxRsClientV1Test extends HttpClientTest { class JaxRsClientV1Test extends HttpClientTest {
@Shared @Shared
Client client = Client.create() Client client = Client.create()
def setupSpec() { def setupSpec() {
client.setConnectTimeout(CONNECT_TIMEOUT_MS)
client.setReadTimeout(READ_TIMEOUT_MS)
// Add filters to ensure spans aren't duplicated. // Add filters to ensure spans aren't duplicated.
client.addFilter(new LoggingFilter()) client.addFilter(new LoggingFilter())
client.addFilter(new GZIPContentEncodingFilter()) client.addFilter(new GZIPContentEncodingFilter())

View File

@ -32,7 +32,8 @@ dependencies {
testCompile group: 'javax.ws.rs', name: 'javax.ws.rs-api', version: '2.0.1' testCompile group: 'javax.ws.rs', name: 'javax.ws.rs-api', version: '2.0.1'
testCompile group: 'org.glassfish.jersey.core', name: 'jersey-client', version: '2.0' testCompile group: 'org.glassfish.jersey.core', name: 'jersey-client', version: '2.0'
testCompile group: 'org.jboss.resteasy', name: 'resteasy-client', version: '3.0.0.Final' testCompile group: 'org.jboss.resteasy', name: 'resteasy-client', version: '3.0.5.Final'
// ^ This version has timeouts https://issues.redhat.com/browse/RESTEASY-975
testCompile group: 'org.apache.cxf', name: 'cxf-rt-rs-client', version: '3.1.0' testCompile group: 'org.apache.cxf', name: 'cxf-rt-rs-client', version: '3.1.0'
// Doesn't work with CXF 3.0.x because their context is wrong: // Doesn't work with CXF 3.0.x because their context is wrong:
// https://github.com/apache/cxf/commit/335c7bad2436f08d6d54180212df5a52157c9f21 // https://github.com/apache/cxf/commit/335c7bad2436f08d6d54180212df5a52157c9f21

View File

@ -15,8 +15,11 @@
*/ */
import io.opentelemetry.auto.test.base.HttpClientTest import io.opentelemetry.auto.test.base.HttpClientTest
import org.apache.cxf.jaxrs.client.spec.ClientBuilderImpl import org.apache.cxf.jaxrs.client.spec.ClientBuilderImpl
import org.glassfish.jersey.client.ClientConfig
import org.glassfish.jersey.client.ClientProperties
import org.glassfish.jersey.client.JerseyClientBuilder import org.glassfish.jersey.client.JerseyClientBuilder
import org.jboss.resteasy.client.jaxrs.ResteasyClientBuilder import org.jboss.resteasy.client.jaxrs.ResteasyClientBuilder
import spock.lang.Timeout
import javax.ws.rs.client.AsyncInvoker import javax.ws.rs.client.AsyncInvoker
import javax.ws.rs.client.Client import javax.ws.rs.client.Client
@ -27,6 +30,7 @@ import javax.ws.rs.client.WebTarget
import javax.ws.rs.core.MediaType import javax.ws.rs.core.MediaType
import javax.ws.rs.core.Response import javax.ws.rs.core.Response
import java.util.concurrent.CountDownLatch import java.util.concurrent.CountDownLatch
import java.util.concurrent.TimeUnit
abstract class JaxRsClientAsyncTest extends HttpClientTest { abstract class JaxRsClientAsyncTest extends HttpClientTest {
@ -61,11 +65,15 @@ abstract class JaxRsClientAsyncTest extends HttpClientTest {
abstract ClientBuilder builder() abstract ClientBuilder builder()
} }
@Timeout(5)
class JerseyClientAsyncTest extends JaxRsClientAsyncTest { class JerseyClientAsyncTest extends JaxRsClientAsyncTest {
@Override @Override
ClientBuilder builder() { ClientBuilder builder() {
return new JerseyClientBuilder() ClientConfig config = new ClientConfig()
config.property(ClientProperties.CONNECT_TIMEOUT, CONNECT_TIMEOUT_MS)
config.property(ClientProperties.READ_TIMEOUT, READ_TIMEOUT_MS)
return new JerseyClientBuilder().withConfig(config)
} }
boolean testCircularRedirects() { boolean testCircularRedirects() {
@ -73,11 +81,14 @@ class JerseyClientAsyncTest extends JaxRsClientAsyncTest {
} }
} }
@Timeout(5)
class ResteasyClientAsyncTest extends JaxRsClientAsyncTest { class ResteasyClientAsyncTest extends JaxRsClientAsyncTest {
@Override @Override
ClientBuilder builder() { ClientBuilder builder() {
return new ResteasyClientBuilder() return new ResteasyClientBuilder()
.establishConnectionTimeout(CONNECT_TIMEOUT_MS, TimeUnit.MILLISECONDS)
.socketTimeout(READ_TIMEOUT_MS, TimeUnit.MILLISECONDS)
} }
boolean testRedirects() { boolean testRedirects() {
@ -85,6 +96,7 @@ class ResteasyClientAsyncTest extends JaxRsClientAsyncTest {
} }
} }
@Timeout(5)
class CxfClientAsyncTest extends JaxRsClientAsyncTest { class CxfClientAsyncTest extends JaxRsClientAsyncTest {
@Override @Override
@ -99,4 +111,9 @@ class CxfClientAsyncTest extends JaxRsClientAsyncTest {
boolean testConnectionFailure() { boolean testConnectionFailure() {
false false
} }
boolean testRemoteConnection() {
// FIXME: span not reported correctly.
false
}
} }

View File

@ -15,8 +15,11 @@
*/ */
import io.opentelemetry.auto.test.base.HttpClientTest import io.opentelemetry.auto.test.base.HttpClientTest
import org.apache.cxf.jaxrs.client.spec.ClientBuilderImpl import org.apache.cxf.jaxrs.client.spec.ClientBuilderImpl
import org.glassfish.jersey.client.ClientConfig
import org.glassfish.jersey.client.ClientProperties
import org.glassfish.jersey.client.JerseyClientBuilder import org.glassfish.jersey.client.JerseyClientBuilder
import org.jboss.resteasy.client.jaxrs.ResteasyClientBuilder import org.jboss.resteasy.client.jaxrs.ResteasyClientBuilder
import spock.lang.Timeout
import javax.ws.rs.client.Client import javax.ws.rs.client.Client
import javax.ws.rs.client.ClientBuilder import javax.ws.rs.client.ClientBuilder
@ -25,6 +28,7 @@ import javax.ws.rs.client.Invocation
import javax.ws.rs.client.WebTarget import javax.ws.rs.client.WebTarget
import javax.ws.rs.core.MediaType import javax.ws.rs.core.MediaType
import javax.ws.rs.core.Response import javax.ws.rs.core.Response
import java.util.concurrent.TimeUnit
abstract class JaxRsClientTest extends HttpClientTest { abstract class JaxRsClientTest extends HttpClientTest {
@ -45,11 +49,15 @@ abstract class JaxRsClientTest extends HttpClientTest {
abstract ClientBuilder builder() abstract ClientBuilder builder()
} }
@Timeout(5)
class JerseyClientTest extends JaxRsClientTest { class JerseyClientTest extends JaxRsClientTest {
@Override @Override
ClientBuilder builder() { ClientBuilder builder() {
return new JerseyClientBuilder() ClientConfig config = new ClientConfig()
config.property(ClientProperties.CONNECT_TIMEOUT, CONNECT_TIMEOUT_MS)
config.property(ClientProperties.READ_TIMEOUT, READ_TIMEOUT_MS)
return new JerseyClientBuilder().withConfig(config)
} }
boolean testCircularRedirects() { boolean testCircularRedirects() {
@ -57,24 +65,29 @@ class JerseyClientTest extends JaxRsClientTest {
} }
} }
@Timeout(5)
class ResteasyClientTest extends JaxRsClientTest { class ResteasyClientTest extends JaxRsClientTest {
@Override @Override
ClientBuilder builder() { ClientBuilder builder() {
return new ResteasyClientBuilder() return new ResteasyClientBuilder()
.establishConnectionTimeout(CONNECT_TIMEOUT_MS, TimeUnit.MILLISECONDS)
.socketTimeout(READ_TIMEOUT_MS, TimeUnit.MILLISECONDS)
} }
boolean testRedirects() { boolean testRedirects() {
false false
} }
} }
@Timeout(5)
class CxfClientTest extends JaxRsClientTest { class CxfClientTest extends JaxRsClientTest {
@Override @Override
ClientBuilder builder() { ClientBuilder builder() {
return new ClientBuilderImpl() return new ClientBuilderImpl()
// .property(ClientImpl.HTTP_CONNECTION_TIMEOUT_PROP, (long) CONNECT_TIMEOUT_MS)
// .property(ClientImpl.HTTP_RECEIVE_TIMEOUT_PROP, (long) READ_TIMEOUT_MS)
} }
boolean testRedirects() { boolean testRedirects() {
@ -84,4 +97,9 @@ class CxfClientTest extends JaxRsClientTest {
boolean testConnectionFailure() { boolean testConnectionFailure() {
false false
} }
boolean testRemoteConnection() {
// FIXME: span not reported correctly.
false
}
} }

View File

@ -17,7 +17,6 @@ import com.ning.http.client.AsyncCompletionHandler
import com.ning.http.client.AsyncHttpClient import com.ning.http.client.AsyncHttpClient
import com.ning.http.client.AsyncHttpClientConfig import com.ning.http.client.AsyncHttpClientConfig
import com.ning.http.client.Response import com.ning.http.client.Response
import io.opentelemetry.auto.instrumentation.api.Tags
import io.opentelemetry.auto.test.base.HttpClientTest import io.opentelemetry.auto.test.base.HttpClientTest
import spock.lang.AutoCleanup import spock.lang.AutoCleanup
import spock.lang.Shared import spock.lang.Shared
@ -65,6 +64,11 @@ class Netty38ClientTest extends HttpClientTest {
false false
} }
@Override
boolean testRemoteConnection() {
return false
}
def "connection error (unopened port)"() { def "connection error (unopened port)"() {
given: given:
def uri = new URI("http://127.0.0.1:$UNUSABLE_PORT/") def uri = new URI("http://127.0.0.1:$UNUSABLE_PORT/")
@ -88,7 +92,6 @@ class Netty38ClientTest extends HttpClientTest {
childOf span(0) childOf span(0)
errored true errored true
tags { tags {
"$Tags.COMPONENT" "netty"
Class errorClass = ConnectException Class errorClass = ConnectException
try { try {
errorClass = Class.forName('io.netty.channel.AbstractChannel$AnnotatedConnectException') errorClass = Class.forName('io.netty.channel.AbstractChannel$AnnotatedConnectException')

View File

@ -17,9 +17,9 @@ import io.opentelemetry.auto.test.base.HttpServerTest
import org.jboss.netty.bootstrap.ServerBootstrap import org.jboss.netty.bootstrap.ServerBootstrap
import org.jboss.netty.buffer.ChannelBuffer import org.jboss.netty.buffer.ChannelBuffer
import org.jboss.netty.buffer.ChannelBuffers import org.jboss.netty.buffer.ChannelBuffers
import org.jboss.netty.channel.Channel
import org.jboss.netty.channel.ChannelHandlerContext import org.jboss.netty.channel.ChannelHandlerContext
import org.jboss.netty.channel.ChannelPipeline import org.jboss.netty.channel.ChannelPipeline
import org.jboss.netty.channel.ChannelPipelineFactory
import org.jboss.netty.channel.DefaultChannelPipeline import org.jboss.netty.channel.DefaultChannelPipeline
import org.jboss.netty.channel.DownstreamMessageEvent import org.jboss.netty.channel.DownstreamMessageEvent
import org.jboss.netty.channel.ExceptionEvent import org.jboss.netty.channel.ExceptionEvent
@ -35,6 +35,8 @@ import org.jboss.netty.handler.codec.http.HttpResponseStatus
import org.jboss.netty.handler.codec.http.HttpServerCodec import org.jboss.netty.handler.codec.http.HttpServerCodec
import org.jboss.netty.handler.logging.LoggingHandler import org.jboss.netty.handler.logging.LoggingHandler
import org.jboss.netty.logging.InternalLogLevel import org.jboss.netty.logging.InternalLogLevel
import org.jboss.netty.logging.InternalLoggerFactory
import org.jboss.netty.logging.Slf4JLoggerFactory
import org.jboss.netty.util.CharsetUtil import org.jboss.netty.util.CharsetUtil
import static io.opentelemetry.auto.test.base.HttpServerTest.ServerEndpoint.ERROR import static io.opentelemetry.auto.test.base.HttpServerTest.ServerEndpoint.ERROR
@ -49,10 +51,17 @@ import static org.jboss.netty.handler.codec.http.HttpHeaders.Names.CONTENT_TYPE
import static org.jboss.netty.handler.codec.http.HttpHeaders.Names.LOCATION import static org.jboss.netty.handler.codec.http.HttpHeaders.Names.LOCATION
import static org.jboss.netty.handler.codec.http.HttpVersion.HTTP_1_1 import static org.jboss.netty.handler.codec.http.HttpVersion.HTTP_1_1
class Netty38ServerTest extends HttpServerTest<Channel> { class Netty38ServerTest extends HttpServerTest<ServerBootstrap> {
static final LoggingHandler LOGGING_HANDLER
static {
InternalLoggerFactory.setDefaultFactory(new Slf4JLoggerFactory())
LOGGING_HANDLER = new LoggingHandler(SERVER_LOGGER.name, InternalLogLevel.DEBUG, true)
}
ChannelPipeline channelPipeline() { ChannelPipeline channelPipeline() {
ChannelPipeline channelPipeline = new DefaultChannelPipeline() ChannelPipeline channelPipeline = new DefaultChannelPipeline()
channelPipeline.addFirst("logger", LOGGING_HANDLER)
channelPipeline.addLast("http-codec", new HttpServerCodec()) channelPipeline.addLast("http-codec", new HttpServerCodec())
channelPipeline.addLast("controller", new SimpleChannelHandler() { channelPipeline.addLast("controller", new SimpleChannelHandler() {
@ -103,7 +112,8 @@ class Netty38ServerTest extends HttpServerTest<Channel> {
@Override @Override
void exceptionCaught(ChannelHandlerContext ctx, ExceptionEvent ex) throws Exception { void exceptionCaught(ChannelHandlerContext ctx, ExceptionEvent ex) throws Exception {
ChannelBuffer buffer = ChannelBuffers.copiedBuffer(ex.getCause().getMessage(), CharsetUtil.UTF_8) def message = ex.getCause() == null ? "<no cause> " + ex.message : ex.cause.message == null ? "<null>" : ex.cause.message
ChannelBuffer buffer = ChannelBuffers.copiedBuffer(message, CharsetUtil.UTF_8)
HttpResponse response = new DefaultHttpResponse(HTTP_1_1, HttpResponseStatus.INTERNAL_SERVER_ERROR) HttpResponse response = new DefaultHttpResponse(HTTP_1_1, HttpResponseStatus.INTERNAL_SERVER_ERROR)
response.setContent(buffer) response.setContent(buffer)
response.headers().set(CONTENT_TYPE, "text/plain") response.headers().set(CONTENT_TYPE, "text/plain")
@ -120,17 +130,23 @@ class Netty38ServerTest extends HttpServerTest<Channel> {
} }
@Override @Override
Channel startServer(int port) { ServerBootstrap startServer(int port) {
ServerBootstrap bootstrap = new ServerBootstrap(new NioServerSocketChannelFactory()) ServerBootstrap bootstrap = new ServerBootstrap(new NioServerSocketChannelFactory())
bootstrap.setParentHandler(new LoggingHandler(InternalLogLevel.INFO)) bootstrap.setParentHandler(LOGGING_HANDLER)
bootstrap.setPipeline(channelPipeline()) bootstrap.setPipelineFactory(new ChannelPipelineFactory() {
@Override
ChannelPipeline getPipeline() throws Exception {
return channelPipeline()
}
})
InetSocketAddress address = new InetSocketAddress(port) InetSocketAddress address = new InetSocketAddress(port)
return bootstrap.bind(address) bootstrap.bind(address)
return bootstrap
} }
@Override @Override
void stopServer(Channel server) { void stopServer(ServerBootstrap server) {
server?.disconnect() server?.shutdown()
} }
} }

View File

@ -26,7 +26,6 @@ import static net.bytebuddy.matcher.ElementMatchers.takesArgument;
import com.google.auto.service.AutoService; import com.google.auto.service.AutoService;
import io.opentelemetry.auto.bootstrap.ContextStore; import io.opentelemetry.auto.bootstrap.ContextStore;
import io.opentelemetry.auto.bootstrap.InstrumentationContext; import io.opentelemetry.auto.bootstrap.InstrumentationContext;
import io.opentelemetry.auto.instrumentation.api.Tags;
import io.opentelemetry.auto.instrumentation.netty.v3_8.server.NettyHttpServerDecorator; import io.opentelemetry.auto.instrumentation.netty.v3_8.server.NettyHttpServerDecorator;
import io.opentelemetry.auto.tooling.Instrumenter; import io.opentelemetry.auto.tooling.Instrumenter;
import io.opentelemetry.context.Scope; import io.opentelemetry.context.Scope;
@ -113,11 +112,7 @@ public class ChannelFutureListenerInstrumentation extends Instrumenter.Default {
final Scope parentScope = NettyHttpServerDecorator.TRACER.withSpan(continuation); final Scope parentScope = NettyHttpServerDecorator.TRACER.withSpan(continuation);
final Span errorSpan = final Span errorSpan =
NettyHttpServerDecorator.TRACER NettyHttpServerDecorator.TRACER.spanBuilder("CONNECT").setSpanKind(CLIENT).startSpan();
.spanBuilder("CONNECT")
.setSpanKind(CLIENT)
.setAttribute(Tags.COMPONENT, "netty")
.startSpan();
try (final Scope scope = NettyHttpServerDecorator.TRACER.withSpan(errorSpan)) { try (final Scope scope = NettyHttpServerDecorator.TRACER.withSpan(errorSpan)) {
NettyHttpServerDecorator.DECORATE.onError(errorSpan, cause); NettyHttpServerDecorator.DECORATE.onError(errorSpan, cause);
NettyHttpServerDecorator.DECORATE.beforeFinish(errorSpan); NettyHttpServerDecorator.DECORATE.beforeFinish(errorSpan);

View File

@ -17,7 +17,6 @@ import com.ning.http.client.AsyncCompletionHandler
import com.ning.http.client.AsyncHttpClient import com.ning.http.client.AsyncHttpClient
import com.ning.http.client.AsyncHttpClientConfig import com.ning.http.client.AsyncHttpClientConfig
import com.ning.http.client.Response import com.ning.http.client.Response
import io.opentelemetry.auto.instrumentation.api.Tags
import io.opentelemetry.auto.test.base.HttpClientTest import io.opentelemetry.auto.test.base.HttpClientTest
import spock.lang.AutoCleanup import spock.lang.AutoCleanup
import spock.lang.Shared import spock.lang.Shared
@ -65,6 +64,11 @@ class Netty38ClientTest extends HttpClientTest {
false false
} }
@Override
boolean testRemoteConnection() {
return false
}
def "connection error (unopened port)"() { def "connection error (unopened port)"() {
given: given:
def uri = new URI("http://127.0.0.1:$UNUSABLE_PORT/") def uri = new URI("http://127.0.0.1:$UNUSABLE_PORT/")
@ -88,7 +92,6 @@ class Netty38ClientTest extends HttpClientTest {
childOf span(0) childOf span(0)
errored true errored true
tags { tags {
"$Tags.COMPONENT" "netty"
Class errorClass = ConnectException Class errorClass = ConnectException
try { try {
errorClass = Class.forName('io.netty.channel.AbstractChannel$AnnotatedConnectException') errorClass = Class.forName('io.netty.channel.AbstractChannel$AnnotatedConnectException')

View File

@ -17,9 +17,9 @@ import io.opentelemetry.auto.test.base.HttpServerTest
import org.jboss.netty.bootstrap.ServerBootstrap import org.jboss.netty.bootstrap.ServerBootstrap
import org.jboss.netty.buffer.ChannelBuffer import org.jboss.netty.buffer.ChannelBuffer
import org.jboss.netty.buffer.ChannelBuffers import org.jboss.netty.buffer.ChannelBuffers
import org.jboss.netty.channel.Channel
import org.jboss.netty.channel.ChannelHandlerContext import org.jboss.netty.channel.ChannelHandlerContext
import org.jboss.netty.channel.ChannelPipeline import org.jboss.netty.channel.ChannelPipeline
import org.jboss.netty.channel.ChannelPipelineFactory
import org.jboss.netty.channel.DefaultChannelPipeline import org.jboss.netty.channel.DefaultChannelPipeline
import org.jboss.netty.channel.DownstreamMessageEvent import org.jboss.netty.channel.DownstreamMessageEvent
import org.jboss.netty.channel.ExceptionEvent import org.jboss.netty.channel.ExceptionEvent
@ -35,6 +35,8 @@ import org.jboss.netty.handler.codec.http.HttpResponseStatus
import org.jboss.netty.handler.codec.http.HttpServerCodec import org.jboss.netty.handler.codec.http.HttpServerCodec
import org.jboss.netty.handler.logging.LoggingHandler import org.jboss.netty.handler.logging.LoggingHandler
import org.jboss.netty.logging.InternalLogLevel import org.jboss.netty.logging.InternalLogLevel
import org.jboss.netty.logging.InternalLoggerFactory
import org.jboss.netty.logging.Slf4JLoggerFactory
import org.jboss.netty.util.CharsetUtil import org.jboss.netty.util.CharsetUtil
import static io.opentelemetry.auto.test.base.HttpServerTest.ServerEndpoint.ERROR import static io.opentelemetry.auto.test.base.HttpServerTest.ServerEndpoint.ERROR
@ -49,10 +51,17 @@ import static org.jboss.netty.handler.codec.http.HttpHeaders.Names.CONTENT_TYPE
import static org.jboss.netty.handler.codec.http.HttpHeaders.Names.LOCATION import static org.jboss.netty.handler.codec.http.HttpHeaders.Names.LOCATION
import static org.jboss.netty.handler.codec.http.HttpVersion.HTTP_1_1 import static org.jboss.netty.handler.codec.http.HttpVersion.HTTP_1_1
class Netty38ServerTest extends HttpServerTest<Channel> { class Netty38ServerTest extends HttpServerTest<ServerBootstrap> {
static final LoggingHandler LOGGING_HANDLER
static {
InternalLoggerFactory.setDefaultFactory(new Slf4JLoggerFactory())
LOGGING_HANDLER = new LoggingHandler(SERVER_LOGGER.name, InternalLogLevel.DEBUG, true)
}
ChannelPipeline channelPipeline() { ChannelPipeline channelPipeline() {
ChannelPipeline channelPipeline = new DefaultChannelPipeline() ChannelPipeline channelPipeline = new DefaultChannelPipeline()
channelPipeline.addFirst("logger", LOGGING_HANDLER)
channelPipeline.addLast("http-codec", new HttpServerCodec()) channelPipeline.addLast("http-codec", new HttpServerCodec())
channelPipeline.addLast("controller", new SimpleChannelHandler() { channelPipeline.addLast("controller", new SimpleChannelHandler() {
@ -103,7 +112,8 @@ class Netty38ServerTest extends HttpServerTest<Channel> {
@Override @Override
void exceptionCaught(ChannelHandlerContext ctx, ExceptionEvent ex) throws Exception { void exceptionCaught(ChannelHandlerContext ctx, ExceptionEvent ex) throws Exception {
ChannelBuffer buffer = ChannelBuffers.copiedBuffer(ex.getCause().getMessage(), CharsetUtil.UTF_8) def message = ex.cause == null ? "<no cause> " + ex.message : ex.cause.message == null ? "<null>" : ex.cause.message
ChannelBuffer buffer = ChannelBuffers.copiedBuffer(message, CharsetUtil.UTF_8)
HttpResponse response = new DefaultHttpResponse(HTTP_1_1, HttpResponseStatus.INTERNAL_SERVER_ERROR) HttpResponse response = new DefaultHttpResponse(HTTP_1_1, HttpResponseStatus.INTERNAL_SERVER_ERROR)
response.setContent(buffer) response.setContent(buffer)
response.headers().set(CONTENT_TYPE, "text/plain") response.headers().set(CONTENT_TYPE, "text/plain")
@ -120,17 +130,23 @@ class Netty38ServerTest extends HttpServerTest<Channel> {
} }
@Override @Override
Channel startServer(int port) { ServerBootstrap startServer(int port) {
ServerBootstrap bootstrap = new ServerBootstrap(new NioServerSocketChannelFactory()) ServerBootstrap bootstrap = new ServerBootstrap(new NioServerSocketChannelFactory())
bootstrap.setParentHandler(new LoggingHandler(InternalLogLevel.INFO)) bootstrap.setParentHandler(LOGGING_HANDLER)
bootstrap.setPipeline(channelPipeline()) bootstrap.setPipelineFactory(new ChannelPipelineFactory() {
@Override
ChannelPipeline getPipeline() throws Exception {
return channelPipeline()
}
})
InetSocketAddress address = new InetSocketAddress(port) InetSocketAddress address = new InetSocketAddress(port)
return bootstrap.bind(address) bootstrap.bind(address)
return bootstrap
} }
@Override @Override
void stopServer(Channel server) { void stopServer(ServerBootstrap server) {
server?.disconnect() server?.shutdown()
} }
} }

View File

@ -20,6 +20,7 @@ import org.asynchttpclient.DefaultAsyncHttpClientConfig
import org.asynchttpclient.Response import org.asynchttpclient.Response
import spock.lang.AutoCleanup import spock.lang.AutoCleanup
import spock.lang.Shared import spock.lang.Shared
import spock.lang.Timeout
import java.util.concurrent.ExecutionException import java.util.concurrent.ExecutionException
import java.util.concurrent.TimeUnit import java.util.concurrent.TimeUnit
@ -29,6 +30,7 @@ import static io.opentelemetry.auto.test.utils.TraceUtils.basicSpan
import static io.opentelemetry.auto.test.utils.TraceUtils.runUnderTrace import static io.opentelemetry.auto.test.utils.TraceUtils.runUnderTrace
import static org.asynchttpclient.Dsl.asyncHttpClient import static org.asynchttpclient.Dsl.asyncHttpClient
@Timeout(5)
class Netty40ClientTest extends HttpClientTest { class Netty40ClientTest extends HttpClientTest {
@Shared @Shared
@ -62,6 +64,11 @@ class Netty40ClientTest extends HttpClientTest {
false false
} }
@Override
boolean testRemoteConnection() {
return false
}
def "connection error (unopened port)"() { def "connection error (unopened port)"() {
given: given:
def uri = new URI("http://127.0.0.1:$UNUSABLE_PORT/") // Use numeric address to avoid ipv4/ipv6 confusion def uri = new URI("http://127.0.0.1:$UNUSABLE_PORT/") // Use numeric address to avoid ipv4/ipv6 confusion

View File

@ -48,16 +48,20 @@ import static io.opentelemetry.auto.test.base.HttpServerTest.ServerEndpoint.SUCC
class Netty40ServerTest extends HttpServerTest<EventLoopGroup> { class Netty40ServerTest extends HttpServerTest<EventLoopGroup> {
static final LoggingHandler LOGGING_HANDLER = new LoggingHandler(SERVER_LOGGER.name, LogLevel.DEBUG)
@Override @Override
EventLoopGroup startServer(int port) { EventLoopGroup startServer(int port) {
def eventLoopGroup = new NioEventLoopGroup() def eventLoopGroup = new NioEventLoopGroup()
ServerBootstrap bootstrap = new ServerBootstrap() ServerBootstrap bootstrap = new ServerBootstrap()
.group(eventLoopGroup) .group(eventLoopGroup)
.handler(new LoggingHandler(LogLevel.INFO)) .handler(LOGGING_HANDLER)
.childHandler([ .childHandler([
initChannel: { ch -> initChannel: { ch ->
ChannelPipeline pipeline = ch.pipeline() ChannelPipeline pipeline = ch.pipeline()
pipeline.addFirst("logger", LOGGING_HANDLER)
def handlers = [new HttpRequestDecoder(), new HttpResponseEncoder()] def handlers = [new HttpRequestDecoder(), new HttpResponseEncoder()]
handlers.each { pipeline.addLast(it) } handlers.each { pipeline.addLast(it) }
pipeline.addLast([ pipeline.addLast([

View File

@ -28,6 +28,7 @@ import org.asynchttpclient.DefaultAsyncHttpClientConfig
import org.asynchttpclient.Response import org.asynchttpclient.Response
import spock.lang.Retry import spock.lang.Retry
import spock.lang.Shared import spock.lang.Shared
import spock.lang.Timeout
import java.util.concurrent.ExecutionException import java.util.concurrent.ExecutionException
import java.util.concurrent.TimeUnit import java.util.concurrent.TimeUnit
@ -39,6 +40,7 @@ import static io.opentelemetry.auto.test.utils.TraceUtils.runUnderTrace
import static org.asynchttpclient.Dsl.asyncHttpClient import static org.asynchttpclient.Dsl.asyncHttpClient
@Retry @Retry
@Timeout(5)
class Netty41ClientTest extends HttpClientTest { class Netty41ClientTest extends HttpClientTest {
@Shared @Shared
@ -71,6 +73,11 @@ class Netty41ClientTest extends HttpClientTest {
false false
} }
@Override
boolean testRemoteConnection() {
return false
}
def "connection error (unopened port)"() { def "connection error (unopened port)"() {
given: given:
def uri = new URI("http://localhost:$UNUSABLE_PORT/") def uri = new URI("http://localhost:$UNUSABLE_PORT/")

View File

@ -47,16 +47,20 @@ import static io.opentelemetry.auto.test.base.HttpServerTest.ServerEndpoint.SUCC
class Netty41ServerTest extends HttpServerTest<EventLoopGroup> { class Netty41ServerTest extends HttpServerTest<EventLoopGroup> {
static final LoggingHandler LOGGING_HANDLER = new LoggingHandler(SERVER_LOGGER.name, LogLevel.DEBUG)
@Override @Override
EventLoopGroup startServer(int port) { EventLoopGroup startServer(int port) {
def eventLoopGroup = new NioEventLoopGroup() def eventLoopGroup = new NioEventLoopGroup()
ServerBootstrap bootstrap = new ServerBootstrap() ServerBootstrap bootstrap = new ServerBootstrap()
.group(eventLoopGroup) .group(eventLoopGroup)
.handler(new LoggingHandler(LogLevel.INFO)) .handler(LOGGING_HANDLER)
.childHandler([ .childHandler([
initChannel: { ch -> initChannel: { ch ->
ChannelPipeline pipeline = ch.pipeline() ChannelPipeline pipeline = ch.pipeline()
pipeline.addFirst("logger", LOGGING_HANDLER)
def handlers = [new HttpServerCodec()] def handlers = [new HttpServerCodec()]
handlers.each { pipeline.addLast(it) } handlers.each { pipeline.addLast(it) }
pipeline.addLast([ pipeline.addLast([

View File

@ -20,10 +20,18 @@ import okhttp3.OkHttpClient
import okhttp3.Request import okhttp3.Request
import okhttp3.RequestBody import okhttp3.RequestBody
import okhttp3.internal.http.HttpMethod import okhttp3.internal.http.HttpMethod
import spock.lang.Timeout
import java.util.concurrent.TimeUnit
@Timeout(5)
class OkHttp3Test extends HttpClientTest { class OkHttp3Test extends HttpClientTest {
def client = new OkHttpClient() def client = new OkHttpClient.Builder()
.connectTimeout(CONNECT_TIMEOUT_MS, TimeUnit.MILLISECONDS)
.readTimeout(READ_TIMEOUT_MS, TimeUnit.MILLISECONDS)
.writeTimeout(READ_TIMEOUT_MS, TimeUnit.MILLISECONDS)
.build()
@Override @Override
int doRequest(String method, URI uri, Map<String, String> headers, Closure callback) { int doRequest(String method, URI uri, Map<String, String> headers, Closure callback) {

View File

@ -23,9 +23,11 @@ import scala.concurrent.ExecutionContext
import scala.concurrent.Future import scala.concurrent.Future
import scala.concurrent.duration.Duration import scala.concurrent.duration.Duration
import spock.lang.Shared import spock.lang.Shared
import spock.lang.Timeout
import java.util.concurrent.TimeUnit import java.util.concurrent.TimeUnit
@Timeout(5)
class PlayJavaWSClientTest extends PlayWSClientTestBase { class PlayJavaWSClientTest extends PlayWSClientTestBase {
@Shared @Shared
StandaloneWSClient wsClient StandaloneWSClient wsClient
@ -52,6 +54,7 @@ class PlayJavaWSClientTest extends PlayWSClientTestBase {
} }
} }
@Timeout(5)
class PlayJavaStreamedWSClientTest extends PlayWSClientTestBase { class PlayJavaStreamedWSClientTest extends PlayWSClientTestBase {
@Shared @Shared
StandaloneWSClient wsClient StandaloneWSClient wsClient
@ -81,6 +84,7 @@ class PlayJavaStreamedWSClientTest extends PlayWSClientTestBase {
} }
} }
@Timeout(5)
class PlayScalaWSClientTest extends PlayWSClientTestBase { class PlayScalaWSClientTest extends PlayWSClientTestBase {
@Shared @Shared
play.api.libs.ws.StandaloneWSClient wsClient play.api.libs.ws.StandaloneWSClient wsClient
@ -111,6 +115,7 @@ class PlayScalaWSClientTest extends PlayWSClientTestBase {
} }
} }
@Timeout(5)
class PlayScalaStreamedWSClientTest extends PlayWSClientTestBase { class PlayScalaStreamedWSClientTest extends PlayWSClientTestBase {
@Shared @Shared
play.api.libs.ws.StandaloneWSClient wsClient play.api.libs.ws.StandaloneWSClient wsClient

View File

@ -0,0 +1 @@
logs/

View File

@ -0,0 +1,57 @@
ext {
minJavaVersionForTests = JavaVersion.VERSION_1_8
// Play doesn't work with Java 9+ until 2.6.12
maxJavaVersionForTests = JavaVersion.VERSION_1_8
}
apply from: "${rootDir}/gradle/instrumentation.gradle"
apply from: "${rootDir}/gradle/test-with-scala.gradle"
apply plugin: 'org.unbroken-dome.test-sets'
muzzle {
pass {
group = 'com.typesafe.play'
module = 'play_2.11'
versions = '[2.3.0,2.4)'
assertInverse = true
}
fail {
group = 'com.typesafe.play'
module = 'play_2.12'
versions = '[,]'
}
fail {
group = 'com.typesafe.play'
module = 'play_2.13'
versions = '[,]'
}
}
testSets {
latestDepTest {
dirName = 'test'
}
}
dependencies {
main_java8Compile group: 'com.typesafe.play', name: 'play_2.11', version: '2.3.0'
testCompile project(':instrumentation:netty:netty-3.8')
testCompile group: 'com.typesafe.play', name: 'play-java_2.11', version: '2.3.0'
testCompile group: 'com.typesafe.play', name: 'play-java-ws_2.11', version: '2.3.0'
testCompile(group: 'com.typesafe.play', name: 'play-test_2.11', version: '2.3.0') {
exclude group: 'org.eclipse.jetty', module: 'jetty-websocket'
}
latestDepTestCompile group: 'com.typesafe.play', name: 'play-java_2.11', version: '2.3.+'
latestDepTestCompile group: 'com.typesafe.play', name: 'play-java-ws_2.11', version: '2.3.+'
latestDepTestCompile(group: 'com.typesafe.play', name: 'play-test_2.11', version: '2.3.+') {
exclude group: 'org.eclipse.jetty', module: 'jetty-websocket'
}
}
compileLatestDepTestGroovy {
classpath = classpath.plus(files(compileLatestDepTestScala.destinationDir))
dependsOn compileLatestDepTestScala
}

View File

@ -0,0 +1,67 @@
/*
* Copyright 2020, OpenTelemetry Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.opentelemetry.auto.instrumentation.play.v2_3;
import static io.opentelemetry.auto.tooling.ClassLoaderMatcher.hasClassesNamed;
import static io.opentelemetry.auto.tooling.bytebuddy.matcher.AgentElementMatchers.implementsInterface;
import static java.util.Collections.singletonMap;
import static net.bytebuddy.matcher.ElementMatchers.named;
import static net.bytebuddy.matcher.ElementMatchers.returns;
import static net.bytebuddy.matcher.ElementMatchers.takesArgument;
import com.google.auto.service.AutoService;
import io.opentelemetry.auto.tooling.Instrumenter;
import java.util.Map;
import net.bytebuddy.description.method.MethodDescription;
import net.bytebuddy.description.type.TypeDescription;
import net.bytebuddy.matcher.ElementMatcher;
@AutoService(Instrumenter.class)
public final class PlayInstrumentation extends Instrumenter.Default {
public PlayInstrumentation() {
super("play", "play-action");
}
@Override
public ElementMatcher<ClassLoader> classLoaderMatcher() {
// Optimization for expensive typeMatcher.
return hasClassesNamed("play.api.mvc.Action");
}
@Override
public ElementMatcher<TypeDescription> typeMatcher() {
return implementsInterface(named("play.api.mvc.Action"));
}
@Override
public String[] helperClassNames() {
return new String[] {
packageName + ".PlayHttpServerDecorator",
packageName + ".RequestCompleteCallback",
packageName + ".PlayHeaders",
};
}
@Override
public Map<? extends ElementMatcher<? super MethodDescription>, String> transformers() {
return singletonMap(
named("apply")
.and(takesArgument(0, named("play.api.mvc.Request")))
.and(returns(named("scala.concurrent.Future"))),
packageName + ".PlayAdvice");
}
}

View File

@ -0,0 +1,82 @@
/*
* Copyright 2020, OpenTelemetry Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.opentelemetry.auto.instrumentation.play.v2_3;
import static io.opentelemetry.auto.bootstrap.instrumentation.decorator.BaseDecorator.extract;
import static io.opentelemetry.auto.instrumentation.play.v2_3.PlayHeaders.GETTER;
import static io.opentelemetry.auto.instrumentation.play.v2_3.PlayHttpServerDecorator.DECORATE;
import static io.opentelemetry.auto.instrumentation.play.v2_3.PlayHttpServerDecorator.TRACER;
import static io.opentelemetry.trace.TracingContextUtils.currentContextWith;
import io.opentelemetry.auto.instrumentation.api.SpanWithScope;
import io.opentelemetry.auto.instrumentation.api.Tags;
import io.opentelemetry.trace.Span;
import io.opentelemetry.trace.SpanContext;
import net.bytebuddy.asm.Advice;
import play.api.mvc.Action;
import play.api.mvc.Request;
import play.api.mvc.Result;
import scala.concurrent.Future;
public class PlayAdvice {
@Advice.OnMethodEnter(suppress = Throwable.class)
public static SpanWithScope onEnter(@Advice.Argument(0) final Request req) {
final Span.Builder spanBuilder = TRACER.spanBuilder("play.request");
if (!TRACER.getCurrentSpan().getContext().isValid()) {
final SpanContext extractedContext = extract(req.headers(), GETTER);
if (extractedContext.isValid()) {
spanBuilder.setParent(extractedContext);
}
} else {
// An upstream framework (e.g. akka-http, netty) has already started the span.
// Do not extract the context.
}
final Span span = spanBuilder.startSpan();
DECORATE.afterStart(span);
DECORATE.onConnection(span, req);
return new SpanWithScope(span, currentContextWith(span));
}
@Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class)
public static void stopTraceOnResponse(
@Advice.Enter final SpanWithScope playControllerScope,
@Advice.This final Object thisAction,
@Advice.Thrown final Throwable throwable,
@Advice.Argument(0) final Request req,
@Advice.Return(readOnly = false) final Future<Result> responseFuture) {
final Span playControllerSpan = playControllerScope.getSpan();
// Call onRequest on return after tags are populated.
DECORATE.onRequest(playControllerSpan, req);
if (throwable == null) {
responseFuture.onComplete(
new RequestCompleteCallback(playControllerSpan),
((Action) thisAction).executionContext());
} else {
DECORATE.onError(playControllerSpan, throwable);
playControllerSpan.setAttribute(Tags.HTTP_STATUS, 500);
DECORATE.beforeFinish(playControllerSpan);
playControllerSpan.end();
}
playControllerScope.closeScope();
final Span rootSpan = TRACER.getCurrentSpan();
// set the resource name on the upstream akka/netty span
DECORATE.onRequest(rootSpan, req);
}
}

View File

@ -0,0 +1,35 @@
/*
* Copyright 2020, OpenTelemetry Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.opentelemetry.auto.instrumentation.play.v2_3;
import io.opentelemetry.context.propagation.HttpTextFormat;
import play.api.mvc.Headers;
import scala.Option;
public class PlayHeaders implements HttpTextFormat.Getter<Headers> {
public static final PlayHeaders GETTER = new PlayHeaders();
@Override
public String get(final Headers headers, final String key) {
final Option<String> option = headers.get(key);
if (option.isDefined()) {
return option.get();
} else {
return null;
}
}
}

View File

@ -0,0 +1,100 @@
/*
* Copyright 2020, OpenTelemetry Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.opentelemetry.auto.instrumentation.play.v2_3;
import io.opentelemetry.OpenTelemetry;
import io.opentelemetry.auto.bootstrap.instrumentation.decorator.HttpServerDecorator;
import io.opentelemetry.auto.instrumentation.api.Tags;
import io.opentelemetry.trace.Span;
import io.opentelemetry.trace.Tracer;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.UndeclaredThrowableException;
import java.net.URI;
import java.net.URISyntaxException;
import lombok.extern.slf4j.Slf4j;
import play.api.mvc.Request;
import play.api.mvc.Result;
import scala.Option;
@Slf4j
public class PlayHttpServerDecorator extends HttpServerDecorator<Request, Request, Result> {
public static final PlayHttpServerDecorator DECORATE = new PlayHttpServerDecorator();
public static final Tracer TRACER =
OpenTelemetry.getTracerProvider().get("io.opentelemetry.auto.play-2.4");
@Override
protected String getComponentName() {
return "play-action";
}
@Override
protected String method(final Request httpRequest) {
return httpRequest.method();
}
@Override
protected URI url(final Request request) throws URISyntaxException {
return new URI((request.secure() ? "https://" : "http://") + request.host() + request.uri());
}
@Override
protected String peerHostIP(final Request request) {
return request.remoteAddress();
}
@Override
protected Integer peerPort(final Request request) {
return null;
}
@Override
protected Integer status(final Result httpResponse) {
return httpResponse.header().status();
}
@Override
public Span onRequest(final Span span, final Request request) {
super.onRequest(span, request);
if (request != null) {
// more about routes here:
// https://github.com/playframework/playframework/blob/master/documentation/manual/releases/release26/migration26/Migration26.md#router-tags-are-now-attributes
final Option pathOption = request.tags().get("ROUTE_PATTERN");
if (!pathOption.isEmpty()) {
final String path = (String) pathOption.get();
span.updateName(request.method() + " " + path);
}
}
return span;
}
@Override
public Span onError(final Span span, Throwable throwable) {
span.setAttribute(Tags.HTTP_STATUS, 500);
if (throwable != null
// This can be moved to instanceof check when using Java 8.
&& throwable.getClass().getName().equals("java.util.concurrent.CompletionException")
&& throwable.getCause() != null) {
throwable = throwable.getCause();
}
while ((throwable instanceof InvocationTargetException
|| throwable instanceof UndeclaredThrowableException)
&& throwable.getCause() != null) {
throwable = throwable.getCause();
}
return super.onError(span, throwable);
}
}

View File

@ -0,0 +1,49 @@
/*
* Copyright 2020, OpenTelemetry Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package io.opentelemetry.auto.instrumentation.play.v2_3;
import static io.opentelemetry.auto.instrumentation.play.v2_3.PlayHttpServerDecorator.DECORATE;
import io.opentelemetry.trace.Span;
import lombok.extern.slf4j.Slf4j;
import play.api.mvc.Result;
import scala.util.Try;
@Slf4j
public class RequestCompleteCallback extends scala.runtime.AbstractFunction1<Try<Result>, Object> {
private final Span span;
public RequestCompleteCallback(final Span span) {
this.span = span;
}
@Override
public Object apply(final Try<Result> result) {
try {
if (result.isFailure()) {
DECORATE.onError(span, result.failed().get());
} else {
DECORATE.onResponse(span, result.get());
}
DECORATE.beforeFinish(span);
} catch (final Throwable t) {
log.debug("error in play instrumentation", t);
} finally {
span.end();
}
return null;
}
}

View File

@ -0,0 +1,86 @@
/*
* Copyright 2020, OpenTelemetry Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package client
import io.opentelemetry.auto.test.base.HttpClientTest
import play.GlobalSettings
import play.libs.ws.WS
import play.test.FakeApplication
import play.test.Helpers
import spock.lang.Shared
import java.util.concurrent.TimeUnit
class PlayWSClientTest extends HttpClientTest {
@Shared
def application = new FakeApplication(
new File("."),
FakeApplication.getClassLoader(),
[
"ws.timeout.connection": CONNECT_TIMEOUT_MS,
"ws.timeout.request" : READ_TIMEOUT_MS
],
Collections.emptyList(),
new GlobalSettings()
)
@Shared
def client
def setupSpec() {
Helpers.start(application)
client = WS.client()
}
def cleanupSpec() {
Helpers.stop(application)
}
@Override
int doRequest(String method, URI uri, Map<String, String> headers, Closure callback) {
def request = client.url(uri.toString())
headers.entrySet().each {
request.setHeader(it.key, it.value)
}
def status = request.execute(method).map({
callback?.call()
it
}).map({
it.status
})
return status.get(1, TimeUnit.SECONDS)
}
@Override
boolean testRedirects() {
false
}
@Override
boolean testConnectionFailure() {
false
}
@Override
boolean testRemoteConnection() {
// On connection failures the operation and resource names end up different from expected.
// This would require a lot of changes to the base client test class to support
// span.operationName = "netty.connect"
// span.resourceName = "netty.connect"
false
}
}

View File

@ -0,0 +1,38 @@
/*
* Copyright 2020, OpenTelemetry Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package server;
import static net.bytebuddy.matcher.ElementMatchers.named;
import com.google.auto.service.AutoService;
import io.opentelemetry.auto.test.base.HttpServerTestAdvice;
import io.opentelemetry.auto.tooling.Instrumenter;
import net.bytebuddy.agent.builder.AgentBuilder;
@AutoService(Instrumenter.class)
public class NettyServerTestInstrumentation implements Instrumenter {
@Override
public AgentBuilder instrument(final AgentBuilder agentBuilder) {
return agentBuilder
.type(named("org.jboss.netty.handler.codec.http.HttpRequestDecoder"))
.transform(
new AgentBuilder.Transformer.ForAdvice()
.advice(
named("createMessage"),
HttpServerTestAdvice.ServerEntryAdvice.class.getName()));
}
}

View File

@ -0,0 +1,27 @@
/*
* Copyright 2020, OpenTelemetry Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package server
import play.api.test.TestServer
class PlayAsyncServerTest extends PlayServerTest {
@Override
TestServer startServer(int port) {
def server = AsyncServer.server(port)
server.start()
return server
}
}

View File

@ -0,0 +1,70 @@
/*
* Copyright 2020, OpenTelemetry Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package server
import io.opentelemetry.auto.instrumentation.api.MoreTags
import io.opentelemetry.auto.instrumentation.api.Tags
import io.opentelemetry.auto.test.asserts.TraceAssert
import io.opentelemetry.auto.test.base.HttpServerTest
import io.opentelemetry.sdk.trace.data.SpanData
import play.api.test.TestServer
import static io.opentelemetry.auto.test.base.HttpServerTest.ServerEndpoint.ERROR
import static io.opentelemetry.auto.test.base.HttpServerTest.ServerEndpoint.EXCEPTION
import static io.opentelemetry.auto.test.base.HttpServerTest.ServerEndpoint.SUCCESS
import static io.opentelemetry.trace.Span.Kind.INTERNAL
class PlayServerTest extends HttpServerTest<TestServer> {
@Override
TestServer startServer(int port) {
def server = SyncServer.server(port)
server.start()
return server
}
@Override
void stopServer(TestServer server) {
server.stop()
}
// We don't have instrumentation for this version of netty yet
@Override
boolean hasHandlerSpan() {
true
}
@Override
void handlerSpan(TraceAssert trace, int index, Object parent, String method = "GET", ServerEndpoint endpoint = SUCCESS) {
trace.span(index) {
operationName "play.request"
spanKind INTERNAL
errored endpoint == ERROR || endpoint == EXCEPTION
childOf((SpanData) parent)
tags {
"$MoreTags.NET_PEER_IP" { it == null || it == "127.0.0.1" } // Optional
"$Tags.HTTP_URL" String
"$Tags.HTTP_METHOD" String
"$Tags.HTTP_STATUS" Long
if (endpoint == EXCEPTION) {
errorTags(Exception, EXCEPTION.body)
}
if (endpoint.query) {
"$MoreTags.HTTP_QUERY" endpoint.query
}
}
}
}
}

View File

@ -0,0 +1,41 @@
/*
* Copyright 2020, OpenTelemetry Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package server
import io.opentelemetry.auto.test.base.HttpServerTest
import io.opentelemetry.auto.test.base.HttpServerTest.ServerEndpoint._
import play.api.mvc.{Action, Handler, Results}
import play.api.test.{FakeApplication, TestServer}
import scala.concurrent.Future
object AsyncServer {
val routes: PartialFunction[(String, String), Handler] = {
case ("GET", "/success") => Action.async { request => HttpServerTest.controller(SUCCESS, new AsyncControllerClosureAdapter(Future.successful(Results.Status(SUCCESS.getStatus).apply(SUCCESS.getBody)))) }
case ("GET", "/redirect") => Action.async { request => HttpServerTest.controller(REDIRECT, new AsyncControllerClosureAdapter(Future.successful(Results.Redirect(REDIRECT.getBody, REDIRECT.getStatus)))) }
case ("GET", "/query") => Action.async { result => HttpServerTest.controller(QUERY_PARAM, new AsyncControllerClosureAdapter(Future.successful(Results.Status(QUERY_PARAM.getStatus).apply(QUERY_PARAM.getBody)))) }
case ("GET", "/error-status") => Action.async { result => HttpServerTest.controller(ERROR, new AsyncControllerClosureAdapter(Future.successful(Results.Status(ERROR.getStatus).apply(ERROR.getBody)))) }
case ("GET", "/exception") => Action.async { result =>
HttpServerTest.controller(EXCEPTION, new AsyncBlockClosureAdapter(() => {
throw new Exception(EXCEPTION.getBody)
}))
}
}
def server(port: Int): TestServer = {
TestServer(port, FakeApplication(withGlobal = Some(new Settings()), withRoutes = routes))
}
}

View File

@ -0,0 +1,37 @@
/*
* Copyright 2020, OpenTelemetry Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package server
import groovy.lang.Closure
import play.api.mvc.Result
import scala.concurrent.Future
class ControllerClosureAdapter(response: Result) extends Closure[Result] {
override def call(): Result = response
}
class BlockClosureAdapter(block: () => Result) extends Closure[Result] {
override def call(): Result = block()
}
class AsyncControllerClosureAdapter(response: Future[Result]) extends Closure[Future[Result]] {
override def call(): Future[Result] = response
}
class AsyncBlockClosureAdapter(block: () => Future[Result]) extends Closure[Future[Result]] {
override def call(): Future[Result] = block()
}

View File

@ -0,0 +1,27 @@
/*
* Copyright 2020, OpenTelemetry Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package server
import play.api.GlobalSettings
import play.api.mvc.{RequestHeader, Result, Results}
import scala.concurrent.Future
class Settings extends GlobalSettings {
override def onError(request: RequestHeader, ex: Throwable): Future[Result] = {
Future.successful(Results.InternalServerError(ex.getCause.getMessage))
}
}

View File

@ -0,0 +1,47 @@
/*
* Copyright 2020, OpenTelemetry Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package server
import io.opentelemetry.auto.test.base.HttpServerTest
import io.opentelemetry.auto.test.base.HttpServerTest.ServerEndpoint._
import play.api.mvc.{Action, Handler, Results}
import play.api.test.{FakeApplication, TestServer}
object SyncServer {
val routes: PartialFunction[(String, String), Handler] = {
case ("GET", "/success") => Action { request =>
HttpServerTest.controller(SUCCESS, new ControllerClosureAdapter(Results.Status(SUCCESS.getStatus).apply(SUCCESS.getBody)))
}
case ("GET", "/redirect") => Action { request =>
HttpServerTest.controller(REDIRECT, new ControllerClosureAdapter(Results.Redirect(REDIRECT.getBody, REDIRECT.getStatus)))
}
case ("GET", "/query") => Action { request =>
HttpServerTest.controller(QUERY_PARAM, new ControllerClosureAdapter(Results.Status(QUERY_PARAM.getStatus).apply(QUERY_PARAM.getBody)))
}
case ("GET", "/error-status") => Action { request =>
HttpServerTest.controller(ERROR, new ControllerClosureAdapter(Results.Status(ERROR.getStatus).apply(ERROR.getBody)))
}
case ("GET", "/exception") => Action { request =>
HttpServerTest.controller(EXCEPTION, new BlockClosureAdapter(() => {
throw new Exception(EXCEPTION.getBody)
}))
}
}
def server(port: Int): TestServer = {
TestServer(port, FakeApplication(withGlobal = Some(new Settings()), withRoutes = routes))
}
}

View File

@ -20,9 +20,11 @@ import play.libs.ws.WS
import spock.lang.AutoCleanup import spock.lang.AutoCleanup
import spock.lang.Shared import spock.lang.Shared
import spock.lang.Subject import spock.lang.Subject
import spock.lang.Timeout
// Play 2.6+ uses a separately versioned client that shades the underlying dependency // Play 2.6+ uses a separately versioned client that shades the underlying dependency
// This means our built in instrumentation won't work. // This means our built in instrumentation won't work.
@Timeout(5)
class PlayWSClientTest extends HttpClientTest { class PlayWSClientTest extends HttpClientTest {
@Subject @Subject
@Shared @Shared
@ -54,4 +56,9 @@ class PlayWSClientTest extends HttpClientTest {
boolean testConnectionFailure() { boolean testConnectionFailure() {
false false
} }
@Override
boolean testRemoteConnection() {
return false
}
} }

View File

@ -21,7 +21,11 @@ import ratpack.http.client.HttpClient
import ratpack.test.exec.ExecHarness import ratpack.test.exec.ExecHarness
import spock.lang.AutoCleanup import spock.lang.AutoCleanup
import spock.lang.Shared import spock.lang.Shared
import spock.lang.Timeout
import java.time.Duration
@Timeout(5)
class RatpackHttpClientTest extends HttpClientTest { class RatpackHttpClientTest extends HttpClientTest {
@AutoCleanup @AutoCleanup
@ -29,12 +33,16 @@ class RatpackHttpClientTest extends HttpClientTest {
ExecHarness exec = ExecHarness.harness() ExecHarness exec = ExecHarness.harness()
@Shared @Shared
def client = HttpClient.of {} def client = HttpClient.of {
it.readTimeout(Duration.ofSeconds(2))
// Connect timeout added in 1.5
}
@Override @Override
int doRequest(String method, URI uri, Map<String, String> headers, Closure callback) { int doRequest(String method, URI uri, Map<String, String> headers, Closure callback) {
ExecResult<Integer> result = exec.yield { ExecResult<Integer> result = exec.yield {
def resp = client.request(uri) { spec -> def resp = client.request(uri) { spec ->
spec.connectTimeout(Duration.ofSeconds(2))
spec.method(method) spec.method(method)
spec.headers { headersSpec -> spec.headers { headersSpec ->
headers.entrySet().each { headers.entrySet().each {
@ -59,4 +67,9 @@ class RatpackHttpClientTest extends HttpClientTest {
boolean testConnectionFailure() { boolean testConnectionFailure() {
false false
} }
@Override
boolean testRemoteConnection() {
return false
}
} }

View File

@ -24,12 +24,14 @@ import org.springframework.web.reactive.function.client.ClientResponse
import org.springframework.web.reactive.function.client.WebClient import org.springframework.web.reactive.function.client.WebClient
import spock.lang.Ignore import spock.lang.Ignore
import spock.lang.Shared import spock.lang.Shared
import spock.lang.Timeout
import static io.opentelemetry.trace.Span.Kind.CLIENT import static io.opentelemetry.trace.Span.Kind.CLIENT
// FIXME this instrumentation is not currently reliable and so is currently disabled // FIXME this instrumentation is not currently reliable and so is currently disabled
// see DefaultWebClientInstrumentation and DefaultWebClientAdvice // see DefaultWebClientInstrumentation and DefaultWebClientAdvice
@Ignore @Ignore
@Timeout(5)
class SpringWebfluxHttpClientTest extends HttpClientTest { class SpringWebfluxHttpClientTest extends HttpClientTest {
@Shared @Shared
@ -93,4 +95,9 @@ class SpringWebfluxHttpClientTest extends HttpClientTest {
boolean testConnectionFailure() { boolean testConnectionFailure() {
false false
} }
boolean testRemoteConnection() {
// FIXME: figure out how to configure timeouts.
false
}
} }

View File

@ -18,7 +18,7 @@ package client
import io.opentelemetry.auto.test.base.HttpClientTest import io.opentelemetry.auto.test.base.HttpClientTest
import io.vertx.core.Vertx import io.vertx.core.Vertx
import io.vertx.core.VertxOptions import io.vertx.core.VertxOptions
import io.vertx.core.http.HttpClient import io.vertx.core.http.HttpClientOptions
import io.vertx.core.http.HttpClientResponse import io.vertx.core.http.HttpClientResponse
import io.vertx.core.http.HttpMethod import io.vertx.core.http.HttpMethod
import spock.lang.Shared import spock.lang.Shared
@ -30,9 +30,11 @@ import java.util.concurrent.CompletableFuture
class VertxHttpClientTest extends HttpClientTest { class VertxHttpClientTest extends HttpClientTest {
@Shared @Shared
Vertx vertx = Vertx.vertx(new VertxOptions()) def vertx = Vertx.vertx(new VertxOptions())
@Shared @Shared
HttpClient httpClient = vertx.createHttpClient() def clientOptions = new HttpClientOptions().setConnectTimeout(CONNECT_TIMEOUT_MS).setIdleTimeout(READ_TIMEOUT_MS)
@Shared
def httpClient = vertx.createHttpClient(clientOptions)
@Override @Override
int doRequest(String method, URI uri, Map<String, String> headers, Closure callback) { int doRequest(String method, URI uri, Map<String, String> headers, Closure callback) {
@ -57,4 +59,9 @@ class VertxHttpClientTest extends HttpClientTest {
boolean testConnectionFailure() { boolean testConnectionFailure() {
false false
} }
boolean testRemoteConnection() {
// FIXME: figure out how to configure timeouts.
false
}
} }

View File

@ -19,6 +19,7 @@ import io.opentelemetry.auto.test.base.HttpClientTest
import io.vertx.circuitbreaker.CircuitBreakerOptions import io.vertx.circuitbreaker.CircuitBreakerOptions
import io.vertx.core.VertxOptions import io.vertx.core.VertxOptions
import io.vertx.core.http.HttpMethod import io.vertx.core.http.HttpMethod
import io.vertx.ext.web.client.WebClientOptions
import io.vertx.reactivex.circuitbreaker.CircuitBreaker import io.vertx.reactivex.circuitbreaker.CircuitBreaker
import io.vertx.reactivex.core.Vertx import io.vertx.reactivex.core.Vertx
import io.vertx.reactivex.ext.web.client.WebClient import io.vertx.reactivex.ext.web.client.WebClient
@ -33,7 +34,9 @@ class VertxRxCircuitBreakerWebClientTest extends HttpClientTest {
@Shared @Shared
Vertx vertx = Vertx.vertx(new VertxOptions()) Vertx vertx = Vertx.vertx(new VertxOptions())
@Shared @Shared
WebClient client = WebClient.create(vertx) def clientOptions = new WebClientOptions().setConnectTimeout(CONNECT_TIMEOUT_MS).setIdleTimeout(READ_TIMEOUT_MS)
@Shared
WebClient client = WebClient.create(vertx, clientOptions)
@Shared @Shared
CircuitBreaker breaker = CircuitBreaker.create("my-circuit-breaker", vertx, CircuitBreaker breaker = CircuitBreaker.create("my-circuit-breaker", vertx,
new CircuitBreakerOptions() new CircuitBreakerOptions()
@ -73,4 +76,9 @@ class VertxRxCircuitBreakerWebClientTest extends HttpClientTest {
boolean testConnectionFailure() { boolean testConnectionFailure() {
false false
} }
boolean testRemoteConnection() {
// FIXME: figure out how to configure timeouts.
false
}
} }

View File

@ -18,6 +18,7 @@ package client
import io.opentelemetry.auto.test.base.HttpClientTest import io.opentelemetry.auto.test.base.HttpClientTest
import io.vertx.core.VertxOptions import io.vertx.core.VertxOptions
import io.vertx.core.http.HttpMethod import io.vertx.core.http.HttpMethod
import io.vertx.ext.web.client.WebClientOptions
import io.vertx.reactivex.core.Vertx import io.vertx.reactivex.core.Vertx
import io.vertx.reactivex.ext.web.client.WebClient import io.vertx.reactivex.ext.web.client.WebClient
import spock.lang.Shared import spock.lang.Shared
@ -29,7 +30,9 @@ class VertxRxWebClientTest extends HttpClientTest {
@Shared @Shared
Vertx vertx = Vertx.vertx(new VertxOptions()) Vertx vertx = Vertx.vertx(new VertxOptions())
@Shared @Shared
WebClient client = WebClient.create(vertx) def clientOptions = new WebClientOptions().setConnectTimeout(CONNECT_TIMEOUT_MS).setIdleTimeout(READ_TIMEOUT_MS)
@Shared
WebClient client = WebClient.create(vertx, clientOptions)
@Override @Override
int doRequest(String method, URI uri, Map<String, String> headers, Closure callback) { int doRequest(String method, URI uri, Map<String, String> headers, Closure callback) {
@ -52,4 +55,9 @@ class VertxRxWebClientTest extends HttpClientTest {
boolean testConnectionFailure() { boolean testConnectionFailure() {
false false
} }
boolean testRemoteConnection() {
// FIXME: figure out how to configure timeouts.
false
}
} }

View File

@ -111,6 +111,7 @@ include ':instrumentation:netty:netty-4.0'
include ':instrumentation:netty:netty-4.1' include ':instrumentation:netty:netty-4.1'
include ':instrumentation:okhttp-3.0' include ':instrumentation:okhttp-3.0'
include ':instrumentation:opentelemetry-api-0.3' include ':instrumentation:opentelemetry-api-0.3'
include ':instrumentation:play:play-2.3'
include ':instrumentation:play:play-2.4' include ':instrumentation:play:play-2.4'
include ':instrumentation:play:play-2.6' include ':instrumentation:play:play-2.6'
include ':instrumentation:play-ws:play-ws-1.0' include ':instrumentation:play-ws:play-ws-1.0'

View File

@ -21,7 +21,7 @@ import spock.lang.Specification
abstract class AbstractSmokeTest extends Specification { abstract class AbstractSmokeTest extends Specification {
public static final PROFILING_API_KEY = "org2_api_key" public static final API_KEY = "some-api-key"
public static final PROFILING_START_DELAY_SECONDS = 1 public static final PROFILING_START_DELAY_SECONDS = 1
public static final int PROFILING_RECORDING_UPLOAD_PERIOD_SECONDS = 5 public static final int PROFILING_RECORDING_UPLOAD_PERIOD_SECONDS = 5
@ -68,7 +68,7 @@ abstract class AbstractSmokeTest extends Specification {
ProcessBuilder processBuilder = createProcessBuilder() ProcessBuilder processBuilder = createProcessBuilder()
processBuilder.environment().put("JAVA_HOME", System.getProperty("java.home")) processBuilder.environment().put("JAVA_HOME", System.getProperty("java.home"))
processBuilder.environment().put("DD_PROFILING_APIKEY", PROFILING_API_KEY) processBuilder.environment().put("DD_API_KEY", API_KEY)
processBuilder.redirectErrorStream(true) processBuilder.redirectErrorStream(true)
logfile = new File("${buildDirectory}/reports/testProcess.${this.getClass().getName()}.log") logfile = new File("${buildDirectory}/reports/testProcess.${this.getClass().getName()}.log")

View File

@ -23,6 +23,7 @@ import io.opentelemetry.auto.test.AgentTestRunner
import io.opentelemetry.auto.test.asserts.TraceAssert import io.opentelemetry.auto.test.asserts.TraceAssert
import io.opentelemetry.sdk.trace.data.SpanData import io.opentelemetry.sdk.trace.data.SpanData
import spock.lang.AutoCleanup import spock.lang.AutoCleanup
import spock.lang.Requires
import spock.lang.Shared import spock.lang.Shared
import spock.lang.Unroll import spock.lang.Unroll
@ -40,6 +41,8 @@ import static org.junit.Assume.assumeTrue
@Unroll @Unroll
abstract class HttpClientTest extends AgentTestRunner { abstract class HttpClientTest extends AgentTestRunner {
protected static final BODY_METHODS = ["POST", "PUT"] protected static final BODY_METHODS = ["POST", "PUT"]
protected static final CONNECT_TIMEOUT_MS = 1000
protected static final READ_TIMEOUT_MS = 2000
@AutoCleanup @AutoCleanup
@Shared @Shared
@ -250,8 +253,7 @@ abstract class HttpClientTest extends AgentTestRunner {
def "basic #method request with circular redirects"() { def "basic #method request with circular redirects"() {
given: given:
assumeTrue(testRedirects()) assumeTrue(testRedirects() && testCircularRedirects())
assumeTrue(testCircularRedirects())
def uri = server.address.resolve("/circular-redirect") def uri = server.address.resolve("/circular-redirect")
when: when:
@ -300,6 +302,77 @@ abstract class HttpClientTest extends AgentTestRunner {
method = "GET" method = "GET"
} }
def "connection error dropped request"() {
given:
assumeTrue(testRemoteConnection())
// https://stackoverflow.com/a/100859
def uri = new URI("http://www.google.com:81/")
when:
runUnderTrace("parent") {
doRequest(method, uri)
}
then:
def ex = thrown(Exception)
def thrownException = ex instanceof ExecutionException ? ex.cause : ex
assertTraces(1) {
trace(0, 2 + extraClientSpans()) {
basicSpan(it, 0, "parent", null, thrownException)
clientSpan(it, 1, span(0), method, false, uri, null, thrownException)
}
}
where:
method = "HEAD"
}
def "connection error non routable address"() {
given:
assumeTrue(testRemoteConnection())
def uri = new URI("https://192.0.2.1/")
when:
runUnderTrace("parent") {
doRequest(method, uri)
}
then:
def ex = thrown(Exception)
def thrownException = ex instanceof ExecutionException ? ex.cause : ex
assertTraces(1) {
trace(0, 2 + extraClientSpans()) {
basicSpan(it, 0, "parent", null, thrownException)
clientSpan(it, 1, span(0), method, false, uri, null, thrownException)
}
}
where:
method = "HEAD"
}
// IBM JVM has different protocol support for TLS
@Requires({ !System.getProperty("java.vm.name").contains("IBM J9 VM") })
def "test https request"() {
given:
assumeTrue(testRemoteConnection())
def uri = new URI("https://www.google.com/")
when:
def status = doRequest(method, uri)
then:
status == 200
assertTraces(1) {
trace(0, 1 + extraClientSpans()) {
clientSpan(it, 0, null, method, false, uri)
}
}
where:
method = "HEAD"
}
// parent span must be cast otherwise it breaks debugging classloading (junit loads it early) // parent span must be cast otherwise it breaks debugging classloading (junit loads it early)
void clientSpan(TraceAssert trace, int index, Object parentSpan, String method = "GET", boolean tagQueryString = false, URI uri = server.address.resolve("/success"), Integer status = 200, Throwable exception = null) { void clientSpan(TraceAssert trace, int index, Object parentSpan, String method = "GET", boolean tagQueryString = false, URI uri = server.address.resolve("/success"), Integer status = 200, Throwable exception = null) {
trace.span(index) { trace.span(index) {
@ -312,9 +385,9 @@ abstract class HttpClientTest extends AgentTestRunner {
spanKind CLIENT spanKind CLIENT
errored exception != null errored exception != null
tags { tags {
"$MoreTags.NET_PEER_NAME" "localhost" "$MoreTags.NET_PEER_NAME" uri.host
"$MoreTags.NET_PEER_IP" { it == null || it == "127.0.0.1" } // Optional "$MoreTags.NET_PEER_IP" { it == null || it == "127.0.0.1" } // Optional
"$MoreTags.NET_PEER_PORT" uri.port "$MoreTags.NET_PEER_PORT" uri.port > 0 ? uri.port : { it == null || it == 443 } // Optional
"$Tags.HTTP_URL" { it == "${uri}" || it == "${removeFragment(uri)}" } "$Tags.HTTP_URL" { it == "${uri}" || it == "${removeFragment(uri)}" }
"$Tags.HTTP_METHOD" method "$Tags.HTTP_METHOD" method
if (status) { if (status) {
@ -366,6 +439,10 @@ abstract class HttpClientTest extends AgentTestRunner {
true true
} }
boolean testRemoteConnection() {
true
}
boolean testCallbackWithParent() { boolean testCallbackWithParent() {
// FIXME: this hack is here because callback with parent is broken in play-ws when the stream() // FIXME: this hack is here because callback with parent is broken in play-ws when the stream()
// function is used. There is no way to stop a test from a derived class hence the flag // function is used. There is no way to stop a test from a derived class hence the flag

View File

@ -15,6 +15,7 @@
*/ */
package io.opentelemetry.auto.test.base package io.opentelemetry.auto.test.base
import ch.qos.logback.classic.Level
import io.opentelemetry.auto.bootstrap.instrumentation.decorator.HttpServerDecorator import io.opentelemetry.auto.bootstrap.instrumentation.decorator.HttpServerDecorator
import io.opentelemetry.auto.instrumentation.api.MoreTags import io.opentelemetry.auto.instrumentation.api.MoreTags
import io.opentelemetry.auto.instrumentation.api.Tags import io.opentelemetry.auto.instrumentation.api.Tags
@ -28,6 +29,8 @@ import okhttp3.HttpUrl
import okhttp3.OkHttpClient import okhttp3.OkHttpClient
import okhttp3.Request import okhttp3.Request
import okhttp3.Response import okhttp3.Response
import org.slf4j.Logger
import org.slf4j.LoggerFactory
import spock.lang.Shared import spock.lang.Shared
import spock.lang.Unroll import spock.lang.Unroll
@ -48,6 +51,11 @@ import static org.junit.Assume.assumeTrue
@Unroll @Unroll
abstract class HttpServerTest<SERVER> extends AgentTestRunner { abstract class HttpServerTest<SERVER> extends AgentTestRunner {
public static final Logger SERVER_LOGGER = LoggerFactory.getLogger("http-server")
static {
((ch.qos.logback.classic.Logger) SERVER_LOGGER).setLevel(Level.DEBUG)
}
@Shared @Shared
SERVER server SERVER server
@Shared @Shared

View File

@ -41,17 +41,14 @@ class TestHttpServer implements AutoCloseable {
private static final Tracer TRACER = OpenTelemetry.getTracerProvider().get("io.opentelemetry.auto") private static final Tracer TRACER = OpenTelemetry.getTracerProvider().get("io.opentelemetry.auto")
static TestHttpServer httpServer(boolean start = true, static TestHttpServer httpServer(@DelegatesTo(value = TestHttpServer, strategy = Closure.DELEGATE_FIRST) Closure spec) {
@DelegatesTo(value = TestHttpServer, strategy = Closure.DELEGATE_FIRST) Closure spec) {
def server = new TestHttpServer() def server = new TestHttpServer()
def clone = (Closure) spec.clone() def clone = (Closure) spec.clone()
clone.delegate = server clone.delegate = server
clone.resolveStrategy = Closure.DELEGATE_FIRST clone.resolveStrategy = Closure.DELEGATE_FIRST
clone(server) clone(server)
if (start) {
server.start() server.start()
}
return server return server
} }

View File

@ -17,6 +17,10 @@ package io.opentelemetry.auto.test.utils;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import okhttp3.OkHttpClient; import okhttp3.OkHttpClient;
import okhttp3.logging.HttpLoggingInterceptor;
import okhttp3.logging.HttpLoggingInterceptor.Level;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/** /**
* This class was moved from groovy to java because groovy kept trying to introspect on the * This class was moved from groovy to java because groovy kept trying to introspect on the
@ -24,9 +28,29 @@ import okhttp3.OkHttpClient;
*/ */
public class OkHttpUtils { public class OkHttpUtils {
private static final Logger CLIENT_LOGGER = LoggerFactory.getLogger("http-client");
static {
((ch.qos.logback.classic.Logger) CLIENT_LOGGER).setLevel(ch.qos.logback.classic.Level.DEBUG);
}
private static final HttpLoggingInterceptor LOGGING_INTERCEPTOR =
new HttpLoggingInterceptor(
new HttpLoggingInterceptor.Logger() {
@Override
public void log(final String message) {
CLIENT_LOGGER.debug(message);
}
});
static {
LOGGING_INTERCEPTOR.setLevel(Level.BASIC);
}
static OkHttpClient.Builder clientBuilder() { static OkHttpClient.Builder clientBuilder() {
final TimeUnit unit = TimeUnit.MINUTES; final TimeUnit unit = TimeUnit.MINUTES;
return new OkHttpClient.Builder() return new OkHttpClient.Builder()
.addInterceptor(LOGGING_INTERCEPTOR)
.connectTimeout(1, unit) .connectTimeout(1, unit)
.writeTimeout(1, unit) .writeTimeout(1, unit)
.readTimeout(1, unit); .readTimeout(1, unit);

View File

@ -31,12 +31,12 @@ class ServerTest extends AgentTestRunner {
def "test server lifecycle"() { def "test server lifecycle"() {
setup: setup:
def server = httpServer(startAutomatically) { def server = httpServer {
handlers {} handlers {}
} }
expect: expect:
server.internalServer.isRunning() == startAutomatically server.internalServer.isRunning()
when: when:
server.start() server.start()
@ -57,16 +57,13 @@ class ServerTest extends AgentTestRunner {
server.internalServer.isRunning() server.internalServer.isRunning()
when: when:
server.stop() server.close()
then: then:
!server.internalServer.isRunning() !server.internalServer.isRunning()
cleanup: cleanup:
server.stop() server.close()
where:
startAutomatically << [true, false]
} }
def "server 404's with no handlers"() { def "server 404's with no handlers"() {

View File

@ -26,6 +26,7 @@ dependencies {
compile group: 'com.squareup.okhttp3', name: 'okhttp', version: '3.11.0' // Last version to support Java7 compile group: 'com.squareup.okhttp3', name: 'okhttp', version: '3.11.0' // Last version to support Java7
compile group: 'org.eclipse.jetty', name: 'jetty-server', version: '8.0.0.v20110901' compile group: 'org.eclipse.jetty', name: 'jetty-server', version: '8.0.0.v20110901'
compile group: 'com.squareup.okhttp3', name: 'logging-interceptor', version: versions.okhttp
compile(project(':auto-tooling')) { compile(project(':auto-tooling')) {
// including :opentelemetry-sdk-shaded-for-testing above instead // including :opentelemetry-sdk-shaded-for-testing above instead