-
Notifications
You must be signed in to change notification settings - Fork 345
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add extension functions and example for OpenTelemetry
- Loading branch information
Showing
33 changed files
with
1,090 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,87 @@ | ||
# OpenTelemetry-Ktor Demo | ||
|
||
[OpenTelemetry](https://opentelemetry.io/) provides support for Ktor with the `KtorClientTracing`and `KtorServerTracing` | ||
plugins for the Ktor client and server respectively. For the source code, see | ||
the [repository on GitHub](https://github.com/open-telemetry/opentelemetry-java-instrumentation/tree/main/instrumentation/ktor). | ||
|
||
This project contains extension functions for plugins that allow you to write code in the Ktor DSL style. | ||
|
||
Take the following code as an example: | ||
|
||
```kotlin | ||
install(KtorServerTracing) { | ||
... | ||
addAttributeExtractor( | ||
object : AttributesExtractor<ApplicationRequest, ApplicationResponse> { | ||
override fun onEnd( | ||
attributes: AttributesBuilder, | ||
context: Context, | ||
request: ApplicationRequest, | ||
response: ApplicationResponse?, | ||
error: Throwable? | ||
) { | ||
attributes.put("end-time", Instant.now().toEpochMilli()) | ||
} | ||
} | ||
) | ||
... | ||
} | ||
``` | ||
|
||
Rewritten in Ktor DSL style, it looks like the following: | ||
|
||
```kotlin | ||
install(KtorServerTracing) { | ||
... | ||
attributeExtractor { | ||
onEnd { | ||
attributes.put("end-time", Instant.now().toEpochMilli()) | ||
} | ||
} | ||
... | ||
} | ||
``` | ||
|
||
You can find all extensions for the client plugin `KtorClientTracing` in | ||
the [extractions](./client/src/main/kotlin/opentelemetry/ktor/example/plugins/opentelemetry/extractions/) folder. \ | ||
And you can find all extensions for the server plugin `KtorServerTracing` in | ||
the [extractions](./server/src/main/kotlin/opentelemetry/ktor/example/plugins/opentelemetry/extractions/) folder. | ||
|
||
## Running | ||
|
||
**Note:** You need to have [Docker](https://www.docker.com/) installed and running to run the sample. | ||
|
||
To run this sample, execute the following command from the `opentelemetry` directory:: | ||
|
||
```bash | ||
./gradlew :runWithDocker | ||
``` | ||
|
||
It will start a `Jaeger` in the docker container (`Jaeger UI` available on http://localhost:16686/search) and | ||
then it will start a `server` on http://localhost:8080/ | ||
|
||
Then, to run the client, which will send requests to a server, you can execute the following command in | ||
an `opentelemetry` directory: | ||
|
||
```bash | ||
./gradlew :client:run | ||
``` | ||
|
||
**Note:** In this example, we use | ||
an [Autoconfiguration OpenTelemetry instance](https://opentelemetry.io/docs/languages/java/instrumentation/#automatic-configuration), | ||
we set environment variables `OTEL_METRICS_EXPORTER` and `OTEL_EXPORTER_OTLP_ENDPOINT` | ||
in [build.gradle.kts](./build.gradle.kts) file. | ||
You can find more information about these environment variables | ||
in the [OpenTelemetry documentation](https://opentelemetry.io/docs/languages/sdk-configuration/). | ||
|
||
Let's check what we will see in the `Jaeger UI` after running the server (with Docker) and the client: | ||
|
||
1. We can see two our services that send opentelemetry data: `opentelemetry-ktor-sample-server` | ||
and `opentelemetry-ktor-sample-client`, and service `jaeger-all-in-one`, it's `Jaeger` tracing some of | ||
its components: | ||
![img.png](images/1.png) | ||
2. If you select `opentelemetry-ktor-sample-server` service and click on **Find traces**, you will see a list of traces: | ||
![img.png](images/2.png) | ||
3. If you click on one of those traces, you will be navigated to a screen providing detailed information about the | ||
selected trace: | ||
![img.png](images/3.png) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
description = "OpenTelemetry-Ktor example" | ||
|
||
plugins { | ||
id("com.avast.gradle.docker-compose") version "0.14.0" | ||
} | ||
|
||
subprojects { | ||
group = "opentelemetry.ktor.example" | ||
version = "0.0.1" | ||
|
||
repositories { | ||
mavenCentral() | ||
} | ||
} | ||
|
||
dockerCompose { | ||
useComposeFiles.add("docker/docker-compose.yml") | ||
} | ||
|
||
tasks.register("runWithDocker") { | ||
dependsOn("composeUp", ":server:run") | ||
} | ||
|
||
project(":server").setEnvironmentVariablesForOpenTelemetry() | ||
project(":client").setEnvironmentVariablesForOpenTelemetry() | ||
|
||
fun Project.setEnvironmentVariablesForOpenTelemetry() { | ||
tasks.withType<JavaExec> { | ||
environment("OTEL_METRICS_EXPORTER", "none") | ||
environment("OTEL_EXPORTER_OTLP_ENDPOINT", "http://localhost:4317/") | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
val ktor_version: String by project | ||
val logback_version: String by project | ||
val kotlin_version: String by project | ||
val opentelemetry_version: String by project | ||
|
||
plugins { | ||
kotlin("jvm") version "1.9.21" | ||
id("io.ktor.plugin") version "2.3.8" | ||
id("application") | ||
} | ||
|
||
application { | ||
mainClass.set("opentelemetry.ktor.example.ClientKt") | ||
|
||
val isDevelopment: Boolean = project.ext.has("development") | ||
applicationDefaultJvmArgs = listOf("-Dio.ktor.development=$isDevelopment") | ||
} | ||
|
||
dependencies { | ||
implementation(project(":shared")) | ||
|
||
implementation("io.ktor:ktor-client-core-jvm") | ||
implementation("io.ktor:ktor-client-cio-jvm") | ||
implementation("io.ktor:ktor-client-websockets:$ktor_version") | ||
implementation("ch.qos.logback:logback-classic:$logback_version") | ||
|
||
implementation("io.opentelemetry.instrumentation:opentelemetry-ktor-2.0:$opentelemetry_version-alpha") | ||
} |
21 changes: 21 additions & 0 deletions
21
opentelemetry/client/src/main/kotlin/opentelemetry/ktor/example/Client.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
package opentelemetry.ktor.example | ||
|
||
import io.ktor.client.* | ||
import io.ktor.client.engine.cio.* | ||
import io.ktor.client.plugins.* | ||
import io.ktor.client.plugins.websocket.* | ||
import opentelemetry.ktor.example.plugins.opentelemetry.setupClientTelemetry | ||
|
||
suspend fun main() { | ||
val client = HttpClient(CIO) { | ||
install(WebSockets) | ||
|
||
defaultRequest { | ||
url("http://$SERVER_HOST:$SERVER_PORT") | ||
} | ||
|
||
setupClientTelemetry() | ||
} | ||
|
||
doRequests(client) | ||
} |
43 changes: 43 additions & 0 deletions
43
opentelemetry/client/src/main/kotlin/opentelemetry/ktor/example/Requests.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,43 @@ | ||
package opentelemetry.ktor.example | ||
|
||
import io.ktor.client.* | ||
import io.ktor.client.plugins.websocket.* | ||
import io.ktor.client.request.* | ||
import io.ktor.websocket.* | ||
|
||
|
||
suspend fun doRequests(client: HttpClient) { | ||
// For this request you can see `CUSTOM` method instead of default `HTTP` in the Jaeger UI | ||
client.request("/known-methods") { | ||
method = CUSTOM_METHOD | ||
} | ||
|
||
// For this request you can't see `CUSTOM_NOT_KNOWN` method, you can see default `HTTP` in the Jaeger UI | ||
client.request("/known-methods") { | ||
method = CUSTOM_METHOD_NOT_KNOWN | ||
} | ||
|
||
// You can see tags `http.request.header.accept` and `http.response.header.content_type` for all requests | ||
// in the Jaeger UI and also `http.response.header.custom_header` for this request | ||
client.get("/captured-headers") | ||
|
||
// For this request you can see tag `error=true` and `Error` icon only for server trace in the Jaeger UI | ||
client.get("/span-status-extractor") | ||
|
||
// For this request you can see tag `span.kind=producer` only for server trace in the Jaeger UI | ||
client.post("/span-kind-extractor") | ||
|
||
// You can see attribute `start-time` and `end-time` in the Jaeger UI for all requests | ||
client.get("/attribute-extractor") | ||
|
||
// For this request you can see several spans and events only for server trace in the Jaeger UI | ||
client.get("/opentelemetry/tracer") | ||
|
||
// For this request you can see several events only for server trace in the Jaeger UI | ||
client.ws("/opentelemetry/websocket") { | ||
send(Frame.Text("Hello, world!")) | ||
repeat(10) { | ||
send(incoming.receive()) | ||
} | ||
} | ||
} |
69 changes: 69 additions & 0 deletions
69
...kotlin/opentelemetry/ktor/example/plugins/opentelemetry/extractions/attributeExtractor.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,69 @@ | ||
package opentelemetry.ktor.example.plugins.opentelemetry.extractions | ||
|
||
import io.ktor.client.request.* | ||
import io.ktor.client.statement.* | ||
import io.opentelemetry.api.common.AttributesBuilder | ||
import io.opentelemetry.context.Context | ||
import io.opentelemetry.instrumentation.api.instrumenter.AttributesExtractor | ||
import io.opentelemetry.instrumentation.ktor.v2_0.client.KtorClientTracingBuilder | ||
|
||
// addAttributeExtractor | ||
fun KtorClientTracingBuilder.attributeExtractor( | ||
extractorBuilder: ExtractorBuilder.() -> Unit = {} | ||
) { | ||
val builder = ExtractorBuilder().apply(extractorBuilder).build() | ||
addAttributesExtractors( | ||
object : AttributesExtractor<HttpRequestData, HttpResponse> { | ||
override fun onStart( | ||
attributes: AttributesBuilder, | ||
parentContext: Context, | ||
request: HttpRequestData | ||
) { | ||
builder.onStart(OnStartData(attributes, parentContext, request)) | ||
} | ||
|
||
override fun onEnd( | ||
attributes: AttributesBuilder, | ||
context: Context, | ||
request: HttpRequestData, | ||
response: HttpResponse?, | ||
error: Throwable? | ||
) { | ||
builder.onEnd(OnEndData(attributes, context, request, response, error)) | ||
} | ||
} | ||
) | ||
} | ||
|
||
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) | ||
} | ||
} | ||
|
||
internal class Extractor(val onStart: OnStartData.() -> Unit, val onEnd: OnEndData.() -> Unit) | ||
|
||
data class OnStartData( | ||
val attributes: AttributesBuilder, | ||
val parentContext: Context, | ||
val request: HttpRequestData | ||
) | ||
|
||
data class OnEndData( | ||
val attributes: AttributesBuilder, | ||
val parentContext: Context, | ||
val request: HttpRequestData, | ||
val response: HttpResponse?, | ||
val error: Throwable? | ||
) |
21 changes: 21 additions & 0 deletions
21
...in/kotlin/opentelemetry/ktor/example/plugins/opentelemetry/extractions/capturedHeaders.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
package opentelemetry.ktor.example.plugins.opentelemetry.extractions | ||
|
||
import io.opentelemetry.instrumentation.ktor.v2_0.client.KtorClientTracingBuilder | ||
|
||
// setCapturedRequestHeaders | ||
fun KtorClientTracingBuilder.capturedRequestHeaders(vararg headers: String) { | ||
capturedRequestHeaders(headers.asIterable()) | ||
} | ||
|
||
fun KtorClientTracingBuilder.capturedRequestHeaders(headers: Iterable<String>) { | ||
setCapturedRequestHeaders(headers.toList()) | ||
} | ||
|
||
// setCapturedResponseHeaders | ||
fun KtorClientTracingBuilder.capturedResponseHeaders(vararg headers: String) { | ||
capturedResponseHeaders(headers.asIterable()) | ||
} | ||
|
||
fun KtorClientTracingBuilder.capturedResponseHeaders(headers: Iterable<String>) { | ||
setCapturedResponseHeaders(headers.toList()) | ||
} |
8 changes: 8 additions & 0 deletions
8
...metry/ktor/example/plugins/opentelemetry/extractions/emitExperimentalHttpClientMetrics.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
package opentelemetry.ktor.example.plugins.opentelemetry.extractions | ||
|
||
import io.opentelemetry.instrumentation.ktor.v2_0.client.KtorClientTracingBuilder | ||
|
||
// setEmitExperimentalHttpClientMetrics | ||
fun KtorClientTracingBuilder.emitExperimentalHttpClientMetrics() { | ||
setEmitExperimentalHttpClientMetrics(true) | ||
} |
13 changes: 13 additions & 0 deletions
13
.../main/kotlin/opentelemetry/ktor/example/plugins/opentelemetry/extractions/knownMethods.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
package opentelemetry.ktor.example.plugins.opentelemetry.extractions | ||
|
||
import io.ktor.http.* | ||
import io.opentelemetry.instrumentation.ktor.v2_0.client.KtorClientTracingBuilder | ||
|
||
// setKnownMethods | ||
fun KtorClientTracingBuilder.knownMethods(vararg methods: HttpMethod) { | ||
knownMethods(methods.asIterable()) | ||
} | ||
|
||
fun KtorClientTracingBuilder.knownMethods(methods: Iterable<HttpMethod>) { | ||
setKnownMethods(methods.map { it.value }.toSet()) | ||
} |
36 changes: 36 additions & 0 deletions
36
.../src/main/kotlin/opentelemetry/ktor/example/plugins/opentelemetry/setupClientTelemetry.kt
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
package opentelemetry.ktor.example.plugins.opentelemetry | ||
|
||
import io.ktor.client.* | ||
import io.ktor.client.engine.cio.* | ||
import io.ktor.http.* | ||
import io.opentelemetry.instrumentation.ktor.v2_0.client.KtorClientTracing | ||
import opentelemetry.ktor.example.CUSTOM_HEADER | ||
import opentelemetry.ktor.example.CUSTOM_METHOD | ||
import opentelemetry.ktor.example.getOpenTelemetry | ||
import opentelemetry.ktor.example.plugins.opentelemetry.extractions.* | ||
|
||
/** | ||
* Install OpenTelemetry on the client. | ||
* You can see usages of new extension functions for [KtorClientTracing]. | ||
*/ | ||
fun HttpClientConfig<CIOEngineConfig>.setupClientTelemetry() { | ||
val openTelemetry = getOpenTelemetry(serviceName = "opentelemetry-ktor-sample-client") | ||
install(KtorClientTracing) { | ||
setOpenTelemetry(openTelemetry) | ||
|
||
emitExperimentalHttpClientMetrics() | ||
|
||
knownMethods(HttpMethod.DefaultMethods + CUSTOM_METHOD) | ||
capturedRequestHeaders(HttpHeaders.Accept) | ||
capturedResponseHeaders(HttpHeaders.ContentType, CUSTOM_HEADER) | ||
|
||
attributeExtractor { | ||
onStart { | ||
attributes.put("start-time", System.currentTimeMillis()) | ||
} | ||
onEnd { | ||
attributes.put("end-time", System.currentTimeMillis()) | ||
} | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
# This file is used to start the Jaeger all-in-one container | ||
version: '3.7' | ||
services: | ||
jaeger: | ||
image: jaegertracing/all-in-one:latest | ||
ports: | ||
- "4317:4317" # OTLP gRPC receiver | ||
- "16686:16686" # Jaeger UI |
Oops, something went wrong.