Interceptors That Actually Help: Request Logging and Automatic Bearer-Token Injection
Cross-cutting HTTP concerns — logging, authentication headers, request IDs, metrics — should never be hand-edited into every request. JQuickCurl rides on OkHttp’s mature interceptor pipeline, and because interceptors are wired into the global configuration, a single registration upgrades every curl command in your application.
In this post:
What an interceptor sees and can do.
The built-in
JLoggingInterceptor.Writing a request/response logging interceptor.
Auto-injecting a
Bearertoken from your secret store.
The Interceptor Model in One Paragraph
An OkHttp Interceptor wraps a call: it receives the outgoing Request, may modify it, calls chain.proceed(...), and then sees the returned Response. Registering interceptors happens on the global config, so both annotation-mode and XML-mode commands pass through them.
Interceptor myInterceptor = chain -> {
Request request = chain.request(); // outgoing
Response response = chain.proceed(request); // perform + get response
return response;
};
JQuickCurlConfig.getInstance().addInterceptor(myInterceptor);
Enter fullscreen mode Exit fullscreen mode
addNetworkInterceptor(...) registers at the network layer (after redirects/retries) when you need to observe the real wire traffic.
Built-In Logging: JLoggingInterceptor
Out of the box, the config already registers a JLoggingInterceptor at level ALL — it measures each call and reports the elapsed time (and failures) through its console logger. If you want a quieter default, the same interceptor can be constructed with an explicit level:
public enum JCurlLevelLog {
NONE, BASIC, HEADERS, ALL
}
Enter fullscreen mode Exit fullscreen mode
import com.github.paohaijiao.enums.JCurlLevelLog;
import com.github.paohaijiao.interceptor.JLoggingInterceptor;
import com.github.paohaijiao.config.JQuickCurlConfig;
JQuickCurlConfig.getInstance()
.addInterceptor(new JLoggingInterceptor(JCurlLevelLog.BASIC));
Enter fullscreen mode Exit fullscreen mode
Expect output in the spirit of: the request cost : 132 ms. Choose the noisiest level in development, BASIC in staging, and keep an eye on token redaction (see below).
Writing a Purpose-Built Logging Interceptor
Build one that logs method, URL, status, and duration — and be deliberate about what you don’t log (authorization headers!).
import okhttp3.Interceptor;
import okhttp3.Request;
import okhttp3.Response;
import okhttp3.ResponseBody;
public class CallLogInterceptor implements Interceptor {
private static final org.slf4j.Logger log =
org.slf4j.LoggerFactory.getLogger(CallLogInterceptor.class);
@Override
public Response intercept(Chain chain) throws java.io.IOException {
Request request = chain.request();
long start = System.nanoTime();
Response response = chain.proceed(request);
long tookMs = java.util.concurrent.TimeUnit.NANOSECONDS.toMillis(
System.nanoTime() - start);
String bodyPreview = "";
if (response.body() != null) {
bodyPreview = safePreview(response.peekBody(256).string());
}
log.info("[http] {} {} -> {} in {} ms body={}",
request.method(), request.url(), response.code(), tookMs, bodyPreview);
return response;
}
private String safePreview(String s) {
return s == null ? "" : s.replace('\n', ' ').substring(
0, Math.min(120, s.length()));
}
}
Enter fullscreen mode Exit fullscreen mode
Register it next to the built-in one:
JQuickCurlConfig.getInstance().addInterceptor(new CallLogInterceptor());
Enter fullscreen mode Exit fullscreen mode
Automatic Bearer-Token Injection
Tokens expire, rotate, and come from secret managers — they don’t belong in @JCurlCommand strings. One interceptor can stamp every outgoing request with the current token:
import okhttp3.Interceptor;
import okhttp3.Request;
import okhttp3.Response;
public class BearerAuthInterceptor implements Interceptor {
private final java.util.function.Supplier<String> tokenSupplier;
public BearerAuthInterceptor(java.util.function.Supplier<String> tokenSupplier) {
this.tokenSupplier = tokenSupplier;
}
@Override
public Response intercept(Chain chain) throws java.io.IOException {
String token = tokenSupplier.get();
Request request = chain.request().newBuilder()
.addHeader("Authorization", "Bearer " + token)
.build();
return chain.proceed(request);
}
}
Enter fullscreen mode Exit fullscreen mode
Because the token is fetched per request via a Supplier, rotating tokens are picked up automatically — no restart, no stale header. Wire it with your vault/environment source:
import com.github.paohaijiao.config.JQuickCurlConfig;
JQuickCurlConfig.getInstance().addInterceptor(
new BearerAuthInterceptor(() -> System.getenv("API_TOKEN")));
Enter fullscreen mode Exit fullscreen mode
Now a plain command stays clean:
@JCurlCommand("curl -X GET 'https://api.example.com/me'")
String me(JQuickCurlReq request); // Authorization stamped by interceptor
Enter fullscreen mode Exit fullscreen mode
The same trick works for API keys, X-Correlation-Id, tenant headers, and basic-auth pairs (Post 15).
Runnable Demo
import com.github.paohaijiao.anno.JCurlCommand;
import com.github.paohaijiao.config.JQuickCurlConfig;
import com.github.paohaijiao.domain.req.JQuickCurlReq;
import com.github.paohaijiao.executor.JCurlInvoker;
public interface InspectApi {
// httpbin.org/headers echoes every header the server received
@JCurlCommand("curl -X GET 'https://httpbin.org/headers'")
String headers(JQuickCurlReq request);
}
class InterceptorDemo {
public static void main(String[] args) {
JQuickCurlConfig.getInstance().addInterceptor(chain -> {
okhttp3.Request r = chain.request().newBuilder()
.addHeader("Authorization", "Bearer demo-token-123")
.build();
return chain.proceed(r);
});
String echo = JCurlInvoker.createProxy(InspectApi.class)
.headers(new JQuickCurlReq());
System.out.println(echo); // "Authorization": "Bearer demo-token-123"
}
}
Enter fullscreen mode Exit fullscreen mode
Ordering and Pitfalls
Interceptors run in registration order (application interceptors before network interceptors).
Headers you add in an interceptor are applied on top of whatever the curl string specified.
Never log or stash raw
Authorizationvalues — hash or redact in logs.Keep interceptors fast; they run on the request thread.
Summary
Interceptors are the clean seam for concerns that cut across every HTTP call. JQuickCurl comes with timing-oriented logging out of the box and accepts any OkHttp interceptor through JQuickCurlConfig, so adding per-request Bearer tokens, correlation IDs, or metrics is a five-line change — while your curl commands stay declarative and secret-free.
Repository: dromara/jquick-curl. Post 13 shows per-method timeout overrides with @JTimeout when one stubborn endpoint needs different limits than the rest.