Skip to content

Commit ce126b7

Browse files
Merge pull request zalando#168 from zalando/feature/duration
Added logging of durations
2 parents b39befc + f7ab36d commit ce126b7

File tree

17 files changed

+79
-27
lines changed

17 files changed

+79
-27
lines changed

README.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -183,6 +183,7 @@ Hello world!
183183

184184
```http
185185
Outgoing Response: 2d66e4bc-9a0d-11e5-a84c-1f39510f0d6b
186+
Duration: 25 ms
186187
HTTP/1.1 200
187188
Content-Type: application/json
188189
@@ -219,6 +220,7 @@ Content-Type: application/json
219220
"origin": "local",
220221
"type": "response",
221222
"correlation": "2d66e4bc-9a0d-11e5-a84c-1f39510f0d6b",
223+
"duration": 25,
222224
"protocol": "HTTP/1.1",
223225
"status": 200,
224226
"headers": {
@@ -236,6 +238,7 @@ a JSON response body will **not** be escaped and represented as a string:
236238
"origin": "local",
237239
"type": "response",
238240
"correlation": "2d66e4bc-9a0d-11e5-a84c-1f39510f0d6b",
241+
"duration": 25,
239242
"protocol": "HTTP/1.1",
240243
"status": 200,
241244
"headers": {

logbook-api/src/main/java/org/zalando/logbook/Correlation.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,13 @@
11
package org.zalando.logbook;
22

3+
import java.time.Duration;
4+
35
public interface Correlation<Request, Response> {
46

57
String getId();
68

9+
Duration getDuration();
10+
711
Request getRequest();
812

913
Response getResponse();
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
@ParametersAreNonnullByDefault
2+
package org.zalando.logbook;
3+
4+
import javax.annotation.ParametersAreNonnullByDefault;

logbook-core/src/main/java/org/zalando/logbook/ChunkingHttpLogWriter.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,8 @@ public void writeRequest(final Precorrelation<String> precorrelation) throws IOE
4242
@Override
4343
public void writeResponse(final Correlation<String, String> correlation) throws IOException {
4444
split(correlation.getResponse()).forEach(throwing(part ->
45-
writer.writeResponse(new SimpleCorrelation<>(correlation.getId(), correlation.getRequest(), part))));
45+
writer.writeResponse(new SimpleCorrelation<>(correlation.getId(), correlation.getDuration(),
46+
correlation.getRequest(), part))));
4647
}
4748

4849
private static <T> Consumer<T> throwing(final ThrowingConsumer<T> consumer) {

logbook-core/src/main/java/org/zalando/logbook/DefaultHttpLogFormatter.java

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -133,15 +133,16 @@ public List<String> prepare(final Correlation<HttpRequest, HttpResponse> correla
133133
final int status = response.getStatus();
134134
final String reasonPhrase = REASON_PHRASES.getOrDefault(Integer.toString(status), "");
135135
final String statusLine = String.format("%s %d %s", response.getProtocolVersion(), status, reasonPhrase).trim();
136-
return prepare(response, "Response", correlation.getId(), statusLine);
136+
return prepare(response, "Response", correlation.getId(),
137+
"Duration: " + correlation.getDuration().toMillis() + " ms", statusLine);
137138
}
138139

139140
private <H extends HttpMessage> List<String> prepare(final H message, final String type,
140-
final String correlationId, final String line) throws IOException {
141+
final String correlationId, final String... prefixes) throws IOException {
141142
final List<String> lines = new ArrayList<>();
142143

143144
lines.add(direction(message) + " " + type + ": " + correlationId);
144-
lines.add(line);
145+
Collections.addAll(lines, prefixes);
145146
lines.addAll(formatHeaders(message));
146147

147148
final String body = message.getBodyAsString();

logbook-core/src/main/java/org/zalando/logbook/DefaultLogbook.java

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
package org.zalando.logbook;
22

33
import java.io.IOException;
4+
import java.time.Clock;
5+
import java.time.Duration;
6+
import java.time.Instant;
47
import java.util.Optional;
58
import java.util.UUID;
69
import java.util.function.Predicate;
@@ -14,6 +17,7 @@ final class DefaultLogbook implements Logbook {
1417
private final ResponseFilter responseFilter;
1518
private final HttpLogFormatter formatter;
1619
private final HttpLogWriter writer;
20+
private final Clock clock = Clock.systemUTC();
1721

1822
DefaultLogbook(
1923
final Predicate<RawHttpRequest> predicate,
@@ -34,6 +38,7 @@ final class DefaultLogbook implements Logbook {
3438

3539
@Override
3640
public Optional<Correlator> write(final RawHttpRequest rawHttpRequest) throws IOException {
41+
final Instant start = Instant.now(clock);
3742
if (writer.isActive(rawHttpRequest) && predicate.test(rawHttpRequest)) {
3843
final String correlationId = UUID.randomUUID().toString();
3944
final RawHttpRequest filteredRawHttpRequest = rawRequestFilter.filter(rawHttpRequest);
@@ -44,12 +49,14 @@ public Optional<Correlator> write(final RawHttpRequest rawHttpRequest) throws IO
4449
writer.writeRequest(new SimplePrecorrelation<>(correlationId, format));
4550

4651
return Optional.of(rawHttpResponse -> {
52+
final Instant end = Instant.now(clock);
53+
final Duration duration = Duration.between(start, end);
4754
final RawHttpResponse filteredRawHttpResponse = rawResponseFilter.filter(rawHttpResponse);
4855
final HttpResponse response = responseFilter.filter(filteredRawHttpResponse.withBody());
4956
final Correlation<HttpRequest, HttpResponse> correlation =
50-
new SimpleCorrelation<>(correlationId, request, response);
57+
new SimpleCorrelation<>(correlationId, duration, request, response);
5158
final String message = formatter.format(correlation);
52-
writer.writeResponse(new SimpleCorrelation<>(correlationId, format, message));
59+
writer.writeResponse(new SimpleCorrelation<>(correlationId, duration, format, message));
5360
});
5461
} else {
5562
return Optional.empty();
@@ -81,11 +88,13 @@ public I getRequest() {
8188
static class SimpleCorrelation<I, O> implements Correlation<I, O> {
8289

8390
private final String id;
91+
private final Duration duration;
8492
private final I request;
8593
private final O response;
8694

87-
public SimpleCorrelation(final String id, final I request, final O response) {
95+
public SimpleCorrelation(final String id, final Duration duration, final I request, final O response) {
8896
this.id = id;
97+
this.duration = duration;
8998
this.request = request;
9099
this.response = response;
91100
}
@@ -95,6 +104,11 @@ public String getId() {
95104
return id;
96105
}
97106

107+
@Override
108+
public Duration getDuration() {
109+
return duration;
110+
}
111+
98112
@Override
99113
public I getRequest() {
100114
return request;

logbook-core/src/main/java/org/zalando/logbook/JsonHttpLogFormatter.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -114,14 +114,14 @@ public String format(final Correlation<HttpRequest, HttpResponse> correlation) t
114114
* @see DefaultHttpLogFormatter#prepare(Correlation)
115115
*/
116116
public Map<String, Object> prepare(final Correlation<HttpRequest, HttpResponse> correlation) throws IOException {
117-
final String correlationId = correlation.getId();
118117
final HttpResponse response = correlation.getResponse();
119118

120119
final Map<String, Object> content = new LinkedHashMap<>();
121120

122121
content.put("origin", translate(response.getOrigin()));
123122
content.put("type", "response");
124-
content.put("correlation", correlationId);
123+
content.put("correlation", correlation.getId());
124+
content.put("duration", correlation.getDuration().toMillis());
125125
content.put("protocol", response.getProtocolVersion());
126126
content.put("status", response.getStatus());
127127

logbook-core/src/test/java/org/zalando/logbook/ChunkingHttpLogWriterTest.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
import java.io.IOException;
1111
import java.util.List;
1212

13+
import static java.time.Duration.ZERO;
1314
import static java.util.stream.Collectors.toList;
1415
import static org.hamcrest.Matchers.contains;
1516
import static org.hamcrest.Matchers.is;
@@ -84,7 +85,7 @@ public void shouldCreateWithSizeOfOne() throws IOException {
8485
}
8586

8687
private List<String> captureResponse(final String response) throws IOException {
87-
unit.writeResponse(new DefaultLogbook.SimpleCorrelation<>("id", "", response));
88+
unit.writeResponse(new DefaultLogbook.SimpleCorrelation<>("id", ZERO, "", response));
8889

8990
verify(delegate, atLeastOnce()).writeResponse(responseCaptor.capture());
9091

logbook-core/src/test/java/org/zalando/logbook/CurlHttpLogFormatterTest.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66

77
import java.io.IOException;
88

9+
import static java.time.Duration.ZERO;
910
import static org.hamcrest.Matchers.is;
1011
import static org.junit.Assert.assertThat;
1112
import static org.mockito.Mockito.mock;
@@ -70,7 +71,7 @@ public void shouldDelegateLogResponse() throws IOException {
7071
final HttpLogFormatter unit = new CurlHttpLogFormatter(fallback);
7172

7273
final Correlation<HttpRequest, HttpResponse> correlation = new SimpleCorrelation<>(
73-
"3881ae92-6824-11e5-921b-10ddb1ee7671", MockHttpRequest.create(), MockHttpResponse.create());
74+
"3881ae92-6824-11e5-921b-10ddb1ee7671", ZERO, MockHttpRequest.create(), MockHttpResponse.create());
7475

7576
unit.format(correlation);
7677

logbook-core/src/test/java/org/zalando/logbook/DefaultHttpLogFormatterTest.java

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,9 @@
55
import org.zalando.logbook.DefaultLogbook.SimplePrecorrelation;
66

77
import java.io.IOException;
8+
import java.time.Duration;
89

10+
import static java.time.Duration.ofMillis;
911
import static org.hamcrest.Matchers.is;
1012
import static org.junit.Assert.assertThat;
1113

@@ -82,9 +84,10 @@ public void shouldLogResponse() throws IOException {
8284
.withHeaders(MockHeaders.of("Content-Type", "application/json"))
8385
.withBodyAsString("{\"success\":true}");
8486

85-
final String http = unit.format(new SimpleCorrelation<>(correlationId, request, response));
87+
final String http = unit.format(new SimpleCorrelation<>(correlationId, ofMillis(125), request, response));
8688

8789
assertThat(http, is("Incoming Response: 2d51bc02-677e-11e5-8b9b-10ddb1ee7671\n" +
90+
"Duration: 125 ms\n" +
8891
"HTTP/1.0 201 Created\n" +
8992
"Content-Type: application/json\n" +
9093
"\n" +
@@ -100,9 +103,10 @@ public void shouldLogResponseWithoutBody() throws IOException {
100103
.withStatus(400)
101104
.withHeaders(MockHeaders.of("Content-Type", "application/json"));
102105

103-
final String http = unit.format(new SimpleCorrelation<>(correlationId, request, response));
106+
final String http = unit.format(new SimpleCorrelation<>(correlationId, ofMillis(100), request, response));
104107

105108
assertThat(http, is("Outgoing Response: 3881ae92-6824-11e5-921b-10ddb1ee7671\n" +
109+
"Duration: 100 ms\n" +
106110
"HTTP/1.1 400 Bad Request\n" +
107111
"Content-Type: application/json"));
108112
}

logbook-core/src/test/java/org/zalando/logbook/DefaultHttpLogWriterLevelTest.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
import java.util.function.BiConsumer;
1414
import java.util.function.Predicate;
1515

16+
import static java.time.Duration.ZERO;
1617
import static org.mockito.Mockito.mock;
1718
import static org.mockito.Mockito.verify;
1819

@@ -67,7 +68,7 @@ public void shouldLogRequestWithCorrectLevel() throws IOException {
6768

6869
@Test
6970
public void shouldLogResponseWithCorrectLevel() throws IOException {
70-
unit.writeResponse(new SimpleCorrelation<>("1", "foo", "bar"));
71+
unit.writeResponse(new SimpleCorrelation<>("1", ZERO, "foo", "bar"));
7172

7273
log.accept(verify(logger), "bar");
7374
}

logbook-core/src/test/java/org/zalando/logbook/DefaultHttpLogWriterTest.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77

88
import java.io.IOException;
99

10+
import static java.time.Duration.ZERO;
1011
import static org.hamcrest.Matchers.equalTo;
1112
import static org.hamcrest.Matchers.is;
1213
import static org.junit.Assert.assertThat;
@@ -47,7 +48,7 @@ public void shouldDefaultToTraceLevelForLoggingResponses() throws IOException {
4748
final Logger logger = mock(Logger.class);
4849
final HttpLogWriter unit = new DefaultHttpLogWriter(logger);
4950

50-
unit.writeResponse(new DefaultLogbook.SimpleCorrelation<>("1", "foo", "bar"));
51+
unit.writeResponse(new DefaultLogbook.SimpleCorrelation<>("1", ZERO, "foo", "bar"));
5152

5253
verify(logger).trace("bar");
5354
}

logbook-core/src/test/java/org/zalando/logbook/JsonHttpLogFormatterTest.java

Lines changed: 14 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,11 @@
55
import org.zalando.logbook.DefaultLogbook.SimplePrecorrelation;
66

77
import java.io.IOException;
8+
import java.time.Duration;
89

910
import static com.jayway.jsonassert.JsonAssert.with;
11+
import static java.time.Duration.ZERO;
12+
import static java.time.Duration.ofMillis;
1013
import static java.util.Collections.singletonList;
1114
import static org.hamcrest.Matchers.containsString;
1215
import static org.hamcrest.Matchers.emptyString;
@@ -240,7 +243,7 @@ public void shouldLogResponse() throws IOException {
240243
.withContentType("application/xml")
241244
.withBodyAsString("<success>true<success>");
242245

243-
final String json = unit.format(new SimpleCorrelation<>(correlationId, request, response));
246+
final String json = unit.format(new SimpleCorrelation<>(correlationId, ofMillis(125), request, response));
244247

245248
with(json)
246249
.assertThat("$.origin", is("local"))
@@ -250,7 +253,8 @@ public void shouldLogResponse() throws IOException {
250253
.assertThat("$.status", is(200))
251254
.assertThat("$.headers.*", hasSize(1))
252255
.assertThat("$.headers['Date']", is(singletonList("Tue, 15 Nov 1994 08:12:31 GMT")))
253-
.assertThat("$.body", is("<success>true<success>"));
256+
.assertThat("$.body", is("<success>true<success>"))
257+
.assertThat("$.duration", is(125));
254258
}
255259

256260
@Test
@@ -259,7 +263,7 @@ public void shouldLogResponseWithoutHeaders() throws IOException {
259263
final HttpRequest request = MockHttpRequest.create();
260264
final HttpResponse response = create();
261265

262-
final String json = unit.format(new SimpleCorrelation<>(correlationId, request, response));
266+
final String json = unit.format(new SimpleCorrelation<>(correlationId, ZERO, request, response));
263267

264268
with(json)
265269
.assertThat("$", not(hasKey("headers")));
@@ -273,7 +277,7 @@ public void shouldLogResponseWithoutBody() throws IOException {
273277
final HttpResponse response = create()
274278
.withBodyAsString("");
275279

276-
final String json = unit.format(new SimpleCorrelation<>(correlationId, request, response));
280+
final String json = unit.format(new SimpleCorrelation<>(correlationId, ZERO, request, response));
277281

278282
with(json)
279283
.assertThat("$", not(hasKey("body")));
@@ -287,7 +291,7 @@ public void shouldEmbedJsonResponseBodyAsIs() throws IOException {
287291
.withContentType("application/json")
288292
.withBodyAsString("{\"name\":\"Bob\"}");
289293

290-
final String json = unit.format(new SimpleCorrelation<>(correlationId, request, response));
294+
final String json = unit.format(new SimpleCorrelation<>(correlationId, ZERO, request, response));
291295

292296
with(json)
293297
.assertThat("$.body.name", is("Bob"));
@@ -301,7 +305,7 @@ public void shouldCompactEmbeddedJsonResponseBody() throws IOException {
301305
.withContentType("application/json")
302306
.withBodyAsString("{\n \"name\": \"Bob\"\n}");
303307

304-
final String json = unit.format(new SimpleCorrelation<>(correlationId, request, response));
308+
final String json = unit.format(new SimpleCorrelation<>(correlationId, ZERO, request, response));
305309

306310
assertThat(json, containsString("{\"name\":\"Bob\"}"));
307311
}
@@ -314,7 +318,7 @@ public void shouldEmbedCustomJsonResponseBodyAsIs() throws IOException {
314318
.withContentType("application/custom+json")
315319
.withBodyAsString("{\"name\":\"Bob\"}");
316320

317-
final String json = unit.format(new SimpleCorrelation<>(correlationId, request, response));
321+
final String json = unit.format(new SimpleCorrelation<>(correlationId, ZERO, request, response));
318322

319323
with(json)
320324
.assertThat("$.body.name", is("Bob"));
@@ -328,7 +332,7 @@ public void shouldNotEmbedCustomTextJsonResponseBodyAsIs() throws IOException {
328332
.withContentType("text/custom+json")
329333
.withBodyAsString("{\"name\":\"Bob\"}");
330334

331-
final String json = unit.format(new SimpleCorrelation<>(correlationId, request, response));
335+
final String json = unit.format(new SimpleCorrelation<>(correlationId, ZERO, request, response));
332336

333337
with(json)
334338
.assertThat("$.body", is("{\"name\":\"Bob\"}"));
@@ -341,7 +345,7 @@ public void shouldEmbedJsonResponseBodyAsNullIfEmpty() throws IOException {
341345
final HttpResponse response = create()
342346
.withContentType("application/json");
343347

344-
final String json = unit.format(new SimpleCorrelation<>(correlationId, request, response));
348+
final String json = unit.format(new SimpleCorrelation<>(correlationId, ZERO, request, response));
345349

346350
with(json)
347351
.assertThat("$.body", is(emptyString()));
@@ -355,7 +359,7 @@ public void shouldLogInvalidJsonResponseBodyAsIs() throws IOException {
355359
.withContentType("text/custom+json")
356360
.withBodyAsString("{\n \"name\":\"Bob\";;;\n;}");
357361

358-
final String json = unit.format(new SimpleCorrelation<>(correlationId, request, response));
362+
final String json = unit.format(new SimpleCorrelation<>(correlationId, ZERO, request, response));
359363

360364
with(json)
361365
.assertThat("$.body", is("{\n \"name\":\"Bob\";;;\n;}"));

logbook-core/src/test/java/org/zalando/logbook/StreamHttpLogWriterTest.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
import java.io.PrintStream;
1111

1212
import static java.lang.System.lineSeparator;
13+
import static java.time.Duration.ZERO;
1314
import static org.hamcrest.Matchers.is;
1415
import static org.junit.Assert.assertThat;
1516
import static org.mockito.Mockito.mock;
@@ -44,7 +45,7 @@ public void shouldLogResponseToStream() throws IOException {
4445
final PrintStream stream = mock(PrintStream.class);
4546
final HttpLogWriter unit = new StreamHttpLogWriter(stream);
4647

47-
unit.writeResponse(new DefaultLogbook.SimpleCorrelation<>("1", "foo", "bar"));
48+
unit.writeResponse(new DefaultLogbook.SimpleCorrelation<>("1", ZERO, "foo", "bar"));
4849

4950
verify(stream).println("bar");
5051
}
@@ -62,7 +63,7 @@ public void shouldRequestToStdoutByDefault() throws IOException {
6263
public void shouldResponseToStdoutByDefault() throws IOException {
6364
final HttpLogWriter unit = new StreamHttpLogWriter();
6465

65-
unit.writeResponse(new DefaultLogbook.SimpleCorrelation<>("1", "foo", "bar"));
66+
unit.writeResponse(new DefaultLogbook.SimpleCorrelation<>("1", ZERO, "foo", "bar"));
6667

6768
assertThat(stdout.getLog(), is("bar" + lineSeparator()));
6869
}
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
@ParametersAreNonnullByDefault
2+
package org.zalando.logbook.httpclient;
3+
4+
import javax.annotation.ParametersAreNonnullByDefault;

0 commit comments

Comments
 (0)