Inicio de sesión con Retrofit 2


308

Estoy tratando de obtener el JSON exacto que se envía en la solicitud. Aquí está mi código:

OkHttpClient client = new OkHttpClient();
client.interceptors().add(new Interceptor(){
   @Override public com.squareup.okhttp.Response intercept(Chain chain) throws IOException {
      Request request = chain.request();
      Log.e(String.format("\nrequest:\n%s\nheaders:\n%s",
                          request.body().toString(), request.headers()));
      com.squareup.okhttp.Response response = chain.proceed(request);
      return response;
   }
});
Retrofit retrofit = new Retrofit.Builder()
   .baseUrl(API_URL)
   .addConverterFactory(GsonConverterFactory.create())
   .client(client).build();

Pero solo veo esto en los registros:

request:
com.squareup.okhttp.RequestBody$1@3ff4074d
headers:
Content-Type: application/vnd.ll.event.list+json

¿Cómo se supone que debo hacer un registro adecuado, dada la eliminación setLog()y setLogLevel()que solíamos usar con Retrofit 1?

Respuestas:


699

En Retrofit 2 debe usar HttpLoggingInterceptor .

Agregar dependencia a build.gradle. La última versión a partir de octubre de 2019 es:

implementation 'com.squareup.okhttp3:logging-interceptor:4.2.1'

Cree un Retrofitobjeto como el siguiente:

HttpLoggingInterceptor interceptor = new HttpLoggingInterceptor();
interceptor.setLevel(HttpLoggingInterceptor.Level.BODY);
OkHttpClient client = new OkHttpClient.Builder().addInterceptor(interceptor).build();

Retrofit retrofit = new Retrofit.Builder()
        .baseUrl("https://backend.example.com")
        .client(client)
        .addConverterFactory(GsonConverterFactory.create())
        .build();

return retrofit.create(ApiClient.class);

En caso de advertencias de desaprobación, simplemente cambie setLevela:

interceptor.level(HttpLoggingInterceptor.Level.BODY);

La solución anterior le brinda mensajes logcat muy similares a los antiguos establecidos por

setLogLevel(RestAdapter.LogLevel.FULL)

En caso dejava.lang.ClassNotFoundException :

La versión anterior de Retrofit puede requerir una logging-interceptorversión anterior . Echa un vistazo a las secciones de comentarios para más detalles.


30
Esto se agregó a OkHttp solo 15 días después de mi pregunta, ¡qué bueno que las necesidades de la comunidad tengan un impacto tan rápido!
Gabor

1
y ya no necesita agregar el repositorio de instantáneas de sonatype a su build.gradle
James Goodwin

13
retrofit 2.1.0 use esta compilación 'com.squareup.okhttp3: logging-interceptor: 3.3.1'
jayellos

2
@jayellos Gracias por denotar eso. Si utiliza una versión inferior a la 3.3.1, no obtendrá dicha excepción de método.
guydemossyrock

2
Estoy obteniendo: ¿No encontré la clase "okhttp3.internal.Platform" alguna idea?
corlaez

35

Conocí la cosa como tú y traté de preguntarle al autor del libro Retrofit: Me encanta trabajar con API en Android (aquí está el enlace ) (¡no! No estoy haciendo algunos anuncios para ellos ... pero son realmente agradables chicos :) Y el autor me respondió muy pronto, con el método Log en Retrofit 1.9 y Retrofit 2.0-beta.

Y aquí está el código de Retrofit 2.0-beta:

HttpLoggingInterceptor logging = new HttpLoggingInterceptor();  
// set your desired log level
logging.setLevel(Level.BODY);

OkHttpClient httpClient = new OkHttpClient();  
// add your other interceptors …

// add logging as last interceptor
httpClient.interceptors().add(logging);  // <-- this is the important line!

Retrofit retrofit = new Retrofit.Builder()  
   .baseUrl(API_BASE_URL)
   .addConverterFactory(GsonConverterFactory.create())
   .client(httpClient)
   .build();

Así es como se agrega el método de registro con la ayuda de HttpLoggingInterceptor . Además, si usted es el lector de ese libro que mencioné anteriormente, puede encontrar que dice que ya no hay un método de registro con Retrofit 2.0, que, le pregunté al autor, no es correcto y actualizarán el libro el próximo año hablando al respecto

// En caso de que no esté tan familiarizado con el método Log en Retrofit, me gustaría compartir algo más.

También debe tenerse en cuenta que hay algunos niveles de registro que puede elegir. Yo uso el Level.BODY la mayor parte del tiempo, lo que me dará algo como esto:

ingrese la descripción de la imagen aquí

Puede encontrar casi todo el personal de http dentro de la imagen: el encabezado, el contenido y la respuesta, etc.

Y a veces realmente no necesitas que todos los invitados asistan a tu fiesta: solo quiero saber si está conectado correctamente, esa llamada de Internet se realizó con éxito dentro de mi Activiy & Fragmetn. Entonces eres libre de usar Level.BASIC , que devolverá algo como esto:

ingrese la descripción de la imagen aquí

¿Puedes encontrar el código de estado? 200 OK dentro? Eso es :)

También hay otro, Level.HEADERS , que solo devolverá el encabezado de la red. Ya, por supuesto, otra foto aquí:

ingrese la descripción de la imagen aquí

Ese es todo el truco de registro;)

Y me gustaría compartirlo con el tutorial que aprendí mucho allí . Tienen un montón de excelentes publicaciones que hablan sobre casi todo lo relacionado con Retrofit, y continúan actualizando la publicación, al mismo tiempo que viene Retrofit 2.0. Eche un vistazo a esos trabajos, que creo que le ahorrarán mucho tiempo.


¿De dónde se origina HttpLoggingInterceptor?
Radu

55
Además, esto no funciona para mí. httpClient.interceptors.add quiere un com.squareup.okhttp.Interceptor y no un okhttp3.logging.HttpLoggingInterceptor
Radu

@Radu ¿En qué versión de Retrofit estás? Su gradle debería tener algo como esto: compile 'com.squareup.retrofit2: retrofit: 2.0.0-beta4'
peitek

¡No funcionaría para los encabezados de solicitud de registro porque es necesario agregar Interceptor como interceptor de red! Ver: stackoverflow.com/a/36847027/2557258
Yazon2006

12

Aquí hay un Interceptorregistro de los cuerpos de solicitud y respuesta (usando Timber, basado en un ejemplo de los documentos OkHttp y algunas otras respuestas SO):

public class TimberLoggingInterceptor implements Interceptor {
    @Override
    public Response intercept(Chain chain) throws IOException {
        Request request = chain.request();

        long t1 = System.nanoTime();
        Timber.i("Sending request %s on %s%n%s", request.url(), chain.connection(), request.headers());
        Timber.v("REQUEST BODY BEGIN\n%s\nREQUEST BODY END", bodyToString(request));

        Response response = chain.proceed(request);

        ResponseBody responseBody = response.body();
        String responseBodyString = response.body().string();

        // now we have extracted the response body but in the process
        // we have consumed the original reponse and can't read it again
        // so we need to build a new one to return from this method

        Response newResponse = response.newBuilder().body(ResponseBody.create(responseBody.contentType(), responseBodyString.getBytes())).build();

        long t2 = System.nanoTime();
        Timber.i("Received response for %s in %.1fms%n%s", response.request().url(), (t2 - t1) / 1e6d, response.headers());
        Timber.v("RESPONSE BODY BEGIN:\n%s\nRESPONSE BODY END", responseBodyString);

        return newResponse;
    }

    private static String bodyToString(final Request request){

        try {
            final Request copy = request.newBuilder().build();
            final Buffer buffer = new Buffer();
            copy.body().writeTo(buffer);
            return buffer.readUtf8();
        } catch (final IOException e) {
            return "did not work";
        }
    }
}

6

Prueba esto:

Request request = chain.request();
Buffer buffer = new Buffer();
request.body().writeTo(buffer);
String body = buffer.readUtf8();

Después de esto, en el bodyhay el JSON que le interesa.


1
¿Cómo puede imprimir la respuesta del cuerpo?
Gilberto Ibarra

3
Uso de @GilbertoIbarra String bodyString = response.body().string(); log(bodyString); response = response.newBuilder().body(ResponseBody.create(response.body().contentType(), bodyString)).build(); (No puede leer el cuerpo de la respuesta más de una vez, por lo que tendrá que crear una nueva respuesta con el constructor)
Anton Ryabyh

Es una muleta que no necesitamos más. Aquí hay un ejemplo de registro de trabajo: stackoverflow.com/a/36847027/2557258
Yazon2006 el

6

El principal problema que enfrenté fue agregar encabezados dinámicamente y registrarlos en debug logcat. Intenté agregar dos interceptores. Uno para iniciar sesión y otro para agregar encabezados sobre la marcha (autorización de token). El problema es que podemos .addInterceptor o .addNetworkInterceptor. Como Jake Wharton me dijo: "Los interceptores de red siempre vienen después de los interceptores de aplicación. Ver https://github.com/square/okhttp/wiki/Interceptors ". Así que aquí hay un ejemplo de trabajo con encabezados y registros:

OkHttpClient httpClient = new OkHttpClient.Builder()
            //here we can add Interceptor for dynamical adding headers
            .addNetworkInterceptor(new Interceptor() {
                @Override
                public Response intercept(Chain chain) throws IOException {
                    Request request = chain.request().newBuilder().addHeader("test", "test").build();
                    return chain.proceed(request);
                }
            })
            //here we adding Interceptor for full level logging
            .addNetworkInterceptor(new HttpLoggingInterceptor().setLevel(HttpLoggingInterceptor.Level.BODY))
            .build();

    Retrofit retrofit = new Retrofit.Builder()
            .addConverterFactory(GsonConverterFactory.create(gsonBuilder.create()))
            .addCallAdapterFactory(RxJavaCallAdapterFactory.create())
            .client(httpClient)
            .baseUrl(AppConstants.SERVER_ADDRESS)
            .build();

5

No sé si setLogLevel () volverá en la versión 2.0 final de Retrofit, pero por ahora puede usar un interceptor para iniciar sesión.

Un buen ejemplo se puede encontrar en OkHttp wiki: https://github.com/square/okhttp/wiki/Interceptors

OkHttpClient client = new OkHttpClient();
client.interceptors().add(new LoggingInterceptor());

Retrofit retrofit = new Retrofit.Builder()
        .baseUrl("http://www.yourjsonapi.com")
        .addConverterFactory(GsonConverterFactory.create())
        .client(client)
        .build();

o no leyó mi pregunta o no leyó la página a la que está vinculando ... porque LoggingInterceptor no intenta registrar el cuerpo de la solicitud (y por lo tanto no resuelve mi problema)
Gabor

Me sale 'UnsupportedOperationException'
Choletski

5

Si está utilizando Retrofit2 y okhttp3, debe saber que Interceptor funciona en cola. Entonces agregue loggingInterceptor al final, después de sus otros interceptores:

HttpLoggingInterceptor loggingInterceptor = new HttpLoggingInterceptor();
        if (BuildConfig.DEBUG)
            loggingInterceptor.setLevel(HttpLoggingInterceptor.Level.HEADERS);

 new OkHttpClient.Builder()
                .connectTimeout(60, TimeUnit.SECONDS)
                .readTimeout(60, TimeUnit.SECONDS)
                .writeTimeout(60, TimeUnit.SECONDS)
                .addInterceptor(new CatalogInterceptor(context))//first
                .addInterceptor(new OAuthInterceptor(context))//second
                .authenticator(new BearerTokenAuthenticator(context))
                .addInterceptor(loggingInterceptor)//third, log at the end
                .build();

4

Para aquellos que necesitan un registro de alto nivel en Retrofit, use el interceptor de esta manera

public static class LoggingInterceptor implements Interceptor {
    @Override public Response intercept(Chain chain) throws IOException {
        Request request = chain.request();
        long t1 = System.nanoTime();
        String requestLog = String.format("Sending request %s on %s%n%s",
                request.url(), chain.connection(), request.headers());
        //YLog.d(String.format("Sending request %s on %s%n%s",
        //        request.url(), chain.connection(), request.headers()));
        if(request.method().compareToIgnoreCase("post")==0){
            requestLog ="\n"+requestLog+"\n"+bodyToString(request);
        }
        Log.d("TAG","request"+"\n"+requestLog);

        Response response = chain.proceed(request);
        long t2 = System.nanoTime();

        String responseLog = String.format("Received response for %s in %.1fms%n%s",
                response.request().url(), (t2 - t1) / 1e6d, response.headers());

        String bodyString = response.body().string();

        Log.d("TAG","response"+"\n"+responseLog+"\n"+bodyString);

        return response.newBuilder()
                .body(ResponseBody.create(response.body().contentType(), bodyString))
                .build();
        //return response;
    }
}

public static String bodyToString(final Request request) {
    try {
        final Request copy = request.newBuilder().build();
        final Buffer buffer = new Buffer();
        copy.body().writeTo(buffer);
        return buffer.readUtf8();
    } catch (final IOException e) {
        return "did not work";
    }
}`

Cortesía : https://github.com/square/retrofit/issues/1072#


debería haber mencionado que copió de github.com/square/retrofit/issues/1072# (para dar crédito a su fuente)
Gabor

Es una muleta que no necesitamos más. Aquí hay un ejemplo de registro de trabajo: stackoverflow.com/a/36847027/2557258
Yazon2006 el

4

Código Kotlin

        val interceptor = HttpLoggingInterceptor()
        interceptor.level = HttpLoggingInterceptor.Level.BODY
        val client = OkHttpClient.Builder().addInterceptor(interceptor).build()
        val retrofit = Retrofit.Builder()
                .baseUrl(BASE_URL)
                .client(client)
                .addConverterFactory(GsonConverterFactory.create())
                .build()

        return retrofit.create(PointApi::class.java)

2

También puede agregar Stetho de Facebook y ver los rastros de red en Chrome: http://facebook.github.io/stetho/

final OkHttpClient.Builder builder = new OkHttpClient.Builder();
if (BuildConfig.DEBUG) {
    builder.networkInterceptors().add(new StethoInterceptor());
}

Luego abra "chrome: // inspeccionar" en Chrome ...


2

esto creará un objeto de actualización con Logging. sin crear objetos separados.

 private static final Retrofit retrofit = new Retrofit.Builder()
            .baseUrl(BASE_URL)
            .client(new OkHttpClient().newBuilder()
                    .addInterceptor(new HttpLoggingInterceptor().setLevel(HttpLoggingInterceptor.Level.BODY))
                    .readTimeout(READ_TIMEOUT_SECONDS, TimeUnit.SECONDS)
                    .writeTimeout(WRITE_TIMEOUT_SECONDS, TimeUnit.SECONDS)
                    .connectTimeout(CONNECTION_TIMEOUT_SECONDS, TimeUnit.SECONDS)
                    .build())
            .addConverterFactory(GsonConverterFactory.create())
            .build();

2

Primero agregue dependencia a build.gradle:

implementación 'com.squareup.okhttp3: logging-interceptor: 3.12.1'

Mientras usa Kotlin, puede agregar Logging Interceptor de esta manera:

companion object {
    val okHttpClient = OkHttpClient().newBuilder()
            .addInterceptor(HttpLoggingInterceptor().apply {
                level = HttpLoggingInterceptor.Level.BODY
            })
            .build()


    fun getRetrofitInstance(): Retrofit {
        val retrofit = Retrofit.Builder()
                .client(okHttpClient)
                .baseUrl(ScanNShopConstants.BASE_URL)
                .addCallAdapterFactory(RxJava2CallAdapterFactory.create())
                .addConverterFactory(GsonConverterFactory.create())
                .build()

        return retrofit
    }
}

2

El siguiente conjunto de código funciona sin ningún problema para mí.

Gradle

// Retrofit
implementation 'com.squareup.retrofit2:retrofit:2.5.0'
implementation 'com.squareup.retrofit2:converter-gson:2.5.0'
implementation 'com.squareup.okhttp3:logging-interceptor:3.12.1'

RetrofitClient

HttpLoggingInterceptor logging = new HttpLoggingInterceptor();
logging.setLevel(HttpLoggingInterceptor.Level.BODY);

OkHttpClient client = new OkHttpClient.Builder()
        .addInterceptor(logging)
        .build();

retrofit = new Retrofit.Builder()
        .baseUrl(BASE_URL)
        .addConverterFactory(GsonConverterFactory.create())
        .client(client)
        .build();

También se pueden verificar los resultados yendo a la pestaña Profiler en la parte inferior de Android Studio, luego haciendo clic en + signo para Iniciar una nueva sesión, y luego seleccione el pico deseado en "Red". Allí puede obtener todo, pero es engorroso y lento. Por favor vea la imagen a continuación.

ingrese la descripción de la imagen aquí


1

Una mejor manera de hacer esto correctamente en Retrofit 2 es agregar el interceptor del registrador como un interceptor de red, esto también imprimirá los encabezados de red y sus encabezados personalizados. Lo importante es recordar que el interceptor funciona como una pila y asegúrese de agregar el registrador al final de todo.

OkHttpClient.Builder builder = new OkHttpClient.Builder();
builder.addInterceptor(new MyCustomInterceptor());
builder.connectTimeout(60, TimeUnit.SECONDS);
builder.readTimeout(60, TimeUnit.SECONDS);
builder.writeTimeout(60, TimeUnit.SECONDS);
// important line here
builder.addNetworkInterceptor(LoggerInterceptor());

1

La mayor parte de la respuesta aquí cubre casi todo excepto esta herramienta, una de las mejores maneras de ver el registro.

Es Stetho de Facebook . Esta es la excelente herramienta para monitorear / registrar el tráfico de red de su aplicación en Google Chrome . También puedes encontrar aquí en Github.

ingrese la descripción de la imagen aquí


1

para Retrofit 2.0.2 el código es como

   **HttpLoggingInterceptor logging = new HttpLoggingInterceptor();
        logging.setLevel(HttpLoggingInterceptor.Level.BODY);
        OkHttpClient.Builder httpClient=new OkHttpClient.Builder();
        httpClient.addInterceptor(logging);**


        if (retrofit == null) {
            retrofit = new Retrofit.Builder()
                    .baseUrl(BASE_URL)
                    .addConverterFactory(GsonConverterFactory.create())
                    **.client(httpClient.build())**
                    .build();
        }

1

Aquí hay una manera simple de filtrar cualquier parámetro de solicitud / respuesta de los registros mediante HttpLoggingInterceptor:

// Request patterns to filter
private static final String[] REQUEST_PATTERNS = {
    "Content-Type",
};
// Response patterns to filter
private static final String[] RESPONSE_PATTERNS = {"Server", "server", "X-Powered-By", "Set-Cookie", "Expires", "Cache-Control", "Pragma", "Content-Length", "access-control-allow-origin"};

// Log requests and response
HttpLoggingInterceptor interceptor = new HttpLoggingInterceptor(new HttpLoggingInterceptor.Logger() {
    @Override
    public void log(String message) {

        // Blacklist the elements not required
        for (String pattern: REQUEST_PATTERNS) {
            if (message.startsWith(pattern)) {
                return;
            }
        }
        // Any response patterns as well...
        for (String pattern: RESPONSE_PATTERNS) {
            if (message.startsWith(pattern)) {
                return;
            }
        }
        Log.d("RETROFIT", message);
    }
});
interceptor.setLevel(HttpLoggingInterceptor.Level.BODY);

Aquí está la esencia completa:

https://gist.github.com/mankum93/179c2d5378f27e95742c3f2434de7168


1

También estaba atrapado en una situación similar, el setLevel()método no llegaba, cuando intentaba llamarlo con la instancia de HttpLoggingInterceptor, así:

HttpLoggingInterceptor loggingInterceptor = new HttpLoggingInterceptor();
loggingInterceptor.setLevel(HttpLoggingInterceptor.Level.BODY);

Así es como lo resolví, para generar el registro de Retrofit2,

Supongo que has agregado la dependencia,

implementation "com.squareup.okhttp3:logging-interceptor:4.7.2"

Para ver la última versión, puede consultar este enlace:

https://github.com/square/okhttp/tree/master/okhttp-logging-interceptor )

Aquí también explicaron cómo agregar.

Creé una clase con nombre AddLoggingInterceptor, aquí está mi código,

public class AddLoggingInterceptor {

    public static OkHttpClient setLogging(){
        HttpLoggingInterceptor loggingInterceptor = new HttpLoggingInterceptor();
        loggingInterceptor.setLevel(HttpLoggingInterceptor.Level.BODY);

        OkHttpClient okHttpClient = new OkHttpClient.Builder()
                .addInterceptor(loggingInterceptor)
                .build();

        return okHttpClient;
    }
}

Entonces, donde estamos creando instancias de nuestra actualización,

 public static Retrofit getRetrofitInstance() {
    if (retrofit == null) {
        retrofit = new retrofit2.Retrofit.Builder()
                .baseUrl(BASE_URL)
                .addConverterFactory(GsonConverterFactory.create())
                .client(AddLoggingInterceptor.setLogging()) // here the method is called inside client() method, with the name of class, since it is a static method.
                .build();
    }
    return retrofit;
}

Ahora puede ver el registro generado en su Android Studio, puede que necesite buscar, okHttppara el proceso de filtrado. Funcionó para mi. Si tiene algún problema, puede enviarme un mensaje de texto aquí.


0

Encontré el camino para Imprimir Iniciar sesión Retrofit

OkHttpClient okHttpClient = new OkHttpClient.Builder()
            .addInterceptor(new Interceptor() {
                @Override
                public Response intercept(Chain chain) throws IOException {
                    Request request = chain.request();
                    if (BuildConfig.DEBUG) {
                        Log.e(getClass().getName(), request.method() + " " + request.url());
                        Log.e(getClass().getName(), "" + request.header("Cookie"));
                        RequestBody rb = request.body();
                        Buffer buffer = new Buffer();
                        if (rb != null)
                            rb.writeTo(buffer);
                        LogUtils.LOGE(getClass().getName(), "Payload- " + buffer.readUtf8());
                    }
                    return chain.proceed(request);
                }
            })
            .readTimeout(60, TimeUnit.SECONDS)
            .connectTimeout(60, TimeUnit.SECONDS)
            .build();

            iServices = new Retrofit.Builder()
                    .baseUrl("Your Base URL")
                    .client(okHttpClient)
                    .addConverterFactory(GsonConverterFactory.create())
                    .build()
                    .create(Your Service Interface .class);

Funciona para mi.


0

El interceptor de Retrofit es una gran característica que le permite trabajar con solicitudes http. Hay dos tipos de ellos: aplicaciones e interceptores de red.

Recomendaría usarlo Charles Web Debugging Proxy Applicationsi necesita registrar sus solicitudes / respuestas. La salida es muy similar a Stetho pero es un instrumento más potente que no necesita agregar como dependencia a una aplicación.


-11

Hola chicos, ya encuentro la solución:

  public static <T> T createApi(Context context, Class<T> clazz, String host, boolean debug) {
    if (singleton == null) {
        synchronized (RetrofitUtils.class) {
            if (singleton == null) {
                RestAdapter.Builder builder = new RestAdapter.Builder();
                builder
                        .setEndpoint(host)
                        .setClient(new OkClient(OkHttpUtils.getInstance(context)))
                        .setRequestInterceptor(RequestIntercepts.newInstance())
                        .setConverter(new GsonConverter(GsonUtils.newInstance()))
                        .setErrorHandler(new ErrorHandlers())
                        .setLogLevel(debug ? RestAdapter.LogLevel.FULL : RestAdapter.LogLevel.NONE)/*LogLevel.BASIC will cause response.getBody().in() close*/
                        .setLog(new RestAdapter.Log() {
                            @Override
                            public void log(String message) {
                                if (message.startsWith("{") || message.startsWith("["))
                                    Logger.json(message);
                                else {
                                    Logger.i(message);
                                }
                            }
                        });
                singleton = builder.build();
            }
        }
    }
    return singleton.create(clazz);
}

La devolución de llamada de setLog puede ingresar cada registro
Vihuela Yao

1
esto está usando la actualización v1, no v2
Gabor
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.