Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Roda Integration #2368

Merged
merged 47 commits into from
Mar 15, 2023
Merged
Show file tree
Hide file tree
Changes from 20 commits
Commits
Show all changes
47 commits
Select commit Hold shift + click to select a range
de8db34
Initial integration of Roda.
randy-girard Mar 13, 2019
3d03eda
Add roda to Appraisals and Rakefile. Start writing tests for Roda int…
wantsui Jul 9, 2019
910d7fe
Add and refactor instrumentation module to Roda instead of Base Insta…
wantsui Jul 10, 2019
9114586
Update default tagging for http url and method. Add tests for various…
wantsui Jul 10, 2019
403831a
Start adding tags for trace search and analytics and add unit tests.
wantsui Jul 11, 2019
c8953fd
Update span type to reference from updated EXT file, add additional t…
wantsui Jul 11, 2019
352d1cb
Fix the tagging for the analytics option and add additional tests for…
wantsui Jul 12, 2019
68d3b5d
Refactor tests. Add distributed tracing to Roda integration. Update u…
wantsui Jul 15, 2019
72553ad
Start to refactor unit tests, remove unused variables and constants f…
wantsui Jul 16, 2019
ab7d7fe
Continue to refactor unit tests and add private method for distribute…
wantsui Jul 16, 2019
6103e1f
Add unit tests for analytics.
wantsui Jul 16, 2019
fef14ff
Move analytics examples to unit tests and add logic to check for Ruby…
wantsui Jul 17, 2019
76c4a38
Update formatting for linting
wantsui Jul 17, 2019
586fd57
Update instrumentation to handle new dispatch api. Refactor unit test…
wantsui Jul 19, 2019
5016c7b
Refactor code for formatting.
wantsui Jul 19, 2019
4c74c4b
Remove duplicated server error example and add http status code tag.
wantsui Jul 19, 2019
f8c0c32
Rewrite Roda integration for 1.x by replacing pin with prepend in the…
wantsui Nov 8, 2022
1462d1e
Rewrite unit tests to work with 1.x updates.
wantsui Nov 15, 2022
aa1db97
Update instrumentation to account for errors where Roda does not have…
wantsui Dec 13, 2022
d16dbd5
Add Roda to the GettingStarted Docs
wantsui Dec 13, 2022
a9db21c
Merge branch 'master' into add-roda-rebase
marcotc Dec 20, 2022
2342811
Update minimum and maximum versions, add integration spec tests, and …
wantsui Dec 20, 2022
41be369
remove content header
wantsui Dec 20, 2022
585b862
WIP
TonyCTHsu Dec 20, 2022
affbc7a
Add missing end block.
wantsui Dec 20, 2022
4f107a0
Revert error logic back to previous assumption and update patcher spe…
wantsui Dec 27, 2022
3061065
Add changes for linter.
wantsui Dec 27, 2022
fe099ac
Update roda files for sorbet type checks.
wantsui Dec 27, 2022
af40cb6
Run install_appraisal_gemfiles with the latest Appraisal file.
wantsui Jan 3, 2023
c3804ba
Merge latest changes.
wantsui Jan 3, 2023
edb4f2b
Merge branch 'master' into add-roda-rebase
wantsui Jan 17, 2023
4555d3d
Add frozen literal comment for rubocop
wantsui Jan 17, 2023
0aff08c
Remove frozen string and relace with typed ignore instead.
wantsui Jan 17, 2023
901297b
Update for rubocop frozen literal and sorbet type.
wantsui Jan 17, 2023
8510779
Import gemfiles from master branch to avoid merge conflict
ivoanjo Jan 24, 2023
426f6cf
Merge branch 'master' into add-roda-rebase
ivoanjo Jan 24, 2023
78237cc
Minor: Tweak spacing in docs table
ivoanjo Jan 24, 2023
c66542d
Merge branch 'master' into add-roda-rebase
wantsui Feb 21, 2023
b3081e9
Remove default service name in favor of global configuration, remove …
wantsui Feb 28, 2023
9323c6c
Merge branch 'master' into add-roda-rebase
wantsui Feb 28, 2023
e18b260
Merge branch 'master' into add-roda-rebase
wantsui Mar 10, 2023
034eef6
Add roda files to Steepfile and remove empty line.
wantsui Mar 10, 2023
c3a1e2c
Catch only standard error during errors in instrumentation for roda.
wantsui Mar 10, 2023
5aa0e94
Merge branch 'master' into add-roda-rebase
wantsui Mar 10, 2023
29a9aa8
Remove roda from Steepfile.
wantsui Mar 10, 2023
0b0fe25
Merge branch 'master' into add-roda-rebase
wantsui Mar 14, 2023
3682891
Remove Rack TraceMiddleware from being loaded by default. Instead, ap…
wantsui Mar 14, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions Appraisals
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,7 @@ if ruby_version?('2.1')
gem 'redis', '< 4.0'
gem 'rest-client'
gem 'resque', '< 2.0'
gem 'roda'
gem 'ruby-kafka', '>= 0.7.10'
gem 'rspec', '>= 3.0.0'
gem 'semantic_logger', '~> 4.0'
Expand Down Expand Up @@ -295,6 +296,7 @@ elsif ruby_version?('2.2')
gem 'redis', '< 4.0'
gem 'rest-client'
gem 'resque', '< 2.0'
gem 'roda'
gem 'ruby-kafka', '>= 0.7.10'
gem 'rspec', '>= 3.0.0'
gem 'semantic_logger', '~> 4.0'
Expand Down Expand Up @@ -486,6 +488,7 @@ elsif ruby_version?('2.3')
gem 'redis', '< 4.0'
gem 'rest-client'
gem 'resque'
gem 'roda'
gem 'ruby-kafka', '>= 0.7.10'
gem 'rspec', '>= 3.0.0'
gem 'semantic_logger', '~> 4.0'
Expand Down Expand Up @@ -611,6 +614,7 @@ elsif ruby_version?('2.4')
gem 'redis', '< 4.0'
gem 'rest-client'
gem 'resque'
gem 'roda'
gem 'ruby-kafka', '>= 0.7.10'
gem 'rspec', '>= 3.0.0'
gem 'semantic_logger', '~> 4.0'
Expand Down Expand Up @@ -866,6 +870,7 @@ elsif ruby_version?('2.5')
gem 'redis', '< 4.0'
gem 'rest-client'
gem 'resque'
gem 'roda'
gem 'ruby-kafka', '>= 0.7.10'
gem 'rspec', '>= 3.0.0'
gem 'semantic_logger', '~> 4.0'
Expand Down Expand Up @@ -1097,6 +1102,7 @@ elsif ruby_version?('2.6')
gem 'redis', '< 4.0'
gem 'rest-client'
gem 'resque'
gem 'roda'
gem 'ruby-kafka', '>= 0.7.10'
gem 'rspec', '>= 3.0.0'
gem 'semantic_logger', '~> 4.0'
Expand Down Expand Up @@ -1308,6 +1314,7 @@ elsif ruby_version?('2.7')
gem 'redis', '< 4.0'
gem 'rest-client'
gem 'resque'
gem 'roda'
gem 'ruby-kafka', '>= 0.7.10'
gem 'rspec', '>= 3.0.0'
gem 'sequel'
Expand Down Expand Up @@ -1424,6 +1431,7 @@ elsif ruby_version?('3.0') || ruby_version?('3.1')
gem 'redis', '< 4.0'
gem 'rest-client'
gem 'resque'
gem 'roda'
gem 'ruby-kafka', '>= 0.7.10'
gem 'rspec', '>= 3.0.0'
gem 'semantic_logger', '~> 4.0'
Expand Down Expand Up @@ -1540,6 +1548,7 @@ elsif ruby_version?('3.2')
gem 'redis', '< 4.0'
gem 'rest-client'
gem 'resque'
gem 'roda'
gem 'ruby-kafka', '>= 0.7.10'
gem 'rspec', '>= 3.0.0'
gem 'semantic_logger', '~> 4.0'
Expand Down
2 changes: 2 additions & 0 deletions Rakefile
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,7 @@ namespace :spec do
:rake,
:redis,
:resque,
:roda,
:rest_client,
:semantic_logger,
:sequel,
Expand Down Expand Up @@ -308,6 +309,7 @@ task :ci do
declare '✅ 2.1 / ✅ 2.2 / ✅ 2.3 / ✅ 2.4 / ✅ 2.5 / ✅ 2.6 / ✅ 2.7 / ✅ 3.0 / ✅ 3.1 / ✅ 3.2 / ✅ jruby' => 'bundle exec appraisal contrib rake spec:redis'
declare '✅ 2.1 / ✅ 2.2 / ✅ 2.3 / ✅ 2.4 / ✅ 2.5 / ✅ 2.6 / ✅ 2.7 / ✅ 3.0 / ✅ 3.1 / ✅ 3.2 / ✅ jruby' => 'bundle exec appraisal contrib rake spec:resque'
declare '✅ 2.1 / ✅ 2.2 / ✅ 2.3 / ✅ 2.4 / ✅ 2.5 / ✅ 2.6 / ✅ 2.7 / ✅ 3.0 / ✅ 3.1 / ✅ 3.2 / ✅ jruby' => 'bundle exec appraisal contrib rake spec:rest_client'
declare '✅ 2.1 / ✅ 2.2 / ✅ 2.3 / ✅ 2.4 / ✅ 2.5 / ✅ 2.6 / ✅ 2.7 / ✅ 3.0 / ✅ 3.1 / ✅ 3.2 / ✅ jruby' => 'bundle exec appraisal contrib rake spec:roda'
declare '✅ 2.1 / ✅ 2.2 / ✅ 2.3 / ✅ 2.4 / ✅ 2.5 / ✅ 2.6 / ✅ 2.7 / ✅ 3.0 / ✅ 3.1 / ✅ 3.2 / ✅ jruby' => 'bundle exec appraisal contrib rake spec:rspec'
declare '✅ 2.1 / ✅ 2.2 / ✅ 2.3 / ✅ 2.4 / ✅ 2.5 / ✅ 2.6 / ✅ 2.7 / ✅ 3.0 / ✅ 3.1 / ✅ 3.2 / ✅ jruby' => 'bundle exec appraisal contrib rake spec:semantic_logger'
declare '✅ 2.1 / ✅ 2.2 / ✅ 2.3 / ✅ 2.4 / ✅ 2.5 / ✅ 2.6 / ✅ 2.7 / ✅ 3.0 / ✅ 3.1 / ✅ 3.2 / ✅ jruby' => 'bundle exec appraisal contrib rake spec:sequel'
Expand Down
24 changes: 24 additions & 0 deletions docs/GettingStarted.md
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ To contribute, check out the [contribution guidelines][contribution docs] and [d
- [Redis](#redis)
- [Resque](#resque)
- [Rest Client](#rest-client)
- [Roda](#roda)
- [RSpec](#rspec)
- [Sequel](#sequel)
- [Shoryuken](#shoryuken)
Expand Down Expand Up @@ -510,6 +511,7 @@ For a list of available integrations, and their configuration options, please re
| Redis | `redis` | `>= 3.2` | `>= 3.2` | *[Link](#redis)* | *[Link](https://github.com/redis/redis-rb)* |
| Resque | `resque` | `>= 1.0` | `>= 1.0` | *[Link](#resque)* | *[Link](https://github.com/resque/resque)* |
| Rest Client | `rest-client` | `>= 1.8` | `>= 1.8` | *[Link](#rest-client)* | *[Link](https://github.com/rest-client/rest-client)* |
| Roda | `roda` | `>= 2.1` | `>= 2.1` | *[Link](#roda)* | *[Link](https://github.com/jeremyevans/roda)* |
| Sequel | `sequel` | `>= 3.41` | `>= 3.41` | *[Link](#sequel)* | *[Link](https://github.com/jeremyevans/sequel)* |
| Shoryuken | `shoryuken` | `>= 3.2` | `>= 3.2` | *[Link](#shoryuken)* | *[Link](https://github.com/phstc/shoryuken)* |
| Sidekiq | `sidekiq` | `>= 3.5.4` | `>= 3.5.4` | *[Link](#sidekiq)* | *[Link](https://github.com/mperham/sidekiq)* |
Expand Down Expand Up @@ -1818,6 +1820,28 @@ end
| `service_name` | Service name for `rest_client` instrumentation. | `'rest_client'` |
| `split_by_domain` | Uses the request domain as the service name when set to `true`. | `false` |

### Roda

The Roda integration traces requests.

It can be enabled through `Datadog.configure`:

```ruby
require 'roda'
require 'ddtrace'

Datadog.configure do |c|
c.tracing.instrument :roda, **options
end
```

`options` are the following keyword arguments:

| Key | Description | Default |
| --- | ----------- | ------- |
| `distributed_tracing` | Enables [distributed tracing](#distributed-tracing) | `true` |
| `service_name` | Service name for `roda` instrumentation. | `'roda'` |

### RSpec

RSpec integration will trace all executions of example groups and examples when using `rspec` test framework.
Expand Down
1 change: 1 addition & 0 deletions lib/datadog/tracing/contrib.rb
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ module Contrib
require_relative 'contrib/redis/integration'
require_relative 'contrib/resque/integration'
require_relative 'contrib/rest_client/integration'
require_relative 'contrib/roda/integration'
require_relative 'contrib/semantic_logger/integration'
require_relative 'contrib/sequel/integration'
require_relative 'contrib/shoryuken/integration'
Expand Down
33 changes: 33 additions & 0 deletions lib/datadog/tracing/contrib/roda/configuration/settings.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
require_relative '../../configuration/settings'
require_relative '../ext'

module Datadog
module Tracing
module Contrib
module Roda
module Configuration
# Custom settings for the Roda integration
class Settings < Contrib::Configuration::Settings
option :enabled do |o|
o.default { env_to_bool(Ext::ENV_ENABLED, true) }
o.lazy
end

option :analytics_enabled do |o|
o.default { env_to_bool(Ext::ENV_ANALYTICS_ENABLED, false) }
o.lazy
end

option :analytics_sample_rate do |o|
o.default { env_to_float(Ext::ENV_ANALYTICS_SAMPLE_RATE, 1.0) }
o.lazy
end

option :distributed_tracing, default: true
marcotc marked this conversation as resolved.
Show resolved Hide resolved
option :service_name, default: Ext::SERVICE_NAME
end
end
end
end
end
end
18 changes: 18 additions & 0 deletions lib/datadog/tracing/contrib/roda/ext.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
module Datadog
module Tracing
module Contrib
module Roda
# Roda integration constants
module Ext
APP = 'roda'.freeze
ENV_ENABLED = 'DD_TRACE_RODA_ENABLED'.freeze
ENV_ANALYTICS_ENABLED = 'DD_RODA_ANALYTICS_ENABLED'.freeze
ENV_ANALYTICS_SAMPLE_RATE = 'DD_RODA_ANALYTICS_SAMPLE_RATE'.freeze

SERVICE_NAME = 'roda'.freeze
SPAN_REQUEST = 'roda.request'.freeze
end
end
end
end
end
84 changes: 84 additions & 0 deletions lib/datadog/tracing/contrib/roda/instrumentation.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
require_relative '../../metadata/ext'
require_relative 'ext'
require_relative '../analytics'

module Datadog
module Tracing
module Contrib
module Roda
# Instrumentation for Roda
module Instrumentation

def _roda_handle_main_route
instrument(Ext::SPAN_REQUEST) { super }
end

def call
instrument(Ext::SPAN_REQUEST) { super }
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can move the error handling logic to the caller

          def call
            instrument(Ext::SPAN_REQUEST) do |span|
              super
            rescue => e
              # ... Handle error
            end
          end

So you could be confident yielding in the block without nested begin/rescue.

response = yield span

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It looks like in the Roda code, dispatch can land on one of these methods (seen in this conditional)

Would I need to duplicate the rescue block and handle the error in both since either one of these can handle the dispatch?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As an update to this, I wasn't able to move the error handling logic anywhere else because it interfered with the errors produced by the underlying app.

end

private

def instrument(span_name, &block)
set_distributed_tracing_context!(request.env)

Tracing.trace(span_name) do |span|
begin
request_method = request.request_method.to_s.upcase

span.service = configuration[:service_name]
span.span_type = Tracing::Metadata::Ext::HTTP::TYPE_INBOUND

# Using the http method as a resource, since the URL/path can trigger
# a possibly infinite number of resources.
span.resource = request_method

span.set_tag(Tracing::Metadata::Ext::HTTP::TAG_URL, request.path)
span.set_tag(Tracing::Metadata::Ext::HTTP::TAG_METHOD, request_method)

# Add analytics tag to the span
if Contrib::Analytics.enabled?(configuration[:analytics_enabled])
Contrib::Analytics.set_sample_rate(span, configuration[:analytics_sample_rate])
end

# Measure service stats
Contrib::Analytics.set_measured(span)

ensure
begin
response = yield
rescue => e
# The status code is unknown to Roda and decided by the upstream web runner.
# In this case, spans default to status code 500 rather than a blank status code.
default_error_status = '500'
span.resource = "#{request_method} #{default_error_status}"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Possibly strange edge case here where request.request_method.to_s.upcase raised an error, hit the ensure block, request raised an error, caught by this rescue block, then request_method will likely be undefined, possibly raising another error.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure how this error can be thrown because all routes should be defined with a type from the start. Do you have a suggestion for handling this scenario?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I paired with Wan and it's not possible for request.request_method.to_s.upcase to fail, as it comes straight from Rack and it's part of the Rack contract. This is the same behaviour of our other Rack-ralated integrations (Sinatra, Rails, Hanami, Grace)

span.set_tag(Tracing::Metadata::Ext::HTTP::TAG_STATUS_CODE, default_error_status)
raise
end
end

status_code = response[0]

# Adds status code to the resource name once the resource comes back
span.resource = "#{request_method} #{status_code}"
marcotc marked this conversation as resolved.
Show resolved Hide resolved
span.set_tag(Tracing::Metadata::Ext::HTTP::TAG_STATUS_CODE, status_code)
span.status = 1 if status_code.to_s.start_with?('5')
response
end
end

def configuration
Datadog.configuration.tracing[:roda]
end

def set_distributed_tracing_context!(env)
if configuration[:distributed_tracing]
trace_digest = Tracing::Propagation::HTTP.extract(env)
Tracing.continue_trace!(trace_digest)
end
end
end
end
end
end
end
38 changes: 38 additions & 0 deletions lib/datadog/tracing/contrib/roda/integration.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
require_relative '../integration'
require_relative 'configuration/settings'
require_relative 'patcher'

module Datadog
module Tracing
module Contrib
module Roda
# Description of Roda integration
class Integration
include Contrib::Integration

register_as :roda

def self.version
Gem.loaded_specs['roda'] && Gem.loaded_specs['roda'].version
end

def self.loaded?
!defined?(::Roda).nil?
end

def self.compatible?
super && version >= Gem::Version.new('2.0.0')
end

def new_configuration
Configuration::Settings.new
end

def patcher
Patcher
end
end
end
end
end
end
27 changes: 27 additions & 0 deletions lib/datadog/tracing/contrib/roda/patcher.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
require_relative 'patcher'
require_relative 'ext'
require_relative 'instrumentation'

module Datadog
module Tracing
module Contrib
module Roda
# Patcher enables patching of 'roda'
module Patcher
include Contrib::Patcher

module_function

def target_version
Integration.version
end

def patch
::Roda::prepend(Instrumentation)
end

end
end
end
end
end
Loading