From 6278a309a0f8cd320347be0f6353ffb15e7e1152 Mon Sep 17 00:00:00 2001 From: Jean Bisutti Date: Wed, 10 Dec 2025 14:33:55 +0100 Subject: [PATCH] Ability to capture database query parameters --- .../internal/configuration/Configuration.java | 2 + .../internal/init/AiConfigCustomizer.java | 3 ++ .../smoketestapp/JdbcServlet.java | 14 ++++--- ...kedTest.java => AbstractJdbcUnmasked.java} | 5 +-- .../smoketest/JdbcCaptureParametersTest.java | 37 +++++++++++++++++++ .../smoketest/JdbcTest.java | 8 ++-- .../JdbcUnmaskedWithUnmakingFeatureTest.java | 10 +++++ .../capture_params_applicationinsights.json | 12 ++++++ 8 files changed, 77 insertions(+), 14 deletions(-) rename smoke-tests/apps/Jdbc/src/smokeTest/java/com/microsoft/applicationinsights/smoketest/{JdbcUnmaskedTest.java => AbstractJdbcUnmasked.java} (87%) create mode 100644 smoke-tests/apps/Jdbc/src/smokeTest/java/com/microsoft/applicationinsights/smoketest/JdbcCaptureParametersTest.java create mode 100644 smoke-tests/apps/Jdbc/src/smokeTest/java/com/microsoft/applicationinsights/smoketest/JdbcUnmaskedWithUnmakingFeatureTest.java create mode 100644 smoke-tests/apps/Jdbc/src/smokeTest/resources/capture_params_applicationinsights.json diff --git a/agent/agent-tooling/src/main/java/com/microsoft/applicationinsights/agent/internal/configuration/Configuration.java b/agent/agent-tooling/src/main/java/com/microsoft/applicationinsights/agent/internal/configuration/Configuration.java index a39a66b454c..2731b2bfbdf 100644 --- a/agent/agent-tooling/src/main/java/com/microsoft/applicationinsights/agent/internal/configuration/Configuration.java +++ b/agent/agent-tooling/src/main/java/com/microsoft/applicationinsights/agent/internal/configuration/Configuration.java @@ -346,6 +346,8 @@ public static class PreviewConfiguration { public boolean captureLog4jMarker; + public boolean captureJdbcQueryParameters; + // this is to support interoperability with other systems // intentionally not allowing the removal of w3c propagator since that is key to many Azure // integrated experiences diff --git a/agent/agent-tooling/src/main/java/com/microsoft/applicationinsights/agent/internal/init/AiConfigCustomizer.java b/agent/agent-tooling/src/main/java/com/microsoft/applicationinsights/agent/internal/init/AiConfigCustomizer.java index 5d17bb98b64..1bba78fd3c1 100644 --- a/agent/agent-tooling/src/main/java/com/microsoft/applicationinsights/agent/internal/init/AiConfigCustomizer.java +++ b/agent/agent-tooling/src/main/java/com/microsoft/applicationinsights/agent/internal/init/AiConfigCustomizer.java @@ -283,6 +283,9 @@ private static void enableInstrumentations( properties.put( "otel.instrumentation.log4j-appender.experimental.capture-marker-attribute", "true"); } + if (config.preview.captureJdbcQueryParameters) { + properties.put("otel.instrumentation.jdbc.experimental.capture-query-parameters", "true"); + } if (config.preview.instrumentation.akka.enabled) { properties.put("otel.instrumentation.akka-actor.enabled", "true"); properties.put("otel.instrumentation.akka-http.enabled", "true"); diff --git a/smoke-tests/apps/Jdbc/src/main/java/com/microsoft/applicationinsights/smoketestapp/JdbcServlet.java b/smoke-tests/apps/Jdbc/src/main/java/com/microsoft/applicationinsights/smoketestapp/JdbcServlet.java index 3037f0bf5f8..6f448aec895 100644 --- a/smoke-tests/apps/Jdbc/src/main/java/com/microsoft/applicationinsights/smoketestapp/JdbcServlet.java +++ b/smoke-tests/apps/Jdbc/src/main/java/com/microsoft/applicationinsights/smoketestapp/JdbcServlet.java @@ -172,8 +172,10 @@ private void oracleStatement() throws Exception { } private static void executePreparedStatement(Connection connection) throws SQLException { - PreparedStatement ps = connection.prepareStatement("select * from abc where xyz = ?"); - ps.setString(1, "y"); + PreparedStatement ps = + connection.prepareStatement("select * from abc where uvw = ? and xyz = ?"); + ps.setString(1, "v"); + ps.setString(2, "y"); ResultSet rs = ps.executeQuery(); while (rs.next()) {} rs.close(); @@ -332,10 +334,10 @@ private static void testConnection(Connection connection, String sql) throws SQL private static void setup(Connection connection) throws SQLException { try (Statement statement = connection.createStatement()) { - statement.execute("create table abc (xyz varchar(10))"); - statement.execute("insert into abc (xyz) values ('x')"); - statement.execute("insert into abc (xyz) values ('y')"); - statement.execute("insert into abc (xyz) values ('z')"); + statement.execute("create table abc (uvw varchar(10), xyz varchar(10))"); + statement.execute("insert into abc (uvw, xyz) values ('u', 'x')"); + statement.execute("insert into abc (uvw, xyz) values ('v', 'y')"); + statement.execute("insert into abc (uvw, xyz) values ('w', 'z')"); } } } diff --git a/smoke-tests/apps/Jdbc/src/smokeTest/java/com/microsoft/applicationinsights/smoketest/JdbcUnmaskedTest.java b/smoke-tests/apps/Jdbc/src/smokeTest/java/com/microsoft/applicationinsights/smoketest/AbstractJdbcUnmasked.java similarity index 87% rename from smoke-tests/apps/Jdbc/src/smokeTest/java/com/microsoft/applicationinsights/smoketest/JdbcUnmaskedTest.java rename to smoke-tests/apps/Jdbc/src/smokeTest/java/com/microsoft/applicationinsights/smoketest/AbstractJdbcUnmasked.java index 56bf3cdcc39..c42171e6e0e 100644 --- a/smoke-tests/apps/Jdbc/src/smokeTest/java/com/microsoft/applicationinsights/smoketest/JdbcUnmaskedTest.java +++ b/smoke-tests/apps/Jdbc/src/smokeTest/java/com/microsoft/applicationinsights/smoketest/AbstractJdbcUnmasked.java @@ -3,16 +3,13 @@ package com.microsoft.applicationinsights.smoketest; -import static com.microsoft.applicationinsights.smoketest.EnvironmentValue.TOMCAT_8_JAVA_8; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.data.MapEntry.entry; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.RegisterExtension; -@Environment(TOMCAT_8_JAVA_8) -@UseAgent("unmasked_applicationinsights.json") -class JdbcUnmaskedTest { +abstract class AbstractJdbcUnmasked { @RegisterExtension static final SmokeTestExtension testing = SmokeTestExtension.create(); diff --git a/smoke-tests/apps/Jdbc/src/smokeTest/java/com/microsoft/applicationinsights/smoketest/JdbcCaptureParametersTest.java b/smoke-tests/apps/Jdbc/src/smokeTest/java/com/microsoft/applicationinsights/smoketest/JdbcCaptureParametersTest.java new file mode 100644 index 00000000000..a7aed576ea9 --- /dev/null +++ b/smoke-tests/apps/Jdbc/src/smokeTest/java/com/microsoft/applicationinsights/smoketest/JdbcCaptureParametersTest.java @@ -0,0 +1,37 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.microsoft.applicationinsights.smoketest; + +import static com.microsoft.applicationinsights.smoketest.EnvironmentValue.TOMCAT_8_JAVA_8; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.data.MapEntry.entry; + +import org.junit.jupiter.api.Test; + +@Environment(TOMCAT_8_JAVA_8) +@UseAgent("capture_params_applicationinsights.json") +class JdbcCaptureParametersTest extends AbstractJdbcUnmasked { + + @Test + @TargetUri("/hsqldbPreparedStatement") + void hsqldbPreparedStatementCapturesParameters() throws Exception { + Telemetry telemetry = testing.getTelemetry(1); + + assertThat(telemetry.rd.getProperties()) + .containsExactly(entry("_MS.ProcessedByMetricExtractors", "True")); + assertThat(telemetry.rd.getSuccess()).isTrue(); + + assertThat(telemetry.rdd1.getName()).isEqualTo("SELECT testdb.abc"); + assertThat(telemetry.rdd1.getData()).isEqualTo("select * from abc where uvw = ? and xyz = ?"); + assertThat(telemetry.rdd1.getType()).isEqualTo("SQL"); + assertThat(telemetry.rdd1.getTarget()).isEqualTo("hsqldb | testdb"); + assertThat(telemetry.rdd1.getSuccess()).isTrue(); + + assertThat(telemetry.rdd1.getProperties()) + .containsExactly(entry("db.query.parameter.0", "v"), entry("db.query.parameter.1", "y")); + + SmokeTestExtension.assertParentChild( + telemetry.rd, telemetry.rdEnvelope, telemetry.rddEnvelope1, "GET /Jdbc/*"); + } +} diff --git a/smoke-tests/apps/Jdbc/src/smokeTest/java/com/microsoft/applicationinsights/smoketest/JdbcTest.java b/smoke-tests/apps/Jdbc/src/smokeTest/java/com/microsoft/applicationinsights/smoketest/JdbcTest.java index 7fc709b7958..69c89652dcb 100644 --- a/smoke-tests/apps/Jdbc/src/smokeTest/java/com/microsoft/applicationinsights/smoketest/JdbcTest.java +++ b/smoke-tests/apps/Jdbc/src/smokeTest/java/com/microsoft/applicationinsights/smoketest/JdbcTest.java @@ -54,7 +54,7 @@ void hsqldbPreparedStatement() throws Exception { assertThat(telemetry.rd.getSuccess()).isTrue(); assertThat(telemetry.rdd1.getName()).isEqualTo("SELECT testdb.abc"); - assertThat(telemetry.rdd1.getData()).isEqualTo("select * from abc where xyz = ?"); + assertThat(telemetry.rdd1.getData()).isEqualTo("select * from abc where uvw = ? and xyz = ?"); assertThat(telemetry.rdd1.getType()).isEqualTo("SQL"); assertThat(telemetry.rdd1.getTarget()).isEqualTo("hsqldb | testdb"); assertThat(telemetry.rdd1.getProperties()).isEmpty(); @@ -170,7 +170,7 @@ void mysqlPreparedStatement() throws Exception { assertThat(telemetry.rd.getSuccess()).isTrue(); assertThat(telemetry.rdd1.getName()).isEqualTo("SELECT mysql.abc"); - assertThat(telemetry.rdd1.getData()).isEqualTo("select * from abc where xyz = ?"); + assertThat(telemetry.rdd1.getData()).isEqualTo("select * from abc where uvw = ? and xyz = ?"); assertThat(telemetry.rdd1.getType()).isEqualTo("mysql"); // not the best test, because this is both the db.name and db.system assertThat(telemetry.rdd1.getTarget()).matches("dependency[0-9]+ \\| mysql"); @@ -214,7 +214,7 @@ void postgresPreparedStatement() throws Exception { assertThat(telemetry.rd.getSuccess()).isTrue(); assertThat(telemetry.rdd1.getName()).isEqualTo("SELECT postgres.abc"); - assertThat(telemetry.rdd1.getData()).isEqualTo("select * from abc where xyz = ?"); + assertThat(telemetry.rdd1.getData()).isEqualTo("select * from abc where uvw = ? and xyz = ?"); assertThat(telemetry.rdd1.getType()).isEqualTo("postgresql"); // not the best test, because this is both the db.name and db.system assertThat(telemetry.rdd1.getTarget()).matches("dependency[0-9]+ \\| postgres"); @@ -256,7 +256,7 @@ void sqlServerPreparedStatement() throws Exception { assertThat(telemetry.rd.getSuccess()).isTrue(); assertThat(telemetry.rdd1.getName()).isEqualTo("SELECT abc"); - assertThat(telemetry.rdd1.getData()).isEqualTo("select * from abc where xyz = ?"); + assertThat(telemetry.rdd1.getData()).isEqualTo("select * from abc where uvw = ? and xyz = ?"); assertThat(telemetry.rdd1.getType()).isEqualTo("SQL"); assertThat(telemetry.rdd1.getTarget()).matches("dependency[0-9]+"); assertThat(telemetry.rdd1.getProperties()).isEmpty(); diff --git a/smoke-tests/apps/Jdbc/src/smokeTest/java/com/microsoft/applicationinsights/smoketest/JdbcUnmaskedWithUnmakingFeatureTest.java b/smoke-tests/apps/Jdbc/src/smokeTest/java/com/microsoft/applicationinsights/smoketest/JdbcUnmaskedWithUnmakingFeatureTest.java new file mode 100644 index 00000000000..86294374be4 --- /dev/null +++ b/smoke-tests/apps/Jdbc/src/smokeTest/java/com/microsoft/applicationinsights/smoketest/JdbcUnmaskedWithUnmakingFeatureTest.java @@ -0,0 +1,10 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +package com.microsoft.applicationinsights.smoketest; + +import static com.microsoft.applicationinsights.smoketest.EnvironmentValue.TOMCAT_8_JAVA_8; + +@UseAgent("unmasked_applicationinsights.json") +@Environment(TOMCAT_8_JAVA_8) +class JdbcUnmaskedWithUnmakingFeatureTest extends AbstractJdbcUnmasked {} diff --git a/smoke-tests/apps/Jdbc/src/smokeTest/resources/capture_params_applicationinsights.json b/smoke-tests/apps/Jdbc/src/smokeTest/resources/capture_params_applicationinsights.json new file mode 100644 index 00000000000..6d34d880c20 --- /dev/null +++ b/smoke-tests/apps/Jdbc/src/smokeTest/resources/capture_params_applicationinsights.json @@ -0,0 +1,12 @@ +{ + "role": { + "name": "testrolename", + "instance": "testroleinstance" + }, + "sampling": { + "percentage": 100 + }, + "preview": { + "captureJdbcQueryParameters": true + } +}