Skip to content

Commit 8fd4ac0

Browse files
authored
Context propagation for mysql and mariadb (#2681)
* Context propagation for mysql and mariadb
1 parent 4bf9964 commit 8fd4ac0

File tree

15 files changed

+654
-4
lines changed

15 files changed

+654
-4
lines changed

.github/workflows/ci.yaml

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,31 @@ jobs:
4545
key: ${{ runner.os }}-gradle-wrapper-cache-${{ hashFiles('gradle/wrapper/gradle-wrapper.properties') }}
4646

4747
- name: Build and test
48-
run: ./gradlew build -x :smoke-tests:test --scan --no-daemon
48+
run: ./gradlew build --scan --no-daemon
49+
50+
- name: Build scan
51+
if: ${{ !cancelled() && hashFiles('build-scan.txt') != '' }}
52+
run: cat build-scan.txt
53+
54+
test-latest-deps:
55+
runs-on: ubuntu-24.04
56+
steps:
57+
- uses: actions/checkout@v6.0.2
58+
59+
- name: Set up JDK 17 for running Gradle
60+
uses: actions/setup-java@v5.2.0
61+
with:
62+
distribution: temurin
63+
java-version: 17
64+
65+
- name: Cache Gradle Wrapper
66+
uses: actions/cache@v5.0.3
67+
with:
68+
path: ~/.gradle/wrapper
69+
key: ${{ runner.os }}-gradle-wrapper-cache-${{ hashFiles('gradle/wrapper/gradle-wrapper.properties') }}
70+
71+
- name: Build and test
72+
run: ./gradlew check -x spotlessCheck -PtestLatestDeps=true --scan --no-daemon
4973

5074
- name: Build scan
5175
if: ${{ !cancelled() && hashFiles('build-scan.txt') != '' }}

.github/workflows/nightly.yaml

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,31 @@ jobs:
3939
key: ${{ runner.os }}-gradle-wrapper-cache-${{ hashFiles('gradle/wrapper/gradle-wrapper.properties') }}
4040

4141
- name: Build and test
42-
run: ./gradlew build -x :smoke-tests:test --scan --no-daemon
42+
run: ./gradlew build --scan --no-daemon
43+
44+
- name: Build scan
45+
if: ${{ !cancelled() && hashFiles('build-scan.txt') != '' }}
46+
run: cat build-scan.txt
47+
48+
test-latest-deps:
49+
runs-on: ubuntu-24.04
50+
steps:
51+
- uses: actions/checkout@v6.0.2
52+
53+
- name: Set up JDK 17 for running Gradle
54+
uses: actions/setup-java@v5.2.0
55+
with:
56+
distribution: temurin
57+
java-version: 17
58+
59+
- name: Cache Gradle Wrapper
60+
uses: actions/cache@v5.0.3
61+
with:
62+
path: ~/.gradle/wrapper
63+
key: ${{ runner.os }}-gradle-wrapper-cache-${{ hashFiles('gradle/wrapper/gradle-wrapper.properties') }}
64+
65+
- name: Build and test
66+
run: ./gradlew check -x spotlessCheck -PtestLatestDeps=true --scan --no-daemon
4367

4468
- name: Build scan
4569
if: ${{ !cancelled() && hashFiles('build-scan.txt') != '' }}

.github/workflows/pr.yaml

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,31 @@ jobs:
4040
key: ${{ runner.os }}-gradle-wrapper-cache-${{ hashFiles('gradle/wrapper/gradle-wrapper.properties') }}
4141

4242
- name: Build and test
43-
run: ./gradlew build -x :smoke-tests:test --scan --no-daemon
43+
run: ./gradlew build --scan --no-daemon
44+
45+
- name: Build scan
46+
if: ${{ !cancelled() && hashFiles('build-scan.txt') != '' }}
47+
run: cat build-scan.txt
48+
49+
test-latest-deps:
50+
runs-on: ubuntu-24.04
51+
steps:
52+
- uses: actions/checkout@v6.0.2
53+
54+
- name: Set up JDK 17 for running Gradle
55+
uses: actions/setup-java@v5.2.0
56+
with:
57+
distribution: temurin
58+
java-version: 17
59+
60+
- name: Cache Gradle Wrapper
61+
uses: actions/cache@v5.0.3
62+
with:
63+
path: ~/.gradle/wrapper
64+
key: ${{ runner.os }}-gradle-wrapper-cache-${{ hashFiles('gradle/wrapper/gradle-wrapper.properties') }}
65+
66+
- name: Build and test
67+
run: ./gradlew check -x spotlessCheck -PtestLatestDeps=true --scan --no-daemon
4468

4569
- name: Build scan
4670
if: ${{ !cancelled() && hashFiles('build-scan.txt') != '' }}

instrumentation/jdbc/build.gradle.kts

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,21 @@ muzzle {
99
module.set("mssql-jdbc")
1010
versions.set("(,)")
1111
}
12+
pass {
13+
group.set("com.oracle.database.jdbc")
14+
module.set("ojdbc8")
15+
versions.set("(,)")
16+
}
17+
pass {
18+
group.set("com.mysql")
19+
module.set("mysql-connector-j")
20+
versions.set("(,)")
21+
}
22+
pass {
23+
group.set("org.mariadb.jdbc")
24+
module.set("mariadb-java-client")
25+
versions.set("(,)")
26+
}
1227
}
1328

1429
dependencies {
@@ -32,6 +47,14 @@ dependencies {
3247
// PostgreSQL
3348
testLibrary("org.postgresql:postgresql:42.1.1")
3449
testImplementation("org.testcontainers:testcontainers-postgresql")
50+
51+
// MySQL
52+
testLibrary("com.mysql:mysql-connector-j:8.0.31")
53+
testImplementation("org.testcontainers:testcontainers-mysql")
54+
55+
// MariaDB
56+
testLibrary("org.mariadb.jdbc:mariadb-java-client:2.0.1")
57+
testImplementation("org.testcontainers:testcontainers-mariadb")
3558
}
3659

3760
tasks.withType<Test>().configureEach {
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
/*
2+
* Copyright Splunk Inc.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package com.splunk.opentelemetry.instrumentation.jdbc;
18+
19+
import java.sql.Connection;
20+
import java.sql.PreparedStatement;
21+
import java.sql.SQLException;
22+
import javax.annotation.Nullable;
23+
24+
public class AbstractMySqlDbContextPropagator extends AbstractDbContextPropagator {
25+
26+
@Override
27+
protected void setContext(Connection connection, @Nullable String contextInfo)
28+
throws SQLException {
29+
PreparedStatement statement = connection.prepareStatement("set @traceparent=?");
30+
statement.setString(1, contextInfo);
31+
statement.executeUpdate();
32+
}
33+
}
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
/*
2+
* Copyright Splunk Inc.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package com.splunk.opentelemetry.instrumentation.jdbc.mariadb;
18+
19+
import com.splunk.opentelemetry.instrumentation.jdbc.AbstractMySqlDbContextPropagator;
20+
21+
public final class MariaDbContextPropagator extends AbstractMySqlDbContextPropagator {
22+
public static final MariaDbContextPropagator INSTANCE = new MariaDbContextPropagator();
23+
24+
private MariaDbContextPropagator() {}
25+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
/*
2+
* Copyright Splunk Inc.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package com.splunk.opentelemetry.instrumentation.jdbc.mariadb;
18+
19+
import static java.util.Collections.singletonList;
20+
21+
import com.google.auto.service.AutoService;
22+
import io.opentelemetry.javaagent.extension.instrumentation.InstrumentationModule;
23+
import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation;
24+
import java.util.List;
25+
26+
@AutoService(InstrumentationModule.class)
27+
public class MariaDbInstrumentationModule extends InstrumentationModule {
28+
29+
public MariaDbInstrumentationModule() {
30+
super("splunk-jdbc", "splunk-jdbc-mariadb");
31+
}
32+
33+
@Override
34+
public int order() {
35+
// run after jdbc instrumentation
36+
return 1;
37+
}
38+
39+
@Override
40+
public boolean defaultEnabled() {
41+
return false;
42+
}
43+
44+
@Override
45+
public List<TypeInstrumentation> typeInstrumentations() {
46+
return singletonList(new MariaDbStatementInstrumentation());
47+
}
48+
49+
@Override
50+
public boolean isHelperClass(String className) {
51+
return className.startsWith("com.splunk.opentelemetry.instrumentation");
52+
}
53+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
/*
2+
* Copyright Splunk Inc.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package com.splunk.opentelemetry.instrumentation.jdbc.mariadb;
18+
19+
import static io.opentelemetry.javaagent.extension.matcher.AgentElementMatchers.extendsClass;
20+
import static io.opentelemetry.javaagent.extension.matcher.AgentElementMatchers.hasClassesNamed;
21+
import static net.bytebuddy.matcher.ElementMatchers.isPublic;
22+
import static net.bytebuddy.matcher.ElementMatchers.nameStartsWith;
23+
import static net.bytebuddy.matcher.ElementMatchers.namedOneOf;
24+
import static net.bytebuddy.matcher.ElementMatchers.takesArgument;
25+
import static net.bytebuddy.matcher.ElementMatchers.takesNoArguments;
26+
27+
import io.opentelemetry.javaagent.bootstrap.CallDepth;
28+
import io.opentelemetry.javaagent.extension.instrumentation.TypeInstrumentation;
29+
import io.opentelemetry.javaagent.extension.instrumentation.TypeTransformer;
30+
import java.sql.Statement;
31+
import net.bytebuddy.asm.Advice;
32+
import net.bytebuddy.description.type.TypeDescription;
33+
import net.bytebuddy.matcher.ElementMatcher;
34+
35+
public class MariaDbStatementInstrumentation implements TypeInstrumentation {
36+
37+
@Override
38+
public ElementMatcher<TypeDescription> typeMatcher() {
39+
// class names change between maridb driver versions
40+
return extendsClass(
41+
namedOneOf("org.mariadb.jdbc.Statement", "org.mariadb.jdbc.MariaDbStatement"));
42+
}
43+
44+
@Override
45+
public ElementMatcher<ClassLoader> classLoaderOptimization() {
46+
return hasClassesNamed("org.mariadb.jdbc.Statement")
47+
.or(hasClassesNamed("org.mariadb.jdbc.MariaDbStatement"));
48+
}
49+
50+
@Override
51+
public void transform(TypeTransformer transformer) {
52+
transformer.applyAdviceToMethod(
53+
isPublic()
54+
.and(
55+
nameStartsWith("execute")
56+
.and(takesNoArguments().or(takesArgument(0, String.class)))),
57+
this.getClass().getName() + "$SetContextAdvice");
58+
}
59+
60+
@SuppressWarnings("unused")
61+
public static class SetContextAdvice {
62+
@Advice.OnMethodEnter(suppress = Throwable.class)
63+
public static void onEnter(
64+
@Advice.This Statement statement, @Advice.Local("splunkCallDepth") CallDepth callDepth)
65+
throws Exception {
66+
callDepth = CallDepth.forClass(MariaDbContextPropagator.class);
67+
if (callDepth.getAndIncrement() > 0) {
68+
return;
69+
}
70+
71+
MariaDbContextPropagator.INSTANCE.propagateContext(statement.getConnection());
72+
}
73+
74+
@Advice.OnMethodExit(suppress = Throwable.class)
75+
public static void onExit(@Advice.Local("splunkCallDepth") CallDepth callDepth) {
76+
callDepth.decrementAndGet();
77+
}
78+
}
79+
}
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
/*
2+
* Copyright Splunk Inc.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package com.splunk.opentelemetry.instrumentation.jdbc.mysql;
18+
19+
import com.splunk.opentelemetry.instrumentation.jdbc.AbstractMySqlDbContextPropagator;
20+
21+
public final class MySqlContextPropagator extends AbstractMySqlDbContextPropagator {
22+
public static final MySqlContextPropagator INSTANCE = new MySqlContextPropagator();
23+
24+
private MySqlContextPropagator() {}
25+
}

0 commit comments

Comments
 (0)