or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

index.md
tile.json

tessl/maven-com-squareup-okhttp3--logging-interceptor

An OkHttp interceptor which logs HTTP request and response data

Workspace
tessl
Visibility
Public
Created
Last updated
Describes
mavenpkg:maven/com.squareup.okhttp3/logging-interceptor@4.12.x

To install, run

npx @tessl/cli install tessl/maven-com-squareup-okhttp3--logging-interceptor@4.12.0

index.mddocs/

OkHttp Logging Interceptor

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.

Package Information

  • Package Name: logging-interceptor
  • Package Type: Maven
  • Language: Kotlin (with Java interop)
  • Installation: implementation("com.squareup.okhttp3:logging-interceptor:4.12.0")

Core Imports

import okhttp3.logging.HttpLoggingInterceptor
import okhttp3.logging.LoggingEventListener

Java:

import okhttp3.logging.HttpLoggingInterceptor;
import okhttp3.logging.LoggingEventListener;

Basic Usage

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

Architecture

The library consists of two main components:

  • HttpLoggingInterceptor: The primary interceptor that logs HTTP request/response data at configurable detail levels
  • LoggingEventListener: An EventListener that provides detailed timing and connection-level event logging
  • Logger Interface: Pluggable logging interface allowing custom output destinations
  • Level Configuration: Four logging levels from silent to full body logging with header redaction

Capabilities

HTTP Request/Response Logging

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)

Custom Logger Implementation

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

Event-Level HTTP Logging

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

Types

// 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.Response

Security Considerations

Header Redaction:

  • Always redact sensitive headers like Authorization, Cookie, Set-Cookie
  • Use redactHeader() method to prevent credential leakage
  • Headers are redacted with "██" in logs

Body Logging Security:

  • Level.BODY exposes full request/response content
  • Only use in development/debugging environments
  • Consider data privacy and compliance requirements
  • Request bodies containing binary data are omitted automatically

Production Usage:

  • Use Level.NONE or Level.BASIC in production
  • Implement custom Logger to control log destination
  • Be aware that logs may contain sensitive URLs and headers
// 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")