ddtrace 1.4.1 → 1.6.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +144 -1
- data/LICENSE-3rdparty.csv +1 -0
- data/ext/ddtrace_profiling_loader/ddtrace_profiling_loader.c +9 -2
- data/ext/ddtrace_profiling_loader/extconf.rb +17 -0
- data/ext/ddtrace_profiling_native_extension/NativeExtensionDesign.md +38 -2
- data/ext/ddtrace_profiling_native_extension/clock_id.h +1 -0
- data/ext/ddtrace_profiling_native_extension/clock_id_from_pthread.c +1 -0
- data/ext/ddtrace_profiling_native_extension/collectors_cpu_and_wall_time.c +517 -42
- data/ext/ddtrace_profiling_native_extension/collectors_cpu_and_wall_time.h +3 -0
- data/ext/ddtrace_profiling_native_extension/collectors_cpu_and_wall_time_worker.c +208 -30
- data/ext/ddtrace_profiling_native_extension/collectors_stack.c +156 -46
- data/ext/ddtrace_profiling_native_extension/collectors_stack.h +11 -2
- data/ext/ddtrace_profiling_native_extension/extconf.rb +11 -1
- data/ext/ddtrace_profiling_native_extension/http_transport.c +83 -64
- data/ext/ddtrace_profiling_native_extension/libdatadog_helpers.h +4 -4
- data/ext/ddtrace_profiling_native_extension/native_extension_helpers.rb +3 -4
- data/ext/ddtrace_profiling_native_extension/private_vm_api_access.c +59 -0
- data/ext/ddtrace_profiling_native_extension/private_vm_api_access.h +3 -0
- data/ext/ddtrace_profiling_native_extension/profiling.c +10 -0
- data/ext/ddtrace_profiling_native_extension/ruby_helpers.c +0 -1
- data/ext/ddtrace_profiling_native_extension/ruby_helpers.h +4 -2
- data/ext/ddtrace_profiling_native_extension/stack_recorder.c +45 -29
- data/ext/ddtrace_profiling_native_extension/stack_recorder.h +7 -7
- data/lib/datadog/appsec/assets/waf_rules/recommended.json +1169 -275
- data/lib/datadog/appsec/assets/waf_rules/risky.json +78 -78
- data/lib/datadog/appsec/assets/waf_rules/strict.json +278 -88
- data/lib/datadog/appsec/configuration/settings.rb +0 -2
- data/lib/datadog/appsec/contrib/rack/gateway/watcher.rb +25 -20
- data/lib/datadog/appsec/contrib/rack/reactive/request.rb +11 -11
- data/lib/datadog/appsec/contrib/rack/reactive/request_body.rb +11 -11
- data/lib/datadog/appsec/contrib/rack/reactive/response.rb +11 -11
- data/lib/datadog/appsec/contrib/rack/request.rb +3 -0
- data/lib/datadog/appsec/contrib/rack/request_middleware.rb +46 -19
- data/lib/datadog/appsec/contrib/rails/gateway/watcher.rb +7 -6
- data/lib/datadog/appsec/contrib/rails/integration.rb +1 -1
- data/lib/datadog/appsec/contrib/rails/reactive/action.rb +11 -11
- data/lib/datadog/appsec/contrib/rails/request.rb +3 -0
- data/lib/datadog/appsec/contrib/sinatra/gateway/watcher.rb +14 -12
- data/lib/datadog/appsec/contrib/sinatra/reactive/routed.rb +11 -11
- data/lib/datadog/appsec/event.rb +6 -10
- data/lib/datadog/appsec/instrumentation/gateway.rb +16 -2
- data/lib/datadog/appsec/processor.rb +18 -2
- data/lib/datadog/ci/ext/environment.rb +16 -4
- data/lib/datadog/core/configuration/agent_settings_resolver.rb +0 -3
- data/lib/datadog/core/configuration/components.rb +28 -16
- data/lib/datadog/core/configuration/settings.rb +127 -8
- data/lib/datadog/core/configuration.rb +1 -1
- data/lib/datadog/core/diagnostics/environment_logger.rb +5 -1
- data/lib/datadog/core/header_collection.rb +41 -0
- data/lib/datadog/core/telemetry/collector.rb +0 -2
- data/lib/datadog/core/utils/compression.rb +5 -1
- data/lib/datadog/core/workers/async.rb +0 -2
- data/lib/datadog/core.rb +0 -54
- data/lib/datadog/opentracer/tracer.rb +4 -6
- data/lib/datadog/profiling/collectors/cpu_and_wall_time.rb +12 -2
- data/lib/datadog/profiling/collectors/cpu_and_wall_time_worker.rb +5 -3
- data/lib/datadog/profiling/collectors/old_stack.rb +1 -1
- data/lib/datadog/profiling/exporter.rb +2 -4
- data/lib/datadog/profiling/http_transport.rb +1 -1
- data/lib/datadog/profiling.rb +1 -1
- data/lib/datadog/tracing/client_ip.rb +164 -0
- data/lib/datadog/tracing/configuration/ext.rb +14 -0
- data/lib/datadog/tracing/contrib/aws/instrumentation.rb +2 -0
- data/lib/datadog/tracing/contrib/aws/services.rb +0 -2
- data/lib/datadog/tracing/contrib/dalli/ext.rb +1 -0
- data/lib/datadog/tracing/contrib/dalli/instrumentation.rb +4 -0
- data/lib/datadog/tracing/contrib/elasticsearch/ext.rb +2 -0
- data/lib/datadog/tracing/contrib/elasticsearch/patcher.rb +3 -0
- data/lib/datadog/tracing/contrib/ethon/easy_patch.rb +2 -2
- data/lib/datadog/tracing/contrib/ethon/multi_patch.rb +2 -0
- data/lib/datadog/tracing/contrib/excon/middleware.rb +2 -0
- data/lib/datadog/tracing/contrib/ext.rb +25 -0
- data/lib/datadog/tracing/contrib/faraday/middleware.rb +3 -2
- data/lib/datadog/tracing/contrib/grape/endpoint.rb +0 -2
- data/lib/datadog/tracing/contrib/graphql/configuration/settings.rb +1 -1
- data/lib/datadog/tracing/contrib/grpc/datadog_interceptor/client.rb +5 -0
- data/lib/datadog/tracing/contrib/grpc/datadog_interceptor/server.rb +7 -1
- data/lib/datadog/tracing/contrib/grpc/ext.rb +2 -0
- data/lib/datadog/tracing/contrib/hanami/action_tracer.rb +47 -0
- data/lib/datadog/tracing/contrib/hanami/configuration/settings.rb +22 -0
- data/lib/datadog/tracing/contrib/hanami/ext.rb +24 -0
- data/lib/datadog/tracing/contrib/hanami/integration.rb +44 -0
- data/lib/datadog/tracing/contrib/hanami/patcher.rb +33 -0
- data/lib/datadog/tracing/contrib/hanami/plugin.rb +23 -0
- data/lib/datadog/tracing/contrib/hanami/renderer_policy_tracing.rb +41 -0
- data/lib/datadog/tracing/contrib/hanami/router_tracing.rb +44 -0
- data/lib/datadog/tracing/contrib/http/instrumentation.rb +2 -0
- data/lib/datadog/tracing/contrib/httpclient/instrumentation.rb +2 -0
- data/lib/datadog/tracing/contrib/httprb/instrumentation.rb +2 -0
- data/lib/datadog/tracing/contrib/mongodb/ext.rb +7 -0
- data/lib/datadog/tracing/contrib/mongodb/subscribers.rb +4 -0
- data/lib/datadog/tracing/contrib/mysql2/configuration/settings.rb +12 -0
- data/lib/datadog/tracing/contrib/mysql2/ext.rb +1 -0
- data/lib/datadog/tracing/contrib/mysql2/instrumentation.rb +16 -0
- data/lib/datadog/tracing/contrib/pg/configuration/settings.rb +12 -0
- data/lib/datadog/tracing/contrib/pg/ext.rb +2 -1
- data/lib/datadog/tracing/contrib/pg/instrumentation.rb +38 -21
- data/lib/datadog/tracing/contrib/propagation/sql_comment/comment.rb +43 -0
- data/lib/datadog/tracing/contrib/propagation/sql_comment/ext.rb +32 -0
- data/lib/datadog/tracing/contrib/propagation/sql_comment/mode.rb +28 -0
- data/lib/datadog/tracing/contrib/propagation/sql_comment.rb +49 -0
- data/lib/datadog/tracing/contrib/rack/header_collection.rb +35 -0
- data/lib/datadog/tracing/contrib/rack/middlewares.rb +105 -43
- data/lib/datadog/tracing/contrib/redis/ext.rb +2 -0
- data/lib/datadog/tracing/contrib/redis/instrumentation.rb +4 -2
- data/lib/datadog/tracing/contrib/redis/integration.rb +2 -1
- data/lib/datadog/tracing/contrib/redis/patcher.rb +40 -0
- data/lib/datadog/tracing/contrib/redis/tags.rb +5 -0
- data/lib/datadog/tracing/contrib/rest_client/request_patch.rb +2 -0
- data/lib/datadog/tracing/contrib/sinatra/env.rb +12 -23
- data/lib/datadog/tracing/contrib/sinatra/ext.rb +7 -3
- data/lib/datadog/tracing/contrib/sinatra/patcher.rb +2 -2
- data/lib/datadog/tracing/contrib/sinatra/tracer.rb +8 -80
- data/lib/datadog/tracing/contrib/sinatra/tracer_middleware.rb +14 -9
- data/lib/datadog/tracing/contrib/utils/quantization/http.rb +92 -10
- data/lib/datadog/tracing/contrib.rb +1 -0
- data/lib/datadog/tracing/distributed/datadog_tags_codec.rb +84 -0
- data/lib/datadog/tracing/distributed/headers/datadog.rb +122 -30
- data/lib/datadog/tracing/distributed/headers/ext.rb +2 -0
- data/lib/datadog/tracing/flush.rb +57 -35
- data/lib/datadog/tracing/metadata/ext.rb +11 -9
- data/lib/datadog/tracing/metadata/tagging.rb +9 -0
- data/lib/datadog/tracing/propagation/http.rb +9 -1
- data/lib/datadog/tracing/sampling/ext.rb +31 -0
- data/lib/datadog/tracing/sampling/priority_sampler.rb +46 -4
- data/lib/datadog/tracing/sampling/rate_by_key_sampler.rb +8 -9
- data/lib/datadog/tracing/sampling/rate_by_service_sampler.rb +29 -5
- data/lib/datadog/tracing/sampling/rate_limiter.rb +3 -0
- data/lib/datadog/tracing/sampling/rate_sampler.rb +20 -3
- data/lib/datadog/tracing/sampling/rule_sampler.rb +4 -3
- data/lib/datadog/tracing/sampling/span/ext.rb +25 -0
- data/lib/datadog/tracing/sampling/span/matcher.rb +9 -0
- data/lib/datadog/tracing/sampling/span/rule.rb +82 -0
- data/lib/datadog/tracing/sampling/span/rule_parser.rb +104 -0
- data/lib/datadog/tracing/sampling/span/sampler.rb +75 -0
- data/lib/datadog/tracing/span_operation.rb +0 -2
- data/lib/datadog/tracing/trace_digest.rb +3 -0
- data/lib/datadog/tracing/trace_operation.rb +32 -3
- data/lib/datadog/tracing/trace_segment.rb +7 -2
- data/lib/datadog/tracing/tracer.rb +34 -6
- data/lib/datadog/tracing/writer.rb +7 -0
- data/lib/ddtrace/transport/trace_formatter.rb +7 -0
- data/lib/ddtrace/transport/traces.rb +3 -1
- data/lib/ddtrace/version.rb +1 -1
- metadata +36 -18
- data/lib/datadog/profiling/old_ext.rb +0 -42
- data/lib/datadog/profiling/transport/http/api/endpoint.rb +0 -85
- data/lib/datadog/profiling/transport/http/api/instance.rb +0 -38
- data/lib/datadog/profiling/transport/http/api/spec.rb +0 -42
- data/lib/datadog/profiling/transport/http/api.rb +0 -45
- data/lib/datadog/profiling/transport/http/builder.rb +0 -30
- data/lib/datadog/profiling/transport/http/client.rb +0 -37
- data/lib/datadog/profiling/transport/http/response.rb +0 -21
- data/lib/datadog/profiling/transport/http.rb +0 -118
@@ -17,69 +17,12 @@ module Datadog
|
|
17
17
|
# Datadog::Tracing::Contrib::Sinatra::Tracer is a Sinatra extension which traces
|
18
18
|
# requests.
|
19
19
|
module Tracer
|
20
|
-
def route(verb, action, *)
|
21
|
-
# Keep track of the route name when the app is instantiated for an
|
22
|
-
# incoming request.
|
23
|
-
condition do
|
24
|
-
# If the option to prepend script names is enabled, then
|
25
|
-
# prepend the script name from the request onto the action.
|
26
|
-
#
|
27
|
-
# DEV: env['sinatra.route'] already exists with very similar information,
|
28
|
-
# DEV: but doesn't account for our `resource_script_names` logic.
|
29
|
-
#
|
30
|
-
@datadog_route = if Datadog.configuration.tracing[:sinatra][:resource_script_names]
|
31
|
-
"#{request.script_name}#{action}"
|
32
|
-
else
|
33
|
-
action
|
34
|
-
end
|
35
|
-
end
|
36
|
-
|
37
|
-
super
|
38
|
-
end
|
39
|
-
|
40
20
|
def self.registered(app)
|
41
21
|
app.use TracerMiddleware, app_instance: app
|
42
|
-
|
43
|
-
app.after do
|
44
|
-
next unless Tracing.enabled?
|
45
|
-
|
46
|
-
span = Sinatra::Env.datadog_span(env, app)
|
47
|
-
|
48
|
-
# TODO: `route` should *only* be populated if @datadog_route is defined.
|
49
|
-
# TODO: If @datadog_route is not defined, then this Sinatra app is not responsible
|
50
|
-
# TODO: for handling this request.
|
51
|
-
# TODO:
|
52
|
-
# TODO: This change would be BREAKING for any Sinatra app (classic or modular),
|
53
|
-
# TODO: as it affects the `resource` value for requests not handled by the Sinatra app.
|
54
|
-
# TODO: Currently we use "#{method} #{path}" in such aces, but `path` is the raw,
|
55
|
-
# TODO: high-cardinality HTTP path, and can contain PII.
|
56
|
-
# TODO:
|
57
|
-
# TODO: The value we should use as the `resource` when the Sinatra app is not
|
58
|
-
# TODO: responsible for the request is a tricky subject.
|
59
|
-
# TODO: The best option is a value that clearly communicates that this app did not
|
60
|
-
# TODO: handle this request. It's important to keep in mind that an unhandled request
|
61
|
-
# TODO: by this Sinatra app might still be handled by another Rack middleware (which can
|
62
|
-
# TODO: be a Sinatra app itself) or it might just 404 if not handled at all.
|
63
|
-
# TODO:
|
64
|
-
# TODO: A possible value for `resource` could set a high level description, e.g.
|
65
|
-
# TODO: `request.request_method`, given we don't have the response object available yet.
|
66
|
-
route = if defined?(@datadog_route)
|
67
|
-
@datadog_route
|
68
|
-
else
|
69
|
-
# Fallback in case no routes have matched
|
70
|
-
request.path
|
71
|
-
end
|
72
|
-
|
73
|
-
span.resource = "#{request.request_method} #{route}"
|
74
|
-
span.set_tag(Ext::TAG_ROUTE_PATH, route)
|
75
|
-
end
|
76
22
|
end
|
77
23
|
|
78
24
|
# Method overrides for Sinatra::Base
|
79
25
|
module Base
|
80
|
-
MISSING_REQUEST_SPAN_ONLY_ONCE = Core::Utils::OnlyOnce.new
|
81
|
-
private_constant :MISSING_REQUEST_SPAN_ONLY_ONCE
|
82
|
-
|
83
26
|
def render(engine, data, *)
|
84
27
|
return super unless Tracing.enabled?
|
85
28
|
|
@@ -102,19 +45,21 @@ module Datadog
|
|
102
45
|
|
103
46
|
# Invoked when a matching route is found.
|
104
47
|
# This method yields directly to user code.
|
105
|
-
# rubocop:disable Metrics/MethodLength
|
106
48
|
def route_eval
|
107
49
|
configuration = Datadog.configuration.tracing[:sinatra]
|
108
50
|
return super unless Tracing.enabled?
|
109
51
|
|
52
|
+
datadog_route = Sinatra::Env.route_path(env)
|
53
|
+
|
110
54
|
Tracing.trace(
|
111
55
|
Ext::SPAN_ROUTE,
|
112
56
|
service: configuration[:service_name],
|
113
57
|
span_type: Tracing::Metadata::Ext::HTTP::TYPE_INBOUND,
|
114
|
-
resource: "#{request.request_method} #{
|
58
|
+
resource: "#{request.request_method} #{datadog_route}",
|
115
59
|
) do |span, trace|
|
116
60
|
span.set_tag(Ext::TAG_APP_NAME, settings.name || settings.superclass.name)
|
117
|
-
span.set_tag(Ext::TAG_ROUTE_PATH,
|
61
|
+
span.set_tag(Ext::TAG_ROUTE_PATH, datadog_route)
|
62
|
+
|
118
63
|
if request.script_name && !request.script_name.empty?
|
119
64
|
span.set_tag(Ext::TAG_SCRIPT_NAME, request.script_name)
|
120
65
|
end
|
@@ -124,32 +69,15 @@ module Datadog
|
|
124
69
|
|
125
70
|
trace.resource = span.resource
|
126
71
|
|
127
|
-
sinatra_request_span =
|
128
|
-
|
129
|
-
|
130
|
-
else
|
131
|
-
Sinatra::Env.datadog_span(env, self.class)
|
132
|
-
end
|
133
|
-
if sinatra_request_span
|
134
|
-
sinatra_request_span.resource = span.resource
|
135
|
-
else
|
136
|
-
MISSING_REQUEST_SPAN_ONLY_ONCE.run do
|
137
|
-
Datadog.logger.warn do
|
138
|
-
'Sinatra integration is misconfigured, reported traces will be missing request metadata ' \
|
139
|
-
'such as path and HTTP status code. ' \
|
140
|
-
'Did you forget to add `register Datadog::Tracing::Contrib::Sinatra::Tracer` to your ' \
|
141
|
-
'`Sinatra::Base` subclass? ' \
|
142
|
-
'See <https://docs.datadoghq.com/tracing/setup_overview/setup/ruby/#sinatra> for more details.'
|
143
|
-
end
|
144
|
-
end
|
145
|
-
end
|
72
|
+
sinatra_request_span = Sinatra::Env.datadog_span(env)
|
73
|
+
|
74
|
+
sinatra_request_span.resource = span.resource
|
146
75
|
|
147
76
|
Contrib::Analytics.set_measured(span)
|
148
77
|
|
149
78
|
super
|
150
79
|
end
|
151
80
|
end
|
152
|
-
# rubocop:enable Metrics/MethodLength
|
153
81
|
end
|
154
82
|
end
|
155
83
|
end
|
@@ -25,10 +25,12 @@ module Datadog
|
|
25
25
|
def call(env)
|
26
26
|
# Set the trace context (e.g. distributed tracing)
|
27
27
|
if configuration[:distributed_tracing] && Tracing.active_trace.nil?
|
28
|
-
original_trace = Propagation::HTTP.extract(env)
|
28
|
+
original_trace = Tracing::Propagation::HTTP.extract(env)
|
29
29
|
Tracing.continue_trace!(original_trace)
|
30
30
|
end
|
31
31
|
|
32
|
+
return @app.call(env) if Sinatra::Env.datadog_span(env)
|
33
|
+
|
32
34
|
Tracing.trace(
|
33
35
|
Ext::SPAN_REQUEST,
|
34
36
|
service: configuration[:service_name],
|
@@ -39,7 +41,7 @@ module Datadog
|
|
39
41
|
# the nil signals that there's no good one yet and is also seen by profiler, when sampling the resource
|
40
42
|
span.resource = nil
|
41
43
|
|
42
|
-
Sinatra::Env.set_datadog_span(env,
|
44
|
+
Sinatra::Env.set_datadog_span(env, span)
|
43
45
|
|
44
46
|
response = @app.call(env)
|
45
47
|
ensure
|
@@ -51,14 +53,18 @@ module Datadog
|
|
51
53
|
span.set_tag(Tracing::Metadata::Ext::TAG_OPERATION, Ext::TAG_OPERATION_REQUEST)
|
52
54
|
|
53
55
|
request = ::Sinatra::Request.new(env)
|
56
|
+
|
54
57
|
span.set_tag(Tracing::Metadata::Ext::HTTP::TAG_URL, request.path)
|
55
58
|
span.set_tag(Tracing::Metadata::Ext::HTTP::TAG_METHOD, request.request_method)
|
59
|
+
|
60
|
+
datadog_route = Sinatra::Env.route_path(env)
|
61
|
+
|
62
|
+
span.set_tag(Ext::TAG_ROUTE_PATH, datadog_route) if datadog_route
|
63
|
+
|
56
64
|
if request.script_name && !request.script_name.empty?
|
57
65
|
span.set_tag(Ext::TAG_SCRIPT_NAME, request.script_name)
|
58
66
|
end
|
59
67
|
|
60
|
-
span.set_tag(Ext::TAG_APP_NAME, @app_instance.settings.name)
|
61
|
-
|
62
68
|
# If this app handled the request, then Contrib::Sinatra::Tracer OR Contrib::Sinatra::Base set the
|
63
69
|
# resource; if no resource was set, let's use a fallback
|
64
70
|
span.resource = env['REQUEST_METHOD'] if span.resource.nil?
|
@@ -79,7 +85,10 @@ module Datadog
|
|
79
85
|
end
|
80
86
|
|
81
87
|
if (headers = response[1])
|
82
|
-
Sinatra::Headers.response_header_tags(
|
88
|
+
Sinatra::Headers.response_header_tags(
|
89
|
+
headers,
|
90
|
+
configuration[:headers][:response]
|
91
|
+
).each do |name, value|
|
83
92
|
span.set_tag(name, value) if span.get_tag(name).nil?
|
84
93
|
end
|
85
94
|
end
|
@@ -111,10 +120,6 @@ module Datadog
|
|
111
120
|
def configuration
|
112
121
|
Datadog.configuration.tracing[:sinatra]
|
113
122
|
end
|
114
|
-
|
115
|
-
def header_to_rack_header(name)
|
116
|
-
"HTTP_#{name.to_s.upcase.gsub(/[-\s]/, '_')}"
|
117
|
-
end
|
118
123
|
end
|
119
124
|
end
|
120
125
|
end
|
@@ -14,12 +14,28 @@ module Datadog
|
|
14
14
|
|
15
15
|
PLACEHOLDER = '?'.freeze
|
16
16
|
|
17
|
+
# taken from Ruby https://github.com/ruby/uri/blob/ffbab83de6d8748c9454414e02db5317609166eb/lib/uri/rfc3986_parser.rb
|
18
|
+
# but adjusted to parse only <scheme>://<host>:<port>/ components
|
19
|
+
# and stop there, since we don't care about the path, query string,
|
20
|
+
# and fragment components
|
21
|
+
RFC3986_URL_BASE = /\A(?<URI>(?<scheme>[A-Za-z][+\-.0-9A-Za-z]*):(?<hier-part>\/\/(?<authority>(?:(?<userinfo>(?:%\h\h|[!$&-.0-;=A-Z_a-z~])*)@)?(?<host>(?<IP-literal>\[(?:(?<IPv6address>(?:\h{1,4}:){6}(?<ls32>\h{1,4}:\h{1,4}|(?<IPv4address>(?<dec-octet>[1-9]\d|1\d{2}|2[0-4]\d|25[0-5]|\d)\.\g<dec-octet>\.\g<dec-octet>\.\g<dec-octet>))|::(?:\h{1,4}:){5}\g<ls32>|\h{1,4}?::(?:\h{1,4}:){4}\g<ls32>|(?:(?:\h{1,4}:)?\h{1,4})?::(?:\h{1,4}:){3}\g<ls32>|(?:(?:\h{1,4}:){,2}\h{1,4})?::(?:\h{1,4}:){2}\g<ls32>|(?:(?:\h{1,4}:){,3}\h{1,4})?::\h{1,4}:\g<ls32>|(?:(?:\h{1,4}:){,4}\h{1,4})?::\g<ls32>|(?:(?:\h{1,4}:){,5}\h{1,4})?::\h{1,4}|(?:(?:\h{1,4}:){,6}\h{1,4})?::)|(?<IPvFuture>v\h+\.[!$&-.0-;=A-Z_a-z~]+))\])|\g<IPv4address>|(?<reg-name>(?:%\h\h|[!$&-.0-9;=A-Z_a-z~])*))(?::(?<port>\d*))?)))(?:\/|\z)/.freeze # rubocop:disable Style/RegexpLiteral, Layout/LineLength
|
22
|
+
|
17
23
|
module_function
|
18
24
|
|
19
25
|
def url(url, options = {})
|
20
26
|
url!(url, options)
|
21
27
|
rescue StandardError
|
22
|
-
options[:placeholder] || PLACEHOLDER
|
28
|
+
placeholder = options[:placeholder] || PLACEHOLDER
|
29
|
+
|
30
|
+
options[:base] == :exclude ? placeholder : "#{base_url(url)}/#{placeholder}"
|
31
|
+
end
|
32
|
+
|
33
|
+
def base_url(url, options = {})
|
34
|
+
if (m = RFC3986_URL_BASE.match(url))
|
35
|
+
m[1]
|
36
|
+
else
|
37
|
+
''
|
38
|
+
end
|
23
39
|
end
|
24
40
|
|
25
41
|
def url!(url, options = {})
|
@@ -32,8 +48,14 @@ module Datadog
|
|
32
48
|
uri.query = (!query.nil? && query.empty? ? nil : query)
|
33
49
|
end
|
34
50
|
|
35
|
-
# Remove any URI
|
51
|
+
# Remove any URI fragments
|
36
52
|
uri.fragment = nil unless options[:fragment] == :show
|
53
|
+
|
54
|
+
if options[:base] == :exclude
|
55
|
+
uri.host = nil
|
56
|
+
uri.port = nil
|
57
|
+
uri.scheme = nil
|
58
|
+
end
|
37
59
|
end.to_s
|
38
60
|
end
|
39
61
|
|
@@ -45,22 +67,26 @@ module Datadog
|
|
45
67
|
|
46
68
|
def query!(query, options = {})
|
47
69
|
options ||= {}
|
48
|
-
options[:
|
70
|
+
options[:obfuscate] = {} if options[:obfuscate] == :internal
|
71
|
+
options[:show] = options[:show] || (options[:obfuscate] ? :all : [])
|
49
72
|
options[:exclude] = options[:exclude] || []
|
50
73
|
|
51
74
|
# Short circuit if query string is meant to exclude everything
|
52
75
|
# or if the query string is meant to include everything
|
53
76
|
return '' if options[:exclude] == :all
|
54
|
-
return query if options[:show] == :all
|
55
77
|
|
56
|
-
|
57
|
-
|
58
|
-
[
|
59
|
-
|
60
|
-
|
61
|
-
|
78
|
+
unless options[:show] == :all && !(options[:obfuscate] && options[:exclude])
|
79
|
+
query = collect_query(query, uniq: true) do |key, value|
|
80
|
+
if options[:exclude].include?(key)
|
81
|
+
[nil, nil]
|
82
|
+
else
|
83
|
+
value = options[:show] == :all || options[:show].include?(key) ? value : nil
|
84
|
+
[key, value]
|
85
|
+
end
|
62
86
|
end
|
63
87
|
end
|
88
|
+
|
89
|
+
options[:obfuscate] ? obfuscate_query(query, options[:obfuscate]) : query
|
64
90
|
end
|
65
91
|
|
66
92
|
# Iterate over each key value pair, yielding to the block given.
|
@@ -91,6 +117,62 @@ module Datadog
|
|
91
117
|
end
|
92
118
|
|
93
119
|
private_class_method :collect_query
|
120
|
+
|
121
|
+
# Scans over the query string and obfuscates sensitive data by
|
122
|
+
# replacing matches with an opaque value
|
123
|
+
def obfuscate_query(query, options = {})
|
124
|
+
options[:regex] = nil if options[:regex] == :internal
|
125
|
+
re = options[:regex] || OBFUSCATOR_REGEX
|
126
|
+
with = options[:with] || OBFUSCATOR_WITH
|
127
|
+
|
128
|
+
query.gsub(re, with)
|
129
|
+
end
|
130
|
+
|
131
|
+
private_class_method :obfuscate_query
|
132
|
+
|
133
|
+
OBFUSCATOR_WITH = '<redacted>'.freeze
|
134
|
+
|
135
|
+
# rubocop:disable Layout/LineLength
|
136
|
+
OBFUSCATOR_REGEX = %r{
|
137
|
+
(?: # JSON-ish leading quote
|
138
|
+
(?:"|%22)?
|
139
|
+
)
|
140
|
+
(?: # common keys
|
141
|
+
(?:old_?|new_?)?p(?:ass)?w(?:or)?d(?:1|2)? # pw, password variants
|
142
|
+
|pass(?:_?phrase)? # pass, passphrase variants
|
143
|
+
|secret
|
144
|
+
|(?: # key, key_id variants
|
145
|
+
api_?
|
146
|
+
|private_?
|
147
|
+
|public_?
|
148
|
+
|access_?
|
149
|
+
|secret_?
|
150
|
+
)key(?:_?id)?
|
151
|
+
|token
|
152
|
+
|consumer_?(?:id|key|secret)
|
153
|
+
|sign(?:ed|ature)?
|
154
|
+
|auth(?:entication|orization)?
|
155
|
+
)
|
156
|
+
(?:
|
157
|
+
# '=' query string separator, plus value til next '&' separator
|
158
|
+
(?:\s|%20)*(?:=|%3D)[^&]+
|
159
|
+
# JSON-ish '": "somevalue"', key being handled with case above, without the opening '"'
|
160
|
+
|(?:"|%22) # closing '"' at end of key
|
161
|
+
(?:\s|%20)*(?::|%3A)(?:\s|%20)* # ':' key-value spearator, with surrounding spaces
|
162
|
+
(?:"|%22) # opening '"' at start of value
|
163
|
+
(?:%2[^2]|%[^2]|[^"%])+ # value
|
164
|
+
(?:"|%22) # closing '"' at end of value
|
165
|
+
)
|
166
|
+
|(?: # other common secret values
|
167
|
+
bearer(?:\s|%20)+[a-z0-9._\-]+
|
168
|
+
|token(?::|%3A)[a-z0-9]{13}
|
169
|
+
|gh[opsu]_[0-9a-zA-Z]{36}
|
170
|
+
|ey[I-L](?:[\w=-]|%3D)+\.ey[I-L](?:[\w=-]|%3D)+(?:\.(?:[\w.+/=-]|%3D|%2F|%2B)+)?
|
171
|
+
|-{5}BEGIN(?:[a-z\s]|%20)+PRIVATE(?:\s|%20)KEY-{5}[^\-]+-{5}END(?:[a-z\s]|%20)+PRIVATE(?:\s|%20)KEY(?:-{5})?(?:\n|%0A)?
|
172
|
+
|(?:ssh-(?:rsa|dss)|ecdsa-[a-z0-9]+-[a-z0-9]+)(?:\s|%20)*(?:[a-z0-9/.+]|%2F|%5C|%2B){100,}(?:=|%3D)*(?:(?:\s+)[a-z0-9._-]+)?
|
173
|
+
)
|
174
|
+
}ix.freeze
|
175
|
+
# rubocop:enable Layout/LineLength
|
94
176
|
end
|
95
177
|
end
|
96
178
|
end
|
@@ -49,6 +49,7 @@ require_relative 'contrib/faraday/integration'
|
|
49
49
|
require_relative 'contrib/grape/integration'
|
50
50
|
require_relative 'contrib/graphql/integration'
|
51
51
|
require_relative 'contrib/grpc/integration'
|
52
|
+
require_relative 'contrib/hanami/integration'
|
52
53
|
require_relative 'contrib/http/integration'
|
53
54
|
require_relative 'contrib/httpclient/integration'
|
54
55
|
require_relative 'contrib/httprb/integration'
|
@@ -0,0 +1,84 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Datadog
|
4
|
+
module Tracing
|
5
|
+
module Distributed
|
6
|
+
# Encodes and decodes distributed 'x-datadog-tags' tags for transport
|
7
|
+
# to and from external processes.
|
8
|
+
module DatadogTagsCodec
|
9
|
+
# Backport `Regexp::match?` because it is measurably the most performant
|
10
|
+
# way to check if a string matches a regular expression.
|
11
|
+
module RefineRegexp
|
12
|
+
unless Regexp.method_defined?(:match?)
|
13
|
+
refine ::Regexp do
|
14
|
+
def match?(*args)
|
15
|
+
!match(*args).nil?
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
using RefineRegexp
|
21
|
+
|
22
|
+
# ASCII characters 32-126, except `,`, `=`, and ` `. At least one character.
|
23
|
+
VALID_KEY_CHARS = /\A(?:(?![,= ])[\u0020-\u007E])+\Z/.freeze
|
24
|
+
# ASCII characters 32-126, except `,`. At least one character.
|
25
|
+
VALID_VALUE_CHARS = /\A(?:(?!,)[\u0020-\u007E])+\Z/.freeze
|
26
|
+
|
27
|
+
# Serializes a {Hash<String,String>} into a `x-datadog-tags`-compatible
|
28
|
+
# String.
|
29
|
+
#
|
30
|
+
# @param tags [Hash<String,String>] trace tag hash
|
31
|
+
# @return [String] serialized tags hash
|
32
|
+
# @raise [EncodingError] if tags cannot be serialized to the `x-datadog-tags` format
|
33
|
+
def self.encode(tags)
|
34
|
+
begin
|
35
|
+
tags.map do |raw_key, raw_value|
|
36
|
+
key = raw_key.to_s
|
37
|
+
value = raw_value.to_s
|
38
|
+
|
39
|
+
raise EncodingError, "Invalid key `#{key}` for value `#{value}`" unless VALID_KEY_CHARS.match?(key)
|
40
|
+
raise EncodingError, "Invalid value `#{value}` for key `#{key}`" unless VALID_VALUE_CHARS.match?(value)
|
41
|
+
|
42
|
+
"#{key}=#{value.strip}"
|
43
|
+
end.join(',')
|
44
|
+
rescue => e
|
45
|
+
raise EncodingError, "Error encoding tags `#{tags}`: `#{e}`"
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
# Deserializes a `x-datadog-tags`-formatted String into a {Hash<String,String>}.
|
50
|
+
#
|
51
|
+
# @param string [String] tags as serialized by {#encode}
|
52
|
+
# @return [Hash<String,String>] decoded input as a hash of strings
|
53
|
+
# @raise [DecodingError] if string does not conform to the `x-datadog-tags` format
|
54
|
+
def self.decode(string)
|
55
|
+
result = Hash[string.split(',').map do |raw_tag|
|
56
|
+
raw_tag.split('=', 2).tap do |raw_key, raw_value|
|
57
|
+
key = raw_key.to_s
|
58
|
+
value = raw_value.to_s
|
59
|
+
|
60
|
+
raise DecodingError, "Invalid key: #{key}" unless VALID_KEY_CHARS.match?(key)
|
61
|
+
raise DecodingError, "Invalid value: #{value}" unless VALID_VALUE_CHARS.match?(value)
|
62
|
+
|
63
|
+
value.strip!
|
64
|
+
end
|
65
|
+
end]
|
66
|
+
|
67
|
+
raise DecodingError, "Invalid empty tags: #{string}" if result.empty? && !string.empty?
|
68
|
+
|
69
|
+
result
|
70
|
+
end
|
71
|
+
|
72
|
+
# An error occurred during distributed tags encoding.
|
73
|
+
# See {#message} for more information.
|
74
|
+
class EncodingError < StandardError
|
75
|
+
end
|
76
|
+
|
77
|
+
# An error occurred during distributed tags decoding.
|
78
|
+
# See {#message} for more information.
|
79
|
+
class DecodingError < StandardError
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
@@ -1,8 +1,11 @@
|
|
1
|
+
# frozen_string_literal: true
|
1
2
|
# typed: true
|
2
3
|
|
3
4
|
require_relative 'parser'
|
4
5
|
require_relative 'ext'
|
6
|
+
require_relative '../../metadata/ext'
|
5
7
|
require_relative '../../trace_digest'
|
8
|
+
require_relative '../datadog_tags_codec'
|
6
9
|
|
7
10
|
module Datadog
|
8
11
|
module Tracing
|
@@ -10,40 +13,129 @@ module Datadog
|
|
10
13
|
module Headers
|
11
14
|
# Datadog provides helpers to inject or extract headers for Datadog style headers
|
12
15
|
module Datadog
|
13
|
-
|
16
|
+
class << self
|
17
|
+
include Ext
|
14
18
|
|
15
|
-
|
16
|
-
|
19
|
+
def inject!(digest, env)
|
20
|
+
return if digest.nil?
|
17
21
|
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
+
env[HTTP_HEADER_TRACE_ID] = digest.trace_id.to_s
|
23
|
+
env[HTTP_HEADER_PARENT_ID] = digest.span_id.to_s
|
24
|
+
env[HTTP_HEADER_SAMPLING_PRIORITY] = digest.trace_sampling_priority.to_s if digest.trace_sampling_priority
|
25
|
+
env[HTTP_HEADER_ORIGIN] = digest.trace_origin.to_s unless digest.trace_origin.nil?
|
22
26
|
|
23
|
-
|
24
|
-
|
27
|
+
inject_tags(digest, env)
|
28
|
+
|
29
|
+
env
|
30
|
+
end
|
31
|
+
|
32
|
+
def extract(env)
|
33
|
+
# Extract values from headers
|
34
|
+
headers = Parser.new(env)
|
35
|
+
trace_id = headers.id(HTTP_HEADER_TRACE_ID)
|
36
|
+
parent_id = headers.id(HTTP_HEADER_PARENT_ID)
|
37
|
+
origin = headers.header(HTTP_HEADER_ORIGIN)
|
38
|
+
sampling_priority = headers.number(HTTP_HEADER_SAMPLING_PRIORITY)
|
39
|
+
|
40
|
+
# Return early if this propagation is not valid
|
41
|
+
# DEV: To be valid we need to have a trace id and a parent id
|
42
|
+
# or when it is a synthetics trace, just the trace id.
|
43
|
+
# DEV: `Parser#id` will not return 0
|
44
|
+
return unless (trace_id && parent_id) || (origin && trace_id)
|
45
|
+
|
46
|
+
trace_distributed_tags = extract_tags(headers)
|
47
|
+
|
48
|
+
# Return new trace headers
|
49
|
+
TraceDigest.new(
|
50
|
+
span_id: parent_id,
|
51
|
+
trace_id: trace_id,
|
52
|
+
trace_origin: origin,
|
53
|
+
trace_sampling_priority: sampling_priority,
|
54
|
+
trace_distributed_tags: trace_distributed_tags,
|
55
|
+
)
|
56
|
+
end
|
57
|
+
|
58
|
+
private
|
59
|
+
|
60
|
+
# Export trace distributed tags through the `x-datadog-tags` header.
|
61
|
+
#
|
62
|
+
# DEV: This method accesses global state (the active trace) to record its error state as a trace tag.
|
63
|
+
# DEV: This means errors cannot be reported if there's not active span.
|
64
|
+
# DEV: Ideally, we'd have a dedicated error reporting stream for all of ddtrace.
|
65
|
+
# DEV: The same comment applies to the {.extract_tags}.
|
66
|
+
def inject_tags(digest, env)
|
67
|
+
return if digest.trace_distributed_tags.nil? || digest.trace_distributed_tags.empty?
|
68
|
+
|
69
|
+
if ::Datadog.configuration.tracing.x_datadog_tags_max_length <= 0
|
70
|
+
active_trace = Tracing.active_trace
|
71
|
+
active_trace.set_tag('_dd.propagation_error', 'disabled') if active_trace
|
72
|
+
return
|
73
|
+
end
|
74
|
+
|
75
|
+
encoded_tags = DatadogTagsCodec.encode(digest.trace_distributed_tags)
|
76
|
+
|
77
|
+
if encoded_tags.size > ::Datadog.configuration.tracing.x_datadog_tags_max_length
|
78
|
+
active_trace = Tracing.active_trace
|
79
|
+
active_trace.set_tag('_dd.propagation_error', 'inject_max_size') if active_trace
|
80
|
+
|
81
|
+
::Datadog.logger.warn(
|
82
|
+
"Failed to inject x-datadog-tags: tags are too large (size:#{encoded_tags.size} " \
|
83
|
+
"limit:#{::Datadog.configuration.tracing.x_datadog_tags_max_length}). This limit can be configured " \
|
84
|
+
'through the DD_TRACE_X_DATADOG_TAGS_MAX_LENGTH environment variable.'
|
85
|
+
)
|
86
|
+
return
|
87
|
+
end
|
88
|
+
|
89
|
+
env[HTTP_HEADER_TAGS] = encoded_tags
|
90
|
+
rescue => e
|
91
|
+
active_trace = Tracing.active_trace
|
92
|
+
active_trace.set_tag('_dd.propagation_error', 'encoding_error') if active_trace
|
93
|
+
::Datadog.logger.warn(
|
94
|
+
"Failed to inject x-datadog-tags: #{e.class.name} #{e.message} at #{Array(e.backtrace).first}"
|
95
|
+
)
|
96
|
+
end
|
97
|
+
|
98
|
+
# Import `x-datadog-tags` header tags as trace distributed tags.
|
99
|
+
# Only tags that have the `_dd.p.` prefix are processed.
|
100
|
+
def extract_tags(headers)
|
101
|
+
tags_header = headers.header(HTTP_HEADER_TAGS)
|
102
|
+
return unless tags_header
|
103
|
+
|
104
|
+
if ::Datadog.configuration.tracing.x_datadog_tags_max_length <= 0
|
105
|
+
active_trace = Tracing.active_trace
|
106
|
+
active_trace.set_tag('_dd.propagation_error', 'disabled') if active_trace
|
107
|
+
return
|
108
|
+
end
|
109
|
+
|
110
|
+
if tags_header.size > ::Datadog.configuration.tracing.x_datadog_tags_max_length
|
111
|
+
active_trace = Tracing.active_trace
|
112
|
+
active_trace.set_tag('_dd.propagation_error', 'extract_max_size') if active_trace
|
113
|
+
|
114
|
+
::Datadog.logger.warn(
|
115
|
+
"Failed to extract x-datadog-tags: tags are too large (size:#{tags_header.size} " \
|
116
|
+
"limit:#{::Datadog.configuration.tracing.x_datadog_tags_max_length}). This limit can be configured " \
|
117
|
+
'through the DD_TRACE_X_DATADOG_TAGS_MAX_LENGTH environment variable.'
|
118
|
+
)
|
119
|
+
return
|
120
|
+
end
|
121
|
+
|
122
|
+
tags = DatadogTagsCodec.decode(tags_header)
|
123
|
+
# Only extract keys with the expected Datadog prefix
|
124
|
+
tags.select! do |key, _|
|
125
|
+
key.start_with?(Tracing::Metadata::Ext::Distributed::TAGS_PREFIX) && key != EXCLUDED_TAG
|
126
|
+
end
|
127
|
+
tags
|
128
|
+
rescue => e
|
129
|
+
active_trace = Tracing.active_trace
|
130
|
+
active_trace.set_tag('_dd.propagation_error', 'decoding_error') if active_trace
|
131
|
+
::Datadog.logger.warn(
|
132
|
+
"Failed to extract x-datadog-tags: #{e.class.name} #{e.message} at #{Array(e.backtrace).first}"
|
133
|
+
)
|
134
|
+
end
|
25
135
|
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
trace_id = headers.id(HTTP_HEADER_TRACE_ID)
|
30
|
-
parent_id = headers.id(HTTP_HEADER_PARENT_ID)
|
31
|
-
origin = headers.header(HTTP_HEADER_ORIGIN)
|
32
|
-
sampling_priority = headers.number(HTTP_HEADER_SAMPLING_PRIORITY)
|
33
|
-
|
34
|
-
# Return early if this propagation is not valid
|
35
|
-
# DEV: To be valid we need to have a trace id and a parent id
|
36
|
-
# or when it is a synthetics trace, just the trace id.
|
37
|
-
# DEV: `Parser#id` will not return 0
|
38
|
-
return unless (trace_id && parent_id) || (origin && trace_id)
|
39
|
-
|
40
|
-
# Return new trace headers
|
41
|
-
TraceDigest.new(
|
42
|
-
span_id: parent_id,
|
43
|
-
trace_id: trace_id,
|
44
|
-
trace_origin: origin,
|
45
|
-
trace_sampling_priority: sampling_priority
|
46
|
-
)
|
136
|
+
# We want to exclude tags that we don't want to propagate downstream.
|
137
|
+
EXCLUDED_TAG = '_dd.p.upstream_services'
|
138
|
+
private_constant :EXCLUDED_TAG
|
47
139
|
end
|
48
140
|
end
|
49
141
|
end
|
@@ -12,6 +12,8 @@ module Datadog
|
|
12
12
|
HTTP_HEADER_PARENT_ID = 'x-datadog-parent-id'.freeze
|
13
13
|
HTTP_HEADER_SAMPLING_PRIORITY = 'x-datadog-sampling-priority'.freeze
|
14
14
|
HTTP_HEADER_ORIGIN = 'x-datadog-origin'.freeze
|
15
|
+
# Distributed trace-level tags
|
16
|
+
HTTP_HEADER_TAGS = 'x-datadog-tags'.freeze
|
15
17
|
|
16
18
|
# B3 headers used for distributed tracing
|
17
19
|
B3_HEADER_TRACE_ID = 'x-b3-traceid'.freeze
|