-
Notifications
You must be signed in to change notification settings - Fork 33
Http Client
- You are writing a Scala or Java application that uses the Apache Http Components
- You would like to seamlessly propagate the trace context to downstream systems
Many, many people use Apache Http Components in order to make http calls to remote servers. The money-http-client module extends the AspectJ module to make it stupid simple to support tracing for projects that use the Apache Http Components library.
BONUS! - by using the http client module, your application will seamlessly pass the current span to downstream systems! It does this by embedding the span id in a custom HTTP request header.
You can use the Http Client Module with or without AspectJ. If you use AspectJ, you can read up on how to use it in the AspectJ documentation. If you choose to not use AspectJ, you can still leverage the Http Client module and record key metrics, just not all of the metrics can be recorded (which is ok for many)
The Http Client module currently uses version 4.3.5 of the Apache Http Components and Client modules. If your version is different, your results...may...be...different
Add a dependency as follows for maven:
<dependency>
<groupId>com.comcast.money</groupId>
<artifactId>money-http-client_2.10</artifactId>
<version>${money.version}</version>
</dependency>
And here is a dependency for SBT:
libraryDependencies += "com.comcast.money" %% "money-http-client" % "${money.version}"
Weaving with AspectJ is optional. Reading the information on weaving in the AspectJ for more information
The http client module will automatically capture several metrics around the calls to http client. The following metrics are recorded:
This is how long it took for the http client to make the http request and get a response back. This is done by timing the httpClient.execute
invocations.
Note: This is not supported unless using AspectJ
This is how long it took for the http client to make the http request, get a response back, and fully retrieve the content of the request body. this metric has a lot more acrobatics to get its work done:
- Start a timer when the
httpClient.execute
method is invoked - Stop the timer when the
EntityUtils
method is invoked to retrieve the contents of the body from the request.
Note: This is not supported unless using AspectJ
This is the time between when the body was retrieved using EntityUtils
and the end of the trace.
This is the status code returned on the Http Response; for example 200, 204, 304, 400, or 500
Not everyone loves the names of our metrics, so we give you the ability to change the names that are emitted if you want something else.
The following shows the default settings. Feel free to change the names to something else if you want.
money {
http-client {
metric-names {
http-call-duration = "httpCallDuration"
http-call-with-body-duration = "httpCallWithBodyDuration"
http-process-response-duration = "processResponseDuration"
http-status-code = "statusCode"
}
}
}
To use the Http-Client module without AspectJ, you need to use the TraceFriendlyHttpClient
class and wrap whatever HttpClient instance you are using. TraceFriendlyHttpClient
extends HttpClient
, so you should be able to use everything normally.
import com.comcast.money.http.client.TraceFriendlyHttpClient;
import static com.comcast.money.japi.JMoney.*;
public class MyService {
private HttpClient httpClient;
public MyService(HttpClient httpClient) {
this.httpClient = new TraceFriendlyHttpClient(httpClient);
}
public String getSomething() {
try (TraceSpan span = newSpan("get-stuff")) {
HttpGet httpGet = new HttpGet(url);
HttpResponse response = httpClient.execute(httpGet);
return EntityUtils.toString(response.getEntity());
}
}
}
In the above example, we have a simple service class that wraps an HttpClient inside of TraceFriendlyHttpClient
. Now, it this setup, we will send the X-MoneyTrace
header on every request; we will also record the response code and http call duration.
We also start a new span. We strongly recommend when using money that all external service calls have their own span. If you do not make a call in its own span, there is no way to override the name of the Notes that are recorded.
Yea, just use the Aspectj money module's @Traced annotation or traced
functions in Scala money-core
or money-concurrent
; as long as you are using the http client like the examples below, you are good!
Well almost, you should follow these guidelines:
- Use
EntityUtils
to retrieve the response body. This is the class that is instrumented by the trace aspect - Use either the
httpClient.execute
method that returns an HttpResponse, or, the variation that takes aResponseHandler
, both variations will be picked up - It is possible that you are following a usage pattern that is not currently picked up by the aspect. If so, let us know, easy enough to add!
- Capture the entire call and processing of the response in a single trace.
- Capture each external service call in its own method, and trace each one separately
@Traced("ExternalMarketPlace.GetAccountInfo")
private AccountInfo load(String accountId) {
HttpGet httpGet = new HttpGet(url);
// Execute the request, this will be picked up by the http trace aspect
HttpResponse httpResponse = httpClient.execute(httpGet);
HttpEntity responseEntity = httpResponse.getEntity();
// Read the response body, this also will be picked up by the http trace aspect
// EntityUtils.toString is one of the methods picked up by the aspect
String body = EntityUtils.toString(responseEntity);
// Parsing the body should also be in here, as it is part of the service call
return json.parse(body);
}
You can also use the http execute method variation that takes a ResponseHandler
as an argument. The http-client money module will still capture metrics accordingly.
The following example is overly contrived and simple, but it illustrates the use case.
public class AccountInfoResponseHandler implements ResponseHandler<AccountInfo> {
@Override
public AccountInfo handleResponse(HttpResponse response) {
int statusCode = response.getStatusLine().getStatusCode();
if (statusCode == 200) {
// EntityUtils is picked up here
return json.parse(EntityUtils.toString(response));
} else {
throw new ServiceCallFailedException(statusCode);
}
}
}
... then somewhere else ...
@Traced("ExternalMarketPlace.GetAccountInfo")
private AccountInfo load(String accountId) {
HttpGet httpGet = new HttpGet(url);
AccountInfoResponseHandler handler = new AccountInfoResponseHandler();
// Execute the request, this will be picked up by the http trace aspect
// calls into the handler are also picked up
return httpClient.execute(httpGet, handler);
}
- Overview
- Configuration
- Logging Setup
- Performance Considerations
- Java Users Guide
- Scala Users Guide
- Modules
- Contributing