Skip to content

Commit

Permalink
Add Datadog::Registry
Browse files Browse the repository at this point in the history
  • Loading branch information
p-lambert committed Oct 16, 2017
1 parent a662829 commit 79941df
Show file tree
Hide file tree
Showing 17 changed files with 190 additions and 37 deletions.
11 changes: 9 additions & 2 deletions lib/ddtrace.rb
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
require 'ddtrace/monkey'
require 'ddtrace/registry'
require 'ddtrace/pin'
require 'ddtrace/tracer'
require 'ddtrace/error'
require 'ddtrace/pipeline'

# \Datadog global namespace that includes all tracing functionality for Tracer and Span classes.
module Datadog
@tracer = Datadog::Tracer.new()
@tracer = Tracer.new
@registry = Registry.new

# Default tracer that can be used as soon as +ddtrace+ is required:
#
Expand All @@ -25,8 +26,14 @@ module Datadog
def self.tracer
@tracer
end

def self.registry
@registry
end
end

require 'ddtrace/monkey'

# Datadog auto instrumentation for frameworks
if defined?(Rails::VERSION)
if !ENV['DISABLE_DATADOG_RAILS']
Expand Down
3 changes: 3 additions & 0 deletions lib/ddtrace/contrib/active_record/patcher.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@ module ActiveRecord
# Patcher enables patching of 'active_record' module.
# This is used in monkey.rb to manually apply patches
module Patcher
include Base
register_as :active_record, auto_patch: false

@patched = false

module_function
Expand Down
3 changes: 3 additions & 0 deletions lib/ddtrace/contrib/aws/patcher.rb
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@ module Aws

# Responsible for hooking the instrumentation into aws-sdk
module Patcher
include Base
register_as :aws, auto_patch: true

@patched = false

class << self
Expand Down
12 changes: 12 additions & 0 deletions lib/ddtrace/contrib/base.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
require 'ddtrace/registry'

module Datadog
module Contrib
# Base provides features that are shared across all integrations
module Base
def self.included(base)
base.send(:include, Registry::Registerable)
end
end
end
end
3 changes: 3 additions & 0 deletions lib/ddtrace/contrib/dalli/patcher.rb
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@ module Dalli

# Responsible for hooking the instrumentation into `dalli`
module Patcher
include Base
register_as :dalli, auto_patch: true

@patched = false

class << self
Expand Down
3 changes: 3 additions & 0 deletions lib/ddtrace/contrib/elasticsearch/patcher.rb
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,9 @@ module Elasticsearch
# Patcher enables patching of 'elasticsearch/transport' module.
# This is used in monkey.rb to automatically apply patches
module Patcher
include Base
register_as :elasticsearch, auto_patch: true

@patched = false

module_function
Expand Down
3 changes: 3 additions & 0 deletions lib/ddtrace/contrib/faraday/patcher.rb
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@ module Faraday

# Responsible for hooking the instrumentation into faraday
module Patcher
include Base
register_as :faraday, auto_patch: true

@patched = false

class << self
Expand Down
3 changes: 3 additions & 0 deletions lib/ddtrace/contrib/grape/patcher.rb
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@ module Grape
# Patcher that introduces more instrumentation for Grape endpoints, so that
# new signals are executed at the beginning of each step (filters, render and run)
module Patcher
include Base
register_as :grape, auto_patch: true

@patched = false

module_function
Expand Down
3 changes: 3 additions & 0 deletions lib/ddtrace/contrib/http/patcher.rb
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,9 @@ def should_skip_distributed_tracing?(pin)
# Patcher enables patching of 'net/http' module.
# This is used in monkey.rb to automatically apply patches
module Patcher
include Base
register_as :http, auto_patch: true

@patched = false

module_function
Expand Down
3 changes: 3 additions & 0 deletions lib/ddtrace/contrib/mongodb/patcher.rb
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,9 @@ module MongoDB
# Use the `Datadog::Monkey.patch_module(:mongodb)` to activate tracing for
# this module.
module Patcher
include Base
register_as :mongo, auto_patch: true

@patched = false

module_function
Expand Down
3 changes: 3 additions & 0 deletions lib/ddtrace/contrib/redis/patcher.rb
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,9 @@ module Redis
# Patcher enables patching of 'redis' module.
# This is used in monkey.rb to automatically apply patches
module Patcher
include Base
register_as :redis, auto_patch: true

@patched = false

module_function
Expand Down
3 changes: 3 additions & 0 deletions lib/ddtrace/contrib/resque/patcher.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@ module Resque

# Patcher for Resque integration - sets up the pin for the integration
module Patcher
include Base
register_as :resque, auto_patch: true

@patched = false

class << self
Expand Down
3 changes: 3 additions & 0 deletions lib/ddtrace/contrib/sucker_punch/patcher.rb
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,9 @@ module SuckerPunch

# Responsible for hooking the instrumentation into `sucker_punch`
module Patcher
include Base
register_as :sucker_punch, auto_patch: true

@patched = false

module_function
Expand Down
49 changes: 14 additions & 35 deletions lib/ddtrace/monkey.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
# We import all patchers for every module we support, but this is fine
# 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/active_record/patcher'
require 'ddtrace/contrib/elasticsearch/patcher'
require 'ddtrace/contrib/faraday/patcher'
Expand All @@ -18,51 +19,30 @@
module Datadog
# Monkey is used for monkey-patching 3rd party libs.
module Monkey
@patched = []
@autopatch_modules = {
elasticsearch: true,
http: true,
redis: true,
grape: true,
faraday: true,
aws: true,
sucker_punch: true,
mongo: true,
dalli: true,
resque: true,
active_record: false
}
# Patchers should expose 2 methods:
# - patch, which applies our patch if needed. Should be idempotent,
# can be call twice but should just do nothing the second time.
# - patched?, which returns true if the module has been succesfully
# patched (patching might have failed if requirements were not here)
@patchers = { elasticsearch: Datadog::Contrib::Elasticsearch::Patcher,
http: Datadog::Contrib::HTTP::Patcher,
redis: Datadog::Contrib::Redis::Patcher,
grape: Datadog::Contrib::Grape::Patcher,
faraday: Datadog::Contrib::Faraday::Patcher,
aws: Datadog::Contrib::Aws::Patcher,
sucker_punch: Datadog::Contrib::SuckerPunch::Patcher,
mongo: Datadog::Contrib::MongoDB::Patcher,
dalli: Datadog::Contrib::Dalli::Patcher,
resque: Datadog::Contrib::Resque::Patcher,
active_record: Datadog::Contrib::ActiveRecord::Patcher }

@mutex = Mutex.new
@registry = Datadog.registry

module_function

attr_accessor :registry

def autopatch_modules
@autopatch_modules.clone
registry.to_h
end

def patch_all
patch @autopatch_modules
patch(autopatch_modules)
end

def patch_module(m)
@mutex.synchronize do
patcher = @patchers[m]
patcher = registry[m]
raise "Unsupported module #{m}" unless patcher
patcher.patch
end
Expand All @@ -75,16 +55,11 @@ def patch(modules)
end

def get_patched_modules
patched = autopatch_modules
@patchers.each do |k, v|
registry.each_with_object({}) do |entry, patched|
@mutex.synchronize do
if v
patcher = @patchers[k]
patched[k] = patcher.patched? if patcher
end
patched[entry.name] = entry.klass.patched?
end
end
patched
end

def without_warnings
Expand All @@ -98,5 +73,9 @@ def without_warnings
$VERBOSE = v
end
end

class << self
attr_accessor :registry
end
end
end
42 changes: 42 additions & 0 deletions lib/ddtrace/registry.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
require_relative 'registry/registerable'

module Datadog
# Registry provides insertion/retrieval capabilities for integrations
class Registry
include Enumerable

Entry = Struct.new(:name, :klass, :auto_patch)

def initialize
@data = {}
@mutex = Mutex.new
end

def add(name, klass, auto_patch = false)
@mutex.synchronize do
@data[name] = Entry.new(name, klass, auto_patch).freeze
end
end

def each
@mutex.synchronize do
@data.each { |_, entry| yield(entry) }
end
end

def [](name)
@mutex.synchronize do
entry = @data[name]
entry.klass if entry
end
end

def to_h
@mutex.synchronize do
@data.each_with_object({}) do |(_, entry), hash|
hash[entry.name] = entry.auto_patch
end
end
end
end
end
20 changes: 20 additions & 0 deletions lib/ddtrace/registry/registerable.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
module Datadog
class Registry
# Registerable provides a convenience method for self-registering
module Registerable
def self.included(base)
base.singleton_class.send(:include, ClassMethods)
end

# ClassMethods
module ClassMethods
def register_as(name, options = {})
registry = options.fetch(:registry, Datadog.registry)
auto_patch = options.fetch(:auto_patch, false)

registry.add(name, self, auto_patch)
end
end
end
end
end
60 changes: 60 additions & 0 deletions test/registry_test.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
require 'minitest/autorun'
require 'ddtrace'
require 'ddtrace/registry'

module Datadog
class RegistryTest < Minitest::Test
def test_object_retrieval
registry = Registry.new

object1 = Object.new
object2 = Object.new

registry.add(:object1, object1)
registry.add(:object2, object2)

assert_same(object1, registry[:object1])
assert_same(object2, registry[:object2])
end

def test_hash_coercion
registry = Registry.new

object1 = Object.new
object2 = Object.new

registry.add(:object1, object1, true)
registry.add(:object2, object2, false)

assert_equal({ object1: true, object2: false }, registry.to_h)
end

def test_enumeration
registry = Registry.new

object1 = Object.new
object2 = Object.new

registry.add(:object1, object1, true)
registry.add(:object2, object2, false)

assert(registry.respond_to?(:each))
assert_kind_of(Enumerable, registry)

# Enumerable#map
objects = registry.map(&:klass)
assert_kind_of(Array, objects)
assert_equal(2, objects.size)
assert_includes(objects, object1)
assert_includes(objects, object2)
end

def test_registry_entry
entry = Registry::Entry.new(:array, Array, true)

assert_equal(:array, entry.name)
assert_equal(Array, entry.klass)
assert_equal(true, entry.auto_patch)
end
end
end

0 comments on commit 79941df

Please sign in to comment.