# frozen_string_literal: true # Copyright The OpenTelemetry Authors # # SPDX-License-Identifier: Apache-2.0module OpenTelemetry require 'opentelemetry-common' module OpenTelemetry module Helpers # This module contains helpers for MySQL instrumentation libraries, like mysql2 and # trilogy. It is intended for use by instrumentation developers and not to # be called directly by an application. # # To use this in your instrumentation, the `Instrumentation` class for # your gem must contain configuration options for: # * `:span_name` # Example: # `option :span_name, default: :statement_type, validate: %I[statement_type db_name db_operation_and_name]` # # @api public module MySQL module_function QUERY_NAMES = [ 'set names', 'select', 'insert', 'update', 'delete', 'begin', 'commit', 'rollback', 'savepoint', 'release savepoint', 'explain', 'drop database', 'drop table', 'create database', 'create table' ].freeze # Ignore query names that might appear in comments prepended to the # statement. PREPENDED_COMMENTS_REGEX = %r{(?:\/\*.*?\*\/)}m QUERY_NAME_REGEX = Regexp.new("^\s*(?:#{PREPENDED_COMMENTS_REGEX})?\s*\\b(#{QUERY_NAMES.join('|')})\\b.*", Regexp::IGNORECASE) # This is a span naming utility intended for use in MySQL database # adapter instrumentation. # # @param sql [String] The SQL statement for the span. # @param operation [String] The database operation. # @param database_name [String] The name of the database. # @param config [Hash] The user's configuration for the database adapter. # Desired keys: # * `:span_name` => A symbol describing the type of name desired. Expected options are `:statement_type`, `:db_name`, and `:db_operation_and_name`. A nil or unknown `:span_name` will return 'mysql' as the span name # @return [String] The span name. # @api public def database_span_name(sql, operation, database_name, config) case config[:span_name] when :statement_type extract_statement_type(sql) when :db_name database_name when :db_operation_and_name db_operation_and_name(operation, database_name) end || 'mysql' end # @api private def extract_statement_type(sql) sql = OpenTelemetry::Common::Utilities.utf8_encode(sql, binary: true) QUERY_NAME_REGEX.match(sql) { |match| match[1].downcase } unless sql.nil? rescue StandardError => e OpenTelemetry.handle_error(message: 'Error extracting SQL statement type', exception: e) nil end # @api private def db_operation_and_name(operation, database_name) if operation && database_name "#{operation} #{database_name}" elsif operation operation elsif database_name database_name end end end end end