From dacba60a25086142e1cb144b3ee2b1b27195dac5 Mon Sep 17 00:00:00 2001 From: David Elner Date: Mon, 26 Mar 2018 17:29:22 -0400 Subject: [PATCH] Added: URL quantization to Rack. --- docs/GettingStarted.md | 35 +++++++++++++++++++++++++ lib/ddtrace/contrib/rack/middlewares.rb | 3 ++- lib/ddtrace/contrib/rack/patcher.rb | 1 + test/contrib/rack/middleware_test.rb | 29 ++++++++++++++++++++ 4 files changed, 67 insertions(+), 1 deletion(-) diff --git a/docs/GettingStarted.md b/docs/GettingStarted.md index 89957cd98ea..d6d9f5fdcae 100644 --- a/docs/GettingStarted.md +++ b/docs/GettingStarted.md @@ -142,9 +142,44 @@ Where `options` is an optional `Hash` that accepts the following parameters: | ``service_name`` | Service name used when tracing application requests | rack | | ``distributed_tracing`` | Enables [distributed tracing](#Distributed_Tracing) so that this service trace is connected with a trace of another service if tracing headers are received | `false` | | ``middleware_names`` | Enable this if you want to use the middleware classes as the resource names for `rack` spans. Must provide the ``application`` option with it. | ``false`` | +| ``quantize`` | Hash containing options for quantization. May include `:query` or `:fragment`. | {} | +| ``quantize.query`` | Hash containing options for query portion of URL quantization. May include `:show` or `:exclude`. See options below. Option must be nested inside the `quantize` option. | {} | +| ``quantize.query.show`` | Defines which values should always be shown. Shows no values by default. May be an Array of strings, or `:all` to show all values. Option must be nested inside the `query` option. | ``nil`` | +| ``quantize.query.exclude`` | Defines which values should be removed entirely. Excludes nothing by default. May be an Array of strings, or `:all` to remove the query string entirely. Option must be nested inside the `query` option. | ``nil`` | +| ``quantize.fragment`` | Defines behavior for URL fragments. Removes fragments by default. May be `:show` to show URL fragments. Option must be nested inside the `quantize` option. | ``nil`` | | ``application`` | Your Rack application. Necessary for enabling middleware resource names. | ``nil`` | | ``tracer`` | A ``Datadog::Tracer`` instance used to instrument the application. Usually you don't need to set that. | ``Datadog.tracer`` | +Configuring URL quantization behavior: + +``` +Datadog.configure do |c| + # Default behavior: all values are quantized, fragment is removed. + # http://example.com/path?category_id=1&sort_by=asc#featured --> http://example.com/path?category_id&sort_by + # http://example.com/path?categories[]=1&categories[]=2 --> http://example.com/path?categories[] + + # Show values for any query string parameter matching 'category_id' exactly + # http://example.com/path?category_id=1&sort_by=asc#featured --> http://example.com/path?category_id=1&sort_by + c.use :rack, quantize: { query: { show: ['category_id'] } } + + # Show all values for all query string parameters + # http://example.com/path?category_id=1&sort_by=asc#featured --> http://example.com/path?category_id=1&sort_by=asc + c.use :rack, quantize: { query: { show: :all } } + + # Totally exclude any query string parameter matching 'sort_by' exactly + # http://example.com/path?category_id=1&sort_by=asc#featured --> http://example.com/path?category_id + c.use :rack, quantize: { query: { exclude: ['sort_by'] } } + + # Remove the query string entirely + # http://example.com/path?category_id=1&sort_by=asc#featured --> http://example.com/path + c.use :rack, quantize: { query: { exclude: :all } } + + # Show URL fragments + # http://example.com/path?category_id=1&sort_by=asc#featured --> http://example.com/path?category_id&sort_by#featured + c.use :rack, quantize: { fragment: :show } +end +``` + ## Other libraries ### GraphQL diff --git a/lib/ddtrace/contrib/rack/middlewares.rb b/lib/ddtrace/contrib/rack/middlewares.rb index 0818e3c1145..0b05d0242bc 100644 --- a/lib/ddtrace/contrib/rack/middlewares.rb +++ b/lib/ddtrace/contrib/rack/middlewares.rb @@ -104,7 +104,8 @@ def set_request_tags!(request_span, env, status, headers, response, original_env request_span.set_tag(Datadog::Ext::HTTP::METHOD, env['REQUEST_METHOD']) end if request_span.get_tag(Datadog::Ext::HTTP::URL).nil? - request_span.set_tag(Datadog::Ext::HTTP::URL, url) + options = Datadog.configuration[:rack][:quantize] + request_span.set_tag(Datadog::Ext::HTTP::URL, Datadog::Quantization::HTTP.url(url, options)) end if request_span.get_tag(Datadog::Ext::HTTP::BASE_URL).nil? request_obj = ::Rack::Request.new(env) diff --git a/lib/ddtrace/contrib/rack/patcher.rb b/lib/ddtrace/contrib/rack/patcher.rb index cc466e0dc10..50649916be1 100644 --- a/lib/ddtrace/contrib/rack/patcher.rb +++ b/lib/ddtrace/contrib/rack/patcher.rb @@ -8,6 +8,7 @@ module Patcher option :tracer, default: Datadog.tracer option :distributed_tracing, default: false option :middleware_names, default: false + option :quantize, default: {} option :application option :service_name, default: 'rack', depends_on: [:tracer] do |value| get_option(:tracer).set_service_info(value, 'rack', Ext::AppTypes::WEB) diff --git a/test/contrib/rack/middleware_test.rb b/test/contrib/rack/middleware_test.rb index 521757f2cfa..c8221a7ad7a 100644 --- a/test/contrib/rack/middleware_test.rb +++ b/test/contrib/rack/middleware_test.rb @@ -64,6 +64,35 @@ def test_request_middleware_get_with_request_uri assert_equal('200', span.get_tag('http.status_code')) # Since REQUEST_URI is set (usually provided by WEBrick/Puma) # it uses REQUEST_URI, which has query string parameters. + # However, that query string will be quantized. + assert_equal('/success?foo', span.get_tag('http.url')) + assert_equal('http://example.org', span.get_tag('http.base_url')) + assert_equal(0, span.status) + assert_nil(span.parent) + end + + def test_request_middleware_get_with_request_uri_and_quantize_option + Datadog.configure do |c| + c.use :rack, quantize: { query: { show: ['foo'] } } + end + + # ensure the Rack request is properly traced + get '/success?foo=bar', {}, 'REQUEST_URI' => '/success?foo=bar' + assert last_response.ok? + + spans = @tracer.writer.spans + assert_equal(1, spans.length) + + span = spans[0] + assert_equal('rack.request', span.name) + assert_equal('http', span.span_type) + assert_equal('rack', span.service) + assert_equal('GET 200', span.resource) + assert_equal('GET', span.get_tag('http.method')) + assert_equal('200', span.get_tag('http.status_code')) + # Since REQUEST_URI is set (usually provided by WEBrick/Puma) + # it uses REQUEST_URI, which has query string parameters. + # However, that query string will be quantized. assert_equal('/success?foo=bar', span.get_tag('http.url')) assert_equal('http://example.org', span.get_tag('http.base_url')) assert_equal(0, span.status)