Как сделать логирование в Retrofit и OkHttp
Комментарии (2)
Ответ сгенерирован нейросетью и может содержать ошибки
Настройка логирования в Retrofit и OkHttp
Логирование сетевых запросов и ответов — критически важная часть разработки Android-приложений, особенно при отладке взаимодействия с API. Retrofit использует OkHttp в качестве HTTP-клиента, поэтому логирование настраивается именно на уровне OkHttp.
Основные подходы к логировамию
1. Использование HttpLoggingInterceptor (наиболее популярный способ)
Это стандартный перехватчик, предоставляемый библиотекой OkHttp, который позволяет логировать различные аспекты HTTP-запросов и ответов.
Шаг 1: Добавьте зависимость в build.gradle:
dependencies {
implementation 'com.squareup.okhttp3:logging-interceptor:4.12.0'
}
Шаг 2: Создайте и настройте HttpLoggingInterceptor:
import okhttp3.logging.HttpLoggingInterceptor
val loggingInterceptor = HttpLoggingInterceptor().apply {
// Уровень логирования зависит от типа сборки
level = if (BuildConfig.DEBUG) {
HttpLoggingInterceptor.Level.BODY
} else {
HttpLoggingInterceptor.Level.NONE
}
}
Доступные уровни логирования:
- NONE — без логирования
- BASIC — логирует метод запроса, URL, код ответа и размер
- HEADERS — добавляет заголовки запроса и ответа
- BODY — полная информация, включая тела запроса и ответа
Шаг 3: Добавьте интерцептор в клиент OkHttp:
import okhttp3.OkHttpClient
import retrofit2.Retrofit
import retrofit2.converter.gson.GsonConverterFactory
val okHttpClient = OkHttpClient.Builder()
.addInterceptor(loggingInterceptor)
.build()
val retrofit = Retrofit.Builder()
.baseUrl("https://api.example.com/")
.client(okHttpClient)
.addConverterFactory(GsonConverterFactory.create())
.build()
2. Создание кастомного интерцептора для расширенного логирования
Если стандартного логирования недостаточно, можно создать собственный интерцептор:
import okhttp3.Interceptor
import okhttp3.Response
import okio.Buffer
import java.nio.charset.Charset
class CustomLoggingInterceptor : Interceptor {
override fun intercept(chain: Interceptor.Chain): Response {
val request = chain.request()
val requestTime = System.currentTimeMillis()
// Логирование запроса
logRequest(request)
val response = chain.proceed(request)
val responseTime = System.currentTimeMillis() - requestTime
// Логирование ответа
logResponse(response, responseTime)
return response
}
private fun logRequest(request: Request) {
println("→ ${request.method} ${request.url}")
request.headers.forEach { name, value ->
println(" $name: $value")
}
request.body?.let { body ->
val buffer = Buffer()
body.writeTo(buffer)
println(" Body: ${buffer.readString(Charset.defaultCharset())}")
}
}
private fun logResponse(response: Response, duration: Long) {
println("← ${response.code} ${response.message} (${duration}ms)")
response.headers.forEach { name, value ->
println(" $name: $value")
}
response.body?.let { body ->
val source = body.source()
source.request(Long.MAX_VALUE)
val buffer = source.buffer
println(" Body: ${buffer.clone().readString(Charset.defaultCharset())}")
}
}
}
Важные рекомендации по логированию
Безопасность и конфиденциальность
- Никогда не логируйте конфиденциальные данные (токены авторизации, пароли, персональные данные) в продакшене
- Используйте разные уровни логирования для debug и release сборок:
val loggingInterceptor = HttpLoggingInterceptor { message ->
// Фильтрация конфиденциальных данных
val filteredMessage = message.replace(
Regex("(Authorization|Bearer|password|token): .+"),
"$1: [FILTERED]"
)
Log.d("HTTP", filteredMessage)
}.apply {
level = if (BuildConfig.DEBUG) {
HttpLoggingInterceptor.Level.HEADERS
} else {
HttpLoggingInterceptor.Level.NONE
}
}
Производительность
- Логирование уровня BODY может существенно замедлить работу приложения, особенно при больших ответах
- В продакшене рекомендуется отключать логирование или использовать минимальный уровень
Форматирование JSON в логах
Для более читаемого вывода JSON-ответов можно использовать кастомный логирующий интерцептор:
class JsonFormattingInterceptor : Interceptor {
override fun intercept(chain: Interceptor.Chain): Response {
val response = chain.proceed(chain.request())
val contentType = response.header("Content-Type") ?: ""
if (contentType.contains("application/json")) {
val responseBody = response.body
val source = responseBody?.source()
source?.request(Long.MAX_VALUE)
val buffer = source?.buffer?.clone()
val json = buffer?.readUtf8()
// Форматирование JSON с отступами
val formattedJson = try {
JSONObject(json).toString(2)
} catch (e: Exception) {
try {
JSONArray(json).toString(2)
} catch (e: Exception) {
json
}
}
Log.d("FormattedJSON", formattedJson ?: "")
}
return response
}
}
Конфигурация для разных сред
Для управления логированием в разных средах рекомендуется использовать конфигурационный класс:
object NetworkLogger {
fun createHttpLogger(): HttpLoggingInterceptor {
return HttpLoggingInterceptor { message ->
when {
message.startsWith("{") || message.startsWith("[") ->
logFormattedJson(message)
else ->
Log.d("Network", message)
}
}.apply {
level = when (BuildConfig.BUILD_TYPE) {
"debug" -> HttpLoggingInterceptor.Level.BODY
"staging" -> HttpLoggingInterceptor.Level.HEADERS
else -> HttpLoggingInterceptor.Level.NONE
}
}
}
private fun logFormattedJson(jsonString: String) {
// Кастомная логика форматирования JSON
}
}
// Использование
val okHttpClient = OkHttpClient.Builder()
.addInterceptor(NetworkLogger.createHttpLogger())
.build()
Вывод
Для эффективного логирования в Retrofit/OkHttp рекомендуется:
- Использовать HttpLoggingInterceptor для базовых нужд
- Создавать кастомные интерцепторы для специфических требований
- Всегда фильтровать конфиденциальные данные
- Настраивать разные уровни логирования для разных типов сборок
- Мониторить влияние логирования на производительность приложения
Правильно настроенное логирование значительно ускоряет отладку сетевых запросов и помогает быстро выявлять проблемы интеграции с бэкендом.