FEATURE: Parse Sidekiq Delayed class names (#130)

FEATURE: Parse Sidekiq Delayed class names (#130)

diff --git a/lib/prometheus_exporter/instrumentation/sidekiq.rb b/lib/prometheus_exporter/instrumentation/sidekiq.rb
index e250005..540cfe0 100644
--- a/lib/prometheus_exporter/instrumentation/sidekiq.rb
+++ b/lib/prometheus_exporter/instrumentation/sidekiq.rb
@@ -1,6 +1,15 @@
 # frozen_string_literal: true
 
+require 'yaml'
+
 module PrometheusExporter::Instrumentation
+  JOB_WRAPPER_CLASS_NAME = 'ActiveJob::QueueAdapters::SidekiqAdapter::JobWrapper'
+  DELAYED_CLASS_NAMES = [
+    'Sidekiq::Extensions::DelayedClass',
+    'Sidekiq::Extensions::DelayedModel',
+    'Sidekiq::Extensions::DelayedMailer',
+  ]
+
   class Sidekiq
     def self.death_handler
       -> (job, ex) do
@@ -32,15 +41,43 @@ module PrometheusExporter::Instrumentation
       raise e
     ensure
       duration = ::Process.clock_gettime(::Process::CLOCK_MONOTONIC) - start
-      class_name = worker.class.to_s == 'ActiveJob::QueueAdapters::SidekiqAdapter::JobWrapper' ?
-                     msg['wrapped'] : worker.class.to_s
       @client.send_json(
         type: "sidekiq",
-        name: class_name,
+        name: get_name(worker, msg),
         success: success,
         shutdown: shutdown,
         duration: duration
       )
     end
+
+    private
+
+    def get_name(worker, msg)
+      class_name = worker.class.to_s
+      if class_name == JOB_WRAPPER_CLASS_NAME
+        get_job_wrapper_name(msg)
+      elsif DELAYED_CLASS_NAMES.include?(class_name)
+        get_delayed_name(msg, class_name)
+      else
+        class_name
+      end
+    end
+
+    def get_job_wrapper_name(msg)
+      msg['wrapped']
+    end
+
+    def get_delayed_name(msg, class_name)
+      # fallback to class_name since we're relying on the internal implementation
+      # of the delayed extensions
+      # https://github.com/mperham/sidekiq/blob/master/lib/sidekiq/extensions/class_methods.rb
+      begin
+        (target, method_name, _args) = YAML.load(msg['args'].first)
+        "#{target.name}##{method_name}"
+      rescue
+        class_name
+        raise
+      end
+    end
   end
 end
diff --git a/test/server/collector_test.rb b/test/server/collector_test.rb
index 229c12b..6b56d48 100644
--- a/test/server/collector_test.rb
+++ b/test/server/collector_test.rb
@@ -286,12 +286,20 @@ class PrometheusCollectorTest < Minitest::Test
       end
     end
 
+    delayed_worker = {}
+    delayed_worker.stub(:class, "Sidekiq::Extensions::DelayedClass") do
+      instrument.call(delayed_worker, { 'args' => [ "---\n- !ruby/class 'String'\n- :foo\n- -" ] }, "default") do
+        # nothing
+      end
+    end
+
     result = collector.prometheus_metrics_text
 
     assert(result.include?('sidekiq_failed_jobs_total{job_name="FalseClass",service="service1"} 1'), "has failed job")
     assert(result.include?('sidekiq_jobs_total{job_name="String",service="service1"} 1'), "has working job")
     assert(result.include?('sidekiq_job_duration_seconds{job_name="FalseClass",service="service1"}'), "has duration")
     assert(result.include?('sidekiq_jobs_total{job_name="WrappedClass",service="service1"} 1'), "has sidekiq working job from ActiveJob")
+    assert(result.include?('sidekiq_jobs_total{job_name="String#foo",service="service1"} 1'), "has sidekiq delayed class")
   end
 
   def test_it_can_collect_shoryuken_metrics_with_custom_lables

GitHub sha: b1a7f857

This commit appears in #130 which was merged by SamSaffron.