Skip to content

Commit

Permalink
Version 1.1.0
Browse files Browse the repository at this point in the history
  • Loading branch information
cirrus-up-cloud committed Oct 18, 2018
1 parent b16e08e commit 108fcd4
Show file tree
Hide file tree
Showing 8 changed files with 462 additions and 37 deletions.
8 changes: 5 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,15 +11,17 @@ i. Declare the dependency in your _pom.xml_ file.
<dependency>
<groupId>cloud.cirrusup</groupId>
<artifactId>aws-latency-request-log-handler</artifactId>
<version>1.0.0</version>
<version>1.1.0</version>
</dependency>
```

ii. Create a _request handler_ object.

```java
AwsLatencyRequestLogHandler handler = new AwsLatencyRequestLogHandler();
```
If you want to publish call details in JSON format, then declare the handler in the following manner:
```java
AwsLatencyRequestLogHandler handler = new AwsLatencyRequestLogHandler(new JSONPublisher());
```

iii. Enhance the _AWS client_ with the request handler created above.

Expand Down
17 changes: 15 additions & 2 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@

<groupId>cloud.cirrusup</groupId>
<artifactId>aws-latency-request-log-handler</artifactId>
<version>1.0.0-SNAPSHOT</version>
<version>1.1.0-SNAPSHOT</version>
<packaging>jar</packaging>

<distributionManagement>
Expand Down Expand Up @@ -124,13 +124,26 @@
</scm>

<dependencies>
<!-- AWS Core -->
<!-- AWS -->
<dependency>
<groupId>com.amazonaws</groupId>
<artifactId>aws-java-sdk-core</artifactId>
<version>1.11.397</version>
</dependency>

<dependency>
<groupId>com.amazonaws</groupId>
<artifactId>aws-java-sdk-s3</artifactId>
<version>1.11.397</version>
</dependency>

<!-- Jackson -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
<version>2.9.7</version>
</dependency>

<!--Log4j-->
<dependency>
<groupId>org.slf4j</groupId>
Expand Down
102 changes: 86 additions & 16 deletions src/main/java/cloud/cirrusup/AwsLatencyRequestLogHandler.java
Original file line number Diff line number Diff line change
@@ -1,30 +1,41 @@
package cloud.cirrusup;

import cloud.cirrusup.publisher.Publisher;
import cloud.cirrusup.publisher.PlainLogPublisher;
import cloud.cirrusup.publisher.model.PublishedInfo;
import com.amazonaws.AmazonServiceException;
import com.amazonaws.Request;
import com.amazonaws.Response;
import com.amazonaws.handlers.RequestHandler2;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.amazonaws.services.s3.model.AmazonS3Exception;

import java.util.List;
import java.util.concurrent.ConcurrentHashMap;

/**
* AWS Request handler that logs in a text file the request latency + status code.
*/
public class AwsLatencyRequestLogHandler extends RequestHandler2 {

private static final String LOG_NAME = "aws-latency-log";
private static final String REQUEST_ID = "requestId";
private static final Logger LOG = LoggerFactory.getLogger(LOG_NAME);


private final Publisher publisher;
private final ConcurrentHashMap<String, Long> REQUEST_MAP = new ConcurrentHashMap<>();

/**
* Constructor.
*/
public AwsLatencyRequestLogHandler() {

this(new PlainLogPublisher());
}

/**
* Constructor.
*/
public AwsLatencyRequestLogHandler(Publisher publisher) {

this.publisher = publisher;
}

/**
Expand All @@ -34,9 +45,6 @@ public AwsLatencyRequestLogHandler() {
public void beforeRequest(Request<?> request) {

final String id = RandomGenerator.getUUIDRandomString();

LOG.info("ID [{}] - Request done to the service [{}] with the method [{}]", id, request.getServiceName(), request.getHttpMethod());

request.getHeaders().put(REQUEST_ID, id);
REQUEST_MAP.put(id, System.currentTimeMillis());
}
Expand All @@ -48,14 +56,20 @@ public void beforeRequest(Request<?> request) {
public void afterResponse(Request<?> request, Response<?> response) {

final String id = request.getHeaders().get(REQUEST_ID);

PublishedInfo info = new PublishedInfo();
try {

LOG.info("Request ID [{}] took [{}] milliseconds and returned the RESPONSE_CODE [{}]",
id, getTime(id), getStatusCode(response, null));
info.addInfo(PublishedInfo.SERVICE_NAME, request.getServiceName());
info.addInfo(PublishedInfo.HTTP_METHOD, request.getHttpMethod().name());
info.addInfo(PublishedInfo.API_METHOD, getApiMethod(request));
info.addInfo(PublishedInfo.REQUEST_ID, getRequestId(request, response));
info.addLatency(getTime(id));
info.setStatusCode(getStatusCode(response, null));
} catch (Exception ex) {

LOG.error("ID [{}] for the request to [{}] was not found.", id, request.getServiceName());
} finally {

publisher.publish(info);
}
}

Expand All @@ -66,15 +80,71 @@ public void afterResponse(Request<?> request, Response<?> response) {
public void afterError(Request<?> request, Response<?> response, Exception e) {

final String id = request.getHeaders().get(REQUEST_ID);

PublishedInfo info = new PublishedInfo();
try {

LOG.error("Request ID [{}] took [{}] milliseconds and returned the RESPONSE_CODE [{}] and ERROR_MESSAGE [{}]",
id, getTime(id), getStatusCode(response, e), e.getMessage());
info.addInfo(PublishedInfo.SERVICE_NAME, request.getServiceName());
info.addInfo(PublishedInfo.HTTP_METHOD, request.getHttpMethod().name());
info.addInfo(PublishedInfo.REQUEST_ID, getRequestId(e));
info.addInfo(PublishedInfo.API_METHOD, getApiMethod(request));
info.addInfo(PublishedInfo.ERROR_SUMMARY, e.getMessage());
info.addLatency(getTime(id));
info.setStatusCode(getStatusCode(response, e));
} catch (Exception ex) {

LOG.error("ID [{}] for the request to [{}] was not found.", id, request.getServiceName());
} finally {

publisher.publish(info);
}
}

private String getApiMethod(Request<?> request) {

if (request != null && request.getHeaders() != null && request.getHeaders().containsKey("X-Amz-Target")) {

return request.getHeaders().get("X-Amz-Target");
}

if (request != null && request.getParameters() != null && request.getParameters().containsKey("Action")) {

List<String> action = request.getParameters().get("Action");
if (!action.isEmpty()) {

return action.get(0);
}
}

return null;
}

private String getRequestId(Exception e) {

if (e != null && e instanceof AmazonServiceException) {

AmazonServiceException awsException = (AmazonServiceException) e;
if (awsException.getServiceName().equals("Amazon S3") && (awsException instanceof AmazonS3Exception)) {

return awsException.getRequestId() + " " + ((AmazonS3Exception) e).getExtendedRequestId();
}
return awsException.getRequestId();
}

return null;
}


private String getRequestId(Request<?> request, Response<?> response) {

if (response != null && response.getHttpResponse() != null && response.getHttpResponse().getHeaders() != null) {

if (request.getServiceName().equals("Amazon S3")) {

return response.getHttpResponse().getHeaders().get("x-amz-request-id") + " " + response.getHttpResponse().getHeaders().get("x-amz-id-2");
}
return response.getHttpResponse().getHeaders().get("x-amzn-RequestId");
}

return null;
}

private int getStatusCode(Response<?> response, Exception e) {
Expand Down
50 changes: 50 additions & 0 deletions src/main/java/cloud/cirrusup/publisher/JSONPublisher.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
package cloud.cirrusup.publisher;

import cloud.cirrusup.publisher.Publisher;
import cloud.cirrusup.publisher.model.PublishedInfo;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import static cloud.cirrusup.publisher.model.PublishedInfo.API_METHOD;
import static cloud.cirrusup.publisher.model.PublishedInfo.ERROR_SUMMARY;
import static cloud.cirrusup.publisher.model.PublishedInfo.HTTP_METHOD;
import static cloud.cirrusup.publisher.model.PublishedInfo.REQUEST_ID;
import static cloud.cirrusup.publisher.model.PublishedInfo.SERVICE_NAME;

/**
* Publisher that exports entries in JSON format.
*/
public class JSONPublisher implements Publisher {

private static final ObjectMapper mapper = new ObjectMapper();
private static final Logger LOG = LoggerFactory.getLogger(LOG_NAME);

/**
* {@inheritDoc}
*/
@Override
public void publish(PublishedInfo info) {

ObjectNode node = mapper.createObjectNode();
node.put("requestId", info.getInfo(REQUEST_ID));
node.put("serviceName", info.getInfo(SERVICE_NAME));
node.put("httpMethod", info.getInfo(HTTP_METHOD));
node.put("latency", info.getLatency());
node.put("statusCode", info.getStatusCode());
if (info.hasInfo(ERROR_SUMMARY)) {
node.put("errorMessage", info.getInfo(ERROR_SUMMARY));
}
if (info.hasInfo(API_METHOD)) {
node.put("apiMethod", info.getInfo(API_METHOD));
}

try {

LOG.info(mapper.writeValueAsString(node));
} catch (JsonProcessingException e) {
}
}
}
38 changes: 38 additions & 0 deletions src/main/java/cloud/cirrusup/publisher/PlainLogPublisher.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package cloud.cirrusup.publisher;

import cloud.cirrusup.publisher.Publisher;
import cloud.cirrusup.publisher.model.PublishedInfo;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import static cloud.cirrusup.publisher.model.PublishedInfo.API_METHOD;
import static cloud.cirrusup.publisher.model.PublishedInfo.ERROR_SUMMARY;
import static cloud.cirrusup.publisher.model.PublishedInfo.HTTP_METHOD;
import static cloud.cirrusup.publisher.model.PublishedInfo.REQUEST_ID;
import static cloud.cirrusup.publisher.model.PublishedInfo.SERVICE_NAME;

/**
* Publisher that uses a text log file to add plain info.
*/
public class PlainLogPublisher implements Publisher {

private static final Logger LOG = LoggerFactory.getLogger(LOG_NAME);

/**
* {@inheritDoc}
*/
@Override
public void publish(PublishedInfo info) {

LOG.info("Request ID [{}] to [{}/{}] took [{}] milliseconds and returned the RESPONSE_CODE [{}]",
info.getInfo(REQUEST_ID), info.getInfo(SERVICE_NAME), info.getInfo(HTTP_METHOD), info.getLatency(), info.getStatusCode());

if (info.hasInfo(ERROR_SUMMARY)) {
LOG.warn("Request ID [{}], RESPONSE_CODE [{}], MESSAGE [{}]", info.getInfo(REQUEST_ID), info.getStatusCode(), info.getInfo(ERROR_SUMMARY));
}

if(info.hasInfo(API_METHOD)){
LOG.info("Request ID [{}], API method [{}]", info.getInfo(REQUEST_ID), info.getInfo(API_METHOD));
}
}
}
18 changes: 18 additions & 0 deletions src/main/java/cloud/cirrusup/publisher/Publisher.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package cloud.cirrusup.publisher;

import cloud.cirrusup.publisher.model.PublishedInfo;

/**
* Model for any publisher.
*/
public interface Publisher {

String LOG_NAME = "aws-latency-log";

/**
* Publish info in the underlying destination.
*
* @param info information to be published
*/
void publish(PublishedInfo info);
}
57 changes: 57 additions & 0 deletions src/main/java/cloud/cirrusup/publisher/model/PublishedInfo.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
package cloud.cirrusup.publisher.model;

import java.util.HashMap;
import java.util.Map;

/**
* POJO class that holds all details about info published.
*/
public class PublishedInfo {

public static final String REQUEST_ID = "requestId";
public static final String SERVICE_NAME = "serviceName";
public static final String HTTP_METHOD = "httpMethod";
public static final String ERROR_SUMMARY = "errorInfo";
public static final String API_METHOD = "apiMethod";


private final Map<String, String> info = new HashMap<>();
private long latency = -1;
private int statusCode = -1;


public void addLatency(long latency) {

this.latency = latency;
}

public void addInfo(String name, String value) {

this.info.put(name, value);
}

public long getLatency() {

return latency;
}

public String getInfo(String name) {

return info.get(name);
}

public boolean hasInfo(String name) {

return info.containsKey(name) && info.get(name) != null;
}

public void setStatusCode(int statusCode) {

this.statusCode = statusCode;
}

public int getStatusCode() {

return statusCode;
}
}
Loading

0 comments on commit 108fcd4

Please sign in to comment.