Skip to content

Commit

Permalink
Add Ktor 3.0.0 support
Browse files Browse the repository at this point in the history
  • Loading branch information
e5l committed Oct 17, 2024
1 parent 4497fbf commit 2aa3669
Show file tree
Hide file tree
Showing 28 changed files with 1,536 additions and 1 deletion.
2 changes: 1 addition & 1 deletion docs/supported-libraries.md
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ These are the supported libraries and frameworks:
| [Jodd Http](https://http.jodd.org/) | 4.2+ | N/A | [HTTP Client Spans], [HTTP Client Metrics] |
| [JSP](https://javaee.github.io/javaee-spec/javadocs/javax/servlet/jsp/package-summary.html) | 2.3+ | N/A | Controller Spans [3] |
| [Kotlin Coroutines](https://kotlinlang.org/docs/coroutines-overview.html) | 1.0+ | N/A | Context propagation |
| [Ktor](https://github.com/ktorio/ktor) | 1.0+ | [opentelemetry-ktor-1.0](../instrumentation/ktor/ktor-1.0/library),<br>[opentelemetry-ktor-2.0](../instrumentation/ktor/ktor-2.0/library) | [HTTP Client Spans], [HTTP Client Metrics], [HTTP Server Spans], [HTTP Server Metrics] |
| [Ktor](https://github.com/ktorio/ktor) | 1.0+ | [opentelemetry-ktor-1.0](../instrumentation/ktor/ktor-1.0/library),<br>[opentelemetry-ktor-2.0](../instrumentation/ktor/ktor-2.0/library),<br>[opentelemetry-ktor-3.0](../instrumentation/ktor/ktor-3.0/library) | [HTTP Client Spans], [HTTP Client Metrics], [HTTP Server Spans], [HTTP Server Metrics] |
| [Kubernetes Client](https://github.com/kubernetes-client/java) | 7.0+ | N/A | [HTTP Client Spans] |
| [Lettuce](https://github.com/lettuce-io/lettuce-core) | 4.0+ | [opentelemetry-lettuce-5.1](../instrumentation/lettuce/lettuce-5.1/library) | [Database Client Spans] |
| [Log4j 1](https://logging.apache.org/log4j/1.2/) | 1.2+ | N/A | none |
Expand Down
48 changes: 48 additions & 0 deletions instrumentation/ktor/ktor-3.0/javaagent/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import org.jetbrains.kotlin.gradle.dsl.JvmTarget

plugins {
id("org.jetbrains.kotlin.jvm")
id("otel.javaagent-instrumentation")
}

muzzle {
pass {
group.set("org.jetbrains.kotlinx")
module.set("ktor-server-core")
versions.set("[3.0.0,)")
assertInverse.set(true)
}
}

val ktorVersion = "3.0.0"

dependencies {
library("io.ktor:ktor-client-core:$ktorVersion")
library("io.ktor:ktor-server-core:$ktorVersion")

implementation(project(":instrumentation:ktor:ktor-3.0:library"))

compileOnly("org.jetbrains.kotlin:kotlin-stdlib-jdk8")

testInstrumentation(project(":instrumentation:netty:netty-4.1:javaagent"))

testImplementation(project(":instrumentation:ktor:ktor-3.0:testing"))
testImplementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8")
testImplementation("io.opentelemetry:opentelemetry-extension-kotlin")

testLibrary("io.ktor:ktor-server-netty:$ktorVersion")
testLibrary("io.ktor:ktor-client-cio:$ktorVersion")

latestDepTestLibrary("io.ktor:ktor-client-core:3.+")
latestDepTestLibrary("io.ktor:ktor-server-core:3.+")
latestDepTestLibrary("io.ktor:ktor-server-netty:3.+")
latestDepTestLibrary("io.ktor:ktor-client-cio:3.+")
}

kotlin {
compilerOptions {
jvmTarget.set(JvmTarget.JVM_1_8)
// generate metadata for Java 1.8 reflection on method parameters, used in @WithSpan tests
javaParameters = true
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/

package io.opentelemetry.javaagent.instrumentation.ktor.v3_0;

import static net.bytebuddy.matcher.ElementMatchers.isConstructor;
import static net.bytebuddy.matcher.ElementMatchers.named;
import static net.bytebuddy.matcher.ElementMatchers.takesArgument;
import static net.bytebuddy.matcher.ElementMatchers.takesArguments;

import io.ktor.client.HttpClientConfig;
import io.ktor.client.engine.HttpClientEngineConfig;
import io.opentelemetry.api.GlobalOpenTelemetry;
import io.opentelemetry.instrumentation.ktor.v3_0.client.KtorClientTracing;
import io.opentelemetry.instrumentation.ktor.v3_0.client.KtorClientTracingBuilder;
import io.opentelemetry.instrumentation.ktor.v3_0.internal.KtorBuilderUtil;
import io.opentelemetry.javaagent.bootstrap.internal.AgentCommonConfig;
import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation;
import io.opentelemetry.javaagent.extension.instrumentation.TypeTransformer;
import kotlin.Unit;
import kotlin.jvm.functions.Function1;
import net.bytebuddy.asm.Advice;
import net.bytebuddy.description.type.TypeDescription;
import net.bytebuddy.matcher.ElementMatcher;

public class HttpClientInstrumentation implements TypeInstrumentation {
@Override
public ElementMatcher<TypeDescription> typeMatcher() {
return named("io.ktor.client.HttpClient");
}

@Override
public void transform(TypeTransformer transformer) {
transformer.applyAdviceToMethod(
isConstructor()
.and(takesArguments(2))
.and(takesArgument(1, named("io.ktor.client.HttpClientConfig"))),
this.getClass().getName() + "$ConstructorAdvice");
}

@SuppressWarnings("unused")
public static class ConstructorAdvice {

@Advice.OnMethodEnter
public static void onEnter(
@Advice.Argument(1) HttpClientConfig<HttpClientEngineConfig> httpClientConfig) {
httpClientConfig.install(KtorClientTracing.Companion, new SetupFunction());
}
}

public static class SetupFunction implements Function1<KtorClientTracingBuilder, Unit> {

@Override
public Unit invoke(KtorClientTracingBuilder builder) {
builder.setOpenTelemetry(GlobalOpenTelemetry.get());
KtorBuilderUtil.clientBuilderExtractor.invoke(builder).configure(AgentCommonConfig.get());
return kotlin.Unit.INSTANCE;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/

package io.opentelemetry.javaagent.instrumentation.ktor.v3_0;

import static java.util.Collections.singletonList;

import com.google.auto.service.AutoService;
import io.opentelemetry.javaagent.extension.instrumentation.InstrumentationModule;
import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation;
import java.util.List;

@AutoService(InstrumentationModule.class)
public class KtorClientInstrumentationModule extends InstrumentationModule {

public KtorClientInstrumentationModule() {
super("ktor", "ktor-client", "ktor-3.0", "ktor-client-3.0");
}

@Override
public boolean isHelperClass(String className) {
return className.startsWith("io.opentelemetry.extension.kotlin.");
}

@Override
public List<TypeInstrumentation> typeInstrumentations() {
return singletonList(new HttpClientInstrumentation());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/

package io.opentelemetry.javaagent.instrumentation.ktor.v3_0;

import static java.util.Collections.singletonList;

import com.google.auto.service.AutoService;
import io.opentelemetry.javaagent.extension.instrumentation.InstrumentationModule;
import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation;
import java.util.List;

@AutoService(InstrumentationModule.class)
public class KtorServerInstrumentationModule extends InstrumentationModule {

public KtorServerInstrumentationModule() {
super("ktor", "ktor-server", "ktor-3.0", "ktor-server-3.0");
}

@Override
public boolean isHelperClass(String className) {
return className.startsWith("io.opentelemetry.extension.kotlin.");
}

@Override
public List<TypeInstrumentation> typeInstrumentations() {
return singletonList(new ServerInstrumentation());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/

package io.opentelemetry.javaagent.instrumentation.ktor.v3_0;

import static net.bytebuddy.matcher.ElementMatchers.isConstructor;
import static net.bytebuddy.matcher.ElementMatchers.named;

import io.ktor.server.application.Application;
import io.ktor.server.application.ApplicationPluginKt;
import io.opentelemetry.api.GlobalOpenTelemetry;
import io.opentelemetry.instrumentation.ktor.v3_0.internal.KtorBuilderUtil;
import io.opentelemetry.instrumentation.ktor.v3_0.server.KtorServerTracing;
import io.opentelemetry.javaagent.bootstrap.internal.AgentCommonConfig;
import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation;
import io.opentelemetry.javaagent.extension.instrumentation.TypeTransformer;
import kotlin.Unit;
import kotlin.jvm.functions.Function1;
import net.bytebuddy.asm.Advice;
import net.bytebuddy.description.type.TypeDescription;
import net.bytebuddy.matcher.ElementMatcher;

public class ServerInstrumentation implements TypeInstrumentation {
@Override
public ElementMatcher<TypeDescription> typeMatcher() {
return named("io.ktor.server.engine.ApplicationEngineEnvironmentReloading");
}

@Override
public void transform(TypeTransformer transformer) {
transformer.applyAdviceToMethod(
isConstructor(), this.getClass().getName() + "$ConstructorAdvice");
}

@SuppressWarnings("unused")
public static class ConstructorAdvice {

@Advice.OnMethodExit
public static void onExit(@Advice.FieldValue("_applicationInstance") Application application) {
ApplicationPluginKt.install(application, KtorServerTracing.Feature, new SetupFunction());
}
}

public static class SetupFunction
implements Function1<KtorServerTracing.Configuration, kotlin.Unit> {

@Override
public Unit invoke(KtorServerTracing.Configuration configuration) {
configuration.setOpenTelemetry(GlobalOpenTelemetry.get());
KtorBuilderUtil.serverBuilderExtractor
.invoke(configuration)
.configure(AgentCommonConfig.get());
return kotlin.Unit.INSTANCE;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/

package io.opentelemetry.instrumentation.ktor.v3_0.client

import io.ktor.client.*
import io.opentelemetry.instrumentation.testing.junit.http.HttpClientInstrumentationExtension
import org.junit.jupiter.api.extension.RegisterExtension

class KtorHttpClientTest : AbstractKtorHttpClientTest() {

companion object {
@JvmStatic
@RegisterExtension
private val TESTING = HttpClientInstrumentationExtension.forAgent()
}

override fun HttpClientConfig<*>.installTracing() {
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/

package io.opentelemetry.instrumentation.ktor.v3_0.server

import io.ktor.server.application.*
import io.opentelemetry.instrumentation.testing.junit.InstrumentationExtension
import io.opentelemetry.instrumentation.testing.junit.http.HttpServerInstrumentationExtension
import io.opentelemetry.instrumentation.testing.junit.http.HttpServerTestOptions
import org.junit.jupiter.api.extension.RegisterExtension

class KtorHttpServerTest : AbstractKtorHttpServerTest() {

companion object {
@JvmStatic
@RegisterExtension
val TESTING: InstrumentationExtension = HttpServerInstrumentationExtension.forAgent()
}

override fun getTesting(): InstrumentationExtension {
return TESTING
}

override fun installOpenTelemetry(application: Application) {
}

override fun configure(options: HttpServerTestOptions) {
super.configure(options)
options.setTestException(false)
}
}
60 changes: 60 additions & 0 deletions instrumentation/ktor/ktor-3.0/library/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
# Library Instrumentation for Ktor version 3.0 and higher

This package contains libraries to help instrument Ktor. Server and client instrumentations are supported.

## Quickstart

### Add these dependencies to your project

Replace `OPENTELEMETRY_VERSION` with the [latest
release](https://search.maven.org/search?q=g:io.opentelemetry.instrumentation%20AND%20a:opentelemetry-ktor-3.0).

For Maven, add to your `pom.xml` dependencies:

```xml
<dependencies>
<dependency>
<groupId>io.opentelemetry.instrumentation</groupId>
<artifactId>opentelemetry-ktor-3.0</artifactId>
<version>OPENTELEMETRY_VERSION</version>
</dependency>
</dependencies>
```

For Gradle, add to your dependencies:

```groovy
implementation("io.opentelemetry.instrumentation:opentelemetry-ktor-3.0:OPENTELEMETRY_VERSION")
```

## Usage

## Initializing server instrumentation

Initialize instrumentation by installing the `KtorServerTracing` feature. You must set the `OpenTelemetry` to use with
the feature.

```kotlin
val openTelemetry: OpenTelemetry = ...

embeddedServer(Netty, 8080) {
install(KtorServerTracing) {
setOpenTelemetry(openTelemetry)
}
}
```

## Initializing client instrumentation

Initialize instrumentation by installing the `KtorClientTracing` feature. You must set the `OpenTelemetry` to use with
the feature.

```kotlin
val openTelemetry: OpenTelemetry = ...

val client = HttpClient {
install(KtorClientTracing) {
setOpenTelemetry(openTelemetry)
}
}
```
Loading

0 comments on commit 2aa3669

Please sign in to comment.