An OkHttp interceptor which logs HTTP request and response data
npx @tessl/cli install tessl/maven-com-squareup-okhttp3--logging-interceptor@4.12.0An OkHttp interceptor which logs HTTP request and response data with configurable logging levels and custom logger support. Provides comprehensive HTTP traffic monitoring for development and debugging.
implementation("com.squareup.okhttp3:logging-interceptor:4.12.0")import okhttp3.logging.HttpLoggingInterceptor
import okhttp3.logging.LoggingEventListenerJava:
import okhttp3.logging.HttpLoggingInterceptor;
import okhttp3.logging.LoggingEventListener;import okhttp3.OkHttpClient
import okhttp3.logging.HttpLoggingInterceptor
// Basic logging interceptor
val logging = HttpLoggingInterceptor()
logging.level = HttpLoggingInterceptor.Level.BODY
val client = OkHttpClient.Builder()
.addInterceptor(logging)
.build()
// Custom logger with header redaction
val customLogger = HttpLoggingInterceptor { message ->
println("HTTP: $message")
}
customLogger.level = HttpLoggingInterceptor.Level.HEADERS
customLogger.redactHeader("Authorization")
customLogger.redactHeader("Cookie")
val secureClient = OkHttpClient.Builder()
.addInterceptor(customLogger)
.build()Java equivalent:
import okhttp3.OkHttpClient;
import okhttp3.logging.HttpLoggingInterceptor;
// Basic logging interceptor
HttpLoggingInterceptor logging = new HttpLoggingInterceptor();
logging.setLevel(HttpLoggingInterceptor.Level.BODY);
OkHttpClient client = new OkHttpClient.Builder()
.addInterceptor(logging)
.build();
// Custom logger with header redaction
HttpLoggingInterceptor customLogger = new HttpLoggingInterceptor(message -> {
System.out.println("HTTP: " + message);
});
customLogger.setLevel(HttpLoggingInterceptor.Level.HEADERS);
customLogger.redactHeader("Authorization");
customLogger.redactHeader("Cookie");
OkHttpClient secureClient = new OkHttpClient.Builder()
.addInterceptor(customLogger)
.build();The library consists of two main components:
Core interceptor functionality for logging HTTP traffic with configurable detail levels and header security.
class HttpLoggingInterceptor @JvmOverloads constructor(
private val logger: Logger = Logger.DEFAULT
) : Interceptor {
@set:JvmName("level")
@Volatile var level: Level
fun redactHeader(name: String): Unit
fun setLevel(level: Level): HttpLoggingInterceptor
@JvmName("-deprecated_level")
@Deprecated(
message = "moved to var",
replaceWith = ReplaceWith(expression = "level"),
level = DeprecationLevel.ERROR
)
fun getLevel(): Level
@Throws(IOException::class)
override fun intercept(chain: Interceptor.Chain): Response
}Logging Levels:
enum class Level {
/** No logs. */
NONE,
/** Logs request and response lines only. */
BASIC,
/** Logs request and response lines and their respective headers. */
HEADERS,
/** Logs request and response lines, headers, and bodies (if present). */
BODY
}Usage Examples:
// Different logging levels
val interceptor = HttpLoggingInterceptor()
// No logging
interceptor.level = HttpLoggingInterceptor.Level.NONE
// Basic: --> POST /api/users http/1.1 (45-byte body)
// <-- 201 Created (123ms, 78-byte body)
interceptor.level = HttpLoggingInterceptor.Level.BASIC
// Headers: includes all request/response headers
interceptor.level = HttpLoggingInterceptor.Level.HEADERS
// Body: includes request/response bodies (be careful with sensitive data)
interceptor.level = HttpLoggingInterceptor.Level.BODY
// Header redaction for security
interceptor.redactHeader("Authorization")
interceptor.redactHeader("Set-Cookie")
// Fluent API (useful for Java)
val configuredInterceptor = HttpLoggingInterceptor()
.setLevel(HttpLoggingInterceptor.Level.HEADERS)Interface for directing log output to custom destinations like files, logging frameworks, or analytics systems.
fun interface Logger {
fun log(message: String): Unit
companion object {
/** A Logger that outputs to platform-appropriate default location. */
@JvmField
val DEFAULT: Logger
}
}Usage Examples:
// File logging
val fileLogger = HttpLoggingInterceptor { message ->
File("http.log").appendText("$message\n")
}
// Integration with logging frameworks
val timberLogger = HttpLoggingInterceptor { message ->
Timber.tag("OkHttp").d(message)
}
// Structured logging
val structuredLogger = HttpLoggingInterceptor { message ->
logger.info("http_traffic") {
"message" to message
"timestamp" to Instant.now()
}
}
// Conditional logging
val debugLogger = HttpLoggingInterceptor { message ->
if (BuildConfig.DEBUG) {
println("HTTP: $message")
}
}Detailed connection and call lifecycle event logging for advanced debugging and performance monitoring.
class LoggingEventListener private constructor(
private val logger: HttpLoggingInterceptor.Logger
) : EventListener() {
// Private constructor - use Factory to create instances
// Connection lifecycle events
override fun callStart(call: Call): Unit
override fun callEnd(call: Call): Unit
override fun callFailed(call: Call, ioe: IOException): Unit
override fun canceled(call: Call): Unit
// DNS and proxy events
override fun proxySelectStart(call: Call, url: HttpUrl): Unit
override fun proxySelectEnd(call: Call, url: HttpUrl, proxies: List<Proxy>): Unit
override fun dnsStart(call: Call, domainName: String): Unit
override fun dnsEnd(call: Call, domainName: String, inetAddressList: List<InetAddress>): Unit
// Connection events
override fun connectStart(call: Call, inetSocketAddress: InetSocketAddress, proxy: Proxy): Unit
override fun connectEnd(call: Call, inetSocketAddress: InetSocketAddress, proxy: Proxy, protocol: Protocol?): Unit
override fun connectFailed(call: Call, inetSocketAddress: InetSocketAddress, proxy: Proxy, protocol: Protocol?, ioe: IOException): Unit
override fun secureConnectStart(call: Call): Unit
override fun secureConnectEnd(call: Call, handshake: Handshake?): Unit
// Connection pool events
override fun connectionAcquired(call: Call, connection: Connection): Unit
override fun connectionReleased(call: Call, connection: Connection): Unit
// Request events
override fun requestHeadersStart(call: Call): Unit
override fun requestHeadersEnd(call: Call, request: Request): Unit
override fun requestBodyStart(call: Call): Unit
override fun requestBodyEnd(call: Call, byteCount: Long): Unit
override fun requestFailed(call: Call, ioe: IOException): Unit
// Response events
override fun responseHeadersStart(call: Call): Unit
override fun responseHeadersEnd(call: Call, response: Response): Unit
override fun responseBodyStart(call: Call): Unit
override fun responseBodyEnd(call: Call, byteCount: Long): Unit
override fun responseFailed(call: Call, ioe: IOException): Unit
// Cache events
override fun satisfactionFailure(call: Call, response: Response): Unit
override fun cacheHit(call: Call, response: Response): Unit
override fun cacheMiss(call: Call): Unit
override fun cacheConditionalHit(call: Call, cachedResponse: Response): Unit
}Factory for Event Listener Creation:
open class Factory @JvmOverloads constructor(
private val logger: HttpLoggingInterceptor.Logger = HttpLoggingInterceptor.Logger.DEFAULT
) : EventListener.Factory {
override fun create(call: Call): EventListener
}Usage Examples:
// Basic event logging
val eventLogger = LoggingEventListener.Factory()
val client = OkHttpClient.Builder()
.eventListenerFactory(eventLogger)
.build()
// Custom event logger
val customEventLogger = LoggingEventListener.Factory { message ->
println("Event: $message")
}
// Combined interceptor and event logging
val interceptor = HttpLoggingInterceptor()
interceptor.level = HttpLoggingInterceptor.Level.BASIC
val eventListener = LoggingEventListener.Factory()
val client = OkHttpClient.Builder()
.addInterceptor(interceptor)
.eventListenerFactory(eventListener)
.build()
// Performance monitoring
val performanceLogger = LoggingEventListener.Factory { message ->
if (message.contains("ms")) {
// Log timing events to analytics
analytics.track("http_timing", message)
}
}// From OkHttp core (referenced types)
interface Interceptor {
@Throws(IOException::class)
fun intercept(chain: Chain): Response
interface Chain {
fun request(): Request
@Throws(IOException::class)
fun proceed(request: Request): Response
fun connection(): Connection?
}
}
abstract class EventListener {
interface Factory {
fun create(call: Call): EventListener
}
}
// Standard Java/Kotlin types used
import java.io.IOException
import java.net.InetAddress
import java.net.InetSocketAddress
import java.net.Proxy
import okhttp3.Call
import okhttp3.Connection
import okhttp3.Handshake
import okhttp3.HttpUrl
import okhttp3.Protocol
import okhttp3.Request
import okhttp3.ResponseHeader Redaction:
Authorization, Cookie, Set-CookieredactHeader() method to prevent credential leakage"██" in logsBody Logging Security:
Level.BODY exposes full request/response contentProduction Usage:
Level.NONE or Level.BASIC in production// Secure production configuration
val productionLogger = HttpLoggingInterceptor { message ->
// Only log to secure, encrypted storage
secureLogger.info(message)
}
productionLogger.level = HttpLoggingInterceptor.Level.BASIC
productionLogger.redactHeader("Authorization")
productionLogger.redactHeader("Cookie")
productionLogger.redactHeader("Set-Cookie")
productionLogger.redactHeader("X-API-Key")