Skip to content

Commit

Permalink
Turn rails into regular contribution
Browse files Browse the repository at this point in the history
  • Loading branch information
p-lambert committed Oct 16, 2017
1 parent 115ac60 commit 996453e
Show file tree
Hide file tree
Showing 8 changed files with 107 additions and 88 deletions.
38 changes: 0 additions & 38 deletions lib/ddtrace.rb
Original file line number Diff line number Diff line change
Expand Up @@ -46,41 +46,3 @@ def configure
end

require 'ddtrace/monkey'

# Datadog auto instrumentation for frameworks
if defined?(Rails::VERSION)
if !ENV['DISABLE_DATADOG_RAILS']
if Rails::VERSION::MAJOR.to_i >= 3
require 'ddtrace/contrib/rails/framework'
require 'ddtrace/contrib/rails/middlewares'

module Datadog
# Railtie class initializes
class Railtie < Rails::Railtie
# add instrumentation middlewares
options = {}
config.app_middleware.insert_before(0, Datadog::Contrib::Rack::TraceMiddleware, options)
config.app_middleware.use(Datadog::Contrib::Rails::ExceptionMiddleware)

# auto instrument Rails and third party components after
# the framework initialization
config.after_initialize do |app|
Datadog::Contrib::Rails::Framework.configure(config: app.config)
Datadog::Contrib::Rails::Framework.auto_instrument()
Datadog::Contrib::Rails::Framework.auto_instrument_redis()
Datadog::Contrib::Rails::Framework.auto_instrument_grape()

# override Rack Middleware configurations with Rails
options.update(::Rails.configuration.datadog_trace)
end
end
end
else
Datadog::Tracer.log.warn 'Detected a Rails version < 3.x.'\
'This version is not supported yet and the'\
'auto-instrumentation for core components will be disabled.'
end
else
Datadog::Tracer.log.info 'Skipping Rails auto-instrumentation, DISABLE_DATADOG_RAILS is set.'
end
end
63 changes: 22 additions & 41 deletions lib/ddtrace/contrib/rails/framework.rb
Original file line number Diff line number Diff line change
Expand Up @@ -30,78 +30,59 @@ module Rails
# - handle configuration entries which are specific to Datadog tracing
# - instrument parts of the framework when needed
module Framework
include Base
register_as :rails

option :enabled, default: true
option :auto_instrument, default: false
option :auto_instrument_redis, default: false
option :auto_instrument_grape, default: false
option :default_service, default: 'rails-app'
option :default_controller_service, default: 'rails-controller'
option :default_cache_service, default: 'rails-cache'
option :default_grape_service, default: 'grape'
option :default_database_service
option :distributed_tracing_enabled, default: false
option :template_base_path, default: 'views/'
option :tracer, default: Datadog.tracer
option :debug, default: false
option :trace_agent_hostname, default: Datadog::Writer::HOSTNAME
option :trace_agent_port, default: Datadog::Writer::PORT
option :env, default: nil
option :tags, default: {}

# configure Datadog settings
# rubocop:disable Metrics/MethodLength
# rubocop:disable Metrics/AbcSize
def self.configure(rails_config)
user_config = config[:config].datadog_trace rescue {}
Datadog.configuration.use(:rails, user_config)
tracer = Datadog.configuration[:rails][:tracer]

tracer.enabled = get_option(:enabled)
tracer.class.debug_logging = get_option(:debug)
tracer.enabled = Datadog.configuration[:rails][:enabled]
tracer.class.debug_logging = Datadog.configuration[:rails][:debug]

tracer.configure(
hostname: get_option(:trace_agent_hostname),
port: get_option(:trace_agent_port)
hostname: Datadog.configuration[:rails][:trace_agent_hostname],
port: Datadog.configuration[:rails][:trace_agent_port]
)

tracer.set_tags(get_option(:tags))
tracer.set_tags('env' => get_option(:env)) if get_option(:env)
tracer.set_tags(Datadog.configuration[:rails][:tags])
tracer.set_tags('env' => Datadog.configuration[:rails][:env]) if Datadog.configuration[:rails][:env]

tracer.set_service_info(
get_option(:default_service),
Datadog.configuration[:rails][:default_service],
'rack',
Datadog::Ext::AppTypes::WEB
)

tracer.set_service_info(
get_option(:default_controller_service),
Datadog.configuration[:rails][:default_controller_service],
'rails',
Datadog::Ext::AppTypes::WEB
)
tracer.set_service_info(
get_option(:default_cache_service),
Datadog.configuration[:rails][:default_cache_service],
'rails',
Datadog::Ext::AppTypes::CACHE
)

# By default, default service would be guessed from the script
# being executed, but here we know better, get it from Rails config.
tracer.default_service = get_option(:default_service)
tracer.default_service = Datadog.configuration[:rails][:default_service]

Datadog.configuration[:rack][:tracer] = tracer
Datadog.configuration[:rack][:default_service] = get_option(:default_service)
Datadog.configuration[:rack][:distributed_tracing_enabled] = get_option(:distributed_tracing_enabled)
Datadog.configuration[:rack][:default_service] = Datadog.configuration[:rails][:default_service]
Datadog.configuration[:rack][:distributed_tracing_enabled] = \
Datadog.configuration[:rails][:distributed_tracing_enabled]

if defined?(::ActiveRecord)
begin
# set default database service details and store it in the configuration
conn_cfg = ::ActiveRecord::Base.connection_config()
adapter_name = Datadog::Contrib::Rails::Utils.normalize_vendor(conn_cfg[:adapter])
set_option(:default_database_service, adapter_name) unless get_option(:default_database_service)
Datadog.configuration[:rails][:default_database_service] ||= adapter_name
tracer.set_service_info(
get_option(:default_database_service),
Datadog.configuration[:rails][:default_database_service],
adapter_name,
Datadog::Ext::AppTypes::DB
)
Expand All @@ -111,11 +92,11 @@ def self.configure(rails_config)
end

# update global configurations
::Rails.configuration.datadog_trace = to_h
::Rails.configuration.datadog_trace = Datadog.registry[:rails].to_h
end

def self.auto_instrument_redis
return unless get_option(:auto_instrument_redis)
return unless Datadog.configuration[:rails][:auto_instrument_redis]
Datadog::Tracer.log.debug('Enabling auto-instrumentation for Redis client')

# patch the Redis library and reload the CacheStore if it was using Redis
Expand All @@ -138,21 +119,21 @@ def self.auto_instrument_redis
end

def self.auto_instrument_grape
return unless get_option(:auto_instrument_grape)
return unless Datadog.configuration[:rails][:auto_instrument_grape]

# patch the Grape library so that endpoints are traced
Datadog::Monkey.patch_module(:grape)

# update the Grape pin object
pin = Datadog::Pin.get_from(::Grape)
return unless pin && pin.enabled?
pin.tracer = get_option(:tracer)
pin.service = get_option(:default_grape_service)
pin.tracer = Datadog.configuration[:rails][:tracer]
pin.service = Datadog.configuration[:rails][:default_grape_service]
end

# automatically instrument all Rails component
def self.auto_instrument
return unless get_option(:auto_instrument)
return unless Datadog.configuration[:rails][:auto_instrument]
Datadog::Tracer.log.debug('Enabling auto-instrumentation for core components')

# instrumenting Rails framework
Expand Down
55 changes: 55 additions & 0 deletions lib/ddtrace/contrib/rails/patcher.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
module Datadog
module Contrib
module Rails
# Patcher
module Patcher
include Base
register_as :rails, auto_patch: true

option :enabled, default: true
option :auto_instrument, default: false
option :auto_instrument_redis, default: false
option :auto_instrument_grape, default: false
option :default_service, default: 'rails-app'
option :default_controller_service, default: 'rails-controller'
option :default_cache_service, default: 'rails-cache'
option :default_grape_service, default: 'grape'
option :default_database_service
option :distributed_tracing_enabled, default: false
option :template_base_path, default: 'views/'
option :tracer, default: Datadog.tracer
option :debug, default: false
option :trace_agent_hostname, default: Datadog::Writer::HOSTNAME
option :trace_agent_port, default: Datadog::Writer::PORT
option :env, default: nil
option :tags, default: {}
option :sidekiq_service, default: 'sidekiq'

@patched = false

class << self
def patch
return @patched if patched? || !compatible?
require_relative 'framework'
@patched = true
rescue => e
Datadog::Tracer.log.error("Unable to apply Rails integration: #{e}")
@patched
end

def patched?
@patched
end

def compatible?
return if ENV['DISABLE_DATADOG_RAILS']

defined?(::Rails::VERSION) && ::Rails::VERSION::MAJOR.to_i >= 3
end
end
end
end
end
end

require 'ddtrace/contrib/rails/railtie' if Datadog.registry[:rails].compatible?
18 changes: 18 additions & 0 deletions lib/ddtrace/contrib/rails/railtie.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
require 'ddtrace/contrib/rails/framework'
require 'ddtrace/contrib/rails/middlewares'
require 'ddtrace/contrib/rack/middlewares'

module Datadog
# Railtie class initializes
class Railtie < Rails::Railtie
config.app_middleware.insert_before(0, Datadog::Contrib::Rack::TraceMiddleware)
config.app_middleware.use(Datadog::Contrib::Rails::ExceptionMiddleware)

config.after_initialize do |app|
Datadog::Contrib::Rails::Framework.configure(config: app.config)
Datadog::Contrib::Rails::Framework.auto_instrument
Datadog::Contrib::Rails::Framework.auto_instrument_redis
Datadog::Contrib::Rails::Framework.auto_instrument_grape
end
end
end
3 changes: 1 addition & 2 deletions lib/ddtrace/contrib/sidekiq/tracer.rb
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,7 @@ class Tracer
def initialize(options = {})
# check if Rails configuration is available and use it to override
# Sidekiq defaults
rails_config = ::Rails.configuration.datadog_trace rescue {}
base_config = DEFAULT_CONFIG.merge(rails_config)
base_config = DEFAULT_CONFIG.merge(Datadog.registry[:rails].to_h)
user_config = base_config.merge(options)
@tracer = user_config[:tracer]
@sidekiq_service = user_config[:sidekiq_service]
Expand Down
3 changes: 3 additions & 0 deletions lib/ddtrace/monkey.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
# because patchers do not include any 3rd party module nor even our
# patching code, which is required on demand, when patching.
require 'ddtrace/contrib/base'
require 'ddtrace/contrib/rails/patcher'
require 'ddtrace/contrib/active_record/patcher'
require 'ddtrace/contrib/elasticsearch/patcher'
require 'ddtrace/contrib/faraday/patcher'
Expand Down Expand Up @@ -79,3 +80,5 @@ class << self
end
end
end

Datadog::Monkey.patch_module(:rails)
2 changes: 1 addition & 1 deletion test/helper.rb
Original file line number Diff line number Diff line change
Expand Up @@ -210,7 +210,7 @@ def reset_config
end

config = { config: ::Rails.application.config }
Datadog.registry[:rails].configure(config)
Datadog::Contrib::Rails::Framework.configure(config)
end

def test_repeat
Expand Down
13 changes: 7 additions & 6 deletions test/monkey_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
class MonkeyTest < Minitest::Test
def test_autopatch_modules
expected = {
rails: true,
elasticsearch: true,
http: true,
redis: true,
Expand Down Expand Up @@ -41,7 +42,7 @@ def test_patch_module
assert_equal(false, Datadog::Contrib::Grape::Patcher.patched?)
assert_equal(false, Datadog::Contrib::Aws::Patcher.patched?)
assert_equal(false, Datadog::Contrib::ActiveRecord::Patcher.patched?)
assert_equal({ elasticsearch: false, http: false, redis: false, grape: false, faraday: false, aws: false, sucker_punch: false, active_record: false, mongo: false, dalli: false, resque: false }, Datadog::Monkey.get_patched_modules())
assert_equal({ rails: false, elasticsearch: false, http: false, redis: false, grape: false, faraday: false, aws: false, sucker_punch: false, active_record: false, mongo: false, dalli: false, resque: false }, Datadog::Monkey.get_patched_modules())

Datadog::Monkey.patch_module(:redis)
assert_equal(false, Datadog::Contrib::Elasticsearch::Patcher.patched?)
Expand All @@ -51,7 +52,7 @@ def test_patch_module
assert_equal(false, Datadog::Contrib::Aws::Patcher.patched?)
assert_equal(false, Datadog::Contrib::ActiveRecord::Patcher.patched?)
refute(Datadog::Contrib::Faraday::Patcher.patched?)
assert_equal({ elasticsearch: false, http: false, redis: true, grape: false, faraday: false, aws: false, sucker_punch: false, active_record: false, mongo: false, dalli: false, resque: false }, Datadog::Monkey.get_patched_modules())
assert_equal({ rails: false, elasticsearch: false, http: false, redis: true, grape: false, faraday: false, aws: false, sucker_punch: false, active_record: false, mongo: false, dalli: false, resque: false }, Datadog::Monkey.get_patched_modules())

# now do it again to check it's idempotent
Datadog::Monkey.patch_module(:redis)
Expand All @@ -62,7 +63,7 @@ def test_patch_module
assert_equal(false, Datadog::Contrib::Aws::Patcher.patched?)
assert_equal(false, Datadog::Contrib::ActiveRecord::Patcher.patched?)
refute(Datadog::Contrib::Faraday::Patcher.patched?)
assert_equal({ elasticsearch: false, http: false, redis: true, grape: false, faraday: false, aws: false, sucker_punch: false, active_record: false, mongo: false, dalli: false, resque: false }, Datadog::Monkey.get_patched_modules())
assert_equal({ rails: false, elasticsearch: false, http: false, redis: true, grape: false, faraday: false, aws: false, sucker_punch: false, active_record: false, mongo: false, dalli: false, resque: false }, Datadog::Monkey.get_patched_modules())

Datadog::Monkey.patch(elasticsearch: true, redis: true)
assert_equal(true, Datadog::Contrib::Elasticsearch::Patcher.patched?)
Expand All @@ -71,7 +72,7 @@ def test_patch_module
assert_equal(false, Datadog::Contrib::Grape::Patcher.patched?)
assert_equal(false, Datadog::Contrib::Aws::Patcher.patched?)
assert_equal(false, Datadog::Contrib::ActiveRecord::Patcher.patched?)
assert_equal({ elasticsearch: true, http: false, redis: true, grape: false, faraday: false, aws: false, sucker_punch: false, active_record: false, mongo: false, dalli: false, resque: false }, Datadog::Monkey.get_patched_modules())
assert_equal({ rails: false, elasticsearch: true, http: false, redis: true, grape: false, faraday: false, aws: false, sucker_punch: false, active_record: false, mongo: false, dalli: false, resque: false }, Datadog::Monkey.get_patched_modules())

# verify that active_record is not auto patched by default
Datadog::Monkey.patch_all()
Expand All @@ -81,7 +82,7 @@ def test_patch_module
assert_equal(false, Datadog::Contrib::Grape::Patcher.patched?)
assert_equal(true, Datadog::Contrib::Aws::Patcher.patched?)
assert_equal(false, Datadog::Contrib::ActiveRecord::Patcher.patched?)
assert_equal({ elasticsearch: true, http: true, redis: true, grape: false, faraday: true, aws: true, sucker_punch: true, active_record: false, mongo: false, dalli: true, resque: true }, Datadog::Monkey.get_patched_modules())
assert_equal({ rails: false, elasticsearch: true, http: true, redis: true, grape: false, faraday: true, aws: true, sucker_punch: true, active_record: false, mongo: false, dalli: true, resque: true }, Datadog::Monkey.get_patched_modules())

Datadog::Monkey.patch_module(:active_record)
assert_equal(true, Datadog::Contrib::Elasticsearch::Patcher.patched?)
Expand All @@ -90,6 +91,6 @@ def test_patch_module
assert_equal(false, Datadog::Contrib::Grape::Patcher.patched?)
assert_equal(true, Datadog::Contrib::Aws::Patcher.patched?)
assert_equal(true, Datadog::Contrib::ActiveRecord::Patcher.patched?)
assert_equal({ elasticsearch: true, http: true, redis: true, grape: false, faraday: true, aws: true, sucker_punch: true, active_record: true, mongo: false, dalli: true, resque: true }, Datadog::Monkey.get_patched_modules())
assert_equal({ rails: false, elasticsearch: true, http: true, redis: true, grape: false, faraday: true, aws: true, sucker_punch: true, active_record: true, mongo: false, dalli: true, resque: true }, Datadog::Monkey.get_patched_modules())
end
end

0 comments on commit 996453e

Please sign in to comment.