fair-ddtrace 0.8.2.a
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 +7 -0
- data/.env +11 -0
- data/.gitignore +59 -0
- data/.rubocop.yml +61 -0
- data/.yardopts +5 -0
- data/Appraisals +136 -0
- data/Gemfile +3 -0
- data/LICENSE +24 -0
- data/README.md +156 -0
- data/Rakefile +176 -0
- data/circle.yml +61 -0
- data/ddtrace.gemspec +44 -0
- data/docker-compose.yml +42 -0
- data/docs/GettingStarted.md +735 -0
- data/gemfiles/contrib.gemfile +16 -0
- data/gemfiles/contrib_old.gemfile +15 -0
- data/gemfiles/rails30_postgres.gemfile +10 -0
- data/gemfiles/rails30_postgres_sidekiq.gemfile +11 -0
- data/gemfiles/rails32_mysql2.gemfile +11 -0
- data/gemfiles/rails32_postgres.gemfile +10 -0
- data/gemfiles/rails32_postgres_redis.gemfile +11 -0
- data/gemfiles/rails32_postgres_sidekiq.gemfile +11 -0
- data/gemfiles/rails4_mysql2.gemfile +9 -0
- data/gemfiles/rails4_postgres.gemfile +9 -0
- data/gemfiles/rails4_postgres_redis.gemfile +10 -0
- data/gemfiles/rails4_postgres_sidekiq.gemfile +11 -0
- data/gemfiles/rails5_mysql2.gemfile +8 -0
- data/gemfiles/rails5_postgres.gemfile +8 -0
- data/gemfiles/rails5_postgres_redis.gemfile +9 -0
- data/gemfiles/rails5_postgres_sidekiq.gemfile +10 -0
- data/lib/ddtrace.rb +73 -0
- data/lib/ddtrace/buffer.rb +52 -0
- data/lib/ddtrace/context.rb +145 -0
- data/lib/ddtrace/contrib/active_record/patcher.rb +94 -0
- data/lib/ddtrace/contrib/elasticsearch/patcher.rb +108 -0
- data/lib/ddtrace/contrib/elasticsearch/quantize.rb +22 -0
- data/lib/ddtrace/contrib/grape/endpoint.rb +164 -0
- data/lib/ddtrace/contrib/grape/patcher.rb +73 -0
- data/lib/ddtrace/contrib/http/patcher.rb +156 -0
- data/lib/ddtrace/contrib/rack/middlewares.rb +150 -0
- data/lib/ddtrace/contrib/rails/action_controller.rb +81 -0
- data/lib/ddtrace/contrib/rails/action_view.rb +110 -0
- data/lib/ddtrace/contrib/rails/active_record.rb +56 -0
- data/lib/ddtrace/contrib/rails/active_support.rb +113 -0
- data/lib/ddtrace/contrib/rails/core_extensions.rb +137 -0
- data/lib/ddtrace/contrib/rails/framework.rb +171 -0
- data/lib/ddtrace/contrib/rails/middlewares.rb +32 -0
- data/lib/ddtrace/contrib/rails/utils.rb +43 -0
- data/lib/ddtrace/contrib/redis/patcher.rb +118 -0
- data/lib/ddtrace/contrib/redis/quantize.rb +30 -0
- data/lib/ddtrace/contrib/redis/tags.rb +19 -0
- data/lib/ddtrace/contrib/sidekiq/tracer.rb +103 -0
- data/lib/ddtrace/contrib/sinatra/tracer.rb +169 -0
- data/lib/ddtrace/distributed.rb +38 -0
- data/lib/ddtrace/encoding.rb +65 -0
- data/lib/ddtrace/error.rb +37 -0
- data/lib/ddtrace/ext/app_types.rb +10 -0
- data/lib/ddtrace/ext/cache.rb +7 -0
- data/lib/ddtrace/ext/distributed.rb +10 -0
- data/lib/ddtrace/ext/errors.rb +10 -0
- data/lib/ddtrace/ext/http.rb +11 -0
- data/lib/ddtrace/ext/net.rb +8 -0
- data/lib/ddtrace/ext/redis.rb +11 -0
- data/lib/ddtrace/ext/sql.rb +8 -0
- data/lib/ddtrace/logger.rb +39 -0
- data/lib/ddtrace/monkey.rb +84 -0
- data/lib/ddtrace/pin.rb +63 -0
- data/lib/ddtrace/provider.rb +21 -0
- data/lib/ddtrace/sampler.rb +49 -0
- data/lib/ddtrace/span.rb +222 -0
- data/lib/ddtrace/tracer.rb +310 -0
- data/lib/ddtrace/transport.rb +162 -0
- data/lib/ddtrace/utils.rb +16 -0
- data/lib/ddtrace/version.rb +9 -0
- data/lib/ddtrace/workers.rb +108 -0
- data/lib/ddtrace/writer.rb +118 -0
- metadata +208 -0
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
# requirements should be kept minimal as Patcher is a shared requirement.
|
|
2
|
+
|
|
3
|
+
module Datadog
|
|
4
|
+
module Contrib
|
|
5
|
+
# Datadog Net/HTTP integration.
|
|
6
|
+
module HTTP
|
|
7
|
+
URL = 'http.url'.freeze
|
|
8
|
+
METHOD = 'http.method'.freeze
|
|
9
|
+
BODY = 'http.body'.freeze
|
|
10
|
+
|
|
11
|
+
NAME = 'http.request'.freeze
|
|
12
|
+
APP = 'net/http'.freeze
|
|
13
|
+
SERVICE = 'net/http'.freeze
|
|
14
|
+
|
|
15
|
+
@distributed_tracing_enabled = false
|
|
16
|
+
|
|
17
|
+
class << self
|
|
18
|
+
attr_accessor :distributed_tracing_enabled
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
module_function
|
|
22
|
+
|
|
23
|
+
def should_skip_tracing?(req, address, port, transport, pin)
|
|
24
|
+
# we don't want to trace our own call to the API (they use net/http)
|
|
25
|
+
# when we know the host & port (from the URI) we use it, else (most-likely
|
|
26
|
+
# called with a block) rely on the URL at the end.
|
|
27
|
+
if req.respond_to?(:uri) && req.uri
|
|
28
|
+
if req.uri.host.to_s == transport.hostname.to_s &&
|
|
29
|
+
req.uri.port.to_i == transport.port.to_i
|
|
30
|
+
return true
|
|
31
|
+
end
|
|
32
|
+
elsif address && port &&
|
|
33
|
+
address.to_s == transport.hostname.to_s &&
|
|
34
|
+
port.to_i == transport.port.to_i
|
|
35
|
+
return true
|
|
36
|
+
end
|
|
37
|
+
# we don't want a "shotgun" effect with two nested traces for one
|
|
38
|
+
# logical get, and request is likely to call itself recursively
|
|
39
|
+
active = pin.tracer.active_span()
|
|
40
|
+
return true if active && (active.name == NAME)
|
|
41
|
+
false
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def should_skip_distributed_tracing?(pin)
|
|
45
|
+
unless pin.config.nil?
|
|
46
|
+
return !pin.config.fetch(:distributed_tracing_enabled, @distributed_tracing_enabled)
|
|
47
|
+
end
|
|
48
|
+
!@distributed_tracing_enabled
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
# Patcher enables patching of 'net/http' module.
|
|
52
|
+
# This is used in monkey.rb to automatically apply patches
|
|
53
|
+
module Patcher
|
|
54
|
+
@patched = false
|
|
55
|
+
|
|
56
|
+
module_function
|
|
57
|
+
|
|
58
|
+
# patch applies our patch if needed
|
|
59
|
+
def patch
|
|
60
|
+
unless @patched
|
|
61
|
+
begin
|
|
62
|
+
require 'uri'
|
|
63
|
+
require 'ddtrace/pin'
|
|
64
|
+
require 'ddtrace/monkey'
|
|
65
|
+
require 'ddtrace/ext/app_types'
|
|
66
|
+
require 'ddtrace/ext/http'
|
|
67
|
+
require 'ddtrace/ext/net'
|
|
68
|
+
require 'ddtrace/ext/distributed'
|
|
69
|
+
|
|
70
|
+
patch_http()
|
|
71
|
+
|
|
72
|
+
@patched = true
|
|
73
|
+
rescue StandardError => e
|
|
74
|
+
Datadog::Tracer.log.error("Unable to apply net/http integration: #{e}")
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
@patched
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
# patched? tells wether patch has been successfully applied
|
|
81
|
+
def patched?
|
|
82
|
+
@patched
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
# rubocop:disable Metrics/MethodLength
|
|
86
|
+
# rubocop:disable Metrics/BlockLength
|
|
87
|
+
# rubocop:disable Metrics/AbcSize
|
|
88
|
+
def patch_http
|
|
89
|
+
::Net::HTTP.class_eval do
|
|
90
|
+
alias_method :initialize_without_datadog, :initialize
|
|
91
|
+
Datadog::Monkey.without_warnings do
|
|
92
|
+
remove_method :initialize
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
def initialize(*args)
|
|
96
|
+
pin = Datadog::Pin.new(SERVICE, app: APP, app_type: Datadog::Ext::AppTypes::WEB)
|
|
97
|
+
pin.onto(self)
|
|
98
|
+
initialize_without_datadog(*args)
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
alias_method :request_without_datadog, :request
|
|
102
|
+
remove_method :request
|
|
103
|
+
|
|
104
|
+
def request(req, body = nil, &block) # :yield: +response+
|
|
105
|
+
pin = Datadog::Pin.get_from(self)
|
|
106
|
+
return request_without_datadog(req, body, &block) unless pin && pin.tracer
|
|
107
|
+
|
|
108
|
+
transport = pin.tracer.writer.transport
|
|
109
|
+
return request_without_datadog(req, body, &block) if
|
|
110
|
+
Datadog::Contrib::HTTP.should_skip_tracing?(req, @address, @port, transport, pin)
|
|
111
|
+
|
|
112
|
+
pin.tracer.trace(NAME) do |span|
|
|
113
|
+
begin
|
|
114
|
+
span.service = pin.service
|
|
115
|
+
span.span_type = Datadog::Ext::HTTP::TYPE
|
|
116
|
+
|
|
117
|
+
span.resource = req.method
|
|
118
|
+
# Using the method as a resource, as URL/path can trigger
|
|
119
|
+
# a possibly infinite number of resources.
|
|
120
|
+
span.set_tag(Datadog::Ext::HTTP::URL, req.path)
|
|
121
|
+
span.set_tag(Datadog::Ext::HTTP::METHOD, req.method)
|
|
122
|
+
|
|
123
|
+
unless Datadog::Contrib::HTTP.should_skip_distributed_tracing?(pin)
|
|
124
|
+
req.add_field(Datadog::Ext::DistributedTracing::HTTP_HEADER_TRACE_ID, span.trace_id)
|
|
125
|
+
req.add_field(Datadog::Ext::DistributedTracing::HTTP_HEADER_PARENT_ID, span.span_id)
|
|
126
|
+
end
|
|
127
|
+
rescue StandardError => e
|
|
128
|
+
Datadog::Tracer.log.error("error preparing span for http request: #{e}")
|
|
129
|
+
ensure
|
|
130
|
+
response = request_without_datadog(req, body, &block)
|
|
131
|
+
end
|
|
132
|
+
span.set_tag(Datadog::Ext::HTTP::STATUS_CODE, response.code)
|
|
133
|
+
if req.respond_to?(:uri) && req.uri
|
|
134
|
+
span.set_tag(Datadog::Ext::NET::TARGET_HOST, req.uri.host)
|
|
135
|
+
span.set_tag(Datadog::Ext::NET::TARGET_PORT, req.uri.port.to_s)
|
|
136
|
+
else
|
|
137
|
+
span.set_tag(Datadog::Ext::NET::TARGET_HOST, @address)
|
|
138
|
+
span.set_tag(Datadog::Ext::NET::TARGET_PORT, @port.to_s)
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
case response.code.to_i / 100
|
|
142
|
+
when 4
|
|
143
|
+
span.set_error(response)
|
|
144
|
+
when 5
|
|
145
|
+
span.set_error(response)
|
|
146
|
+
end
|
|
147
|
+
|
|
148
|
+
response
|
|
149
|
+
end
|
|
150
|
+
end
|
|
151
|
+
end
|
|
152
|
+
end
|
|
153
|
+
end
|
|
154
|
+
end
|
|
155
|
+
end
|
|
156
|
+
end
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
require 'ddtrace/ext/app_types'
|
|
2
|
+
require 'ddtrace/ext/http'
|
|
3
|
+
require 'ddtrace/distributed'
|
|
4
|
+
|
|
5
|
+
module Datadog
|
|
6
|
+
module Contrib
|
|
7
|
+
# Rack module includes middlewares that are required to trace any framework
|
|
8
|
+
# and application built on top of Rack.
|
|
9
|
+
module Rack
|
|
10
|
+
# RACK headers to test when doing distributed tracing.
|
|
11
|
+
# They are slightly different from real headers as Rack uppercases everything
|
|
12
|
+
|
|
13
|
+
# Header used to transmit the trace ID.
|
|
14
|
+
HTTP_HEADER_TRACE_ID = 'HTTP_X_DATADOG_TRACE_ID'.freeze
|
|
15
|
+
|
|
16
|
+
# Header used to transmit the parent ID.
|
|
17
|
+
HTTP_HEADER_PARENT_ID = 'HTTP_X_DATADOG_PARENT_ID'.freeze
|
|
18
|
+
|
|
19
|
+
# TraceMiddleware ensures that the Rack Request is properly traced
|
|
20
|
+
# from the beginning to the end. The middleware adds the request span
|
|
21
|
+
# in the Rack environment so that it can be retrieved by the underlying
|
|
22
|
+
# application. If request tags are not set by the app, they will be set using
|
|
23
|
+
# information available at the Rack level.
|
|
24
|
+
class TraceMiddleware
|
|
25
|
+
DEFAULT_CONFIG = {
|
|
26
|
+
tracer: Datadog.tracer,
|
|
27
|
+
default_service: 'rack',
|
|
28
|
+
distributed_tracing_enabled: false
|
|
29
|
+
}.freeze
|
|
30
|
+
|
|
31
|
+
def initialize(app, options = {})
|
|
32
|
+
# update options with our configuration, unless it's already available
|
|
33
|
+
[:tracer, :default_service, :distributed_tracing_enabled].each do |k|
|
|
34
|
+
options[k] ||= DEFAULT_CONFIG[k]
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
@app = app
|
|
38
|
+
@options = options
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def configure
|
|
42
|
+
# ensure that the configuration is executed only once
|
|
43
|
+
return clean_context if @tracer && @service
|
|
44
|
+
|
|
45
|
+
# retrieve the current tracer and service
|
|
46
|
+
@tracer = @options.fetch(:tracer)
|
|
47
|
+
@service = @options.fetch(:default_service)
|
|
48
|
+
@distributed_tracing_enabled = @options.fetch(:distributed_tracing_enabled)
|
|
49
|
+
|
|
50
|
+
# configure the Rack service
|
|
51
|
+
@tracer.set_service_info(
|
|
52
|
+
@service,
|
|
53
|
+
'rack',
|
|
54
|
+
Datadog::Ext::AppTypes::WEB
|
|
55
|
+
)
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
# rubocop:disable Metrics/MethodLength
|
|
59
|
+
def call(env)
|
|
60
|
+
# configure the Rack middleware once
|
|
61
|
+
configure()
|
|
62
|
+
|
|
63
|
+
trace_options = {
|
|
64
|
+
service: @service,
|
|
65
|
+
resource: nil,
|
|
66
|
+
span_type: Datadog::Ext::HTTP::TYPE
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
# start a new request span and attach it to the current Rack environment;
|
|
70
|
+
# we must ensure that the span `resource` is set later
|
|
71
|
+
request_span = @tracer.trace('rack.request', trace_options)
|
|
72
|
+
|
|
73
|
+
if @distributed_tracing_enabled
|
|
74
|
+
# Merge distributed trace ids if present
|
|
75
|
+
#
|
|
76
|
+
# Use integer values for tests, as it will catch both
|
|
77
|
+
# a non-existing header or a badly formed one.
|
|
78
|
+
trace_id, parent_id = Datadog::Distributed.parse_trace_headers(
|
|
79
|
+
env[Datadog::Contrib::Rack::HTTP_HEADER_TRACE_ID],
|
|
80
|
+
env[Datadog::Contrib::Rack::HTTP_HEADER_PARENT_ID]
|
|
81
|
+
)
|
|
82
|
+
request_span.trace_id = trace_id unless trace_id.nil?
|
|
83
|
+
request_span.parent_id = parent_id unless parent_id.nil?
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
env[:datadog_rack_request_span] = request_span
|
|
87
|
+
|
|
88
|
+
# call the rest of the stack
|
|
89
|
+
status, headers, response = @app.call(env)
|
|
90
|
+
|
|
91
|
+
# rubocop:disable Lint/RescueException
|
|
92
|
+
# Here we really want to catch *any* exception, not only StandardError,
|
|
93
|
+
# as we really have no clue of what is in the block,
|
|
94
|
+
# and it is user code which should be executed no matter what.
|
|
95
|
+
# It's not a problem since we re-raise it afterwards so for example a
|
|
96
|
+
# SignalException::Interrupt would still bubble up.
|
|
97
|
+
rescue Exception => e
|
|
98
|
+
# catch exceptions that may be raised in the middleware chain
|
|
99
|
+
# Note: if a middleware catches an Exception without re raising,
|
|
100
|
+
# the Exception cannot be recorded here.
|
|
101
|
+
request_span.set_error(e)
|
|
102
|
+
raise e
|
|
103
|
+
ensure
|
|
104
|
+
# the source of truth in Rack is the PATH_INFO key that holds the
|
|
105
|
+
# URL for the current request; some framework may override that
|
|
106
|
+
# value, especially during exception handling and because of that
|
|
107
|
+
# we prefer using the `REQUEST_URI` if this is available.
|
|
108
|
+
# NOTE: `REQUEST_URI` is Rails specific and may not apply for other frameworks
|
|
109
|
+
url = env['REQUEST_URI'] || env['PATH_INFO']
|
|
110
|
+
|
|
111
|
+
# Rack is a really low level interface and it doesn't provide any
|
|
112
|
+
# advanced functionality like routers. Because of that, we assume that
|
|
113
|
+
# the underlying framework or application has more knowledge about
|
|
114
|
+
# the result for this request; `resource` and `tags` are expected to
|
|
115
|
+
# be set in another level but if they're missing, reasonable defaults
|
|
116
|
+
# are used.
|
|
117
|
+
request_span.resource = "#{env['REQUEST_METHOD']} #{status}".strip unless request_span.resource
|
|
118
|
+
if request_span.get_tag(Datadog::Ext::HTTP::METHOD).nil?
|
|
119
|
+
request_span.set_tag(Datadog::Ext::HTTP::METHOD, env['REQUEST_METHOD'])
|
|
120
|
+
end
|
|
121
|
+
if request_span.get_tag(Datadog::Ext::HTTP::URL).nil?
|
|
122
|
+
request_span.set_tag(Datadog::Ext::HTTP::URL, url)
|
|
123
|
+
end
|
|
124
|
+
if request_span.get_tag(Datadog::Ext::HTTP::STATUS_CODE).nil? && status
|
|
125
|
+
request_span.set_tag(Datadog::Ext::HTTP::STATUS_CODE, status)
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
# detect if the status code is a 5xx and flag the request span as an error
|
|
129
|
+
# unless it has been already set by the underlying framework
|
|
130
|
+
if status.to_s.start_with?('5') && request_span.status.zero?
|
|
131
|
+
request_span.status = 1
|
|
132
|
+
end
|
|
133
|
+
|
|
134
|
+
request_span.finish()
|
|
135
|
+
|
|
136
|
+
[status, headers, response]
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
private
|
|
140
|
+
|
|
141
|
+
# TODO: Remove this once we change how context propagation works. This
|
|
142
|
+
# ensures we clean thread-local variables on each HTTP request avoiding
|
|
143
|
+
# memory leaks.
|
|
144
|
+
def clean_context
|
|
145
|
+
@tracer.provider.context = Datadog::Context.new
|
|
146
|
+
end
|
|
147
|
+
end
|
|
148
|
+
end
|
|
149
|
+
end
|
|
150
|
+
end
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
require 'ddtrace/ext/http'
|
|
2
|
+
require 'ddtrace/ext/errors'
|
|
3
|
+
|
|
4
|
+
module Datadog
|
|
5
|
+
module Contrib
|
|
6
|
+
module Rails
|
|
7
|
+
# Code used to create and handle 'rails.action_controller' spans.
|
|
8
|
+
module ActionController
|
|
9
|
+
KEY = 'datadog_actioncontroller'.freeze
|
|
10
|
+
|
|
11
|
+
def self.instrument
|
|
12
|
+
# subscribe when the request processing starts
|
|
13
|
+
::ActiveSupport::Notifications.subscribe('start_processing.action_controller') do |*args|
|
|
14
|
+
start_processing(*args)
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
# subscribe when the request processing has been completed
|
|
18
|
+
::ActiveSupport::Notifications.subscribe('process_action.action_controller') do |*args|
|
|
19
|
+
process_action(*args)
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def self.start_processing(*)
|
|
24
|
+
return if Thread.current[KEY]
|
|
25
|
+
|
|
26
|
+
tracer = ::Rails.configuration.datadog_trace.fetch(:tracer)
|
|
27
|
+
service = ::Rails.configuration.datadog_trace.fetch(:default_controller_service)
|
|
28
|
+
type = Datadog::Ext::HTTP::TYPE
|
|
29
|
+
tracer.trace('rails.action_controller', service: service, span_type: type)
|
|
30
|
+
|
|
31
|
+
Thread.current[KEY] = true
|
|
32
|
+
rescue StandardError => e
|
|
33
|
+
Datadog::Tracer.log.error(e.message)
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def self.process_action(_name, start, finish, _id, payload)
|
|
37
|
+
return unless Thread.current[KEY]
|
|
38
|
+
Thread.current[KEY] = false
|
|
39
|
+
|
|
40
|
+
tracer = ::Rails.configuration.datadog_trace.fetch(:tracer)
|
|
41
|
+
span = tracer.active_span()
|
|
42
|
+
return unless span
|
|
43
|
+
|
|
44
|
+
begin
|
|
45
|
+
resource = "#{payload.fetch(:controller)}##{payload.fetch(:action)}"
|
|
46
|
+
span.resource = resource
|
|
47
|
+
|
|
48
|
+
# set the parent resource if it's a `rack.request` span
|
|
49
|
+
if !span.parent.nil? && span.parent.name == 'rack.request'
|
|
50
|
+
span.parent.resource = resource
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
span.set_tag('rails.route.action', payload.fetch(:action))
|
|
54
|
+
span.set_tag('rails.route.controller', payload.fetch(:controller))
|
|
55
|
+
|
|
56
|
+
if payload[:exception].nil?
|
|
57
|
+
# [christian] in some cases :status is not defined,
|
|
58
|
+
# rather than firing an error, simply acknowledge we don't know it.
|
|
59
|
+
status = payload.fetch(:status, '?').to_s
|
|
60
|
+
span.status = 1 if status.starts_with?('5')
|
|
61
|
+
else
|
|
62
|
+
error = payload[:exception]
|
|
63
|
+
if defined?(::ActionDispatch::ExceptionWrapper)
|
|
64
|
+
status = ::ActionDispatch::ExceptionWrapper.status_code_for_exception(error[0])
|
|
65
|
+
status = status ? status.to_s : '?'
|
|
66
|
+
else
|
|
67
|
+
status = '500'
|
|
68
|
+
end
|
|
69
|
+
span.set_error(error) if status.starts_with?('5')
|
|
70
|
+
end
|
|
71
|
+
ensure
|
|
72
|
+
span.start_time = start
|
|
73
|
+
span.finish(finish)
|
|
74
|
+
end
|
|
75
|
+
rescue StandardError => e
|
|
76
|
+
Datadog::Tracer.log.error(e.message)
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
end
|
|
80
|
+
end
|
|
81
|
+
end
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
require 'ddtrace/contrib/rails/utils'
|
|
2
|
+
|
|
3
|
+
module Datadog
|
|
4
|
+
module Contrib
|
|
5
|
+
module Rails
|
|
6
|
+
# Code used to create and handle 'rails.render_template' and 'rails.render_partial' spans.
|
|
7
|
+
module ActionView
|
|
8
|
+
def self.instrument
|
|
9
|
+
# patch Rails core components
|
|
10
|
+
Datadog::RailsRendererPatcher.patch_renderer()
|
|
11
|
+
|
|
12
|
+
# subscribe when the template rendering starts
|
|
13
|
+
::ActiveSupport::Notifications.subscribe('start_render_template.action_view') do |*args|
|
|
14
|
+
start_render_template(*args)
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
# subscribe when the partial rendering starts
|
|
18
|
+
::ActiveSupport::Notifications.subscribe('start_render_partial.action_view') do |*args|
|
|
19
|
+
start_render_partial(*args)
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
# subscribe when the template rendering has been processed
|
|
23
|
+
::ActiveSupport::Notifications.subscribe('render_template.action_view') do |*args|
|
|
24
|
+
render_template(*args)
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
# subscribe when the partial rendering has been processed
|
|
28
|
+
::ActiveSupport::Notifications.subscribe('render_partial.action_view') do |*args|
|
|
29
|
+
render_partial(*args)
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def self.get_key(f)
|
|
34
|
+
'datadog_actionview_' + f
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def self.start_render_template(*)
|
|
38
|
+
key = get_key('render_template')
|
|
39
|
+
return if Thread.current[key]
|
|
40
|
+
|
|
41
|
+
tracer = ::Rails.configuration.datadog_trace.fetch(:tracer)
|
|
42
|
+
type = Datadog::Ext::HTTP::TEMPLATE
|
|
43
|
+
tracer.trace('rails.render_template', span_type: type)
|
|
44
|
+
|
|
45
|
+
Thread.current[key] = true
|
|
46
|
+
rescue StandardError => e
|
|
47
|
+
Datadog::Tracer.log.error(e.message)
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def self.start_render_partial(*)
|
|
51
|
+
key = get_key('render_partial')
|
|
52
|
+
return if Thread.current[key]
|
|
53
|
+
|
|
54
|
+
tracer = ::Rails.configuration.datadog_trace.fetch(:tracer)
|
|
55
|
+
type = Datadog::Ext::HTTP::TEMPLATE
|
|
56
|
+
tracer.trace('rails.render_partial', span_type: type)
|
|
57
|
+
|
|
58
|
+
Thread.current[key] = true
|
|
59
|
+
rescue StandardError => e
|
|
60
|
+
Datadog::Tracer.log.error(e.message)
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
def self.render_template(_name, start, finish, _id, payload)
|
|
64
|
+
key = get_key('render_template')
|
|
65
|
+
return unless Thread.current[key]
|
|
66
|
+
Thread.current[key] = false
|
|
67
|
+
|
|
68
|
+
# finish the tracing and update the execution time
|
|
69
|
+
tracer = ::Rails.configuration.datadog_trace.fetch(:tracer)
|
|
70
|
+
span = tracer.active_span()
|
|
71
|
+
return unless span
|
|
72
|
+
|
|
73
|
+
begin
|
|
74
|
+
template_name = Datadog::Contrib::Rails::Utils.normalize_template_name(payload.fetch(:identifier))
|
|
75
|
+
span.set_tag('rails.template_name', template_name)
|
|
76
|
+
span.set_tag('rails.layout', payload.fetch(:layout))
|
|
77
|
+
span.set_error(payload[:exception]) if payload[:exception]
|
|
78
|
+
ensure
|
|
79
|
+
span.start_time = start
|
|
80
|
+
span.finish(finish)
|
|
81
|
+
end
|
|
82
|
+
rescue StandardError => e
|
|
83
|
+
Datadog::Tracer.log.error(e.message)
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
def self.render_partial(_name, start, finish, _id, payload)
|
|
87
|
+
key = get_key('render_partial')
|
|
88
|
+
return unless Thread.current[key]
|
|
89
|
+
Thread.current[key] = false
|
|
90
|
+
|
|
91
|
+
# finish the tracing and update the execution time
|
|
92
|
+
tracer = ::Rails.configuration.datadog_trace.fetch(:tracer)
|
|
93
|
+
span = tracer.active_span()
|
|
94
|
+
return unless span
|
|
95
|
+
|
|
96
|
+
begin
|
|
97
|
+
template_name = Datadog::Contrib::Rails::Utils.normalize_template_name(payload.fetch(:identifier))
|
|
98
|
+
span.set_tag('rails.template_name', template_name)
|
|
99
|
+
span.set_error(payload[:exception]) if payload[:exception]
|
|
100
|
+
ensure
|
|
101
|
+
span.start_time = start
|
|
102
|
+
span.finish(finish)
|
|
103
|
+
end
|
|
104
|
+
rescue StandardError => e
|
|
105
|
+
Datadog::Tracer.log.error(e.message)
|
|
106
|
+
end
|
|
107
|
+
end
|
|
108
|
+
end
|
|
109
|
+
end
|
|
110
|
+
end
|