Merge pull request #283 from trask/dd-merge

Merge changes from dd-trace-java 0.47.0
This commit is contained in:
Tyler Benson 2020-04-07 12:55:52 -04:00 committed by GitHub
commit 8ba53dc5ca
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
69 changed files with 2395 additions and 451 deletions

View File

@ -1,4 +1,4 @@
version: 2
version: 2.1
defaults: &defaults
working_directory: ~/dd-trace-java
@ -142,6 +142,11 @@ jobs:
environment:
- TEST_TASK: testJava13
test_14:
<<: *default_test_job
environment:
- TEST_TASK: testJava14
check:
<<: *defaults
@ -278,7 +283,13 @@ workflows:
# filters:
# tags:
# only: /.*/
- test_13:
# - test_13:
# requires:
# - build
# filters:
# tags:
# only: /.*/
- test_14:
requires:
- build
filters:
@ -311,7 +322,8 @@ workflows:
- test_11
# - test_zulu11
# - test_12
- test_13
# - test_13
- test_14
- check
filters:
branches:
@ -331,7 +343,8 @@ workflows:
- test_11
# - test_zulu11
# - test_12
- test_13
# - test_13
- test_14
- check
filters:
branches:

View File

@ -0,0 +1,52 @@
name: Add assets to release
on:
release:
types: [published]
env:
VERSION: ${{ github.event.release.name }} #Can't use GITHUB_REF because it starts with a "v"
jobs:
dd-java-agent:
runs-on: ubuntu-latest
steps:
- name: Download from jcenter
run: |
wget https://oss.jfrog.org/artifactory/oss-release-local/com/datadoghq/dd-java-agent/$VERSION/dd-java-agent-$VERSION.jar
- name: Upload to release
uses: actions/upload-release-asset@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
upload_url: ${{ github.event.release.upload_url }}
asset_path: dd-java-agent-${{ env.VERSION }}.jar
asset_name: dd-java-agent-${{ env.VERSION }}.jar
asset_content_type: application/java-archive
dd-trace-api:
runs-on: ubuntu-latest
steps:
- name: Download from jcenter
run: |
wget https://oss.jfrog.org/artifactory/oss-release-local/com/datadoghq/dd-trace-api/$VERSION/dd-trace-api-$VERSION.jar
- name: Upload to release
uses: actions/upload-release-asset@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
upload_url: ${{ github.event.release.upload_url }}
asset_path: dd-trace-api-${{ env.VERSION }}.jar
asset_name: dd-trace-api-${{ env.VERSION }}.jar
asset_content_type: application/java-archive
dd-trace-ot:
runs-on: ubuntu-latest
steps:
- name: Download from jcenter
run: |
wget https://oss.jfrog.org/artifactory/oss-release-local/com/datadoghq/dd-trace-ot/$VERSION/dd-trace-ot-$VERSION.jar
- name: Upload to release
uses: actions/upload-release-asset@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
with:
upload_url: ${{ github.event.release.upload_url }}
asset_path: dd-trace-ot-${{ env.VERSION }}.jar
asset_name: dd-trace-ot-${{ env.VERSION }}.jar
asset_content_type: application/java-archive

View File

@ -0,0 +1,34 @@
/*
* 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.bootstrap;
import java.util.concurrent.Callable;
public interface WeakCache<K, V> {
interface Provider<K, V> {
WeakCache<K, V> newWeakCache();
WeakCache<K, V> newWeakCache(final long maxSize);
}
V getIfPresent(final K key);
V getIfPresentOrCompute(final K key, final Callable<? extends V> loader);
V get(final K key, final Callable<? extends V> loader);
void put(final K key, final V value);
}

View File

@ -33,10 +33,6 @@ public abstract class HttpClientDecorator<REQUEST, RESPONSE> extends ClientDecor
protected abstract URI url(REQUEST request) throws URISyntaxException;
protected abstract String hostname(REQUEST request);
protected abstract Integer port(REQUEST request);
protected abstract Integer status(RESPONSE response);
@Override
@ -68,9 +64,13 @@ public abstract class HttpClientDecorator<REQUEST, RESPONSE> extends ClientDecor
}
if (url.getHost() != null) {
urlBuilder.append(url.getHost());
if (url.getPort() > 0 && url.getPort() != 80 && url.getPort() != 443) {
urlBuilder.append(":");
urlBuilder.append(url.getPort());
span.setAttribute(MoreTags.NET_PEER_NAME, url.getHost());
if (url.getPort() > 0) {
span.setAttribute(MoreTags.NET_PEER_PORT, url.getPort());
if (url.getPort() != 80 && url.getPort() != 443) {
urlBuilder.append(":");
urlBuilder.append(url.getPort());
}
}
}
final String path = url.getPath();
@ -98,15 +98,6 @@ public abstract class HttpClientDecorator<REQUEST, RESPONSE> extends ClientDecor
} catch (final Exception e) {
log.debug("Error tagging url", e);
}
final String hostname = hostname(request);
span.setAttribute(MoreTags.NET_PEER_NAME, hostname);
final Integer port = port(request);
// Negative or Zero ports might represent an unset/null value for an int type. Skip setting.
if (port != null && port > 0) {
span.setAttribute(MoreTags.NET_PEER_PORT, port);
}
}
return span;
}

View File

@ -29,7 +29,6 @@ import java.util.Map;
import java.util.Properties;
import java.util.Set;
import java.util.SortedSet;
import java.util.concurrent.TimeUnit;
import java.util.regex.Pattern;
import lombok.Getter;
import lombok.ToString;
@ -67,8 +66,6 @@ public class Config {
public static final String HTTP_SERVER_TAG_QUERY_STRING = "http.server.tag.query-string";
public static final String HTTP_CLIENT_TAG_QUERY_STRING = "http.client.tag.query-string";
public static final String SCOPE_DEPTH_LIMIT = "trace.scope.depth.limit";
public static final String SPAN_DURATION_ABOVE_AVERAGE_STACKTRACE_MILLIS =
"trace.span.duration-above-average.stacktrace.millis";
public static final String RUNTIME_CONTEXT_FIELD_INJECTION =
"trace.runtime.context.field.injection";
@ -88,8 +85,6 @@ public class Config {
private static final boolean DEFAULT_HTTP_SERVER_TAG_QUERY_STRING = false;
private static final boolean DEFAULT_HTTP_CLIENT_TAG_QUERY_STRING = false;
private static final int DEFAULT_SCOPE_DEPTH_LIMIT = 100;
private static final int DEFAULT_SPAN_DURATION_ABOVE_AVERAGE_STACKTRACE_MILLIS =
(int) TimeUnit.SECONDS.toMillis(1);
public static final boolean DEFAULT_LOG_INJECTION_ENABLED = false;
public static final String DEFAULT_EXPERIMENTAL_LOG_CAPTURE_THRESHOLD = null;
@ -111,7 +106,6 @@ public class Config {
@Getter private final boolean httpServerTagQueryString;
@Getter private final boolean httpClientTagQueryString;
@Getter private final Integer scopeDepthLimit;
@Getter private final long spanDurationAboveAverageStacktraceNanos;
@Getter private final boolean runtimeContextFieldInjection;
@Getter private final boolean logInjectionEnabled;
@ -174,13 +168,6 @@ public class Config {
scopeDepthLimit =
getIntegerSettingFromEnvironment(SCOPE_DEPTH_LIMIT, DEFAULT_SCOPE_DEPTH_LIMIT);
spanDurationAboveAverageStacktraceNanos =
TimeUnit.MILLISECONDS.toNanos(
getIntegerSettingFromEnvironment(
SPAN_DURATION_ABOVE_AVERAGE_STACKTRACE_MILLIS,
DEFAULT_SPAN_DURATION_ABOVE_AVERAGE_STACKTRACE_MILLIS)
.longValue());
runtimeContextFieldInjection =
getBooleanSettingFromEnvironment(
RUNTIME_CONTEXT_FIELD_INJECTION, DEFAULT_RUNTIME_CONTEXT_FIELD_INJECTION);
@ -235,15 +222,6 @@ public class Config {
scopeDepthLimit =
getPropertyIntegerValue(properties, SCOPE_DEPTH_LIMIT, parent.scopeDepthLimit);
// do we care about the integer downcast here?
spanDurationAboveAverageStacktraceNanos =
TimeUnit.MILLISECONDS.toNanos(
getPropertyIntegerValue(
properties,
SPAN_DURATION_ABOVE_AVERAGE_STACKTRACE_MILLIS,
(int)
TimeUnit.NANOSECONDS.toMillis(parent.spanDurationAboveAverageStacktraceNanos)));
runtimeContextFieldInjection =
getPropertyBooleanValue(
properties, RUNTIME_CONTEXT_FIELD_INJECTION, parent.runtimeContextFieldInjection);

View File

@ -21,11 +21,11 @@ class WeakMapTest extends Specification {
def supplier = new CounterSupplier()
def sut = new WeakMap.MapAdapter<String, Integer>(new WeakHashMap<>())
def weakMap = new WeakMap.MapAdapter<String, Integer>(new WeakHashMap<>())
def "getOrCreate a value"() {
when:
def count = sut.computeIfAbsent('key', supplier)
def count = weakMap.computeIfAbsent('key', supplier)
then:
count == 1
@ -34,8 +34,8 @@ class WeakMapTest extends Specification {
def "getOrCreate a value multiple times same class loader same key"() {
when:
def count1 = sut.computeIfAbsent('key', supplier)
def count2 = sut.computeIfAbsent('key', supplier)
def count1 = weakMap.computeIfAbsent('key', supplier)
def count2 = weakMap.computeIfAbsent('key', supplier)
then:
count1 == 1
@ -45,8 +45,8 @@ class WeakMapTest extends Specification {
def "getOrCreate a value multiple times same class loader different keys"() {
when:
def count1 = sut.computeIfAbsent('key1', supplier)
def count2 = sut.computeIfAbsent('key2', supplier)
def count1 = weakMap.computeIfAbsent('key1', supplier)
def count2 = weakMap.computeIfAbsent('key2', supplier)
then:
count1 == 1

View File

@ -27,7 +27,7 @@ import static io.opentelemetry.auto.test.utils.ConfigUtils.withConfigOverride
class HttpClientDecoratorTest extends ClientDecoratorTest {
@Shared
def testUrl = new URI("http://myhost/somepath")
def testUrl = new URI("http://myhost:123/somepath")
def span = Mock(Span)
@ -42,15 +42,15 @@ class HttpClientDecoratorTest extends ClientDecoratorTest {
if (req) {
1 * span.setAttribute(Tags.HTTP_METHOD, req.method)
1 * span.setAttribute(Tags.HTTP_URL, "$req.url")
1 * span.setAttribute(MoreTags.NET_PEER_NAME, req.host)
1 * span.setAttribute(MoreTags.NET_PEER_PORT, req.port)
1 * span.setAttribute(MoreTags.NET_PEER_NAME, req.url.host)
1 * span.setAttribute(MoreTags.NET_PEER_PORT, req.url.port)
}
0 * _
where:
req << [
null,
[method: "test-method", url: testUrl, host: "test-host", port: 555]
[method: "test-method", url: testUrl]
]
}
@ -72,23 +72,28 @@ class HttpClientDecoratorTest extends ClientDecoratorTest {
1 * span.setAttribute(MoreTags.HTTP_FRAGMENT, expectedFragment)
}
1 * span.setAttribute(Tags.HTTP_METHOD, null)
1 * span.setAttribute(MoreTags.NET_PEER_NAME, null)
if (hostname) {
1 * span.setAttribute(MoreTags.NET_PEER_NAME, hostname)
}
if (port) {
1 * span.setAttribute(MoreTags.NET_PEER_PORT, port)
}
0 * _
where:
tagQueryString | url | expectedUrl | expectedQuery | expectedFragment
false | null | null | null | null
false | "" | "/" | "" | null
false | "/path?query" | "/path?query" | "" | null
false | "https://host:0" | "https://host/" | "" | null
false | "https://host/path" | "https://host/path" | "" | null
false | "http://host:99/path?query#fragment" | "http://host:99/path?query#fragment" | "" | null
true | null | null | null | null
true | "" | "/" | null | null
true | "/path?encoded+%28query%29%3F" | "/path?encoded+(query)?" | "encoded+(query)?" | null
true | "https://host:0" | "https://host/" | null | null
true | "https://host/path" | "https://host/path" | null | null
true | "http://host:99/path?query#encoded+%28fragment%29%3F" | "http://host:99/path?query#encoded+(fragment)?" | "query" | "encoded+(fragment)?"
tagQueryString | url | expectedUrl | expectedQuery | expectedFragment | hostname | port
false | null | null | null | null | null | null
false | "" | "/" | "" | null | null | null
false | "/path?query" | "/path?query" | "" | null | null | null
false | "https://host:0" | "https://host/" | "" | null | "host" | null
false | "https://host/path" | "https://host/path" | "" | null | "host" | null
false | "http://host:99/path?query#fragment" | "http://host:99/path?query#fragment" | "" | null | "host" | 99
true | null | null | null | null | null | null
true | "" | "/" | null | null | null | null
true | "/path?encoded+%28query%29%3F" | "/path?encoded+(query)?" | "encoded+(query)?" | null | null | null
true | "https://host:0" | "https://host/" | null | null | "host" | null
true | "https://host/path" | "https://host/path" | null | null | "host" | null
true | "http://host:99/path?query#encoded+%28fragment%29%3F" | "http://host:99/path?query#encoded+(fragment)?" | "query" | "encoded+(fragment)?" | "host" | 99
req = [url: url == null ? null : new URI(url)]
}
@ -166,16 +171,6 @@ class HttpClientDecoratorTest extends ClientDecoratorTest {
return m.url
}
@Override
protected String hostname(Map m) {
return m.host
}
@Override
protected Integer port(Map m) {
return m.port
}
@Override
protected Integer status(Map m) {
return m.status

View File

@ -15,9 +15,13 @@
*/
package io.opentelemetry.auto.tooling;
import io.opentelemetry.auto.bootstrap.WeakCache;
import io.opentelemetry.auto.bootstrap.WeakCache.Provider;
import io.opentelemetry.auto.bootstrap.WeakMap;
import io.opentelemetry.auto.tooling.bytebuddy.AgentCachingPoolStrategy;
import io.opentelemetry.auto.tooling.bytebuddy.AgentLocationStrategy;
import java.util.Iterator;
import java.util.ServiceLoader;
/**
* This class contains class references for objects shared by the agent installer as well as muzzle
@ -31,9 +35,41 @@ public class AgentTooling {
registerWeakMapProvider();
}
private static void registerWeakMapProvider() {
if (!WeakMap.Provider.isProviderRegistered()) {
WeakMap.Provider.registerIfAbsent(new WeakMapSuppliers.WeakConcurrent(new Cleaner()));
// WeakMap.Provider.registerIfAbsent(new WeakMapSuppliers.WeakConcurrent.Inline());
// WeakMap.Provider.registerIfAbsent(new WeakMapSuppliers.Guava());
}
}
private static <K, V> Provider loadWeakCacheProvider() {
final Iterator<Provider> providers =
ServiceLoader.load(Provider.class, AgentInstaller.class.getClassLoader()).iterator();
if (providers.hasNext()) {
final Provider provider = providers.next();
if (providers.hasNext()) {
throw new IllegalStateException(
"Only one implementation of WeakCache.Provider suppose to be in classpath");
}
return provider;
}
throw new IllegalStateException("Can't load implementation of WeakCache.Provider");
}
private static final Provider weakCacheProvider = loadWeakCacheProvider();
private static final AgentLocationStrategy LOCATION_STRATEGY = new AgentLocationStrategy();
private static final AgentCachingPoolStrategy POOL_STRATEGY = new AgentCachingPoolStrategy();
public static <K, V> WeakCache<K, V> newWeakCache() {
return weakCacheProvider.newWeakCache();
}
public static <K, V> WeakCache<K, V> newWeakCache(final long maxSize) {
return weakCacheProvider.newWeakCache(maxSize);
}
public static AgentLocationStrategy locationStrategy() {
return LOCATION_STRATEGY;
}
@ -41,12 +77,4 @@ public class AgentTooling {
public static AgentCachingPoolStrategy poolStrategy() {
return POOL_STRATEGY;
}
private static void registerWeakMapProvider() {
if (!WeakMap.Provider.isProviderRegistered()) {
WeakMap.Provider.registerIfAbsent(new WeakMapSuppliers.WeakConcurrent(new Cleaner()));
// WeakMap.Provider.registerIfAbsent(new WeakMapSuppliers.WeakConcurrent.Inline());
// WeakMap.Provider.registerIfAbsent(new WeakMapSuppliers.Guava());
}
}
}

View File

@ -43,7 +43,7 @@ import java.util.Set;
public class ClassHierarchyIterable implements Iterable<Class<?>> {
private final Class<?> baseClass;
public ClassHierarchyIterable(final Class baseClass) {
public ClassHierarchyIterable(final Class<?> baseClass) {
this.baseClass = baseClass;
}

View File

@ -15,18 +15,14 @@
*/
package io.opentelemetry.auto.tooling;
import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;
import io.opentelemetry.auto.bootstrap.PatchLogger;
import io.opentelemetry.auto.bootstrap.WeakCache;
import lombok.extern.slf4j.Slf4j;
import net.bytebuddy.matcher.ElementMatcher;
@Slf4j
public final class ClassLoaderMatcher {
public static final ClassLoader BOOTSTRAP_CLASSLOADER = null;
public static final int CACHE_MAX_SIZE = 25; // limit number of cached responses for each matcher.
public static final int CACHE_CONCURRENCY =
Math.max(8, Runtime.getRuntime().availableProcessors());
/** A private constructor that must not be invoked. */
private ClassLoaderMatcher() {
@ -53,12 +49,11 @@ public final class ClassLoaderMatcher {
extends ElementMatcher.Junction.AbstractBase<ClassLoader> {
public static final SkipClassLoaderMatcher INSTANCE = new SkipClassLoaderMatcher();
/* Cache of classloader-instance -> (true|false). True = skip instrumentation. False = safe to instrument. */
private static final Cache<ClassLoader, Boolean> skipCache =
CacheBuilder.newBuilder().weakKeys().concurrencyLevel(CACHE_CONCURRENCY).build();
private static final String AGENT_CLASSLOADER_NAME =
"io.opentelemetry.auto.bootstrap.AgentClassLoader";
private static final String EXPORTER_CLASSLOADER_NAME =
"io.opentelemetry.auto.tooling.ExporterClassLoader";
private static final WeakCache<ClassLoader, Boolean> skipCache = AgentTooling.newWeakCache();
private SkipClassLoaderMatcher() {}
@ -128,12 +123,7 @@ public final class ClassLoaderMatcher {
private static class ClassLoaderHasClassesNamedMatcher
extends ElementMatcher.Junction.AbstractBase<ClassLoader> {
private final Cache<ClassLoader, Boolean> cache =
CacheBuilder.newBuilder()
.weakKeys()
.maximumSize(CACHE_MAX_SIZE)
.concurrencyLevel(CACHE_CONCURRENCY)
.build();
private final WeakCache<ClassLoader, Boolean> cache = AgentTooling.newWeakCache(25);
private final String[] resources;

View File

@ -0,0 +1,105 @@
/*
* 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 com.google.auto.service.AutoService;
import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;
import io.opentelemetry.auto.bootstrap.WeakCache;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import lombok.extern.slf4j.Slf4j;
/**
* no null keys nor null values are permitted
*
* @param <K>
* @param <V>
*/
@Slf4j
public final class GuavaWeakCache<K, V> implements WeakCache<K, V> {
@AutoService(WeakCache.Provider.class)
public static final class Provider<K, V> implements WeakCache.Provider<K, V> {
private static final int CACHE_CONCURRENCY =
Math.max(8, Runtime.getRuntime().availableProcessors());
@Override
public GuavaWeakCache<K, V> newWeakCache() {
return new GuavaWeakCache(
CacheBuilder.newBuilder()
.weakKeys()
.concurrencyLevel(CACHE_CONCURRENCY)
.expireAfterAccess(10, TimeUnit.MINUTES)
.build());
}
@Override
public GuavaWeakCache<K, V> newWeakCache(final long maxSize) {
return new GuavaWeakCache(
CacheBuilder.newBuilder()
.weakKeys()
.maximumSize(maxSize)
.concurrencyLevel(CACHE_CONCURRENCY)
.expireAfterAccess(10, TimeUnit.MINUTES)
.build());
}
}
private final Cache<K, V> cache;
private GuavaWeakCache(final Cache<K, V> cache) {
this.cache = cache;
}
/**
* @return null if key is not present
* @param key
*/
@Override
public V getIfPresent(final K key) {
return cache.getIfPresent(key);
}
@Override
public V getIfPresentOrCompute(final K key, final Callable<? extends V> loader) {
final V v = cache.getIfPresent(key);
if (v != null) {
return v;
}
try {
return cache.get(key, loader);
} catch (final ExecutionException e) {
log.error("Can't get value from cache", e);
}
return null;
}
@Override
public V get(final K key, final Callable<? extends V> loader) {
try {
return cache.get(key, loader);
} catch (final ExecutionException e) {
log.error("Can't get value from cache", e);
}
return null;
}
@Override
public void put(final K key, final V value) {
cache.put(key, value);
}
}

View File

@ -15,10 +15,9 @@
*/
package io.opentelemetry.auto.tooling.muzzle;
import static io.opentelemetry.auto.bootstrap.WeakMap.Provider.newWeakMap;
import static net.bytebuddy.dynamic.loading.ClassLoadingStrategy.BOOTSTRAP_LOADER;
import io.opentelemetry.auto.bootstrap.WeakMap;
import io.opentelemetry.auto.bootstrap.WeakCache;
import io.opentelemetry.auto.tooling.AgentTooling;
import io.opentelemetry.auto.tooling.Utils;
import io.opentelemetry.auto.tooling.muzzle.Reference.Mismatch;
@ -29,6 +28,7 @@ import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.concurrent.Callable;
import lombok.extern.slf4j.Slf4j;
import net.bytebuddy.description.field.FieldDescription;
import net.bytebuddy.description.method.MethodDescription;
@ -37,8 +37,8 @@ import net.bytebuddy.pool.TypePool;
/** Matches a set of references against a classloader. */
@Slf4j
public final class ReferenceMatcher implements WeakMap.ValueSupplier<ClassLoader, Boolean> {
private final WeakMap<ClassLoader, Boolean> mismatchCache = newWeakMap();
public final class ReferenceMatcher {
private final WeakCache<ClassLoader, Boolean> mismatchCache = AgentTooling.newWeakCache();
private final Reference[] references;
private final Set<String> helperClassNames;
@ -65,12 +65,18 @@ public final class ReferenceMatcher implements WeakMap.ValueSupplier<ClassLoader
if (loader == BOOTSTRAP_LOADER) {
loader = Utils.getBootstrapProxy();
}
return mismatchCache.computeIfAbsent(loader, this);
final ClassLoader cl = loader;
return mismatchCache.getIfPresentOrCompute(
loader,
new Callable<Boolean>() {
@Override
public Boolean call() {
return doesMatch(cl);
}
});
}
@Override
public Boolean get(final ClassLoader loader) {
private boolean doesMatch(final ClassLoader loader) {
for (final Reference reference : references) {
// Don't reference-check helper classes.
// They will be injected by the instrumentation's HelperInjector.

View File

@ -0,0 +1,95 @@
/*
* 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 spock.lang.Specification
import java.util.concurrent.Callable
class WeakCacheTest extends Specification {
def supplier = new CounterSupplier()
def weakCache = AgentTooling.newWeakCache()
def weakCacheFor1elem = AgentTooling.newWeakCache(1)
def "getOrCreate a value"() {
when:
def count = weakCache.get('key', supplier)
then:
count == 1
supplier.counter == 1
weakCache.cache.size() == 1
}
def "getOrCreate a value multiple times same class loader same key"() {
when:
def count1 = weakCache.get('key', supplier)
def count2 = weakCache.get('key', supplier)
then:
count1 == 1
count2 == 1
supplier.counter == 1
weakCache.cache.size() == 1
}
def "getOrCreate a value multiple times same class loader different keys"() {
when:
def count1 = weakCache.get('key1', supplier)
def count2 = weakCache.get('key2', supplier)
then:
count1 == 1
count2 == 2
supplier.counter == 2
weakCache.cache.size() == 2
}
def "max size check"() {
when:
def sizeBefore = weakCacheFor1elem.cache.size()
def valBefore = weakCacheFor1elem.getIfPresent("key1")
def sizeAfter = weakCacheFor1elem.cache.size()
def valAfterGet = weakCacheFor1elem.getIfPresentOrCompute("key1", supplier)
def sizeAfterCompute = weakCacheFor1elem.cache.size()
weakCacheFor1elem.put("key1", 42)
def valAfterPut = weakCacheFor1elem.getIfPresentOrCompute("key1", supplier)
def valByKey2 = weakCacheFor1elem.getIfPresentOrCompute("key2", supplier)
def valAfterReplace = weakCacheFor1elem.getIfPresent("key1")
then:
valBefore == null
valAfterGet == 1
sizeBefore == 0
sizeAfter == 0
sizeAfterCompute == 1
valAfterPut == 42
valByKey2 == 2
valAfterReplace == null
weakCacheFor1elem.cache.size() == 1
}
class CounterSupplier implements Callable<Integer> {
def counter = 0
@Override
Integer call() {
counter = counter + 1
return counter
}
}
}

View File

@ -1,4 +1,4 @@
def groovyVer = "2.5.8"
def groovyVer = "2.5.10"
def spockGroovyVer = groovyVer.replaceAll(/\.\d+$/, '')
ext {

View File

@ -50,10 +50,15 @@ if (project.hasProperty('minJavaVersionForTests') && project.getProperty('minJav
from sourceSets."main_$name".output
}
tasks.withType(JavaCompile).configureEach {
if (it.name.toLowerCase().contains("test")) {
sourceCompatibility = version
targetCompatibility = version
// In some cases we would like to avoid setting java version to `minJavaVersionForTests`.
// For example we would like to be able to run profiling tests with ZULU8, but we cannot run it with other JDK8 implementations at the moment
def skipSettingTestJavaVersion = project.hasProperty('skipSettingTestJavaVersion') && project.getProperty('skipSettingTestJavaVersion')
if (!skipSettingTestJavaVersion) {
tasks.withType(JavaCompile).configureEach {
if (it.name.toLowerCase().contains("test")) {
sourceCompatibility = version
targetCompatibility = version
}
}
}
}

View File

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

View File

@ -44,16 +44,6 @@ public class AkkaHttpClientDecorator extends HttpClientDecorator<HttpRequest, Ht
return new URI(httpRequest.uri().toString());
}
@Override
protected String hostname(final HttpRequest httpRequest) {
return httpRequest.getUri().host().address();
}
@Override
protected Integer port(final HttpRequest httpRequest) {
return httpRequest.getUri().port();
}
@Override
protected Integer status(final HttpResponse httpResponse) {
return httpResponse.status().intValue();

View File

@ -29,6 +29,7 @@ import org.apache.http.protocol.HttpContext;
import org.apache.http.protocol.HttpCoreContext;
public class ApacheHttpAsyncClientDecorator extends HttpClientDecorator<HttpRequest, HttpContext> {
public static final ApacheHttpAsyncClientDecorator DECORATE =
new ApacheHttpAsyncClientDecorator();
@ -65,34 +66,6 @@ public class ApacheHttpAsyncClientDecorator extends HttpClientDecorator<HttpRequ
}
}
@Override
protected String hostname(final HttpRequest request) {
try {
final URI uri = url(request);
if (uri != null) {
return uri.getHost();
} else {
return null;
}
} catch (final URISyntaxException e) {
return null;
}
}
@Override
protected Integer port(final HttpRequest request) {
try {
final URI uri = url(request);
if (uri != null) {
return uri.getPort();
} else {
return null;
}
} catch (final URISyntaxException e) {
return null;
}
}
@Override
protected Integer status(final HttpContext context) {
final Object responseObject = context.getAttribute(HttpCoreContext.HTTP_RESPONSE);

View File

@ -50,24 +50,6 @@ public class CommonsHttpClientDecorator extends HttpClientDecorator<HttpMethod,
}
}
@Override
protected String hostname(final HttpMethod httpMethod) {
try {
return httpMethod.getURI().getHost();
} catch (final URIException e) {
return null;
}
}
@Override
protected Integer port(final HttpMethod httpMethod) {
try {
return httpMethod.getURI().getPort();
} catch (final URIException e) {
return null;
}
}
@Override
protected Integer status(final HttpMethod httpMethod) {
final StatusLine statusLine = httpMethod.getStatusLine();

View File

@ -43,26 +43,6 @@ public class ApacheHttpClientDecorator extends HttpClientDecorator<HttpUriReques
return request.getURI();
}
@Override
protected String hostname(final HttpUriRequest httpRequest) {
final URI uri = httpRequest.getURI();
if (uri != null) {
return uri.getHost();
} else {
return null;
}
}
@Override
protected Integer port(final HttpUriRequest httpRequest) {
final URI uri = httpRequest.getURI();
if (uri != null) {
return uri.getPort();
} else {
return null;
}
}
@Override
protected Integer status(final HttpResponse httpResponse) {
return httpResponse.getStatusLine().getStatusCode();

View File

@ -120,16 +120,6 @@ public class AwsSdkClientDecorator extends HttpClientDecorator<Request, Response
return request.getEndpoint();
}
@Override
protected String hostname(final Request request) {
return null;
}
@Override
protected Integer port(final Request request) {
return null;
}
@Override
protected Integer status(final Response response) {
return response.getHttpResponse().getStatusCode();

View File

@ -158,6 +158,8 @@ class AWSClientTest extends AgentTestRunner {
"$Tags.HTTP_URL" "$server.address/"
"$Tags.HTTP_METHOD" "$method"
"$Tags.HTTP_STATUS" 200
"$MoreTags.NET_PEER_PORT" server.address.port
"$MoreTags.NET_PEER_NAME" "localhost"
"aws.service" { it.contains(service) }
"aws.endpoint" "$server.address"
"aws.operation" "${operation}Request"
@ -242,6 +244,8 @@ class AWSClientTest extends AgentTestRunner {
tags {
"$Tags.HTTP_URL" "http://localhost:${UNUSABLE_PORT}/"
"$Tags.HTTP_METHOD" "$method"
"$MoreTags.NET_PEER_NAME" "localhost"
"$MoreTags.NET_PEER_PORT" 61
"aws.service" { it.contains(service) }
"aws.endpoint" "http://localhost:${UNUSABLE_PORT}"
"aws.operation" "${operation}Request"
@ -299,6 +303,7 @@ class AWSClientTest extends AgentTestRunner {
tags {
"$Tags.HTTP_URL" "https://s3.amazonaws.com/"
"$Tags.HTTP_METHOD" "HEAD"
"$MoreTags.NET_PEER_NAME" "s3.amazonaws.com"
"aws.service" "Amazon S3"
"aws.endpoint" "https://s3.amazonaws.com"
"aws.operation" "HeadBucketRequest"
@ -340,6 +345,8 @@ class AWSClientTest extends AgentTestRunner {
tags {
"$Tags.HTTP_URL" "$server.address/"
"$Tags.HTTP_METHOD" "GET"
"$MoreTags.NET_PEER_PORT" server.address.port
"$MoreTags.NET_PEER_NAME" "localhost"
"aws.service" "Amazon S3"
"aws.endpoint" "$server.address"
"aws.operation" "GetObjectRequest"

View File

@ -121,6 +121,8 @@ class AWSClientTest extends AgentTestRunner {
"$Tags.HTTP_URL" "$server.address/"
"$Tags.HTTP_METHOD" "$method"
"$Tags.HTTP_STATUS" 200
"$MoreTags.NET_PEER_PORT" server.address.port
"$MoreTags.NET_PEER_NAME" "localhost"
"aws.service" { it.contains(service) }
"aws.endpoint" "$server.address"
"aws.operation" "${operation}Request"
@ -187,6 +189,8 @@ class AWSClientTest extends AgentTestRunner {
tags {
"$Tags.HTTP_URL" "http://localhost:${UNUSABLE_PORT}/"
"$Tags.HTTP_METHOD" "$method"
"$MoreTags.NET_PEER_PORT" 61
"$MoreTags.NET_PEER_NAME" "localhost"
"aws.service" { it.contains(service) }
"aws.endpoint" "http://localhost:${UNUSABLE_PORT}"
"aws.operation" "${operation}Request"
@ -244,6 +248,7 @@ class AWSClientTest extends AgentTestRunner {
tags {
"$Tags.HTTP_URL" "https://s3.amazonaws.com/"
"$Tags.HTTP_METHOD" "GET"
"$MoreTags.NET_PEER_NAME" "s3.amazonaws.com"
"aws.service" "Amazon S3"
"aws.endpoint" "https://s3.amazonaws.com"
"aws.operation" "GetObjectRequest"
@ -286,6 +291,8 @@ class AWSClientTest extends AgentTestRunner {
tags {
"$Tags.HTTP_URL" "$server.address/"
"$Tags.HTTP_METHOD" "GET"
"$MoreTags.NET_PEER_PORT" server.address.port
"$MoreTags.NET_PEER_NAME" "localhost"
"aws.service" "Amazon S3"
"aws.endpoint" "http://localhost:$server.address.port"
"aws.operation" "GetObjectRequest"

View File

@ -106,16 +106,6 @@ public class AwsSdkClientDecorator extends HttpClientDecorator<SdkHttpRequest, S
return request.getUri();
}
@Override
protected String hostname(final SdkHttpRequest request) {
return request.host();
}
@Override
protected Integer port(final SdkHttpRequest request) {
return request.port();
}
@Override
protected Integer status(final SdkHttpResponse response) {
return response.statusCode();

View File

@ -43,16 +43,6 @@ public class GoogleHttpClientDecorator extends HttpClientDecorator<HttpRequest,
return new URI(fixedUrl);
}
@Override
protected String hostname(final HttpRequest httpRequest) {
return httpRequest.getUrl().getHost();
}
@Override
protected Integer port(final HttpRequest httpRequest) {
return httpRequest.getUrl().getPort();
}
@Override
protected Integer status(final HttpResponse httpResponse) {
return httpResponse.getStatusCode();

View File

@ -21,9 +21,9 @@ import io.opentelemetry.trace.Tracer;
import java.net.HttpURLConnection;
import java.net.URI;
import java.net.URISyntaxException;
import javax.net.ssl.HttpsURLConnection;
public class HttpUrlConnectionDecorator extends HttpClientDecorator<HttpURLConnection, Integer> {
public static final HttpUrlConnectionDecorator DECORATE = new HttpUrlConnectionDecorator();
public static final Tracer TRACER =
OpenTelemetry.getTracerProvider().get("io.opentelemetry.auto.http-url-connection");
@ -43,23 +43,6 @@ public class HttpUrlConnectionDecorator extends HttpClientDecorator<HttpURLConne
return connection.getURL().toURI();
}
@Override
protected String hostname(final HttpURLConnection connection) {
return connection.getURL().getHost();
}
@Override
protected Integer port(final HttpURLConnection connection) {
final int port = connection.getURL().getPort();
if (port > 0) {
return port;
} else if (connection instanceof HttpsURLConnection) {
return 443;
} else {
return 80;
}
}
@Override
protected Integer status(final Integer status) {
return status;

View File

@ -43,16 +43,6 @@ public class JaxRsClientV1Decorator extends HttpClientDecorator<ClientRequest, C
return httpRequest.getURI();
}
@Override
protected String hostname(final ClientRequest httpRequest) {
return httpRequest.getURI().getHost();
}
@Override
protected Integer port(final ClientRequest httpRequest) {
return httpRequest.getURI().getPort();
}
@Override
protected Integer status(final ClientResponse clientResponse) {
return clientResponse.getStatus();

View File

@ -44,16 +44,6 @@ public class JaxRsClientDecorator
return httpRequest.getUri();
}
@Override
protected String hostname(final ClientRequestContext httpRequest) {
return httpRequest.getUri().getHost();
}
@Override
protected Integer port(final ClientRequestContext httpRequest) {
return httpRequest.getUri().getPort();
}
@Override
protected Integer status(final ClientResponseContext httpResponse) {
return httpResponse.getStatus();

View File

@ -33,7 +33,7 @@ import javax.ws.rs.Path;
public class JaxRsAnnotationsDecorator extends BaseDecorator {
public static final JaxRsAnnotationsDecorator DECORATE = new JaxRsAnnotationsDecorator();
private final WeakMap<Class, Map<Method, String>> resourceNames = newWeakMap();
private final WeakMap<Class<?>, Map<Method, String>> resourceNames = newWeakMap();
public static final Tracer TRACER =
OpenTelemetry.getTracerProvider().get("io.opentelemetry.auto.jaxrs-1.0");
@ -72,9 +72,8 @@ public class JaxRsAnnotationsDecorator extends BaseDecorator {
*
* @return The result can be an empty string but will never be {@code null}.
*/
private String getPathResourceName(final Class target, final Method method) {
private String getPathResourceName(final Class<?> target, final Method method) {
Map<Method, String> classMap = resourceNames.get(target);
if (classMap == null) {
resourceNames.putIfAbsent(target, new ConcurrentHashMap<Method, String>());
classMap = resourceNames.get(target);
@ -129,7 +128,7 @@ public class JaxRsAnnotationsDecorator extends BaseDecorator {
return method.getAnnotation(Path.class);
}
private Path findClassPath(final Class<Object> target) {
private Path findClassPath(final Class<?> target) {
for (final Class<?> currentClass : new ClassHierarchyIterable(target)) {
final Path annotation = currentClass.getAnnotation(Path.class);
if (annotation != null) {

View File

@ -28,7 +28,7 @@ import java.lang.reflect.Method
import static io.opentelemetry.auto.test.utils.TraceUtils.runUnderTrace
class JaxRsAnnotationsInstrumentationTest extends AgentTestRunner {
class JaxRsAnnotations1InstrumentationTest extends AgentTestRunner {
def "instrumentation can be used as root span and resource is set to METHOD PATH"() {
setup:
@ -139,7 +139,7 @@ class JaxRsAnnotationsInstrumentationTest extends AgentTestRunner {
// TODO: uncomment when we drop support for Java 7
// "GET /child/invoke" | new JavaInterfaces.DefaultChildClassOnInterface()
className = getName(obj.class)
className = getClassName(obj.class)
// JavaInterfaces classes are loaded on a different classloader, so we need to find the right cache instance.
decorator = obj.class.classLoader.loadClass(JaxRsAnnotationsDecorator.name).getField("DECORATE").get(null)
@ -194,18 +194,4 @@ class JaxRsAnnotationsInstrumentationTest extends AgentTestRunner {
void call() {
}
}
def getName(Class clazz) {
String className = clazz.getSimpleName()
if (className.isEmpty()) {
className = clazz.getName()
if (clazz.getPackage() != null) {
final String pkgName = clazz.getPackage().getName()
if (!pkgName.isEmpty()) {
className = clazz.getName().replace(pkgName, "").substring(1)
}
}
}
return className
}
}

View File

@ -15,8 +15,6 @@
*/
package io.opentelemetry.auto.instrumentation.jaxrs.v2_0;
import static io.opentelemetry.auto.bootstrap.WeakMap.Provider.newWeakMap;
import io.opentelemetry.OpenTelemetry;
import io.opentelemetry.auto.bootstrap.WeakMap;
import io.opentelemetry.auto.bootstrap.instrumentation.decorator.BaseDecorator;
@ -45,7 +43,8 @@ public class JaxRsAnnotationsDecorator extends BaseDecorator {
public static final Tracer TRACER =
OpenTelemetry.getTracerProvider().get("io.opentelemetry.auto.jaxrs-2.0");
private final WeakMap<Class, Map<Method, String>> resourceNames = newWeakMap();
private final WeakMap<Class<?>, Map<Method, String>> resourceNames =
WeakMap.Provider.newWeakMap();
@Override
protected String getComponentName() {
@ -83,7 +82,7 @@ public class JaxRsAnnotationsDecorator extends BaseDecorator {
*
* @return The result can be an empty string but will never be {@code null}.
*/
private String getPathResourceName(final Class target, final Method method) {
private String getPathResourceName(final Class<?> target, final Method method) {
Map<Method, String> classMap = resourceNames.get(target);
if (classMap == null) {
@ -140,7 +139,7 @@ public class JaxRsAnnotationsDecorator extends BaseDecorator {
return method.getAnnotation(Path.class);
}
private Path findClassPath(final Class<Object> target) {
private Path findClassPath(final Class<?> target) {
for (final Class<?> currentClass : new ClassHierarchyIterable(target)) {
final Path annotation = currentClass.getAnnotation(Path.class);
if (annotation != null) {

View File

@ -28,7 +28,7 @@ import java.lang.reflect.Method
import static io.opentelemetry.auto.test.utils.TraceUtils.runUnderTrace
class JaxRsAnnotationsInstrumentationTest extends AgentTestRunner {
class JaxRsAnnotations2InstrumentationTest extends AgentTestRunner {
def "instrumentation can be used as root span and resource is set to METHOD PATH"() {
setup:
@ -139,7 +139,7 @@ class JaxRsAnnotationsInstrumentationTest extends AgentTestRunner {
// TODO: uncomment when we drop support for Java 7
// "GET /child/invoke" | new JavaInterfaces.DefaultChildClassOnInterface()
className = getName(obj.class)
className = getClassName(obj.class)
// JavaInterfaces classes are loaded on a different classloader, so we need to find the right cache instance.
decorator = obj.class.classLoader.loadClass(JaxRsAnnotationsDecorator.name).getField("DECORATE").get(null)
@ -194,18 +194,4 @@ class JaxRsAnnotationsInstrumentationTest extends AgentTestRunner {
void call() {
}
}
def getName(Class clazz) {
String className = clazz.getSimpleName()
if (className.isEmpty()) {
className = clazz.getName()
if (clazz.getPackage() != null) {
final String pkgName = clazz.getPackage().getName()
if (!pkgName.isEmpty()) {
className = clazz.getName().replace(pkgName, "").substring(1)
}
}
}
return className
}
}

View File

@ -0,0 +1,48 @@
// Set properties before any plugins get loaded
ext {
minJavaVersionForTests = JavaVersion.VERSION_1_8
maxJavaVersionForTests = JavaVersion.VERSION_1_8
}
apply from: "${rootDir}/gradle/instrumentation.gradle"
apply plugin: 'org.unbroken-dome.test-sets'
muzzle {
pass {
group = "io.netty"
module = "netty"
versions = "[3.8.0.Final,4)"
assertInverse = true
}
fail {
group = "io.netty"
module = "netty-all"
versions = "[,]"
}
}
testSets {
latestDepTest
}
dependencies {
compileOnly group: 'io.netty', name: 'netty', version: '3.8.0.Final'
testCompile group: 'io.netty', name: 'netty', version: '3.8.0.Final'
testCompile group: 'com.ning', name: 'async-http-client', version: '1.8.0'
latestDepTestCompile group: 'io.netty', name: 'netty', version: '3.10.+'
latestDepTestCompile group: 'com.ning', name: 'async-http-client', version: '1.9.+'
}
// We need to force the dependency to the earliest supported version because other libraries declare newer versions.
configurations.testCompile {
resolutionStrategy {
eachDependency { DependencyResolveDetails details ->
//specifying a fixed version for all libraries with io.netty' group
if (details.requested.group == 'io.netty') {
details.useVersion "3.8.0.Final"
}
}
}
}

View File

@ -0,0 +1,110 @@
/*
* 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.
*/
import com.ning.http.client.AsyncCompletionHandler
import com.ning.http.client.AsyncHttpClient
import com.ning.http.client.AsyncHttpClientConfig
import com.ning.http.client.Response
import io.opentelemetry.auto.instrumentation.api.Tags
import io.opentelemetry.auto.test.base.HttpClientTest
import spock.lang.AutoCleanup
import spock.lang.Shared
import java.util.concurrent.ExecutionException
import java.util.concurrent.TimeUnit
import static io.opentelemetry.auto.test.utils.PortUtils.UNUSABLE_PORT
import static io.opentelemetry.auto.test.utils.TraceUtils.basicSpan
import static io.opentelemetry.auto.test.utils.TraceUtils.runUnderTrace
class Netty38ClientTest extends HttpClientTest {
@Shared
def clientConfig = new AsyncHttpClientConfig.Builder()
.setRequestTimeout(TimeUnit.SECONDS.toMillis(10).toInteger())
.build()
@Shared
@AutoCleanup
AsyncHttpClient asyncHttpClient = new AsyncHttpClient(clientConfig)
@Override
int doRequest(String method, URI uri, Map<String, String> headers, Closure callback) {
def methodName = "prepare" + method.toLowerCase().capitalize()
def requestBuilder = asyncHttpClient."$methodName"(uri.toString())
headers.each { requestBuilder.setHeader(it.key, it.value) }
def response = requestBuilder.execute(new AsyncCompletionHandler() {
@Override
Object onCompleted(Response response) throws Exception {
callback?.call()
return response
}
}).get()
return response.statusCode
}
@Override
boolean testRedirects() {
false
}
@Override
boolean testConnectionFailure() {
false
}
def "connection error (unopened port)"() {
given:
def uri = new URI("http://127.0.0.1:$UNUSABLE_PORT/")
when:
runUnderTrace("parent") {
doRequest(method, uri)
}
then:
def ex = thrown(Exception)
def thrownException = ex instanceof ExecutionException ? ex.cause : ex
and:
assertTraces(1) {
trace(0, 2) {
basicSpan(it, 0, "parent", null, thrownException)
span(1) {
operationName "CONNECT"
childOf span(0)
errored true
tags {
"$Tags.COMPONENT" "netty"
Class errorClass = ConnectException
try {
errorClass = Class.forName('io.netty.channel.AbstractChannel$AnnotatedConnectException')
} catch (ClassNotFoundException e) {
// Older versions use 'java.net.ConnectException' and do not have 'io.netty.channel.AbstractChannel$AnnotatedConnectException'
}
"error.type" errorClass.name
"error.stack" String
// slightly different message on windows
"error.msg" ~/Connection refused:( no further information:)? \/127.0.0.1:$UNUSABLE_PORT/
}
}
}
}
where:
method = "GET"
}
}

View File

@ -0,0 +1,136 @@
/*
* 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.
*/
import io.opentelemetry.auto.test.base.HttpServerTest
import org.jboss.netty.bootstrap.ServerBootstrap
import org.jboss.netty.buffer.ChannelBuffer
import org.jboss.netty.buffer.ChannelBuffers
import org.jboss.netty.channel.Channel
import org.jboss.netty.channel.ChannelHandlerContext
import org.jboss.netty.channel.ChannelPipeline
import org.jboss.netty.channel.DefaultChannelPipeline
import org.jboss.netty.channel.DownstreamMessageEvent
import org.jboss.netty.channel.ExceptionEvent
import org.jboss.netty.channel.FailedChannelFuture
import org.jboss.netty.channel.MessageEvent
import org.jboss.netty.channel.SimpleChannelHandler
import org.jboss.netty.channel.SucceededChannelFuture
import org.jboss.netty.channel.socket.nio.NioServerSocketChannelFactory
import org.jboss.netty.handler.codec.http.DefaultHttpResponse
import org.jboss.netty.handler.codec.http.HttpRequest
import org.jboss.netty.handler.codec.http.HttpResponse
import org.jboss.netty.handler.codec.http.HttpResponseStatus
import org.jboss.netty.handler.codec.http.HttpServerCodec
import org.jboss.netty.handler.logging.LoggingHandler
import org.jboss.netty.logging.InternalLogLevel
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.EXCEPTION
import static io.opentelemetry.auto.test.base.HttpServerTest.ServerEndpoint.NOT_FOUND
import static io.opentelemetry.auto.test.base.HttpServerTest.ServerEndpoint.QUERY_PARAM
import static io.opentelemetry.auto.test.base.HttpServerTest.ServerEndpoint.REDIRECT
import static io.opentelemetry.auto.test.base.HttpServerTest.ServerEndpoint.SUCCESS
import static io.opentelemetry.auto.test.base.HttpServerTest.ServerEndpoint.forPath
import static org.jboss.netty.handler.codec.http.HttpHeaders.Names.CONTENT_LENGTH
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.HttpVersion.HTTP_1_1
class Netty38ServerTest extends HttpServerTest<Channel> {
ChannelPipeline channelPipeline() {
ChannelPipeline channelPipeline = new DefaultChannelPipeline()
channelPipeline.addLast("http-codec", new HttpServerCodec())
channelPipeline.addLast("controller", new SimpleChannelHandler() {
@Override
void messageReceived(ChannelHandlerContext ctx, MessageEvent msg) throws Exception {
if (msg.getMessage() instanceof HttpRequest) {
def uri = URI.create((msg.getMessage() as HttpRequest).getUri())
HttpServerTest.ServerEndpoint endpoint = forPath(uri.path)
ctx.sendDownstream controller(endpoint) {
HttpResponse response
ChannelBuffer responseContent = null
switch (endpoint) {
case SUCCESS:
case ERROR:
responseContent = ChannelBuffers.copiedBuffer(endpoint.body, CharsetUtil.UTF_8)
response = new DefaultHttpResponse(HTTP_1_1, HttpResponseStatus.valueOf(endpoint.status))
response.setContent(responseContent)
break
case QUERY_PARAM:
responseContent = ChannelBuffers.copiedBuffer(uri.query, CharsetUtil.UTF_8)
response = new DefaultHttpResponse(HTTP_1_1, HttpResponseStatus.valueOf(endpoint.status))
response.setContent(responseContent)
break
case REDIRECT:
response = new DefaultHttpResponse(HTTP_1_1, HttpResponseStatus.valueOf(endpoint.status))
response.headers().set(LOCATION, endpoint.body)
break
case EXCEPTION:
throw new Exception(endpoint.body)
default:
responseContent = ChannelBuffers.copiedBuffer(NOT_FOUND.body, CharsetUtil.UTF_8)
response = new DefaultHttpResponse(HTTP_1_1, HttpResponseStatus.valueOf(endpoint.status))
response.setContent(responseContent)
break
}
response.headers().set(CONTENT_TYPE, "text/plain")
if (responseContent) {
response.headers().set(CONTENT_LENGTH, responseContent.readableBytes())
}
return new DownstreamMessageEvent(
ctx.getChannel(),
new SucceededChannelFuture(ctx.getChannel()),
response,
ctx.getChannel().getRemoteAddress())
}
}
}
@Override
void exceptionCaught(ChannelHandlerContext ctx, ExceptionEvent ex) throws Exception {
ChannelBuffer buffer = ChannelBuffers.copiedBuffer(ex.getCause().getMessage(), CharsetUtil.UTF_8)
HttpResponse response = new DefaultHttpResponse(HTTP_1_1, HttpResponseStatus.INTERNAL_SERVER_ERROR)
response.setContent(buffer)
response.headers().set(CONTENT_TYPE, "text/plain")
response.headers().set(CONTENT_LENGTH, buffer.readableBytes())
ctx.sendDownstream(new DownstreamMessageEvent(
ctx.getChannel(),
new FailedChannelFuture(ctx.getChannel(), ex.getCause()),
response,
ctx.getChannel().getRemoteAddress()))
}
})
return channelPipeline
}
@Override
Channel startServer(int port) {
ServerBootstrap bootstrap = new ServerBootstrap(new NioServerSocketChannelFactory())
bootstrap.setParentHandler(new LoggingHandler(InternalLogLevel.INFO))
bootstrap.setPipeline(channelPipeline())
InetSocketAddress address = new InetSocketAddress(port)
return bootstrap.bind(address)
}
@Override
void stopServer(Channel server) {
server?.disconnect()
}
}

View File

@ -0,0 +1,36 @@
/*
* 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.
*/
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,25 @@
/*
* 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.netty.v3_8;
import org.jboss.netty.handler.codec.http.HttpHeaders;
import org.jboss.netty.handler.codec.http.HttpRequest;
public class AbstractNettyAdvice {
public static void muzzleCheck(final HttpRequest httpRequest) {
final HttpHeaders headers = httpRequest.headers();
}
}

View File

@ -0,0 +1,137 @@
/*
* 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.netty.v3_8;
import static io.opentelemetry.auto.tooling.ClassLoaderMatcher.hasClassesNamed;
import static io.opentelemetry.auto.tooling.bytebuddy.matcher.AgentElementMatchers.implementsInterface;
import static io.opentelemetry.trace.Span.Kind.CLIENT;
import static java.util.Collections.singletonMap;
import static net.bytebuddy.matcher.ElementMatchers.isMethod;
import static net.bytebuddy.matcher.ElementMatchers.named;
import static net.bytebuddy.matcher.ElementMatchers.takesArgument;
import com.google.auto.service.AutoService;
import io.opentelemetry.auto.bootstrap.ContextStore;
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.tooling.Instrumenter;
import io.opentelemetry.context.Scope;
import io.opentelemetry.trace.Span;
import java.util.Collections;
import java.util.Map;
import net.bytebuddy.asm.Advice;
import net.bytebuddy.description.method.MethodDescription;
import net.bytebuddy.description.type.TypeDescription;
import net.bytebuddy.matcher.ElementMatcher;
import org.jboss.netty.channel.Channel;
import org.jboss.netty.channel.ChannelFuture;
@AutoService(Instrumenter.class)
public class ChannelFutureListenerInstrumentation extends Instrumenter.Default {
public ChannelFutureListenerInstrumentation() {
super(
NettyChannelPipelineInstrumentation.INSTRUMENTATION_NAME,
NettyChannelPipelineInstrumentation.ADDITIONAL_INSTRUMENTATION_NAMES);
}
@Override
public ElementMatcher<ClassLoader> classLoaderMatcher() {
// Optimization for expensive typeMatcher.
return hasClassesNamed("org.jboss.netty.channel.ChannelFutureListener");
}
@Override
public ElementMatcher<TypeDescription> typeMatcher() {
return implementsInterface(named("org.jboss.netty.channel.ChannelFutureListener"));
}
@Override
public String[] helperClassNames() {
return new String[] {
packageName + ".AbstractNettyAdvice",
packageName + ".ChannelTraceContext",
packageName + ".ChannelTraceContext$Factory",
packageName + ".server.NettyHttpServerDecorator",
packageName + ".server.NettyRequestExtractAdapter"
};
}
@Override
public Map<? extends ElementMatcher<? super MethodDescription>, String> transformers() {
return singletonMap(
isMethod()
.and(named("operationComplete"))
.and(takesArgument(0, named("org.jboss.netty.channel.ChannelFuture"))),
ChannelFutureListenerInstrumentation.class.getName() + "$OperationCompleteAdvice");
}
@Override
public Map<String, String> contextStore() {
return Collections.singletonMap(
"org.jboss.netty.channel.Channel", packageName + ".ChannelTraceContext");
}
public static class OperationCompleteAdvice extends AbstractNettyAdvice {
@Advice.OnMethodEnter
public static Scope activateScope(@Advice.Argument(0) final ChannelFuture future) {
/*
Idea here is:
- To return scope only if we have captured it.
- To capture scope only in case of error.
*/
final Throwable cause = future.getCause();
if (cause == null) {
return null;
}
final ContextStore<Channel, ChannelTraceContext> contextStore =
InstrumentationContext.get(Channel.class, ChannelTraceContext.class);
final Span continuation =
contextStore
.putIfAbsent(future.getChannel(), ChannelTraceContext.Factory.INSTANCE)
.getConnectionContinuation();
contextStore.get(future.getChannel()).setConnectionContinuation(null);
if (continuation == null) {
return null;
}
final Scope parentScope = NettyHttpServerDecorator.TRACER.withSpan(continuation);
final Span errorSpan =
NettyHttpServerDecorator.TRACER
.spanBuilder("CONNECT")
.setSpanKind(CLIENT)
.setAttribute(Tags.COMPONENT, "netty")
.startSpan();
try (final Scope scope = NettyHttpServerDecorator.TRACER.withSpan(errorSpan)) {
NettyHttpServerDecorator.DECORATE.onError(errorSpan, cause);
NettyHttpServerDecorator.DECORATE.beforeFinish(errorSpan);
errorSpan.end();
}
return parentScope;
}
@Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class)
public static void deactivateScope(@Advice.Enter final Scope scope) {
if (scope != null) {
scope.close();
}
}
}
}

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 io.opentelemetry.auto.instrumentation.netty.v3_8;
import io.opentelemetry.auto.bootstrap.ContextStore;
import io.opentelemetry.trace.Span;
import lombok.Data;
@Data
public class ChannelTraceContext {
public static class Factory implements ContextStore.Factory<ChannelTraceContext> {
public static final Factory INSTANCE = new Factory();
@Override
public ChannelTraceContext create() {
return new ChannelTraceContext();
}
}
Span connectionContinuation;
Span serverSpan;
Span clientSpan;
Span clientParentSpan;
}

View File

@ -0,0 +1,102 @@
/*
* 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.netty.v3_8;
import static io.opentelemetry.auto.tooling.ClassLoaderMatcher.hasClassesNamed;
import static io.opentelemetry.auto.tooling.bytebuddy.matcher.AgentElementMatchers.implementsInterface;
import static net.bytebuddy.matcher.ElementMatchers.isMethod;
import static net.bytebuddy.matcher.ElementMatchers.named;
import static net.bytebuddy.matcher.ElementMatchers.returns;
import com.google.auto.service.AutoService;
import io.opentelemetry.auto.bootstrap.ContextStore;
import io.opentelemetry.auto.bootstrap.InstrumentationContext;
import io.opentelemetry.auto.instrumentation.netty.v3_8.server.NettyHttpServerDecorator;
import io.opentelemetry.auto.tooling.Instrumenter;
import io.opentelemetry.trace.Span;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import net.bytebuddy.asm.Advice;
import net.bytebuddy.description.method.MethodDescription;
import net.bytebuddy.description.type.TypeDescription;
import net.bytebuddy.matcher.ElementMatcher;
import org.jboss.netty.channel.Channel;
@AutoService(Instrumenter.class)
public class NettyChannelInstrumentation extends Instrumenter.Default {
public NettyChannelInstrumentation() {
super(
NettyChannelPipelineInstrumentation.INSTRUMENTATION_NAME,
NettyChannelPipelineInstrumentation.ADDITIONAL_INSTRUMENTATION_NAMES);
}
@Override
public ElementMatcher<ClassLoader> classLoaderMatcher() {
// Optimization for expensive typeMatcher.
return hasClassesNamed("org.jboss.netty.channel.Channel");
}
@Override
public ElementMatcher<TypeDescription> typeMatcher() {
return implementsInterface(named("org.jboss.netty.channel.Channel"));
}
@Override
public String[] helperClassNames() {
return new String[] {
packageName + ".AbstractNettyAdvice",
packageName + ".ChannelTraceContext",
packageName + ".ChannelTraceContext$Factory",
packageName + ".server.NettyHttpServerDecorator",
};
}
@Override
public Map<? extends ElementMatcher<? super MethodDescription>, String> transformers() {
final Map<ElementMatcher<? super MethodDescription>, String> transformers = new HashMap<>();
transformers.put(
isMethod()
.and(named("connect"))
.and(returns(named("org.jboss.netty.channel.ChannelFuture"))),
NettyChannelInstrumentation.class.getName() + "$ChannelConnectAdvice");
return transformers;
}
@Override
public Map<String, String> contextStore() {
return Collections.singletonMap(
"org.jboss.netty.channel.Channel", ChannelTraceContext.class.getName());
}
public static class ChannelConnectAdvice extends AbstractNettyAdvice {
@Advice.OnMethodEnter
public static void addConnectContinuation(@Advice.This final Channel channel) {
final Span span = NettyHttpServerDecorator.TRACER.getCurrentSpan();
if (span.getContext().isValid()) {
final ContextStore<Channel, ChannelTraceContext> contextStore =
InstrumentationContext.get(Channel.class, ChannelTraceContext.class);
if (contextStore
.putIfAbsent(channel, ChannelTraceContext.Factory.INSTANCE)
.getConnectionContinuation()
== null) {
contextStore.get(channel).setConnectionContinuation(span);
}
}
}
}
}

View File

@ -0,0 +1,222 @@
/*
* 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.netty.v3_8;
import static io.opentelemetry.auto.tooling.ClassLoaderMatcher.hasClassesNamed;
import static io.opentelemetry.auto.tooling.bytebuddy.matcher.AgentElementMatchers.implementsInterface;
import static net.bytebuddy.matcher.ElementMatchers.isMethod;
import static net.bytebuddy.matcher.ElementMatchers.nameStartsWith;
import static net.bytebuddy.matcher.ElementMatchers.named;
import static net.bytebuddy.matcher.ElementMatchers.takesArgument;
import com.google.auto.service.AutoService;
import io.opentelemetry.auto.bootstrap.CallDepthThreadLocalMap;
import io.opentelemetry.auto.bootstrap.ContextStore;
import io.opentelemetry.auto.bootstrap.InstrumentationContext;
import io.opentelemetry.auto.instrumentation.netty.v3_8.client.HttpClientRequestTracingHandler;
import io.opentelemetry.auto.instrumentation.netty.v3_8.client.HttpClientResponseTracingHandler;
import io.opentelemetry.auto.instrumentation.netty.v3_8.client.HttpClientTracingHandler;
import io.opentelemetry.auto.instrumentation.netty.v3_8.server.HttpServerRequestTracingHandler;
import io.opentelemetry.auto.instrumentation.netty.v3_8.server.HttpServerResponseTracingHandler;
import io.opentelemetry.auto.instrumentation.netty.v3_8.server.HttpServerTracingHandler;
import io.opentelemetry.auto.tooling.Instrumenter;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import net.bytebuddy.asm.Advice;
import net.bytebuddy.description.method.MethodDescription;
import net.bytebuddy.description.type.TypeDescription;
import net.bytebuddy.matcher.ElementMatcher;
import org.jboss.netty.channel.Channel;
import org.jboss.netty.channel.ChannelHandler;
import org.jboss.netty.channel.ChannelPipeline;
import org.jboss.netty.handler.codec.http.HttpClientCodec;
import org.jboss.netty.handler.codec.http.HttpRequestDecoder;
import org.jboss.netty.handler.codec.http.HttpRequestEncoder;
import org.jboss.netty.handler.codec.http.HttpResponseDecoder;
import org.jboss.netty.handler.codec.http.HttpResponseEncoder;
import org.jboss.netty.handler.codec.http.HttpServerCodec;
@AutoService(Instrumenter.class)
public class NettyChannelPipelineInstrumentation extends Instrumenter.Default {
static final String INSTRUMENTATION_NAME = "netty";
static final String[] ADDITIONAL_INSTRUMENTATION_NAMES = {"netty-3.9"};
public NettyChannelPipelineInstrumentation() {
super(INSTRUMENTATION_NAME, ADDITIONAL_INSTRUMENTATION_NAMES);
}
@Override
public ElementMatcher<ClassLoader> classLoaderMatcher() {
// Optimization for expensive typeMatcher.
return hasClassesNamed("org.jboss.netty.channel.ChannelPipeline");
}
@Override
public ElementMatcher<TypeDescription> typeMatcher() {
return implementsInterface(named("org.jboss.netty.channel.ChannelPipeline"));
}
@Override
public String[] helperClassNames() {
return new String[] {
packageName + ".AbstractNettyAdvice",
packageName + ".ChannelTraceContext",
packageName + ".ChannelTraceContext$Factory",
NettyChannelPipelineInstrumentation.class.getName() + "$ChannelPipelineAdviceUtil",
// Util
packageName + ".util.CombinedSimpleChannelHandler",
// client helpers
packageName + ".client.NettyHttpClientDecorator",
packageName + ".client.NettyResponseInjectAdapter",
packageName + ".client.HttpClientRequestTracingHandler",
packageName + ".client.HttpClientResponseTracingHandler",
packageName + ".client.HttpClientTracingHandler",
// server helpers
packageName + ".server.NettyHttpServerDecorator",
packageName + ".server.NettyRequestExtractAdapter",
packageName + ".server.HttpServerRequestTracingHandler",
packageName + ".server.HttpServerResponseTracingHandler",
packageName + ".server.HttpServerTracingHandler"
};
}
@Override
public Map<? extends ElementMatcher<? super MethodDescription>, String> transformers() {
final Map<ElementMatcher<? super MethodDescription>, String> transformers = new HashMap<>();
transformers.put(
isMethod()
.and(nameStartsWith("add"))
.and(takesArgument(1, named("org.jboss.netty.channel.ChannelHandler"))),
NettyChannelPipelineInstrumentation.class.getName() + "$ChannelPipelineAdd2ArgsAdvice");
transformers.put(
isMethod()
.and(nameStartsWith("add"))
.and(takesArgument(2, named("org.jboss.netty.channel.ChannelHandler"))),
NettyChannelPipelineInstrumentation.class.getName() + "$ChannelPipelineAdd3ArgsAdvice");
return transformers;
}
@Override
public Map<String, String> contextStore() {
return Collections.singletonMap(
"org.jboss.netty.channel.Channel", ChannelTraceContext.class.getName());
}
/**
* When certain handlers are added to the pipeline, we want to add our corresponding tracing
* handlers. If those handlers are later removed, we may want to remove our handlers. That is not
* currently implemented.
*/
public static class ChannelPipelineAdviceUtil {
public static void wrapHandler(
final ContextStore<Channel, ChannelTraceContext> contextStore,
final ChannelPipeline pipeline,
final ChannelHandler handler) {
try {
// Server pipeline handlers
if (handler instanceof HttpServerCodec) {
pipeline.addLast(
HttpServerTracingHandler.class.getName(), new HttpServerTracingHandler(contextStore));
} else if (handler instanceof HttpRequestDecoder) {
pipeline.addLast(
HttpServerRequestTracingHandler.class.getName(),
new HttpServerRequestTracingHandler(contextStore));
} else if (handler instanceof HttpResponseEncoder) {
pipeline.addLast(
HttpServerResponseTracingHandler.class.getName(),
new HttpServerResponseTracingHandler(contextStore));
} else
// Client pipeline handlers
if (handler instanceof HttpClientCodec) {
pipeline.addLast(
HttpClientTracingHandler.class.getName(), new HttpClientTracingHandler(contextStore));
} else if (handler instanceof HttpRequestEncoder) {
pipeline.addLast(
HttpClientRequestTracingHandler.class.getName(),
new HttpClientRequestTracingHandler(contextStore));
} else if (handler instanceof HttpResponseDecoder) {
pipeline.addLast(
HttpClientResponseTracingHandler.class.getName(),
new HttpClientResponseTracingHandler(contextStore));
}
} finally {
CallDepthThreadLocalMap.reset(ChannelPipeline.class);
}
}
}
public static class ChannelPipelineAdd2ArgsAdvice extends AbstractNettyAdvice {
@Advice.OnMethodEnter
public static int checkDepth(
@Advice.This final ChannelPipeline pipeline,
@Advice.Argument(1) final ChannelHandler handler) {
// Pipelines are created once as a factory and then copied multiple times using the same add
// methods as we are hooking. If our handler has already been added we need to remove it so we
// don't end up with duplicates (this throws an exception)
if (pipeline.get(handler.getClass().getName()) != null) {
pipeline.remove(handler.getClass().getName());
}
return CallDepthThreadLocalMap.incrementCallDepth(ChannelPipeline.class);
}
@Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class)
public static void addHandler(
@Advice.Enter final int depth,
@Advice.This final ChannelPipeline pipeline,
@Advice.Argument(1) final ChannelHandler handler) {
if (depth > 0) {
return;
}
final ContextStore<Channel, ChannelTraceContext> contextStore =
InstrumentationContext.get(Channel.class, ChannelTraceContext.class);
ChannelPipelineAdviceUtil.wrapHandler(contextStore, pipeline, handler);
}
}
public static class ChannelPipelineAdd3ArgsAdvice extends AbstractNettyAdvice {
@Advice.OnMethodEnter
public static int checkDepth(
@Advice.This final ChannelPipeline pipeline,
@Advice.Argument(2) final ChannelHandler handler) {
// Pipelines are created once as a factory and then copied multiple times using the same add
// methods as we are hooking. If our handler has already been added we need to remove it so we
// don't end up with duplicates (this throws an exception)
if (pipeline.get(handler.getClass().getName()) != null) {
pipeline.remove(handler.getClass().getName());
}
return CallDepthThreadLocalMap.incrementCallDepth(ChannelPipeline.class);
}
@Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class)
public static void addHandler(
@Advice.Enter final int depth,
@Advice.This final ChannelPipeline pipeline,
@Advice.Argument(2) final ChannelHandler handler) {
if (depth > 0) {
return;
}
final ContextStore<Channel, ChannelTraceContext> contextStore =
InstrumentationContext.get(Channel.class, ChannelTraceContext.class);
ChannelPipelineAdviceUtil.wrapHandler(contextStore, pipeline, handler);
}
}
}

View File

@ -0,0 +1,96 @@
/*
* 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.netty.v3_8.client;
import static io.opentelemetry.auto.instrumentation.netty.v3_8.client.NettyHttpClientDecorator.DECORATE;
import static io.opentelemetry.auto.instrumentation.netty.v3_8.client.NettyHttpClientDecorator.TRACER;
import static io.opentelemetry.auto.instrumentation.netty.v3_8.client.NettyResponseInjectAdapter.SETTER;
import static io.opentelemetry.trace.Span.Kind.CLIENT;
import static io.opentelemetry.trace.TracingContextUtils.withSpan;
import io.grpc.Context;
import io.opentelemetry.OpenTelemetry;
import io.opentelemetry.auto.bootstrap.ContextStore;
import io.opentelemetry.auto.instrumentation.netty.v3_8.ChannelTraceContext;
import io.opentelemetry.context.Scope;
import io.opentelemetry.trace.Span;
import java.net.InetSocketAddress;
import lombok.extern.slf4j.Slf4j;
import org.jboss.netty.channel.Channel;
import org.jboss.netty.channel.ChannelHandlerContext;
import org.jboss.netty.channel.MessageEvent;
import org.jboss.netty.channel.SimpleChannelDownstreamHandler;
import org.jboss.netty.handler.codec.http.HttpRequest;
@Slf4j
public class HttpClientRequestTracingHandler extends SimpleChannelDownstreamHandler {
private final ContextStore<Channel, ChannelTraceContext> contextStore;
public HttpClientRequestTracingHandler(
final ContextStore<Channel, ChannelTraceContext> contextStore) {
this.contextStore = contextStore;
}
@Override
public void writeRequested(final ChannelHandlerContext ctx, final MessageEvent msg)
throws Exception {
if (!(msg.getMessage() instanceof HttpRequest)) {
ctx.sendDownstream(msg);
return;
}
final ChannelTraceContext channelTraceContext =
contextStore.putIfAbsent(ctx.getChannel(), ChannelTraceContext.Factory.INSTANCE);
Scope parentScope = null;
final Span continuation = channelTraceContext.getConnectionContinuation();
if (continuation != null) {
parentScope = TRACER.withSpan(continuation);
channelTraceContext.setConnectionContinuation(null);
}
final HttpRequest request = (HttpRequest) msg.getMessage();
channelTraceContext.setClientParentSpan(TRACER.getCurrentSpan());
final Span span =
TRACER.spanBuilder(DECORATE.spanNameForRequest(request)).setSpanKind(CLIENT).startSpan();
try (final Scope scope = TRACER.withSpan(span)) {
DECORATE.afterStart(span);
DECORATE.onRequest(span, request);
DECORATE.onPeerConnection(span, (InetSocketAddress) ctx.getChannel().getRemoteAddress());
final Context context = withSpan(span, Context.current());
OpenTelemetry.getPropagators().getHttpTextFormat().inject(context, request.headers(), SETTER);
channelTraceContext.setClientSpan(span);
try {
ctx.sendDownstream(msg);
} catch (final Throwable throwable) {
DECORATE.onError(span, throwable);
DECORATE.beforeFinish(span);
span.end();
throw throwable;
}
} finally {
if (parentScope != null) {
parentScope.close();
}
}
}
}

View File

@ -0,0 +1,69 @@
/*
* 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.netty.v3_8.client;
import static io.opentelemetry.auto.instrumentation.netty.v3_8.client.NettyHttpClientDecorator.DECORATE;
import static io.opentelemetry.auto.instrumentation.netty.v3_8.client.NettyHttpClientDecorator.TRACER;
import io.opentelemetry.auto.bootstrap.ContextStore;
import io.opentelemetry.auto.instrumentation.netty.v3_8.ChannelTraceContext;
import io.opentelemetry.context.Scope;
import io.opentelemetry.trace.DefaultSpan;
import io.opentelemetry.trace.Span;
import org.jboss.netty.channel.Channel;
import org.jboss.netty.channel.ChannelHandlerContext;
import org.jboss.netty.channel.MessageEvent;
import org.jboss.netty.channel.SimpleChannelUpstreamHandler;
import org.jboss.netty.handler.codec.http.HttpResponse;
public class HttpClientResponseTracingHandler extends SimpleChannelUpstreamHandler {
private final ContextStore<Channel, ChannelTraceContext> contextStore;
public HttpClientResponseTracingHandler(
final ContextStore<Channel, ChannelTraceContext> contextStore) {
this.contextStore = contextStore;
}
@Override
public void messageReceived(final ChannelHandlerContext ctx, final MessageEvent msg)
throws Exception {
final ChannelTraceContext channelTraceContext =
contextStore.putIfAbsent(ctx.getChannel(), ChannelTraceContext.Factory.INSTANCE);
Span parent = channelTraceContext.getClientParentSpan();
if (parent == null) {
parent = DefaultSpan.getInvalid();
channelTraceContext.setClientParentSpan(DefaultSpan.getInvalid());
}
final Span span = channelTraceContext.getClientSpan();
final boolean finishSpan = msg.getMessage() instanceof HttpResponse;
if (span != null && finishSpan) {
try (final Scope scope = TRACER.withSpan(span)) {
DECORATE.onResponse(span, (HttpResponse) msg.getMessage());
DECORATE.beforeFinish(span);
span.end();
}
}
// We want the callback in the scope of the parent, not the client span
try (final Scope scope = TRACER.withSpan(parent)) {
ctx.sendUpstream(msg);
}
}
}

View File

@ -0,0 +1,32 @@
/*
* 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.netty.v3_8.client;
import io.opentelemetry.auto.bootstrap.ContextStore;
import io.opentelemetry.auto.instrumentation.netty.v3_8.ChannelTraceContext;
import io.opentelemetry.auto.instrumentation.netty.v3_8.util.CombinedSimpleChannelHandler;
import org.jboss.netty.channel.Channel;
public class HttpClientTracingHandler
extends CombinedSimpleChannelHandler<
HttpClientResponseTracingHandler, HttpClientRequestTracingHandler> {
public HttpClientTracingHandler(final ContextStore<Channel, ChannelTraceContext> contextStore) {
super(
new HttpClientResponseTracingHandler(contextStore),
new HttpClientRequestTracingHandler(contextStore));
}
}

View File

@ -0,0 +1,60 @@
/*
* 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.netty.v3_8.client;
import static org.jboss.netty.handler.codec.http.HttpHeaders.Names.HOST;
import io.opentelemetry.OpenTelemetry;
import io.opentelemetry.auto.bootstrap.instrumentation.decorator.HttpClientDecorator;
import io.opentelemetry.trace.Tracer;
import java.net.URI;
import java.net.URISyntaxException;
import lombok.extern.slf4j.Slf4j;
import org.jboss.netty.handler.codec.http.HttpRequest;
import org.jboss.netty.handler.codec.http.HttpResponse;
@Slf4j
public class NettyHttpClientDecorator extends HttpClientDecorator<HttpRequest, HttpResponse> {
public static final NettyHttpClientDecorator DECORATE = new NettyHttpClientDecorator();
public static final Tracer TRACER =
OpenTelemetry.getTracerProvider().get("io.opentelemetry.auto.netty-3.8");
@Override
protected String getComponentName() {
return "netty-client";
}
@Override
protected String method(final HttpRequest httpRequest) {
return httpRequest.getMethod().getName();
}
@Override
protected URI url(final HttpRequest request) throws URISyntaxException {
final URI uri = new URI(request.getUri());
if ((uri.getHost() == null || uri.getHost().equals("")) && request.headers().contains(HOST)) {
return new URI("http://" + request.headers().get(HOST) + request.getUri());
} else {
return uri;
}
}
@Override
protected Integer status(final HttpResponse httpResponse) {
return httpResponse.getStatus().getCode();
}
}

View File

@ -0,0 +1,29 @@
/*
* 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.netty.v3_8.client;
import io.opentelemetry.context.propagation.HttpTextFormat;
import org.jboss.netty.handler.codec.http.HttpHeaders;
public class NettyResponseInjectAdapter implements HttpTextFormat.Setter<HttpHeaders> {
public static final NettyResponseInjectAdapter SETTER = new NettyResponseInjectAdapter();
@Override
public void set(final HttpHeaders headers, final String key, final String value) {
headers.set(key, value);
}
}

View File

@ -0,0 +1,92 @@
/*
* 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.netty.v3_8.server;
import static io.opentelemetry.auto.bootstrap.instrumentation.decorator.BaseDecorator.extract;
import static io.opentelemetry.auto.instrumentation.netty.v3_8.server.NettyHttpServerDecorator.DECORATE;
import static io.opentelemetry.auto.instrumentation.netty.v3_8.server.NettyHttpServerDecorator.TRACER;
import static io.opentelemetry.auto.instrumentation.netty.v3_8.server.NettyRequestExtractAdapter.GETTER;
import static io.opentelemetry.trace.Span.Kind.SERVER;
import io.opentelemetry.auto.bootstrap.ContextStore;
import io.opentelemetry.auto.instrumentation.netty.v3_8.ChannelTraceContext;
import io.opentelemetry.context.Scope;
import io.opentelemetry.trace.Span;
import io.opentelemetry.trace.SpanContext;
import org.jboss.netty.channel.Channel;
import org.jboss.netty.channel.ChannelHandlerContext;
import org.jboss.netty.channel.MessageEvent;
import org.jboss.netty.channel.SimpleChannelUpstreamHandler;
import org.jboss.netty.handler.codec.http.HttpRequest;
public class HttpServerRequestTracingHandler extends SimpleChannelUpstreamHandler {
private final ContextStore<Channel, ChannelTraceContext> contextStore;
public HttpServerRequestTracingHandler(
final ContextStore<Channel, ChannelTraceContext> contextStore) {
this.contextStore = contextStore;
}
@Override
public void messageReceived(final ChannelHandlerContext ctx, final MessageEvent msg)
throws Exception {
final ChannelTraceContext channelTraceContext =
contextStore.putIfAbsent(ctx.getChannel(), ChannelTraceContext.Factory.INSTANCE);
if (!(msg.getMessage() instanceof HttpRequest)) {
final Span span = channelTraceContext.getServerSpan();
if (span == null) {
ctx.sendUpstream(msg); // superclass does not throw
} else {
try (final Scope scope = TRACER.withSpan(span)) {
ctx.sendUpstream(msg); // superclass does not throw
}
}
return;
}
final HttpRequest request = (HttpRequest) msg.getMessage();
final Span.Builder spanBuilder =
TRACER.spanBuilder(DECORATE.spanNameForRequest(request)).setSpanKind(SERVER);
final SpanContext extractedContext = extract(request.headers(), GETTER);
if (extractedContext.isValid()) {
spanBuilder.setParent(extractedContext);
} else {
// explicitly setting "no parent" in case a span was propagated to this thread
// by the java-concurrent instrumentation when the thread was started
spanBuilder.setNoParent();
}
final Span span = spanBuilder.startSpan();
try (final Scope scope = TRACER.withSpan(span)) {
DECORATE.afterStart(span);
DECORATE.onConnection(span, ctx.getChannel());
DECORATE.onRequest(span, request);
channelTraceContext.setServerSpan(span);
try {
ctx.sendUpstream(msg);
} catch (final Throwable throwable) {
DECORATE.onError(span, throwable);
DECORATE.beforeFinish(span);
span.end(); // Finish the span manually since finishSpanOnClose was false
throw throwable;
}
}
}
}

View File

@ -0,0 +1,63 @@
/*
* 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.netty.v3_8.server;
import io.opentelemetry.auto.bootstrap.ContextStore;
import io.opentelemetry.auto.instrumentation.api.Tags;
import io.opentelemetry.auto.instrumentation.netty.v3_8.ChannelTraceContext;
import io.opentelemetry.trace.Span;
import org.jboss.netty.channel.Channel;
import org.jboss.netty.channel.ChannelHandlerContext;
import org.jboss.netty.channel.MessageEvent;
import org.jboss.netty.channel.SimpleChannelDownstreamHandler;
import org.jboss.netty.handler.codec.http.HttpResponse;
public class HttpServerResponseTracingHandler extends SimpleChannelDownstreamHandler {
private final ContextStore<Channel, ChannelTraceContext> contextStore;
public HttpServerResponseTracingHandler(
final ContextStore<Channel, ChannelTraceContext> contextStore) {
this.contextStore = contextStore;
}
@Override
public void writeRequested(final ChannelHandlerContext ctx, final MessageEvent msg)
throws Exception {
final ChannelTraceContext channelTraceContext =
contextStore.putIfAbsent(ctx.getChannel(), ChannelTraceContext.Factory.INSTANCE);
final Span span = channelTraceContext.getServerSpan();
if (span == null || !(msg.getMessage() instanceof HttpResponse)) {
ctx.sendDownstream(msg);
return;
}
final HttpResponse response = (HttpResponse) msg.getMessage();
try {
ctx.sendDownstream(msg);
} catch (final Throwable throwable) {
NettyHttpServerDecorator.DECORATE.onError(span, throwable);
span.setAttribute(Tags.HTTP_STATUS, 500);
span.end(); // Finish the span manually since finishSpanOnClose was false
throw throwable;
}
NettyHttpServerDecorator.DECORATE.onResponse(span, response);
NettyHttpServerDecorator.DECORATE.beforeFinish(span);
span.end(); // Finish the span manually since finishSpanOnClose was false
}
}

View File

@ -0,0 +1,32 @@
/*
* 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.netty.v3_8.server;
import io.opentelemetry.auto.bootstrap.ContextStore;
import io.opentelemetry.auto.instrumentation.netty.v3_8.ChannelTraceContext;
import io.opentelemetry.auto.instrumentation.netty.v3_8.util.CombinedSimpleChannelHandler;
import org.jboss.netty.channel.Channel;
public class HttpServerTracingHandler
extends CombinedSimpleChannelHandler<
HttpServerRequestTracingHandler, HttpServerResponseTracingHandler> {
public HttpServerTracingHandler(final ContextStore<Channel, ChannelTraceContext> contextStore) {
super(
new HttpServerRequestTracingHandler(contextStore),
new HttpServerResponseTracingHandler(contextStore));
}
}

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.netty.v3_8.server;
import static org.jboss.netty.handler.codec.http.HttpHeaders.Names.HOST;
import io.opentelemetry.OpenTelemetry;
import io.opentelemetry.auto.bootstrap.instrumentation.decorator.HttpServerDecorator;
import io.opentelemetry.trace.Tracer;
import java.net.InetSocketAddress;
import java.net.SocketAddress;
import java.net.URI;
import java.net.URISyntaxException;
import lombok.extern.slf4j.Slf4j;
import org.jboss.netty.channel.Channel;
import org.jboss.netty.handler.codec.http.HttpRequest;
import org.jboss.netty.handler.codec.http.HttpResponse;
@Slf4j
public class NettyHttpServerDecorator
extends HttpServerDecorator<HttpRequest, Channel, HttpResponse> {
public static final NettyHttpServerDecorator DECORATE = new NettyHttpServerDecorator();
public static final Tracer TRACER =
OpenTelemetry.getTracerProvider().get("io.opentelemetry.auto.netty-3.8");
@Override
protected String getComponentName() {
return "netty";
}
@Override
protected String method(final HttpRequest httpRequest) {
return httpRequest.getMethod().getName();
}
@Override
protected URI url(final HttpRequest request) throws URISyntaxException {
final URI uri = new URI(request.getUri());
if ((uri.getHost() == null || uri.getHost().equals("")) && request.headers().contains(HOST)) {
return new URI("http://" + request.headers().get(HOST) + request.getUri());
} else {
return uri;
}
}
@Override
protected String peerHostIP(final Channel channel) {
final SocketAddress socketAddress = channel.getRemoteAddress();
if (socketAddress instanceof InetSocketAddress) {
return ((InetSocketAddress) socketAddress).getAddress().getHostAddress();
}
return null;
}
@Override
protected Integer peerPort(final Channel channel) {
final SocketAddress socketAddress = channel.getRemoteAddress();
if (socketAddress instanceof InetSocketAddress) {
return ((InetSocketAddress) socketAddress).getPort();
}
return null;
}
@Override
protected Integer status(final HttpResponse httpResponse) {
return httpResponse.getStatus().getCode();
}
}

View File

@ -0,0 +1,29 @@
/*
* 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.netty.v3_8.server;
import io.opentelemetry.context.propagation.HttpTextFormat;
import org.jboss.netty.handler.codec.http.HttpHeaders;
public class NettyRequestExtractAdapter implements HttpTextFormat.Getter<HttpHeaders> {
public static final NettyRequestExtractAdapter GETTER = new NettyRequestExtractAdapter();
@Override
public String get(final HttpHeaders headers, final String key) {
return headers.get(key);
}
}

View File

@ -0,0 +1,167 @@
/*
* 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.netty.v3_8.util;
import org.jboss.netty.channel.ChannelEvent;
import org.jboss.netty.channel.ChannelHandlerContext;
import org.jboss.netty.channel.ChannelStateEvent;
import org.jboss.netty.channel.ChildChannelStateEvent;
import org.jboss.netty.channel.ExceptionEvent;
import org.jboss.netty.channel.MessageEvent;
import org.jboss.netty.channel.SimpleChannelDownstreamHandler;
import org.jboss.netty.channel.SimpleChannelHandler;
import org.jboss.netty.channel.SimpleChannelUpstreamHandler;
import org.jboss.netty.channel.WriteCompletionEvent;
public class CombinedSimpleChannelHandler<
Upstream extends SimpleChannelUpstreamHandler,
Downstream extends SimpleChannelDownstreamHandler>
extends SimpleChannelHandler {
private final Upstream upstream;
private final Downstream downstream;
public CombinedSimpleChannelHandler(final Upstream upstream, final Downstream downstream) {
this.upstream = upstream;
this.downstream = downstream;
}
@Override
public void handleUpstream(final ChannelHandlerContext ctx, final ChannelEvent e)
throws Exception {
upstream.handleUpstream(ctx, e);
}
@Override
public void messageReceived(final ChannelHandlerContext ctx, final MessageEvent e)
throws Exception {
upstream.messageReceived(ctx, e);
}
@Override
public void exceptionCaught(final ChannelHandlerContext ctx, final ExceptionEvent e)
throws Exception {
upstream.exceptionCaught(ctx, e);
}
@Override
public void channelOpen(final ChannelHandlerContext ctx, final ChannelStateEvent e)
throws Exception {
upstream.channelOpen(ctx, e);
}
@Override
public void channelBound(final ChannelHandlerContext ctx, final ChannelStateEvent e)
throws Exception {
upstream.channelBound(ctx, e);
}
@Override
public void channelConnected(final ChannelHandlerContext ctx, final ChannelStateEvent e)
throws Exception {
upstream.channelConnected(ctx, e);
}
@Override
public void channelInterestChanged(final ChannelHandlerContext ctx, final ChannelStateEvent e)
throws Exception {
upstream.channelInterestChanged(ctx, e);
}
@Override
public void channelDisconnected(final ChannelHandlerContext ctx, final ChannelStateEvent e)
throws Exception {
upstream.channelDisconnected(ctx, e);
}
@Override
public void channelUnbound(final ChannelHandlerContext ctx, final ChannelStateEvent e)
throws Exception {
upstream.channelUnbound(ctx, e);
}
@Override
public void channelClosed(final ChannelHandlerContext ctx, final ChannelStateEvent e)
throws Exception {
upstream.channelClosed(ctx, e);
}
@Override
public void writeComplete(final ChannelHandlerContext ctx, final WriteCompletionEvent e)
throws Exception {
upstream.writeComplete(ctx, e);
}
@Override
public void childChannelOpen(final ChannelHandlerContext ctx, final ChildChannelStateEvent e)
throws Exception {
upstream.childChannelOpen(ctx, e);
}
@Override
public void childChannelClosed(final ChannelHandlerContext ctx, final ChildChannelStateEvent e)
throws Exception {
upstream.childChannelClosed(ctx, e);
}
@Override
public void handleDownstream(final ChannelHandlerContext ctx, final ChannelEvent e)
throws Exception {
downstream.handleDownstream(ctx, e);
}
@Override
public void writeRequested(final ChannelHandlerContext ctx, final MessageEvent e)
throws Exception {
downstream.writeRequested(ctx, e);
}
@Override
public void bindRequested(final ChannelHandlerContext ctx, final ChannelStateEvent e)
throws Exception {
downstream.bindRequested(ctx, e);
}
@Override
public void connectRequested(final ChannelHandlerContext ctx, final ChannelStateEvent e)
throws Exception {
downstream.connectRequested(ctx, e);
}
@Override
public void setInterestOpsRequested(final ChannelHandlerContext ctx, final ChannelStateEvent e)
throws Exception {
downstream.setInterestOpsRequested(ctx, e);
}
@Override
public void disconnectRequested(final ChannelHandlerContext ctx, final ChannelStateEvent e)
throws Exception {
downstream.disconnectRequested(ctx, e);
}
@Override
public void unbindRequested(final ChannelHandlerContext ctx, final ChannelStateEvent e)
throws Exception {
downstream.unbindRequested(ctx, e);
}
@Override
public void closeRequested(final ChannelHandlerContext ctx, final ChannelStateEvent e)
throws Exception {
downstream.closeRequested(ctx, e);
}
}

View File

@ -0,0 +1,110 @@
/*
* 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.
*/
import com.ning.http.client.AsyncCompletionHandler
import com.ning.http.client.AsyncHttpClient
import com.ning.http.client.AsyncHttpClientConfig
import com.ning.http.client.Response
import io.opentelemetry.auto.instrumentation.api.Tags
import io.opentelemetry.auto.test.base.HttpClientTest
import spock.lang.AutoCleanup
import spock.lang.Shared
import java.util.concurrent.ExecutionException
import java.util.concurrent.TimeUnit
import static io.opentelemetry.auto.test.utils.PortUtils.UNUSABLE_PORT
import static io.opentelemetry.auto.test.utils.TraceUtils.basicSpan
import static io.opentelemetry.auto.test.utils.TraceUtils.runUnderTrace
class Netty38ClientTest extends HttpClientTest {
@Shared
def clientConfig = new AsyncHttpClientConfig.Builder()
.setRequestTimeoutInMs(TimeUnit.SECONDS.toMillis(10).toInteger())
.build()
@Shared
@AutoCleanup
AsyncHttpClient asyncHttpClient = new AsyncHttpClient(clientConfig)
@Override
int doRequest(String method, URI uri, Map<String, String> headers, Closure callback) {
def methodName = "prepare" + method.toLowerCase().capitalize()
def requestBuilder = asyncHttpClient."$methodName"(uri.toString())
headers.each { requestBuilder.setHeader(it.key, it.value) }
def response = requestBuilder.execute(new AsyncCompletionHandler() {
@Override
Object onCompleted(Response response) throws Exception {
callback?.call()
return response
}
}).get()
return response.statusCode
}
@Override
boolean testRedirects() {
false
}
@Override
boolean testConnectionFailure() {
false
}
def "connection error (unopened port)"() {
given:
def uri = new URI("http://127.0.0.1:$UNUSABLE_PORT/")
when:
runUnderTrace("parent") {
doRequest(method, uri)
}
then:
def ex = thrown(Exception)
def thrownException = ex instanceof ExecutionException ? ex.cause : ex
and:
assertTraces(1) {
trace(0, 2) {
basicSpan(it, 0, "parent", null, thrownException)
span(1) {
operationName "CONNECT"
childOf span(0)
errored true
tags {
"$Tags.COMPONENT" "netty"
Class errorClass = ConnectException
try {
errorClass = Class.forName('io.netty.channel.AbstractChannel$AnnotatedConnectException')
} catch (ClassNotFoundException e) {
// Older versions use 'java.net.ConnectException' and do not have 'io.netty.channel.AbstractChannel$AnnotatedConnectException'
}
"error.type" errorClass.name
"error.stack" String
// slightly different message on windows
"error.msg" ~/Connection refused:( no further information:)? \/127.0.0.1:$UNUSABLE_PORT/
}
}
}
}
where:
method = "GET"
}
}

View File

@ -0,0 +1,136 @@
/*
* 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.
*/
import io.opentelemetry.auto.test.base.HttpServerTest
import org.jboss.netty.bootstrap.ServerBootstrap
import org.jboss.netty.buffer.ChannelBuffer
import org.jboss.netty.buffer.ChannelBuffers
import org.jboss.netty.channel.Channel
import org.jboss.netty.channel.ChannelHandlerContext
import org.jboss.netty.channel.ChannelPipeline
import org.jboss.netty.channel.DefaultChannelPipeline
import org.jboss.netty.channel.DownstreamMessageEvent
import org.jboss.netty.channel.ExceptionEvent
import org.jboss.netty.channel.FailedChannelFuture
import org.jboss.netty.channel.MessageEvent
import org.jboss.netty.channel.SimpleChannelHandler
import org.jboss.netty.channel.SucceededChannelFuture
import org.jboss.netty.channel.socket.nio.NioServerSocketChannelFactory
import org.jboss.netty.handler.codec.http.DefaultHttpResponse
import org.jboss.netty.handler.codec.http.HttpRequest
import org.jboss.netty.handler.codec.http.HttpResponse
import org.jboss.netty.handler.codec.http.HttpResponseStatus
import org.jboss.netty.handler.codec.http.HttpServerCodec
import org.jboss.netty.handler.logging.LoggingHandler
import org.jboss.netty.logging.InternalLogLevel
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.EXCEPTION
import static io.opentelemetry.auto.test.base.HttpServerTest.ServerEndpoint.NOT_FOUND
import static io.opentelemetry.auto.test.base.HttpServerTest.ServerEndpoint.QUERY_PARAM
import static io.opentelemetry.auto.test.base.HttpServerTest.ServerEndpoint.REDIRECT
import static io.opentelemetry.auto.test.base.HttpServerTest.ServerEndpoint.SUCCESS
import static io.opentelemetry.auto.test.base.HttpServerTest.ServerEndpoint.forPath
import static org.jboss.netty.handler.codec.http.HttpHeaders.Names.CONTENT_LENGTH
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.HttpVersion.HTTP_1_1
class Netty38ServerTest extends HttpServerTest<Channel> {
ChannelPipeline channelPipeline() {
ChannelPipeline channelPipeline = new DefaultChannelPipeline()
channelPipeline.addLast("http-codec", new HttpServerCodec())
channelPipeline.addLast("controller", new SimpleChannelHandler() {
@Override
void messageReceived(ChannelHandlerContext ctx, MessageEvent msg) throws Exception {
if (msg.getMessage() instanceof HttpRequest) {
def uri = URI.create((msg.getMessage() as HttpRequest).getUri())
HttpServerTest.ServerEndpoint endpoint = forPath(uri.path)
ctx.sendDownstream controller(endpoint) {
HttpResponse response
ChannelBuffer responseContent = null
switch (endpoint) {
case SUCCESS:
case ERROR:
responseContent = ChannelBuffers.copiedBuffer(endpoint.body, CharsetUtil.UTF_8)
response = new DefaultHttpResponse(HTTP_1_1, HttpResponseStatus.valueOf(endpoint.status))
response.setContent(responseContent)
break
case QUERY_PARAM:
responseContent = ChannelBuffers.copiedBuffer(uri.query, CharsetUtil.UTF_8)
response = new DefaultHttpResponse(HTTP_1_1, HttpResponseStatus.valueOf(endpoint.status))
response.setContent(responseContent)
break
case REDIRECT:
response = new DefaultHttpResponse(HTTP_1_1, HttpResponseStatus.valueOf(endpoint.status))
response.headers().set(LOCATION, endpoint.body)
break
case EXCEPTION:
throw new Exception(endpoint.body)
default:
responseContent = ChannelBuffers.copiedBuffer(NOT_FOUND.body, CharsetUtil.UTF_8)
response = new DefaultHttpResponse(HTTP_1_1, HttpResponseStatus.valueOf(endpoint.status))
response.setContent(responseContent)
break
}
response.headers().set(CONTENT_TYPE, "text/plain")
if (responseContent) {
response.headers().set(CONTENT_LENGTH, responseContent.readableBytes())
}
return new DownstreamMessageEvent(
ctx.getChannel(),
new SucceededChannelFuture(ctx.getChannel()),
response,
ctx.getChannel().getRemoteAddress())
}
}
}
@Override
void exceptionCaught(ChannelHandlerContext ctx, ExceptionEvent ex) throws Exception {
ChannelBuffer buffer = ChannelBuffers.copiedBuffer(ex.getCause().getMessage(), CharsetUtil.UTF_8)
HttpResponse response = new DefaultHttpResponse(HTTP_1_1, HttpResponseStatus.INTERNAL_SERVER_ERROR)
response.setContent(buffer)
response.headers().set(CONTENT_TYPE, "text/plain")
response.headers().set(CONTENT_LENGTH, buffer.readableBytes())
ctx.sendDownstream(new DownstreamMessageEvent(
ctx.getChannel(),
new FailedChannelFuture(ctx.getChannel(), ex.getCause()),
response,
ctx.getChannel().getRemoteAddress()))
}
})
return channelPipeline
}
@Override
Channel startServer(int port) {
ServerBootstrap bootstrap = new ServerBootstrap(new NioServerSocketChannelFactory())
bootstrap.setParentHandler(new LoggingHandler(InternalLogLevel.INFO))
bootstrap.setPipeline(channelPipeline())
InetSocketAddress address = new InetSocketAddress(port)
return bootstrap.bind(address)
}
@Override
void stopServer(Channel server) {
server?.disconnect()
}
}

View File

@ -0,0 +1,36 @@
/*
* 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.
*/
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

@ -20,19 +20,17 @@ import io.opentelemetry.auto.bootstrap.WeakMap;
import io.opentelemetry.auto.instrumentation.netty.v4_0.client.HttpClientTracingHandler;
import io.opentelemetry.auto.instrumentation.netty.v4_0.server.HttpServerTracingHandler;
import io.opentelemetry.trace.Span;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
public class AttributeKeys {
private static final WeakMap<ClassLoader, Map<String, AttributeKey<?>>> map =
private static final WeakMap<ClassLoader, ConcurrentMap<String, AttributeKey<?>>> map =
WeakMap.Implementation.DEFAULT.get();
private static final WeakMap.ValueSupplier<ClassLoader, Map<String, AttributeKey<?>>>
private static final WeakMap.ValueSupplier<ClassLoader, ConcurrentMap<String, AttributeKey<?>>>
mapSupplier =
new WeakMap.ValueSupplier<ClassLoader, Map<String, AttributeKey<?>>>() {
new WeakMap.ValueSupplier<ClassLoader, ConcurrentMap<String, AttributeKey<?>>>() {
@Override
public Map<String, AttributeKey<?>> get(final ClassLoader ignored) {
public ConcurrentMap<String, AttributeKey<?>> get(final ClassLoader ignore) {
return new ConcurrentHashMap<>();
}
};
@ -57,7 +55,7 @@ public class AttributeKeys {
* cassandra driver.
*/
private static <T> AttributeKey<T> attributeKey(final String key) {
final Map<String, AttributeKey<?>> classLoaderMap =
final ConcurrentMap<String, AttributeKey<?>> classLoaderMap =
map.computeIfAbsent(AttributeKey.class.getClassLoader(), mapSupplier);
if (classLoaderMap.containsKey(key)) {
return (AttributeKey<T>) classLoaderMap.get(key);

View File

@ -62,31 +62,33 @@ public class HttpClientRequestTracingHandler extends ChannelOutboundHandlerAdapt
final Span span =
TRACER.spanBuilder(DECORATE.spanNameForRequest(request)).setSpanKind(CLIENT).startSpan();
try (final Scope scope = TRACER.withSpan(span)) {
DECORATE.afterStart(span);
DECORATE.onRequest(span, request);
DECORATE.onPeerConnection(span, (InetSocketAddress) ctx.channel().remoteAddress());
DECORATE.afterStart(span);
DECORATE.onRequest(span, request);
DECORATE.onPeerConnection(span, (InetSocketAddress) ctx.channel().remoteAddress());
// AWS calls are often signed, so we can't add headers without breaking the signature.
if (!request.headers().contains("amz-sdk-invocation-id")) {
final Context context = withSpan(span, Context.current());
OpenTelemetry.getPropagators()
.getHttpTextFormat()
.inject(context, request.headers(), SETTER);
}
final Context context = withSpan(span, Context.current());
ctx.channel().attr(AttributeKeys.CLIENT_ATTRIBUTE_KEY).set(span);
// AWS calls are often signed, so we can't add headers without breaking the signature.
if (!request.headers().contains("amz-sdk-invocation-id")) {
OpenTelemetry.getPropagators().getHttpTextFormat().inject(context, request.headers(), SETTER);
}
ctx.channel().attr(AttributeKeys.CLIENT_ATTRIBUTE_KEY).set(span);
try (final Scope scope = currentContextWith(span)) {
ctx.write(msg, prm);
} catch (final Throwable throwable) {
DECORATE.onError(span, throwable);
DECORATE.beforeFinish(span);
span.end();
throw throwable;
}
if (null != parentScope) {
parentScope.close();
try {
ctx.write(msg, prm);
} catch (final Throwable throwable) {
DECORATE.onError(span, throwable);
DECORATE.beforeFinish(span);
span.end();
throw throwable;
}
} finally {
if (null != parentScope) {
parentScope.close();
}
}
}
}

View File

@ -53,35 +53,6 @@ public class NettyHttpClientDecorator extends HttpClientDecorator<HttpRequest, H
}
}
@Override
protected String hostname(final HttpRequest request) {
try {
final URI uri = new URI(request.getUri());
if ((uri.getHost() == null || uri.getHost().equals("")) && request.headers().contains(HOST)) {
return request.headers().get(HOST).split(":")[0];
} else {
return uri.getHost();
}
} catch (final Exception e) {
return null;
}
}
@Override
protected Integer port(final HttpRequest request) {
try {
final URI uri = new URI(request.getUri());
if ((uri.getHost() == null || uri.getHost().equals("")) && request.headers().contains(HOST)) {
final String[] hostPort = request.headers().get(HOST).split(":");
return hostPort.length == 2 ? Integer.parseInt(hostPort[1]) : null;
} else {
return uri.getPort();
}
} catch (final Exception e) {
return null;
}
}
@Override
protected Integer status(final HttpResponse httpResponse) {
return httpResponse.getStatus().code();

View File

@ -20,19 +20,17 @@ import io.opentelemetry.auto.bootstrap.WeakMap;
import io.opentelemetry.auto.instrumentation.netty.v4_1.client.HttpClientTracingHandler;
import io.opentelemetry.auto.instrumentation.netty.v4_1.server.HttpServerTracingHandler;
import io.opentelemetry.trace.Span;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
public class AttributeKeys {
private static final WeakMap<ClassLoader, Map<String, AttributeKey<?>>> map =
private static final WeakMap<ClassLoader, ConcurrentMap<String, AttributeKey<?>>> map =
WeakMap.Implementation.DEFAULT.get();
private static final WeakMap.ValueSupplier<ClassLoader, Map<String, AttributeKey<?>>>
private static final WeakMap.ValueSupplier<ClassLoader, ConcurrentMap<String, AttributeKey<?>>>
mapSupplier =
new WeakMap.ValueSupplier<ClassLoader, Map<String, AttributeKey<?>>>() {
new WeakMap.ValueSupplier<ClassLoader, ConcurrentMap<String, AttributeKey<?>>>() {
@Override
public Map<String, AttributeKey<?>> get(final ClassLoader ignored) {
public ConcurrentMap<String, AttributeKey<?>> get(final ClassLoader ignore) {
return new ConcurrentHashMap<>();
}
};
@ -62,7 +60,7 @@ public class AttributeKeys {
* cassandra driver.
*/
private static <T> AttributeKey<T> attributeKey(final String key) {
final Map<String, AttributeKey<?>> classLoaderMap =
final ConcurrentMap<String, AttributeKey<?>> classLoaderMap =
map.computeIfAbsent(AttributeKey.class.getClassLoader(), mapSupplier);
if (classLoaderMap.containsKey(key)) {
return (AttributeKey<T>) classLoaderMap.get(key);

View File

@ -18,7 +18,6 @@ package io.opentelemetry.auto.instrumentation.netty.v4_1.client;
import static io.opentelemetry.auto.instrumentation.netty.v4_1.client.NettyHttpClientDecorator.DECORATE;
import static io.opentelemetry.auto.instrumentation.netty.v4_1.client.NettyHttpClientDecorator.TRACER;
import static io.opentelemetry.auto.instrumentation.netty.v4_1.client.NettyResponseInjectAdapter.SETTER;
import static io.opentelemetry.context.ContextUtils.withScopedContext;
import static io.opentelemetry.trace.Span.Kind.CLIENT;
import static io.opentelemetry.trace.TracingContextUtils.currentContextWith;
import static io.opentelemetry.trace.TracingContextUtils.withSpan;
@ -63,31 +62,33 @@ public class HttpClientRequestTracingHandler extends ChannelOutboundHandlerAdapt
final Span span =
TRACER.spanBuilder(DECORATE.spanNameForRequest(request)).setSpanKind(CLIENT).startSpan();
try (final Scope scope = TRACER.withSpan(span)) {
DECORATE.afterStart(span);
DECORATE.onRequest(span, request);
DECORATE.onPeerConnection(span, (InetSocketAddress) ctx.channel().remoteAddress());
DECORATE.afterStart(span);
DECORATE.onRequest(span, request);
DECORATE.onPeerConnection(span, (InetSocketAddress) ctx.channel().remoteAddress());
// AWS calls are often signed, so we can't add headers without breaking the signature.
if (!request.headers().contains("amz-sdk-invocation-id")) {
final Context context = withSpan(span, Context.current());
OpenTelemetry.getPropagators()
.getHttpTextFormat()
.inject(context, request.headers(), SETTER);
}
final Context context = withSpan(span, Context.current());
ctx.channel().attr(AttributeKeys.CLIENT_ATTRIBUTE_KEY).set(span);
// AWS calls are often signed, so we can't add headers without breaking the signature.
if (!request.headers().contains("amz-sdk-invocation-id")) {
OpenTelemetry.getPropagators().getHttpTextFormat().inject(context, request.headers(), SETTER);
}
ctx.channel().attr(AttributeKeys.CLIENT_ATTRIBUTE_KEY).set(span);
try (final Scope scope = withScopedContext(context)) {
ctx.write(msg, prm);
} catch (final Throwable throwable) {
DECORATE.onError(span, throwable);
DECORATE.beforeFinish(span);
span.end();
throw throwable;
}
if (null != parentScope) {
parentScope.close();
try {
ctx.write(msg, prm);
} catch (final Throwable throwable) {
DECORATE.onError(span, throwable);
DECORATE.beforeFinish(span);
span.end();
throw throwable;
}
} finally {
if (null != parentScope) {
parentScope.close();
}
}
}
}

View File

@ -53,35 +53,6 @@ public class NettyHttpClientDecorator extends HttpClientDecorator<HttpRequest, H
}
}
@Override
protected String hostname(final HttpRequest request) {
try {
final URI uri = new URI(request.uri());
if ((uri.getHost() == null || uri.getHost().equals("")) && request.headers().contains(HOST)) {
return request.headers().get(HOST).split(":")[0];
} else {
return uri.getHost();
}
} catch (final Exception e) {
return null;
}
}
@Override
protected Integer port(final HttpRequest request) {
try {
final URI uri = new URI(request.uri());
if ((uri.getHost() == null || uri.getHost().equals("")) && request.headers().contains(HOST)) {
final String[] hostPort = request.headers().get(HOST).split(":");
return hostPort.length == 2 ? Integer.parseInt(hostPort[1]) : null;
} else {
return uri.getPort();
}
} catch (final Exception e) {
return null;
}
}
@Override
protected Integer status(final HttpResponse httpResponse) {
return httpResponse.status().code();

View File

@ -43,16 +43,6 @@ public class OkHttpClientDecorator extends HttpClientDecorator<Request, Response
return httpRequest.url().uri();
}
@Override
protected String hostname(final Request httpRequest) {
return httpRequest.url().host();
}
@Override
protected Integer port(final Request httpRequest) {
return httpRequest.url().port();
}
@Override
protected Integer status(final Response httpResponse) {
return httpResponse.code();

View File

@ -39,16 +39,6 @@ public class PlayWSClientDecorator extends HttpClientDecorator<Request, Response
return request.getUri().toJavaNetURI();
}
@Override
protected String hostname(final Request request) {
return request.getUri().getHost();
}
@Override
protected Integer port(final Request request) {
return request.getUri().getPort();
}
@Override
protected Integer status(final Response response) {
return response.getStatusCode();

View File

@ -39,7 +39,7 @@ dependencies {
// JPA dependencies
testCompile project(':instrumentation:jdbc')
testCompile group: 'org.springframework.data', name: 'spring-data-jpa', version: '1.7.0.RELEASE'
testCompile group: 'org.springframework.data', name: 'spring-data-jpa', version: '1.8.0.RELEASE'
testCompile group: 'com.mysema.querydsl', name: 'querydsl-jpa', version: '3.7.4'
testCompile group: 'org.hsqldb', name: 'hsqldb', version: '2.0.0'
testCompile group: 'org.hibernate', name: 'hibernate-entitymanager', version: '4.3.0.Final'

View File

@ -53,16 +53,6 @@ public class SpringWebfluxHttpClientDecorator
return httpRequest.url();
}
@Override
protected String hostname(final ClientRequest httpRequest) {
return httpRequest.url().getHost();
}
@Override
protected Integer port(final ClientRequest httpRequest) {
return httpRequest.url().getPort();
}
@Override
protected Integer status(final ClientResponse httpResponse) {
return httpResponse.statusCode().value();

View File

@ -81,8 +81,8 @@ dependencies {
tasks.withType(Test).configureEach {
// Multi-threaded logging seems to be causing deadlocks with Gradle's log capture.
// jvmArgs "-Dio.opentelemetry.auto.slf4j.simpleLogger.defaultLogLevel=debug"
// jvmArgs "-Dorg.slf4j.simpleLogger.defaultLogLevel=debug"
// jvmArgs "-Dio.opentelemetry.auto.slf4j.simpleLogger.defaultLogLevel=debug"
// jvmArgs "-Dorg.slf4j.simpleLogger.defaultLogLevel=debug"
doFirst {
// Defining here to allow jacoco to be first on the command line.

View File

@ -106,6 +106,7 @@ include ':instrumentation:mongo:mongo-3.1'
include ':instrumentation:mongo:mongo-3.7'
include ':instrumentation:mongo:mongo-async-3.3'
include ':instrumentation:mongo:mongo-common'
include ':instrumentation:netty:netty-3.8'
include ':instrumentation:netty:netty-4.0'
include ':instrumentation:netty:netty-4.1'
include ':instrumentation:okhttp-3.0'

View File

@ -301,4 +301,18 @@ public abstract class AgentTestRunner extends AgentSpecification {
}
}
}
protected static String getClassName(Class clazz) {
String className = clazz.getSimpleName();
if (className.isEmpty()) {
className = clazz.getName();
if (clazz.getPackage() != null) {
final String pkgName = clazz.getPackage().getName();
if (!pkgName.isEmpty()) {
className = clazz.getName().replace(pkgName, "").substring(1);
}
}
}
return className;
}
}

View File

@ -24,7 +24,6 @@ import java.util.regex.Pattern
class TagsAssert {
private final Map<String, AttributeValue> tags
private final Set<String> assertedTags = new TreeSet<>()
private final Set<String> ignoredTags = ["slow.stack"] // Don't error if this tag isn't checked.
private TagsAssert(attributes) {
this.tags = attributes
@ -85,7 +84,6 @@ class TagsAssert {
void assertTagsAllVerified() {
def set = new TreeMap<>(tags).keySet()
set.removeAll(assertedTags)
set.removeAll(ignoredTags)
// The primary goal is to ensure the set is empty.
// tags and assertedTags are included via an "always true" comparison
// so they provide better context in the error message.