Retrofit 2 — Log Requests and Responses

Retrofit 1 integrated a log feature for basic request and response debugging. The logging functionality was removed in Retrofit 2, since the required HTTP layer is now completely based on OkHttp. Since many developers asked for logging capabilities in Retrofit 2, the developers of OkHttp added a logging interceptor in release 2.6.0. This post will show you how to add and use the logging interceptor in your Android app in combination with Retrofit 2.


Retrofit Series Overview

  1. Getting Started and Creating an Android Client
  2. Retrofit 2 — Basics of API Description
  3. Retrofit 2 — Creating a Sustainable Android Client
  4. Synchronous and Asynchronous Requests
  5. Send Objects in Request Body
  6. Add Custom Request Header
  7. Retrofit 2 — Manage Request Headers in OkHttp Interceptor
  8. Retrofit 2 — Dynamic Request Headers with @HeaderMap
  9. Multiple Query Parameters of Same Name
  10. Optional Query Parameters
  11. Using the Log Level to Debug Requests
  12. Retrofit 2 — Log Requests and Responses
  13. Retrofit 2 — Enable Logging for Development Builds Only
  14. How to Upload Files to Server
  15. Retrofit 2 — How to Upload Files to Server
  16. Retrofit 2 — How to Upload Multiple Files to Server
  17. Retrofit 2 — Passing Multiple Parts Along a File with @PartMap
  18. Basic Authentication on Android
  19. Token Authentication on Android
  20. OAuth on Android
  21. Retrofit 2 — Hawk Authentication on Android
  22. How to Refresh an Access Token
  23. Retrofit 2 — Upgrade Guide from 1.9
  24. Retrofit 2 — Simple Error Handling
  25. How to use OkHttp 3 with Retrofit 1
  26. Send Data Form-Urlencoded
  27. Send Data Form-Urlencoded Using FieldMap
  28. Retrofit 2 — How to Add Query Parameters to Every Request
  29. Retrofit 2 — Add Multiple Query Parameter With QueryMap
  30. Retrofit 2 — How to Use Dynamic Urls for Requests
  31. Retrofit 2 — URL Handling, Resolution and Parsing
  32. Retrofit 2 — Constant, Default and Logic Values for POST and PUT Requests
  33. Retrofit 2 — How to Download Files from Server
  34. Retrofit 2 — Cancel Requests
  35. Retrofit 2 — Reuse and Analyze Requests
  36. Retrofit 2 — How to Change API Base Url at Runtime
  37. Optional Path Parameters
  38. Retrofit 2 — How to Send Plain Text Request Body
  39. Retrofit 2 — Pagination Using Query Parameter
  40. Retrofit 2 — Pagination Using Link Header and Dynamic Urls (Like GitHub)
  41. Retrofit 2 — Pagination Using Range Header Fields (Like Heroku)
  42. How to Integrate XML Converter
  43. Define a Custom Response Converter
  44. Retrofit 2 — Introduction to (Multiple) Converters
  45. Retrofit 2 — Adding & Customizing the Gson Converter
  46. Retrofit 2 — Implementing Custom Converters
  47. Retrofit 2 — Basics of Mocking Server Responses
  48. Retrofit 2 — Customizing Network Behavior of Mocked Server Responses (soon)
  49. Java Basics for Retrofit — Callbacks (soon)
  50. Java Basics for Retrofit — Annotations (soon)
  51. Java Basics for Retrofit — Fluent Interface with Builders (soon)

Logging in Retrofit 1

We’ve published a blog post on how to debug requests using Retrofit 1. If you’re using the first major version of Retrofit, please follow the link to the related post.

Logging In Retrofit 2

Retrofit 2 completely relies on OkHttp for any network operation. The developers of OkHttp have released a separate logging interceptor project, which implements logging for OkHttp. You can add it to your project with a quick edit of your build.gradle:

compile 'com.squareup.okhttp3:logging-interceptor:3.3.1'  

While developing your app and for debugging purposes it’s nice to have a log feature integrated to show request and response information. Since logging isn’t integrated by default anymore in Retrofit 2, we need to add a logging interceptor for OkHttp. Luckily OkHttp already ships with this interceptor and you only need to activate it for your OkHttpClient.

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

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

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

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

We recommend to add logging as the last interceptor, because this will also log the information which you added with previous interceptors to your request.

Log Levels

Logging too much information will blow up your Android monitor, that’s why OkHttp’s logging interceptor has four log levels: NONE, BASIC, HEADERS, BODY. We’ll walk you through each of the log levels and describe their output.

None

No logging.

Use this log level for production environments to enhance your apps performance by skipping any logging operation.

Basic

Log request type, url, size of request body, response status and size of response body.

D/HttpLoggingInterceptor$Logger: --> POST /upload HTTP/1.1 (277-byte body)  
D/HttpLoggingInterceptor$Logger: <-- HTTP/1.1 200 OK (543ms, -1-byte body)  

Using the log level BASIC will only log minimal information about your request. If you’re just interested in the request body size, response body size and response status, this log level is the right one.

Headers

Log request and response headers, request type, url, response status.

Using the HEADERS log level will only log request and response headers. Retrofit or OkHttp will add appropriate request headers by default, but they won’t show up on your request since they are added later in the request chain. If you didn’t intercept the actual request on Android, there is nothing to see. If you add request headers yourself, make sure the logging interceptor is the last interceptor added to the OkHttp client. If you add the interceptor first, there isn’t any header data set on the request yet.

We use the two header fields Accept and Content-Type to illustrate the output if you define the values yourself.

D/HttpLoggingInterceptor$Logger: --> POST /upload HTTP/1.1  
D/HttpLoggingInterceptor$Logger: Accept: application/json  
D/HttpLoggingInterceptor$Logger: Content-Type: application/json  
D/HttpLoggingInterceptor$Logger: --> END POST  
D/HttpLoggingInterceptor$Logger: <-- HTTP/1.1 200 OK (1039ms)  
D/HttpLoggingInterceptor$Logger: content-type: text/html; charset=utf-8  
D/HttpLoggingInterceptor$Logger: cache-control: no-cache  
D/HttpLoggingInterceptor$Logger: vary: accept-encoding  
D/HttpLoggingInterceptor$Logger: Date: Wed, 28 Oct 2015 08:24:20 GMT  
D/HttpLoggingInterceptor$Logger: Connection: keep-alive  
D/HttpLoggingInterceptor$Logger: Transfer-Encoding: chunked  
D/HttpLoggingInterceptor$Logger: OkHttp-Selected-Protocol: http/1.1  
D/HttpLoggingInterceptor$Logger: OkHttp-Sent-Millis: 1446020610352  
D/HttpLoggingInterceptor$Logger: OkHttp-Received-Millis: 1446020610369  
D/HttpLoggingInterceptor$Logger: <-- END HTTP  

Besides the headers of server’s response, you’ll get the information which protocol was selected and the respective milliseconds when your request was sent and the response was received.

Body

Log request and response headers and body.

This is the most complete log level and will print out every related information for your request and response.

D/HttpLoggingInterceptor$Logger: --> POST /upload HTTP/1.1  
D/HttpLoggingInterceptor$Logger: --9df820bb-bc7e-4a93-bb67-5f28f4140795  
D/HttpLoggingInterceptor$Logger: Content-Disposition: form-data; name="description"  
D/HttpLoggingInterceptor$Logger: Content-Transfer-Encoding: binary  
D/HttpLoggingInterceptor$Logger: Content-Type: application/json; charset=UTF-8  
D/HttpLoggingInterceptor$Logger: Content-Length: 37  
D/HttpLoggingInterceptor$Logger:  
D/HttpLoggingInterceptor$Logger: "hello, this is description speaking"  
D/HttpLoggingInterceptor$Logger: --9df820bb-bc7e-4a93-bb67-5f28f4140795--  
D/HttpLoggingInterceptor$Logger: --> END POST (277-byte body)  
D/HttpLoggingInterceptor$Logger: <-- HTTP/1.1 200 OK (1099ms)  
D/HttpLoggingInterceptor$Logger: content-type: text/html; charset=utf-8  
D/HttpLoggingInterceptor$Logger: cache-control: no-cache  
D/HttpLoggingInterceptor$Logger: vary: accept-encoding  
D/HttpLoggingInterceptor$Logger: Date: Wed, 28 Oct 2015 08:33:40 GMT  
D/HttpLoggingInterceptor$Logger: Connection: keep-alive  
D/HttpLoggingInterceptor$Logger: Transfer-Encoding: chunked  
D/HttpLoggingInterceptor$Logger: OkHttp-Selected-Protocol: http/1.1  
D/HttpLoggingInterceptor$Logger: OkHttp-Sent-Millis: 1446021170095  
D/HttpLoggingInterceptor$Logger: OkHttp-Received-Millis: 1446021170107  
D/HttpLoggingInterceptor$Logger: Perfect!  
D/HttpLoggingInterceptor$Logger: <-- END HTTP (8-byte body)  

This is the only log level where you’ll get the response body data. If you’re in an argument with your backend developer, use this log level to show the received response data. However, the BODY log level will clutter your Android monitor if you’re receiving large data sets. Use this level only if necessary.

Outlook

We hope you can apply logging to your development flow and the shown information about the log level will help you choose the right one for you. Use different log levels and try what works best for you. Even if the BODY log level might clutter up your log, maybe you’ll find your desired information only showing every request detail.

The next post will show you a how to integrate Hawk authentication on Android.


Additionally Resources


Explore the Library

Find interesting tutorials and solutions for your problems.

Miscellaneous