Skip to content

Commit

Permalink
Merge pull request #700 from DataDog/refactor/grape_replace_pin_with_…
Browse files Browse the repository at this point in the history
…config

Deprecate Grape pin in favor of configuration API
  • Loading branch information
delner authored Feb 26, 2019
2 parents 2315a45 + 4a96080 commit d3940d8
Show file tree
Hide file tree
Showing 7 changed files with 263 additions and 169 deletions.
1 change: 1 addition & 0 deletions docs/GettingStarted.md
Original file line number Diff line number Diff line change
Expand Up @@ -659,6 +659,7 @@ Where `options` is an optional `Hash` that accepts the following parameters:

| Key | Description | Default |
| --- | ----------- | ------- |
| `enabled` | Defines whether Grape should be traced. Useful for temporarily disabling tracing. `true` or `false` | `true` |
| `service_name` | Service name used for `grape` instrumentation | `'grape'` |
| `tracer` | `Datadog::Tracer` used to perform instrumentation. Usually you don't need to set this. | `Datadog.tracer` |
Expand Down
1 change: 1 addition & 0 deletions lib/ddtrace/contrib/grape/configuration/settings.rb
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ module Grape
module Configuration
# Custom settings for the Grape integration
class Settings < Contrib::Configuration::Settings
option :enabled, default: true
option :service_name, default: Ext::SERVICE_NAME
end
end
Expand Down
271 changes: 140 additions & 131 deletions lib/ddtrace/contrib/grape/endpoint.rb
Original file line number Diff line number Diff line change
Expand Up @@ -12,158 +12,167 @@ module Endpoint
KEY_RUN = 'datadog_grape_endpoint_run'.freeze
KEY_RENDER = 'datadog_grape_endpoint_render'.freeze

def self.subscribe
# subscribe when a Grape endpoint is hit
::ActiveSupport::Notifications.subscribe('endpoint_run.grape.start_process') do |*args|
endpoint_start_process(*args)
end
::ActiveSupport::Notifications.subscribe('endpoint_run.grape') do |*args|
endpoint_run(*args)
end
::ActiveSupport::Notifications.subscribe('endpoint_render.grape.start_render') do |*args|
endpoint_start_render(*args)
end
::ActiveSupport::Notifications.subscribe('endpoint_render.grape') do |*args|
endpoint_render(*args)
end
::ActiveSupport::Notifications.subscribe('endpoint_run_filters.grape') do |*args|
endpoint_run_filters(*args)
class << self
def subscribe
# subscribe when a Grape endpoint is hit
::ActiveSupport::Notifications.subscribe('endpoint_run.grape.start_process') do |*args|
endpoint_start_process(*args)
end
::ActiveSupport::Notifications.subscribe('endpoint_run.grape') do |*args|
endpoint_run(*args)
end
::ActiveSupport::Notifications.subscribe('endpoint_render.grape.start_render') do |*args|
endpoint_start_render(*args)
end
::ActiveSupport::Notifications.subscribe('endpoint_render.grape') do |*args|
endpoint_render(*args)
end
::ActiveSupport::Notifications.subscribe('endpoint_run_filters.grape') do |*args|
endpoint_run_filters(*args)
end
end
end

def self.endpoint_start_process(*)
return if Thread.current[KEY_RUN]

# retrieve the tracer from the PIN object
pin = Datadog::Pin.get_from(::Grape)
return unless pin && pin.enabled?
def endpoint_start_process(*)
return if Thread.current[KEY_RUN]
return unless enabled?

# store the beginning of a trace
tracer = pin.tracer
service = pin.service
type = Datadog::Ext::HTTP::TYPE
tracer.trace(Ext::SPAN_ENDPOINT_RUN, service: service, span_type: type)
# Store the beginning of a trace
tracer.trace(
Ext::SPAN_ENDPOINT_RUN,
service: service_name,
span_type: Datadog::Ext::HTTP::TYPE
)

Thread.current[KEY_RUN] = true
rescue StandardError => e
Datadog::Tracer.log.error(e.message)
end
Thread.current[KEY_RUN] = true
rescue StandardError => e
Datadog::Tracer.log.error(e.message)
end

def self.endpoint_run(name, start, finish, id, payload)
return unless Thread.current[KEY_RUN]
Thread.current[KEY_RUN] = false

# retrieve the tracer from the PIN object
pin = Datadog::Pin.get_from(::Grape)
return unless pin && pin.enabled?

tracer = pin.tracer
span = tracer.active_span
return unless span

begin
# collect endpoint details
api = payload[:endpoint].options[:for]
# If the API inherits from Grape::API in version >= 1.2.0
# then the API will be an instance and the name must be derived from the base.
# See https://github.com/ruby-grape/grape/issues/1825
api_view = if defined?(::Grape::API::Instance) && api <= ::Grape::API::Instance
api.base.to_s
else
api.to_s
end

path = payload[:endpoint].options[:path].join('/')
resource = "#{api_view}##{path}"
span.resource = resource

# set the request span resource if it's a `rack.request` span
request_span = payload[:env][Datadog::Contrib::Rack::Ext::RACK_ENV_REQUEST_SPAN]
if !request_span.nil? && request_span.name == Datadog::Contrib::Rack::Ext::SPAN_REQUEST
request_span.resource = resource
def endpoint_run(name, start, finish, id, payload)
return unless Thread.current[KEY_RUN]
Thread.current[KEY_RUN] = false

return unless enabled?

span = tracer.active_span
return unless span

begin
# collect endpoint details
api = payload[:endpoint].options[:for]
# If the API inherits from Grape::API in version >= 1.2.0
# then the API will be an instance and the name must be derived from the base.
# See https://github.com/ruby-grape/grape/issues/1825
api_view = if defined?(::Grape::API::Instance) && api <= ::Grape::API::Instance
api.base.to_s
else
api.to_s
end

path = payload[:endpoint].options[:path].join('/')
resource = "#{api_view}##{path}"
span.resource = resource

# set the request span resource if it's a `rack.request` span
request_span = payload[:env][Datadog::Contrib::Rack::Ext::RACK_ENV_REQUEST_SPAN]
if !request_span.nil? && request_span.name == Datadog::Contrib::Rack::Ext::SPAN_REQUEST
request_span.resource = resource
end

# catch thrown exceptions
span.set_error(payload[:exception_object]) unless payload[:exception_object].nil?

# override the current span with this notification values
span.set_tag(Ext::TAG_ROUTE_ENDPOINT, api_view) unless api_view.nil?
span.set_tag(Ext::TAG_ROUTE_PATH, path)
ensure
span.start_time = start
span.finish(finish)
end

# catch thrown exceptions
span.set_error(payload[:exception_object]) unless payload[:exception_object].nil?

# override the current span with this notification values
span.set_tag(Ext::TAG_ROUTE_ENDPOINT, api_view) unless api_view.nil?
span.set_tag(Ext::TAG_ROUTE_PATH, path)
ensure
span.start_time = start
span.finish(finish)
rescue StandardError => e
Datadog::Tracer.log.error(e.message)
end
rescue StandardError => e
Datadog::Tracer.log.error(e.message)
end

def self.endpoint_start_render(*)
return if Thread.current[KEY_RENDER]
def endpoint_start_render(*)
return if Thread.current[KEY_RENDER]
return unless enabled?

# retrieve the tracer from the PIN object
pin = Datadog::Pin.get_from(::Grape)
return unless pin && pin.enabled?
# Store the beginning of a trace
tracer.trace(
Ext::SPAN_ENDPOINT_RENDER,
service: service_name,
span_type: Datadog::Ext::HTTP::TYPE
)

# store the beginning of a trace
tracer = pin.tracer
service = pin.service
type = Datadog::Ext::HTTP::TYPE
tracer.trace(Ext::SPAN_ENDPOINT_RENDER, service: service, span_type: type)
Thread.current[KEY_RENDER] = true
rescue StandardError => e
Datadog::Tracer.log.error(e.message)
end

Thread.current[KEY_RENDER] = true
rescue StandardError => e
Datadog::Tracer.log.error(e.message)
end
def endpoint_render(name, start, finish, id, payload)
return unless Thread.current[KEY_RENDER]
Thread.current[KEY_RENDER] = false

def self.endpoint_render(name, start, finish, id, payload)
return unless Thread.current[KEY_RENDER]
Thread.current[KEY_RENDER] = false
return unless enabled?

# retrieve the tracer from the PIN object
pin = Datadog::Pin.get_from(::Grape)
return unless pin && pin.enabled?
span = tracer.active_span
return unless span

tracer = pin.tracer
span = tracer.active_span
return unless span
# catch thrown exceptions
begin
span.set_error(payload[:exception_object]) unless payload[:exception_object].nil?
ensure
span.start_time = start
span.finish(finish)
end
rescue StandardError => e
Datadog::Tracer.log.error(e.message)
end

# catch thrown exceptions
begin
span.set_error(payload[:exception_object]) unless payload[:exception_object].nil?
ensure
span.start_time = start
span.finish(finish)
def endpoint_run_filters(name, start, finish, id, payload)
return unless enabled?

# safe-guard to prevent submitting empty filters
zero_length = (finish - start).zero?
filters = payload[:filters]
type = payload[:type]
return if (!filters || filters.empty?) || !type || zero_length

span = tracer.trace(
Ext::SPAN_ENDPOINT_RUN_FILTERS,
service: service_name,
span_type: Datadog::Ext::HTTP::TYPE
)

begin
# catch thrown exceptions
span.set_error(payload[:exception_object]) unless payload[:exception_object].nil?
span.set_tag(Ext::TAG_FILTER_TYPE, type.to_s)
ensure
span.start_time = start
span.finish(finish)
end
rescue StandardError => e
Datadog::Tracer.log.error(e.message)
end
rescue StandardError => e
Datadog::Tracer.log.error(e.message)
end

def self.endpoint_run_filters(name, start, finish, id, payload)
# retrieve the tracer from the PIN object
pin = Datadog::Pin.get_from(::Grape)
return unless pin && pin.enabled?
private

# safe-guard to prevent submitting empty filters
zero_length = (finish - start).zero?
filters = payload[:filters]
type = payload[:type]
return if (!filters || filters.empty?) || !type || zero_length
def tracer
datadog_configuration[:tracer]
end

tracer = pin.tracer
service = pin.service
type = Datadog::Ext::HTTP::TYPE
span = tracer.trace('grape.endpoint_run_filters', service: service, span_type: type)
def service_name
datadog_configuration[:service_name]
end

begin
# catch thrown exceptions
span.set_error(payload[:exception_object]) unless payload[:exception_object].nil?
span.set_tag(Ext::TAG_FILTER_TYPE, type.to_s)
ensure
span.start_time = start
span.finish(finish)
def enabled?
datadog_configuration[:enabled] == true
end

def datadog_configuration
Datadog.configuration[:grape]
end
rescue StandardError => e
Datadog::Tracer.log.error(e.message)
end
end
end
Expand Down
73 changes: 73 additions & 0 deletions lib/ddtrace/contrib/grape/instrumentation.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
module Datadog
module Contrib
module Grape
# Instrumentation for Grape::Endpoint
module Instrumentation
def self.included(base)
if Gem::Version.new(RUBY_VERSION) < Gem::Version.new('2.0.0')
base.class_eval do
# Class methods
singleton_class.send(:include, ClassMethodsCompatibility)
singleton_class.send(:include, ClassMethods)

# Instance methods
include InstanceMethodsCompatibility
include InstanceMethods
end
else
base.singleton_class.send(:prepend, ClassMethods)
base.send(:prepend, InstanceMethods)
end
end

# Compatibility shim for Rubies not supporting `.prepend`
module ClassMethodsCompatibility
def self.included(base)
base.class_eval do
alias_method :generate_api_method_without_datadog, :generate_api_method
remove_method :generate_api_method
end
end

def generate_api_method(*args, &block)
generate_api_method_without_datadog(*args, &block)
end
end

# Compatibility shim for Rubies not supporting `.prepend`
module InstanceMethodsCompatibility
def self.included(base)
base.class_eval do
alias_method :run_without_datadog, :run
remove_method :run
end
end

def run(*args, &block)
run_without_datadog(*args, &block)
end
end

# ClassMethods - implementing instrumentation
module ClassMethods
def generate_api_method(*params, &block)
method_api = super

proc do |*args|
::ActiveSupport::Notifications.instrument('endpoint_render.grape.start_render')
method_api.call(*args)
end
end
end

# InstanceMethods - implementing instrumentation
module InstanceMethods
def run(*args)
::ActiveSupport::Notifications.instrument('endpoint_run.grape.start_process')
super
end
end
end
end
end
end
Loading

0 comments on commit d3940d8

Please sign in to comment.