Skip to content

Commit

Permalink
HttpLoggingInterceptor (#157)
Browse files Browse the repository at this point in the history
* initial impl for HttpLoggerInterceptor.
Changed structure of LogInterceptor. Simplified LoggingStrategy

* Introduced flatMap for body, add timeMeasuring in http log

* improved tests for Curl logging

* Http log interecptor tests
  • Loading branch information
rybalkinsd authored Sep 11, 2019
1 parent b79ac37 commit ef6f909
Show file tree
Hide file tree
Showing 13 changed files with 412 additions and 179 deletions.
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
package io.github.rybalkinsd.kohttp.dsl

import io.github.rybalkinsd.kohttp.jackson.ext.toJson
import io.github.rybalkinsd.kohttp.assertContainsAtLeast
import io.github.rybalkinsd.kohttp.assertContainsExactly
import io.github.rybalkinsd.kohttp.client.defaultHttpClient
import io.github.rybalkinsd.kohttp.client.fork
import io.github.rybalkinsd.kohttp.interceptors.logging.HttpLoggingInterceptor
import io.github.rybalkinsd.kohttp.jackson.ext.toJson
import org.junit.Test
import java.io.File
import kotlin.test.assertEquals
Expand All @@ -29,7 +32,13 @@ class HttpPostDslKtTest {
"email" to "[email protected]"
)

httpPost {
val client = defaultHttpClient.fork {
interceptors {
+HttpLoggingInterceptor()
}
}

httpPost(client) {
host = "postman-echo.com"
path = "/post"

Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
package io.github.rybalkinsd.kohttp.interceptors.logging

import io.github.rybalkinsd.kohttp.ext.asSequence
import io.github.rybalkinsd.kohttp.util.flatMap
import okhttp3.*

/**
* Request Logging Interceptor
*
* Logs cURL commands for outgoing requests.
*
* @param log log consumer
*
* -->
* curl -X POST -H "one: 42" -H "cookie: aaa=bbb; ccc=42" \
* --data $'login=user&email=john.doe%40gmail.com' \
* "http://postman-echo.com/post?arg=iphone"
* ---
*
* @since 0.11.0
* @author doyaaaaaken, gokul, sergey
*/
class CurlLoggingInterceptor(
private val log: (String) -> Unit = ::println
) : Interceptor {
override fun intercept(chain: Interceptor.Chain): Response {
val request = chain.request()
log("-->")
val command = buildCurlCommand(request)
log(command)
log("---")

return chain.proceed(request)
}

private fun buildCurlCommand(request: Request) = buildString {
append("curl -X ${request.method()}")
append(buildCurlHeaderOption(request.headers()))
append(buildBodyOption(request.body()))
append(""" "${request.url()}"""")
}

private fun buildCurlHeaderOption(headers: Headers): String {
return headers.asSequence().map { (name, value) ->
val trimmedValue = value.trimDoubleQuote()
""" -H "$name: $trimmedValue""""
}.joinToString("")
}

private fun buildBodyOption(body: RequestBody?): String {
if (body == null) return ""
return body.flatMap {
it.replace("\n", "\\n")
.replace("\r", "\\r")
}.run {
" -d '$this'"
}
}

}

private fun String.trimDoubleQuote() =
if (startsWith('"') && endsWith('"')) {
substring(1, length - 1)
} else this

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
package io.github.rybalkinsd.kohttp.interceptors.logging

import io.github.rybalkinsd.kohttp.ext.asSequence
import io.github.rybalkinsd.kohttp.util.flatMap
import okhttp3.Headers
import okhttp3.Interceptor
import okhttp3.RequestBody
import okhttp3.Response
import java.util.*

/**
* Logs HTTP requests.
*
* @param log log consumer
*
* --> 706213ee-3a26-4d2d-ab09-f4895f7ce295
* POST http://postman-echo.com/post?arg=iphone
* one 42
* cookie aaa=bbb; ccc=42
*
* login=user&email=john.doe%40gmail.com
* ---
* <-- 706213ee-3a26-4d2d-ab09-f4895f7ce295
* 200 http://postman-echo.com/post?arg=iphone
* Content-Type application/json; charset=utf-8
* Server nginx
* Vary Accept-Encoding
* Connection keep-alive
* ---
*
* @since 0.11.0
* @author sergey
*/
class HttpLoggingInterceptor(
private val log: (String) -> Unit = ::println
) : Interceptor {

override fun intercept(chain: Interceptor.Chain): Response {
val request = chain.request()
val connection = chain.connection()
val id = UUID.randomUUID()

log("--> $id")
log("${request.method()} ${request.url()} ${connection?.protocol() ?: ""}")
log(request.headers())
log("")
request.body()?.let { log(it) }
log("---")


val start = System.currentTimeMillis()
val timing: Long
val response: Response = try {
chain.proceed(request).also {
timing = System.currentTimeMillis() - start
}
} catch (e: Exception) {
log("<-- FAIL: $e")
throw e
}

log("<-- $id in $timing ms")
log("${response.code()} ${response.request().url()}")
log(response.headers())
log("---")
return response
}

private fun log(headers: Headers) {
headers.asSequence().forEach { (k, v) ->
log("$k $v")
}
}

private fun log(body: RequestBody) {
body.flatMap {
it.replace("\n", "\\n")
.replace("\r", "\\r")
}.also {
log(it)
}
}
}

This file was deleted.

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package io.github.rybalkinsd.kohttp.util

import okhttp3.RequestBody
import okio.Buffer

/**
* Created by Sergey Rybalkin on 2019-09-11.
*/
internal inline fun <reified T> RequestBody.flatMap(transform: (String) -> T): T = transform(
Buffer().use {
writeTo(it)
it.readUtf8()
}
)
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@ import io.github.rybalkinsd.kohttp.client.defaultHttpClient
import io.github.rybalkinsd.kohttp.client.fork
import io.github.rybalkinsd.kohttp.dsl.upload
import io.github.rybalkinsd.kohttp.ext.httpGet
import io.github.rybalkinsd.kohttp.interceptors.logging.CurlLoggingStrategy
import io.github.rybalkinsd.kohttp.interceptors.logging.CurlLoggingInterceptor
import io.github.rybalkinsd.kohttp.interceptors.logging.HttpLoggingInterceptor
import org.junit.Test
import kotlin.test.assertEquals
import kotlin.test.assertTrue
Expand All @@ -15,7 +16,7 @@ class LoggingInterceptorTest {
var logSize = 0
val client = defaultHttpClient.fork {
interceptors {
+LoggingInterceptor {
+HttpLoggingInterceptor {
logSize += it.length
}
}
Expand All @@ -30,7 +31,7 @@ class LoggingInterceptorTest {
fun `default logging happens without exceptions`() {
val client = defaultHttpClient.fork {
interceptors {
+LoggingInterceptor()
+HttpLoggingInterceptor()
}
}

Expand All @@ -43,7 +44,7 @@ class LoggingInterceptorTest {
fun `default logging happens without exceptions when we have response body`() {
val client = defaultHttpClient.fork {
interceptors {
+LoggingInterceptor()
+HttpLoggingInterceptor()
}
}

Expand All @@ -60,7 +61,7 @@ class LoggingInterceptorTest {
fun `curl command logging happens without exceptions `() {
val client = defaultHttpClient.fork {
interceptors {
+LoggingInterceptor(CurlLoggingStrategy())
+CurlLoggingInterceptor()
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package io.github.rybalkinsd.kohttp.interceptors
import io.github.rybalkinsd.kohttp.client.defaultHttpClient
import io.github.rybalkinsd.kohttp.client.fork
import io.github.rybalkinsd.kohttp.dsl.httpGet
import io.github.rybalkinsd.kohttp.interceptors.logging.HttpLoggingInterceptor
import io.mockk.spyk
import io.mockk.verify
import okhttp3.*
Expand Down Expand Up @@ -107,7 +108,7 @@ class RetryInterceptorTest {
val query = (query() ?: "").toByteArray()
urlEncoder.encodeToString(md5.digest(query))
})
val loggingInterceptorSpy = spyk(LoggingInterceptor())
val loggingInterceptorSpy = spyk(HttpLoggingInterceptor())

createExpectationForGetWithResponseCode(4, 503)
val client = defaultHttpClient.fork {
Expand Down
Loading

0 comments on commit ef6f909

Please sign in to comment.