An OkHttp interceptor which logs HTTP request and response data
—
Pending
Does it follow best practices?
Impact
Pending
No eval scenarios have been run
Pending
The risk profile of this skill
An 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")