SDK logging — How to implement Rest API logging if the Java SDK is used as a library in a client project (before SDK refactoring in Q3/Q4 2026)?
1. Overview
This guide explains how to implement REST API request/response logging for the Oddin SDK when consuming it as a library (JAR dependency). Since you cannot modify the SDK's internal classes, the approach uses Fuel HTTP interceptors to capture all HTTP traffic made by the SDK, including response payloads and timing information. How it works The Oddin SDK uses Fuel as its HTTP client. Fuel provides a global interceptor mechanism through FuelManager.instance that allows you to hook into every HTTP request and response — including those made internally by the SDK. By registering interceptors at application startup (before initializing the SDK), you can transparently capture:
- The HTTP method and URL of every SDK API call
- Timestamps and response times (network latency)
- Response status codes and bodies
- Error details for failed requests
ℹ️ Note: This approach requires no changes to the SDK source code. All configuration is done in your application.
2. Prerequisites
Your application must include the following dependencies alongside the Oddin SDK. Gradle (build.gradle) groovy
dependencies {
// SLF4J API
implementation 'org.slf4j:slf4j-api:1.7.36'
// Logback (logging implementation)
implementation 'ch.qos.logback:logback-classic:1.2.11'
implementation 'ch.qos.logback:logback-core:1.2.11'
// KotlinLogging (SLF4J facade for Kotlin)
implementation 'io.github.microutils:kotlin-logging:3.0.5'
}
Maven (pom.xml) xml
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.36</version>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.2.11</version>
</dependency>
<dependency>
<groupId>io.github.microutils</groupId>
<artifactId>kotlin-logging-jvm</artifactId>
<version>3.0.5</version>
</dependency>
3. Implementing the Fuel Interceptors
Register the following interceptors early in your application's initialization, before the Oddin SDK makes any HTTP calls. This is typically done in your main function or application bootstrap class.
3.1 Required Imports
kotlin
import com.github.kittinunf.fuel.core.FuelManager
import mu.KotlinLogging
import java.util.*
3.2 Logger Declaration
Create a logger instance named to match the SDK's ApiClient class. This naming is important — it allows the Logback configuration (Section 4) to route these logs to a dedicated file. kotlin
private val apiLogger = KotlinLogging.logger("com.oddin.oddsfeedsdk.api.ApiClient")
3.3 Request Interceptor (Timing)
This interceptor stamps each outgoing request with the current timestamp so the response interceptor can calculate network latency. kotlin
FuelManager.instance.addRequestInterceptor { next ->
{ request ->
request.tag = System.currentTimeMillis()
next(request)
}
}
3.4 Response Interceptor (Logging)
This interceptor captures the response, calculates timing, and logs the full details. kotlin
FuelManager.instance.addResponseInterceptor { next ->
{ request, response ->
val result = next(request, response)
val startTime = request.tag as? Long
val networkMillis = if (startTime != null)
System.currentTimeMillis() - startTime else -1
val responseBody = String(response.data)
val statusCode = response.statusCode
if (statusCode in 200..299) {
apiLogger.info {
"API Request: ${request.method} ${request.url}" +
" - Timestamp: ${Date()}" +
" - Response Time: ${networkMillis}ms" +
" - Status: $statusCode" +
" - Response Body: $responseBody"
}
} else {
apiLogger.warn {
"API Request: ${request.method} ${request.url}" +
" - Timestamp: ${Date()}" +
" - Response Time: ${networkMillis}ms" +
" - Status: $statusCode" +
" - Failed" +
" - Response Body: $responseBody"
}
}
result
}
}
3.5 Complete Example
Here is a full example of how the interceptor setup looks in a single initialization block: kotlin
import com.github.kittinunf.fuel.core.FuelManager
import mu.KotlinLogging
import java.util.*
private val apiLogger = KotlinLogging.logger("com.oddin.oddsfeedsdk.api.ApiClient")
fun setupApiLogging() {
// Stamp each request with a start timestamp
FuelManager.instance.addRequestInterceptor { next ->
{ request ->
request.tag = System.currentTimeMillis()
next(request)
}
}
// Log every response with timing and payload
FuelManager.instance.addResponseInterceptor { next ->
{ request, response ->
val result = next(request, response)
val startTime = request.tag as? Long
val networkMillis = if (startTime != null)
System.currentTimeMillis() - startTime else -1
val responseBody = String(response.data)
val statusCode = response.statusCode
if (statusCode in 200..299) {
apiLogger.info {
"API Request: ${request.method} ${request.url}" +
" - Timestamp: ${Date()}" +
" - Response Time: ${networkMillis}ms" +
" - Status: $statusCode" +
" - Response Body: $responseBody"
}
} else {
apiLogger.warn {
"API Request: ${request.method} ${request.url}" +
" - Timestamp: ${Date()}" +
" - Response Time: ${networkMillis}ms" +
" - Status: $statusCode" +
" - Failed" +
" - Response Body: $responseBody"
}
}
result
}
}
}
fun main() {
// Call this BEFORE initializing the Oddin SDK
setupApiLogging()
// Now initialize the SDK as usual
// val sdk = OddsFeedSdk(...)
}
4. Isolating Logs into a Dedicated File
By default, the interceptor logs will appear in your standard console output mixed with your application logs. To route them to a dedicated, automatically rotating compressed file, configure Logback as described below.
4.1 Logback Configuration
Create or edit the file src/main/resources/logback.xml in your application with the following content: xml
<configuration>
<!-- 1. Standard Console Appender for normal application logs -->
<appender name="STDOUT"
class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>%d{ISO8601} [%level] - %logger{36}:%line - %msg%n</pattern>
</encoder>
</appender>
<!-- 2. Rolling File Appender for API logs -->
<appender name="API_LOG_FILE"
class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>logs/api-requests.log</file>
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
<fileNamePattern>logs/api-requests-%d{yyyy-MM-dd}.%i.log.gz</fileNamePattern>
<maxFileSize>10MB</maxFileSize>
<maxHistory>30</maxHistory>
<totalSizeCap>500MB</totalSizeCap>
</rollingPolicy>
<encoder>
<pattern>%d{ISO8601} [%level] - %msg%n</pattern>
</encoder>
</appender>
<!-- 3. Route SDK API logs to the file appender only -->
<logger name="com.oddin.oddsfeedsdk.api.ApiClient"
level="INFO" additivity="false">
<appender-ref ref="API_LOG_FILE" />
</logger>
<!-- 4. Default root logger (console) -->
<root level="INFO">
<appender-ref ref="STDOUT" />
</root>
</configuration>
4.2 How It Works
- Logger name matching: Because the interceptor's logger is named
"com.oddin.oddsfeedsdk.api.ApiClient", the Logback<logger>element with that exact name captures all its output. - additivity="false": This prevents the API logs from propagating up to the root logger, so they will not appear in your console output.
- Rolling policy: Log files rotate daily and are compressed to
.gz. Files larger than 10 MB trigger an intra-day rollover. History is retained for 30 days with a 500 MB total cap. - Log location: Logs are written to
logs/api-requests.logrelative to your application's working directory.
5. Log Output Format
Once configured, each API call made by the SDK will produce a log entry like the following: Successful request
2025-07-15T14:32:01.123+00:00 [INFO] - API Request: GET
<https://api.oddin.gg/v1/sports/en/sport.xml>
- Timestamp: Tue Jul 15 14:32:00 UTC 2025
- Response Time: 142ms
- Status: 200
- Response Body: <?xml version="1.0" ...>...
Failed request
2025-07-15T14:32:05.456+00:00 [WARN] - API Request: POST
<https://api.oddin.gg/v1/some/endpoint>
- Timestamp: Tue Jul 15 14:32:05 UTC 2025
- Response Time: 89ms
- Status: 403
- Failed
- Response Body: {"error":"Forbidden"}
6. Summary of Steps
- Add the required dependencies (SLF4J, Logback, KotlinLogging) to your
build.gradleorpom.xml. - Create a
setupApiLogging()function that registers the Fuel request and response interceptors (see Section 3.5). - Call
setupApiLogging()before initializing the Oddin SDK. - Add the
logback.xmlconfiguration tosrc/main/resources/to isolate logs into a dedicated rotating file (see Section 4.1).
ℹ️ No changes to the SDK source code are required. All logging is implemented externally through Fuel's global interceptor mechanism and Logback's configuration.