Repository: zalando/logbook (java)

Issue:
Add classifier to JSON formatted messages

    {
        "type": "request",
        "correlationId": "..."
    }

Possible values: `request` and `response`
Should be the very first property in the object to make it easier to read in scalyr

Pull request description:
Added classifier to JSON output
This fixes #31

Patch:
diff --git a/README.md b/README.md
index a6debbd4..eba58459 100644
--- a/README.md
+++ b/README.md
@@ -87,7 +87,7 @@ where requests and responses are logged to, that's the work of writers.
 
 Logbook comes with two different formatters by default - *HTTP* and *JSON*:
 
-### HTTP Style
+### HTTP
 
 *HTTP* is the default formatting style, is provided by the `DefaultHttpLogFormatter` and is primarily designed to be
 used for local development and debugging. Since it's harder to read by machines this is usually not meant to be used
@@ -96,6 +96,7 @@ in production.
 #### Request
 
 ```http
+Request: 2d66e4bc-9a0d-11e5-a84c-1f39510f0d6b
 GET /test HTTP/1.1
 Accept: application/json
 Host: localhost
@@ -107,6 +108,7 @@ Hello world!
 #### Response
 
 ```http
+Response: 2d66e4bc-9a0d-11e5-a84c-1f39510f0d6b
 HTTP/1.1 200
 Content-Type: application/json
 
@@ -122,6 +124,8 @@ used for production since it's easily consumed by parsers and log consumers.
 
 ```json
 {
+  "type": "request",
+  "correlation": "2d66e4bc-9a0d-11e5-a84c-1f39510f0d6b",
   "sender": "127.0.0.1",
   "method": "GET",
   "path": "/test",
@@ -140,6 +144,8 @@ used for production since it's easily consumed by parsers and log consumers.
 
 ```json
 {
+  "type": "response",
+  "correlation": "2d66e4bc-9a0d-11e5-a84c-1f39510f0d6b",
   "status": 200,
   "headers": {
     "Content-Type": ["text/plain"]
diff --git a/logbook-core/src/main/java/org/zalando/logbook/DefaultHttpLogFormatter.java b/logbook-core/src/main/java/org/zalando/logbook/DefaultHttpLogFormatter.java
index bf196138..e2fe6aa6 100644
--- a/logbook-core/src/main/java/org/zalando/logbook/DefaultHttpLogFormatter.java
+++ b/logbook-core/src/main/java/org/zalando/logbook/DefaultHttpLogFormatter.java
@@ -36,7 +36,7 @@ public final class DefaultHttpLogFormatter implements HttpLogFormatter {
 
     @Override
     public String format(final Precorrelation<HttpRequest> precorrelation) throws IOException {
-        return format(precorrelation.getRequest(), this::formatRequestLine);
+        return format(precorrelation.getRequest(), "Request", precorrelation.getId(), this::formatRequestLine);
     }
 
     private String formatRequestLine(final HttpRequest request) {
@@ -45,18 +45,20 @@ public final class DefaultHttpLogFormatter implements HttpLogFormatter {
 
     @Override
     public String format(final Correlation<HttpRequest, HttpResponse> correlation) throws IOException {
-        return format(correlation.getResponse(), this::formatStatusLine);
+        return format(correlation.getResponse(), "Response", correlation.getId(), this::formatStatusLine);
     }
 
     private String formatStatusLine(final HttpResponse response) {
         // TODO we are missing the reason phrase here, e.g. "OK", but there is no complete list in the JDK alone
         return String.format("HTTP/1.1 %d", response.getStatus());
     }
-    
-    private <H extends HttpMessage> String format(final H message, final Function<H, String> lineCreator) 
+
+    private <H extends HttpMessage> String format(final H message, final String type, final String correlationId,
+            final Function<H, String> lineCreator)
             throws IOException {
         final List<String> lines = Lists.newArrayListWithExpectedSize(4);
 
+        lines.add(type + ": " + correlationId);
         lines.add(lineCreator.apply(message));
         lines.addAll(formatHeaders(message));
 
diff --git a/logbook-core/src/main/java/org/zalando/logbook/JsonHttpLogFormatter.java b/logbook-core/src/main/java/org/zalando/logbook/JsonHttpLogFormatter.java
index 813a8c98..4a65914f 100644
--- a/logbook-core/src/main/java/org/zalando/logbook/JsonHttpLogFormatter.java
+++ b/logbook-core/src/main/java/org/zalando/logbook/JsonHttpLogFormatter.java
@@ -57,6 +57,7 @@ public final class JsonHttpLogFormatter implements HttpLogFormatter {
 
         final ImmutableMap.Builder<String, Object> builder = ImmutableMap.<String, Object>builder();
 
+        builder.put("type", "request");
         builder.put("correlation", correlationId);
         builder.put("remote", request.getRemote());
         builder.put("method", request.getMethod());
@@ -77,6 +78,7 @@ public final class JsonHttpLogFormatter implements HttpLogFormatter {
 
         final ImmutableMap.Builder<String, Object> builder = ImmutableMap.<String, Object>builder();
 
+        builder.put("type", "response");
         builder.put("correlation", correlationId);
         builder.put("status", response.getStatus());
         addUnless(builder, "headers", response.getHeaders().asMap(), Map::isEmpty);


Test patch:
diff --git a/logbook-core/src/test/java/org/zalando/logbook/DefaultHttpLogFormatterTest.java b/logbook-core/src/test/java/org/zalando/logbook/DefaultHttpLogFormatterTest.java
index c8c24797..37118df1 100644
--- a/logbook-core/src/test/java/org/zalando/logbook/DefaultHttpLogFormatterTest.java
+++ b/logbook-core/src/test/java/org/zalando/logbook/DefaultHttpLogFormatterTest.java
@@ -45,7 +45,8 @@ public final class DefaultHttpLogFormatterTest {
 
         final String http = unit.format(new SimplePrecorrelation<>(correlationId, request));
 
-        assertThat(http, equalTo("GET /test?limit=1 HTTP/1.1\n" +
+        assertThat(http, equalTo("Request: c9408eaa-677d-11e5-9457-10ddb1ee7671\n" +
+                "GET /test?limit=1 HTTP/1.1\n" +
                 "Accept: application/json\n" +
                 "Content-Type: text/plain\n" +
                 "\n" +
@@ -64,7 +65,8 @@ public final class DefaultHttpLogFormatterTest {
 
         final String http = unit.format(new SimplePrecorrelation<>(correlationId, request));
 
-        assertThat(http, equalTo("GET /test HTTP/1.1\n" +
+        assertThat(http, equalTo("Request: 2bd05240-6827-11e5-bbee-10ddb1ee7671\n" +
+                "GET /test HTTP/1.1\n" +
                 "Accept: application/json\n" +
                 "Content-Type: text/plain\n" +
                 "\n" +
@@ -81,7 +83,8 @@ public final class DefaultHttpLogFormatterTest {
 
         final String http = unit.format(new SimplePrecorrelation<>(correlationId, request));
 
-        assertThat(http, equalTo("GET /test HTTP/1.1\n" +
+        assertThat(http, equalTo("Request: 0eae9f6c-6824-11e5-8b0a-10ddb1ee7671\n" +
+                "GET /test HTTP/1.1\n" +
                 "Accept: application/json"));
     }
 
@@ -96,7 +99,8 @@ public final class DefaultHttpLogFormatterTest {
 
         final String http = unit.format(new SimpleCorrelation<>(correlationId, request, response));
 
-        assertThat(http, equalTo("HTTP/1.1 200\n" +
+        assertThat(http, equalTo("Response: 2d51bc02-677e-11e5-8b9b-10ddb1ee7671\n" +
+                "HTTP/1.1 200\n" +
                 "Content-Type: application/json\n" +
                 "\n" +
                 "{\"success\":true}"));
@@ -112,7 +116,8 @@ public final class DefaultHttpLogFormatterTest {
 
         final String http = unit.format(new SimpleCorrelation<>(correlationId, request, response));
 
-        assertThat(http, equalTo("HTTP/1.1 200\n" +
+        assertThat(http, equalTo("Response: 3881ae92-6824-11e5-921b-10ddb1ee7671\n" +
+                "HTTP/1.1 200\n" +
                 "Content-Type: application/json"));
     }
 
diff --git a/logbook-core/src/test/java/org/zalando/logbook/JsonHttpLogFormatterTest.java b/logbook-core/src/test/java/org/zalando/logbook/JsonHttpLogFormatterTest.java
index 4c18a14d..2c12419b 100644
--- a/logbook-core/src/test/java/org/zalando/logbook/JsonHttpLogFormatterTest.java
+++ b/logbook-core/src/test/java/org/zalando/logbook/JsonHttpLogFormatterTest.java
@@ -54,6 +54,7 @@ public final class JsonHttpLogFormatterTest {
         final String json = unit.format(new SimplePrecorrelation<>(correlationId, request));
 
         with(json)
+                .assertThat("$.type", is("request"))
                 .assertThat("$.correlation", is("3ce91230-677b-11e5-87b7-10ddb1ee7671"))
                 .assertThat("$.remote", is("127.0.0.1"))
                 .assertThat("$.method", is("GET"))
@@ -172,6 +173,7 @@ public final class JsonHttpLogFormatterTest {
         final String json = unit.format(new SimpleCorrelation<>(correlationId, request, response));
 
         with(json)
+                .assertThat("$.type", is("response"))
                 .assertThat("$.correlation", is("53de2640-677d-11e5-bc84-10ddb1ee7671"))
                 .assertThat("$.status", is(200))
                 .assertThat("$.headers.*", hasSize(1))
diff --git a/logbook-httpclient/src/test/java/org/zalando/logbook/httpclient/LogbookHttpInterceptorsTest.java b/logbook-httpclient/src/test/java/org/zalando/logbook/httpclient/LogbookHttpInterceptorsTest.java
index 4e523beb..a2242df9 100644
--- a/logbook-httpclient/src/test/java/org/zalando/logbook/httpclient/LogbookHttpInterceptorsTest.java
+++ b/logbook-httpclient/src/test/java/org/zalando/logbook/httpclient/LogbookHttpInterceptorsTest.java
@@ -85,7 +85,8 @@ public final class LogbookHttpInterceptorsTest {
         verify(writer).writeRequest(captor.capture());
         final String request = captor.getValue().getRequest();
 
-        assertThat(request, is("GET / HTTP/1.1"));
+        assertThat(request, startsWith("Request:"));
+        assertThat(request, containsString("GET / HTTP/1.1"));
     }
 
     @Test
@@ -106,7 +107,8 @@ public final class LogbookHttpInterceptorsTest {
         verify(writer).writeResponse(captor.capture());
         final String response = captor.getValue().getResponse();
 
-        assertThat(response, startsWith("HTTP/1.1 200"));
+        assertThat(response, startsWith("Response:"));
+        assertThat(response, containsString("HTTP/1.1 200"));
         assertThat(response, containsString("Content-Type: text/plain"));
         assertThat(response, containsString("Hello, world!"));
     }
diff --git a/logbook-servlet/src/test/java/org/zalando/logbook/servlet/WritingTest.java b/logbook-servlet/src/test/java/org/zalando/logbook/servlet/WritingTest.java
index 7fc4c1d9..5f6d918e 100644
--- a/logbook-servlet/src/test/java/org/zalando/logbook/servlet/WritingTest.java
+++ b/logbook-servlet/src/test/java/org/zalando/logbook/servlet/WritingTest.java
@@ -36,7 +36,8 @@ import org.zalando.logbook.servlet.example.ExampleController;
 
 import java.io.IOException;
 
-import static org.hamcrest.Matchers.is;
+import static org.hamcrest.Matchers.endsWith;
+import static org.hamcrest.Matchers.startsWith;
 import static org.junit.Assert.assertThat;
 import static org.mockito.Matchers.any;
 import static org.mockito.Mockito.mock;
@@ -82,7 +83,9 @@ public final class WritingTest {
         verify(writer).writeRequest(captor.capture());
         final Precorrelation<String> precorrelation = captor.getValue();
 
-        assertThat(precorrelation.getRequest(), is("GET /api/sync HTTP/1.1\n" +
+        assertThat(precorrelation.getRequest(), startsWith("Request:"));
+        assertThat(precorrelation.getRequest(), endsWith(
+                "GET /api/sync HTTP/1.1\n" +
                 "Accept: application/json\n" +
                 "Host: localhost\n" +
                 "Content-Type: text/plain\n" +
@@ -99,7 +102,9 @@ public final class WritingTest {
         verify(writer).writeResponse(captor.capture());
         final Correlation<String, String> correlation = captor.getValue();
 
-        assertThat(correlation.getResponse(), is("HTTP/1.1 200\n" +
+        assertThat(correlation.getResponse(), startsWith("Response:"));
+        assertThat(correlation.getResponse(), endsWith(
+                "HTTP/1.1 200\n" +
                 "Content-Type: application/json\n" +
                 "\n" +
                 "{\"value\":\"Hello, world!\"}"));
diff --git a/logbook-spring-boot-starter/src/test/java/org/zalando/logbook/spring/HttpFormatterTest.java b/logbook-spring-boot-starter/src/test/java/org/zalando/logbook/spring/HttpFormatterTest.java
index 39e8f3fa..a4e5ebc8 100644
--- a/logbook-spring-boot-starter/src/test/java/org/zalando/logbook/spring/HttpFormatterTest.java
+++ b/logbook-spring-boot-starter/src/test/java/org/zalando/logbook/spring/HttpFormatterTest.java
@@ -25,7 +25,6 @@ import org.junit.Test;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.context.annotation.Bean;
 import org.springframework.context.annotation.Configuration;
-import org.springframework.test.context.ActiveProfiles;
 import org.springframework.test.context.ContextConfiguration;
 import org.springframework.test.context.TestPropertySource;
 import org.zalando.logbook.HttpLogWriter;
@@ -35,7 +34,7 @@ import org.zalando.logbook.Precorrelation;
 import java.io.IOException;
 import java.util.function.Function;
 
-import static org.hamcrest.Matchers.startsWith;
+import static org.hamcrest.Matchers.containsString;
 import static org.hobsoft.hamcrest.compose.ComposeMatchers.hasFeature;
 import static org.mockito.Matchers.any;
 import static org.mockito.Mockito.mock;
@@ -69,12 +68,12 @@ public final class HttpFormatterTest extends AbstractTest {
 
         logbook.write(MockRawHttpRequest.create());
 
-        verify(writer).writeRequest(argThat(isJsonFormatted()));
+        verify(writer).writeRequest(argThat(isHttpFormatted()));
     }
 
-    private Matcher<Precorrelation<String>> isJsonFormatted() {
+    private Matcher<Precorrelation<String>> isHttpFormatted() {
         final Function<Precorrelation<String>, String> getRequest = Precorrelation::getRequest;
-        return hasFeature("request", getRequest, startsWith("GET / HTTP/1.1"));
+        return hasFeature("request", getRequest, containsString("GET / HTTP/1.1"));
     }
 
 }
\ No newline at end of file