Merge pull request #283 from trask/dd-merge
Merge changes from dd-trace-java 0.47.0
This commit is contained in:
commit
8ba53dc5ca
|
@ -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:
|
||||
|
|
|
@ -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
|
|
@ -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);
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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.
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
def groovyVer = "2.5.8"
|
||||
def groovyVer = "2.5.10"
|
||||
def spockGroovyVer = groovyVer.replaceAll(/\.\d+$/, '')
|
||||
|
||||
ext {
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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"
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
|
@ -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) {
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
|
@ -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"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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"
|
||||
}
|
||||
}
|
|
@ -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()
|
||||
}
|
||||
}
|
|
@ -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()));
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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));
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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
|
||||
}
|
||||
}
|
|
@ -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));
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
|
@ -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"
|
||||
}
|
||||
}
|
|
@ -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()
|
||||
}
|
||||
}
|
|
@ -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()));
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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'
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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'
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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.
|
||||
|
|
Loading…
Reference in New Issue