diff --git a/lib/ddtrace.rb b/lib/ddtrace.rb index 3cdffd9d680..f72b8533927 100644 --- a/lib/ddtrace.rb +++ b/lib/ddtrace.rb @@ -45,43 +45,3 @@ def configure(&blk) 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 - 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 - - # Configure datadog settings before building the middleware stack. - # This is required because the middleware stack is frozen after - # the initialization and so it's too late to add our tracing - # functionalities. - initializer :datadog_config, before: :build_middleware_stack do |app| - app.config.middleware.insert_before( - 0, Datadog::Contrib::Rack::TraceMiddleware - ) - 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 diff --git a/lib/ddtrace/contrib/rails/framework.rb b/lib/ddtrace/contrib/rails/framework.rb index 12b6a26be32..a424af5c4d9 100644 --- a/lib/ddtrace/contrib/rails/framework.rb +++ b/lib/ddtrace/contrib/rails/framework.rb @@ -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 ) @@ -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 @@ -138,7 +119,7 @@ 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) @@ -146,13 +127,13 @@ def self.auto_instrument_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 diff --git a/lib/ddtrace/contrib/rails/patcher.rb b/lib/ddtrace/contrib/rails/patcher.rb new file mode 100644 index 00000000000..06d4dbcbd8b --- /dev/null +++ b/lib/ddtrace/contrib/rails/patcher.rb @@ -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 + + @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? diff --git a/lib/ddtrace/contrib/rails/railtie.rb b/lib/ddtrace/contrib/rails/railtie.rb new file mode 100644 index 00000000000..adfedbc5225 --- /dev/null +++ b/lib/ddtrace/contrib/rails/railtie.rb @@ -0,0 +1,27 @@ +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.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 + + # Configure datadog settings before building the middleware stack. + # This is required because the middleware stack is frozen after + # the initialization and so it's too late to add our tracing + # functionalities. + initializer :datadog_config, before: :build_middleware_stack do |app| + app.config.middleware.insert_before( + 0, Datadog::Contrib::Rack::TraceMiddleware + ) + end + end +end diff --git a/lib/ddtrace/contrib/sidekiq/tracer.rb b/lib/ddtrace/contrib/sidekiq/tracer.rb index 1a12607554c..7221ee22e7a 100644 --- a/lib/ddtrace/contrib/sidekiq/tracer.rb +++ b/lib/ddtrace/contrib/sidekiq/tracer.rb @@ -28,7 +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 {} + rails_config = Datadog.registry[:rails].to_h base_config = DEFAULT_CONFIG.merge(rails_config) user_config = base_config.merge(options) @tracer = user_config[:tracer] diff --git a/lib/ddtrace/monkey.rb b/lib/ddtrace/monkey.rb index 9120a3386cf..266af6e620f 100644 --- a/lib/ddtrace/monkey.rb +++ b/lib/ddtrace/monkey.rb @@ -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' diff --git a/test/helper.rb b/test/helper.rb index 1f11f657a2f..85573e69bc5 100644 --- a/test/helper.rb +++ b/test/helper.rb @@ -208,7 +208,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