module Sidekiq module Datadog class TagBuilder def initialize(custom_tags = [], skip_tags = [], custom_hostname = nil) @tags = Array(custom_tags) @skip_tags = Array(skip_tags).map(&:to_s) env = Sidekiq.options[:environment] || ENV['RACK_ENV'] host = custom_hostname || ENV['INSTRUMENTATION_HOSTNAME'] || Socket.gethostname setup_defaults(host: host, env: env) end def build_tags(worker_or_worker_class, job, queue = nil, error = nil) tags = @tags.flat_map do |tag| case tag when String then tag when Proc then tag.call(worker_or_worker_class, job, queue, error) end end tags.compact! tags.push "name:#{job_name(worker_or_worker_class, job)}" if include_tag?('name') tags.push "queue:#{queue}" if queue && include_tag?('queue') if error.nil? tags.push 'status:ok' if include_tag?('status') else kind = underscore(error.class.name.sub(/Error$/, '')) tags.push 'status:error' if include_tag?('status') tags.push "error:#{kind}" if include_tag?('error') end tags end private def job_name(worker_or_worker_class, job) # Client middleware sends a `worker_class` String, # whereas Server middleware send a Worker object worker_class = if worker_or_worker_class.is_a?(String) worker_or_worker_class else worker_or_worker_class.class.to_s end underscore(job['wrapped'] || worker_class) end def setup_defaults(hash) hash.each do |key, val| key = key.to_s next unless val && include_tag?(key) prefix = "#{key}:" next if @tags.any? {|t| t.is_a?(String) && t.start_with?(prefix) } @tags.push [key, val].join(':') end end def include_tag?(tag) !@skip_tags.include?(tag) end def underscore(word) word = word.to_s.gsub(/::/, '/') word.gsub!(/([A-Z\d]+)([A-Z][a-z])/, '\1_\2') word.gsub!(/([a-z\d])([A-Z])/, '\1_\2') word.tr!('-', '_') word.downcase end end end end