Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Deprecate Grape pin in favor of configuration API #700

Merged
merged 3 commits into from
Feb 26, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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