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