Support for khttp library (#401)
This commit is contained in:
parent
6d0d3f54ce
commit
fe8819fb8f
|
@ -34,6 +34,7 @@ to capture telemetry from a number of popular libraries and frameworks.
|
||||||
| [JMS](https://javaee.github.io/javaee-spec/javadocs/javax/jms/package-summary.html) | 1.1+ |
|
| [JMS](https://javaee.github.io/javaee-spec/javadocs/javax/jms/package-summary.html) | 1.1+ |
|
||||||
| [JSP](https://javaee.github.io/javaee-spec/javadocs/javax/servlet/jsp/package-summary.html) | 2.3+ |
|
| [JSP](https://javaee.github.io/javaee-spec/javadocs/javax/servlet/jsp/package-summary.html) | 2.3+ |
|
||||||
| [Kafka](https://kafka.apache.org/20/javadoc/overview-summary.html) | 0.11+ |
|
| [Kafka](https://kafka.apache.org/20/javadoc/overview-summary.html) | 0.11+ |
|
||||||
|
| [khttp](https://khttp.readthedocs.io) | 0.1.0+ |
|
||||||
| [Lettuce](https://github.com/lettuce-io/lettuce-core) | 4.0+ |
|
| [Lettuce](https://github.com/lettuce-io/lettuce-core) | 4.0+ |
|
||||||
| [Log4j](https://logging.apache.org/log4j/2.x/) | 1.1+ |
|
| [Log4j](https://logging.apache.org/log4j/2.x/) | 1.1+ |
|
||||||
| [Logback](https://github.com/qos-ch/logback) | 1.0+ |
|
| [Logback](https://github.com/qos-ch/logback) | 1.0+ |
|
||||||
|
|
|
@ -0,0 +1,28 @@
|
||||||
|
ext {
|
||||||
|
minJavaVersionForTests = JavaVersion.VERSION_1_8
|
||||||
|
}
|
||||||
|
|
||||||
|
apply from: "${rootDir}/gradle/instrumentation.gradle"
|
||||||
|
apply plugin: 'org.unbroken-dome.test-sets'
|
||||||
|
|
||||||
|
muzzle {
|
||||||
|
pass {
|
||||||
|
group = 'khttp'
|
||||||
|
module = 'khttp'
|
||||||
|
versions = "(,)"
|
||||||
|
assertInverse = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
testSets {
|
||||||
|
latestDepTest
|
||||||
|
}
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
compileOnly group: 'khttp', name: 'khttp', version: '0.1.0'
|
||||||
|
|
||||||
|
testCompile group: 'khttp', name: 'khttp', version: '0.1.0'
|
||||||
|
|
||||||
|
latestDepTestCompile group: 'khttp', name: 'khttp', version: '+'
|
||||||
|
}
|
|
@ -0,0 +1,81 @@
|
||||||
|
/*
|
||||||
|
* Copyright The 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.khttp;
|
||||||
|
|
||||||
|
import static io.opentelemetry.auto.instrumentation.khttp.KHttpDecorator.DECORATE;
|
||||||
|
import static io.opentelemetry.auto.instrumentation.khttp.KHttpDecorator.TRACER;
|
||||||
|
import static io.opentelemetry.auto.instrumentation.khttp.KHttpHeadersInjectAdapter.SETTER;
|
||||||
|
import static io.opentelemetry.auto.instrumentation.khttp.KHttpHeadersInjectAdapter.asWritable;
|
||||||
|
import static io.opentelemetry.context.ContextUtils.withScopedContext;
|
||||||
|
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.CallDepthThreadLocalMap;
|
||||||
|
import io.opentelemetry.auto.instrumentation.api.SpanWithScope;
|
||||||
|
import io.opentelemetry.trace.Span;
|
||||||
|
import java.util.Map;
|
||||||
|
import khttp.KHttp;
|
||||||
|
import khttp.responses.Response;
|
||||||
|
import net.bytebuddy.asm.Advice;
|
||||||
|
|
||||||
|
public class KHttpAdvice {
|
||||||
|
|
||||||
|
@Advice.OnMethodEnter(suppress = Throwable.class)
|
||||||
|
public static SpanWithScope methodEnter(
|
||||||
|
@Advice.Argument(value = 0) String method,
|
||||||
|
@Advice.Argument(value = 1) String uri,
|
||||||
|
@Advice.Argument(value = 2) Map<String, String> headers) {
|
||||||
|
|
||||||
|
final int callDepth = CallDepthThreadLocalMap.incrementCallDepth(KHttp.class);
|
||||||
|
if (callDepth > 0) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
final Span span = TRACER.spanBuilder("HTTP " + method).setSpanKind(CLIENT).startSpan();
|
||||||
|
|
||||||
|
DECORATE.afterStart(span);
|
||||||
|
DECORATE.onRequest(span, new RequestWrapper(method, uri));
|
||||||
|
|
||||||
|
final Context context = withSpan(span, Context.current());
|
||||||
|
|
||||||
|
OpenTelemetry.getPropagators().getHttpTextFormat().inject(context, asWritable(headers), SETTER);
|
||||||
|
return new SpanWithScope(span, withScopedContext(context));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class)
|
||||||
|
public static void methodExit(
|
||||||
|
@Advice.Enter final SpanWithScope spanWithScope,
|
||||||
|
@Advice.Return final Response result,
|
||||||
|
@Advice.Thrown final Throwable throwable) {
|
||||||
|
if (spanWithScope == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
CallDepthThreadLocalMap.reset(KHttp.class);
|
||||||
|
|
||||||
|
try {
|
||||||
|
final Span span = spanWithScope.getSpan();
|
||||||
|
|
||||||
|
DECORATE.onResponse(span, result);
|
||||||
|
DECORATE.onError(span, throwable);
|
||||||
|
DECORATE.beforeFinish(span);
|
||||||
|
span.end();
|
||||||
|
} finally {
|
||||||
|
spanWithScope.closeScope();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,45 @@
|
||||||
|
/*
|
||||||
|
* Copyright The 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.khttp;
|
||||||
|
|
||||||
|
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 khttp.responses.Response;
|
||||||
|
|
||||||
|
public class KHttpDecorator extends HttpClientDecorator<RequestWrapper, Response> {
|
||||||
|
public static final KHttpDecorator DECORATE = new KHttpDecorator();
|
||||||
|
|
||||||
|
public static final Tracer TRACER =
|
||||||
|
OpenTelemetry.getTracerProvider().get("io.opentelemetry.auto.khttp-0.1");
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected String method(RequestWrapper requestWrapper) {
|
||||||
|
return requestWrapper.method;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected URI url(RequestWrapper requestWrapper) throws URISyntaxException {
|
||||||
|
return new URI(requestWrapper.uri);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Integer status(Response response) {
|
||||||
|
return response.getStatusCode();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,49 @@
|
||||||
|
/*
|
||||||
|
* Copyright The 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.khttp;
|
||||||
|
|
||||||
|
import io.opentelemetry.context.propagation.HttpTextFormat;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
public class KHttpHeadersInjectAdapter implements HttpTextFormat.Setter<Map<String, String>> {
|
||||||
|
|
||||||
|
private static Class emptyMap;
|
||||||
|
|
||||||
|
static {
|
||||||
|
try {
|
||||||
|
emptyMap = Class.forName("kotlin.collections.EmptyMap");
|
||||||
|
} catch (ClassNotFoundException e) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Map<String, String> asWritable(Map<String, String> headers) {
|
||||||
|
// EmptyMap is read-only so we have to substitute it with writable instance to be able to inject
|
||||||
|
// headers
|
||||||
|
if (emptyMap != null && emptyMap.isInstance(headers)) {
|
||||||
|
return new HashMap<>();
|
||||||
|
} else {
|
||||||
|
return headers;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static final KHttpHeadersInjectAdapter SETTER = new KHttpHeadersInjectAdapter();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void set(Map<String, String> carrier, String key, String value) {
|
||||||
|
carrier.put(key, value);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,73 @@
|
||||||
|
/*
|
||||||
|
* Copyright The 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.khttp;
|
||||||
|
|
||||||
|
import static io.opentelemetry.auto.tooling.ClassLoaderMatcher.hasClassesNamed;
|
||||||
|
import static io.opentelemetry.auto.tooling.bytebuddy.matcher.AgentElementMatchers.safeHasSuperType;
|
||||||
|
import static java.util.Collections.singletonMap;
|
||||||
|
import static net.bytebuddy.matcher.ElementMatchers.isAbstract;
|
||||||
|
import static net.bytebuddy.matcher.ElementMatchers.isMethod;
|
||||||
|
import static net.bytebuddy.matcher.ElementMatchers.named;
|
||||||
|
import static net.bytebuddy.matcher.ElementMatchers.not;
|
||||||
|
import static net.bytebuddy.matcher.ElementMatchers.returns;
|
||||||
|
import static net.bytebuddy.matcher.ElementMatchers.takesArgument;
|
||||||
|
|
||||||
|
import com.google.auto.service.AutoService;
|
||||||
|
import io.opentelemetry.auto.tooling.Instrumenter;
|
||||||
|
import java.util.Map;
|
||||||
|
import net.bytebuddy.description.method.MethodDescription;
|
||||||
|
import net.bytebuddy.description.type.TypeDescription;
|
||||||
|
import net.bytebuddy.matcher.ElementMatcher;
|
||||||
|
|
||||||
|
@AutoService(Instrumenter.class)
|
||||||
|
public class KHttpInstrumentation extends Instrumenter.Default {
|
||||||
|
|
||||||
|
public KHttpInstrumentation() {
|
||||||
|
super("khttp");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ElementMatcher<ClassLoader> classLoaderMatcher() {
|
||||||
|
return hasClassesNamed("khttp.KHttp");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ElementMatcher<TypeDescription> typeMatcher() {
|
||||||
|
return safeHasSuperType(named("khttp.KHttp"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String[] helperClassNames() {
|
||||||
|
return new String[] {
|
||||||
|
packageName + ".KHttpHeadersInjectAdapter",
|
||||||
|
packageName + ".KHttpDecorator",
|
||||||
|
packageName + ".RequestWrapper",
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Map<? extends ElementMatcher<? super MethodDescription>, String> transformers() {
|
||||||
|
return singletonMap(
|
||||||
|
isMethod()
|
||||||
|
.and(not(isAbstract()))
|
||||||
|
.and(named("request"))
|
||||||
|
.and(takesArgument(0, named("java.lang.String")))
|
||||||
|
.and(takesArgument(1, named("java.lang.String")))
|
||||||
|
.and(takesArgument(2, named("java.util.Map")))
|
||||||
|
.and(returns(named("khttp.responses.Response"))),
|
||||||
|
packageName + ".KHttpAdvice");
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,26 @@
|
||||||
|
/*
|
||||||
|
* Copyright The 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.khttp;
|
||||||
|
|
||||||
|
public class RequestWrapper {
|
||||||
|
String method;
|
||||||
|
String uri;
|
||||||
|
|
||||||
|
public RequestWrapper(String method, String uri) {
|
||||||
|
this.method = method;
|
||||||
|
this.uri = uri;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,34 @@
|
||||||
|
/*
|
||||||
|
* Copyright The 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.HttpClientTest
|
||||||
|
import khttp.KHttp
|
||||||
|
|
||||||
|
class KHttpClientTest extends HttpClientTest {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
int doRequest(String method, URI uri, Map<String, String> headers, Closure callback) {
|
||||||
|
def response = KHttp.request(method, uri.toString(), headers, Collections.emptyMap(), null, null, null, null, 1)
|
||||||
|
if (callback != null) {
|
||||||
|
callback.call()
|
||||||
|
}
|
||||||
|
return response.statusCode
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
boolean testCircularRedirects() {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
|
@ -99,6 +99,7 @@ include ':instrumentation:jms-1.1'
|
||||||
include ':instrumentation:jsp-2.3'
|
include ':instrumentation:jsp-2.3'
|
||||||
include ':instrumentation:kafka-clients-0.11'
|
include ':instrumentation:kafka-clients-0.11'
|
||||||
include ':instrumentation:kafka-streams-0.11'
|
include ':instrumentation:kafka-streams-0.11'
|
||||||
|
include ':instrumentation:khttp-0.1'
|
||||||
include ':instrumentation:lettuce:lettuce-4.0'
|
include ':instrumentation:lettuce:lettuce-4.0'
|
||||||
include ':instrumentation:lettuce:lettuce-5.0'
|
include ':instrumentation:lettuce:lettuce-5.0'
|
||||||
include ':instrumentation:log4j:log4j-1.1'
|
include ':instrumentation:log4j:log4j-1.1'
|
||||||
|
|
Loading…
Reference in New Issue