Ведение журнала с помощью Retrofit 2


308

Я пытаюсь получить точный JSON, который отправляется в запросе. Вот мой код:

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

Но я вижу это только в логах:

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

Как я должен делать правильное ведение журнала, учитывая удаление setLog()и setLogLevel()которое мы использовали для Retrofit 1?

Ответы:


699

В Retrofit 2 вы должны использовать HttpLoggingInterceptor .

Добавить зависимость к build.gradle. Последняя версия на октябрь 2019 года:

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

Создайте Retrofitобъект, подобный следующему:

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

В случае предупреждения об устаревании просто измените setLevelна:

interceptor.level(HttpLoggingInterceptor.Level.BODY);

Приведенное выше решение дает вам сообщения logcat, очень похожие на старые, установленные

setLogLevel(RestAdapter.LogLevel.FULL)

В случаеjava.lang.ClassNotFoundException :

Старая версия Retrofit может потребовать более старой logging-interceptorверсии. Посмотрите на разделы комментариев для деталей.


30
Это было добавлено в OkHttp всего через 15 дней после моего вопроса, приятно, что потребности сообщества оказывают такое быстрое влияние!
Габор

1
и вам больше не нужно добавлять хранилище снимков sonatype в ваш build.gradle
Джеймс Гудвин,

13
дооснащение 2.1.0 использовать этот компилятор 'com.squareup.okhttp3: logging-interceptor: 3.3.1'
jayellos

2
@jayellos Спасибо за это. Если вы используете версию ниже 3.3.1, вы не получите такой метод исключения.
guydemossyrock

2
Я получаю: Не нашли класс "okhttp3.internal.Platform" идеи?
Corlaez

35

Я встретил то же самое, что и вы, и попытался спросить у автора книги Retrofit: люблю работать с API на Android (вот ссылка ) (нет! Я не делаю для них рекламу ... но они действительно хороши ребята :) И автор ответил мне очень скоро, с обоими методами журнала на Retrofit 1.9 и Retrofit 2.0-бета.

А вот код 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();

Это как добавить метод регистрации с помощью HttpLoggingInterceptor . Также, если вы читаете ту книгу, о которой я упоминал выше, вы можете обнаружить, что там написано, что в Retrofit 2.0 нет метода ведения журнала - что, как я спросил автора, неверно, и они обновят книгу в следующем году. об этом.

// Если вы не знакомы с методом Log в Retrofit, я хотел бы поделиться чем-то еще.

Также следует заметить, что есть несколько уровней ведения журнала, которые вы можете выбрать. Я использую Level.BODY большую часть времени, что даст примерно такую ​​вещь:

введите описание изображения здесь

Вы можете найти почти всех сотрудников http внутри картинки: заголовок, содержание и ответ и т. Д.

И иногда вам действительно не нужно, чтобы все гости посещали вашу вечеринку: я просто хочу знать, успешно ли она подключена, что интернет-звонок успешно осуществляется в моей Activiy & Fragmetn. Тогда вы можете использовать Level.BASIC , который будет возвращать что-то вроде этого:

введите описание изображения здесь

Можете ли вы найти код состояния 200 OK внутри? Вот и все :)

Также есть еще один, Level.HEADERS , который будет возвращать только заголовок сети. Я, конечно, другая картина здесь:

введите описание изображения здесь

Вот и весь трюк с логгингом;)

И я хотел бы поделиться с вами на уроке я узнал много там . У них есть куча отличных постов, рассказывающих почти обо всем, что касается Retrofit, и они продолжают обновлять пост, в то же время выходит Retrofit 2.0. Пожалуйста, взгляните на те работы, которые, я думаю, сэкономят вам много времени.


Откуда происходит HttpLoggingInterceptor?
Раду

5
Кроме того, это не работает для меня. httpClient.interceptors.add хочет com.squareup.okhttp.Interceptor, а не okhttp3.logging.HttpLoggingInterceptor
Раду

@Radu На какой версии Retrofit вы работаете? У вашего gradle должно быть что-то вроде этого: compile 'com.squareup.retrofit2: retrofit: 2.0.0-beta4'
peitek

Это не будет работать для регистрации заголовков запросов, потому что необходимо добавить Interceptor как сетевой перехватчик! См .: stackoverflow.com/a/36847027/2557258
Yazon2006

12

Вот Interceptorчто регистрирует тела запросов и ответов (используя Timber, на основе примера из документации OkHttp и некоторых других ответов 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

Попробуй это:

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

После этого в bodyJSON вы заинтересованы.


1
как можно распечатать ответ тела?
Гилберто Ибарра,

3
Использование @GilbertoIbarra String bodyString = response.body().string(); log(bodyString); response = response.newBuilder().body(ResponseBody.create(response.body().contentType(), bodyString)).build(); (Вы не можете прочитать тело ответа более одного раза, поэтому вам нужно будет создать новый ответ вместе со строителем)
Антон Рябых,

Это костыль, который нам больше не нужен. Вот пример рабочего ведения журнала: stackoverflow.com/a/36847027/2557258
Yazon2006

6

Основной проблемой, с которой я столкнулся, было динамическое добавление заголовков и их запись в отладочный logcat. Я пытался добавить два перехватчика. Один для регистрации и один для добавления заголовков на ходу (авторизация токена). Проблема заключалась в том, что мы можем .addInterceptor или .addNetworkInterceptor. Как сказал мне Джейк Уортон: «Сетевые перехватчики всегда идут после перехватчиков приложений. См. Https://github.com/square/okhttp/wiki/Interceptors ». Итак, вот рабочий пример с заголовками и журналами:

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

Я не знаю, вернется ли setLogLevel () в финальной версии Retrofit 2.0, но сейчас вы можете использовать перехватчик для ведения журнала.

Хороший пример можно найти в википедии OkHttp: 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();

Вы либо не прочитали мой вопрос, либо не прочитали страницу, на которую ссылаетесь ... потому что LoggingInterceptor не пытается зарегистрировать тело запроса (и поэтому не решает мою проблему)
Gabor

Я получаю 'UnsupportedOperationException'
Холецкий

5

Если вы используете 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))//first
                .addInterceptor(new OAuthInterceptor(context))//second
                .authenticator(new BearerTokenAuthenticator(context))
                .addInterceptor(loggingInterceptor)//third, log at the end
                .build();

4

Для тех, кому нужна высокоуровневая регистрация в Retrofit, используйте перехватчик как этот

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

Предоставлено : https://github.com/square/retrofit/issues/1072#


Вы должны были упомянуть, что вы скопировали с github.com/square/retrofit/issues/1072# (чтобы отдать должное вашему источнику)
Габор

Это костыль, который нам больше не нужен. Вот пример рабочего ведения журнала: stackoverflow.com/a/36847027/2557258
Yazon2006

4

Код Котлина

        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

Вы также можете добавить Stetho на Facebook и посмотреть на следы сети в Chrome: http://facebook.github.io/stetho/

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

Затем откройте «chrome: // inspect» в Chrome ...


2

это создаст модифицированный объект с регистрацией. без создания отдельных объектов.

 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

Сначала добавьте зависимость к build.gradle:

реализация 'com.squareup.okhttp3: logging-interceptor: 3.12.1'

При использовании Kotlin вы можете добавить Logging Interceptor следующим образом:

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

Следующий набор кода работает без проблем для меня

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

Можно также проверить результаты, перейдя на вкладку «Профилировщик» в нижней части Android Studio, затем щелкнув значок «+», чтобы начать новую сессию, а затем выберите нужный пик в «Сети». Там вы можете получить все, но это громоздко и медленно. Пожалуйста, смотрите изображение ниже.

введите описание изображения здесь


1

Лучший способ сделать это правильно в Retrofit 2 - добавить перехватчик регистратора в качестве networkInterceptor, который будет распечатывать заголовки сети и ваши пользовательские заголовки. Важно помнить, что перехватчик работает как стек и быть уверенным, что вы добавляете регистратор в конце всего.

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

Большая часть ответа здесь охватывает почти все, кроме этого инструмента, одного из самых классных способов просмотра журнала.

Это Stetho в Facebook . Это превосходный инструмент для мониторинга / регистрации сетевого трафика вашего приложения в Google Chrome . Вы также можете найти здесь, на Github.

введите описание изображения здесь


1

для Retrofit 2.0.2 код похож

   **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

Вот простой способ отфильтровать любые параметры запроса / ответа из журналов, используя 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);

Вот полная суть:

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


1

Я также застрял в подобной ситуации, setLevel()метод не приходил, когда я пытался вызвать его с помощью экземпляра HttpLoggingInterceptor, например так:

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

Вот как я решил, чтобы создать журнал для Retrofit2,

Я полагаю, вы добавили зависимость,

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

Для последней версии вы можете проверить, эту ссылку:

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

Здесь они также объяснили, как добавить.

Я создал класс с именем AddLoggingInterceptor, вот мой код,

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

Затем, где мы создаем нашу модернизацию,

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

Теперь вы можете увидеть журнал, сгенерированный в вашей Android Studio, вам может понадобиться поискать okHttpпроцесс фильтрации. Это сработало для меня. Если какие-либо вопросы вы можете написать мне здесь.


0

Я нашел способ для печати Войти в систему дооснащения

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

Работает для меня.


0

Перехватчик Retrofit - отличная функция, которая позволяет вам работать с http-запросами. Есть два типа: приложение и сетевые перехватчики.

Я бы порекомендовал использовать, Charles Web Debugging Proxy Applicationесли вам нужно регистрировать ваши запросы / ответы. Вывод очень похож на Stetho, но это более мощный инструмент, который вам не нужно добавлять в качестве зависимости от приложения.


-11

Эй, ребята, я уже нахожу решение:

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

Функция обратного вызова setLog может вводить каждый журнал
Vihuela Yao

1
это использует модификацию v1, а не v2
Gabor
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.