End-to-End Network Performance Monitoring with OkHttp EventListeners
Implementation Strategy
To achieve granular visibility into network operations, developers can leverage the EventListener API provided by OkHttp. This mechanism allows for the interception of specific events during an HTTP request's lifecycle, enabling the collection of metrics such as DNS resolution time, TCP handshake duration, TLS negotiation time, and server processing latency.
Configuration
The OkHttpClient.Builder exposes methods to attach a listener. While a single listener instance can be reused, using a factory is recommended for advanced scenarios where call-specific data needs to be tracked.
OkHttpClient client = new OkHttpClient.Builder()
.eventListenerFactory(new EventListener.Factory() {
@Override public EventListener create(Call call) {
return new NetworkTraceListener();
}
})
.build();
Request Lifecycle Analysis
A standard network call traverses several distinct states. To accurately measure performance, timestamps must be captured at the following transition points:
- callStart: The moment the request is initiated.
- DNS Phase: The interval between
dnsStartanddnsEnd, representing the time required to resolve the domain name. - Connection Phase: Spanning from
connectStarttoconnectEnd. This includes the TCP handshake and, if applicable, the TLS handshake. - Request Submission: The duration taken to send headers and the request body.
- Response Phase: The time spent receiving response headers and the body.
- callEnd: The termination of the request.
Custom Listener Implementation
The following code demonstrates a custom listener that calculates the duration for each critical phase. It utilizes nanosecond precision to ensure high accuracy.
public final class NetworkTraceListener extends EventListener {
private long startTimestamp;
private long dnsStartTimestamp;
private long connectStartTimestamp;
private long secureConnectStartTimestamp;
private long requestEndTimestamp;
private long responseHeaderStartTimestamp;
@Override
public void callStart(Call call) {
startTimestamp = System.nanoTime();
}
@Override
public void dnsStart(Call call, String domainName) {
dnsStartTimestamp = System.nanoTime();
}
@Override
public void dnsEnd(Call call, String domainName, List<InetAddress> addressList) {
long dnsDuration = System.nanoTime() - dnsStartTimestamp;
// Log or store DNS duration
}
@Override
public void connectStart(Call call, InetSocketAddress inetSocketAddress, Proxy proxy) {
connectStartTimestamp = System.nanoTime();
}
@Override
public void secureConnectStart(Call call) {
secureConnectStartTimestamp = System.nanoTime();
}
@Override
public void secureConnectEnd(Call call, Handshake handshake) {
long tlsDuration = System.nanoTime() - secureConnectStartTimestamp;
// Log TLS duration
}
@Override
public void connectEnd(Call call, InetSocketAddress inetSocketAddress, Proxy proxy, Protocol protocol) {
long totalConnectTime = System.nanoTime() - connectStartTimestamp;
// Log total connection duration
}
@Override
public void requestHeadersEnd(Call call, Request request) {
// Headers sent
}
@Override
public void requestBodyEnd(Call call, long byteCount) {
requestEndTimestamp = System.nanoTime();
}
@Override
public void responseHeadersStart(Call call) {
responseHeaderStartTimestamp = System.nanoTime();
long serverProcessingTime = responseHeaderStartTimestamp - requestEndTimestamp;
// Log server processing time
}
@Override
public void responseBodyEnd(Call call, long byteCount) {
long totalDuration = System.nanoTime() - startTimestamp;
// Log total request time
}
}
Calculated Metrics
Based on the captured timestamps, the following key performance indicators (KPIs) can be derived:
- Total Latency:
callEnd - callStart - Waiting Duration:
dnsStart - callStart(Time spent in the queue before network activity begins). - DNS Lookup Time:
dnsEnd - dnsStart - TCP Handshake:
secureConnectStart - connectStart(if HTTPS is used). - TLS Handshake:
secureConnectEnd - secureConnectStart - Network Download Time:
responseBodyEnd - responseHeadersStart
Supplementary Telemetry
Beyond timing, effective monitoring requires contextual data to diagnose root causes. The following parameters should be aggregated alongside timing metrics:
- Resource Identifiers: Request URL, HTTP Method (GET, POST), and HTTP Status Code.
- Connection Details: Proxy usage, target IP address, and resolved DNS list.
- Traffic Analysis: Total upstream and downstream bytes (including retries and redirects).
- Environment: Network type (WiFi, 4G, 5G) and security protocol (TLS version).
- Error Context: Stack traces for exceptions such as
SocketTimeoutExceptionorUnknownHostException.