-
Notifications
You must be signed in to change notification settings - Fork 828
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add Jodd-Http instrumentation (#7868)
This PR resolves #7629 This adds javaagent instrumentation for the [jodd-http](https://http.jodd.org/) `HttpRequest`. It creates `Http Client Spans` and `Http Client Metrics`, the lowest supported version is `org.jodd:jodd-http:4.2.0` (most recent: `6.3.0`), since this is the first version of the library supporting java 8, having follow-redirect capability and `HttpRequest#overwriteHeader()` method. The instrumented method's signature and return type `HttpRequest#send()` has not been modified since, and therefore the instrumentation works for all `jodd-http` versions above `4.2.0`. Since this is my first contribution/instrumentation, I orientated myself on the `apache-httpclient-5.0` instrumentation, but obviously I would be glad to get some feedback on this --------- Co-authored-by: Mateusz Rzeszutek <mrzeszutek@splunk.com>
- Loading branch information
Showing
11 changed files
with
467 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
plugins { | ||
id("otel.javaagent-instrumentation") | ||
} | ||
|
||
muzzle { | ||
pass { | ||
group.set("org.jodd") | ||
module.set("jodd-http") | ||
versions.set("[4.2.0,)") | ||
} | ||
} | ||
|
||
dependencies { | ||
// 4.2 is the first version with java 8, follow-redirects and HttpRequest#headerOverwrite method | ||
library("org.jodd:jodd-http:4.2.0") | ||
|
||
testImplementation(project(":instrumentation:jodd-http-4.2:javaagent")) | ||
testImplementation(project(":instrumentation-api-semconv")) | ||
} |
22 changes: 22 additions & 0 deletions
22
.../main/java/io/opentelemetry/javaagent/instrumentation/joddhttp/v4_2/HttpHeaderSetter.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
/* | ||
* Copyright The OpenTelemetry Authors | ||
* SPDX-License-Identifier: Apache-2.0 | ||
*/ | ||
|
||
package io.opentelemetry.javaagent.instrumentation.joddhttp.v4_2; | ||
|
||
import io.opentelemetry.context.propagation.TextMapSetter; | ||
import javax.annotation.Nullable; | ||
import jodd.http.HttpRequest; | ||
|
||
enum HttpHeaderSetter implements TextMapSetter<HttpRequest> { | ||
INSTANCE; | ||
|
||
@Override | ||
public void set(@Nullable HttpRequest carrier, String key, String value) { | ||
if (carrier == null) { | ||
return; | ||
} | ||
carrier.headerOverwrite(key, value); | ||
} | ||
} |
76 changes: 76 additions & 0 deletions
76
...o/opentelemetry/javaagent/instrumentation/joddhttp/v4_2/JoddHttpHttpAttributesGetter.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,76 @@ | ||
/* | ||
* Copyright The OpenTelemetry Authors | ||
* SPDX-License-Identifier: Apache-2.0 | ||
*/ | ||
|
||
package io.opentelemetry.javaagent.instrumentation.joddhttp.v4_2; | ||
|
||
import static io.opentelemetry.semconv.trace.attributes.SemanticAttributes.HttpFlavorValues.HTTP_1_0; | ||
import static io.opentelemetry.semconv.trace.attributes.SemanticAttributes.HttpFlavorValues.HTTP_1_1; | ||
import static io.opentelemetry.semconv.trace.attributes.SemanticAttributes.HttpFlavorValues.HTTP_2_0; | ||
import static io.opentelemetry.semconv.trace.attributes.SemanticAttributes.HttpFlavorValues.HTTP_3_0; | ||
|
||
import io.opentelemetry.instrumentation.api.instrumenter.http.HttpClientAttributesGetter; | ||
import java.util.Arrays; | ||
import java.util.HashSet; | ||
import java.util.List; | ||
import java.util.Set; | ||
import java.util.logging.Level; | ||
import java.util.logging.Logger; | ||
import javax.annotation.Nullable; | ||
import jodd.http.HttpRequest; | ||
import jodd.http.HttpResponse; | ||
|
||
final class JoddHttpHttpAttributesGetter | ||
implements HttpClientAttributesGetter<HttpRequest, HttpResponse> { | ||
private static final Logger logger = | ||
Logger.getLogger(JoddHttpHttpAttributesGetter.class.getName()); | ||
private static final Set<String> ALLOWED_HTTP_FLAVORS = | ||
new HashSet<>(Arrays.asList(HTTP_1_0, HTTP_1_1, HTTP_2_0, HTTP_3_0)); | ||
|
||
@Override | ||
public String getMethod(HttpRequest request) { | ||
return request.method(); | ||
} | ||
|
||
@Override | ||
public String getUrl(HttpRequest request) { | ||
return request.url(); | ||
} | ||
|
||
@Override | ||
public List<String> getRequestHeader(HttpRequest request, String name) { | ||
return request.headers(name); | ||
} | ||
|
||
@Override | ||
public Integer getStatusCode( | ||
HttpRequest request, HttpResponse response, @Nullable Throwable error) { | ||
return response.statusCode(); | ||
} | ||
|
||
@Override | ||
@Nullable | ||
public String getFlavor(HttpRequest request, @Nullable HttpResponse response) { | ||
String httpVersion = request.httpVersion(); | ||
if (httpVersion == null && response != null) { | ||
httpVersion = response.httpVersion(); | ||
} | ||
if (httpVersion != null) { | ||
if (httpVersion.contains("/")) { | ||
httpVersion = httpVersion.substring(httpVersion.lastIndexOf("/") + 1); | ||
} | ||
|
||
if (ALLOWED_HTTP_FLAVORS.contains(httpVersion)) { | ||
return httpVersion; | ||
} | ||
} | ||
logger.log(Level.FINE, "unexpected http protocol version: {0}", httpVersion); | ||
return null; | ||
} | ||
|
||
@Override | ||
public List<String> getResponseHeader(HttpRequest request, HttpResponse response, String name) { | ||
return response.headers(name); | ||
} | ||
} |
68 changes: 68 additions & 0 deletions
68
...ava/io/opentelemetry/javaagent/instrumentation/joddhttp/v4_2/JoddHttpInstrumentation.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,68 @@ | ||
/* | ||
* Copyright The OpenTelemetry Authors | ||
* SPDX-License-Identifier: Apache-2.0 | ||
*/ | ||
|
||
package io.opentelemetry.javaagent.instrumentation.joddhttp.v4_2; | ||
|
||
import static io.opentelemetry.javaagent.bootstrap.Java8BytecodeBridge.currentContext; | ||
import static io.opentelemetry.javaagent.instrumentation.joddhttp.v4_2.JoddHttpSingletons.instrumenter; | ||
import static net.bytebuddy.matcher.ElementMatchers.isMethod; | ||
import static net.bytebuddy.matcher.ElementMatchers.named; | ||
import static net.bytebuddy.matcher.ElementMatchers.takesArguments; | ||
|
||
import io.opentelemetry.context.Context; | ||
import io.opentelemetry.context.Scope; | ||
import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation; | ||
import io.opentelemetry.javaagent.extension.instrumentation.TypeTransformer; | ||
import jodd.http.HttpRequest; | ||
import jodd.http.HttpResponse; | ||
import net.bytebuddy.asm.Advice; | ||
import net.bytebuddy.description.type.TypeDescription; | ||
import net.bytebuddy.matcher.ElementMatcher; | ||
|
||
class JoddHttpInstrumentation implements TypeInstrumentation { | ||
|
||
@Override | ||
public ElementMatcher<TypeDescription> typeMatcher() { | ||
return named("jodd.http.HttpRequest"); | ||
} | ||
|
||
@Override | ||
public void transform(TypeTransformer transformer) { | ||
transformer.applyAdviceToMethod( | ||
isMethod().and(named("send")).and(takesArguments(0)), | ||
this.getClass().getName() + "$RequestAdvice"); | ||
} | ||
|
||
@SuppressWarnings("unused") | ||
public static class RequestAdvice { | ||
|
||
@Advice.OnMethodEnter(suppress = Throwable.class) | ||
public static void methodEnter( | ||
@Advice.This HttpRequest request, | ||
@Advice.Local("otelContext") Context context, | ||
@Advice.Local("otelScope") Scope scope) { | ||
Context parentContext = currentContext(); | ||
if (!instrumenter().shouldStart(parentContext, request)) { | ||
return; | ||
} | ||
context = instrumenter().start(parentContext, request); | ||
scope = context.makeCurrent(); | ||
} | ||
|
||
@Advice.OnMethodExit(onThrowable = Throwable.class, suppress = Throwable.class) | ||
public static void methodExit( | ||
@Advice.This HttpRequest request, | ||
@Advice.Return HttpResponse response, | ||
@Advice.Thrown Throwable throwable, | ||
@Advice.Local("otelContext") Context context, | ||
@Advice.Local("otelScope") Scope scope) { | ||
if (scope == null) { | ||
return; | ||
} | ||
scope.close(); | ||
instrumenter().end(context, request, response, throwable); | ||
} | ||
} | ||
} |
25 changes: 25 additions & 0 deletions
25
.../opentelemetry/javaagent/instrumentation/joddhttp/v4_2/JoddHttpInstrumentationModule.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
/* | ||
* Copyright The OpenTelemetry Authors | ||
* SPDX-License-Identifier: Apache-2.0 | ||
*/ | ||
|
||
package io.opentelemetry.javaagent.instrumentation.joddhttp.v4_2; | ||
|
||
import com.google.auto.service.AutoService; | ||
import io.opentelemetry.javaagent.extension.instrumentation.InstrumentationModule; | ||
import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation; | ||
import java.util.Collections; | ||
import java.util.List; | ||
|
||
@AutoService(InstrumentationModule.class) | ||
public class JoddHttpInstrumentationModule extends InstrumentationModule { | ||
|
||
public JoddHttpInstrumentationModule() { | ||
super("jodd-http", "jodd-http-4.2"); | ||
} | ||
|
||
@Override | ||
public List<TypeInstrumentation> typeInstrumentations() { | ||
return Collections.singletonList(new JoddHttpInstrumentation()); | ||
} | ||
} |
32 changes: 32 additions & 0 deletions
32
...io/opentelemetry/javaagent/instrumentation/joddhttp/v4_2/JoddHttpNetAttributesGetter.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
/* | ||
* Copyright The OpenTelemetry Authors | ||
* SPDX-License-Identifier: Apache-2.0 | ||
*/ | ||
|
||
package io.opentelemetry.javaagent.instrumentation.joddhttp.v4_2; | ||
|
||
import io.opentelemetry.instrumentation.api.instrumenter.net.NetClientAttributesGetter; | ||
import io.opentelemetry.semconv.trace.attributes.SemanticAttributes; | ||
import javax.annotation.Nullable; | ||
import jodd.http.HttpRequest; | ||
import jodd.http.HttpResponse; | ||
|
||
final class JoddHttpNetAttributesGetter | ||
implements NetClientAttributesGetter<HttpRequest, HttpResponse> { | ||
|
||
@Override | ||
public String getTransport(HttpRequest request, @Nullable HttpResponse response) { | ||
return SemanticAttributes.NetTransportValues.IP_TCP; | ||
} | ||
|
||
@Override | ||
@Nullable | ||
public String getPeerName(HttpRequest request) { | ||
return request.host(); | ||
} | ||
|
||
@Override | ||
public Integer getPeerPort(HttpRequest request) { | ||
return request.port(); | ||
} | ||
} |
51 changes: 51 additions & 0 deletions
51
...ain/java/io/opentelemetry/javaagent/instrumentation/joddhttp/v4_2/JoddHttpSingletons.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,51 @@ | ||
/* | ||
* Copyright The OpenTelemetry Authors | ||
* SPDX-License-Identifier: Apache-2.0 | ||
*/ | ||
|
||
package io.opentelemetry.javaagent.instrumentation.joddhttp.v4_2; | ||
|
||
import io.opentelemetry.api.GlobalOpenTelemetry; | ||
import io.opentelemetry.instrumentation.api.instrumenter.Instrumenter; | ||
import io.opentelemetry.instrumentation.api.instrumenter.http.HttpClientAttributesExtractor; | ||
import io.opentelemetry.instrumentation.api.instrumenter.http.HttpClientMetrics; | ||
import io.opentelemetry.instrumentation.api.instrumenter.http.HttpSpanNameExtractor; | ||
import io.opentelemetry.instrumentation.api.instrumenter.http.HttpSpanStatusExtractor; | ||
import io.opentelemetry.instrumentation.api.instrumenter.net.PeerServiceAttributesExtractor; | ||
import io.opentelemetry.javaagent.bootstrap.internal.CommonConfig; | ||
import jodd.http.HttpRequest; | ||
import jodd.http.HttpResponse; | ||
|
||
public final class JoddHttpSingletons { | ||
private static final String INSTRUMENTATION_NAME = "io.opentelemetry.jodd-http-4.2"; | ||
|
||
private static final Instrumenter<HttpRequest, HttpResponse> INSTRUMENTER; | ||
|
||
static { | ||
JoddHttpHttpAttributesGetter httpAttributesGetter = new JoddHttpHttpAttributesGetter(); | ||
JoddHttpNetAttributesGetter netAttributesGetter = new JoddHttpNetAttributesGetter(); | ||
|
||
INSTRUMENTER = | ||
Instrumenter.<HttpRequest, HttpResponse>builder( | ||
GlobalOpenTelemetry.get(), | ||
INSTRUMENTATION_NAME, | ||
HttpSpanNameExtractor.create(httpAttributesGetter)) | ||
.setSpanStatusExtractor(HttpSpanStatusExtractor.create(httpAttributesGetter)) | ||
.addAttributesExtractor( | ||
HttpClientAttributesExtractor.builder(httpAttributesGetter, netAttributesGetter) | ||
.setCapturedRequestHeaders(CommonConfig.get().getClientRequestHeaders()) | ||
.setCapturedResponseHeaders(CommonConfig.get().getClientResponseHeaders()) | ||
.build()) | ||
.addAttributesExtractor( | ||
PeerServiceAttributesExtractor.create( | ||
netAttributesGetter, CommonConfig.get().getPeerServiceMapping())) | ||
.addOperationMetrics(HttpClientMetrics.get()) | ||
.buildClientInstrumenter(HttpHeaderSetter.INSTANCE); | ||
} | ||
|
||
public static Instrumenter<HttpRequest, HttpResponse> instrumenter() { | ||
return INSTRUMENTER; | ||
} | ||
|
||
private JoddHttpSingletons() {} | ||
} |
Oops, something went wrong.