5
5
6
6
package io.opentelemetry.instrumentation.ktor.v2_0.server
7
7
8
+ import io.ktor.http.*
8
9
import io.ktor.server.application.*
9
10
import io.ktor.server.request.*
10
11
import io.ktor.server.response.*
11
12
import io.ktor.server.routing.*
12
13
import io.ktor.util.*
13
14
import io.ktor.util.pipeline.*
14
15
import io.opentelemetry.api.OpenTelemetry
16
+ import io.opentelemetry.api.common.AttributesBuilder
17
+ import io.opentelemetry.api.trace.SpanKind
15
18
import io.opentelemetry.context.Context
16
19
import io.opentelemetry.extension.kotlin.asContextElement
17
20
import io.opentelemetry.instrumentation.api.instrumenter.AttributesExtractor
18
21
import io.opentelemetry.instrumentation.api.instrumenter.Instrumenter
19
22
import io.opentelemetry.instrumentation.api.instrumenter.SpanKindExtractor
23
+ import io.opentelemetry.instrumentation.api.instrumenter.SpanStatusBuilder
20
24
import io.opentelemetry.instrumentation.api.instrumenter.SpanStatusExtractor
21
25
import io.opentelemetry.instrumentation.api.internal.InstrumenterUtil
22
26
import io.opentelemetry.instrumentation.api.semconv.http.HttpServerAttributesExtractor
@@ -59,28 +63,143 @@ class KtorServerTracing private constructor(
59
63
this .statusExtractor = extractor
60
64
}
61
65
66
+ fun spanStatusExtractor (extract : SpanStatusData .() -> Unit ) {
67
+ setStatusExtractor {
68
+ SpanStatusExtractor <ApplicationRequest , ApplicationResponse > { spanStatusBuilder: SpanStatusBuilder ,
69
+ request: ApplicationRequest ,
70
+ response: ApplicationResponse ? ,
71
+ throwable: Throwable ? ->
72
+ extract(SpanStatusData (spanStatusBuilder, request, response, throwable))
73
+ }
74
+ }
75
+ }
76
+
77
+ data class SpanStatusData (
78
+ val spanStatusBuilder : SpanStatusBuilder ,
79
+ val request : ApplicationRequest ,
80
+ val response : ApplicationResponse ? ,
81
+ val error : Throwable ?
82
+ )
83
+
62
84
fun setSpanKindExtractor (extractor : (SpanKindExtractor <ApplicationRequest >) -> SpanKindExtractor <ApplicationRequest >) {
63
85
this .spanKindExtractor = extractor
64
86
}
65
87
88
+ fun spanKindExtractor (extract : ApplicationRequest .() -> SpanKind ) {
89
+ setSpanKindExtractor {
90
+ SpanKindExtractor <ApplicationRequest > { request: ApplicationRequest ->
91
+ extract(request)
92
+ }
93
+ }
94
+ }
95
+
66
96
fun addAttributeExtractor (extractor : AttributesExtractor <in ApplicationRequest , in ApplicationResponse >) {
67
97
additionalExtractors.add(extractor)
68
98
}
69
99
100
+ fun attributeExtractor (
101
+ extractorBuilder : ExtractorBuilder .() -> Unit = {}
102
+ ) {
103
+ val builder = ExtractorBuilder ().apply (extractorBuilder).build()
104
+ addAttributeExtractor(
105
+ object : AttributesExtractor <ApplicationRequest , ApplicationResponse > {
106
+ override fun onStart (attributes : AttributesBuilder , parentContext : Context , request : ApplicationRequest ) {
107
+ builder.onStart(OnStartData (attributes, parentContext, request))
108
+ }
109
+
110
+ override fun onEnd (
111
+ attributes : AttributesBuilder ,
112
+ context : Context ,
113
+ request : ApplicationRequest ,
114
+ response : ApplicationResponse ? ,
115
+ error : Throwable ?
116
+ ) {
117
+ builder.onEnd(OnEndData (attributes, context, request, response, error))
118
+ }
119
+ }
120
+ )
121
+ }
122
+
123
+ class ExtractorBuilder {
124
+ private var onStart: OnStartData .() -> Unit = {}
125
+ private var onEnd: OnEndData .() -> Unit = {}
126
+
127
+ fun onStart (block : OnStartData .() -> Unit ) {
128
+ onStart = block
129
+ }
130
+
131
+ fun onEnd (block : OnEndData .() -> Unit ) {
132
+ onEnd = block
133
+ }
134
+
135
+ internal fun build (): Extractor {
136
+ return Extractor (onStart, onEnd)
137
+ }
138
+ }
139
+
140
+ internal class Extractor (val onStart : OnStartData .() -> Unit , val onEnd : OnEndData .() -> Unit )
141
+
142
+ data class OnStartData (
143
+ val attributes : AttributesBuilder ,
144
+ val parentContext : Context ,
145
+ val request : ApplicationRequest
146
+ )
147
+
148
+ data class OnEndData (
149
+ val attributes : AttributesBuilder ,
150
+ val parentContext : Context ,
151
+ val request : ApplicationRequest ,
152
+ val response : ApplicationResponse ? ,
153
+ val error : Throwable ?
154
+ )
155
+
70
156
fun setCapturedRequestHeaders (requestHeaders : List <String >) {
71
157
httpAttributesExtractorBuilder.setCapturedRequestHeaders(requestHeaders)
72
158
}
73
159
160
+ fun capturedRequestHeaders (headers : Iterable <String >) {
161
+ setCapturedRequestHeaders(headers.toList())
162
+ }
163
+
164
+ fun capturedRequestHeaders (vararg headers : String ) {
165
+ capturedRequestHeaders(headers.asIterable())
166
+ }
167
+
74
168
fun setCapturedResponseHeaders (responseHeaders : List <String >) {
75
169
httpAttributesExtractorBuilder.setCapturedResponseHeaders(responseHeaders)
76
170
}
77
171
172
+ fun capturedResponseHeaders (headers : Iterable <String >) {
173
+ setCapturedResponseHeaders(headers.toList())
174
+ }
175
+
176
+ fun capturedResponseHeaders (vararg headers : String ) {
177
+ capturedResponseHeaders(headers.asIterable())
178
+ }
179
+
78
180
fun setKnownMethods (knownMethods : Set <String >) {
79
181
httpAttributesExtractorBuilder.setKnownMethods(knownMethods)
80
182
httpSpanNameExtractorBuilder.setKnownMethods(knownMethods)
81
183
httpServerRouteBuilder.setKnownMethods(knownMethods)
82
184
}
83
185
186
+ fun knownMethods (vararg methods : String ) {
187
+ setKnownMethods(methods.toSet())
188
+ }
189
+
190
+ fun knownMethods (methods : Iterable <String >) {
191
+ setKnownMethods(methods.toSet())
192
+ }
193
+
194
+ @JvmName(" knownMethodsJvm" )
195
+ fun knownMethods (methods : Iterable <HttpMethod >) {
196
+ knownMethods(methods.map { it.value })
197
+ }
198
+
199
+ fun knownMethods (vararg methods : HttpMethod ) {
200
+ knownMethods(methods.map { it.value })
201
+ }
202
+
84
203
internal fun isOpenTelemetryInitialized (): Boolean = this ::openTelemetry.isInitialized
85
204
}
86
205
@@ -107,9 +226,7 @@ class KtorServerTracing private constructor(
107
226
override fun install (pipeline : Application , configure : Configuration .() -> Unit ): KtorServerTracing {
108
227
val configuration = Configuration ().apply (configure)
109
228
110
- if (! configuration.isOpenTelemetryInitialized()) {
111
- throw IllegalArgumentException (" OpenTelemetry must be set" )
112
- }
229
+ require(configuration.isOpenTelemetryInitialized()) { " OpenTelemetry must be set" }
113
230
114
231
val httpAttributesGetter = KtorHttpServerAttributesGetter .INSTANCE
115
232
0 commit comments