|
@@ -5,6 +5,7 @@
|
|
|
|
|
|
package io.opentelemetry.instrumentation.ktor.v2_0.server
|
|
|
|
|
|
+import io.ktor.http.*
|
|
|
import io.ktor.server.application.*
|
|
|
import io.ktor.server.request.*
|
|
|
import io.ktor.server.response.*
|
|
@@ -12,11 +13,14 @@ import io.ktor.server.routing.*
|
|
|
import io.ktor.util.*
|
|
|
import io.ktor.util.pipeline.*
|
|
|
import io.opentelemetry.api.OpenTelemetry
|
|
|
+import io.opentelemetry.api.common.AttributesBuilder
|
|
|
+import io.opentelemetry.api.trace.SpanKind
|
|
|
import io.opentelemetry.context.Context
|
|
|
import io.opentelemetry.extension.kotlin.asContextElement
|
|
|
import io.opentelemetry.instrumentation.api.instrumenter.AttributesExtractor
|
|
|
import io.opentelemetry.instrumentation.api.instrumenter.Instrumenter
|
|
|
import io.opentelemetry.instrumentation.api.instrumenter.SpanKindExtractor
|
|
|
+import io.opentelemetry.instrumentation.api.instrumenter.SpanStatusBuilder
|
|
|
import io.opentelemetry.instrumentation.api.instrumenter.SpanStatusExtractor
|
|
|
import io.opentelemetry.instrumentation.api.internal.InstrumenterUtil
|
|
|
import io.opentelemetry.instrumentation.api.semconv.http.HttpServerAttributesExtractor
|
|
@@ -53,32 +57,151 @@ class KtorServerTracing private constructor(
|
|
|
this.openTelemetry = openTelemetry
|
|
|
}
|
|
|
|
|
|
+ @Deprecated("Please use method `spanStatusExtractor`")
|
|
|
fun setStatusExtractor(
|
|
|
extractor: (SpanStatusExtractor<ApplicationRequest, ApplicationResponse>) -> SpanStatusExtractor<in ApplicationRequest, in ApplicationResponse>
|
|
|
) {
|
|
|
- this.statusExtractor = extractor
|
|
|
+ spanStatusExtractor { prevStatusExtractor ->
|
|
|
+ extractor(prevStatusExtractor).extract(spanStatusBuilder, request, response, error)
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ fun spanStatusExtractor(extract: SpanStatusData.(SpanStatusExtractor<ApplicationRequest, ApplicationResponse>) -> Unit) {
|
|
|
+ statusExtractor = { prevExtractor ->
|
|
|
+ SpanStatusExtractor<ApplicationRequest, ApplicationResponse> { spanStatusBuilder: SpanStatusBuilder,
|
|
|
+ request: ApplicationRequest,
|
|
|
+ response: ApplicationResponse?,
|
|
|
+ throwable: Throwable? ->
|
|
|
+ extract(SpanStatusData(spanStatusBuilder, request, response, throwable), prevExtractor)
|
|
|
+ }
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
+ data class SpanStatusData(
|
|
|
+ val spanStatusBuilder: SpanStatusBuilder,
|
|
|
+ val request: ApplicationRequest,
|
|
|
+ val response: ApplicationResponse?,
|
|
|
+ val error: Throwable?
|
|
|
+ )
|
|
|
+
|
|
|
+ @Deprecated("Please use method `spanKindExtractor`")
|
|
|
fun setSpanKindExtractor(extractor: (SpanKindExtractor<ApplicationRequest>) -> SpanKindExtractor<ApplicationRequest>) {
|
|
|
- this.spanKindExtractor = extractor
|
|
|
+ spanKindExtractor { prevSpanKindExtractor ->
|
|
|
+ extractor(prevSpanKindExtractor).extract(this)
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ fun spanKindExtractor(extract: ApplicationRequest.(SpanKindExtractor<ApplicationRequest>) -> SpanKind) {
|
|
|
+ spanKindExtractor = { prevExtractor ->
|
|
|
+ SpanKindExtractor<ApplicationRequest> { request: ApplicationRequest ->
|
|
|
+ extract(request, prevExtractor)
|
|
|
+ }
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
+ @Deprecated("Please use method `attributeExtractor`")
|
|
|
fun addAttributeExtractor(extractor: AttributesExtractor<in ApplicationRequest, in ApplicationResponse>) {
|
|
|
- additionalExtractors.add(extractor)
|
|
|
+ attributeExtractor {
|
|
|
+ onStart {
|
|
|
+ extractor.onStart(attributes, parentContext, request)
|
|
|
+ }
|
|
|
+ onEnd {
|
|
|
+ extractor.onEnd(attributes, parentContext, request, response, error)
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ fun attributeExtractor(extractorBuilder: ExtractorBuilder.() -> Unit = {}) {
|
|
|
+ val builder = ExtractorBuilder().apply(extractorBuilder).build()
|
|
|
+ additionalExtractors.add(
|
|
|
+ object : AttributesExtractor<ApplicationRequest, ApplicationResponse> {
|
|
|
+ override fun onStart(attributes: AttributesBuilder, parentContext: Context, request: ApplicationRequest) {
|
|
|
+ builder.onStart(OnStartData(attributes, parentContext, request))
|
|
|
+ }
|
|
|
+
|
|
|
+ override fun onEnd(attributes: AttributesBuilder, context: Context, request: ApplicationRequest, response: ApplicationResponse?, error: Throwable?) {
|
|
|
+ builder.onEnd(OnEndData(attributes, context, request, response, error))
|
|
|
+ }
|
|
|
+ }
|
|
|
+ )
|
|
|
}
|
|
|
|
|
|
- fun setCapturedRequestHeaders(requestHeaders: List<String>) {
|
|
|
- httpAttributesExtractorBuilder.setCapturedRequestHeaders(requestHeaders)
|
|
|
+ class ExtractorBuilder {
|
|
|
+ private var onStart: OnStartData.() -> Unit = {}
|
|
|
+ private var onEnd: OnEndData.() -> Unit = {}
|
|
|
+
|
|
|
+ fun onStart(block: OnStartData.() -> Unit) {
|
|
|
+ onStart = block
|
|
|
+ }
|
|
|
+
|
|
|
+ fun onEnd(block: OnEndData.() -> Unit) {
|
|
|
+ onEnd = block
|
|
|
+ }
|
|
|
+
|
|
|
+ internal fun build(): Extractor {
|
|
|
+ return Extractor(onStart, onEnd)
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
- fun setCapturedResponseHeaders(responseHeaders: List<String>) {
|
|
|
- httpAttributesExtractorBuilder.setCapturedResponseHeaders(responseHeaders)
|
|
|
+ internal class Extractor(val onStart: OnStartData.() -> Unit, val onEnd: OnEndData.() -> Unit)
|
|
|
+
|
|
|
+ data class OnStartData(
|
|
|
+ val attributes: AttributesBuilder,
|
|
|
+ val parentContext: Context,
|
|
|
+ val request: ApplicationRequest
|
|
|
+ )
|
|
|
+
|
|
|
+ data class OnEndData(
|
|
|
+ val attributes: AttributesBuilder,
|
|
|
+ val parentContext: Context,
|
|
|
+ val request: ApplicationRequest,
|
|
|
+ val response: ApplicationResponse?,
|
|
|
+ val error: Throwable?
|
|
|
+ )
|
|
|
+
|
|
|
+ @Deprecated(
|
|
|
+ "Please use method `capturedRequestHeaders`",
|
|
|
+ ReplaceWith("capturedRequestHeaders(headers)")
|
|
|
+ )
|
|
|
+ fun setCapturedRequestHeaders(headers: List<String>) = capturedRequestHeaders(headers)
|
|
|
+
|
|
|
+ fun capturedRequestHeaders(vararg headers: String) = capturedRequestHeaders(headers.asIterable())
|
|
|
+
|
|
|
+ fun capturedRequestHeaders(headers: Iterable<String>) {
|
|
|
+ httpAttributesExtractorBuilder.setCapturedRequestHeaders(headers.toList())
|
|
|
}
|
|
|
|
|
|
- fun setKnownMethods(knownMethods: Set<String>) {
|
|
|
- httpAttributesExtractorBuilder.setKnownMethods(knownMethods)
|
|
|
- httpSpanNameExtractorBuilder.setKnownMethods(knownMethods)
|
|
|
- httpServerRouteBuilder.setKnownMethods(knownMethods)
|
|
|
+ @Deprecated(
|
|
|
+ "Please use method `capturedResponseHeaders`",
|
|
|
+ ReplaceWith("capturedResponseHeaders(headers)")
|
|
|
+ )
|
|
|
+ fun setCapturedResponseHeaders(headers: List<String>) = capturedResponseHeaders(headers)
|
|
|
+
|
|
|
+ fun capturedResponseHeaders(vararg headers: String) = capturedResponseHeaders(headers.asIterable())
|
|
|
+
|
|
|
+ fun capturedResponseHeaders(headers: Iterable<String>) {
|
|
|
+ httpAttributesExtractorBuilder.setCapturedResponseHeaders(headers.toList())
|
|
|
+ }
|
|
|
+
|
|
|
+ @Deprecated(
|
|
|
+ "Please use method `knownMethods`",
|
|
|
+ ReplaceWith("knownMethods(knownMethods)")
|
|
|
+ )
|
|
|
+ fun setKnownMethods(knownMethods: Set<String>) = knownMethods(knownMethods)
|
|
|
+
|
|
|
+ fun knownMethods(vararg methods: String) = knownMethods(methods.asIterable())
|
|
|
+
|
|
|
+ fun knownMethods(vararg methods: HttpMethod) = knownMethods(methods.asIterable())
|
|
|
+
|
|
|
+ @JvmName("knownMethodsJvm")
|
|
|
+ fun knownMethods(methods: Iterable<HttpMethod>) = knownMethods(methods.map { it.value })
|
|
|
+
|
|
|
+ fun knownMethods(methods: Iterable<String>) {
|
|
|
+ methods.toSet().apply {
|
|
|
+ httpAttributesExtractorBuilder.setKnownMethods(this)
|
|
|
+ httpSpanNameExtractorBuilder.setKnownMethods(this)
|
|
|
+ httpServerRouteBuilder.setKnownMethods(this)
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
internal fun isOpenTelemetryInitialized(): Boolean = this::openTelemetry.isInitialized
|
|
@@ -107,9 +230,7 @@ class KtorServerTracing private constructor(
|
|
|
override fun install(pipeline: Application, configure: Configuration.() -> Unit): KtorServerTracing {
|
|
|
val configuration = Configuration().apply(configure)
|
|
|
|
|
|
- if (!configuration.isOpenTelemetryInitialized()) {
|
|
|
- throw IllegalArgumentException("OpenTelemetry must be set")
|
|
|
- }
|
|
|
+ require(configuration.isOpenTelemetryInitialized()) { "OpenTelemetry must be set" }
|
|
|
|
|
|
val httpAttributesGetter = KtorHttpServerAttributesGetter.INSTANCE
|
|
|
|