Skip to content

Commit

Permalink
setup up structure for testing with or without metrics sdk
Browse files Browse the repository at this point in the history
  • Loading branch information
zvkemp committed Dec 20, 2024
1 parent 0abb927 commit 8d5ce77
Show file tree
Hide file tree
Showing 6 changed files with 139 additions and 18 deletions.
22 changes: 22 additions & 0 deletions instrumentation/sidekiq/Appraisals
Original file line number Diff line number Diff line change
Expand Up @@ -2,23 +2,45 @@

appraise 'sidekiq-7.0' do
gem 'sidekiq', '~> 7.0'

remove_gem 'opentelemetry-metrics-sdk'
remove_gem 'opentelemetry-metrics-api'
end

appraise 'sidekiq-7.0-metrics-sdk' do
gem 'sidekiq', '~> 7.0'
end

# explicitly test without SDK
appraise 'sidekiq-7.0-metrics-api' do
gem 'sidekiq', '~> 7.0'

remove_gem 'opentelemetry-metrics-sdk'
end

appraise 'sidekiq-6.5' do
gem 'sidekiq', '>= 6.5', '< 7.0'
remove_gem 'opentelemetry-metrics-api'
remove_gem 'opentelemetry-metrics-sdk'
end

appraise 'sidekiq-6.0' do
gem 'sidekiq', '>= 6.0', '< 6.5'
gem 'redis', '< 4.8'
remove_gem 'opentelemetry-metrics-api'
remove_gem 'opentelemetry-metrics-sdk'
end

appraise 'sidekiq-5.2' do
gem 'sidekiq', '~> 5.2'
gem 'redis', '< 4.8'
remove_gem 'opentelemetry-metrics-api'
remove_gem 'opentelemetry-metrics-sdk'
end

appraise 'sidekiq-4.2' do
gem 'sidekiq', '~> 4.2'
gem 'redis', '< 4.8'
remove_gem 'opentelemetry-metrics-api'
remove_gem 'opentelemetry-metrics-sdk'
end
14 changes: 14 additions & 0 deletions instrumentation/sidekiq/Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,22 @@ source 'https://rubygems.org'

gemspec

# FIXME: the metrics-api is behind the metrics-sdk gem for some reason; bundle from git for now
OTEL_RUBY_GEM = lambda do |short_name|
short_name = short_name.split(/-|_/)
long_name = ['opentelemetry', *short_name].join('-')

gem long_name,
git: 'https://www.github.com/open-telemetry/opentelemetry-ruby',
glob: "#{short_name.join('_')}/*.gemspec",
ref: '035c32ad9791f6200733e087f2ee49e0a615879a'
end

OTEL_RUBY_GEM['metrics-api']

group :test do
gem 'opentelemetry-instrumentation-base', path: '../base'
gem 'opentelemetry-instrumentation-redis', path: '../redis'
OTEL_RUBY_GEM['metrics-sdk']
gem 'pry-byebug'
end
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
#
# SPDX-License-Identifier: Apache-2.0

require_relative '../common'

module OpenTelemetry
module Instrumentation
module Sidekiq
Expand All @@ -12,6 +14,7 @@ module Client
# TracerMiddleware propagates context and instruments Sidekiq client
# by way of its middleware system
class TracerMiddleware
include Common
include ::Sidekiq::ClientMiddleware if defined?(::Sidekiq::ClientMiddleware)

def call(_worker_class, job, _queue, _redis_pool)
Expand Down Expand Up @@ -61,22 +64,6 @@ def messaging_client_sent_messages_counter
instrumentation.counter('messaging.client.sent.messages')
end

def messaging_client_operation_duration_histogram
instrumentation.histogram('messaging.client.operation.duration')
end

def messaging_client_consumed_messages_counter
instrumentation.counter('messaging.client.consumed.messages')
end

def instrumentation
Sidekiq::Instrumentation.instance
end

def instrumentation_config
instrumentation.config
end

def tracer
instrumentation.tracer
end
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@
#
# SPDX-License-Identifier: Apache-2.0

require_relative '../common'

module OpenTelemetry
module Instrumentation
module Sidekiq
Expand All @@ -12,7 +14,7 @@ module Server
# TracerMiddleware propagates context and instruments Sidekiq requests
# by way of its middleware system
class TracerMiddleware
include OpenTelemetry::Instrumentation::Sidekiq::Middlewares::Common
include Common
include ::Sidekiq::ServerMiddleware if defined?(::Sidekiq::ServerMiddleware)

def call(_worker, msg, _queue)
Expand Down Expand Up @@ -60,6 +62,8 @@ def call(_worker, msg, _queue)
end
end
end

count_consumed_message(msg)
end
end

Expand Down Expand Up @@ -93,6 +97,16 @@ def messaging_process_duration_histogram
instrumentation.histogram('messaging.process.duration')
end

def count_consumed_message(msg)
with_meter do
messaging_client_consumed_messages_counter.add(1, attributes: metrics_attributes(msg))
end
end

def messaging_client_consumed_messages_counter
instrumentation.counter('messaging.client.consumed.messages')
end

def queue_latency_gauge
instrumentation.gauge('messaging.queue.latency')
end
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,18 +11,28 @@
describe OpenTelemetry::Instrumentation::Sidekiq::Middlewares::Server::TracerMiddleware do
let(:instrumentation) { OpenTelemetry::Instrumentation::Sidekiq::Instrumentation.instance }
let(:exporter) { EXPORTER }
let(:metrics_exporter) { METRICS_EXPORTER }
let(:spans) { exporter.finished_spans }
let(:enqueuer_span) { spans.first }
let(:job_span) { spans.last }
let(:root_span) { spans.find { |s| s.parent_span_id == OpenTelemetry::Trace::INVALID_SPAN_ID } }
let(:config) { {} }

with_metrics do
let(:metric_snapshots) do
METRICS_EXPORTER.tap(&:pull).metric_snapshots.group_by(&:name)
end
end

before do
instrumentation.install(config)
exporter.reset
with_metrics { metrics_exporter.tap(&:pull).reset }
end

after { instrumentation.instance_variable_set(:@installed, false) }
after do
instrumentation.instance_variable_set(:@installed, false)
end

describe 'enqueue spans' do
it 'before performing any jobs' do
Expand All @@ -49,6 +59,37 @@
_(job_span.events[1].name).must_equal('enqueued_at')
end

it 'metrics processing', with_metrics: true do
job_id = SimpleJob.perform_async
SimpleJob.drain

queue_latency = metric_snapshots['messaging.queue.latency']
_(queue_latency.count).must_equal 1
_(queue_latency.first.data_points.count).must_equal 1
queue_latency_attributes = queue_latency.first.data_points.first.attributes
_(queue_latency_attributes['messaging.system']).must_equal 'sidekiq'
_(queue_latency_attributes['messaging.destination.name']).must_equal 'default' # FIXME: newer semconv specifies this key

process_duration = metric_snapshots['messaging.process.duration']
_(process_duration.count).must_equal 1
_(process_duration.first.data_points.count).must_equal 1
process_duration_attributes = process_duration.first.data_points.first.attributes
_(process_duration_attributes['messaging.system']).must_equal 'sidekiq'
_(process_duration_attributes['messaging.operation.name']).must_equal 'process'
_(process_duration_attributes['messaging.destination.name']).must_equal 'default'

process_duration_data_point = process_duration.first.data_points.first
_(process_duration_data_point.count).must_equal 1

consumed_messages = metric_snapshots['messaging.client.consumed.messages']
_(consumed_messages.count).must_equal 1
_(consumed_messages.first.data_points.count).must_equal 1
consumed_messages_attributes = queue_latency.first.data_points.first.attributes
_(consumed_messages_attributes['messaging.system']).must_equal 'sidekiq'
_(consumed_messages_attributes['messaging.destination.name']).must_equal 'default' # FIXME: newer semconv specifies this key
_(consumed_messages.first.data_points.first.value).must_equal 1
end

it 'traces when enqueued with Active Job' do
SimpleJobWithActiveJob.perform_later(1, 2)
Sidekiq::Worker.drain_all
Expand Down
43 changes: 43 additions & 0 deletions instrumentation/sidekiq/test/test_helper.rb
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,49 @@
c.add_span_processor span_processor
end

module LoadedMetricsFeatures
OTEL_METRICS_API_LOADED = !Gem.loaded_specs['opentelemetry-metrics-api'].nil?
OTEL_METRICS_SDK_LOADED = !Gem.loaded_specs['opentelemetry-metrics-sdk'].nil?

extend self

def api_loaded?
OTEL_METRICS_API_LOADED
end

def sdk_loaded?
OTEL_METRICS_SDK_LOADED
end
end

if LoadedMetricsFeatures.sdk_loaded?
METRICS_EXPORTER = OpenTelemetry::SDK::Metrics::Export::InMemoryMetricPullExporter.new
OpenTelemetry.meter_provider.add_metric_reader(METRICS_EXPORTER)
end

module ConditionalEvaluation
def self.included(base)
base.extend(self)
end

def self.prepended(base)
base.extend(self)
end

def with_metrics
yield if LoadedMetricsFeatures.sdk_loaded?
end

def it(desc = 'anonymous', with_metrics: false, &block)
return super(desc, &block) unless with_metrics
return unless LoadedMetricsFeatures.sdk_loaded?

super(desc, &block)
end
end

Minitest::Spec.prepend(ConditionalEvaluation)

# Sidekiq redis configuration
ENV['TEST_REDIS_HOST'] ||= '127.0.0.1'
ENV['TEST_REDIS_PORT'] ||= '16379'
Expand Down

0 comments on commit 8d5ce77

Please sign in to comment.