Skip to content

Commit aa6c127

Browse files
authored
make adding the db.statement tag configurable in jdbc calls (#1103)
* make adding the db.statement tag configurable in jdbc calls * add the db statement setting to the Slick tests
1 parent af70b8e commit aa6c127

File tree

4 files changed

+91
-8
lines changed

4 files changed

+91
-8
lines changed

instrumentation/kamon-jdbc/src/main/resources/reference.conf

+7-1
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,13 @@ kamon.instrumentation.jdbc {
66

77
# Parses sql to generate nicer operation name
88
# Might have a negative performance impact
9-
parse-sql-for-operation-name = false
9+
parse-sql-for-operation-name = no
10+
11+
# Decides when to include the db.statement tag in Spans. The possible values are:
12+
# - always: always adds the db.statement tag, regardless of the statement type
13+
# - prepared: adds the db.statement tag on prepared statements only
14+
# - never: completely disables adding the db.statement tag to JDBC spans
15+
add-db-statement-as-span-tag = always
1016

1117
# Defines additional instrumentation actions to be taken when Slow or Failed JDBC statements are detected.
1218
#

instrumentation/kamon-jdbc/src/main/scala/kamon/instrumentation/jdbc/StatementMonitor.scala

+31-7
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ import kamon.trace.Span
2525
import kanela.agent.bootstrap.stack.CallStackDepth
2626
import net.sf.jsqlparser.parser.CCJSqlParserUtil
2727

28+
import java.sql.PreparedStatement
2829
import java.time.Instant
2930
import java.time.temporal.ChronoUnit
3031

@@ -38,14 +39,34 @@ object StatementMonitor extends LoggingSupport {
3839
val GenericExecute = "execute"
3940
}
4041

41-
@volatile private var parseSqlOperationName: Boolean = loadSqlParsingConfig(Kamon.config())
42+
@volatile private var parseSqlOperationName: Boolean = false
43+
@volatile private var addStatementSQL: Boolean = true
44+
@volatile private var addPreparedStatementSQL: Boolean = true
4245

43-
Kamon.onReconfigure { config =>
44-
parseSqlOperationName = loadSqlParsingConfig(config)
45-
}
46+
Kamon.onReconfigure(c => updateSettings(c))
47+
updateSettings(Kamon.config())
48+
49+
private def updateSettings(config: Config): Unit = {
50+
val jdbcInstrumentationConfig = config.getConfig("kamon.instrumentation.jdbc")
51+
parseSqlOperationName = jdbcInstrumentationConfig.getBoolean("parse-sql-for-operation-name")
52+
53+
jdbcInstrumentationConfig.getString("add-db-statement-as-span-tag") match {
54+
case "always" =>
55+
addStatementSQL = true
56+
addPreparedStatementSQL = true
57+
58+
case "prepared" =>
59+
addStatementSQL = false
60+
addPreparedStatementSQL = true
4661

47-
private def loadSqlParsingConfig(config: Config) = {
48-
config.getBoolean("kamon.instrumentation.jdbc.parse-sql-for-operation-name")
62+
case "never" =>
63+
addStatementSQL = false
64+
addPreparedStatementSQL = false
65+
66+
case unrecognizedValue =>
67+
logger.warn(s"Unrecognized value [${unrecognizedValue}] for the [add-db-statement-as-span-tag] setting. " +
68+
s"Falling back to [always]")
69+
}
4970
}
5071

5172
def start(statement: Any, sql: String, statementType: String): Option[Invocation] = {
@@ -83,7 +104,10 @@ object StatementMonitor extends LoggingSupport {
83104
statementType
84105
}
85106

86-
val clientSpan = Kamon.clientSpanBuilder(spanName, "jdbc").tag("db.statement", sql)
107+
val clientSpan = Kamon.clientSpanBuilder(spanName, "jdbc")
108+
109+
if(addStatementSQL || (statement.isInstanceOf[PreparedStatement] && addPreparedStatementSQL))
110+
clientSpan.tag("db.statement", sql)
87111

88112
databaseTags.spanTags.iterator().foreach(t => clientSpan.tag(t.key, databaseTags.spanTags.get(Lookups.coerce(t.key))))
89113
databaseTags.metricTags.iterator().foreach(t => clientSpan.tagMetrics(t.key, databaseTags.metricTags.get(Lookups.coerce(t.key))))

instrumentation/kamon-jdbc/src/test/scala/kamon/instrumentation/jdbc/SlickInstrumentationSpec.scala

+2
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,8 @@ class SlickInstrumentationSpec extends AnyWordSpec with Matchers with Eventually
3737

3838
"the Slick instrumentation" should {
3939
"propagate the current Context to the AsyncExecutor on Slick" in {
40+
applyConfig("kamon.instrumentation.jdbc.add-db-statement-as-span-tag=always")
41+
4042
val db = setup(Database.forConfig("slick-h2"))
4143
val parent = Kamon.spanBuilder("parent").start()
4244

instrumentation/kamon-jdbc/src/test/scala/kamon/instrumentation/jdbc/StatementInstrumentationSpec.scala

+51
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,8 @@ class StatementInstrumentationSpec extends AnyWordSpec
7979

8080
s"instrumenting the ${driver.name} driver" should {
8181
"generate Spans on calls to .execute() in prepared statements" in {
82+
applyConfig("kamon.instrumentation.jdbc.add-db-statement-as-span-tag=always")
83+
8284
val select = s"SELECT * FROM Address where Nr = ?"
8385
val statement = connection.prepareStatement(select)
8486
statement.setLong(1, 1)
@@ -215,6 +217,54 @@ class StatementInstrumentationSpec extends AnyWordSpec
215217
.toLowerCase() should include("notatable")
216218
}
217219

220+
"include the db.statement tag in prepared statements only when enabled via configuration" in {
221+
applyConfig("kamon.instrumentation.jdbc.add-db-statement-as-span-tag=prepared")
222+
223+
connection
224+
.createStatement()
225+
.execute("SELECT * FROM Address where Nr = 1")
226+
227+
eventually(timeout(scaled(5 seconds)), interval(200 millis)) {
228+
val span = commonSpanValidations(testSpanReporter(), driver)
229+
span.tags.get(option("db.statement")) shouldBe empty
230+
}
231+
232+
233+
val ps = connection.prepareStatement("INSERT INTO Address VALUES(?, ?)")
234+
ps.setInt(1, 1)
235+
ps.setString(2, "test")
236+
ps.execute()
237+
238+
eventually(timeout(scaled(5 seconds)), interval(200 millis)) {
239+
val span = commonSpanValidations(testSpanReporter(), driver)
240+
span.tags.get(plain("db.statement")) shouldBe "INSERT INTO Address VALUES(?, ?)"
241+
}
242+
}
243+
244+
"not include the db.statement tag when disabled via configuration" in {
245+
applyConfig("kamon.instrumentation.jdbc.add-db-statement-as-span-tag=never")
246+
247+
connection
248+
.createStatement()
249+
.execute("SELECT * FROM Address where Nr = 1")
250+
251+
eventually(timeout(scaled(5 seconds)), interval(200 millis)) {
252+
val span = commonSpanValidations(testSpanReporter(), driver)
253+
span.tags.get(option("db.statement")) shouldBe empty
254+
}
255+
256+
257+
val ps = connection.prepareStatement("INSERT INTO Address VALUES(?, ?)")
258+
ps.setInt(1, 1)
259+
ps.setString(2, "test")
260+
ps.execute()
261+
262+
eventually(timeout(scaled(5 seconds)), interval(200 millis)) {
263+
val span = commonSpanValidations(testSpanReporter(), driver)
264+
span.tags.get(option("db.statement")) shouldBe empty
265+
}
266+
}
267+
218268
"track in-flight operations" in {
219269
if (driver.supportSleeping) {
220270
val vendorTags = TagSet.of("db.vendor", driver.vendor)
@@ -241,6 +291,7 @@ class StatementInstrumentationSpec extends AnyWordSpec
241291
}
242292

243293
}
294+
244295
connection.close()
245296
driver.cleanup()
246297
}

0 commit comments

Comments
 (0)