lib/opentelemetry/instrumentation/redis/patches/client.rb in opentelemetry-instrumentation-redis-0.17.0 vs lib/opentelemetry/instrumentation/redis/patches/client.rb in opentelemetry-instrumentation-redis-0.18.0
- old
+ new
@@ -8,55 +8,76 @@
module Instrumentation
module Redis
module Patches
# Module to prepend to Redis::Client for instrumentation
module Client
- def call(*args, &block)
- response = nil
+ MAX_STATEMENT_LENGTH = 500
+ private_constant :MAX_STATEMENT_LENGTH
- attributes = client_attributes
- attributes['db.statement'] = Utils.format_statement(args)
- tracer.in_span(
- Utils.format_command(args),
- attributes: attributes,
- kind: :client
- ) do
- response = super(*args, &block)
- end
-
- response
- end
-
- def call_pipeline(*args, &block)
- response = nil
-
- attributes = client_attributes
- attributes['db.statement'] = Utils.format_pipeline_statement(args)
- tracer.in_span(
- 'pipeline',
- attributes: attributes,
- kind: :client
- ) do
- response = super(*args, &block)
- end
-
- response
- end
-
- private
-
- def client_attributes
+ def process(commands) # rubocop:disable Metrics/AbcSize, Metrics/MethodLength
host = options[:host]
port = options[:port]
attributes = {
'db.system' => 'redis',
'net.peer.name' => host,
'net.peer.port' => port
}
+
attributes['db.redis.database_index'] = options[:db] unless options[:db].zero?
attributes['peer.service'] = config[:peer_service] if config[:peer_service]
- attributes.merge(OpenTelemetry::Instrumentation::Redis.attributes)
+ attributes.merge!(OpenTelemetry::Instrumentation::Redis.attributes)
+
+ parsed_commands = parse_commands(commands)
+ parsed_commands = OpenTelemetry::Common::Utilities.truncate(parsed_commands, MAX_STATEMENT_LENGTH)
+ parsed_commands = OpenTelemetry::Common::Utilities.utf8_encode(parsed_commands, binary: true)
+ attributes['db.statement'] = parsed_commands
+
+ span_name = if commands.length == 1
+ commands[0][0].to_s.upcase
+ else
+ 'PIPELINED'
+ end
+
+ tracer.in_span(span_name, attributes: attributes, kind: :client) do |s|
+ super(commands).tap do |reply|
+ if reply.is_a?(::Redis::CommandError)
+ s.record_exception(reply)
+ s.status = Trace::Status.new(
+ Trace::Status::ERROR,
+ description: reply.message
+ )
+ end
+ end
+ end
+ end
+
+ private
+
+ # Examples of commands received for parsing
+ # Redis#queue [[[:set, "v1", "0"]], [[:incr, "v1"]], [[:get, "v1"]]]
+ # Redis#pipeline: [[:set, "v1", "0"], [:incr, "v1"], [:get, "v1"]]
+ # Redis#hmset [[:hmset, "hash", "f1", 1234567890.0987654]]
+ # Redis#set [[:set, "K", "x"]]
+ def parse_commands(commands) # rubocop:disable Metrics/AbcSize
+ commands.map do |command|
+ # We are checking for the use of Redis#queue command, if we detect the
+ # extra level of array nesting we return the first element so it
+ # can be parsed.
+ command = command[0] if command.is_a?(Array) && command[0].is_a?(Array)
+
+ # If we receive an authentication request command
+ # we want to short circuit parsing the commands
+ # and return the obfuscated command
+ return 'AUTH ?' if command[0] == :auth
+
+ command[0] = command[0].to_s.upcase
+ if config[:enable_statement_obfuscation]
+ command[0] + ' ?' * (command.size - 1)
+ else
+ command.join(' ')
+ end
+ end.join("\n")
end
def tracer
Redis::Instrumentation.instance.tracer
end