-
Notifications
You must be signed in to change notification settings - Fork 313
Add support for DBM comment injection with MongoDB #9589
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from all commits
6250b78
19d6052
ef9b8c3
a47cba7
67e8695
9e27a2d
7bf95d4
1a8af2d
631958f
a339fc5
704d509
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -46,6 +46,10 @@ out/ | |
###################### | ||
.vscode | ||
|
||
# Vim # | ||
####### | ||
*.sw[nop] | ||
|
||
# Others # | ||
########## | ||
/logs/* | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,47 +1,22 @@ | ||
package datadog.trace.instrumentation.jdbc; | ||
|
||
import static datadog.trace.bootstrap.instrumentation.api.AgentTracer.activeSpan; | ||
|
||
import datadog.trace.api.BaseHash; | ||
import datadog.trace.api.Config; | ||
import datadog.trace.bootstrap.instrumentation.api.AgentSpan; | ||
import datadog.trace.bootstrap.instrumentation.api.Tags; | ||
import java.io.UnsupportedEncodingException; | ||
import java.net.URLEncoder; | ||
import java.nio.charset.StandardCharsets; | ||
import datadog.trace.core.database.SharedDBCommenter; | ||
import org.slf4j.Logger; | ||
import org.slf4j.LoggerFactory; | ||
|
||
public class SQLCommenter { | ||
private static final Logger log = LoggerFactory.getLogger(SQLCommenter.class); | ||
private static final String UTF8 = StandardCharsets.UTF_8.toString(); | ||
|
||
private static final char EQUALS = '='; | ||
private static final char COMMA = ','; | ||
private static final char QUOTE = '\''; | ||
// SQL-specific constants, rest defined in SharedDBCommenter | ||
private static final char SPACE = ' '; | ||
private static final String OPEN_COMMENT = "/*"; | ||
private static final int OPEN_COMMENT_LEN = OPEN_COMMENT.length(); | ||
private static final String CLOSE_COMMENT = "*/"; | ||
|
||
// Injected fields. When adding a new one, be sure to update this and the methods below. | ||
private static final int NUMBER_OF_FIELDS = 9; | ||
private static final String PARENT_SERVICE = encode("ddps"); | ||
private static final String DATABASE_SERVICE = encode("dddbs"); | ||
private static final String DD_HOSTNAME = encode("ddh"); | ||
private static final String DD_DB_NAME = encode("dddb"); | ||
private static final String DD_PEER_SERVICE = "ddprs"; | ||
private static final String DD_ENV = encode("dde"); | ||
private static final String DD_VERSION = encode("ddpv"); | ||
private static final String TRACEPARENT = encode("traceparent"); | ||
private static final String DD_SERVICE_HASH = encode("ddsh"); | ||
|
||
private static final int KEY_AND_SEPARATORS_ESTIMATED_SIZE = 10; | ||
private static final int VALUE_ESTIMATED_SIZE = 10; | ||
private static final int TRACE_PARENT_EXTRA_ESTIMATED_SIZE = 50; | ||
private static final int INJECTED_COMMENT_ESTIMATED_SIZE = | ||
NUMBER_OF_FIELDS * (KEY_AND_SEPARATORS_ESTIMATED_SIZE + VALUE_ESTIMATED_SIZE) | ||
+ TRACE_PARENT_EXTRA_ESTIMATED_SIZE; | ||
// Size estimation for StringBuilder pre-allocation | ||
private static final int SPACE_CHARS = 2; // Leading and trailing spaces | ||
private static final int COMMENT_DELIMITERS = 4; // "/*" + "*/" | ||
private static final int BUFFER_EXTRA = 4; | ||
private static final int SQL_COMMENT_OVERHEAD = SPACE_CHARS + COMMENT_DELIMITERS + BUFFER_EXTRA; | ||
|
||
protected static String getFirstWord(String sql) { | ||
int beginIndex = 0; | ||
|
@@ -99,30 +74,22 @@ public static String inject( | |
return sql; | ||
} | ||
|
||
Config config = Config.get(); | ||
String commentContent = | ||
SharedDBCommenter.buildComment(dbService, dbType, hostname, dbName, traceParent); | ||
|
||
StringBuilder sb = new StringBuilder(sql.length() + INJECTED_COMMENT_ESTIMATED_SIZE); | ||
if (commentContent == null) { | ||
return sql; | ||
} | ||
|
||
// SQL-specific wrapping with /* */ | ||
StringBuilder sb = | ||
new StringBuilder(sql.length() + commentContent.length() + SQL_COMMENT_OVERHEAD); | ||
Comment on lines
+77
to
+86
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This builder logic looks very over-complicated to me...
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yeah, it would be okay to err on the side of a too big buffer. The GC is going to over allocate space for the array anyway, so the simpler calculation will probably be a net win both in performance and readability. |
||
if (appendComment) { | ||
sb.append(sql); | ||
sb.append(SPACE); | ||
} | ||
sb.append(OPEN_COMMENT); | ||
int initSize = sb.length(); | ||
append(sb, PARENT_SERVICE, config.getServiceName(), initSize); | ||
append(sb, DATABASE_SERVICE, dbService, initSize); | ||
append(sb, DD_HOSTNAME, hostname, initSize); | ||
append(sb, DD_DB_NAME, dbName, initSize); | ||
append(sb, DD_PEER_SERVICE, getPeerService(), initSize); | ||
append(sb, DD_ENV, config.getEnv(), initSize); | ||
append(sb, DD_VERSION, config.getVersion(), initSize); | ||
append(sb, TRACEPARENT, traceParent, initSize); | ||
if (config.isDbmInjectSqlBaseHash() && config.isExperimentalPropagateProcessTagsEnabled()) { | ||
append(sb, DD_SERVICE_HASH, BaseHash.getBaseHashStr(), initSize); | ||
} | ||
if (initSize == sb.length()) { | ||
// no comment was added | ||
return sql; | ||
} | ||
sb.append(commentContent); | ||
sb.append(CLOSE_COMMENT); | ||
if (!appendComment) { | ||
sb.append(SPACE); | ||
|
@@ -131,71 +98,29 @@ public static String inject( | |
return sb.toString(); | ||
} | ||
|
||
private static String getPeerService() { | ||
AgentSpan span = activeSpan(); | ||
Object peerService = null; | ||
if (span != null) { | ||
peerService = span.getTag(Tags.PEER_SERVICE); | ||
} | ||
return peerService != null ? peerService.toString() : null; | ||
} | ||
|
||
private static boolean hasDDComment(String sql, boolean appendComment) { | ||
if ((!sql.endsWith(CLOSE_COMMENT) && appendComment) | ||
|| ((!sql.startsWith(OPEN_COMMENT)) && !appendComment)) { | ||
return false; | ||
} | ||
int startIdx = OPEN_COMMENT_LEN; | ||
if (appendComment) { | ||
startIdx += sql.lastIndexOf(OPEN_COMMENT); | ||
} | ||
return hasMatchingSubstring(sql, startIdx, PARENT_SERVICE) | ||
|| hasMatchingSubstring(sql, startIdx, DATABASE_SERVICE) | ||
|| hasMatchingSubstring(sql, startIdx, DD_HOSTNAME) | ||
|| hasMatchingSubstring(sql, startIdx, DD_DB_NAME) | ||
|| hasMatchingSubstring(sql, startIdx, DD_PEER_SERVICE) | ||
|| hasMatchingSubstring(sql, startIdx, DD_ENV) | ||
|| hasMatchingSubstring(sql, startIdx, DD_VERSION) | ||
|| hasMatchingSubstring(sql, startIdx, TRACEPARENT) | ||
|| hasMatchingSubstring(sql, startIdx, DD_SERVICE_HASH); | ||
} | ||
|
||
private static boolean hasMatchingSubstring(String sql, int startIndex, String substring) { | ||
boolean tooLong = startIndex + substring.length() >= sql.length(); | ||
if (tooLong || !(sql.charAt(startIndex + substring.length()) == EQUALS)) { | ||
return false; | ||
} | ||
return sql.startsWith(substring, startIndex); | ||
} | ||
|
||
private static String encode(String val) { | ||
try { | ||
return URLEncoder.encode(val, UTF8); | ||
} catch (UnsupportedEncodingException exe) { | ||
if (log.isDebugEnabled()) { | ||
log.debug("exception thrown while encoding sql comment key %s", exe); | ||
} | ||
} | ||
return val; | ||
String commentContent = extractCommentContent(sql, appendComment); | ||
return SharedDBCommenter.containsTraceComment(commentContent); | ||
} | ||
|
||
private static void append(StringBuilder sb, String key, String value, int initSize) { | ||
if (null == value || value.isEmpty()) { | ||
return; | ||
} | ||
String encodedValue; | ||
try { | ||
encodedValue = URLEncoder.encode(value, UTF8); | ||
} catch (UnsupportedEncodingException e) { | ||
if (log.isDebugEnabled()) { | ||
log.debug("exception thrown while encoding sql comment %s", e); | ||
} | ||
return; | ||
private static String extractCommentContent(String sql, boolean appendComment) { | ||
int startIdx; | ||
int endIdx; | ||
if (appendComment) { | ||
startIdx = sql.lastIndexOf(OPEN_COMMENT); | ||
endIdx = sql.lastIndexOf(CLOSE_COMMENT); | ||
} else { | ||
startIdx = sql.indexOf(OPEN_COMMENT); | ||
endIdx = sql.indexOf(CLOSE_COMMENT); | ||
} | ||
|
||
if (sb.length() > initSize) { | ||
sb.append(COMMA); | ||
if (startIdx != -1 && endIdx != -1 && endIdx > startIdx) { | ||
return sql.substring(startIdx + OPEN_COMMENT_LEN, endIdx); | ||
} | ||
sb.append(key).append(EQUALS).append(QUOTE).append(encodedValue).append(QUOTE); | ||
return ""; | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just thinking if we could use real
.length()
instead? to make sure that we are in sync with actual string consts?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't feel strongly either way, but it is worth mentioning that dynamic constants are totally fine in Java.
For all intents and purposes, you can usually treat these two as equivalent.
static final int COMMENT_DELIMITERS = 4;
static final int COMMENT_DELIMITERS = "/**/".length();
While the simple constant is a little better because it gets constant propagated by javac, the dynamic constant is fine because it will still get constant propagated by the JIT.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yep, I think we can keep consts as we have not many of them and all in same place.