From 57b51d15879da91df1b06db166fe0210905492df Mon Sep 17 00:00:00 2001
From: Michael Rittmeister <michael@rittmeister.in>
Date: Fri, 10 Sep 2021 13:29:03 +0200
Subject: [PATCH 1/3] Migrate to Ktor 2.0.0

---
 build.gradle                                  | 15 +++---
 gradle/wrapper/gradle-wrapper.properties      |  2 +-
 .../ktor/opentracing/OpenTracingClient.kt     | 12 ++---
 .../ktor/opentracing/OpenTracingServer.kt     | 20 ++++----
 .../ktor/opentracing/RequestBuilderCarrier.kt |  2 +-
 .../kotlin/com/zopa/ktor/opentracing/utils.kt | 34 ++++++++-----
 .../ktor/opentracing/ConfigurationTest.kt     | 16 +++---
 .../zopa/ktor/opentracing/CoroutinesTest.kt   | 12 ++---
 .../opentracing/KtorOpenTracingServerTest.kt  | 51 ++++++++++---------
 .../ktor/opentracing/OpenTracingClientTest.kt | 14 +++--
 .../com/zopa/ktor/opentracing/SpanTest.kt     | 10 ++--
 .../com/zopa/ktor/opentracing/UuidUtilTest.kt | 10 ++--
 12 files changed, 106 insertions(+), 92 deletions(-)

diff --git a/build.gradle b/build.gradle
index 13efe75..efa73dc 100644
--- a/build.gradle
+++ b/build.gradle
@@ -1,6 +1,6 @@
 buildscript {
-    ext.kotlin_version = "1.5.10"
-    ext.ktor_version = "1.6.0"
+    ext.kotlin_version = "1.5.30"
+    ext.ktor_version = "2.0.0-eap-205"
     ext.opentracing_version = "0.33.0"
 }
 
@@ -14,10 +14,11 @@ apply from: "${rootDir}/scripts/publish-root.gradle"
 
 repositories {
     mavenCentral()
+    maven { url 'https://maven.pkg.jetbrains.space/public/p/ktor/eap' }
 }
 
 dependencies {
-    implementation "io.github.microutils:kotlin-logging-jvm:2.0.8"
+    implementation "io.github.microutils:kotlin-logging-jvm:2.0.11"
 
     implementation "io.ktor:ktor-server-core:$ktor_version"
     implementation "io.ktor:ktor-client-core:$ktor_version"
@@ -26,14 +27,12 @@ dependencies {
     implementation "io.opentracing:opentracing-noop:$opentracing_version"
     implementation "io.opentracing:opentracing-util:$opentracing_version"
 
-    testImplementation 'org.jetbrains.kotlin:kotlin-test:1.4.31'
-    testImplementation 'org.jetbrains.kotlin:kotlin-test-junit:1.4.31'
-    testImplementation "org.junit.jupiter:junit-jupiter-api:5.4.2"
-    testImplementation "com.willowtreeapps.assertk:assertk-jvm:0.23"
+    testImplementation 'org.junit.jupiter:junit-jupiter-api:5.7.2'
+    testImplementation 'com.willowtreeapps.assertk:assertk-jvm:0.24'
     testImplementation "io.opentracing:opentracing-mock:$opentracing_version"
     testImplementation "io.ktor:ktor-server-tests:$ktor_version"
     testImplementation "io.ktor:ktor-client-mock-jvm:$ktor_version"
-    testRuntime('org.junit.jupiter:junit-jupiter-engine:5.4.2')
+    testRuntimeOnly('org.junit.jupiter:junit-jupiter-engine:5.7.2')
 }
 
 test {
diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties
index 442d913..ffed3a2 100644
--- a/gradle/wrapper/gradle-wrapper.properties
+++ b/gradle/wrapper/gradle-wrapper.properties
@@ -1,5 +1,5 @@
 distributionBase=GRADLE_USER_HOME
 distributionPath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-6.8.3-bin.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-7.2-bin.zip
 zipStoreBase=GRADLE_USER_HOME
 zipStorePath=wrapper/dists
diff --git a/src/main/kotlin/com/zopa/ktor/opentracing/OpenTracingClient.kt b/src/main/kotlin/com/zopa/ktor/opentracing/OpenTracingClient.kt
index 80cdcb5..20cfa00 100644
--- a/src/main/kotlin/com/zopa/ktor/opentracing/OpenTracingClient.kt
+++ b/src/main/kotlin/com/zopa/ktor/opentracing/OpenTracingClient.kt
@@ -1,7 +1,7 @@
 package com.zopa.ktor.opentracing
 
 import io.ktor.client.HttpClient
-import io.ktor.client.features.HttpClientFeature
+import io.ktor.client.plugins.HttpClientPlugin
 import io.ktor.client.request.HttpSendPipeline
 import io.ktor.client.statement.HttpReceivePipeline
 import io.ktor.util.AttributeKey
@@ -13,7 +13,7 @@ import io.opentracing.tag.Tags
 class OpenTracingClient {
     class Config
 
-    companion object : HttpClientFeature<Config, OpenTracingClient> {
+    companion object : HttpClientPlugin<Config, OpenTracingClient> {
         override val key: AttributeKey<OpenTracingClient> = AttributeKey("OpenTracingClient")
 
         override fun prepare(block: Config.() -> Unit): OpenTracingClient {
@@ -31,8 +31,8 @@ class OpenTracingClient {
                     return@intercept
                 }
 
-                val pathUuid: PathUuid = context.url.encodedPath.UuidFromPath()
-                val name = "Call to ${context.method.value} ${context.url.host}${pathUuid.path}"
+                val pathUuid: PathUuid = context.url.pathSegments.toPathUuid()
+                val name = "Call to ${context.method.value} ${context.url.build().host}${pathUuid}"
 
                 val spanBuilder = tracer.buildSpan(name)
                 if (pathUuid.uuid != null) spanBuilder.withTag("UUID", pathUuid.uuid)
@@ -43,7 +43,7 @@ class OpenTracingClient {
 
                 Tags.SPAN_KIND.set(span, Tags.SPAN_KIND_CLIENT)
                 Tags.HTTP_METHOD.set(span, context.method.value)
-                Tags.HTTP_URL.set(span, "${context.url.host}${context.url.encodedPath}")
+                Tags.HTTP_URL.set(span, "${context.url.host}${context.url.pathSegments.joinToString("/")}")
 
                 spanStack.push(span)
                 tracer.inject(span?.context(), Format.Builtin.HTTP_HEADERS, RequestBuilderCarrier(context.headers))
@@ -62,7 +62,7 @@ class OpenTracingClient {
                 }
                 val span = spanStack.pop()
 
-                val statusCode = context.response.status
+                val statusCode = subject.status
                 Tags.HTTP_STATUS.set(span, statusCode.value)
                 if (statusCode.value >= 400) span.setTag("error", true)
 
diff --git a/src/main/kotlin/com/zopa/ktor/opentracing/OpenTracingServer.kt b/src/main/kotlin/com/zopa/ktor/opentracing/OpenTracingServer.kt
index 24ab8ed..f2a69e7 100644
--- a/src/main/kotlin/com/zopa/ktor/opentracing/OpenTracingServer.kt
+++ b/src/main/kotlin/com/zopa/ktor/opentracing/OpenTracingServer.kt
@@ -1,14 +1,14 @@
 package com.zopa.ktor.opentracing
 
-import io.ktor.application.Application
-import io.ktor.application.ApplicationCall
-import io.ktor.application.ApplicationCallPipeline
-import io.ktor.application.ApplicationFeature
-import io.ktor.application.call
+import io.ktor.server.application.Application
+import io.ktor.server.application.ApplicationCall
+import io.ktor.server.application.ApplicationCallPipeline
+import io.ktor.server.application.call
 import io.ktor.http.Headers
-import io.ktor.request.httpMethod
-import io.ktor.request.path
-import io.ktor.routing.Routing
+import io.ktor.server.application.ApplicationPlugin
+import io.ktor.server.request.httpMethod
+import io.ktor.server.request.path
+import io.ktor.server.routing.Routing
 import io.ktor.util.AttributeKey
 import io.ktor.util.pipeline.PipelinePhase
 import io.opentracing.Span
@@ -37,7 +37,7 @@ class OpenTracingServer {
         }
     }
 
-    companion object Feature : ApplicationFeature<Application, Configuration, OpenTracingServer> {
+    companion object Plugin : ApplicationPlugin<Application, Configuration, OpenTracingServer> {
         override val key = AttributeKey<OpenTracingServer>("OpenTracingServer")
         internal var config = Configuration()
 
@@ -92,7 +92,7 @@ class OpenTracingServer {
                     span.setTag(param.key, param.value.first())
                     pathWithParamsReplaced = pathWithParamsReplaced.replace(param.value.first(),  "{${param.key}}")
                 }
-                
+
                 span.setOperationName("${call.request.httpMethod.value} $pathWithParamsReplaced")
             }
 
diff --git a/src/main/kotlin/com/zopa/ktor/opentracing/RequestBuilderCarrier.kt b/src/main/kotlin/com/zopa/ktor/opentracing/RequestBuilderCarrier.kt
index a2c8610..39bf1cc 100644
--- a/src/main/kotlin/com/zopa/ktor/opentracing/RequestBuilderCarrier.kt
+++ b/src/main/kotlin/com/zopa/ktor/opentracing/RequestBuilderCarrier.kt
@@ -14,4 +14,4 @@ internal class RequestBuilderCarrier(private val headerBuilder: HeadersBuilder):
     override fun iterator(): MutableIterator<MutableMap.MutableEntry<String, String>> {
         throw UnsupportedOperationException("carrier is write-only")
     }
-}
\ No newline at end of file
+}
diff --git a/src/main/kotlin/com/zopa/ktor/opentracing/utils.kt b/src/main/kotlin/com/zopa/ktor/opentracing/utils.kt
index 4febc64..ee3e283 100644
--- a/src/main/kotlin/com/zopa/ktor/opentracing/utils.kt
+++ b/src/main/kotlin/com/zopa/ktor/opentracing/utils.kt
@@ -13,23 +13,33 @@ import kotlin.coroutines.coroutineContext
 
 val log = KotlinLogging.logger { }
 
-internal data class PathUuid(val path: String, val uuid: String?)
-internal fun String.UuidFromPath(): PathUuid {
-    val match = """\b[0-9a-fA-F]{8}\b-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-\b[0-9a-fA-F]{12}\b""".toRegex().find(this)
+private val uuidRegex =
+    """\b[0-9a-fA-F]{8}\b-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-\b[0-9a-fA-F]{12}\b""".toRegex()
 
-    if (match == null)
-        return PathUuid(this, null)
-    else {
-        val uuid = match.value
-        val pathWithReplacement = this.replace(uuid, "<UUID>")
-        return PathUuid(pathWithReplacement, uuid)
+internal data class PathUuid(val path: List<String>, val uuid: String?) {
+    override fun toString(): String = path.joinToString("/")
+}
+
+internal fun List<String>.toPathUuid(): PathUuid {
+    forEachIndexed { index, input ->
+        val match = uuidRegex.find(input)
+        if(match != null) {
+            val pathWithReplacement = toMutableList().apply {
+                removeAt(index)
+                add(index, "<UUID>")
+            }.toList()
+
+            return PathUuid(pathWithReplacement, match.value)
+        }
     }
+
+    return PathUuid(this, null)
 }
 
 fun getGlobalTracer(): Tracer {
     return GlobalTracer.get()
         ?: NoopTracerFactory.create()
-            .also { log.warn("Tracer not registered in GlobalTracer. Using Noop tracer instead.") }
+            .also { log.warn { "Tracer not registered in GlobalTracer. Using Noop tracer instead." } }
 }
 
 internal suspend fun Span.addCleanup() {
@@ -60,8 +70,8 @@ fun Span.addConfiguredLambdaTags() {
     Note that this function will give unexpected results if used in regular functions, extension functions and init functions. For these spans, it is preferable to define span names explicitly.
 */
 fun classAndMethodName(
-        currentInstance: Any,
-        anonymousObjectCreatedInMethod: Any
+    currentInstance: Any,
+    anonymousObjectCreatedInMethod: Any
 ): String {
     val className = currentInstance::class.simpleName
 
diff --git a/src/test/kotlin/com/zopa/ktor/opentracing/ConfigurationTest.kt b/src/test/kotlin/com/zopa/ktor/opentracing/ConfigurationTest.kt
index de4c20f..7bcf0f0 100644
--- a/src/test/kotlin/com/zopa/ktor/opentracing/ConfigurationTest.kt
+++ b/src/test/kotlin/com/zopa/ktor/opentracing/ConfigurationTest.kt
@@ -4,21 +4,21 @@ import assertk.assertThat
 import assertk.assertions.isEqualTo
 import assertk.assertions.isNull
 import com.zopa.ktor.opentracing.util.mockTracer
-import io.ktor.application.call
-import io.ktor.application.install
 import io.ktor.client.HttpClient
 import io.ktor.client.engine.mock.MockEngine
 import io.ktor.client.engine.mock.respond
 import io.ktor.client.request.get
+import io.ktor.client.statement.bodyAsText
 import io.ktor.http.HttpMethod
 import io.ktor.http.HttpStatusCode
-import io.ktor.request.path
-import io.ktor.response.respond
-import io.ktor.routing.get
-import io.ktor.routing.routing
+import io.ktor.server.application.call
+import io.ktor.server.application.install
+import io.ktor.server.request.path
+import io.ktor.server.response.respond
+import io.ktor.server.routing.get
+import io.ktor.server.routing.routing
 import io.ktor.server.testing.handleRequest
 import io.ktor.server.testing.withTestApplication
-import io.opentracing.mock.MockTracer
 import io.opentracing.util.GlobalTracer
 import org.junit.jupiter.api.BeforeEach
 import org.junit.jupiter.api.Test
@@ -130,7 +130,7 @@ class ConfigurationTest {
 
         application.routing {
             get(path) {
-                val clientResponse = client.get<String>("/member/74c144e6-ec05-49af-b3a2-217e1254897f")
+                val clientResponse = client.get("/member/74c144e6-ec05-49af-b3a2-217e1254897f").bodyAsText()
                 call.respond(clientResponse)
             }
         }
diff --git a/src/test/kotlin/com/zopa/ktor/opentracing/CoroutinesTest.kt b/src/test/kotlin/com/zopa/ktor/opentracing/CoroutinesTest.kt
index 1d2a4a6..2535d14 100644
--- a/src/test/kotlin/com/zopa/ktor/opentracing/CoroutinesTest.kt
+++ b/src/test/kotlin/com/zopa/ktor/opentracing/CoroutinesTest.kt
@@ -3,13 +3,13 @@ package com.zopa.ktor.opentracing
 import assertk.assertThat
 import assertk.assertions.isEqualTo
 import com.zopa.ktor.opentracing.util.mockTracer
-import io.ktor.application.call
-import io.ktor.application.install
 import io.ktor.http.HttpMethod
 import io.ktor.http.HttpStatusCode
-import io.ktor.response.respond
-import io.ktor.routing.get
-import io.ktor.routing.routing
+import io.ktor.server.application.call
+import io.ktor.server.application.install
+import io.ktor.server.response.respond
+import io.ktor.server.routing.get
+import io.ktor.server.routing.routing
 import io.ktor.server.testing.handleRequest
 import io.ktor.server.testing.withTestApplication
 import io.opentracing.util.GlobalTracer
@@ -39,7 +39,7 @@ class CoroutinesTest {
 
         fun getDouble(parentSpanName: Int): Int = span {
             setTag("parentSpanName", parentSpanName)
-            return getDoubleSecondTime(parentSpanName)*2
+            return getDoubleSecondTime(parentSpanName) * 2
         }
 
         application.routing {
diff --git a/src/test/kotlin/com/zopa/ktor/opentracing/KtorOpenTracingServerTest.kt b/src/test/kotlin/com/zopa/ktor/opentracing/KtorOpenTracingServerTest.kt
index 2e5d78b..44ae159 100644
--- a/src/test/kotlin/com/zopa/ktor/opentracing/KtorOpenTracingServerTest.kt
+++ b/src/test/kotlin/com/zopa/ktor/opentracing/KtorOpenTracingServerTest.kt
@@ -4,13 +4,13 @@ import assertk.assertThat
 import assertk.assertions.isEqualTo
 import assertk.assertions.isNotEqualTo
 import com.zopa.ktor.opentracing.util.mockTracer
-import io.ktor.application.call
-import io.ktor.application.install
 import io.ktor.http.HttpMethod
 import io.ktor.http.HttpStatusCode
-import io.ktor.response.respond
-import io.ktor.routing.get
-import io.ktor.routing.routing
+import io.ktor.server.application.call
+import io.ktor.server.application.install
+import io.ktor.server.response.respond
+import io.ktor.server.routing.get
+import io.ktor.server.routing.routing
 import io.ktor.server.testing.handleRequest
 import io.ktor.server.testing.withTestApplication
 import io.opentracing.propagation.Format
@@ -58,32 +58,33 @@ class KtorOpenTracingServerTest {
     }
 
     @Test
-    fun `(Bug) Server span name incorrectly has all occurrence of request param replaced with value`() = withTestApplication {
-        val routePath = "/hello/there/{name}"
-        val path = "/hello/there/hello"
+    fun `(Bug) Server span name incorrectly has all occurrence of request param replaced with value`() =
+        withTestApplication {
+            val routePath = "/hello/there/{name}"
+            val path = "/hello/there/hello"
 
-        application.install(OpenTracingServer)
+            application.install(OpenTracingServer)
 
-        application.routing {
-            get(routePath) {
-                call.respond("OK")
+            application.routing {
+                get(routePath) {
+                    call.respond("OK")
+                }
             }
-        }
-
-        handleRequest(HttpMethod.Get, path) {}.let { call ->
-            assertThat(call.response.status()).isEqualTo(HttpStatusCode.OK)
 
-            with(mockTracer.finishedSpans()) {
-                assertThat(size).isEqualTo(1)
-                assertThat(first().parentId()).isEqualTo(0L) // no parent span
-                assertThat(first().operationName()).isEqualTo("GET /{name}/there/{name}")
-                assertThat(first().tags().get("name")).isEqualTo("hello")
-                assertThat(first().tags().get("span.kind")).isEqualTo("server")
-                assertThat(first().tags().get("http.status_code")).isEqualTo(200)
+            handleRequest(HttpMethod.Get, path) {}.let { call ->
+                assertThat(call.response.status()).isEqualTo(HttpStatusCode.OK)
+
+                with(mockTracer.finishedSpans()) {
+                    assertThat(size).isEqualTo(1)
+                    assertThat(first().parentId()).isEqualTo(0L) // no parent span
+                    assertThat(first().operationName()).isEqualTo("GET /{name}/there/{name}")
+                    assertThat(first().tags().get("name")).isEqualTo("hello")
+                    assertThat(first().tags().get("span.kind")).isEqualTo("server")
+                    assertThat(first().tags().get("http.status_code")).isEqualTo(200)
+                }
             }
-        }
 
-    }
+        }
 
     @Test
     fun `Server span with error code has error tag`() = withTestApplication {
diff --git a/src/test/kotlin/com/zopa/ktor/opentracing/OpenTracingClientTest.kt b/src/test/kotlin/com/zopa/ktor/opentracing/OpenTracingClientTest.kt
index 448d1a7..c35e4ca 100644
--- a/src/test/kotlin/com/zopa/ktor/opentracing/OpenTracingClientTest.kt
+++ b/src/test/kotlin/com/zopa/ktor/opentracing/OpenTracingClientTest.kt
@@ -10,13 +10,17 @@ import io.ktor.application.install
 import io.ktor.client.HttpClient
 import io.ktor.client.engine.mock.MockEngine
 import io.ktor.client.engine.mock.respond
+import io.ktor.client.plugins.get
 import io.ktor.client.request.get
 import io.ktor.client.request.request
+import io.ktor.client.statement.bodyAsText
 import io.ktor.http.HttpMethod
 import io.ktor.http.HttpStatusCode
-import io.ktor.response.respond
-import io.ktor.routing.get
-import io.ktor.routing.routing
+import io.ktor.server.application.call
+import io.ktor.server.application.install
+import io.ktor.server.response.respond
+import io.ktor.server.routing.get
+import io.ktor.server.routing.routing
 import io.ktor.server.testing.handleRequest
 import io.ktor.server.testing.withTestApplication
 import io.opentracing.Span
@@ -56,7 +60,7 @@ class OpenTracingClientTest {
 
         application.routing {
             get(path) {
-                val clientResponse = client.get<String>("/member/74c144e6-ec05-49af-b3a2-217e1254897f")
+                val clientResponse = client.get("/member/74c144e6-ec05-49af-b3a2-217e1254897f").bodyAsText()
                 call.respond(clientResponse)
             }
         }
@@ -104,7 +108,7 @@ class OpenTracingClientTest {
 
         assertDoesNotThrow { runBlocking {
             withContext(threadLocalSpanStack.asContextElement(spanStack)) {
-                client.request<String>("/4DCA6409-D958-417E-B4DD-20738C721C48/view")
+                client.request("/4DCA6409-D958-417E-B4DD-20738C721C48/view").bodyAsText()
             }
         } }
 
diff --git a/src/test/kotlin/com/zopa/ktor/opentracing/SpanTest.kt b/src/test/kotlin/com/zopa/ktor/opentracing/SpanTest.kt
index 7140406..76b7750 100644
--- a/src/test/kotlin/com/zopa/ktor/opentracing/SpanTest.kt
+++ b/src/test/kotlin/com/zopa/ktor/opentracing/SpanTest.kt
@@ -3,13 +3,13 @@ package com.zopa.ktor.opentracing
 import assertk.assertThat
 import assertk.assertions.isEqualTo
 import com.zopa.ktor.opentracing.util.mockTracer
-import io.ktor.application.call
-import io.ktor.application.install
 import io.ktor.http.HttpMethod
 import io.ktor.http.HttpStatusCode
-import io.ktor.response.respond
-import io.ktor.routing.get
-import io.ktor.routing.routing
+import io.ktor.server.application.call
+import io.ktor.server.application.install
+import io.ktor.server.response.respond
+import io.ktor.server.routing.get
+import io.ktor.server.routing.routing
 import io.ktor.server.testing.handleRequest
 import io.ktor.server.testing.withTestApplication
 import io.opentracing.util.GlobalTracer
diff --git a/src/test/kotlin/com/zopa/ktor/opentracing/UuidUtilTest.kt b/src/test/kotlin/com/zopa/ktor/opentracing/UuidUtilTest.kt
index 051cf9b..0aa8ead 100644
--- a/src/test/kotlin/com/zopa/ktor/opentracing/UuidUtilTest.kt
+++ b/src/test/kotlin/com/zopa/ktor/opentracing/UuidUtilTest.kt
@@ -9,9 +9,9 @@ import org.junit.jupiter.api.TestInstance
 class UuidUtilTest {
     @Test
     fun `UuidFromPath returns unchanged path and no uuid if no UUID in path`() {
-        val path = "/evidence"
+        val path = listOf("evidence")
 
-        val pathUuid = path.UuidFromPath()
+        val pathUuid = path.toPathUuid()
 
         assertThat(pathUuid.path).isEqualTo(path)
         assertThat(pathUuid.uuid).isEqualTo(null)
@@ -19,11 +19,11 @@ class UuidUtilTest {
 
     @Test
     fun `UuidFromPath returns path with UUID and uuid if UUID in path`() {
-        val path = "/evidence/AB7AD59A-A0FF-4EB1-90CF-BC6D5C24095F"
+        val path = listOf("evidence", "AB7AD59A-A0FF-4EB1-90CF-BC6D5C24095F")
 
-        val pathUuid = path.UuidFromPath()
+        val pathUuid = path.toPathUuid()
 
-        assertThat(pathUuid.path).isEqualTo("/evidence/<UUID>")
+        assertThat(pathUuid.path).isEqualTo(listOf("evidence", "<UUID>"))
         assertThat(pathUuid.uuid).isEqualTo("AB7AD59A-A0FF-4EB1-90CF-BC6D5C24095F")
     }
 }

From 4c303bff0100a286869034c69dd79a44ccaf2794 Mon Sep 17 00:00:00 2001
From: Michael Rittmeister <michael@rittmeister.in>
Date: Fri, 10 Sep 2021 13:42:26 +0200
Subject: [PATCH 2/3] Fix intellij inspections

---
 build.gradle                                  |  5 ++++
 .../com/zopa/ktor/opentracing/Coroutines.kt   | 20 +++++++++----
 .../ktor/opentracing/OpenTracingClient.kt     |  2 +-
 .../ktor/opentracing/OpenTracingServer.kt     | 13 ++++----
 .../kotlin/com/zopa/ktor/opentracing/Span.kt  |  2 +-
 .../zopa/ktor/opentracing/CoroutinesTest.kt   |  2 +-
 .../opentracing/KtorOpenTracingServerTest.kt  | 22 +++++++-------
 .../zopa/ktor/opentracing/NamingUtilTest.kt   |  8 ++---
 .../ktor/opentracing/OpenTracingClientTest.kt | 30 ++++++++-----------
 .../com/zopa/ktor/opentracing/SpanTest.kt     | 11 +++----
 10 files changed, 62 insertions(+), 53 deletions(-)

diff --git a/build.gradle b/build.gradle
index efa73dc..ca2b696 100644
--- a/build.gradle
+++ b/build.gradle
@@ -29,6 +29,7 @@ dependencies {
 
     testImplementation 'org.junit.jupiter:junit-jupiter-api:5.7.2'
     testImplementation 'com.willowtreeapps.assertk:assertk-jvm:0.24'
+    testImplementation 'org.jetbrains.kotlinx:kotlinx-coroutines-test:1.5.2'
     testImplementation "io.opentracing:opentracing-mock:$opentracing_version"
     testImplementation "io.ktor:ktor-server-tests:$ktor_version"
     testImplementation "io.ktor:ktor-client-mock-jvm:$ktor_version"
@@ -45,4 +46,8 @@ ext {
     PUBLISH_ARTIFACT_ID = 'ktor-opentracing'
 }
 
+java {
+    sourceCompatibility = JavaVersion.VERSION_1_8
+}
+
 apply from: "${rootProject.projectDir}/scripts/publish-module.gradle"
diff --git a/src/main/kotlin/com/zopa/ktor/opentracing/Coroutines.kt b/src/main/kotlin/com/zopa/ktor/opentracing/Coroutines.kt
index 86b93f4..b2b13c6 100644
--- a/src/main/kotlin/com/zopa/ktor/opentracing/Coroutines.kt
+++ b/src/main/kotlin/com/zopa/ktor/opentracing/Coroutines.kt
@@ -3,11 +3,11 @@ package com.zopa.ktor.opentracing
 import io.opentracing.Span
 import kotlinx.coroutines.CoroutineScope
 import kotlinx.coroutines.CoroutineStart
-import kotlinx.coroutines.asContextElement
-import kotlinx.coroutines.launch
-import kotlinx.coroutines.Job
 import kotlinx.coroutines.Deferred
+import kotlinx.coroutines.Job
+import kotlinx.coroutines.asContextElement
 import kotlinx.coroutines.async
+import kotlinx.coroutines.launch
 import java.util.Stack
 import kotlin.coroutines.CoroutineContext
 import kotlin.coroutines.EmptyCoroutineContext
@@ -25,15 +25,23 @@ fun tracingContext(): CoroutineContext {
 }
 
 fun CoroutineScope.launchTraced(
-        context: CoroutineContext = EmptyCoroutineContext,
-        start: CoroutineStart = CoroutineStart.DEFAULT,
-        block: suspend CoroutineScope.() -> Unit
+    context: CoroutineContext = EmptyCoroutineContext,
+    start: CoroutineStart = CoroutineStart.DEFAULT,
+    block: suspend CoroutineScope.() -> Unit
 ): Job = launch(context + tracingContext(), start, block)
 
+@Suppress("DeferredIsResult")
+@Deprecated("Use tracedAsync instead", ReplaceWith("tracedAsync(context, start, block)"))
 fun <T> CoroutineScope.asyncTraced(
     context: CoroutineContext = EmptyCoroutineContext,
     start: CoroutineStart = CoroutineStart.DEFAULT,
     block: suspend CoroutineScope.() -> T
+) = tracedAsync(context, start, block)
+
+fun <T> CoroutineScope.tracedAsync(
+    context: CoroutineContext = EmptyCoroutineContext,
+    start: CoroutineStart = CoroutineStart.DEFAULT,
+    block: suspend CoroutineScope.() -> T
 ): Deferred<T> = async(context + tracingContext(), start) {
     block()
 }
diff --git a/src/main/kotlin/com/zopa/ktor/opentracing/OpenTracingClient.kt b/src/main/kotlin/com/zopa/ktor/opentracing/OpenTracingClient.kt
index 20cfa00..423be5a 100644
--- a/src/main/kotlin/com/zopa/ktor/opentracing/OpenTracingClient.kt
+++ b/src/main/kotlin/com/zopa/ktor/opentracing/OpenTracingClient.kt
@@ -20,7 +20,7 @@ class OpenTracingClient {
             return OpenTracingClient()
         }
 
-        override fun install(feature: OpenTracingClient, scope: HttpClient) {
+        override fun install(plugin: OpenTracingClient, scope: HttpClient) {
 
             val tracer: Tracer = getGlobalTracer()
 
diff --git a/src/main/kotlin/com/zopa/ktor/opentracing/OpenTracingServer.kt b/src/main/kotlin/com/zopa/ktor/opentracing/OpenTracingServer.kt
index f2a69e7..acee355 100644
--- a/src/main/kotlin/com/zopa/ktor/opentracing/OpenTracingServer.kt
+++ b/src/main/kotlin/com/zopa/ktor/opentracing/OpenTracingServer.kt
@@ -59,7 +59,8 @@ class OpenTracingServer {
                 val headers: MutableMap<String, String> = call.request.headers.toMap()
                 headers.remove("Authorization")
 
-                val clientSpanContext: SpanContext? = tracer.extract(Format.Builtin.HTTP_HEADERS, TextMapAdapter(headers))
+                val clientSpanContext: SpanContext? =
+                    tracer.extract(Format.Builtin.HTTP_HEADERS, TextMapAdapter(headers))
                 if (clientSpanContext == null) log.debug("Tracing context could not be found in request headers. Starting a new server trace.")
 
                 val spanName = "${context.request.httpMethod.value} ${context.request.path()}"
@@ -90,7 +91,7 @@ class OpenTracingServer {
                 var pathWithParamsReplaced = call.request.path()
                 call.parameters.entries().forEach { param ->
                     span.setTag(param.key, param.value.first())
-                    pathWithParamsReplaced = pathWithParamsReplaced.replace(param.value.first(),  "{${param.key}}")
+                    pathWithParamsReplaced = pathWithParamsReplaced.replace(param.value.first(), "{${param.key}}")
                 }
 
                 span.setOperationName("${call.request.httpMethod.value} $pathWithParamsReplaced")
@@ -124,10 +125,8 @@ class OpenTracingServer {
         }
 
         private fun Headers.toMap(): MutableMap<String, String> =
-                this.entries()
-                    .filter { (_, values) -> values.isNotEmpty() }
-                    .map { (key, values) -> key to values.first() }
-                    .toMap()
-                    .toMutableMap()
+            this.entries()
+                .filter { (_, values) -> values.isNotEmpty() }.associate { (key, values) -> key to values.first() }
+                .toMutableMap()
     }
 }
diff --git a/src/main/kotlin/com/zopa/ktor/opentracing/Span.kt b/src/main/kotlin/com/zopa/ktor/opentracing/Span.kt
index ba2c637..cbcac9e 100644
--- a/src/main/kotlin/com/zopa/ktor/opentracing/Span.kt
+++ b/src/main/kotlin/com/zopa/ktor/opentracing/Span.kt
@@ -9,7 +9,7 @@ inline fun <T> span(name: String = "defaultSpanName", block: Span.() -> T): T {
     span.addConfiguredLambdaTags()
 
     try {
-        tracer.scopeManager().activate(span).use { scope ->
+        tracer.scopeManager().activate(span).use {
             return block(span)
         }
     } finally {
diff --git a/src/test/kotlin/com/zopa/ktor/opentracing/CoroutinesTest.kt b/src/test/kotlin/com/zopa/ktor/opentracing/CoroutinesTest.kt
index 2535d14..e62aa5c 100644
--- a/src/test/kotlin/com/zopa/ktor/opentracing/CoroutinesTest.kt
+++ b/src/test/kotlin/com/zopa/ktor/opentracing/CoroutinesTest.kt
@@ -45,7 +45,7 @@ class CoroutinesTest {
         application.routing {
             get(path) {
                 val result: List<Int> = listOf(1, 10).map { id ->
-                    asyncTraced {
+                    tracedAsync {
                         span(id.toString()) {
                             getDouble(id)
                         }
diff --git a/src/test/kotlin/com/zopa/ktor/opentracing/KtorOpenTracingServerTest.kt b/src/test/kotlin/com/zopa/ktor/opentracing/KtorOpenTracingServerTest.kt
index 44ae159..8c5683b 100644
--- a/src/test/kotlin/com/zopa/ktor/opentracing/KtorOpenTracingServerTest.kt
+++ b/src/test/kotlin/com/zopa/ktor/opentracing/KtorOpenTracingServerTest.kt
@@ -49,10 +49,10 @@ class KtorOpenTracingServerTest {
                 assertThat(size).isEqualTo(1)
                 assertThat(first().parentId()).isEqualTo(0L) // no parent span
                 assertThat(first().operationName()).isEqualTo("GET /greeting/{id}/{name}")
-                assertThat(first().tags().get("id")).isEqualTo("ab7ad59a-a0ff-4eb1-90cf-bc6d5c24095f")
-                assertThat(first().tags().get("name")).isEqualTo("Ruth")
-                assertThat(first().tags().get("span.kind")).isEqualTo("server")
-                assertThat(first().tags().get("http.status_code")).isEqualTo(200)
+                assertThat(first().tags()["id"]).isEqualTo("ab7ad59a-a0ff-4eb1-90cf-bc6d5c24095f")
+                assertThat(first().tags()["name"]).isEqualTo("Ruth")
+                assertThat(first().tags()["span.kind"]).isEqualTo("server")
+                assertThat(first().tags()["http.status_code"]).isEqualTo(200)
             }
         }
     }
@@ -78,9 +78,9 @@ class KtorOpenTracingServerTest {
                     assertThat(size).isEqualTo(1)
                     assertThat(first().parentId()).isEqualTo(0L) // no parent span
                     assertThat(first().operationName()).isEqualTo("GET /{name}/there/{name}")
-                    assertThat(first().tags().get("name")).isEqualTo("hello")
-                    assertThat(first().tags().get("span.kind")).isEqualTo("server")
-                    assertThat(first().tags().get("http.status_code")).isEqualTo(200)
+                    assertThat(first().tags()["name"]).isEqualTo("hello")
+                    assertThat(first().tags()["span.kind"]).isEqualTo("server")
+                    assertThat(first().tags()["http.status_code"]).isEqualTo(200)
                 }
             }
 
@@ -106,9 +106,9 @@ class KtorOpenTracingServerTest {
                 assertThat(size).isEqualTo(1)
                 assertThat(first().parentId()).isEqualTo(0L) // no parent span
                 assertThat(first().operationName()).isEqualTo("GET /greeting")
-                assertThat(first().tags().get("span.kind")).isEqualTo("server")
-                assertThat(first().tags().get("http.status_code")).isEqualTo(401)
-                assertThat(first().tags().get("error")).isEqualTo(true)
+                assertThat(first().tags()["span.kind"]).isEqualTo("server")
+                assertThat(first().tags()["http.status_code"]).isEqualTo(401)
+                assertThat(first().tags()["error"]).isEqualTo(true)
             }
         }
     }
@@ -180,7 +180,7 @@ class KtorOpenTracingServerTest {
                 assertThat(first().parentId()).isNotEqualTo(0L) // has parent span
                 assertThat(first().references().first().referenceType).isEqualTo("child_of")
                 assertThat(first().operationName()).isEqualTo("GET /greeting")
-                assertThat(first().tags().get("span.kind")).isEqualTo("server")
+                assertThat(first().tags()["span.kind"]).isEqualTo("server")
             }
         }
     }
diff --git a/src/test/kotlin/com/zopa/ktor/opentracing/NamingUtilTest.kt b/src/test/kotlin/com/zopa/ktor/opentracing/NamingUtilTest.kt
index 26dec9f..1e7c96a 100644
--- a/src/test/kotlin/com/zopa/ktor/opentracing/NamingUtilTest.kt
+++ b/src/test/kotlin/com/zopa/ktor/opentracing/NamingUtilTest.kt
@@ -7,11 +7,11 @@ import org.junit.jupiter.api.Test
 
 class TracingUtilTest {
     @Test
-    fun `classAndMethodName returns class and method name for method of a class`() = runBlocking<Unit> {
+    fun `classAndMethodName returns class and method name for method of a class`() = runBlocking {
         var name: String? = null
 
         class Dog {
-            suspend fun bark() {
+            fun bark() {
                 name = classAndMethodName(this, object {})
             }
         }
@@ -40,7 +40,7 @@ class TracingUtilTest {
     fun `classAndMethodName does not return function name`() = runBlocking<Unit> {
         var name: String? = null
 
-        suspend fun bark() {
+        fun bark() {
             name = classAndMethodName(this, object {})
         }
 
@@ -50,7 +50,7 @@ class TracingUtilTest {
     }
 
     @Test
-    fun `classAndMethodName in extension function does not return function name`() = runBlocking<Unit> {
+    fun `classAndMethodName in extension function does not return function name`() = runBlocking {
         var name: String? = null
 
         fun String.toSomethingElse() {
diff --git a/src/test/kotlin/com/zopa/ktor/opentracing/OpenTracingClientTest.kt b/src/test/kotlin/com/zopa/ktor/opentracing/OpenTracingClientTest.kt
index c35e4ca..d5020df 100644
--- a/src/test/kotlin/com/zopa/ktor/opentracing/OpenTracingClientTest.kt
+++ b/src/test/kotlin/com/zopa/ktor/opentracing/OpenTracingClientTest.kt
@@ -5,12 +5,9 @@ import assertk.assertions.contains
 import assertk.assertions.isEqualTo
 import assertk.assertions.isNotEqualTo
 import com.zopa.ktor.opentracing.util.mockTracer
-import io.ktor.application.call
-import io.ktor.application.install
 import io.ktor.client.HttpClient
 import io.ktor.client.engine.mock.MockEngine
 import io.ktor.client.engine.mock.respond
-import io.ktor.client.plugins.get
 import io.ktor.client.request.get
 import io.ktor.client.request.request
 import io.ktor.client.statement.bodyAsText
@@ -26,14 +23,14 @@ import io.ktor.server.testing.withTestApplication
 import io.opentracing.Span
 import io.opentracing.util.GlobalTracer
 import kotlinx.coroutines.asContextElement
-import kotlinx.coroutines.runBlocking
+import kotlinx.coroutines.test.runBlockingTest
 import kotlinx.coroutines.withContext
 import org.junit.jupiter.api.Assertions.assertDoesNotThrow
 import org.junit.jupiter.api.BeforeEach
 import org.junit.jupiter.api.Test
 import org.junit.jupiter.api.TestInstance
 import org.junit.jupiter.api.TestInstance.Lifecycle
-import java.util.*
+import java.util.Stack
 
 @TestInstance(Lifecycle.PER_CLASS)
 class OpenTracingClientTest {
@@ -73,12 +70,12 @@ class OpenTracingClientTest {
 
                 assertThat(first().parentId()).isNotEqualTo(last().parentId())
                 assertThat(first().operationName()).isEqualTo("Call to GET localhostmember/<UUID>")
-                assertThat(first().tags().get("http.status_code")).isEqualTo(200)
-                assertThat(first().tags().get("UUID")).isEqualTo("74c144e6-ec05-49af-b3a2-217e1254897f")
+                assertThat(first().tags()["http.status_code"]).isEqualTo(200)
+                assertThat(first().tags()["UUID"]).isEqualTo("74c144e6-ec05-49af-b3a2-217e1254897f")
 
                 assertThat(last().parentId()).isEqualTo(0L)
                 assertThat(last().operationName()).isEqualTo("GET /sqrt")
-                assertThat(last().tags().get("span.kind")).isEqualTo("server")
+                assertThat(last().tags()["span.kind"]).isEqualTo("server")
             }
         }
     }
@@ -91,8 +88,7 @@ class OpenTracingClientTest {
                 addHandler { request ->
                     val headers = request.headers.entries()
                         .filter { (_, values) -> values.isNotEmpty() }
-                        .map { (key, values) -> key to values.first() }
-                        .toMap()
+                        .associate { (key, values) -> key to values.first() }
 
                     if (headers.containsKey("traceid"))
                         respond("OK", HttpStatusCode.OK)
@@ -106,19 +102,19 @@ class OpenTracingClientTest {
         val spanStack = Stack<Span>()
         spanStack.push(span)
 
-        assertDoesNotThrow { runBlocking {
-            withContext(threadLocalSpanStack.asContextElement(spanStack)) {
-                client.request("/4DCA6409-D958-417E-B4DD-20738C721C48/view").bodyAsText()
+        assertDoesNotThrow {
+            runBlockingTest {
+                withContext(threadLocalSpanStack.asContextElement(spanStack)) {
+                    client.request("/4DCA6409-D958-417E-B4DD-20738C721C48/view").bodyAsText()
+                }
             }
-        } }
+        }
 
         with(mockTracer.finishedSpans()) {
             assertThat(size).isEqualTo(1)
             assertThat(first().tags()).contains(Pair("span.kind", "client"))
             assertThat(first().operationName()).isEqualTo("Call to GET localhost<UUID>/view")
-            assertThat(first().tags()).contains(Pair("UUID","4DCA6409-D958-417E-B4DD-20738C721C48"))
+            assertThat(first().tags()).contains(Pair("UUID", "4DCA6409-D958-417E-B4DD-20738C721C48"))
         }
     }
-
-
 }
diff --git a/src/test/kotlin/com/zopa/ktor/opentracing/SpanTest.kt b/src/test/kotlin/com/zopa/ktor/opentracing/SpanTest.kt
index 76b7750..0e6b6df 100644
--- a/src/test/kotlin/com/zopa/ktor/opentracing/SpanTest.kt
+++ b/src/test/kotlin/com/zopa/ktor/opentracing/SpanTest.kt
@@ -14,7 +14,7 @@ import io.ktor.server.testing.handleRequest
 import io.ktor.server.testing.withTestApplication
 import io.opentracing.util.GlobalTracer
 import kotlinx.coroutines.delay
-import kotlinx.coroutines.runBlocking
+import kotlinx.coroutines.test.runBlockingTest
 import org.junit.jupiter.api.BeforeEach
 import org.junit.jupiter.api.Test
 import org.junit.jupiter.api.TestInstance
@@ -47,11 +47,12 @@ class SpanTest {
                 }
 
                 val sqrt: Double = sqrtOfInt(2)
-                val sqrtSuspend: Double = runBlocking {
-                    sqrtOfIntSuspend(10)
+                runBlockingTest {
+                    val sqrtSuspend: Double = sqrtOfIntSuspend(10)
+
+                    call.respond("Square root of 2: $sqrt, Square root of 10: $sqrtSuspend")
                 }
 
-                call.respond("Square root of 2: $sqrt, Square root of 10: $sqrtSuspend")
             }
         }
 
@@ -64,7 +65,7 @@ class SpanTest {
                 // server span
                 assertThat(last().parentId()).isEqualTo(0L)
                 assertThat(last().operationName()).isEqualTo("GET /sqrt")
-                assertThat(last().tags().get("span.kind")).isEqualTo("server")
+                assertThat(last().tags()["span.kind"]).isEqualTo("server")
 
                 // first child span
                 assertThat(first().context().traceId()).isEqualTo(last().context().traceId())

From f4308c02cfc5d1359407f435a05afff3637941d4 Mon Sep 17 00:00:00 2001
From: mingchuno <mingchuno@gmail.com>
Date: Thu, 28 Jul 2022 22:40:46 +0800
Subject: [PATCH 3/3] Migrate to ktor 2.0.3

---
 build.gradle                                  | 17 ++++++-----
 .../com/zopa/ktor/opentracing/Coroutines.kt   | 11 ++++---
 .../ktor/opentracing/OpenTracingClient.kt     | 14 +++++----
 .../ktor/opentracing/OpenTracingServer.kt     | 30 +++++++++----------
 .../kotlin/com/zopa/ktor/opentracing/Span.kt  |  2 +-
 .../ThreadContextElementScopeManager.kt       |  8 +++--
 .../kotlin/com/zopa/ktor/opentracing/utils.kt | 23 +++++++-------
 .../ktor/opentracing/OpenTracingClientTest.kt | 23 ++++++--------
 ...ServerTest.kt => OpenTracingServerTest.kt} |  2 +-
 .../com/zopa/ktor/opentracing/SpanTest.kt     |  5 ++--
 10 files changed, 66 insertions(+), 69 deletions(-)
 rename src/test/kotlin/com/zopa/ktor/opentracing/{KtorOpenTracingServerTest.kt => OpenTracingServerTest.kt} (99%)

diff --git a/build.gradle b/build.gradle
index ca2b696..8230468 100644
--- a/build.gradle
+++ b/build.gradle
@@ -1,6 +1,6 @@
 buildscript {
     ext.kotlin_version = "1.5.30"
-    ext.ktor_version = "2.0.0-eap-205"
+    ext.ktor_version = "2.0.3"
     ext.opentracing_version = "0.33.0"
 }
 
@@ -14,11 +14,10 @@ apply from: "${rootDir}/scripts/publish-root.gradle"
 
 repositories {
     mavenCentral()
-    maven { url 'https://maven.pkg.jetbrains.space/public/p/ktor/eap' }
 }
 
 dependencies {
-    implementation "io.github.microutils:kotlin-logging-jvm:2.0.11"
+    implementation 'io.github.microutils:kotlin-logging-jvm:2.1.23'
 
     implementation "io.ktor:ktor-server-core:$ktor_version"
     implementation "io.ktor:ktor-client-core:$ktor_version"
@@ -27,13 +26,13 @@ dependencies {
     implementation "io.opentracing:opentracing-noop:$opentracing_version"
     implementation "io.opentracing:opentracing-util:$opentracing_version"
 
-    testImplementation 'org.junit.jupiter:junit-jupiter-api:5.7.2'
-    testImplementation 'com.willowtreeapps.assertk:assertk-jvm:0.24'
-    testImplementation 'org.jetbrains.kotlinx:kotlinx-coroutines-test:1.5.2'
+    testImplementation 'org.junit.jupiter:junit-jupiter-api:5.8.2'
+    testImplementation 'com.willowtreeapps.assertk:assertk-jvm:0.25'
+    testImplementation 'org.jetbrains.kotlinx:kotlinx-coroutines-test:1.6.2'
     testImplementation "io.opentracing:opentracing-mock:$opentracing_version"
     testImplementation "io.ktor:ktor-server-tests:$ktor_version"
     testImplementation "io.ktor:ktor-client-mock-jvm:$ktor_version"
-    testRuntimeOnly('org.junit.jupiter:junit-jupiter-engine:5.7.2')
+    testRuntimeOnly('org.junit.jupiter:junit-jupiter-engine:5.8.2')
 }
 
 test {
@@ -50,4 +49,8 @@ java {
     sourceCompatibility = JavaVersion.VERSION_1_8
 }
 
+kotlin {
+    explicitApi()
+}
+
 apply from: "${rootProject.projectDir}/scripts/publish-module.gradle"
diff --git a/src/main/kotlin/com/zopa/ktor/opentracing/Coroutines.kt b/src/main/kotlin/com/zopa/ktor/opentracing/Coroutines.kt
index b2b13c6..333c316 100644
--- a/src/main/kotlin/com/zopa/ktor/opentracing/Coroutines.kt
+++ b/src/main/kotlin/com/zopa/ktor/opentracing/Coroutines.kt
@@ -12,8 +12,7 @@ import java.util.Stack
 import kotlin.coroutines.CoroutineContext
 import kotlin.coroutines.EmptyCoroutineContext
 
-
-fun tracingContext(): CoroutineContext {
+public fun tracingContext(): CoroutineContext {
     val activeSpan: Span? = getGlobalTracer().scopeManager().activeSpan()
 
     val spanStack = Stack<Span>()
@@ -24,7 +23,7 @@ fun tracingContext(): CoroutineContext {
     return threadLocalSpanStack.asContextElement(spanStack)
 }
 
-fun CoroutineScope.launchTraced(
+public fun CoroutineScope.launchTraced(
     context: CoroutineContext = EmptyCoroutineContext,
     start: CoroutineStart = CoroutineStart.DEFAULT,
     block: suspend CoroutineScope.() -> Unit
@@ -32,13 +31,13 @@ fun CoroutineScope.launchTraced(
 
 @Suppress("DeferredIsResult")
 @Deprecated("Use tracedAsync instead", ReplaceWith("tracedAsync(context, start, block)"))
-fun <T> CoroutineScope.asyncTraced(
+public fun <T> CoroutineScope.asyncTraced(
     context: CoroutineContext = EmptyCoroutineContext,
     start: CoroutineStart = CoroutineStart.DEFAULT,
     block: suspend CoroutineScope.() -> T
-) = tracedAsync(context, start, block)
+): Deferred<T> = tracedAsync(context, start, block)
 
-fun <T> CoroutineScope.tracedAsync(
+public fun <T> CoroutineScope.tracedAsync(
     context: CoroutineContext = EmptyCoroutineContext,
     start: CoroutineStart = CoroutineStart.DEFAULT,
     block: suspend CoroutineScope.() -> T
diff --git a/src/main/kotlin/com/zopa/ktor/opentracing/OpenTracingClient.kt b/src/main/kotlin/com/zopa/ktor/opentracing/OpenTracingClient.kt
index 423be5a..0a5f6d1 100644
--- a/src/main/kotlin/com/zopa/ktor/opentracing/OpenTracingClient.kt
+++ b/src/main/kotlin/com/zopa/ktor/opentracing/OpenTracingClient.kt
@@ -8,12 +8,14 @@ import io.ktor.util.AttributeKey
 import io.opentracing.Tracer
 import io.opentracing.propagation.Format
 import io.opentracing.tag.Tags
+import mu.KotlinLogging
 
+private val logger = KotlinLogging.logger {}
 
-class OpenTracingClient {
-    class Config
+public class OpenTracingClient {
+    public class Config
 
-    companion object : HttpClientPlugin<Config, OpenTracingClient> {
+    public companion object : HttpClientPlugin<Config, OpenTracingClient> {
         override val key: AttributeKey<OpenTracingClient> = AttributeKey("OpenTracingClient")
 
         override fun prepare(block: Config.() -> Unit): OpenTracingClient {
@@ -27,7 +29,7 @@ class OpenTracingClient {
             scope.sendPipeline.intercept(HttpSendPipeline.State) {
                 val spanStack = threadLocalSpanStack.get()
                 if (spanStack == null) {
-                    log.warn("spanStack is null")
+                    logger.warn("spanStack is null")
                     return@intercept
                 }
 
@@ -52,12 +54,12 @@ class OpenTracingClient {
             scope.receivePipeline.intercept(HttpReceivePipeline.State) {
                 val spanStack = threadLocalSpanStack.get()
                 if (spanStack == null) {
-                    log.warn("spanStack is null")
+                    logger.warn("spanStack is null")
                     return@intercept
                 }
 
                 if (spanStack.isEmpty()) {
-                    log.error("span could not be found in thread local span context")
+                    logger.error("span could not be found in thread local span context")
                     return@intercept
                 }
                 val span = spanStack.pop()
diff --git a/src/main/kotlin/com/zopa/ktor/opentracing/OpenTracingServer.kt b/src/main/kotlin/com/zopa/ktor/opentracing/OpenTracingServer.kt
index acee355..1b3cf98 100644
--- a/src/main/kotlin/com/zopa/ktor/opentracing/OpenTracingServer.kt
+++ b/src/main/kotlin/com/zopa/ktor/opentracing/OpenTracingServer.kt
@@ -1,11 +1,7 @@
 package com.zopa.ktor.opentracing
 
-import io.ktor.server.application.Application
-import io.ktor.server.application.ApplicationCall
-import io.ktor.server.application.ApplicationCallPipeline
-import io.ktor.server.application.call
 import io.ktor.http.Headers
-import io.ktor.server.application.ApplicationPlugin
+import io.ktor.server.application.*
 import io.ktor.server.request.httpMethod
 import io.ktor.server.request.path
 import io.ktor.server.routing.Routing
@@ -20,25 +16,27 @@ import io.opentracing.tag.Tags
 import io.opentracing.util.GlobalTracer
 import kotlinx.coroutines.asContextElement
 import kotlinx.coroutines.withContext
+import mu.KotlinLogging
 import java.util.Stack
 
+private val logger = KotlinLogging.logger {}
 
-class OpenTracingServer {
-    class Configuration {
-        val filters = mutableListOf<(ApplicationCall) -> Boolean>()
-        val lambdaTags = mutableListOf<Pair<String, () -> String>>()
+public class OpenTracingServer {
+    public class Configuration {
+        internal val filters = mutableListOf<(ApplicationCall) -> Boolean>()
+        internal val lambdaTags = mutableListOf<Pair<String, () -> String>>()
 
-        fun filter(predicate: (ApplicationCall) -> Boolean) {
+        public fun filter(predicate: (ApplicationCall) -> Boolean) {
             filters.add(predicate)
         }
 
-        fun addTag(name: String, lambda: () -> String) {
+        public fun addTag(name: String, lambda: () -> String) {
             lambdaTags.add(Pair(name, lambda))
         }
     }
 
-    companion object Plugin : ApplicationPlugin<Application, Configuration, OpenTracingServer> {
-        override val key = AttributeKey<OpenTracingServer>("OpenTracingServer")
+    public companion object Plugin : BaseApplicationPlugin<Application, Configuration, OpenTracingServer> {
+        override val key: AttributeKey<OpenTracingServer> = AttributeKey("OpenTracingServer")
         internal var config = Configuration()
 
         override fun install(pipeline: Application, configure: Configuration.() -> Unit): OpenTracingServer {
@@ -61,7 +59,7 @@ class OpenTracingServer {
 
                 val clientSpanContext: SpanContext? =
                     tracer.extract(Format.Builtin.HTTP_HEADERS, TextMapAdapter(headers))
-                if (clientSpanContext == null) log.debug("Tracing context could not be found in request headers. Starting a new server trace.")
+                if (clientSpanContext == null) logger.debug("Tracing context could not be found in request headers. Starting a new server trace.")
 
                 val spanName = "${context.request.httpMethod.value} ${context.request.path()}"
 
@@ -102,12 +100,12 @@ class OpenTracingServer {
 
                 val spanStack = threadLocalSpanStack.get()
                 if (spanStack == null) {
-                    log.warn("spanStack is null")
+                    logger.warn("spanStack is null")
                     return@intercept
                 }
 
                 if (spanStack.isEmpty()) {
-                    log.error("Active span could not be found in thread local trace context")
+                    logger.error("Active span could not be found in thread local trace context")
                     return@intercept
                 }
                 val span = spanStack.pop()
diff --git a/src/main/kotlin/com/zopa/ktor/opentracing/Span.kt b/src/main/kotlin/com/zopa/ktor/opentracing/Span.kt
index cbcac9e..9a1bb1c 100644
--- a/src/main/kotlin/com/zopa/ktor/opentracing/Span.kt
+++ b/src/main/kotlin/com/zopa/ktor/opentracing/Span.kt
@@ -2,7 +2,7 @@ package com.zopa.ktor.opentracing
 
 import io.opentracing.Span
 
-inline fun <T> span(name: String = "defaultSpanName", block: Span.() -> T): T {
+public inline fun <T> span(name: String = "defaultSpanName", block: Span.() -> T): T {
     val tracer = getGlobalTracer()
     val span = tracer.buildSpan(name).start()
 
diff --git a/src/main/kotlin/com/zopa/ktor/opentracing/ThreadContextElementScopeManager.kt b/src/main/kotlin/com/zopa/ktor/opentracing/ThreadContextElementScopeManager.kt
index fda1b1d..7766866 100644
--- a/src/main/kotlin/com/zopa/ktor/opentracing/ThreadContextElementScopeManager.kt
+++ b/src/main/kotlin/com/zopa/ktor/opentracing/ThreadContextElementScopeManager.kt
@@ -3,17 +3,19 @@ package com.zopa.ktor.opentracing
 import io.opentracing.Scope
 import io.opentracing.ScopeManager
 import io.opentracing.Span
+import mu.KotlinLogging
 import java.util.Stack
 
+private val logger = KotlinLogging.logger {}
 
 internal val threadLocalSpanStack = ThreadLocal<Stack<Span>>()
 
-class ThreadContextElementScopeManager: ScopeManager {
+public class ThreadContextElementScopeManager: ScopeManager {
     override fun activate(span: Span?): Scope {
         var spanStack = threadLocalSpanStack.get()
 
         if (spanStack == null) {
-            log.info { "Span stack is null, instantiating a new one." }
+            logger.info { "Span stack is null, instantiating a new one." }
             spanStack = Stack<Span>()
             threadLocalSpanStack.set(spanStack)
         }
@@ -32,7 +34,7 @@ internal class CoroutineThreadLocalScope: Scope {
     override fun close() {
         val spanStack = threadLocalSpanStack.get()
         if (spanStack == null) {
-            log.error { "spanStack is null" }
+            logger.error { "spanStack is null" }
             return
         }
 
diff --git a/src/main/kotlin/com/zopa/ktor/opentracing/utils.kt b/src/main/kotlin/com/zopa/ktor/opentracing/utils.kt
index ee3e283..f6ca3f7 100644
--- a/src/main/kotlin/com/zopa/ktor/opentracing/utils.kt
+++ b/src/main/kotlin/com/zopa/ktor/opentracing/utils.kt
@@ -10,8 +10,7 @@ import java.io.PrintWriter
 import java.io.StringWriter
 import kotlin.coroutines.coroutineContext
 
-
-val log = KotlinLogging.logger { }
+private val logger = KotlinLogging.logger {}
 
 private val uuidRegex =
     """\b[0-9a-fA-F]{8}\b-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-\b[0-9a-fA-F]{12}\b""".toRegex()
@@ -36,10 +35,10 @@ internal fun List<String>.toPathUuid(): PathUuid {
     return PathUuid(this, null)
 }
 
-fun getGlobalTracer(): Tracer {
+public fun getGlobalTracer(): Tracer {
     return GlobalTracer.get()
         ?: NoopTracerFactory.create()
-            .also { log.warn { "Tracer not registered in GlobalTracer. Using Noop tracer instead." } }
+            .also { logger.warn { "Tracer not registered in GlobalTracer. Using Noop tracer instead." } }
 }
 
 internal suspend fun Span.addCleanup() {
@@ -54,22 +53,22 @@ internal suspend fun Span.addCleanup() {
     }
 }
 
-fun Span.addConfiguredLambdaTags() {
+public fun Span.addConfiguredLambdaTags() {
     OpenTracingServer.config.lambdaTags.forEach {
         try {
             this.setTag(it.first, it.second.invoke())
         } catch (e: Exception) {
-            log.warn(e) { "Could not add tag: ${it.first}" }
+            logger.warn(e) { "Could not add tag: ${it.first}" }
         }
     }
 }
 
-/*
-    Helper function to name spans. Should only be used in method of a class as such:
-    classAndMethodName(this, object {})
-    Note that this function will give unexpected results if used in regular functions, extension functions and init functions. For these spans, it is preferable to define span names explicitly.
-*/
-fun classAndMethodName(
+/**
+ * Helper function to name spans. Should only be used in method of a class as such: `classAndMethodName(this, object {})`
+ * Note that this function will give unexpected results if used in regular functions, extension functions and init
+ * functions. For these spans, it is preferable to define span names explicitly.
+ */
+public fun classAndMethodName(
     currentInstance: Any,
     anonymousObjectCreatedInMethod: Any
 ): String {
diff --git a/src/test/kotlin/com/zopa/ktor/opentracing/OpenTracingClientTest.kt b/src/test/kotlin/com/zopa/ktor/opentracing/OpenTracingClientTest.kt
index d5020df..c1a5f28 100644
--- a/src/test/kotlin/com/zopa/ktor/opentracing/OpenTracingClientTest.kt
+++ b/src/test/kotlin/com/zopa/ktor/opentracing/OpenTracingClientTest.kt
@@ -13,18 +13,14 @@ import io.ktor.client.request.request
 import io.ktor.client.statement.bodyAsText
 import io.ktor.http.HttpMethod
 import io.ktor.http.HttpStatusCode
-import io.ktor.server.application.call
-import io.ktor.server.application.install
+import io.ktor.server.application.*
 import io.ktor.server.response.respond
-import io.ktor.server.routing.get
-import io.ktor.server.routing.routing
-import io.ktor.server.testing.handleRequest
-import io.ktor.server.testing.withTestApplication
+import io.ktor.server.routing.*
+import io.ktor.server.testing.*
 import io.opentracing.Span
 import io.opentracing.util.GlobalTracer
 import kotlinx.coroutines.asContextElement
-import kotlinx.coroutines.test.runBlockingTest
-import kotlinx.coroutines.withContext
+import kotlinx.coroutines.test.runTest
 import org.junit.jupiter.api.Assertions.assertDoesNotThrow
 import org.junit.jupiter.api.BeforeEach
 import org.junit.jupiter.api.Test
@@ -66,10 +62,11 @@ class OpenTracingClientTest {
             assertThat(call.response.status()).isEqualTo(HttpStatusCode.OK)
 
             with(mockTracer.finishedSpans()) {
+                println(this)
                 assertThat(size).isEqualTo(2)
 
                 assertThat(first().parentId()).isNotEqualTo(last().parentId())
-                assertThat(first().operationName()).isEqualTo("Call to GET localhostmember/<UUID>")
+                assertThat(first().operationName()).isEqualTo("Call to GET localhost/member/<UUID>")
                 assertThat(first().tags()["http.status_code"]).isEqualTo(200)
                 assertThat(first().tags()["UUID"]).isEqualTo("74c144e6-ec05-49af-b3a2-217e1254897f")
 
@@ -103,17 +100,15 @@ class OpenTracingClientTest {
         spanStack.push(span)
 
         assertDoesNotThrow {
-            runBlockingTest {
-                withContext(threadLocalSpanStack.asContextElement(spanStack)) {
-                    client.request("/4DCA6409-D958-417E-B4DD-20738C721C48/view").bodyAsText()
-                }
+            runTest(threadLocalSpanStack.asContextElement(spanStack)) {
+                client.request("/4DCA6409-D958-417E-B4DD-20738C721C48/view").bodyAsText()
             }
         }
 
         with(mockTracer.finishedSpans()) {
             assertThat(size).isEqualTo(1)
             assertThat(first().tags()).contains(Pair("span.kind", "client"))
-            assertThat(first().operationName()).isEqualTo("Call to GET localhost<UUID>/view")
+            assertThat(first().operationName()).isEqualTo("Call to GET localhost/<UUID>/view")
             assertThat(first().tags()).contains(Pair("UUID", "4DCA6409-D958-417E-B4DD-20738C721C48"))
         }
     }
diff --git a/src/test/kotlin/com/zopa/ktor/opentracing/KtorOpenTracingServerTest.kt b/src/test/kotlin/com/zopa/ktor/opentracing/OpenTracingServerTest.kt
similarity index 99%
rename from src/test/kotlin/com/zopa/ktor/opentracing/KtorOpenTracingServerTest.kt
rename to src/test/kotlin/com/zopa/ktor/opentracing/OpenTracingServerTest.kt
index 8c5683b..d6cdd1a 100644
--- a/src/test/kotlin/com/zopa/ktor/opentracing/KtorOpenTracingServerTest.kt
+++ b/src/test/kotlin/com/zopa/ktor/opentracing/OpenTracingServerTest.kt
@@ -22,7 +22,7 @@ import org.junit.jupiter.api.TestInstance
 import org.junit.jupiter.api.TestInstance.Lifecycle
 
 @TestInstance(Lifecycle.PER_CLASS)
-class KtorOpenTracingServerTest {
+class OpenTracingServerTest {
     @BeforeEach
     fun setup() {
         mockTracer.reset()
diff --git a/src/test/kotlin/com/zopa/ktor/opentracing/SpanTest.kt b/src/test/kotlin/com/zopa/ktor/opentracing/SpanTest.kt
index 0e6b6df..b789ae1 100644
--- a/src/test/kotlin/com/zopa/ktor/opentracing/SpanTest.kt
+++ b/src/test/kotlin/com/zopa/ktor/opentracing/SpanTest.kt
@@ -14,7 +14,7 @@ import io.ktor.server.testing.handleRequest
 import io.ktor.server.testing.withTestApplication
 import io.opentracing.util.GlobalTracer
 import kotlinx.coroutines.delay
-import kotlinx.coroutines.test.runBlockingTest
+import kotlinx.coroutines.test.runTest
 import org.junit.jupiter.api.BeforeEach
 import org.junit.jupiter.api.Test
 import org.junit.jupiter.api.TestInstance
@@ -47,9 +47,8 @@ class SpanTest {
                 }
 
                 val sqrt: Double = sqrtOfInt(2)
-                runBlockingTest {
+                runTest {
                     val sqrtSuspend: Double = sqrtOfIntSuspend(10)
-
                     call.respond("Square root of 2: $sqrt, Square root of 10: $sqrtSuspend")
                 }