Ведение журнала с помощью 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?

Gabor
источник

Ответы:

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версии. Посмотрите на разделы комментариев для деталей.

Klimat
источник
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. Пожалуйста, взгляните на те работы, которые, я думаю, сэкономят вам много времени.

Anthonyeef
источник
Откуда происходит 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";
        }
    }
}
david.mihola
источник
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();
Yazon2006
источник
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();
oceanfeeling
источник
Вы либо не прочитали мой вопрос, либо не прочитали страницу, на которую ссылаетесь ... потому что 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();
NickUnuchek
источник
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#

DGN
источник
Вы должны были упомянуть, что вы скопировали с 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 ...

kenyee
источник
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());
Catluc
источник
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, но это более мощный инструмент, который вам не нужно добавлять в качестве зависимости от приложения.

yoAlex5
источник
-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