Skip to content

Commit c8dbb13

Browse files
committed
Normalizes SQL IN(?, ?, ...) statements to "in(?)" to reduce cardinality of span metrics using db.statement as an attribute
1 parent 1cddf1b commit c8dbb13

File tree

2 files changed

+18
-2
lines changed

2 files changed

+18
-2
lines changed

instrumentation-api-incubator/src/main/jflex/SqlSanitizer.jflex

+7-1
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@
55

66
package io.opentelemetry.instrumentation.api.incubator.semconv.db;
77

8+
import java.util.regex.Pattern;
9+
810
%%
911

1012
%final
@@ -52,6 +54,9 @@ WHITESPACE = [ \t\r\n]+
5254
// max length of the sanitized statement - SQLs longer than this will be trimmed
5355
static final int LIMIT = 32 * 1024;
5456

57+
private static final Pattern IN_STATEMENT_PATTERN = Pattern.compile("\\sin\\s*\\((?:\\s*\\?\\s*,?\\s*)+\\)", Pattern.CASE_INSENSITIVE);
58+
private static final String IN_STATEMENT_NORMALIZED = " in(?)";
59+
5560
private final StringBuilder builder = new StringBuilder();
5661

5762
private void appendCurrentFragment() {
@@ -278,7 +283,8 @@ WHITESPACE = [ \t\r\n]+
278283
builder.delete(LIMIT, builder.length());
279284
}
280285
String fullStatement = builder.toString();
281-
return operation.getResult(fullStatement);
286+
String normalizedStatement = IN_STATEMENT_PATTERN.matcher(fullStatement).replaceAll(IN_STATEMENT_NORMALIZED);
287+
return operation.getResult(normalizedStatement);
282288
}
283289

284290
%}

instrumentation-api-incubator/src/test/java/io/opentelemetry/instrumentation/api/incubator/semconv/db/SqlStatementSanitizerTest.java

+11-1
Original file line numberDiff line numberDiff line change
@@ -271,7 +271,11 @@ public Stream<? extends Arguments> provideArguments(ExtensionContext context) th
271271
Arguments.of("select col from table1 as t1, table2 as t2", expect("SELECT", null)),
272272
Arguments.of(
273273
"select col from table where col in (1, 2, 3)",
274-
expect("select col from table where col in (?, ?, ?)", "SELECT", "table")),
274+
expect("select col from table where col in(?)", "SELECT", "table")),
275+
Arguments.of(
276+
"select 'a' IN(x, 'b') from table where col in(1) and z IN( '3', '4' )",
277+
expect(
278+
"select ? IN(x, ?) from table where col in(?) and z in(?)", "SELECT", "table")),
275279
Arguments.of("select col from table order by col, col2", expect("SELECT", "table")),
276280
Arguments.of("select ąś∂ń© from źćļńĶ order by col, col2", expect("SELECT", "źćļńĶ")),
277281
Arguments.of("select 12345678", expect("select ?", "SELECT", null)),
@@ -298,6 +302,9 @@ public Stream<? extends Arguments> provideArguments(ExtensionContext context) th
298302
"delete from `my table` where something something", expect("DELETE", "my table")),
299303
Arguments.of(
300304
"delete from \"my table\" where something something", expect("DELETE", "my table")),
305+
Arguments.of(
306+
"delete from foo where x IN(1, 2, 3)",
307+
expect("delete from foo where x in(?)", "DELETE", "foo")),
301308
Arguments.of("delete from 12345678", expect("delete from ?", "DELETE", null)),
302309
Arguments.of("delete (((", expect("delete (((", "DELETE", null)),
303310

@@ -307,6 +314,9 @@ public Stream<? extends Arguments> provideArguments(ExtensionContext context) th
307314
Arguments.of(
308315
"update `my table` set answer=42",
309316
expect("update `my table` set answer=?", "UPDATE", "my table")),
317+
Arguments.of(
318+
"update `my table` set answer=42 where x IN('a', 'b')",
319+
expect("update `my table` set answer=? where x in(?)", "UPDATE", "my table")),
310320
Arguments.of(
311321
"update \"my table\" set answer=42",
312322
expect("update \"my table\" set answer=?", "UPDATE", "my table")),

0 commit comments

Comments
 (0)