Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Request Content-Length fix for Apache HttpClients #6749

Open
wants to merge 63 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
63 commits
Select commit Hold shift + click to select a range
d8d29e4
Uses last request sent from context for apache http client 5.0
anuragagarwal561994 Sep 25, 2022
4bdc6c8
Uses http context in http asynclcient 4.1 for request content length
anuragagarwal561994 Sep 25, 2022
230755a
Apply spotless checks
anuragagarwal561994 Sep 25, 2022
47a1584
Rough implementation of httpclient 4.0 and > 4.3
anuragagarwal561994 Sep 25, 2022
e6659cb
Revert "Rough implementation of httpclient 4.0 and > 4.3"
anuragagarwal561994 Dec 26, 2022
41508a7
Revert "Apply spotless checks"
anuragagarwal561994 Dec 26, 2022
e008137
Revert "Uses http context in http asynclcient 4.1 for request content…
anuragagarwal561994 Dec 26, 2022
b93fa85
Revert "Uses last request sent from context for apache http client 5.0"
anuragagarwal561994 Dec 26, 2022
5e91f20
Wrapping http request & response
anuragagarwal561994 Dec 21, 2022
dfb561f
Adds request and response length attributes for Async client
anuragagarwal561994 Dec 21, 2022
74b6c3d
Adds request content length metrics to sync httpclient
anuragagarwal561994 Dec 21, 2022
2337b2f
Apply spotless checks
anuragagarwal561994 Dec 21, 2022
a8464f6
Handles stream closing
anuragagarwal561994 Dec 25, 2022
043f653
Removes unnecessary wrapping of http response
anuragagarwal561994 Dec 25, 2022
0e6f250
Keeps only doExecute advice for non async http client
anuragagarwal561994 Dec 25, 2022
146ebe3
Replaces with actual request from contextg
anuragagarwal561994 Dec 25, 2022
6f45920
Adds content-length from entity details as default
anuragagarwal561994 Dec 25, 2022
0aa8307
Removes redundant comments
anuragagarwal561994 Dec 26, 2022
eeb3c6b
Fixes name for consistency
anuragagarwal561994 Dec 26, 2022
7a2be5a
Reverts using internal request for http client 5.x
anuragagarwal561994 Dec 26, 2022
821f2d1
Adds request bytes isntrumentation
anuragagarwal561994 Dec 22, 2022
ba6a198
Content length metrics for apache http client 4.x
anuragagarwal561994 Dec 22, 2022
7caeca0
Spotless Apply
anuragagarwal561994 Dec 22, 2022
6effc07
Don't record metrics on complete
anuragagarwal561994 Dec 25, 2022
be27c08
Checks with bytes remaining
anuragagarwal561994 Dec 25, 2022
8081652
Prefer content-length over metrics calculated
anuragagarwal561994 Dec 26, 2022
0c2b014
Uses content-length and byte transfer metrics for httpclient
anuragagarwal561994 Dec 26, 2022
3eb7f0e
Corrects names and function fetching
anuragagarwal561994 Dec 26, 2022
7f10309
Fixes and adding instrumentation for closeable http client
anuragagarwal561994 Dec 26, 2022
4605912
Adds content lengths in test attributes
anuragagarwal561994 Dec 26, 2022
aa50885
Moves test cases to java
anuragagarwal561994 Dec 26, 2022
f58870f
Applies spotless
anuragagarwal561994 Dec 26, 2022
55a7067
Reverts back to use original implementation of instrumentation
anuragagarwal561994 Dec 26, 2022
cef8524
Fixes request content length in other modules
anuragagarwal561994 Dec 26, 2022
ac43b70
Runs spotless apply
anuragagarwal561994 Dec 26, 2022
df61e8e
Fixes import
anuragagarwal561994 Dec 26, 2022
1898b27
Removes fetching internal request from context
anuragagarwal561994 Dec 26, 2022
53ad1c4
Adds comment for test cases
anuragagarwal561994 Dec 26, 2022
2ae6915
Reverts content length checks from es
anuragagarwal561994 Dec 26, 2022
ea0dbf0
Corrects test cases for opensearch
anuragagarwal561994 Dec 26, 2022
77895c3
Uses internal request / repsonse from http context for async clients
anuragagarwal561994 Jan 2, 2023
ea34668
Ends instrumentation consistently with http response if present for s…
anuragagarwal561994 Jan 2, 2023
8d92bd2
Moves to httpclient common reusable classes
anuragagarwal561994 Jan 2, 2023
4d8c287
Corrects end instrumentation
anuragagarwal561994 Jan 2, 2023
06ebee2
Fixes liscence string
anuragagarwal561994 Jan 2, 2023
6d6ec81
Moves commons classes for http client 4.0 in commons
anuragagarwal561994 Jan 2, 2023
795ec58
Consistent classifiers for classes
anuragagarwal561994 Jan 2, 2023
ea1e666
Corrects http header setter
anuragagarwal561994 Jan 2, 2023
9560967
Moves all logic to extract http attributes at one place
anuragagarwal561994 Jan 3, 2023
70ea4e9
Removes dependency of context from CountingOutstream
anuragagarwal561994 Jan 3, 2023
3a95f14
Moves attributes assignee logic to byte transfer metrics
anuragagarwal561994 Jan 3, 2023
ac03cbd
Uses internal http requests and responses
anuragagarwal561994 Jan 3, 2023
71d20e3
Fixes uri parsing
anuragagarwal561994 Jan 3, 2023
6ee6faa
Corrects condition for apache async client 4.1
anuragagarwal561994 Jan 4, 2023
022796d
Corrects other instrumentation tests
anuragagarwal561994 Jan 4, 2023
dcafa57
Handles not running http processor instrumentation for server kind an…
anuragagarwal561994 Jan 4, 2023
b91a928
Puts common logic together as generics
anuragagarwal561994 Jan 28, 2023
7760022
Runs spotless
anuragagarwal561994 Jan 5, 2023
a106ca4
Reverts back to remove inetaddresss
anuragagarwal561994 Jan 28, 2023
e6aec3e
Uses httpcore:4.0 as the base dependency for commons:4.0
anuragagarwal561994 Jan 28, 2023
63e2084
Spotless apply
anuragagarwal561994 Jan 28, 2023
46728d9
Fixes @Override
anuragagarwal561994 Jan 28, 2023
061a3d6
Merge branch 'main' into request-content-length-apache-httpclient
anuragagarwal561994 Feb 16, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -14,5 +14,7 @@ muzzle {
}

dependencies {
implementation(project(":instrumentation:apache-httpclient:commons:javaagent"))
implementation(project(":instrumentation:apache-httpclient:commons-4.0:javaagent"))
library("org.apache.httpcomponents:httpasyncclient:4.1")
}

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -8,34 +8,25 @@
import static io.opentelemetry.javaagent.bootstrap.Java8BytecodeBridge.currentContext;
import static io.opentelemetry.javaagent.extension.matcher.AgentElementMatchers.hasClassesNamed;
import static io.opentelemetry.javaagent.extension.matcher.AgentElementMatchers.implementsInterface;
import static io.opentelemetry.javaagent.instrumentation.apachehttpasyncclient.ApacheHttpAsyncClientSingletons.instrumenter;
import static net.bytebuddy.matcher.ElementMatchers.isMethod;
import static net.bytebuddy.matcher.ElementMatchers.named;
import static net.bytebuddy.matcher.ElementMatchers.takesArgument;
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 java.io.IOException;
import java.util.logging.Logger;
import javax.annotation.Nullable;
import io.opentelemetry.javaagent.instrumentation.apachehttpclient.v4_0.commons.ApacheHttpClientOtelContext;
import net.bytebuddy.asm.Advice;
import net.bytebuddy.description.type.TypeDescription;
import net.bytebuddy.matcher.ElementMatcher;
import org.apache.http.HttpException;
import org.apache.http.HttpHost;
import org.apache.http.HttpRequest;
import org.apache.http.HttpResponse;
import org.apache.http.concurrent.FutureCallback;
import org.apache.http.nio.ContentEncoder;
import org.apache.http.nio.IOControl;
import org.apache.http.nio.protocol.HttpAsyncRequestProducer;
import org.apache.http.nio.protocol.HttpAsyncResponseConsumer;
import org.apache.http.protocol.BasicHttpContext;
import org.apache.http.protocol.HttpContext;
import org.apache.http.protocol.HttpCoreContext;

public class ApacheHttpAsyncClientInstrumentation implements TypeInstrumentation {
public final class ApacheHttpAsyncClientInstrumentation implements TypeInstrumentation {

@Override
public ElementMatcher<ClassLoader> classLoaderOptimization() {
Expand Down Expand Up @@ -66,193 +57,24 @@ public static class ClientAdvice {
@Advice.OnMethodEnter(suppress = Throwable.class)
public static void methodEnter(
@Advice.Argument(value = 0, readOnly = false) HttpAsyncRequestProducer requestProducer,
@Advice.Argument(2) HttpContext httpContext,
@Advice.Argument(value = 1, readOnly = false) HttpAsyncResponseConsumer<?> responseConsumer,
@Advice.Argument(value = 2, readOnly = false) HttpContext httpContext,
@Advice.Argument(value = 3, readOnly = false) FutureCallback<?> futureCallback) {

Context parentContext = currentContext();
if (httpContext == null) {
httpContext = new BasicHttpContext();
}
ApacheHttpClientOtelContext httpOtelContext = ApacheHttpClientOtelContext.adapt(httpContext);
httpOtelContext.markAsyncClient();

WrappedFutureCallback<?> wrappedFutureCallback =
new WrappedFutureCallback<>(parentContext, httpContext, futureCallback);
requestProducer =
new DelegatingRequestProducer(parentContext, requestProducer, wrappedFutureCallback);
new WrappedRequestProducer(
parentContext, httpContext, requestProducer, wrappedFutureCallback);
responseConsumer = new WrappedResponseConsumer<>(parentContext, responseConsumer);
futureCallback = wrappedFutureCallback;
}
}

public static class DelegatingRequestProducer implements HttpAsyncRequestProducer {
private final Context parentContext;
private final HttpAsyncRequestProducer delegate;
private final WrappedFutureCallback<?> wrappedFutureCallback;

public DelegatingRequestProducer(
Context parentContext,
HttpAsyncRequestProducer delegate,
WrappedFutureCallback<?> wrappedFutureCallback) {
this.parentContext = parentContext;
this.delegate = delegate;
this.wrappedFutureCallback = wrappedFutureCallback;
}

@Override
public HttpHost getTarget() {
return delegate.getTarget();
}

@Override
public HttpRequest generateRequest() throws IOException, HttpException {
HttpHost target = delegate.getTarget();
HttpRequest request = delegate.generateRequest();

ApacheHttpClientRequest otelRequest = new ApacheHttpClientRequest(target, request);

if (instrumenter().shouldStart(parentContext, otelRequest)) {
wrappedFutureCallback.context = instrumenter().start(parentContext, otelRequest);
wrappedFutureCallback.otelRequest = otelRequest;
}

return request;
}

@Override
public void produceContent(ContentEncoder encoder, IOControl ioctrl) throws IOException {
delegate.produceContent(encoder, ioctrl);
}

@Override
public void requestCompleted(HttpContext context) {
delegate.requestCompleted(context);
}

@Override
public void failed(Exception ex) {
delegate.failed(ex);
}

@Override
public boolean isRepeatable() {
return delegate.isRepeatable();
}

@Override
public void resetRequest() throws IOException {
delegate.resetRequest();
}

@Override
public void close() throws IOException {
delegate.close();
}
}

public static class WrappedFutureCallback<T> implements FutureCallback<T> {

private static final Logger logger = Logger.getLogger(WrappedFutureCallback.class.getName());

private final Context parentContext;
@Nullable private final HttpContext httpContext;
private final FutureCallback<T> delegate;

private volatile Context context;
private volatile ApacheHttpClientRequest otelRequest;

public WrappedFutureCallback(
Context parentContext, HttpContext httpContext, FutureCallback<T> delegate) {
this.parentContext = parentContext;
this.httpContext = httpContext;
// Note: this can be null in real life, so we have to handle this carefully
this.delegate = delegate;
}

@Override
public void completed(T result) {
if (context == null) {
// this is unexpected
logger.fine("context was never set");
completeDelegate(result);
return;
}

instrumenter().end(context, otelRequest, getResponseFromHttpContext(), null);

if (parentContext == null) {
completeDelegate(result);
return;
}

try (Scope ignored = parentContext.makeCurrent()) {
completeDelegate(result);
}
}

@Override
public void failed(Exception ex) {
if (context == null) {
// this is unexpected
logger.fine("context was never set");
failDelegate(ex);
return;
}

// end span before calling delegate
instrumenter().end(context, otelRequest, getResponseFromHttpContext(), ex);

if (parentContext == null) {
failDelegate(ex);
return;
}

try (Scope ignored = parentContext.makeCurrent()) {
failDelegate(ex);
}
}

@Override
public void cancelled() {
if (context == null) {
// this is unexpected
logger.fine("context was never set");
cancelDelegate();
return;
}

// TODO (trask) add "canceled" span attribute
// end span before calling delegate
instrumenter().end(context, otelRequest, getResponseFromHttpContext(), null);

if (parentContext == null) {
cancelDelegate();
return;
}

try (Scope ignored = parentContext.makeCurrent()) {
cancelDelegate();
}
}

private void completeDelegate(T result) {
if (delegate != null) {
delegate.completed(result);
}
}

private void failDelegate(Exception ex) {
if (delegate != null) {
delegate.failed(ex);
}
}

private void cancelDelegate() {
if (delegate != null) {
delegate.cancelled();
}
}

@Nullable
private HttpResponse getResponseFromHttpContext() {
if (httpContext == null) {
return null;
}
return (HttpResponse) httpContext.getAttribute(HttpCoreContext.HTTP_RESPONSE);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,21 +5,24 @@

package io.opentelemetry.javaagent.instrumentation.apachehttpasyncclient;

import static java.util.Collections.singletonList;

import com.google.auto.service.AutoService;
import io.opentelemetry.javaagent.extension.instrumentation.InstrumentationModule;
import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation;
import io.opentelemetry.javaagent.instrumentation.apachehttpclient.v4_0.commons.ApacheHttpClientProcessorInstrumentation;
import java.util.ArrayList;
import java.util.List;

@AutoService(InstrumentationModule.class)
public class ApacheHttpAsyncClientInstrumentationModule extends InstrumentationModule {
public final class ApacheHttpAsyncClientInstrumentationModule extends InstrumentationModule {
public ApacheHttpAsyncClientInstrumentationModule() {
super("apache-httpasyncclient", "apache-httpasyncclient-4.1");
}

@Override
public List<TypeInstrumentation> typeInstrumentations() {
return singletonList(new ApacheHttpAsyncClientInstrumentation());
List<TypeInstrumentation> instrumentationList = new ArrayList<>();
instrumentationList.add(new ApacheHttpAsyncClientInstrumentation());
instrumentationList.add(new ApacheHttpClientProcessorInstrumentation());
return instrumentationList;
}
}

This file was deleted.

Loading