Skip to content

Commit

Permalink
feat: log HTTP calls (#93)
Browse files Browse the repository at this point in the history
  • Loading branch information
y-lakhdar authored Jun 29, 2023
1 parent 8840df4 commit 0cd3c60
Show file tree
Hide file tree
Showing 9 changed files with 195 additions and 9 deletions.
25 changes: 25 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,31 @@ public class PushOneDocument {

```

## Logging
When pushing multiple documents into your source using a service (e.g. `PushService`, `StreamService`), make sure to configure a **logger** to be able to see what happens.
to do so .. in your `resources` folder.

### Log4j2 XML Configuration Example
To log execution output into the console, use the below `log4j2.xml` configuration:
```xml
<!-- log4j2.xml -->
<?xml version="1.0" encoding="UTF-8"?>
<Configuration>
<Appenders>
<Console name="ConsoleAppender" target="SYSTEM_OUT">
<PatternLayout pattern="%d{yyyy-MM-dd HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n" />
</Console>
</Appenders>
<Loggers>
<Root level="debug">
<AppenderRef ref="ConsoleAppender" />
</Root>
</Loggers>
</Configuration>
```

See [Log4j2 configuration](https://logging.apache.org/log4j/2.x/manual/configuration.html) for more details.

## Local Setup to Contribute

### Formatting
Expand Down
5 changes: 5 additions & 0 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,11 @@
</profiles>

<dependencies>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId>
<version>2.20.0</version>
</dependency>
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
Expand Down
49 changes: 44 additions & 5 deletions src/main/java/com/coveo/pushapiclient/ApiCore.java
Original file line number Diff line number Diff line change
Expand Up @@ -6,17 +6,22 @@
import java.net.http.HttpRequest;
import java.net.http.HttpRequest.BodyPublisher;
import java.net.http.HttpResponse;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

// TODO: LENS-934 - Support throttling
class ApiCore {
private final HttpClient httpClient;
private final Logger logger;

public ApiCore() {
this.httpClient = HttpClient.newHttpClient();
this.logger = LogManager.getLogger(ApiCore.class);
}

public ApiCore(HttpClient httpClient) {
public ApiCore(HttpClient httpClient, Logger logger) {
this.httpClient = httpClient;
this.logger = logger;
}

public HttpResponse<String> post(URI uri, String[] headers)
Expand All @@ -26,26 +31,60 @@ public HttpResponse<String> post(URI uri, String[] headers)

public HttpResponse<String> post(URI uri, String[] headers, BodyPublisher body)
throws IOException, InterruptedException {
this.logger.debug("POST " + uri);
HttpRequest request = HttpRequest.newBuilder().headers(headers).uri(uri).POST(body).build();
return this.httpClient.send(request, HttpResponse.BodyHandlers.ofString());
HttpResponse<String> response =
this.httpClient.send(request, HttpResponse.BodyHandlers.ofString());
this.logResponse(response);
return response;
}

public HttpResponse<String> put(URI uri, String[] headers, BodyPublisher body)
throws IOException, InterruptedException {
this.logger.debug("PUT " + uri);
HttpRequest request = HttpRequest.newBuilder().headers(headers).uri(uri).PUT(body).build();
return this.httpClient.send(request, HttpResponse.BodyHandlers.ofString());
HttpResponse<String> response =
this.httpClient.send(request, HttpResponse.BodyHandlers.ofString());
this.logResponse(response);
return response;
}

public HttpResponse<String> delete(URI uri, String[] headers)
throws IOException, InterruptedException {
this.logger.debug("DELETE " + uri);
HttpRequest request = HttpRequest.newBuilder().headers(headers).uri(uri).DELETE().build();
return this.httpClient.send(request, HttpResponse.BodyHandlers.ofString());
HttpResponse<String> response =
this.httpClient.send(request, HttpResponse.BodyHandlers.ofString());
this.logResponse(response);
return response;
}

public HttpResponse<String> delete(URI uri, String[] headers, BodyPublisher body)
throws IOException, InterruptedException {
this.logger.debug("DELETE " + uri);
HttpRequest request =
HttpRequest.newBuilder().headers(headers).uri(uri).method("DELETE", body).build();
return this.httpClient.send(request, HttpResponse.BodyHandlers.ofString());
HttpResponse<String> response =
this.httpClient.send(request, HttpResponse.BodyHandlers.ofString());
this.logResponse(response);
return response;
}

private void logResponse(HttpResponse<String> response) {
if (response == null) {
return;
}
int status = response.statusCode();
String method = response.request().method();
String statusMessage = method + " status: " + status;
String responseMessage = method + " response: " + response.body();

if (status < 200 || status >= 300) {
this.logger.error(statusMessage);
this.logger.error(responseMessage);
} else {
this.logger.debug(statusMessage);
this.logger.debug(responseMessage);
}
}
}
10 changes: 9 additions & 1 deletion src/main/java/com/coveo/pushapiclient/DocumentUploadQueue.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,12 @@

import java.io.IOException;
import java.util.ArrayList;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

/** Represents a queue for uploading documents using a specified upload strategy */
class DocumentUploadQueue {
private static final Logger logger = LogManager.getLogger(DocumentUploadQueue.class);
private final UploadStrategy uploader;
private final int maxQueueSize = 5 * 1024 * 1024;
private ArrayList<DocumentBuilder> documentToAddList;
Expand All @@ -30,11 +33,14 @@ public DocumentUploadQueue(UploadStrategy uploader) {
*/
public void flush() throws IOException, InterruptedException {
if (this.isEmpty()) {
logger.debug("Empty batch. Skipping upload");
return;
}
BatchUpdate batch = this.getBatch();
// TODO: LENS-871: support concurrent requests
BatchUpdate batch = this.getBatch();
logger.info("Uploading document batch");
this.uploader.apply(batch);

this.size = 0;
this.documentToAddList.clear();
this.documentToDeleteList.clear();
Expand All @@ -58,6 +64,7 @@ public void add(DocumentBuilder document) throws IOException, InterruptedExcepti
this.flush();
}
documentToAddList.add(document);
logger.info("Adding document to batch: " + document.getDocument().uri);
this.size += sizeOfDoc;
}

Expand All @@ -79,6 +86,7 @@ public void add(DeleteDocument document) throws IOException, InterruptedExceptio
this.flush();
}
documentToDeleteList.add(document);
logger.info("Adding document to batch: " + document.documentId);
this.size += sizeOfDoc;
}

Expand Down
3 changes: 2 additions & 1 deletion src/main/java/com/coveo/pushapiclient/PlatformClient.java
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
import java.util.Arrays;
import java.util.HashMap;
import java.util.stream.Stream;
import org.apache.logging.log4j.LogManager;

/** PlatformClient handles network requests to the Coveo platform */
public class PlatformClient {
Expand Down Expand Up @@ -58,7 +59,7 @@ public PlatformClient(String apiKey, String organizationId, PlatformUrl platform
public PlatformClient(String apiKey, String organizationId, HttpClient httpClient) {
this.apiKey = apiKey;
this.organizationId = organizationId;
this.api = new ApiCore(httpClient);
this.api = new ApiCore(httpClient, LogManager.getLogger(ApiCore.class));
this.platformUrl = new PlatformUrlBuilder().build();
}

Expand Down
6 changes: 5 additions & 1 deletion src/main/java/com/coveo/pushapiclient/StreamService.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
import com.google.gson.Gson;
import java.io.IOException;
import java.net.http.HttpResponse;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

public class StreamService {
private final StreamEnabledSource source;
Expand All @@ -27,11 +29,13 @@ public StreamService(StreamEnabledSource source) {
String organizationId = source.getOrganizationId();
PlatformUrl platformUrl = source.getPlatformUrl();
UploadStrategy uploader = this.getUploadStrategy();
Logger logger = LogManager.getLogger(StreamService.class);

this.source = source;
this.queue = new DocumentUploadQueue(uploader);
this.platformClient = new PlatformClient(apiKey, organizationId, platformUrl);
this.service = new StreamServiceInternal(this.source, this.queue, this.platformClient);

this.service = new StreamServiceInternal(this.source, this.queue, this.platformClient, logger);
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,19 +4,25 @@
import com.google.gson.Gson;
import java.io.IOException;
import java.net.http.HttpResponse;
import org.apache.logging.log4j.Logger;

/** For internal use only. Made to easily test the service without having to use PowerMock */
class StreamServiceInternal {
private Logger logger;
private final StreamEnabledSource source;
private final PlatformClient platformClient;
private String streamId;
private DocumentUploadQueue queue;

public StreamServiceInternal(
StreamEnabledSource source, DocumentUploadQueue queue, PlatformClient platformClient) {
StreamEnabledSource source,
DocumentUploadQueue queue,
PlatformClient platformClient,
Logger logger) {
this.source = source;
this.queue = queue;
this.platformClient = platformClient;
this.logger = logger;
}

public String add(DocumentBuilder document) throws IOException, InterruptedException {
Expand All @@ -35,10 +41,12 @@ public HttpResponse<String> close()
}
queue.flush();
String sourceId = this.getSourceId();
this.logger.info("Closing open stream " + this.streamId);
return this.platformClient.closeStream(sourceId, this.streamId);
}

private String getStreamId() throws IOException, InterruptedException {
this.logger.info("Opening new stream");
String sourceId = this.getSourceId();
HttpResponse<String> response = this.platformClient.openStream(sourceId);
StreamResponse streamResponse = new Gson().fromJson(response.body(), StreamResponse.class);
Expand Down
82 changes: 82 additions & 0 deletions src/test/java/com/coveo/pushapiclient/ApiCoreTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
package com.coveo.pushapiclient;

import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.http.HttpClient;
import java.net.http.HttpRequest;
import java.net.http.HttpResponse;
import java.net.http.HttpResponse.BodyHandler;
import org.apache.logging.log4j.Logger;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;

public class ApiCoreTest {

@Mock private HttpClient httpClient;
@Mock private HttpRequest httpRequest;
@Mock private Logger logger;
@Mock private HttpResponse<String> httpResponse;

@InjectMocks private ApiCore api;

private AutoCloseable closeable;
private static final String[] headers = {
"Content-Type", "application/json", "Accept", "application/json"
};

private void mockSuccessResponse() {
when(httpResponse.statusCode()).thenReturn(200);
when(httpResponse.body()).thenReturn("All good!");
when(httpRequest.method()).thenReturn("POST");
}

private void mockErrorResponse() {
when(httpResponse.statusCode()).thenReturn(412);
when(httpResponse.body()).thenReturn("BAD_REQUEST");
when(httpRequest.method()).thenReturn("DELETE");
}

@Before
public void setUp() throws Exception {
closeable = MockitoAnnotations.openMocks(this);

when(httpClient.send(any(HttpRequest.class), any(BodyHandler.class))).thenReturn(httpResponse);
when(httpResponse.request()).thenReturn(httpRequest);
}

@After
public void closeService() throws Exception {
closeable.close();
}

@Test
public void testShouldLogRequestAndResonse()
throws IOException, InterruptedException, URISyntaxException {
this.mockSuccessResponse();
this.api.post(new URI("https://perdu.com/"), headers);

verify(logger, times(1)).debug("POST https://perdu.com/");
verify(logger, times(1)).debug("POST status: 200");
verify(logger, times(1)).debug("POST response: All good!");
}

@Test
public void testShouldLogResponse() throws IOException, InterruptedException, URISyntaxException {
this.mockErrorResponse();
this.api.delete(new URI("https://perdu.com/"), headers);

verify(logger, times(1)).debug("DELETE https://perdu.com/");
verify(logger, times(1)).error("DELETE status: 412");
verify(logger, times(1)).error("DELETE response: BAD_REQUEST");
}
}
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
package com.coveo.pushapiclient;

import static org.mockito.ArgumentMatchers.contains;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

import com.coveo.pushapiclient.exceptions.NoOpenStreamException;
import java.io.IOException;
import java.net.http.HttpResponse;
import org.apache.logging.log4j.core.Logger;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
Expand All @@ -21,6 +23,8 @@ public class StreamServiceInternalTest {

@Mock private PlatformClient platformClient;

@Mock private Logger logger;

@InjectMocks private StreamServiceInternal service;

@Mock private HttpResponse<String> httpResponse;
Expand Down Expand Up @@ -86,4 +90,14 @@ public void givenNoOpenStream_whenClose_thenShouldThrow()
throws IOException, InterruptedException, NoOpenStreamException {
service.close();
}

@Test
public void testShouldLogInfo() throws IOException, InterruptedException, NoOpenStreamException {
service.add(documentA);
service.add(documentB);
verify(logger, times(1)).info("Opening new stream");

service.close();
verify(logger, times(1)).info(contains("Closing open stream"));
}
}

0 comments on commit 0cd3c60

Please sign in to comment.