Skip to content

Commit

Permalink
Add standalone Rails ActiveJob integration
Browse files Browse the repository at this point in the history
  • Loading branch information
bensheldon committed Aug 7, 2021
1 parent bad39d4 commit 6a21e43
Show file tree
Hide file tree
Showing 12 changed files with 427 additions and 46 deletions.
32 changes: 32 additions & 0 deletions lib/ddtrace/contrib/active_job/configuration/settings.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
# typed: false
require 'ddtrace/contrib/configuration/settings'
require 'ddtrace/contrib/active_job/ext'

module Datadog
module Contrib
module ActiveJob
module Configuration
# Custom settings for the DelayedJob integration
class Settings < Contrib::Configuration::Settings
option :enabled do |o|
o.default { env_to_bool(Ext::ENV_ENABLED, true) }
o.lazy
end

option :analytics_enabled do |o|
o.default { env_to_bool([Ext::ENV_ANALYTICS_ENABLED, Ext::ENV_ANALYTICS_ENABLED_OLD], false) }
o.lazy
end

option :analytics_sample_rate do |o|
o.default { env_to_float([Ext::ENV_ANALYTICS_SAMPLE_RATE, Ext::ENV_ANALYTICS_SAMPLE_RATE_OLD], 1.0) }
o.lazy
end

option :service_name, default: Ext::SERVICE_NAME
option :error_handler, default: Datadog::Tracer::DEFAULT_ON_ERROR
end
end
end
end
end
31 changes: 31 additions & 0 deletions lib/ddtrace/contrib/active_job/event.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
# typed: true
require 'ddtrace/contrib/active_support/notifications/event'

module Datadog
module Contrib
module ActiveJob
# Defines basic behaviors for an ActiveJob event.
module Event
def self.included(base)
base.include(ActiveSupport::Notifications::Event)
base.extend(ClassMethods)
end

# Class methods for ActiveJob events.
module ClassMethods
def span_options
{ service: configuration[:service_name] }
end

def tracer
-> { configuration[:tracer] }
end

def configuration
Datadog.configuration[:active_job]
end
end
end
end
end
end
29 changes: 29 additions & 0 deletions lib/ddtrace/contrib/active_job/events.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
# typed: false
require 'ddtrace/contrib/active_job/events/perform'

module Datadog
module Contrib
module ActiveJob
# Defines collection of instrumented ActiveJob events
module Events
ALL = [
Events::Perform,
].freeze

module_function

def all
self::ALL
end

def subscriptions
all.collect(&:subscriptions).collect(&:to_a).flatten
end

def subscribe!
all.each(&:subscribe!)
end
end
end
end
end
55 changes: 55 additions & 0 deletions lib/ddtrace/contrib/active_job/events/perform.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
# typed: false
require 'ddtrace/ext/integration'
require 'ddtrace/contrib/analytics'
require 'ddtrace/contrib/active_job/ext'
require 'ddtrace/contrib/active_job/event'

module Datadog
module Contrib
module ActiveJob
module Events
# Defines instrumentation for perform.active_job event
module Perform
include ActiveJob::Event

EVENT_NAME = 'perform.active_job'.freeze

module_function

def event_name
self::EVENT_NAME
end

def span_name
Ext::SPAN_PERFORM
end

def process(span, event, _id, payload)
span.name = span_name
span.service = configuration[:service_name]
span.resource = payload[:job].class.name

adapter_name = if payload[:adapter].is_a?(Class)
payload[:adapter].name
else
payload[:adapter].class.name
end
span.set_tag(Ext::TAG_ADAPTER, adapter_name)

# Set analytics sample rate
if Contrib::Analytics.enabled?(configuration[:analytics_enabled])
Contrib::Analytics.set_sample_rate(span, configuration[:analytics_sample_rate])
end

span.set_tag(Ext::TAG_JOB_ID, payload[:job].job_id)
span.set_tag(Ext::TAG_JOB_QUEUE, payload[:job].queue_name)

span.set_tag(Ext::TAG_JOB_PRIORITY, payload[:job].priority) if payload[:job].respond_to?(:priority)
rescue StandardError => e
Datadog.logger.debug(e.message)
end
end
end
end
end
end
24 changes: 24 additions & 0 deletions lib/ddtrace/contrib/active_job/ext.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
# typed: true
module Datadog
module Contrib
module ActiveJob
module Ext
APP = 'active_job'.freeze
SERVICE_NAME = 'active_job'.freeze

ENV_ENABLED = 'DD_TRACE_ACTIVE_JOB_ENABLED'.freeze
ENV_ANALYTICS_ENABLED = 'DD_TRACE_ACTIVE_JOB_ANALYTICS_ENABLED'.freeze
ENV_ANALYTICS_ENABLED_OLD = 'DD_ACTIVE_JOB_ANALYTICS_ENABLED'.freeze
ENV_ANALYTICS_SAMPLE_RATE = 'DD_TRACE_ACTIVE_JOB_ANALYTICS_SAMPLE_RATE'.freeze
ENV_ANALYTICS_SAMPLE_RATE_OLD = 'DD_ACTIVE_JOB_ANALYTICS_SAMPLE_RATE'.freeze

SPAN_PERFORM = 'active_job.perform'.freeze

TAG_ADAPTER = 'active_job.adapter'.freeze
TAG_JOB_ID = 'active_job.job.id'.freeze
TAG_JOB_QUEUE = 'active_job.job.queue'.freeze
TAG_JOB_PRIORITY = 'active_job.job.priority'.freeze
end
end
end
end
46 changes: 46 additions & 0 deletions lib/ddtrace/contrib/active_job/integration.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
# typed: false
require 'ddtrace/contrib/integration'
require 'ddtrace/contrib/active_job/configuration/settings'
require 'ddtrace/contrib/active_job/patcher'
require 'ddtrace/contrib/rails/utils'

module Datadog
module Contrib
module ActiveJob
# Describes the ActiveJob integration
class Integration
include Contrib::Integration

MINIMUM_VERSION = Gem::Version.new('4.2')

register_as :active_job, auto_patch: false

def self.version
Gem.loaded_specs['activejob'] && Gem.loaded_specs['activejob'].version
end

def self.loaded?
!defined?(::ActiveJob).nil?
end

def self.compatible?
super && version >= MINIMUM_VERSION
end

# enabled by rails integration so should only auto instrument
# if detected that it is being used without rails
def auto_instrument?
!Datadog::Contrib::Rails::Utils.railtie_supported?
end

def default_configuration
Configuration::Settings.new
end

def patcher
ActiveJob::Patcher
end
end
end
end
end
25 changes: 25 additions & 0 deletions lib/ddtrace/contrib/active_job/patcher.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
# typed: true
require 'ddtrace/contrib/patcher'
require 'ddtrace/contrib/active_job/ext'
require 'ddtrace/contrib/active_job/events'

module Datadog
module Contrib
module ActiveJob
# Patcher enables patching of 'active_job' module.
module Patcher
include Contrib::Patcher

module_function

def target_version
Integration.version
end

def patch
Events.subscribe!
end
end
end
end
end
78 changes: 78 additions & 0 deletions spec/ddtrace/contrib/active_job/integration_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
# typed: ignore
require 'ddtrace/contrib/support/spec_helper'
require 'ddtrace/contrib/auto_instrument_examples'

require 'ddtrace/contrib/active_job/integration'

RSpec.describe Datadog::Contrib::ActiveJob::Integration do
extend ConfigurationHelpers

let(:integration) { described_class.new(:active_job) }

describe '.version' do
subject(:version) { described_class.version }

context 'when the "activejob" gem is loaded' do
include_context 'loaded gems', activejob: described_class::MINIMUM_VERSION
it { is_expected.to be_a_kind_of(Gem::Version) }
end

context 'when "activejob" gem is not loaded' do
include_context 'loaded gems', activejob: nil
it { is_expected.to be nil }
end
end

describe '.loaded?' do
subject(:loaded?) { described_class.loaded? }

context 'when ActiveJob is defined' do
before { stub_const('ActiveJob', Class.new) }

it { is_expected.to be true }
end

context 'when ActiveJob is not defined' do
before { hide_const('ActiveJob') }

it { is_expected.to be false }
end
end

describe '.compatible?' do
subject(:compatible?) { described_class.compatible? }

context 'when "activejob" gem is loaded with a version' do
context 'that is less than the minimum' do
include_context 'loaded gems', activejob: decrement_gem_version(described_class::MINIMUM_VERSION)
it { is_expected.to be false }
end

context 'that meets the minimum version' do
include_context 'loaded gems', activejob: described_class::MINIMUM_VERSION
it { is_expected.to be true }
end
end

context 'when gem is not loaded' do
include_context 'loaded gems', actionpack: nil, activejob: nil
it { is_expected.to be false }
end
end

describe '#auto_instrument?' do
it_behaves_like 'rails sub-gem auto_instrument?'
end

describe '#default_configuration' do
subject(:default_configuration) { integration.default_configuration }

it { is_expected.to be_a_kind_of(Datadog::Contrib::ActiveJob::Configuration::Settings) }
end

describe '#patcher' do
subject(:patcher) { integration.patcher }

it { is_expected.to be Datadog::Contrib::ActiveJob::Patcher }
end
end
Loading

0 comments on commit 6a21e43

Please sign in to comment.