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+ |
|
||||
| [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+ |
|
||||
| [khttp](https://khttp.readthedocs.io) | 0.1.0+ |
|
||||
| [Lettuce](https://github.com/lettuce-io/lettuce-core) | 4.0+ |
|
||||
| [Log4j](https://logging.apache.org/log4j/2.x/) | 1.1+ |
|
||||
| [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:kafka-clients-0.11'
|
||||
include ':instrumentation:kafka-streams-0.11'
|
||||
include ':instrumentation:khttp-0.1'
|
||||
include ':instrumentation:lettuce:lettuce-4.0'
|
||||
include ':instrumentation:lettuce:lettuce-5.0'
|
||||
include ':instrumentation:log4j:log4j-1.1'
|
||||
|
|
Loading…
Reference in New Issue