DatabaseClientTracer.java 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156
  1. /*
  2. * Copyright The OpenTelemetry Authors
  3. * SPDX-License-Identifier: Apache-2.0
  4. */
  5. package io.opentelemetry.instrumentation.api.tracer;
  6. import static io.opentelemetry.api.trace.SpanKind.CLIENT;
  7. import io.opentelemetry.api.OpenTelemetry;
  8. import io.opentelemetry.api.trace.SpanBuilder;
  9. import io.opentelemetry.context.Context;
  10. import io.opentelemetry.instrumentation.api.tracer.net.NetPeerAttributes;
  11. import io.opentelemetry.semconv.trace.attributes.SemanticAttributes;
  12. import java.net.InetSocketAddress;
  13. import javax.annotation.Nullable;
  14. /**
  15. * Base class for implementing Tracers for database clients.
  16. *
  17. * @param <CONNECTION> type of the database connection.
  18. * @param <STATEMENT> type of the database statement being executed.
  19. * @param <SANITIZEDSTATEMENT> type of the database statement after sanitization.
  20. */
  21. public abstract class DatabaseClientTracer<CONNECTION, STATEMENT, SANITIZEDSTATEMENT>
  22. extends BaseTracer {
  23. private static final String DB_QUERY = "DB Query";
  24. protected final NetPeerAttributes netPeerAttributes;
  25. protected DatabaseClientTracer(NetPeerAttributes netPeerAttributes) {
  26. this.netPeerAttributes = netPeerAttributes;
  27. }
  28. protected DatabaseClientTracer(OpenTelemetry openTelemetry, NetPeerAttributes netPeerAttributes) {
  29. super(openTelemetry);
  30. this.netPeerAttributes = netPeerAttributes;
  31. }
  32. public boolean shouldStartSpan(Context parentContext) {
  33. return shouldStartSpan(parentContext, CLIENT);
  34. }
  35. public Context startSpan(Context parentContext, CONNECTION connection, STATEMENT statement) {
  36. SANITIZEDSTATEMENT sanitizedStatement = sanitizeStatement(statement);
  37. SpanBuilder span =
  38. spanBuilder(parentContext, spanName(connection, statement, sanitizedStatement), CLIENT)
  39. .setAttribute(SemanticAttributes.DB_SYSTEM, dbSystem(connection));
  40. if (connection != null) {
  41. onConnection(span, connection);
  42. setNetSemanticConvention(span, connection);
  43. }
  44. onStatement(span, connection, statement, sanitizedStatement);
  45. return withClientSpan(parentContext, span.startSpan());
  46. }
  47. protected abstract SANITIZEDSTATEMENT sanitizeStatement(STATEMENT statement);
  48. protected String spanName(
  49. CONNECTION connection, STATEMENT statement, SANITIZEDSTATEMENT sanitizedStatement) {
  50. return conventionSpanName(
  51. dbName(connection), dbOperation(connection, statement, sanitizedStatement), null);
  52. }
  53. /**
  54. * A helper method for constructing the span name formatting according to DB semantic conventions:
  55. * {@code <db.operation> <db.name><table>}.
  56. */
  57. public static String conventionSpanName(
  58. @Nullable String dbName, @Nullable String operation, @Nullable String table) {
  59. return conventionSpanName(dbName, operation, table, DB_QUERY);
  60. }
  61. /**
  62. * A helper method for constructing the span name formatting according to DB semantic conventions:
  63. * {@code <db.operation> <db.name><table>}. If {@code dbName} and {@code operation} are not
  64. * provided then {@code defaultValue} is returned.
  65. */
  66. public static String conventionSpanName(
  67. @Nullable String dbName,
  68. @Nullable String operation,
  69. @Nullable String table,
  70. String defaultValue) {
  71. if (operation == null) {
  72. return dbName == null ? defaultValue : dbName;
  73. }
  74. StringBuilder name = new StringBuilder(operation);
  75. if (dbName != null || table != null) {
  76. name.append(' ');
  77. }
  78. if (dbName != null) {
  79. name.append(dbName);
  80. if (table != null) {
  81. name.append('.');
  82. }
  83. }
  84. if (table != null) {
  85. name.append(table);
  86. }
  87. return name.toString();
  88. }
  89. protected abstract String dbSystem(CONNECTION connection);
  90. /** This should be called when the connection is being used, not when it's created. */
  91. protected void onConnection(SpanBuilder span, CONNECTION connection) {
  92. span.setAttribute(SemanticAttributes.DB_USER, dbUser(connection));
  93. span.setAttribute(SemanticAttributes.DB_NAME, dbName(connection));
  94. span.setAttribute(SemanticAttributes.DB_CONNECTION_STRING, dbConnectionString(connection));
  95. }
  96. protected String dbUser(CONNECTION connection) {
  97. return null;
  98. }
  99. protected String dbName(CONNECTION connection) {
  100. return null;
  101. }
  102. @Nullable
  103. protected String dbConnectionString(CONNECTION connection) {
  104. return null;
  105. }
  106. protected void setNetSemanticConvention(SpanBuilder span, CONNECTION connection) {
  107. netPeerAttributes.setNetPeer(span, peerAddress(connection));
  108. }
  109. @Nullable
  110. protected abstract InetSocketAddress peerAddress(CONNECTION connection);
  111. protected void onStatement(
  112. SpanBuilder span,
  113. CONNECTION connection,
  114. STATEMENT statement,
  115. SANITIZEDSTATEMENT sanitizedStatement) {
  116. span.setAttribute(
  117. SemanticAttributes.DB_STATEMENT, dbStatement(connection, statement, sanitizedStatement));
  118. span.setAttribute(
  119. SemanticAttributes.DB_OPERATION, dbOperation(connection, statement, sanitizedStatement));
  120. }
  121. protected String dbStatement(
  122. CONNECTION connection, STATEMENT statement, SANITIZEDSTATEMENT sanitizedStatement) {
  123. return null;
  124. }
  125. protected String dbOperation(
  126. CONNECTION connection, STATEMENT statement, SANITIZEDSTATEMENT sanitizedStatement) {
  127. return null;
  128. }
  129. }