diff --git a/instrumentation/redis/Appraisals b/instrumentation/redis/Appraisals index abb68fa4b..33bcaa143 100644 --- a/instrumentation/redis/Appraisals +++ b/instrumentation/redis/Appraisals @@ -1,5 +1,23 @@ # frozen_string_literal: true +appraise 'redis-4.x-metrics-sdk' do + gem 'redis-client', '~> 0.22' + gem 'redis', '~> 4.8' + gem 'opentelemetry-metrics-sdk', github: "zvkemp/opentelemetry-ruby", glob: 'metrics_sdk/*.gemspec', ref: "metrics-kwargs" + gem 'opentelemetry-metrics-api', github: "zvkemp/opentelemetry-ruby", glob: 'metrics_api/*.gemspec', ref: "metrics-kwargs" +end + +appraise 'redis-5.x-metrics-sdk' do + gem 'redis', '~> 5.0' + gem 'opentelemetry-metrics-sdk', github: "zvkemp/opentelemetry-ruby", glob: 'metrics_sdk/*.gemspec', ref: "metrics-kwargs" + gem 'opentelemetry-metrics-api', github: "zvkemp/opentelemetry-ruby", glob: 'metrics_api/*.gemspec', ref: "metrics-kwargs" +end + +appraise 'redis-5.x-metrics-api' do + gem 'redis', '~> 5.0' + gem 'opentelemetry-metrics-api', github: "zvkemp/opentelemetry-ruby", glob: 'metrics_api/*.gemspec', ref: "metrics-kwargs" +end + appraise 'redis-4.x' do gem 'redis-client', '~> 0.22' gem 'redis', '~> 4.8' diff --git a/instrumentation/redis/Gemfile b/instrumentation/redis/Gemfile index 2baf57ac4..8cc6b1145 100644 --- a/instrumentation/redis/Gemfile +++ b/instrumentation/redis/Gemfile @@ -11,3 +11,5 @@ gemspec group :test do gem 'opentelemetry-instrumentation-base', path: '../base' end + +gem 'pry-byebug' diff --git a/instrumentation/redis/lib/opentelemetry/instrumentation/redis/instrumentation.rb b/instrumentation/redis/lib/opentelemetry/instrumentation/redis/instrumentation.rb index d30462ca6..d201fd735 100644 --- a/instrumentation/redis/lib/opentelemetry/instrumentation/redis/instrumentation.rb +++ b/instrumentation/redis/lib/opentelemetry/instrumentation/redis/instrumentation.rb @@ -22,16 +22,32 @@ class Instrumentation < OpenTelemetry::Instrumentation::Base option :peer_service, default: nil, validate: :string option :trace_root_spans, default: true, validate: :boolean option :db_statement, default: :obfuscate, validate: %I[omit include obfuscate] + option :metrics, default: false, validate: :boolean + + # https://opentelemetry.io/docs/specs/semconv/database/database-metrics/#metric-dbclientoperationduration + histogram 'db.client.operation.duration', + attributes: { 'db.system.name' => 'redis' }, + unit: 's', + boundaries: [0.001, 0.005, 0.01, 0.05, 0.1, 0.5, 1, 5, 10] + + def client_operation_duration_histogram + histogram('db.client.operation.duration') + end private def require_dependencies require_relative 'patches/redis_v4_client' if defined?(::Redis) && ::Redis::VERSION < '5' - require_relative 'middlewares/redis_client' if defined?(::RedisClient) + + return unless defined?(::RedisClient) + + require_relative 'middlewares/redis_client' + require_relative 'middlewares/redis_client_metrics' end def patch_client ::RedisClient.register(Middlewares::RedisClientInstrumentation) if defined?(::RedisClient) + ::RedisClient.register(Middlewares::RedisClientMetrics) if defined?(::RedisClient) ::Redis::Client.prepend(Patches::RedisV4Client) if defined?(::Redis) && ::Redis::VERSION < '5' end end diff --git a/instrumentation/redis/lib/opentelemetry/instrumentation/redis/middlewares/redis_client.rb b/instrumentation/redis/lib/opentelemetry/instrumentation/redis/middlewares/redis_client.rb index 7599cf3f5..1c7c295a4 100644 --- a/instrumentation/redis/lib/opentelemetry/instrumentation/redis/middlewares/redis_client.rb +++ b/instrumentation/redis/lib/opentelemetry/instrumentation/redis/middlewares/redis_client.rb @@ -49,6 +49,7 @@ def span_attributes(redis_config) attributes['db.redis.database_index'] = redis_config.db unless redis_config.db.zero? attributes['peer.service'] = instrumentation.config[:peer_service] if instrumentation.config[:peer_service] + attributes.merge!(OpenTelemetry::Instrumentation::Redis.attributes) attributes end diff --git a/instrumentation/redis/lib/opentelemetry/instrumentation/redis/middlewares/redis_client_metrics.rb b/instrumentation/redis/lib/opentelemetry/instrumentation/redis/middlewares/redis_client_metrics.rb new file mode 100644 index 000000000..1f8209591 --- /dev/null +++ b/instrumentation/redis/lib/opentelemetry/instrumentation/redis/middlewares/redis_client_metrics.rb @@ -0,0 +1,66 @@ +# frozen_string_literal: true + +# Copyright The OpenTelemetry Authors +# +# SPDX-License-Identifier: Apache-2.0 + +module OpenTelemetry + module Instrumentation + module Redis + module Middlewares + # Adapter for redis-client instrumentation interface + module RedisClientMetrics + def call(command, redis_config) + return super unless (histogram = instrumentation.client_operation_duration_histogram) + + timed(histogram, command.first, redis_config) do + super + end + end + + def call_pipelined(commands, redis_config) + return super unless (histogram = instrumentation.client_operation_duration_histogram) + + timed(histogram, 'PIPELINE', redis_config) do + super + end + end + + private + + def timed(histogram, operation_name, redis_config) + t0 = monotonic_now + + yield.tap do + duration = monotonic_now - t0 + + histogram.record(duration, attributes: metric_attributes(redis_config, operation_name)) + end + end + + def monotonic_now + Process.clock_gettime(Process::CLOCK_MONOTONIC, :float_second) + end + + def metric_attributes(redis_config, operation_name) + attributes = { + 'db.system' => 'redis', + 'db.operation.name' => operation_name, + 'net.peer.name' => redis_config.host, + 'net.peer.port' => redis_config.port + } + + attributes['db.redis.database_index'] = redis_config.db unless redis_config.db.zero? + attributes['peer.service'] = instrumentation.config[:peer_service] if instrumentation.config[:peer_service] + attributes.merge!(OpenTelemetry::Instrumentation::Redis.attributes) + attributes + end + + def instrumentation + Redis::Instrumentation.instance + end + end + end + end + end +end