lib/opentelemetry/instrumentation/mysql2/patches/client.rb in opentelemetry-instrumentation-mysql2-0.15.0 vs lib/opentelemetry/instrumentation/mysql2/patches/client.rb in opentelemetry-instrumentation-mysql2-0.16.0
- old
+ new
@@ -28,24 +28,69 @@
'create table'
].freeze
QUERY_NAME_RE = Regexp.new("^(#{QUERY_NAMES.join('|')})", Regexp::IGNORECASE)
+ COMPONENTS_REGEX_MAP = {
+ single_quotes: /'(?:[^']|'')*?(?:\\'.*|'(?!'))/,
+ double_quotes: /"(?:[^"]|"")*?(?:\\".*|"(?!"))/,
+ numeric_literals: /-?\b(?:[0-9]+\.)?[0-9]+([eE][+-]?[0-9]+)?\b/,
+ boolean_literals: /\b(?:true|false|null)\b/i,
+ hexadecimal_literals: /0x[0-9a-fA-F]+/,
+ comments: /(?:#|--).*?(?=\r|\n|$)/i,
+ multi_line_comments: %r{\/\*(?:[^\/]|\/[^*])*?(?:\*\/|\/\*.*)}
+ }.freeze
+
+ MYSQL_COMPONENTS = %i[
+ single_quotes
+ double_quotes
+ numeric_literals
+ boolean_literals
+ hexadecimal_literals
+ comments
+ multi_line_comments
+ ].freeze
+
def query(sql, options = {})
tracer.in_span(
database_span_name(sql),
attributes: client_attributes.merge(
- 'db.statement' => sql
+ 'db.statement' => obfuscate_sql(sql)
),
kind: :client
) do
super(sql, options)
end
end
private
+ def obfuscate_sql(sql)
+ return sql unless config[:enable_sql_obfuscation]
+
+ if sql.size > 2000
+ 'SQL query too large to remove sensitive data ...'
+ else
+ obfuscated = sql.gsub(generated_mysql_regex, '?')
+ obfuscated = 'Failed to obfuscate SQL query - quote characters remained after obfuscation' if detect_unmatched_pairs(obfuscated)
+ obfuscated
+ end
+ end
+
+ def generated_mysql_regex
+ @generated_mysql_regex ||= Regexp.union(MYSQL_COMPONENTS.map { |component| COMPONENTS_REGEX_MAP[component] })
+ end
+
+ def detect_unmatched_pairs(obfuscated)
+ # We use this to check whether the query contains any quote characters
+ # after obfuscation. If so, that's a good indication that the original
+ # query was malformed, and so our obfuscation can't reliably find
+ # literals. In such a case, we'll replace the entire query with a
+ # placeholder.
+ %r{'|"|\/\*|\*\/}.match(obfuscated)
+ end
+
def database_span_name(sql)
# Setting span name to the SQL query without obfuscation would
# result in PII + cardinality issues.
# First attempt to infer the statement type then fallback to
# current Otel approach {database.component_name}.{database_instance_name}
@@ -72,11 +117,10 @@
host = (query_options[:host] || query_options[:hostname]).to_s
port = query_options[:port].to_s
attributes = {
'db.system' => 'mysql',
- 'db.instance' => database_name,
- 'db.url' => "mysql://#{host}:#{port}",
+ 'db.name' => database_name,
'net.peer.name' => host,
'net.peer.port' => port
}
attributes['peer.service'] = config[:peer_service] if config[:peer_service]
attributes