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,32 @@
|
|
|
1
|
+
require 'ddtrace/ext/http'
|
|
2
|
+
|
|
3
|
+
module Datadog
|
|
4
|
+
module Contrib
|
|
5
|
+
# Rails module includes middlewares that are required for Rails to be properly instrumented.
|
|
6
|
+
module Rails
|
|
7
|
+
# This is only here to catch errors, the Rack module does something very similar, however,
|
|
8
|
+
# since it's not in the same place in the stack, when the Rack middleware is called,
|
|
9
|
+
# error is already swallowed and handled by Rails so we miss the call stack, for instance.
|
|
10
|
+
class ExceptionMiddleware
|
|
11
|
+
def initialize(app)
|
|
12
|
+
@app = app
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def call(env)
|
|
16
|
+
@app.call(env)
|
|
17
|
+
# rubocop:disable Lint/RescueException
|
|
18
|
+
# Here we really want to catch *any* exception, not only StandardError,
|
|
19
|
+
# as we really have no clue of what is in the block,
|
|
20
|
+
# and it is user code which should be executed no matter what.
|
|
21
|
+
# It's not a problem since we re-raise it afterwards so for example a
|
|
22
|
+
# SignalException::Interrupt would still bubble up.
|
|
23
|
+
rescue Exception => e
|
|
24
|
+
tracer = ::Rails.configuration.datadog_trace.fetch(:tracer)
|
|
25
|
+
span = tracer.active_span()
|
|
26
|
+
span.set_error(e) unless span.nil?
|
|
27
|
+
raise e
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
module Datadog
|
|
2
|
+
module Contrib
|
|
3
|
+
module Rails
|
|
4
|
+
# common utilities for Rails
|
|
5
|
+
module Utils
|
|
6
|
+
# in Rails the template name includes the template full path
|
|
7
|
+
# and it's better to avoid storing such information. This method
|
|
8
|
+
# returns the relative path from `views/` or the template name
|
|
9
|
+
# if a `views/` folder is not in the template full path. A wrong
|
|
10
|
+
# usage ensures that this method will not crash the tracing system.
|
|
11
|
+
def self.normalize_template_name(name)
|
|
12
|
+
return if name.nil?
|
|
13
|
+
|
|
14
|
+
base_path = ::Rails.configuration.datadog_trace.fetch(:template_base_path, 'views/')
|
|
15
|
+
sections_view = name.split(base_path)
|
|
16
|
+
|
|
17
|
+
if sections_view.length == 1
|
|
18
|
+
name.split('/')[-1]
|
|
19
|
+
else
|
|
20
|
+
sections_view[-1]
|
|
21
|
+
end
|
|
22
|
+
rescue
|
|
23
|
+
return name.to_s
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
# TODO: Consider moving this out of Rails.
|
|
27
|
+
# Return a canonical name for a type of database
|
|
28
|
+
def self.normalize_vendor(vendor)
|
|
29
|
+
case vendor
|
|
30
|
+
when nil
|
|
31
|
+
'defaultdb'
|
|
32
|
+
when 'sqlite3'
|
|
33
|
+
'sqlite'
|
|
34
|
+
when 'postgresql'
|
|
35
|
+
'postgres'
|
|
36
|
+
else
|
|
37
|
+
vendor
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
end
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
# requirements should be kept minimal as Patcher is a shared requirement.
|
|
2
|
+
|
|
3
|
+
module Datadog
|
|
4
|
+
module Contrib
|
|
5
|
+
module Redis
|
|
6
|
+
SERVICE = 'redis'.freeze
|
|
7
|
+
DRIVER = 'redis.driver'.freeze
|
|
8
|
+
|
|
9
|
+
# Patcher enables patching of 'redis' module.
|
|
10
|
+
# This is used in monkey.rb to automatically apply patches
|
|
11
|
+
module Patcher
|
|
12
|
+
@patched = false
|
|
13
|
+
|
|
14
|
+
module_function
|
|
15
|
+
|
|
16
|
+
# patch applies our patch if needed
|
|
17
|
+
def patch
|
|
18
|
+
if !@patched && (defined?(::Redis::VERSION) && \
|
|
19
|
+
Gem::Version.new(::Redis::VERSION) >= Gem::Version.new('3.0.0'))
|
|
20
|
+
begin
|
|
21
|
+
# do not require these by default, but only when actually patching
|
|
22
|
+
require 'ddtrace/monkey'
|
|
23
|
+
require 'ddtrace/ext/app_types'
|
|
24
|
+
require 'ddtrace/contrib/redis/tags'
|
|
25
|
+
require 'ddtrace/contrib/redis/quantize'
|
|
26
|
+
|
|
27
|
+
patch_redis()
|
|
28
|
+
patch_redis_client()
|
|
29
|
+
|
|
30
|
+
@patched = true
|
|
31
|
+
rescue StandardError => e
|
|
32
|
+
Datadog::Tracer.log.error("Unable to apply Redis integration: #{e}")
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
@patched
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def patch_redis
|
|
39
|
+
::Redis.module_eval do
|
|
40
|
+
def datadog_pin=(pin)
|
|
41
|
+
# Forward the pin to client, which actually traces calls.
|
|
42
|
+
Datadog::Pin.onto(client, pin)
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def datadog_pin
|
|
46
|
+
# Get the pin from client, which actually traces calls.
|
|
47
|
+
Datadog::Pin.get_from(client)
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
# rubocop:disable Metrics/MethodLength
|
|
53
|
+
# rubocop:disable Metrics/BlockLength
|
|
54
|
+
def patch_redis_client
|
|
55
|
+
::Redis::Client.class_eval do
|
|
56
|
+
alias_method :initialize_without_datadog, :initialize
|
|
57
|
+
Datadog::Monkey.without_warnings do
|
|
58
|
+
remove_method :initialize
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def initialize(*args)
|
|
62
|
+
pin = Datadog::Pin.new(SERVICE, app: 'redis', app_type: Datadog::Ext::AppTypes::DB)
|
|
63
|
+
pin.onto(self)
|
|
64
|
+
if pin.tracer && pin.service
|
|
65
|
+
pin.tracer.set_service_info(pin.service, pin.app, pin.app_type)
|
|
66
|
+
end
|
|
67
|
+
initialize_without_datadog(*args)
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
alias_method :call_without_datadog, :call
|
|
71
|
+
remove_method :call
|
|
72
|
+
def call(*args, &block)
|
|
73
|
+
pin = Datadog::Pin.get_from(self)
|
|
74
|
+
return call_without_datadog(*args, &block) unless pin && pin.tracer
|
|
75
|
+
|
|
76
|
+
response = nil
|
|
77
|
+
pin.tracer.trace('redis.command') do |span|
|
|
78
|
+
span.service = pin.service
|
|
79
|
+
span.span_type = Datadog::Ext::Redis::TYPE
|
|
80
|
+
span.resource = Datadog::Contrib::Redis::Quantize.format_command_args(*args)
|
|
81
|
+
Datadog::Contrib::Redis::Tags.set_common_tags(self, span)
|
|
82
|
+
|
|
83
|
+
response = call_without_datadog(*args, &block)
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
response
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
alias_method :call_pipeline_without_datadog, :call_pipeline
|
|
90
|
+
remove_method :call_pipeline
|
|
91
|
+
def call_pipeline(*args, &block)
|
|
92
|
+
pin = Datadog::Pin.get_from(self)
|
|
93
|
+
return call_pipeline_without_datadog(*args, &block) unless pin && pin.tracer
|
|
94
|
+
|
|
95
|
+
response = nil
|
|
96
|
+
pin.tracer.trace('redis.command') do |span|
|
|
97
|
+
span.service = pin.service
|
|
98
|
+
span.span_type = Datadog::Ext::Redis::TYPE
|
|
99
|
+
commands = args[0].commands.map { |c| Datadog::Contrib::Redis::Quantize.format_command_args(c) }
|
|
100
|
+
span.resource = commands.join("\n")
|
|
101
|
+
Datadog::Contrib::Redis::Tags.set_common_tags(self, span)
|
|
102
|
+
|
|
103
|
+
response = call_pipeline_without_datadog(*args, &block)
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
response
|
|
107
|
+
end
|
|
108
|
+
end
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
# patched? tells wether patch has been successfully applied
|
|
112
|
+
def patched?
|
|
113
|
+
@patched
|
|
114
|
+
end
|
|
115
|
+
end
|
|
116
|
+
end
|
|
117
|
+
end
|
|
118
|
+
end
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
module Datadog
|
|
2
|
+
module Contrib
|
|
3
|
+
module Redis
|
|
4
|
+
# Quantize contains Redis-specific resource quantization tools.
|
|
5
|
+
module Quantize
|
|
6
|
+
PLACEHOLDER = '?'.freeze
|
|
7
|
+
TOO_LONG_MARK = '...'.freeze
|
|
8
|
+
VALUE_MAX_LEN = 100
|
|
9
|
+
CMD_MAX_LEN = 1000
|
|
10
|
+
|
|
11
|
+
module_function
|
|
12
|
+
|
|
13
|
+
def format_arg(arg)
|
|
14
|
+
a = arg.to_s
|
|
15
|
+
a = a[0..(VALUE_MAX_LEN - TOO_LONG_MARK.length - 1)] + TOO_LONG_MARK if a.length > VALUE_MAX_LEN
|
|
16
|
+
a
|
|
17
|
+
rescue StandardError => e
|
|
18
|
+
Datadog::Tracer.log.debug("non formattable Redis arg #{a}: #{e}")
|
|
19
|
+
PLACEHOLDER
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def format_command_args(command_args)
|
|
23
|
+
cmd = command_args.map { |x| format_arg(x) }.join(' ')
|
|
24
|
+
cmd = cmd[0..(CMD_MAX_LEN - TOO_LONG_MARK.length - 1)] + TOO_LONG_MARK if cmd.length > CMD_MAX_LEN
|
|
25
|
+
cmd
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
require 'ddtrace/ext/net'
|
|
2
|
+
require 'ddtrace/ext/redis'
|
|
3
|
+
|
|
4
|
+
module Datadog
|
|
5
|
+
module Contrib
|
|
6
|
+
module Redis
|
|
7
|
+
# Tags handles generic common tags assignment.
|
|
8
|
+
module Tags
|
|
9
|
+
module_function
|
|
10
|
+
|
|
11
|
+
def set_common_tags(client, span)
|
|
12
|
+
span.set_tag Datadog::Ext::NET::TARGET_HOST, client.host
|
|
13
|
+
span.set_tag Datadog::Ext::NET::TARGET_PORT, client.port
|
|
14
|
+
span.set_tag Datadog::Ext::Redis::DB, client.db
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
require 'sidekiq/api'
|
|
2
|
+
|
|
3
|
+
require 'ddtrace/ext/app_types'
|
|
4
|
+
|
|
5
|
+
sidekiq_vs = Gem::Version.new(Sidekiq::VERSION)
|
|
6
|
+
sidekiq_min_vs = Gem::Version.new('4.0.0')
|
|
7
|
+
if sidekiq_vs < sidekiq_min_vs
|
|
8
|
+
raise "sidekiq version #{sidekiq_vs} is not supported yet " \
|
|
9
|
+
+ "(supporting versions >=#{sidekiq_min_vs})"
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
Datadog::Tracer.log.debug("Activating instrumentation for Sidekiq '#{sidekiq_vs}'")
|
|
13
|
+
|
|
14
|
+
module Datadog
|
|
15
|
+
module Contrib
|
|
16
|
+
module Sidekiq
|
|
17
|
+
DEFAULT_CONFIG = {
|
|
18
|
+
enabled: true,
|
|
19
|
+
sidekiq_service: 'sidekiq',
|
|
20
|
+
tracer: Datadog.tracer,
|
|
21
|
+
debug: false,
|
|
22
|
+
trace_agent_hostname: Datadog::Writer::HOSTNAME,
|
|
23
|
+
trace_agent_port: Datadog::Writer::PORT
|
|
24
|
+
}.freeze
|
|
25
|
+
|
|
26
|
+
# Middleware is a Sidekiq server-side middleware which traces executed jobs
|
|
27
|
+
class Tracer
|
|
28
|
+
def initialize(options = {})
|
|
29
|
+
# check if Rails configuration is available and use it to override
|
|
30
|
+
# Sidekiq defaults
|
|
31
|
+
rails_config = ::Rails.configuration.datadog_trace rescue {}
|
|
32
|
+
base_config = DEFAULT_CONFIG.merge(rails_config)
|
|
33
|
+
user_config = base_config.merge(options)
|
|
34
|
+
@tracer = user_config[:tracer]
|
|
35
|
+
@sidekiq_service = user_config[:sidekiq_service]
|
|
36
|
+
|
|
37
|
+
# set Tracer status
|
|
38
|
+
@tracer.enabled = user_config[:enabled]
|
|
39
|
+
Datadog::Tracer.debug_logging = user_config[:debug]
|
|
40
|
+
|
|
41
|
+
# configure the Tracer instance
|
|
42
|
+
@tracer.configure(
|
|
43
|
+
hostname: user_config[:trace_agent_hostname],
|
|
44
|
+
port: user_config[:trace_agent_port]
|
|
45
|
+
)
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def call(worker, job, queue)
|
|
49
|
+
# If class is wrapping something else, the interesting resource info
|
|
50
|
+
# is the underlying, wrapped class, and not the wrapper.
|
|
51
|
+
resource = if job['wrapped']
|
|
52
|
+
job['wrapped']
|
|
53
|
+
else
|
|
54
|
+
job['class']
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
# configure Sidekiq service
|
|
58
|
+
service = sidekiq_service(resource_worker(resource))
|
|
59
|
+
set_service_info(service)
|
|
60
|
+
|
|
61
|
+
@tracer.trace('sidekiq.job', service: service, span_type: 'job') do |span|
|
|
62
|
+
span.resource = resource
|
|
63
|
+
span.set_tag('sidekiq.job.id', job['jid'])
|
|
64
|
+
span.set_tag('sidekiq.job.retry', job['retry'])
|
|
65
|
+
span.set_tag('sidekiq.job.queue', job['queue'])
|
|
66
|
+
span.set_tag('sidekiq.job.wrapper', job['class']) if job['wrapped']
|
|
67
|
+
|
|
68
|
+
yield
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
private
|
|
73
|
+
|
|
74
|
+
# rubocop:disable Lint/HandleExceptions
|
|
75
|
+
def resource_worker(resource)
|
|
76
|
+
Object.const_get(resource)
|
|
77
|
+
rescue NameError
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
def worker_config(worker)
|
|
81
|
+
if worker.respond_to?(:datadog_tracer_config)
|
|
82
|
+
worker.datadog_tracer_config
|
|
83
|
+
else
|
|
84
|
+
{}
|
|
85
|
+
end
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
def sidekiq_service(resource)
|
|
89
|
+
worker_config(resource).fetch(:service, @sidekiq_service)
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
def set_service_info(service)
|
|
93
|
+
return if @tracer.services[service]
|
|
94
|
+
@tracer.set_service_info(
|
|
95
|
+
service,
|
|
96
|
+
'sidekiq',
|
|
97
|
+
Datadog::Ext::AppTypes::WORKER
|
|
98
|
+
)
|
|
99
|
+
end
|
|
100
|
+
end
|
|
101
|
+
end
|
|
102
|
+
end
|
|
103
|
+
end
|
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
|
|
2
|
+
require 'sinatra/base'
|
|
3
|
+
|
|
4
|
+
require 'ddtrace/ext/app_types'
|
|
5
|
+
require 'ddtrace/ext/errors'
|
|
6
|
+
require 'ddtrace/ext/http'
|
|
7
|
+
|
|
8
|
+
sinatra_vs = Gem::Version.new(Sinatra::VERSION)
|
|
9
|
+
sinatra_min_vs = Gem::Version.new('1.4.0')
|
|
10
|
+
if sinatra_vs < sinatra_min_vs
|
|
11
|
+
raise "sinatra version #{sinatra_vs} is not supported yet " \
|
|
12
|
+
+ "(supporting versions >=#{sinatra_min_vs})"
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
Datadog::Tracer.log.info("activating instrumentation for sinatra #{sinatra_vs}")
|
|
16
|
+
|
|
17
|
+
module Datadog
|
|
18
|
+
module Contrib
|
|
19
|
+
module Sinatra
|
|
20
|
+
# TracerCfg is used to manipulate the configuration of the Sinatra
|
|
21
|
+
# tracing extension.
|
|
22
|
+
class TracerCfg
|
|
23
|
+
DEFAULT_CFG = {
|
|
24
|
+
enabled: true,
|
|
25
|
+
default_service: 'sinatra',
|
|
26
|
+
tracer: Datadog.tracer,
|
|
27
|
+
debug: false,
|
|
28
|
+
trace_agent_hostname: Datadog::Writer::HOSTNAME,
|
|
29
|
+
trace_agent_port: Datadog::Writer::PORT
|
|
30
|
+
}.freeze()
|
|
31
|
+
|
|
32
|
+
attr_accessor :cfg
|
|
33
|
+
|
|
34
|
+
def initialize
|
|
35
|
+
@cfg = DEFAULT_CFG.dup()
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def configure(args = {})
|
|
39
|
+
args.each do |name, value|
|
|
40
|
+
self[name] = value
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
apply()
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def apply
|
|
47
|
+
Datadog::Tracer.debug_logging = @cfg[:debug]
|
|
48
|
+
|
|
49
|
+
tracer = @cfg[:tracer]
|
|
50
|
+
|
|
51
|
+
tracer.enabled = @cfg[:enabled]
|
|
52
|
+
tracer.configure(hostname: @cfg[:trace_agent_hostname],
|
|
53
|
+
port: @cfg[:trace_agent_port])
|
|
54
|
+
|
|
55
|
+
tracer.set_service_info(@cfg[:default_service], 'sinatra',
|
|
56
|
+
Datadog::Ext::AppTypes::WEB)
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
def [](key)
|
|
60
|
+
raise ArgumentError, "unknown setting '#{key}'" unless @cfg.key? key
|
|
61
|
+
@cfg[key]
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
def []=(key, value)
|
|
65
|
+
raise ArgumentError, "unknown setting '#{key}'" unless @cfg.key? key
|
|
66
|
+
@cfg[key] = value
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
def enabled?
|
|
70
|
+
@cfg[:enabled] && !@cfg[:tracer].nil?
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
# Datadog::Contrib::Sinatra::Tracer is a Sinatra extension which traces
|
|
75
|
+
# requests.
|
|
76
|
+
module Tracer
|
|
77
|
+
def route(verb, action, *)
|
|
78
|
+
# Keep track of the route name when the app is instantiated for an
|
|
79
|
+
# incoming request.
|
|
80
|
+
condition do
|
|
81
|
+
@datadog_route = action
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
super
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
# rubocop:disable Metrics/AbcSize
|
|
88
|
+
# rubocop:disable Metrics/MethodLength
|
|
89
|
+
def self.registered(app)
|
|
90
|
+
::Sinatra::Base.module_eval do
|
|
91
|
+
def render(engine, data, *)
|
|
92
|
+
cfg = settings.datadog_tracer
|
|
93
|
+
|
|
94
|
+
output = ''
|
|
95
|
+
if cfg.enabled?
|
|
96
|
+
tracer = cfg[:tracer]
|
|
97
|
+
tracer.trace('sinatra.render_template') do |span|
|
|
98
|
+
# If data is a string, it is a literal template and we don't
|
|
99
|
+
# want to record it.
|
|
100
|
+
span.set_tag('sinatra.template_name', data) if data.is_a? Symbol
|
|
101
|
+
output = super
|
|
102
|
+
end
|
|
103
|
+
else
|
|
104
|
+
output = super
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
output
|
|
108
|
+
end
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
app.set :datadog_tracer, TracerCfg.new()
|
|
112
|
+
|
|
113
|
+
app.configure do
|
|
114
|
+
app.settings.datadog_tracer.apply()
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
app.before do
|
|
118
|
+
cfg = settings.datadog_tracer
|
|
119
|
+
return unless cfg.enabled?
|
|
120
|
+
|
|
121
|
+
if instance_variable_defined? :@datadog_request_span
|
|
122
|
+
if @datadog_request_span
|
|
123
|
+
Datadog::Tracer.log.error('request span active in :before hook')
|
|
124
|
+
@datadog_request_span.finish()
|
|
125
|
+
@datadog_request_span = nil
|
|
126
|
+
end
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
tracer = cfg[:tracer]
|
|
130
|
+
|
|
131
|
+
span = tracer.trace('sinatra.request',
|
|
132
|
+
service: cfg.cfg[:default_service],
|
|
133
|
+
span_type: Datadog::Ext::HTTP::TYPE)
|
|
134
|
+
span.set_tag(Datadog::Ext::HTTP::URL, request.path)
|
|
135
|
+
span.set_tag(Datadog::Ext::HTTP::METHOD, request.request_method)
|
|
136
|
+
|
|
137
|
+
@datadog_request_span = span
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
app.after do
|
|
141
|
+
cfg = settings.datadog_tracer
|
|
142
|
+
return unless cfg.enabled?
|
|
143
|
+
|
|
144
|
+
span = @datadog_request_span
|
|
145
|
+
begin
|
|
146
|
+
unless span
|
|
147
|
+
Datadog::Tracer.log.error('missing request span in :after hook')
|
|
148
|
+
return
|
|
149
|
+
end
|
|
150
|
+
|
|
151
|
+
span.resource = "#{request.request_method} #{@datadog_route}"
|
|
152
|
+
span.set_tag('sinatra.route.path', @datadog_route)
|
|
153
|
+
span.set_tag(Datadog::Ext::HTTP::STATUS_CODE, response.status)
|
|
154
|
+
span.set_error(env['sinatra.error']) if response.server_error?
|
|
155
|
+
span.finish()
|
|
156
|
+
ensure
|
|
157
|
+
@datadog_request_span = nil
|
|
158
|
+
end
|
|
159
|
+
end
|
|
160
|
+
end
|
|
161
|
+
end
|
|
162
|
+
end
|
|
163
|
+
end
|
|
164
|
+
end
|
|
165
|
+
|
|
166
|
+
# rubocop:disable Style/Documentation
|
|
167
|
+
module Sinatra
|
|
168
|
+
register Datadog::Contrib::Sinatra::Tracer
|
|
169
|
+
end
|