From fa11a86905ad03a2f9ac3d8b253897b5947239e4 Mon Sep 17 00:00:00 2001 From: Mijhael Samaniego <67170467+mijhaels@users.noreply.github.com> Date: Mon, 12 Oct 2026 20:15:06 -0300 Subject: [PATCH] feat!: stop logging full HTTP traffic by default TuRuc always added an HttpLoggingInterceptor at BODY level, so every lookup wrote the full request and response, including taxpayer data, to the consumer's logs with no way to turn it off. Default to no logging and add TuRuc.withLogLevel(Level) to opt in. logging-interceptor becomes an api dependency because Level is part of the public API. Co-Authored-By: Claude Opus 5.5 --- CHANGELOG.md | 4 ++ README.md | 23 +++++- build.gradle | 3 +- src/main/java/org/ith/client/TuRuc.java | 25 +++++-- .../java/org/ith/client/TuRucClientTest.java | 72 ++++++++++++++++++- 5 files changed, 117 insertions(+), 10 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index db442c3..692f077 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,10 @@ ## 0.1.0 — Sin publicar +- Cambio incompatible: el cliente deja de registrar por defecto cada petición y + respuesta completas (`HttpLoggingInterceptor.Level.BODY`). El nuevo + `TuRuc.withLogLevel(Level)` activa el logging de OkHttp con el nivel indicado; + `logging-interceptor` pasa a ser dependencia `api`. - Compatibilidad verificada con Java 17, 21 y 25; JDK 25 para compilar por defecto y bytecode Java 17 para consumidores. - Gradle 9.8.1 con wrapper regenerado y SHA-256 de distribución verificado. diff --git a/README.md b/README.md index c399751..870c34f 100644 --- a/README.md +++ b/README.md @@ -61,8 +61,6 @@ var page = client.searchContribuyentes("NUÑEZ", 0).execute(); síncrona o `enqueue()` para una asíncrona. - Crea el cliente una vez y reutilízalo; cada llamada a `getClient()` crea un cliente HTTP nuevo. -- El cliente registra cada petición y respuesta completas, con su cuerpo, en el - log de OkHttp. ### Respuestas @@ -97,6 +95,24 @@ completa. Para cambiarlos, indica los valores en segundos: TuRucClient client = new TuRuc("https://turuc.com.py/", 30, 10, 10, 5).getClient(); ``` +### Logging + +Por defecto, el cliente no registra el tráfico HTTP. Para depurar, indica un +nivel de `HttpLoggingInterceptor` de OkHttp: + +```java +import okhttp3.logging.HttpLoggingInterceptor; + +TuRucClient client = new TuRuc("https://turuc.com.py/") + .withLogLevel(HttpLoggingInterceptor.Level.BODY) + .getClient(); +``` + +`BASIC` registra la petición y el estado de la respuesta; `HEADERS` agrega las +cabeceras; `BODY` también registra los cuerpos, con los datos de los +contribuyentes consultados. Los mensajes van al log de OkHttp +(`java.util.logging`, logger `okhttp3.OkHttpClient`). + ## Migración desde 0.0.1-beta La versión 0.1.0 cambia el modelo `Contribuyente` para reflejar la respuesta @@ -122,6 +138,9 @@ si tu proyecto lo usa, decláralo por tu cuenta. Las dependencias transitivas pasan a Retrofit 3.0.0 y OkHttp 5.5.0. +El cliente ya no registra las peticiones y respuestas completas. Si dependías de +esos logs, actívalos con `withLogLevel(HttpLoggingInterceptor.Level.BODY)`. + ## Cambios Consulta [CHANGELOG.md](CHANGELOG.md) para conocer los cambios de cada versión. diff --git a/build.gradle b/build.gradle index f0e682b..f65be9e 100644 --- a/build.gradle +++ b/build.gradle @@ -29,7 +29,8 @@ dependencies { api(platform("com.squareup.okhttp3:okhttp-bom:5.5.0")) implementation("com.squareup.okhttp3:okhttp") - implementation("com.squareup.okhttp3:logging-interceptor") + // HttpLoggingInterceptor.Level forma parte de la API pública (TuRuc.withLogLevel). + api("com.squareup.okhttp3:logging-interceptor") testImplementation(platform("org.junit:junit-bom:6.1.3")) testImplementation("org.junit.jupiter:junit-jupiter") diff --git a/src/main/java/org/ith/client/TuRuc.java b/src/main/java/org/ith/client/TuRuc.java index 7649846..b04c781 100644 --- a/src/main/java/org/ith/client/TuRuc.java +++ b/src/main/java/org/ith/client/TuRuc.java @@ -1,6 +1,7 @@ package org.ith.client; import com.alibaba.fastjson2.support.retrofit.Retrofit2ConverterFactory; +import java.util.Objects; import java.util.concurrent.TimeUnit; import okhttp3.OkHttpClient; import okhttp3.logging.HttpLoggingInterceptor; @@ -15,6 +16,7 @@ public class TuRuc { private final int writeTimeout; private final int readTimeout; private final int connectTimeout; + private HttpLoggingInterceptor.Level logLevel = HttpLoggingInterceptor.Level.NONE; public TuRuc(String apiUrl) { this.apiUrl = apiUrl; @@ -32,17 +34,28 @@ public TuRuc(String apiUrl, int callTimeout, int writeTimeout, int readTimeout, this.connectTimeout = connectTimeout; } + /** + * Registra el tráfico HTTP en el log de OkHttp. Por defecto no se registra nada; {@code BODY} incluye los + * datos de los contribuyentes consultados. + */ + public TuRuc withLogLevel(HttpLoggingInterceptor.Level logLevel) { + this.logLevel = Objects.requireNonNull(logLevel, "logLevel"); + return this; + } + public TuRucClient getClient() { - HttpLoggingInterceptor interceptor = new HttpLoggingInterceptor(); - interceptor.setLevel(HttpLoggingInterceptor.Level.BODY); // Http Client - OkHttpClient client = new OkHttpClient.Builder() - .addInterceptor(interceptor) + OkHttpClient.Builder builder = new OkHttpClient.Builder() .callTimeout(callTimeout, TimeUnit.SECONDS) .writeTimeout(writeTimeout, TimeUnit.SECONDS) .readTimeout(readTimeout, TimeUnit.SECONDS) - .connectTimeout(connectTimeout, TimeUnit.SECONDS) - .build(); + .connectTimeout(connectTimeout, TimeUnit.SECONDS); + if (logLevel != HttpLoggingInterceptor.Level.NONE) { + HttpLoggingInterceptor interceptor = new HttpLoggingInterceptor(); + interceptor.setLevel(logLevel); + builder.addInterceptor(interceptor); + } + OkHttpClient client = builder.build(); // Retrofit Retrofit retrofit = new Retrofit.Builder() .baseUrl(apiUrl) diff --git a/src/test/java/org/ith/client/TuRucClientTest.java b/src/test/java/org/ith/client/TuRucClientTest.java index 053152e..580409f 100644 --- a/src/test/java/org/ith/client/TuRucClientTest.java +++ b/src/test/java/org/ith/client/TuRucClientTest.java @@ -14,11 +14,18 @@ import java.net.URI; import java.net.URLDecoder; import java.nio.charset.StandardCharsets; +import java.util.ArrayList; import java.util.Arrays; +import java.util.List; import java.util.Map; import java.util.concurrent.BlockingQueue; import java.util.concurrent.LinkedBlockingQueue; +import java.util.logging.Handler; +import java.util.logging.LogRecord; +import java.util.logging.Logger; import java.util.stream.Collectors; +import okhttp3.OkHttpClient; +import okhttp3.logging.HttpLoggingInterceptor; import org.ith.model.Contribuyente; import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; @@ -35,7 +42,7 @@ class TuRucClientTest { @BeforeEach void startServer() throws IOException { server = HttpServer.create(new InetSocketAddress("127.0.0.1", 0), 0); - client = new TuRuc("http://127.0.0.1:" + server.getAddress().getPort() + "/", 5, 5, 5, 5).getClient(); + client = new TuRuc(baseUrl(), 5, 5, 5, 5).getClient(); } @AfterEach @@ -43,6 +50,37 @@ void stopServer() { server.stop(0); } + @Test + void doesNotLogRequestsByDefault() throws Exception { + respond(200, """ + {"data":{"doc":80024191,"ruc":"80024191-6"},"message":"OK"} + """); + + var logs = captureOkHttpLogs(() -> client.getContribuyente("80024191-6").execute()); + + assertEquals(List.of(), logs); + } + + @Test + void logsResponseBodyWhenRequested() throws Exception { + respond(200, """ + {"data":{"doc":80024191,"ruc":"80024191-6"},"message":"OK"} + """); + var loggingClient = new TuRuc(baseUrl(), 5, 5, 5, 5) + .withLogLevel(HttpLoggingInterceptor.Level.BODY) + .getClient(); + + var logs = captureOkHttpLogs( + () -> loggingClient.getContribuyente("80024191-6").execute()); + + assertTrue(logs.stream().anyMatch(line -> line.contains("\"ruc\":\"80024191-6\"")), logs.toString()); + } + + @Test + void rejectsNullLogLevel() { + assertThrows(NullPointerException.class, () -> new TuRuc(baseUrl()).withLogLevel(null)); + } + @Test void looksUpContributorWithFullRuc() throws Exception { respond(200, """ @@ -156,6 +194,38 @@ private Map queryParameters(URI uri) { pair -> URLDecoder.decode(pair[1], StandardCharsets.UTF_8))); } + private String baseUrl() { + return "http://127.0.0.1:" + server.getAddress().getPort() + "/"; + } + + private interface Call { + void run() throws IOException; + } + + private static List captureOkHttpLogs(Call call) throws IOException { + var logger = Logger.getLogger(OkHttpClient.class.getName()); + var lines = new ArrayList(); + var handler = new Handler() { + @Override + public void publish(LogRecord logRecord) { + lines.add(logRecord.getMessage()); + } + + @Override + public void flush() {} + + @Override + public void close() {} + }; + logger.addHandler(handler); + try { + call.run(); + } finally { + logger.removeHandler(handler); + } + return lines; + } + private void respond(int status, String json) { server.createContext("/", exchange -> { requests.add(exchange.getRequestURI());