ddtrace 0.12.0 → 0.13.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.circleci/config.yml +468 -0
- data/.circleci/images/primary/Dockerfile-1.9.3 +69 -0
- data/.circleci/images/primary/Dockerfile-2.0.0 +69 -0
- data/.circleci/images/primary/Dockerfile-2.1.10 +69 -0
- data/.circleci/images/primary/Dockerfile-2.2.10 +69 -0
- data/.circleci/images/primary/Dockerfile-2.3.7 +73 -0
- data/.circleci/images/primary/Dockerfile-2.4.4 +73 -0
- data/.dockerignore +1 -0
- data/.env +21 -11
- data/.gitignore +1 -1
- data/.rubocop.yml +4 -1
- data/Appraisals +471 -66
- data/CHANGELOG.md +80 -2
- data/Rakefile +304 -80
- data/ddtrace.gemspec +7 -7
- data/docker-compose.yml +240 -37
- data/docs/GettingStarted.md +273 -7
- data/lib/ddtrace/configuration.rb +2 -2
- data/lib/ddtrace/contrib/active_model_serializers/event.rb +57 -0
- data/lib/ddtrace/contrib/active_model_serializers/events/render.rb +32 -0
- data/lib/ddtrace/contrib/active_model_serializers/events/serialize.rb +35 -0
- data/lib/ddtrace/contrib/active_model_serializers/events.rb +30 -0
- data/lib/ddtrace/contrib/active_model_serializers/patcher.rb +62 -0
- data/lib/ddtrace/contrib/active_record/event.rb +30 -0
- data/lib/ddtrace/contrib/active_record/events/instantiation.rb +51 -0
- data/lib/ddtrace/contrib/active_record/events/sql.rb +48 -0
- data/lib/ddtrace/contrib/active_record/events.rb +30 -0
- data/lib/ddtrace/contrib/active_record/patcher.rb +3 -73
- data/lib/ddtrace/contrib/active_record/utils.rb +1 -15
- data/lib/ddtrace/contrib/active_support/notifications/event.rb +62 -0
- data/lib/ddtrace/contrib/aws/instrumentation.rb +2 -2
- data/lib/ddtrace/contrib/elasticsearch/patcher.rb +2 -2
- data/lib/ddtrace/contrib/elasticsearch/quantize.rb +18 -49
- data/lib/ddtrace/contrib/excon/middleware.rb +139 -0
- data/lib/ddtrace/contrib/excon/patcher.rb +50 -0
- data/lib/ddtrace/contrib/grpc/datadog_interceptor/client.rb +49 -0
- data/lib/ddtrace/contrib/grpc/datadog_interceptor/server.rb +66 -0
- data/lib/ddtrace/contrib/grpc/datadog_interceptor.rb +65 -0
- data/lib/ddtrace/contrib/grpc/intercept_with_datadog.rb +49 -0
- data/lib/ddtrace/contrib/grpc/patcher.rb +62 -0
- data/lib/ddtrace/contrib/http/patcher.rb +18 -16
- data/lib/ddtrace/contrib/mongodb/parsers.rb +24 -13
- data/lib/ddtrace/contrib/mongodb/patcher.rb +1 -0
- data/lib/ddtrace/contrib/mongodb/subscribers.rb +5 -4
- data/lib/ddtrace/contrib/mysql2/client.rb +60 -0
- data/lib/ddtrace/contrib/mysql2/patcher.rb +43 -0
- data/lib/ddtrace/contrib/racecar/event.rb +61 -0
- data/lib/ddtrace/contrib/racecar/events/batch.rb +27 -0
- data/lib/ddtrace/contrib/racecar/events/message.rb +27 -0
- data/lib/ddtrace/contrib/racecar/events.rb +30 -0
- data/lib/ddtrace/contrib/racecar/patcher.rb +6 -52
- data/lib/ddtrace/contrib/rack/middlewares.rb +65 -11
- data/lib/ddtrace/contrib/rack/patcher.rb +16 -0
- data/lib/ddtrace/contrib/rack/request_queue.rb +34 -0
- data/lib/ddtrace/contrib/rails/action_controller_patch.rb +77 -0
- data/lib/ddtrace/contrib/rails/action_view.rb +0 -65
- data/lib/ddtrace/contrib/rails/active_support.rb +9 -8
- data/lib/ddtrace/contrib/rails/core_extensions.rb +74 -115
- data/lib/ddtrace/contrib/rake/instrumentation.rb +70 -0
- data/lib/ddtrace/contrib/rake/patcher.rb +53 -0
- data/lib/ddtrace/contrib/sequel/database.rb +58 -0
- data/lib/ddtrace/contrib/sequel/dataset.rb +59 -0
- data/lib/ddtrace/contrib/sequel/patcher.rb +56 -0
- data/lib/ddtrace/contrib/sequel/utils.rb +28 -0
- data/lib/ddtrace/contrib/sidekiq/tracer.rb +1 -0
- data/lib/ddtrace/contrib/sinatra/env.rb +39 -0
- data/lib/ddtrace/contrib/sinatra/headers.rb +31 -0
- data/lib/ddtrace/contrib/sinatra/tracer.rb +21 -38
- data/lib/ddtrace/contrib/sinatra/tracer_middleware.rb +61 -0
- data/lib/ddtrace/ext/distributed.rb +5 -0
- data/lib/ddtrace/ext/grpc.rb +7 -0
- data/lib/ddtrace/ext/http.rb +35 -5
- data/lib/ddtrace/propagation/grpc_propagator.rb +54 -0
- data/lib/ddtrace/quantization/hash.rb +103 -0
- data/lib/ddtrace/tracer.rb +4 -1
- data/lib/ddtrace/utils/database.rb +21 -0
- data/lib/ddtrace/utils.rb +12 -2
- data/lib/ddtrace/version.rb +1 -1
- data/lib/ddtrace.rb +7 -0
- metadata +50 -21
- data/circle.yml +0 -69
- data/gemfiles/contrib.gemfile +0 -25
- data/gemfiles/contrib_old.gemfile +0 -23
- data/gemfiles/rails30_postgres.gemfile +0 -11
- data/gemfiles/rails30_postgres_sidekiq.gemfile +0 -12
- data/gemfiles/rails32_mysql2.gemfile +0 -12
- data/gemfiles/rails32_postgres.gemfile +0 -11
- data/gemfiles/rails32_postgres_redis.gemfile +0 -13
- data/gemfiles/rails32_postgres_sidekiq.gemfile +0 -12
- data/gemfiles/rails4_mysql2.gemfile +0 -10
- data/gemfiles/rails4_postgres.gemfile +0 -10
- data/gemfiles/rails4_postgres_redis.gemfile +0 -12
- data/gemfiles/rails4_postgres_sidekiq.gemfile +0 -12
- data/gemfiles/rails5_mysql2.gemfile +0 -9
- data/gemfiles/rails5_postgres.gemfile +0 -9
- data/gemfiles/rails5_postgres_redis.gemfile +0 -11
- data/gemfiles/rails5_postgres_sidekiq.gemfile +0 -11
|
@@ -4,28 +4,26 @@ module Datadog
|
|
|
4
4
|
# Quantize contains ES-specific resource quantization tools.
|
|
5
5
|
module Quantize
|
|
6
6
|
PLACEHOLDER = '?'.freeze
|
|
7
|
+
ID_PLACEHOLDER = '\1?'.freeze
|
|
7
8
|
EXCLUDE_KEYS = [].freeze
|
|
8
9
|
SHOW_KEYS = [:_index, :_type, :_id].freeze
|
|
9
|
-
DEFAULT_OPTIONS = {
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
INDEX_REGEXP = /[0-9]{2,}/
|
|
15
|
-
INDEX_PLACEHOLDER = '?'.freeze
|
|
10
|
+
DEFAULT_OPTIONS = {
|
|
11
|
+
exclude: EXCLUDE_KEYS,
|
|
12
|
+
show: SHOW_KEYS,
|
|
13
|
+
placeholder: PLACEHOLDER
|
|
14
|
+
}.freeze
|
|
16
15
|
|
|
17
16
|
module_function
|
|
18
17
|
|
|
19
|
-
# Very basic quantization, complex processing should be done in the agent
|
|
20
18
|
def format_url(url)
|
|
21
|
-
|
|
22
|
-
|
|
19
|
+
sanitize_fragment_with_id(url)
|
|
20
|
+
.gsub(/(?:[\d]+)/, PLACEHOLDER)
|
|
23
21
|
end
|
|
24
22
|
|
|
25
23
|
def format_body(body, options = {})
|
|
26
24
|
format_body!(body, options)
|
|
27
25
|
rescue StandardError
|
|
28
|
-
PLACEHOLDER
|
|
26
|
+
options[:placeholder] || PLACEHOLDER
|
|
29
27
|
end
|
|
30
28
|
|
|
31
29
|
def format_body!(body, options = {})
|
|
@@ -36,48 +34,12 @@ module Datadog
|
|
|
36
34
|
|
|
37
35
|
# Parse each statement and quantize them.
|
|
38
36
|
statements.collect do |string|
|
|
39
|
-
reserialize_json(string) do |obj|
|
|
40
|
-
|
|
37
|
+
reserialize_json(string, options[:placeholder]) do |obj|
|
|
38
|
+
Datadog::Quantization::Hash.format(obj, options)
|
|
41
39
|
end
|
|
42
40
|
end.join("\n")
|
|
43
41
|
end
|
|
44
42
|
|
|
45
|
-
def format_statement(statement, options = {})
|
|
46
|
-
return statement if options[:show] == :all
|
|
47
|
-
|
|
48
|
-
case statement
|
|
49
|
-
when Hash
|
|
50
|
-
statement.each_with_object({}) do |(key, value), quantized|
|
|
51
|
-
if options[:show].include?(key.to_sym)
|
|
52
|
-
quantized[key] = value
|
|
53
|
-
elsif !options[:exclude].include?(key.to_sym)
|
|
54
|
-
quantized[key] = format_value(value, options)
|
|
55
|
-
end
|
|
56
|
-
end
|
|
57
|
-
else
|
|
58
|
-
format_value(statement, options)
|
|
59
|
-
end
|
|
60
|
-
end
|
|
61
|
-
|
|
62
|
-
def format_value(value, options = {})
|
|
63
|
-
return value if options[:show] == :all
|
|
64
|
-
|
|
65
|
-
case value
|
|
66
|
-
when Hash
|
|
67
|
-
format_statement(value, options)
|
|
68
|
-
when Array
|
|
69
|
-
# If any are objects, format them.
|
|
70
|
-
if value.any? { |v| v.class <= Hash || v.class <= Array }
|
|
71
|
-
value.collect { |i| format_value(i, options) }
|
|
72
|
-
# Otherwise short-circuit and return single placeholder
|
|
73
|
-
else
|
|
74
|
-
PLACEHOLDER
|
|
75
|
-
end
|
|
76
|
-
else
|
|
77
|
-
PLACEHOLDER
|
|
78
|
-
end
|
|
79
|
-
end
|
|
80
|
-
|
|
81
43
|
def merge_options(original, additional)
|
|
82
44
|
{}.tap do |options|
|
|
83
45
|
# Show
|
|
@@ -105,6 +67,13 @@ module Datadog
|
|
|
105
67
|
fail_value
|
|
106
68
|
end
|
|
107
69
|
end
|
|
70
|
+
|
|
71
|
+
# Sanitizes URL fragment by changing it to ? whenever a number is detected
|
|
72
|
+
# This is meant as simple heuristic that attempts to detect if particular fragment
|
|
73
|
+
# represents document Id. This is meant to reduce the cardinality in most frequent cases.
|
|
74
|
+
def sanitize_fragment_with_id(url)
|
|
75
|
+
url.gsub(%r{^(/?[^/]*/[^/]*/)(?:[^\?/\d]*[\d]+[^\?/]*)}, ID_PLACEHOLDER)
|
|
76
|
+
end
|
|
108
77
|
end
|
|
109
78
|
end
|
|
110
79
|
end
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
require 'excon'
|
|
2
|
+
require 'ddtrace/ext/http'
|
|
3
|
+
require 'ddtrace/ext/net'
|
|
4
|
+
require 'ddtrace/ext/distributed'
|
|
5
|
+
require 'ddtrace/propagation/http_propagator'
|
|
6
|
+
|
|
7
|
+
module Datadog
|
|
8
|
+
module Contrib
|
|
9
|
+
module Excon
|
|
10
|
+
# Middleware implements an excon-middleware for ddtrace instrumentation
|
|
11
|
+
class Middleware < ::Excon::Middleware::Base
|
|
12
|
+
SPAN_NAME = 'excon.request'.freeze
|
|
13
|
+
DEFAULT_ERROR_HANDLER = lambda do |response|
|
|
14
|
+
Ext::HTTP::ERROR_RANGE.cover?(response[:status])
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def initialize(stack, options = {})
|
|
18
|
+
super(stack)
|
|
19
|
+
@options = Datadog.configuration[:excon].merge(options)
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def request_call(datum)
|
|
23
|
+
begin
|
|
24
|
+
unless datum.key?(:datadog_span)
|
|
25
|
+
tracer.trace(SPAN_NAME).tap do |span|
|
|
26
|
+
datum[:datadog_span] = span
|
|
27
|
+
annotate!(span, datum)
|
|
28
|
+
propagate!(span, datum) if distributed_tracing?
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
rescue StandardError => e
|
|
32
|
+
Datadog::Tracer.log.debug(e.message)
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
@stack.request_call(datum)
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def response_call(datum)
|
|
39
|
+
@stack.response_call(datum).tap do |d|
|
|
40
|
+
handle_response(d)
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def error_call(datum)
|
|
45
|
+
handle_response(datum)
|
|
46
|
+
@stack.error_call(datum)
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
# Returns a child class of this trace middleware
|
|
50
|
+
# With options given as defaults.
|
|
51
|
+
def self.with(options = {})
|
|
52
|
+
Class.new(self) do
|
|
53
|
+
@options = options
|
|
54
|
+
|
|
55
|
+
# rubocop:disable Style/TrivialAccessors
|
|
56
|
+
def self.options
|
|
57
|
+
@options
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def initialize(stack)
|
|
61
|
+
super(stack, self.class.options)
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
# Returns a copy of the default stack with the trace middleware injected
|
|
67
|
+
def self.around_default_stack
|
|
68
|
+
::Excon.defaults[:middlewares].dup.tap do |default_stack|
|
|
69
|
+
# If the default stack contains a version of the trace middleware already...
|
|
70
|
+
existing_trace_middleware = default_stack.find { |m| m <= Middleware }
|
|
71
|
+
default_stack.delete(existing_trace_middleware) if existing_trace_middleware
|
|
72
|
+
|
|
73
|
+
# Inject after the ResponseParser middleware
|
|
74
|
+
response_middleware_index = default_stack.index(::Excon::Middleware::ResponseParser).to_i
|
|
75
|
+
default_stack.insert(response_middleware_index + 1, self)
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
private
|
|
80
|
+
|
|
81
|
+
def tracer
|
|
82
|
+
@options[:tracer]
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
def distributed_tracing?
|
|
86
|
+
@options[:distributed_tracing] == true && tracer.enabled
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
def error_handler
|
|
90
|
+
@options[:error_handler] || DEFAULT_ERROR_HANDLER
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
def split_by_domain?
|
|
94
|
+
@options[:split_by_domain] == true
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
def annotate!(span, datum)
|
|
98
|
+
span.resource = datum[:method].to_s.upcase
|
|
99
|
+
span.service = service_name(datum)
|
|
100
|
+
span.span_type = Ext::HTTP::TYPE
|
|
101
|
+
span.set_tag(Ext::HTTP::URL, datum[:path])
|
|
102
|
+
span.set_tag(Ext::HTTP::METHOD, datum[:method].to_s.upcase)
|
|
103
|
+
span.set_tag(Ext::NET::TARGET_HOST, datum[:host])
|
|
104
|
+
span.set_tag(Ext::NET::TARGET_PORT, datum[:port].to_s)
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
def handle_response(datum)
|
|
108
|
+
if datum.key?(:datadog_span)
|
|
109
|
+
datum[:datadog_span].tap do |span|
|
|
110
|
+
return span if span.finished?
|
|
111
|
+
|
|
112
|
+
if datum.key?(:response)
|
|
113
|
+
response = datum[:response]
|
|
114
|
+
if error_handler.call(response)
|
|
115
|
+
span.set_error(["Error #{response[:status]}", response[:body]])
|
|
116
|
+
end
|
|
117
|
+
span.set_tag(Ext::HTTP::STATUS_CODE, response[:status])
|
|
118
|
+
end
|
|
119
|
+
span.set_error(datum[:error]) if datum.key?(:error)
|
|
120
|
+
span.finish
|
|
121
|
+
datum.delete(:datadog_span)
|
|
122
|
+
end
|
|
123
|
+
end
|
|
124
|
+
rescue StandardError => e
|
|
125
|
+
Datadog::Tracer.log.debug(e.message)
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
def propagate!(span, datum)
|
|
129
|
+
Datadog::HTTPPropagator.inject!(span.context, datum[:headers])
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
def service_name(datum)
|
|
133
|
+
# TODO: Change this to implement more sensible multiplexing
|
|
134
|
+
split_by_domain? ? datum[:host] : @options[:service_name]
|
|
135
|
+
end
|
|
136
|
+
end
|
|
137
|
+
end
|
|
138
|
+
end
|
|
139
|
+
end
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
require 'ddtrace/ext/app_types'
|
|
2
|
+
|
|
3
|
+
module Datadog
|
|
4
|
+
module Contrib
|
|
5
|
+
module Excon
|
|
6
|
+
# Responsible for hooking the instrumentation into Excon
|
|
7
|
+
module Patcher
|
|
8
|
+
include Base
|
|
9
|
+
|
|
10
|
+
DEFAULT_SERVICE = 'excon'.freeze
|
|
11
|
+
|
|
12
|
+
register_as :excon
|
|
13
|
+
option :tracer, default: Datadog.tracer
|
|
14
|
+
option :service_name, default: DEFAULT_SERVICE
|
|
15
|
+
option :distributed_tracing, default: false
|
|
16
|
+
option :split_by_domain, default: false
|
|
17
|
+
option :error_handler, default: nil
|
|
18
|
+
|
|
19
|
+
@patched = false
|
|
20
|
+
|
|
21
|
+
module_function
|
|
22
|
+
|
|
23
|
+
def patch
|
|
24
|
+
return @patched if patched? || !compatible?
|
|
25
|
+
|
|
26
|
+
require 'ddtrace/contrib/excon/middleware'
|
|
27
|
+
|
|
28
|
+
add_middleware
|
|
29
|
+
|
|
30
|
+
@patched = true
|
|
31
|
+
rescue => e
|
|
32
|
+
Tracer.log.error("Unable to apply Excon integration: #{e}")
|
|
33
|
+
@patched
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def patched?
|
|
37
|
+
@patched
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def compatible?
|
|
41
|
+
defined?(::Excon)
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def add_middleware
|
|
45
|
+
::Excon.defaults[:middlewares] = Middleware.around_default_stack
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
end
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
module Datadog
|
|
2
|
+
module Contrib
|
|
3
|
+
module GRPC
|
|
4
|
+
module DatadogInterceptor
|
|
5
|
+
# The DatadogInterceptor::Client implements the tracing strategy
|
|
6
|
+
# for gRPC client-side endpoitns. This middleware compoent will
|
|
7
|
+
# inject trace context information into gRPC metadata prior to
|
|
8
|
+
# sending the request to the server.
|
|
9
|
+
class Client < Base
|
|
10
|
+
def trace(keywords)
|
|
11
|
+
keywords[:metadata] ||= {}
|
|
12
|
+
|
|
13
|
+
options = {
|
|
14
|
+
span_type: Datadog::Ext::GRPC::TYPE,
|
|
15
|
+
service: datadog_pin.service_name,
|
|
16
|
+
resource: format_resource(keywords[:method])
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
tracer.trace('grpc.client', options) do |span|
|
|
20
|
+
annotate!(span, keywords[:metadata])
|
|
21
|
+
|
|
22
|
+
yield
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
private
|
|
27
|
+
|
|
28
|
+
def annotate!(span, metadata)
|
|
29
|
+
metadata.each do |header, value|
|
|
30
|
+
span.set_tag(header, value)
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
Datadog::GRPCPropagator
|
|
34
|
+
.inject!(span.context, metadata)
|
|
35
|
+
rescue StandardError => e
|
|
36
|
+
Datadog::Tracer.log.debug("GRPC client trace failed: #{e}")
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def format_resource(proto_method)
|
|
40
|
+
proto_method.downcase
|
|
41
|
+
.split('/')
|
|
42
|
+
.reject(&:empty?)
|
|
43
|
+
.join('.')
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
end
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
module Datadog
|
|
2
|
+
module Contrib
|
|
3
|
+
module GRPC
|
|
4
|
+
module DatadogInterceptor
|
|
5
|
+
# The DatadogInterceptor::Server implements the tracing strategy
|
|
6
|
+
# for gRPC server-side endpoints. When the datadog fields have been
|
|
7
|
+
# added to the gRPC call metadata, this middleware component will
|
|
8
|
+
# extract any client-side tracing information, attempting to associate
|
|
9
|
+
# its tracing context with a parent client-side context
|
|
10
|
+
class Server < Base
|
|
11
|
+
def trace(keywords)
|
|
12
|
+
options = {
|
|
13
|
+
span_type: Datadog::Ext::GRPC::TYPE,
|
|
14
|
+
service: datadog_pin.service_name,
|
|
15
|
+
resource: format_resource(keywords[:method])
|
|
16
|
+
}
|
|
17
|
+
metadata = keywords[:call].metadata
|
|
18
|
+
|
|
19
|
+
set_distributed_context!(tracer, metadata)
|
|
20
|
+
|
|
21
|
+
tracer.trace('grpc.service', options) do |span|
|
|
22
|
+
annotate!(span, metadata)
|
|
23
|
+
|
|
24
|
+
yield
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
private
|
|
29
|
+
|
|
30
|
+
def set_distributed_context!(tracer, metadata)
|
|
31
|
+
tracer.provider.context = Datadog::GRPCPropagator
|
|
32
|
+
.extract(metadata)
|
|
33
|
+
rescue StandardError => e
|
|
34
|
+
Datadog::Tracer.log.debug(
|
|
35
|
+
"unable to propagate GRPC metadata to context: #{e}"
|
|
36
|
+
)
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def annotate!(span, metadata)
|
|
40
|
+
metadata.each do |header, value|
|
|
41
|
+
next if reserved_headers.include?(header)
|
|
42
|
+
span.set_tag(header, value)
|
|
43
|
+
end
|
|
44
|
+
rescue StandardError => e
|
|
45
|
+
Datadog::Tracer.log.debug("GRPC client trace failed: #{e}")
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def reserved_headers
|
|
49
|
+
[Datadog::Ext::DistributedTracing::GRPC_METADATA_TRACE_ID,
|
|
50
|
+
Datadog::Ext::DistributedTracing::GRPC_METADATA_PARENT_ID,
|
|
51
|
+
Datadog::Ext::DistributedTracing::GRPC_METADATA_SAMPLING_PRIORITY]
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
def format_resource(proto_method)
|
|
55
|
+
proto_method.owner
|
|
56
|
+
.to_s
|
|
57
|
+
.downcase
|
|
58
|
+
.split('::')
|
|
59
|
+
.<<(proto_method.name)
|
|
60
|
+
.join('.')
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
end
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
module Datadog
|
|
2
|
+
module Contrib
|
|
3
|
+
module GRPC
|
|
4
|
+
# :nodoc:
|
|
5
|
+
module DatadogInterceptor
|
|
6
|
+
# :nodoc:
|
|
7
|
+
class Base < ::GRPC::Interceptor
|
|
8
|
+
attr_accessor :datadog_pin
|
|
9
|
+
|
|
10
|
+
def initialize(options = {})
|
|
11
|
+
datadog_pin_configuration { |c| yield(c) if block_given? }
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def request_response(**keywords)
|
|
15
|
+
trace(keywords) { yield }
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def client_streamer(**keywords)
|
|
19
|
+
trace(keywords) { yield }
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def server_streamer(**keywords)
|
|
23
|
+
trace(keywords) { yield }
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def bidi_streamer(**keywords)
|
|
27
|
+
trace(keywords) { yield }
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
private
|
|
31
|
+
|
|
32
|
+
def datadog_pin_configuration
|
|
33
|
+
pin = default_datadog_pin
|
|
34
|
+
|
|
35
|
+
if block_given?
|
|
36
|
+
pin = Pin.new(
|
|
37
|
+
pin.service_name,
|
|
38
|
+
app: pin.app,
|
|
39
|
+
app_type: pin.app_type,
|
|
40
|
+
tracer: pin.tracer
|
|
41
|
+
)
|
|
42
|
+
|
|
43
|
+
yield(pin)
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
pin.onto(self)
|
|
47
|
+
|
|
48
|
+
pin
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def default_datadog_pin
|
|
52
|
+
Pin.get_from(::GRPC)
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def tracer
|
|
56
|
+
datadog_pin.tracer
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
require_relative 'datadog_interceptor/client'
|
|
61
|
+
require_relative 'datadog_interceptor/server'
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
end
|
|
65
|
+
end
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
require_relative 'datadog_interceptor'
|
|
2
|
+
|
|
3
|
+
module Datadog
|
|
4
|
+
module Contrib
|
|
5
|
+
module GRPC
|
|
6
|
+
# :nodoc:
|
|
7
|
+
# The `#intercept!` method is implemented in gRPC; this module
|
|
8
|
+
# will be prepended to the original class, effectively injecting
|
|
9
|
+
# our tracing middleware into the head of the call chain.
|
|
10
|
+
module InterceptWithDatadog
|
|
11
|
+
def intercept!(type, args = {})
|
|
12
|
+
if should_prepend?
|
|
13
|
+
datadog_interceptor = choose_datadog_interceptor(args)
|
|
14
|
+
|
|
15
|
+
@interceptors.unshift(datadog_interceptor.new) if datadog_interceptor
|
|
16
|
+
|
|
17
|
+
@trace_started = true
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
super
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
private
|
|
24
|
+
|
|
25
|
+
def should_prepend?
|
|
26
|
+
!trace_started? && !already_prepended?
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def trace_started?
|
|
30
|
+
defined?(@trace_started) && @trace_started
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def already_prepended?
|
|
34
|
+
@interceptors.any? do |interceptor|
|
|
35
|
+
interceptor.class.ancestors.include?(Datadog::Contrib::GRPC::DatadogInterceptor::Base)
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def choose_datadog_interceptor(args)
|
|
40
|
+
if args.key?(:metadata)
|
|
41
|
+
Datadog::Contrib::GRPC::DatadogInterceptor::Client
|
|
42
|
+
elsif args.key?(:call)
|
|
43
|
+
Datadog::Contrib::GRPC::DatadogInterceptor::Server
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
end
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
# requirements should be kept minimal as Patcher is a shared requirement.
|
|
2
|
+
|
|
3
|
+
module Datadog
|
|
4
|
+
module Contrib
|
|
5
|
+
module GRPC
|
|
6
|
+
SERVICE = 'grpc'.freeze
|
|
7
|
+
|
|
8
|
+
# Patcher enables patching of 'grpc' module.
|
|
9
|
+
module Patcher
|
|
10
|
+
include Base
|
|
11
|
+
register_as :grpc, auto_patch: true
|
|
12
|
+
option :tracer, default: Datadog.tracer
|
|
13
|
+
option :service_name, default: SERVICE
|
|
14
|
+
|
|
15
|
+
@patched = false
|
|
16
|
+
|
|
17
|
+
module_function
|
|
18
|
+
|
|
19
|
+
def patch
|
|
20
|
+
return false unless compatible?
|
|
21
|
+
return @patched if @patched
|
|
22
|
+
|
|
23
|
+
require 'ddtrace/ext/grpc'
|
|
24
|
+
require 'ddtrace/propagation/grpc_propagator'
|
|
25
|
+
require 'ddtrace/contrib/grpc/datadog_interceptor'
|
|
26
|
+
require 'ddtrace/contrib/grpc/intercept_with_datadog'
|
|
27
|
+
|
|
28
|
+
add_pin
|
|
29
|
+
prepend_interceptor
|
|
30
|
+
|
|
31
|
+
@patched = true
|
|
32
|
+
rescue StandardError => e
|
|
33
|
+
Datadog::Tracer.log.error("Unable to apply gRPC integration: #{e}")
|
|
34
|
+
ensure
|
|
35
|
+
@patched
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def compatible?
|
|
39
|
+
defined?(::GRPC::VERSION) && Gem::Version.new(::GRPC::VERSION) >= Gem::Version.new('0.10.0')
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def patched?
|
|
43
|
+
@patched
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def add_pin
|
|
47
|
+
Pin.new(
|
|
48
|
+
get_option(:service_name),
|
|
49
|
+
app: 'grpc',
|
|
50
|
+
app_type: 'grpc',
|
|
51
|
+
tracer: get_option(:tracer)
|
|
52
|
+
).onto(::GRPC)
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def prepend_interceptor
|
|
56
|
+
::GRPC::InterceptionContext
|
|
57
|
+
.prepend(Datadog::Contrib::GRPC::InterceptWithDatadog)
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
end
|
|
@@ -48,6 +48,8 @@ module Datadog
|
|
|
48
48
|
include Base
|
|
49
49
|
register_as :http, auto_patch: true
|
|
50
50
|
option :distributed_tracing, default: false
|
|
51
|
+
option :service_name, default: SERVICE
|
|
52
|
+
option :tracer, default: Datadog.tracer
|
|
51
53
|
|
|
52
54
|
@patched = false
|
|
53
55
|
|
|
@@ -64,7 +66,7 @@ module Datadog
|
|
|
64
66
|
require 'ddtrace/ext/net'
|
|
65
67
|
require 'ddtrace/ext/distributed'
|
|
66
68
|
|
|
67
|
-
patch_http
|
|
69
|
+
patch_http
|
|
68
70
|
|
|
69
71
|
@patched = true
|
|
70
72
|
rescue StandardError => e
|
|
@@ -74,7 +76,7 @@ module Datadog
|
|
|
74
76
|
@patched
|
|
75
77
|
end
|
|
76
78
|
|
|
77
|
-
# patched? tells
|
|
79
|
+
# patched? tells whether patch has been successfully applied
|
|
78
80
|
def patched?
|
|
79
81
|
@patched
|
|
80
82
|
end
|
|
@@ -84,27 +86,27 @@ module Datadog
|
|
|
84
86
|
# rubocop:disable Metrics/AbcSize
|
|
85
87
|
def patch_http
|
|
86
88
|
::Net::HTTP.class_eval do
|
|
87
|
-
alias_method :initialize_without_datadog, :initialize
|
|
88
|
-
Datadog::Patcher.without_warnings do
|
|
89
|
-
remove_method :initialize
|
|
90
|
-
end
|
|
91
|
-
|
|
92
|
-
def initialize(*args)
|
|
93
|
-
pin = Datadog::Pin.new(SERVICE, app: APP, app_type: Datadog::Ext::AppTypes::WEB)
|
|
94
|
-
pin.onto(self)
|
|
95
|
-
initialize_without_datadog(*args)
|
|
96
|
-
end
|
|
97
|
-
|
|
98
89
|
alias_method :request_without_datadog, :request
|
|
99
90
|
remove_method :request
|
|
100
91
|
|
|
92
|
+
def datadog_pin
|
|
93
|
+
@datadog_pindatadog_pin ||= begin
|
|
94
|
+
service = Datadog.configuration[:http][:service_name]
|
|
95
|
+
tracer = Datadog.configuration[:http][:tracer]
|
|
96
|
+
|
|
97
|
+
Datadog::Pin.new(service, app: APP, app_type: Datadog::Ext::AppTypes::WEB, tracer: tracer)
|
|
98
|
+
end
|
|
99
|
+
end
|
|
100
|
+
|
|
101
101
|
def request(req, body = nil, &block) # :yield: +response+
|
|
102
|
-
pin =
|
|
102
|
+
pin = datadog_pin
|
|
103
103
|
return request_without_datadog(req, body, &block) unless pin && pin.tracer
|
|
104
104
|
|
|
105
105
|
transport = pin.tracer.writer.transport
|
|
106
|
-
|
|
107
|
-
|
|
106
|
+
|
|
107
|
+
if Datadog::Contrib::HTTP.should_skip_tracing?(req, @address, @port, transport, pin)
|
|
108
|
+
return request_without_datadog(req, body, &block)
|
|
109
|
+
end
|
|
108
110
|
|
|
109
111
|
pin.tracer.trace(NAME) do |span|
|
|
110
112
|
begin
|