如何使用Retrofit-Android记录请求和响应体?

153

我在Retrofit API中找不到记录完整请求/响应体的相关方法。我本以为Profiler会提供一些帮助(但它只提供有关响应的元数据)。我尝试在Builder中设置日志级别,但这也没有帮助:

RestAdapter adapter = (new RestAdapter.Builder()).
                setEndpoint(baseUrl).
                setRequestInterceptor(interceptor).
                setProfiler(profiler).
                setClient(client).
                setExecutors(MyApplication.getWebServiceThreadPool()).
                setLogLevel(LogLevel.FULL).
                setLog(new RestAdapter.Log() {
                    @Override
                    public void log(String msg) {
                        Log.i(TAG, msg);
                    }
                }).
                build();

编辑:这段代码现在可以工作了。我不知道为什么之前它不能工作。可能是因为我使用了一些旧版本的Retrofit。


你解决了这个问题吗?文档说FULL应该返回正文,但好像并没有。 - theblang
1
@mattblang:我不知道之前出了什么问题,但现在这段代码可以工作了。 - dev
可能是 Logging with Retrofit 2 的重复问题。 - bryant1410
https://dev59.com/f5Dea4cB1Zd3GeqPXByZ#38890345 - Nilesh Senta
如果之前无法正常工作,可能是即时运行出了问题。 - deathangel908
13个回答

155

Retrofit 2.0 :

更新: @Marcus Pöhls 撰写

Retrofit 2 中的日志记录

Retrofit 2 完全依赖于 OkHttp 进行任何网络操作。由于 OkHttp 是 Retrofit 2 的对等依赖项,因此在发布稳定版本的 Retrofit 2 后,您不需要添加其他依赖项。

OkHttp 2.6.0 内部依赖项包含一个日志拦截器,您可以直接将其用于 Retrofit 客户端。Retrofit 2.0.0-beta2 仍使用 OkHttp 2.5.0。未来的版本将提高 OkHttp 的依赖版本。这就是为什么您需要手动导入日志拦截器的原因。在 build.gradle 文件中的 gradle 导入中添加以下行以获取日志拦截器依赖项。

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

你也可以访问Square的GitHub页面了解有关这个拦截器的信息。

为Retrofit 2添加日志记录

在开发应用程序和调试时,将日志记录功能集成到应用程序中以显示请求和响应信息非常方便。由于Retrofit 2不再默认集成日志记录,因此我们需要为OkHttp添加日志拦截器。幸运的是,OkHttp已经内置了此拦截器,您只需为您的OkHttpClient激活它即可。

HttpLoggingInterceptor logging = new HttpLoggingInterceptor();  
// set your desired log level
logging.setLevel(HttpLoggingInterceptor.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();

我们建议将日志记录添加为最后一个拦截器,因为这样可以记录您在之前的拦截器中添加到请求中的信息。

日志级别

记录过多的信息会使您的Android监视器崩溃,这就是为什么OkHttp的日志记录拦截器具有四个日志级别:NONE、BASIC、HEADERS和BODY。我们将逐个说明每个日志级别并描述其输出。

更多信息请访问:Retrofit 2-记录请求和响应

旧回答:

Retrofit 2不再提供日志记录功能。开发团队删除了日志记录功能。说实话,日志记录功能也并不是很可靠。Jake Wharton明确表示,记录的消息或对象是假定值,不能证明它们是真实的。到达服务器的实际请求可能已更改请求主体或其他内容。

即使默认情况下没有集成日志记录功能,您仍然可以利用任何Java日志记录器,并将其用于自定义的OkHttp拦截器中。

有关Retrofit 2的更多信息,请参考:Retrofit-入门和创建Android客户端


1
还有一个专门用于Retrofit 2.0登录的帖子:https://futurestud.io/blog/retrofit-2-log-requests-and-responses/ - peitek
让所有事情变得比它本应该的更加复杂,这是一个不错的方法。 这让我想起了Jersey 1与Jersey 2日志记录之间的区别。需要编写更多样板代码... - breakline
以这种方式添加拦截器现在将在OkHttp v3中导致UnsupportedOperationException。新的方法是:OkHttpClient.Builder().addInterceptor(logging).build()https://github.com/square/okhttp/issues/2219 - Jim Pekarek
@JawadLeWywadi 是的,使用这段代码你可以打印正文。 - Dhaval Jivani
对于Retrofit 2,这个更加简单。 - Gary99
链接需要注册,可能需要付款。 - blimpse

95

我使用了 setLogLevel(LogLevel.FULL).setLog(new AndroidLog("YOUR_LOG_TAG")),它对我很有帮助。
更新.
你也可以尝试将 retrofit.client.Response 作为响应模型来进行调试。


3
AndroidLog是什么类? - theblang
它附带了Retrofit库。 - Alex Dzeshko
2
我明白了。不幸的是,这并没有给你响应体,尽管文档中确实说明LogLevel.FULL应该给你响应体 - theblang
在 Android 的 logcat 中,可以在 verbose 标签下看到 "YOUR_LOG_TAG"。 - rajeesh
6
在Retrofit 2中,不存在LogLevel.Full。 - user5156016

33

更新:Retrofit 2.0.0-beta3

现在您必须使用 okhttp3 进行构建。旧的拦截器无法使用。此响应适用于 Android。

下面是新内容的快速复制粘贴。

1. 修改您的 gradle 文件为:

  compile 'com.squareup.retrofit2:retrofit:2.0.0-beta3'
  compile "com.squareup.retrofit2:converter-gson:2.0.0-beta3"
  compile "com.squareup.retrofit2:adapter-rxjava:2.0.0-beta3"
  compile 'com.squareup.okhttp3:logging-interceptor:3.0.1'

2. 检查此示例代码:

使用新的导入。如果您不使用Rx,则可以删除它,还可以删除您不需要的内容。

import okhttp3.OkHttpClient;
import okhttp3.logging.HttpLoggingInterceptor;
import retrofit2.GsonConverterFactory;
import retrofit2.Retrofit;
import retrofit2.RxJavaCallAdapterFactory;
import retrofit2.http.GET;
import retrofit2.http.Query;
import rx.Observable;

public interface APIService {

  String ENDPOINT = "http://api.openweathermap.org";
  String API_KEY = "2de143494c0b2xxxx0e0";

  @GET("/data/2.5/weather?appid=" + API_KEY) Observable<WeatherPojo> getWeatherForLatLon(@Query("lat") double lat, @Query("lng") double lng, @Query("units") String units);


  class Factory {

    public static APIService create(Context context) {

      OkHttpClient.Builder builder = new OkHttpClient().newBuilder();
      builder.readTimeout(10, TimeUnit.SECONDS);
      builder.connectTimeout(5, TimeUnit.SECONDS);

      if (BuildConfig.DEBUG) {
        HttpLoggingInterceptor interceptor = new HttpLoggingInterceptor();
        interceptor.setLevel(HttpLoggingInterceptor.Level.BASIC);
        builder.addInterceptor(interceptor);
      }

      //Extra Headers

      //builder.addNetworkInterceptor().add(chain -> {
      //  Request request = chain.request().newBuilder().addHeader("Authorization", authToken).build();
      //  return chain.proceed(request);
      //});

      builder.addInterceptor(new UnauthorisedInterceptor(context));
      OkHttpClient client = builder.build();

      Retrofit retrofit =
          new Retrofit.Builder().baseUrl(APIService.ENDPOINT).client(client).addConverterFactory(GsonConverterFactory.create()).addCallAdapterFactory(RxJavaCallAdapterFactory.create()).build();

      return retrofit.create(APIService.class);
    }
  }
}

奖金

我知道这不是主题,但我觉得很酷。

如果出现HTTP错误代码未经授权,这里有一个拦截器。 我使用事件总线传输事件。

import android.content.Context;
import android.os.Handler;
import android.os.Looper;
import com.androidadvance.ultimateandroidtemplaterx.BaseApplication;
import com.androidadvance.ultimateandroidtemplaterx.events.AuthenticationErrorEvent;

import de.greenrobot.event.EventBus;
import java.io.IOException;
import javax.inject.Inject;
import okhttp3.Interceptor;
import okhttp3.Response;

public class UnauthorisedInterceptor implements Interceptor {

  @Inject EventBus eventBus;

  public UnauthorisedInterceptor(Context context) {
    BaseApplication.get(context).getApplicationComponent().inject(this);
  }

  @Override public Response intercept(Chain chain) throws IOException {
    Response response = chain.proceed(chain.request());
    if (response.code() == 401) {
      new Handler(Looper.getMainLooper()).post(() -> eventBus.post(new AuthenticationErrorEvent()));
    }
    return response;
  }
}

代码取自https://github.com/AndreiD/UltimateAndroidTemplateRx(我的项目)。


9

目前似乎没有直接实现基本+正文的方法,但你可以使用FULL模式,并过滤掉你不想要的头部信息。

RestAdapter adapter = new RestAdapter.Builder()
                          .setEndpoint(syncServer)
                          .setErrorHandler(err)
                          .setConverter(new GsonConverter(gson))
                          .setLogLevel(logLevel)
                          .setLog(new RestAdapter.Log() {
                              @Override
                              public void log(String msg) {
                                  String[] blacklist = {"Access-Control", "Cache-Control", "Connection", "Content-Type", "Keep-Alive", "Pragma", "Server", "Vary", "X-Powered-By"};
                                  for (String bString : blacklist) {
                                      if (msg.startsWith(bString)) {
                                          return;
                                      }
                                  }
                                  Log.d("Retrofit", msg);
                              }
                          }).build();

似乎在覆盖日志时,正文会以类似于

的标签为前缀。
[ 02-25 10:42:30.317 25645:26335 D/Retrofit ]

因此,通过调整自定义过滤器,记录基本信息和正文应该很容易。我正在使用黑名单,但根据您的需求也可以使用白名单。


6
以下代码适用于打印请求和响应日志,无论是否使用标头。请注意:如果不使用标头,请注释掉 .addHeader() 行。
(Note: "Comment"在此处的意思是“注释掉”,而非“评论”)
HttpLoggingInterceptor interceptor = new HttpLoggingInterceptor();
        interceptor.setLevel(HttpLoggingInterceptor.Level.BODY);
        OkHttpClient client = new OkHttpClient.Builder()
                .addInterceptor(interceptor)
                //.addInterceptor(REWRITE_CACHE_CONTROL_INTERCEPTOR)
                .addNetworkInterceptor(new Interceptor() {

                    @Override

                    public okhttp3.Response intercept(Chain chain) throws IOException {
                        Request request = chain.request().newBuilder()
                                // .addHeader(Constant.Header, authToken)
                                   .build();
                        return chain.proceed(request);
                    }
                }).build();

        final Retrofit retrofit = new Retrofit.Builder()
                .baseUrl(Constant.baseUrl)
                .client(client) // This line is important
                .addConverterFactory(GsonConverterFactory.create())
                .build();

4

理想情况下,如果您使用Timber一起工作,会更好,因为您可以全局地处理应用程序中的日志消息,例如将它们保存到文件中。以下是如何与Timber一起使用Httplogginginterceptor的方法:

val loggingInterceptor = HttpLoggingInterceptor { message -> 
Timber.d(message) 
}.apply {
     level = HttpLoggingInterceptor.Level.BODY
}

val client = OkHttpClient.Builder()
            .addInterceptor(loggingInterceptor)
            .build()

return Retrofit.Builder()
            .baseUrl(baseUrl)
            .addConverterFactory(GsonConverterFactory.create())
            .client(client)
            .build()
            .create(RetrofitApiService::class.java)

您也可以通过以下方式创建自己的日志记录器拦截器实现:
class HttpLoggingInterceptor : Interceptor {
    override fun intercept(chain: Interceptor.Chain): Response {
        val request: Request = chain.request()

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

        val response = chain.proceed(request)

        val responseBody: ResponseBody? = response.body
        val responseBodyString = response.body!!.string()

        val newResponse = response.newBuilder()
            .body(responseBodyString.toResponseBody(responseBody?.contentType()))
            .build()

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

        return newResponse
    }

    private fun bodyToString(request: Request): String? {
        return try {
            val copy = request.newBuilder().build()
            val buffer = Buffer()
            copy.body!!.writeTo(buffer)
            buffer.readUtf8()
        } catch (e: Exception) {
            "did not work"
        }
    }
}

以下是如何添加Timber的步骤:

implementation "com.jakewharton.timber:timber:5.0.1"

3

我希望这段代码能帮助您进行日志记录。
您只需在Build.Gradle中添加拦截器,然后创建RetrofitClient

第一步

将以下行添加到您的build.gradle

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

第二步

创建你的Retrofit客户端


   public class RetrofitClient {

    private Retrofit retrofit;
    private static OkHttpClient.Builder httpClient =
            new OkHttpClient.Builder();
    private static RetrofitClient instance = null;
    private static ApiServices service = null;
    private static HttpLoggingInterceptor logging =
            new HttpLoggingInterceptor();

    private RetrofitClient(final Context context) {
        httpClient.interceptors().add(new Interceptor() {
            @Override
            public okhttp3.Response intercept(Interceptor.Chain chain) throws IOException {
                Request originalRequest = chain.request();
                Request.Builder builder = originalRequest.newBuilder().
                        method(originalRequest.method(), originalRequest.body());
                okhttp3.Response response = chain.proceed(builder.build());
                /*
                Do what you want
                 */
                return response;
            }
        });

        if (BuildConfig.DEBUG) {
            logging.setLevel(HttpLoggingInterceptor.Level.BODY);
            // add logging as last interceptor
            httpClient.addInterceptor(logging);
        }

        retrofit = new Retrofit.Builder().client(httpClient.build()).
                baseUrl(Constants.BASE_URL).
                addConverterFactory(GsonConverterFactory.create()).build();
        service = retrofit.create(ApiServices.class);
    }


    public static RetrofitClient getInstance(Context context) {
        if (instance == null) {
            instance = new RetrofitClient(context);
        }
        return instance;
    }

    public ApiServices getApiService() {
        return service;
    }
}

调用

RetrofitClient.getInstance(context).getApiService().yourRequestCall(); 


2

如果您正在使用Retrofit2和okhttp3,则需要知道Interceptor是通过队列工作的。因此,在其他拦截器之后,将loggingInterceptor添加到末尾:

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))
                .addInterceptor(new OAuthInterceptor(context))
                .authenticator(new BearerTokenAuthenticator(context))
                .addInterceptor(loggingInterceptor)//at the end
                .build();

2

2
call.request().toString();

向服务器发送的截图请求: 在此输入图像描述

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接