Logging with Retrofit 2

2018-12-31 20:34发布

问题:

I\'m trying to get the exact JSON that is being sent in the request. Here is my code:

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();

But I only see this in the logs:

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

How am I supposed to do proper logging, given the removal of setLog() and setLogLevel() which we used to use with Retrofit 1?

回答1:

In Retrofit 2 you should use HttpLoggingInterceptor.

Add dependency to build.gradle:

compile \'com.squareup.okhttp3:logging-interceptor:3.9.1\'

Create a Retrofit object like the following:

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);

The above solution gives you logcat messages very similar to the old ones set by

setLogLevel(RestAdapter.LogLevel.FULL)

In case of java.lang.ClassNotFoundException:

Older Retrofit version might require an older logging-interceptor version. Take a look at comments sections for details.



回答2:

I met the thing as you and I tried to ask the author of the book Retrofit: Love working with APIs on Android (here is the link) (nope! I am not making some ads for them....but they are really nice guys :) And the author replied to me very soon, with both Log method on Retrofit 1.9 and Retrofit 2.0-beta.

And here is the code of 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();

This is how to add logging method with the help of HttpLoggingInterceptor. Also if you are the reader of that book I mentioned above, you may find that it says there is not log method with Retrofit 2.0 anymore -- which, I had asked the author, is not correct and they will update the book next year talking about it.

// In case you are not that familiar with the Log method in Retrofit, I would like to share something more.

Also should be noticed that there are some Logging Levels you could pick. I use the Level.BODY most of the time, which will give some thing like this:

\"enter

You can find almost all the http staff inside the picture: the header, the content and response, etc.

And sometimes you really don\'t need all the guests to attend your party: I just want to know whether it\'s successfully connected, that internet call is successfully made within my Activiy & Fragmetn. Then you are free to use Level.BASIC, which will return something like this:

\"enter

Can you find the status code 200 OK inside? That is it :)

Also there is another one, Level.HEADERS, which will only return the header of the network. Ya of course another picture here:

\"enter

That\'s all of the Logging trick ;)

And I would like to share you with the tutorial I learned a lot there. They have a bunch of great post talking about almost everything related to Retrofit, and they are continuing updating the post, at the same time Retrofit 2.0 is coming. Please take a look at those work, which I think will save you lots of time.



回答3:

Here is an Interceptor that logs both the request and response bodies (using Timber, based on an example from the OkHttp docs and some other SO answers):

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\";
        }
    }
}


回答4:

Try this:

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

After this, in the body there is the JSON you are interested in.



回答5:

I don\'t know if setLogLevel() will return in the final 2.0 version of Retrofit but for now you can use an interceptor for logging.

A good example can found in 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();


回答6:

For those who need high level logging in Retrofit, use the interceptor like this

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\";
    }
}`

Courtesy: https://github.com/square/retrofit/issues/1072#



回答7:

The main problem which I faced was dynamical adding headers and logging them into debug logcat. I\'ve tried to add two interceptors. One for logging and one for adding headers on-the-go (token authorization). The problem was that we may .addInterceptor or .addNetworkInterceptor. As Jake Wharton said to me: \"Network interceptors always come after application interceptors. See https://github.com/square/okhttp/wiki/Interceptors\". So here is working example with headers and logs:

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();


回答8:

If you are using Retrofit2 and okhttp3 then you need to know that Interceptor works by queue. So add loggingInterceptor at the end, after your other Interceptors:

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();


回答9:

You can also add Facebook\'s Stetho and look at the network traces in Chrome: http://facebook.github.io/stetho/

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

Then open \"chrome://inspect\" in Chrome...



回答10:

A best way to do this right in Retrofit 2 is to add the logger interceptor as a networkInterceptor this will print out the network headers and your custom headers too. The important thing is to remember that interceptor work as a stack and be sure u add the logger at the end of all.

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());


回答11:

Most of the answer here covers almost everything except this tool, one of the coolest ways to see the log.

It is Facebook\'s Stetho. This is the superb tool to monitor/log your app\'s network traffic on google chrome. You can also find here on Github.

\"enter



回答12:

I found way for Print Log in 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);

Works for me.



回答13:

for Retrofit 2.0.2 the code is like

   **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();
        }


回答14:

Kotlin Code

        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)


回答15:

this will create a retrofit object with Logging. without creating separate objects.

 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();


回答16:

hey guys,i already find solution:

  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);
}