Skip to content

Commit 827cb08

Browse files
authored
Add human-readable HTTP client stats (#134296) (#134318)
Today the timestamps in the HTTP client stats output are not rendered as proper human-readable timestamps even when `?human=true` is specified. This commit adds the missing human-readable variants of these fields.
1 parent 8ebc6ae commit 827cb08

File tree

3 files changed

+74
-5
lines changed

3 files changed

+74
-5
lines changed

docs/changelog/134296.yaml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
pr: 134296
2+
summary: Add human-readable HTTP client stats
3+
area: Network
4+
type: enhancement
5+
issues: []

server/src/main/java/org/elasticsearch/http/HttpStats.java

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
import org.elasticsearch.common.io.stream.Writeable;
1616
import org.elasticsearch.common.xcontent.ChunkedToXContent;
1717
import org.elasticsearch.xcontent.ToXContent;
18-
import org.elasticsearch.xcontent.ToXContentFragment;
18+
import org.elasticsearch.xcontent.ToXContentObject;
1919
import org.elasticsearch.xcontent.XContentBuilder;
2020

2121
import java.io.IOException;
@@ -89,8 +89,11 @@ static final class Fields {
8989
static final String CLIENT_LOCAL_ADDRESS = "local_address";
9090
static final String CLIENT_REMOTE_ADDRESS = "remote_address";
9191
static final String CLIENT_LAST_URI = "last_uri";
92+
static final String CLIENT_OPENED_TIME = "opened_time";
9293
static final String CLIENT_OPENED_TIME_MILLIS = "opened_time_millis";
94+
static final String CLIENT_CLOSED_TIME = "closed_time";
9395
static final String CLIENT_CLOSED_TIME_MILLIS = "closed_time_millis";
96+
static final String CLIENT_LAST_REQUEST_TIME = "last_request_time";
9497
static final String CLIENT_LAST_REQUEST_TIME_MILLIS = "last_request_time_millis";
9598
static final String CLIENT_REQUEST_COUNT = "request_count";
9699
static final String CLIENT_REQUEST_SIZE_BYTES = "request_size_bytes";
@@ -136,7 +139,7 @@ public record ClientStats(
136139
long lastRequestTimeMillis,
137140
long requestCount,
138141
long requestSizeBytes
139-
) implements Writeable, ToXContentFragment {
142+
) implements Writeable, ToXContentObject {
140143

141144
public static final long NOT_CLOSED = -1L;
142145

@@ -179,11 +182,15 @@ public XContentBuilder toXContent(XContentBuilder builder, Params params) throws
179182
if (opaqueId != null) {
180183
builder.field(Fields.CLIENT_OPAQUE_ID, opaqueId);
181184
}
182-
builder.field(Fields.CLIENT_OPENED_TIME_MILLIS, openedTimeMillis);
185+
builder.timestampFieldsFromUnixEpochMillis(Fields.CLIENT_OPENED_TIME_MILLIS, Fields.CLIENT_OPENED_TIME, openedTimeMillis);
183186
if (closedTimeMillis != NOT_CLOSED) {
184-
builder.field(Fields.CLIENT_CLOSED_TIME_MILLIS, closedTimeMillis);
187+
builder.timestampFieldsFromUnixEpochMillis(Fields.CLIENT_CLOSED_TIME_MILLIS, Fields.CLIENT_CLOSED_TIME, closedTimeMillis);
185188
}
186-
builder.field(Fields.CLIENT_LAST_REQUEST_TIME_MILLIS, lastRequestTimeMillis);
189+
builder.timestampFieldsFromUnixEpochMillis(
190+
Fields.CLIENT_LAST_REQUEST_TIME_MILLIS,
191+
Fields.CLIENT_LAST_REQUEST_TIME,
192+
lastRequestTimeMillis
193+
);
187194
builder.field(Fields.CLIENT_REQUEST_COUNT, requestCount);
188195
builder.field(Fields.CLIENT_REQUEST_SIZE_BYTES, requestSizeBytes);
189196
builder.endObject();

server/src/test/java/org/elasticsearch/http/HttpClientStatsTrackerTests.java

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,14 @@
99

1010
package org.elasticsearch.http;
1111

12+
import org.elasticsearch.common.Strings;
1213
import org.elasticsearch.common.bytes.BytesArray;
14+
import org.elasticsearch.common.bytes.BytesReference;
1315
import org.elasticsearch.common.network.NetworkAddress;
1416
import org.elasticsearch.common.settings.ClusterSettings;
1517
import org.elasticsearch.common.settings.Settings;
1618
import org.elasticsearch.common.util.Maps;
19+
import org.elasticsearch.common.xcontent.XContentHelper;
1720
import org.elasticsearch.core.TimeValue;
1821
import org.elasticsearch.node.Node;
1922
import org.elasticsearch.rest.RestRequest;
@@ -22,8 +25,13 @@
2225
import org.elasticsearch.test.rest.FakeRestRequest;
2326
import org.elasticsearch.threadpool.DefaultBuiltInExecutorBuilders;
2427
import org.elasticsearch.threadpool.ThreadPool;
28+
import org.elasticsearch.xcontent.ToXContent;
29+
import org.elasticsearch.xcontent.XContentFactory;
30+
import org.elasticsearch.xcontent.XContentType;
2531

32+
import java.io.IOException;
2633
import java.net.InetSocketAddress;
34+
import java.time.Instant;
2735
import java.util.HashMap;
2836
import java.util.List;
2937
import java.util.Map;
@@ -379,6 +387,55 @@ public void testClearsStatsIfDisabledConcurrently() throws InterruptedException
379387
}
380388
}
381389

390+
public void testToXContent() throws IOException {
391+
final var clientStats = new HttpStats.ClientStats(
392+
randomNonNegativeInt(),
393+
randomIdentifier(),
394+
randomIdentifier(),
395+
randomIdentifier(),
396+
randomIdentifier(),
397+
randomIdentifier(),
398+
randomIdentifier(),
399+
randomLongBetween(1, Long.MAX_VALUE),
400+
randomLongBetween(1, Long.MAX_VALUE),
401+
randomLongBetween(1, Long.MAX_VALUE),
402+
randomNonNegativeLong(),
403+
randomNonNegativeLong()
404+
);
405+
final var description = Strings.toString(clientStats, false, true);
406+
logger.info("description: {}", description);
407+
408+
final Map<String, Object> xcontentMap;
409+
try (var builder = XContentFactory.jsonBuilder()) {
410+
builder.humanReadable(true).value(clientStats, ToXContent.EMPTY_PARAMS);
411+
xcontentMap = XContentHelper.convertToMap(BytesReference.bytes(builder), false, XContentType.JSON).v2();
412+
}
413+
414+
assertEquals(description, clientStats.id(), xcontentMap.get("id"));
415+
assertEquals(description, clientStats.agent(), xcontentMap.get("agent"));
416+
assertEquals(description, clientStats.localAddress(), xcontentMap.get("local_address"));
417+
assertEquals(description, clientStats.remoteAddress(), xcontentMap.get("remote_address"));
418+
assertEquals(description, clientStats.lastUri(), xcontentMap.get("last_uri"));
419+
assertEquals(description, clientStats.forwardedFor(), xcontentMap.get("x_forwarded_for"));
420+
assertEquals(description, clientStats.opaqueId(), xcontentMap.get("x_opaque_id"));
421+
422+
assertEquals(description, clientStats.openedTimeMillis(), xcontentMap.get("opened_time_millis"));
423+
assertEquals(description, Instant.ofEpochMilli(clientStats.openedTimeMillis()).toString(), xcontentMap.get("opened_time"));
424+
425+
assertEquals(description, clientStats.closedTimeMillis(), xcontentMap.get("closed_time_millis"));
426+
assertEquals(description, Instant.ofEpochMilli(clientStats.closedTimeMillis()).toString(), xcontentMap.get("closed_time"));
427+
428+
assertEquals(description, clientStats.lastRequestTimeMillis(), xcontentMap.get("last_request_time_millis"));
429+
assertEquals(
430+
description,
431+
Instant.ofEpochMilli(clientStats.lastRequestTimeMillis()).toString(),
432+
xcontentMap.get("last_request_time")
433+
);
434+
435+
assertEquals(description, clientStats.requestCount(), (long) xcontentMap.get("request_count"));
436+
assertEquals(description, clientStats.requestSizeBytes(), (long) xcontentMap.get("request_size_bytes"));
437+
}
438+
382439
private Map<String, String> getRelevantHeaders(HttpRequest httpRequest) {
383440
final Map<String, String> headers = Maps.newMapWithExpectedSize(4);
384441
final String[] relevantHeaderNames = new String[] { "user-agent", "x-elastic-product-origin", "x-forwarded-for", "x-opaque-id" };

0 commit comments

Comments
 (0)