From ebaa0d0532abcd29683a7de8410c5a8afa10bf90 Mon Sep 17 00:00:00 2001 From: Wu Date: Tue, 9 Jan 2018 11:15:31 -0800 Subject: [PATCH] initial commit --- .gitignore | 16 + .yardopts | 5 + Gemfile | 3 + LICENSE | 1 - NOTICE | 5 +- README.md | 188 +++++++++- Rakefile | 23 ++ aws-xray-sdk.gemspec | 31 ++ images/example_servicemap.png | Bin 0 -> 220346 bytes lib/aws-xray-sdk.rb | 10 + lib/aws-xray-sdk/configuration.rb | 158 ++++++++ lib/aws-xray-sdk/context/context.rb | 26 ++ lib/aws-xray-sdk/context/default_context.rb | 81 +++++ lib/aws-xray-sdk/emitter/default_emitter.rb | 53 +++ lib/aws-xray-sdk/emitter/emitter.rb | 24 ++ lib/aws-xray-sdk/exceptions.rb | 31 ++ lib/aws-xray-sdk/facets/aws_sdk.rb | 127 +++++++ lib/aws-xray-sdk/facets/helper.rb | 61 ++++ lib/aws-xray-sdk/facets/net_http.rb | 61 ++++ lib/aws-xray-sdk/facets/rack.rb | 87 +++++ .../facets/rails/active_record.rb | 66 ++++ .../facets/rails/ex_middleware.rb | 24 ++ lib/aws-xray-sdk/facets/rails/railtie.rb | 23 ++ .../facets/resources/aws_params_whitelist.rb | 340 ++++++++++++++++++ .../resources/aws_services_whitelist.rb | 147 ++++++++ lib/aws-xray-sdk/logger.rb | 19 + lib/aws-xray-sdk/model/annotations.rb | 97 +++++ lib/aws-xray-sdk/model/cause.rb | 70 ++++ lib/aws-xray-sdk/model/dummy_entities.rb | 72 ++++ lib/aws-xray-sdk/model/entity.rb | 187 ++++++++++ lib/aws-xray-sdk/model/metadata.rb | 77 ++++ lib/aws-xray-sdk/model/segment.rb | 63 ++++ lib/aws-xray-sdk/model/subsegment.rb | 67 ++++ lib/aws-xray-sdk/model/trace_header.rb | 54 +++ lib/aws-xray-sdk/patcher.rb | 21 ++ lib/aws-xray-sdk/plugins/ec2.rb | 39 ++ lib/aws-xray-sdk/plugins/ecs.rb | 23 ++ lib/aws-xray-sdk/plugins/elastic_beanstalk.rb | 25 ++ lib/aws-xray-sdk/recorder.rb | 209 +++++++++++ lib/aws-xray-sdk/sampling/default_sampler.rb | 105 ++++++ lib/aws-xray-sdk/sampling/reservoir.rb | 35 ++ lib/aws-xray-sdk/sampling/sampler.rb | 27 ++ lib/aws-xray-sdk/sampling/sampling_rule.rb | 57 +++ lib/aws-xray-sdk/search_pattern.rb | 82 +++++ .../segment_naming/dynamic_naming.rb | 26 ++ .../segment_naming/segment_naming.rb | 10 + .../streaming/default_streamer.rb | 53 +++ lib/aws-xray-sdk/streaming/streamer.rb | 17 + lib/aws-xray-sdk/version.rb | 3 + test/aws-xray-sdk/tc_aws_sdk.rb | 113 ++++++ test/aws-xray-sdk/tc_cause.rb | 87 +++++ test/aws-xray-sdk/tc_context.rb | 42 +++ test/aws-xray-sdk/tc_dummy_entities.rb | 45 +++ test/aws-xray-sdk/tc_dynamic_naming.rb | 30 ++ test/aws-xray-sdk/tc_emitter.rb | 25 ++ test/aws-xray-sdk/tc_plugin.rb | 42 +++ test/aws-xray-sdk/tc_recorder.rb | 143 ++++++++ test/aws-xray-sdk/tc_sampling.rb | 115 ++++++ test/aws-xray-sdk/tc_search_pattern.rb | 40 +++ test/aws-xray-sdk/tc_segment.rb | 140 ++++++++ test/aws-xray-sdk/tc_streaming.rb | 88 +++++ test/aws-xray-sdk/tc_subsegment.rb | 56 +++ test/aws-xray-sdk/tc_trace_header.rb | 63 ++++ test/test_helper.rb | 26 ++ 64 files changed, 4079 insertions(+), 5 deletions(-) create mode 100644 .gitignore create mode 100644 .yardopts create mode 100644 Gemfile create mode 100644 Rakefile create mode 100644 aws-xray-sdk.gemspec create mode 100644 images/example_servicemap.png create mode 100644 lib/aws-xray-sdk.rb create mode 100644 lib/aws-xray-sdk/configuration.rb create mode 100644 lib/aws-xray-sdk/context/context.rb create mode 100644 lib/aws-xray-sdk/context/default_context.rb create mode 100644 lib/aws-xray-sdk/emitter/default_emitter.rb create mode 100644 lib/aws-xray-sdk/emitter/emitter.rb create mode 100644 lib/aws-xray-sdk/exceptions.rb create mode 100644 lib/aws-xray-sdk/facets/aws_sdk.rb create mode 100644 lib/aws-xray-sdk/facets/helper.rb create mode 100644 lib/aws-xray-sdk/facets/net_http.rb create mode 100644 lib/aws-xray-sdk/facets/rack.rb create mode 100644 lib/aws-xray-sdk/facets/rails/active_record.rb create mode 100644 lib/aws-xray-sdk/facets/rails/ex_middleware.rb create mode 100644 lib/aws-xray-sdk/facets/rails/railtie.rb create mode 100644 lib/aws-xray-sdk/facets/resources/aws_params_whitelist.rb create mode 100644 lib/aws-xray-sdk/facets/resources/aws_services_whitelist.rb create mode 100644 lib/aws-xray-sdk/logger.rb create mode 100644 lib/aws-xray-sdk/model/annotations.rb create mode 100644 lib/aws-xray-sdk/model/cause.rb create mode 100644 lib/aws-xray-sdk/model/dummy_entities.rb create mode 100644 lib/aws-xray-sdk/model/entity.rb create mode 100644 lib/aws-xray-sdk/model/metadata.rb create mode 100644 lib/aws-xray-sdk/model/segment.rb create mode 100644 lib/aws-xray-sdk/model/subsegment.rb create mode 100644 lib/aws-xray-sdk/model/trace_header.rb create mode 100644 lib/aws-xray-sdk/patcher.rb create mode 100644 lib/aws-xray-sdk/plugins/ec2.rb create mode 100644 lib/aws-xray-sdk/plugins/ecs.rb create mode 100644 lib/aws-xray-sdk/plugins/elastic_beanstalk.rb create mode 100644 lib/aws-xray-sdk/recorder.rb create mode 100644 lib/aws-xray-sdk/sampling/default_sampler.rb create mode 100644 lib/aws-xray-sdk/sampling/reservoir.rb create mode 100644 lib/aws-xray-sdk/sampling/sampler.rb create mode 100644 lib/aws-xray-sdk/sampling/sampling_rule.rb create mode 100644 lib/aws-xray-sdk/search_pattern.rb create mode 100644 lib/aws-xray-sdk/segment_naming/dynamic_naming.rb create mode 100644 lib/aws-xray-sdk/segment_naming/segment_naming.rb create mode 100644 lib/aws-xray-sdk/streaming/default_streamer.rb create mode 100644 lib/aws-xray-sdk/streaming/streamer.rb create mode 100644 lib/aws-xray-sdk/version.rb create mode 100644 test/aws-xray-sdk/tc_aws_sdk.rb create mode 100644 test/aws-xray-sdk/tc_cause.rb create mode 100644 test/aws-xray-sdk/tc_context.rb create mode 100644 test/aws-xray-sdk/tc_dummy_entities.rb create mode 100644 test/aws-xray-sdk/tc_dynamic_naming.rb create mode 100644 test/aws-xray-sdk/tc_emitter.rb create mode 100644 test/aws-xray-sdk/tc_plugin.rb create mode 100644 test/aws-xray-sdk/tc_recorder.rb create mode 100644 test/aws-xray-sdk/tc_sampling.rb create mode 100644 test/aws-xray-sdk/tc_search_pattern.rb create mode 100644 test/aws-xray-sdk/tc_segment.rb create mode 100644 test/aws-xray-sdk/tc_streaming.rb create mode 100644 test/aws-xray-sdk/tc_subsegment.rb create mode 100644 test/aws-xray-sdk/tc_trace_header.rb create mode 100644 test/test_helper.rb diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..b22aedb --- /dev/null +++ b/.gitignore @@ -0,0 +1,16 @@ +*.gem +*.rbc + +.bundle +.config +.coverage +.yardoc +Gemfile.lock + +doc/ +pkg/ + +tmp +test/tmp +coverage +vendor/ diff --git a/.yardopts b/.yardopts new file mode 100644 index 0000000..971c6ac --- /dev/null +++ b/.yardopts @@ -0,0 +1,5 @@ +--title 'AWS X-Ray SDK for Ruby' +--markup markdown +--markup-provider rdiscount +--no-progress +- LICENSE diff --git a/Gemfile b/Gemfile new file mode 100644 index 0000000..fa75df1 --- /dev/null +++ b/Gemfile @@ -0,0 +1,3 @@ +source 'https://rubygems.org' + +gemspec diff --git a/LICENSE b/LICENSE index d645695..261eeb9 100644 --- a/LICENSE +++ b/LICENSE @@ -1,4 +1,3 @@ - Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ diff --git a/NOTICE b/NOTICE index f883730..2971941 100644 --- a/NOTICE +++ b/NOTICE @@ -1,2 +1,3 @@ -AWS Xray SDK Ruby -Copyright 2017 Amazon.com, Inc. or its affiliates. All Rights Reserved. +AWS X-Ray SDK for Ruby +Copyright 2017 Amazon.com, Inc. or its affiliates. All Rights Reserved. + diff --git a/README.md b/README.md index 794dcb8..d80ba7a 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,189 @@ -AWS Xray SDK Ruby +# AWS X-Ray SDK for Ruby (beta) + +![Screenshot of the AWS X-Ray console](/images/example_servicemap.png?raw=true) + +## Installing + +The AWS X-Ray SDK for Ruby is compatible with Ruby 2.3.6 and newer Ruby versions. + +To install the Ruby gem for your project, add it to your project Gemfile. + +``` +# Gemfile +gem 'aws-xray-sdk' +``` +Then run `bundle install`. + +## Getting Help + +Use the following community resources for getting help with the SDK. We use the GitHub +issues for tracking bugs and feature requests. + +* Ask a question in the [AWS X-Ray Forum](https://forums.aws.amazon.com/forum.jspa?forumID=241&start=0). +* Open a support ticket with [AWS Support](http://docs.aws.amazon.com/awssupport/latest/user/getting-started.html). +* If you think you may have found a bug, open an [issue](https://github.com/aws/aws-xray-sdk-ruby/issues/new). + +## Opening Issues + +If you encounter a bug with the AWS X-Ray SDK for Ruby, we want to hear about +it. Before opening a new issue, search the [existing issues](https://github.com/aws/aws-xray-sdk-ruby/issues) +to see if others are also experiencing the issue. Include the version of the AWS X-Ray +SDK for Ruby, Ruby language, and other gems if applicable. In addition, +include the repro case when appropriate. + +The GitHub issues are intended for bug reports and feature requests. For help and +questions about using the AWS SDK for Ruby, use the resources listed +in the [Getting Help](https://github.com/aws/aws-xray-sdk-ruby#getting-help) section. Keeping the list of open issues lean helps us respond in a timely manner. + +## Documentation + +The [developer guide](https://docs.aws.amazon.com/xray/latest/devguide) provides in-depth guidance about using the AWS X-Ray service. +The [API Reference](http://docs.aws.amazon.com/xray-sdk-for-ruby/latest/reference/) provides documentation for public APIs of all classes in the SDK. + +## Quick Start + +**Configuration** + +```ruby +require 'aws-xray-sdk' + +# configure path based sampling rules in case of web app +my_sampling_rules = { + version: 1, + rules: [ + { + description: 'healthcheck', + service_name: '*', + http_method: 'GET', + url_path: '/ping', + fixed_target: 0, + rate: 0 + } + ], + default: { + fixed_target: 1, + rate: 0.2 + } +} + +user_config = { + sampling: true, + sampling_rules: my_sampling_rules, + name: 'default_segment_name', + daemon_address: '127.0.0.1:3000', + context_missing: 'LOG_ERROR', + patch: %I[net_http aws_sdk] +} + +XRay.recorder.configure(user_config) +``` + +**Working with Rails** + +```ruby +# Edit Gemfile to add XRay middleware +gem 'aws-xray-sdk', require: ['aws-xray-sdk/facets/rails/railtie'] + +# Configure the recorder in app_root/config/initializers/aws_xray.rb +Rails.application.config.xray = { + # default segment name generated by XRay middleware + name: 'myrails', + patch: %I[net_http aws_sdk], + # record db transactions as subsegments + active_record: true +} +``` + +**Adding metadata/annotations using recorder** + +```ruby +require 'aws-xray-sdk' + +# Add annotations to the current active entity +XRay.recorder.annotations[:k1] = 'v1' +XRay.recorder.annotations.update k2: 'v2', k3: 3 + +# Add metadata to the current active entity +XRay.recorder.metadata[:k1] = 'v1' # add to default namespace +XRay.recorder.metadata(namespace: :my_ns).update k2: 'v2' + +XRay.recorder.sampled? do + XRay.recorder.metadata.update my_meta # expensive metadata generation here +end +``` + +**Capture** + +```ruby +require 'aws-xray-sdk' + +XRay.recorder.capture('name_for_subsegment') do |subsegment| + resp = myfunc() + subsegment.annotations.update k1: 'v1' + resp +end + +# Manually apply the parent segment for the captured subsegment +XRay.recorder.capture('name_for_subsegment', segment: my_segment) do |subsegment| + myfunc() +end +``` + +**Thread Injection** + +```ruby +require 'aws-xray-sdk' + +XRay.recorder.configure({patch: %I[net_http]}) + +uri = URI.parse('http://aws.amazon.com/') +# Get the active entity from current call context +entity = XRay.recorder.current_entity + +workers = (0...3).map do + Thread.new do + begin + # set X-Ray context for this thread + XRay.recorder.inject_context entity do + http = Net::HTTP.new(uri.host, uri.port) + http.request(Net::HTTP::Get.new(uri.request_uri)) + end + rescue ThreadError + end + end +end + +workers.map(&:join) +``` + +**Start a custom segment/subsegment** + +```ruby +require 'aws-xray-sdk' + +# Start a segment +segment = XRay.recorder.begin_segment 'my_service' +# Start a subsegment +subsegment = XRay.recorder.begin_subsegment 'outbound_call', namespace: 'remote' + +# Add metadata or annotation here if necessary +my_annotations = { + k1: 'v1', + k2: 1024 +} +segment.annotations.update my_annotations + +# Add metadata to default namespace +subsegment.metadata[:k1] = 'v1' + +# Set user for the segment (subsegment is not supported) +segment.user = 'my_name' + +# End segment/subsegment +XRay.recorder.end_subsegment +XRay.recorder.end_segment +``` ## License -This library is licensed under the Apache 2.0 License. +The AWS X-Ray SDK for Ruby is licensed under the Apache 2.0 License. See LICENSE and NOTICE for more information. diff --git a/Rakefile b/Rakefile new file mode 100644 index 0000000..706907a --- /dev/null +++ b/Rakefile @@ -0,0 +1,23 @@ +require 'bundler/gem_tasks' + +Rake::Task['release'].clear + +# default task +task default: %i[yard test] + +require 'rake' +require 'rake/testtask' +require 'yard' + +# tasks +desc 'execute all tests' +Rake::TestTask.new :test do |t| + t.test_files = FileList['test/**/tc_*.rb'] + t.verbose = false + t.warning = false +end + +desc 'generate API reference documentation' +YARD::Rake::YardocTask.new :yard do |t| + t.files = ['lib/**/*.rb'] +end diff --git a/aws-xray-sdk.gemspec b/aws-xray-sdk.gemspec new file mode 100644 index 0000000..885c36e --- /dev/null +++ b/aws-xray-sdk.gemspec @@ -0,0 +1,31 @@ +lib = File.expand_path('../lib', __FILE__) +$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib) +require 'aws-xray-sdk/version' + +Gem::Specification.new do |spec| + spec.name = 'aws-xray-sdk' + spec.version = XRay::VERSION + spec.author = 'Amazon Web Services' + spec.email = 'aws-xray-ruby@amazon.com' + spec.summary = 'AWS X-Ray SDK for Ruby' + spec.description = 'The AWS X-Ray SDK for Ruby enables Ruby developers to record and emit information from within their applications to the AWS X-Ray service.' + spec.homepage = 'https://github.com/aws/aws-xray-sdk-ruby' + spec.license = 'Apache-2.0' + + spec.files = Dir.glob('lib/**/*') + spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) } + spec.test_files = spec.files.grep(%r{^(test|spec|features)/}) + spec.require_paths = ['lib'] + + spec.add_dependency 'oj', '~> 3.0' + + spec.add_development_dependency 'aws-sdk-dynamodb', '~> 1' + spec.add_development_dependency 'aws-sdk-s3', '~> 1' + spec.add_development_dependency 'bundler', '~> 1.0' + spec.add_development_dependency 'minitest', '~> 5.0' + spec.add_development_dependency 'rake', '~> 12.0' + spec.add_development_dependency 'rdiscount', '~> 2.2' + spec.add_development_dependency 'simplecov', '~> 0.15' + spec.add_development_dependency 'webmock', '~> 3.0' + spec.add_development_dependency 'yard', '~> 0.9' +end diff --git a/images/example_servicemap.png b/images/example_servicemap.png new file mode 100644 index 0000000000000000000000000000000000000000..3f00ba479b7cf3d46c4e94c38f69c7a0ca20c88e GIT binary patch literal 220346 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 z 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%0Dog101<>L_mWR6pltZ5Fi(k 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 i&e4j@c#g`p^Z`i literal 0 HcmV?d00001 diff --git a/lib/aws-xray-sdk.rb b/lib/aws-xray-sdk.rb new file mode 100644 index 0000000..b335b0a --- /dev/null +++ b/lib/aws-xray-sdk.rb @@ -0,0 +1,10 @@ +require 'aws-xray-sdk/recorder' + +module XRay + @recorder = Recorder.new + + # providing the default global recorder + def self.recorder + @recorder + end +end diff --git a/lib/aws-xray-sdk/configuration.rb b/lib/aws-xray-sdk/configuration.rb new file mode 100644 index 0000000..da1355b --- /dev/null +++ b/lib/aws-xray-sdk/configuration.rb @@ -0,0 +1,158 @@ +require 'aws-xray-sdk/exceptions' +require 'aws-xray-sdk/patcher' +require 'aws-xray-sdk/emitter/default_emitter' +require 'aws-xray-sdk/context/default_context' +require 'aws-xray-sdk/sampling/default_sampler' +require 'aws-xray-sdk/streaming/default_streamer' +require 'aws-xray-sdk/segment_naming/dynamic_naming' +require 'aws-xray-sdk/plugins/ec2' +require 'aws-xray-sdk/plugins/ecs' +require 'aws-xray-sdk/plugins/elastic_beanstalk' +require 'aws-xray-sdk/logger' + +module XRay + # This class stores all configurations for X-Ray recorder + # and should be initialized only once. + class Configuration + include Patcher + + SEGMENT_NAME_KEY = 'AWS_XRAY_TRACING_NAME'.freeze + CONFIG_KEY = %I[logger name sampling plugins daemon_address segment_naming + naming_pattern emitter streamer context context_missing + sampling_rules stream_threshold patch].freeze + + def initialize + @name = ENV[SEGMENT_NAME_KEY] + @sampling = true + @emitter = DefaultEmitter.new + @context = DefaultContext.new + @sampler = DefaultSampler.new + @streamer = DefaultStreamer.new + @segment_naming = DynamicNaming.new fallback: @name + @plugins = [] + end + + # @param [String] v The default segment name. + # Environment vairable takes higher precedence. + def name=(v) + @name = ENV[SEGMENT_NAME_KEY] || v + end + + # proxy method to the emitter's daemon_address config. + def daemon_address=(v) + emitter.daemon_address = v + end + + # proxy method to the context's context_missing config. + def context_missing=(v) + context.context_missing = v + end + + # proxy method to the sampler's sampling rule config. + def sampling_rules=(v) + sampler.sampling_rules = v + end + + # proxy method to the streamer's stream threshold config. + def stream_threshold=(v) + streamer.stream_threshold = v + end + + # proxy method to the dynamic naming's pattern config. + def naming_pattern=(v) + segment_naming.pattern = v + end + + # makes a sampling decision based on internal configure, e.g. + # if sampling enabled and the default sampling rule. + def sample? + return true unless sampling + sampler.sample? + end + + # @param [Hash] user_config The user configuration overrides. + def configure(user_config) + raise InvalidConfigurationError.new('User config must be a Hash.') unless user_config.is_a?(Hash) + return if user_config.empty? + + user_config.each_key do |key| + case key + when :logger + XRay::Logging.logger = user_config[key] + when :name + self.name = user_config[key] + when :context + self.context = user_config[key] + when :context_missing + self.context_missing = user_config[key] + when :sampler + self.sampler = user_config[key] + when :sampling_rules + self.sampling_rules = user_config[key] + when :sampling + self.sampling = user_config[key] + when :emitter + self.emitter = user_config[key] + when :daemon_address + self.daemon_address = user_config[key] + when :segment_naming + self.segment_naming = user_config[key] + when :naming_pattern + self.naming_pattern = user_config[key] + when :streamer + self.streamer = user_config[key] + when :stream_threshold + self.stream_threshold = user_config[key] + when :plugins + self.plugins = load_plugins(user_config[key]) + when :patch + patch(user_config[key]) + else + raise InvalidConfigurationError.new(%(Invalid config key #{key}.)) + end + end + end + + attr_accessor :emitter + + attr_accessor :context + + attr_accessor :sampler + + attr_accessor :streamer + + attr_accessor :segment_naming + + attr_accessor :plugins + + attr_accessor :sampling + + # @return [String] The default segment name. + attr_reader :name + + # The global logger used across the X-Ray SDK. + # @return [Logger] + attr_reader :logger + + private + + def load_plugins(symbols) + plugins = [] + symbols.each do |symbol| + case symbol + when :ec2 + plugins << XRay::Plugins::EC2 + when :ecs + plugins << XRay::Plugins::ECS + when :elastic_beanstalk + plugins << XRay::Plugins::ElasticBeanstalk + else + raise InvalidConfigurationError.new(%(Unsupported plugin #{symbol}.)) + end + end + # eager loads aws metadata to eliminate impact on first incoming request + plugins.each(&:aws) + plugins + end + end +end diff --git a/lib/aws-xray-sdk/context/context.rb b/lib/aws-xray-sdk/context/context.rb new file mode 100644 index 0000000..630f740 --- /dev/null +++ b/lib/aws-xray-sdk/context/context.rb @@ -0,0 +1,26 @@ +module XRay + # The interface of context management for the X-Ray recorder. + module Context + # @param [Entity] entity The entity to be stored in the context. + def store_entity(entity:) + raise 'Not implemented' + end + + def current_entity + raise 'Not implemented' + end + + def clear! + raise 'Not implemented' + end + + # Put current active entity to the new context storage. + def inject_context(entity, target_ctx: nil) + raise 'Not implemented' + end + + def handle_context_missing + raise 'Not implemented' + end + end +end diff --git a/lib/aws-xray-sdk/context/default_context.rb b/lib/aws-xray-sdk/context/default_context.rb new file mode 100644 index 0000000..72643ba --- /dev/null +++ b/lib/aws-xray-sdk/context/default_context.rb @@ -0,0 +1,81 @@ +require 'aws-xray-sdk/logger' +require 'aws-xray-sdk/context/context' +require 'aws-xray-sdk/exceptions' + +module XRay + # The default context storage management used by + # the X-Ray recorder. It uses thread local to store + # segments and subsegments. + class DefaultContext + include Context + include Logging + + LOCAL_KEY = '_aws_xray_entity'.freeze + CONTEXT_MISSING_KEY = 'AWS_XRAY_CONTEXT_MISSING'.freeze + SUPPORTED_STRATEGY = %w[RUNTIME_ERROR LOG_ERROR].freeze + DEFAULT_STRATEGY = SUPPORTED_STRATEGY[0] + + attr_reader :context_missing + + def initialize + strategy = ENV[CONTEXT_MISSING_KEY] || DEFAULT_STRATEGY + @context_missing = sanitize_strategy(strategy) + end + + # @param [Entity] entity The entity to be stored in the context. + def store_entity(entity:) + Thread.current[LOCAL_KEY] = entity + end + + # @return [Entity] The current active entity(could be segment or subsegment). + def current_entity + if entity = Thread.current[LOCAL_KEY] + entity + else + handle_context_missing + end + end + + # Clear the current thread local storage on X-Ray related entities. + def clear! + Thread.current[LOCAL_KEY] = nil + end + + # @param [Entity] entity The entity to inject. + # @param [Thread] target_ctx Put the provided entity to the new thread. + def inject_context(entity, target_ctx: nil) + target_ctx ||= Thread.current + target_ctx[LOCAL_KEY] = entity if entity + end + + # When the current entity needs to be accessed but there is none, + # it handles the missing context based on the configuration. + # On `RUNTIME_ERROR` it raises `ContextMissingError`. + # On 'LOG_ERROR' it logs an error message and return `nil`. + def handle_context_missing + case context_missing + when 'RUNTIME_ERROR' + raise ContextMissingError + when 'LOG_ERROR' + logger.error %(can not find the current context.) + end + nil + end + + def context_missing=(v) + strategy = ENV[CONTEXT_MISSING_KEY] || v + @context_missing = sanitize_strategy(strategy) + end + + private + + def sanitize_strategy(v) + if SUPPORTED_STRATEGY.include?(v) + v + else + logger.warn %(context missing #{v} is not supported, switch to default #{DEFAULT_STRATEGY}.) + DEFAULT_STRATEGY + end + end + end +end diff --git a/lib/aws-xray-sdk/emitter/default_emitter.rb b/lib/aws-xray-sdk/emitter/default_emitter.rb new file mode 100644 index 0000000..13c332b --- /dev/null +++ b/lib/aws-xray-sdk/emitter/default_emitter.rb @@ -0,0 +1,53 @@ +require 'socket' +require 'aws-xray-sdk/logger' +require 'aws-xray-sdk/emitter/emitter' +require 'aws-xray-sdk/exceptions' + +module XRay + # The default emitter the X-Ray recorder uses to send segments/subsegments + # to the X-Ray daemon over UDP using a non-blocking socket. + class DefaultEmitter + include Emitter + include Logging + + attr_reader :address + + def initialize + @socket = UDPSocket.new + @address = ENV[DAEMON_ADDRESS_KEY] || '127.0.0.1:2000' + configure_socket(@address) + end + + # Serializes a segment/subsegment and sends it to the X-Ray daemon + # over UDP. It is no-op for non-sampled entity. + # @param [Entity] entity The entity to send + def send_entity(entity:) + return nil unless entity.sampled + begin + payload = %(#{@@protocol_header}#{@@protocol_delimiter}#{entity.to_json}) + logger.debug %(sending payload #{payload} to daemon at #{address}.) + @socket.send payload, 0 + rescue StandardError => e + logger.warn %(failed to send payload due to #{e.message}) + end + end + + def daemon_address=(v) + v = ENV[DAEMON_ADDRESS_KEY] || v + @address = v + configure_socket(v) + end + + private + + def configure_socket(v) + begin + addr = v.split(':') + host, ip = addr[0], addr[1].to_i + @socket.connect(host, ip) + rescue StandardError + raise InvalidDaemonAddressError, %(Invalid X-Ray daemon address specified: #{v}.) + end + end + end +end diff --git a/lib/aws-xray-sdk/emitter/emitter.rb b/lib/aws-xray-sdk/emitter/emitter.rb new file mode 100644 index 0000000..14aeb98 --- /dev/null +++ b/lib/aws-xray-sdk/emitter/emitter.rb @@ -0,0 +1,24 @@ +require 'json' + +module XRay + # The emitter interface the X-Ray recorder uses to send segments/subsegments + # to the X-Ray daemon over UDP. + module Emitter + DAEMON_ADDRESS_KEY = 'AWS_XRAY_DAEMON_ADDRESS'.freeze + + @@protocol_header = { + format: 'json', + version: 1 + }.to_json + @@protocol_delimiter = "\n" + + # @param [Entity] entity Entity to send. + def send_entity(entity:) + raise 'Not implemented' + end + + def daemon_address=(v) + raise 'Not implemented' + end + end +end diff --git a/lib/aws-xray-sdk/exceptions.rb b/lib/aws-xray-sdk/exceptions.rb new file mode 100644 index 0000000..abbf9fe --- /dev/null +++ b/lib/aws-xray-sdk/exceptions.rb @@ -0,0 +1,31 @@ +module XRay + # All custom exception thrown by the SDK should subclass AwsXRayError. + class AwsXRaySdkError < ::StandardError; end + + class EntityClosedError < AwsXRaySdkError + def initialize + super('Segment or subsegment already ended.') + end + end + + class ContextMissingError < AwsXRaySdkError + def initialize + super('Can not find any active segment or subsegment.') + end + end + + class SegmentNameMissingError < AwsXRaySdkError + end + + class InvalidDaemonAddressError < AwsXRaySdkError + end + + class InvalidSamplingConfigError < AwsXRaySdkError + end + + class InvalidConfigurationError < AwsXRaySdkError + end + + class UnsupportedPatchingTargetError < AwsXRaySdkError + end +end diff --git a/lib/aws-xray-sdk/facets/aws_sdk.rb b/lib/aws-xray-sdk/facets/aws_sdk.rb new file mode 100644 index 0000000..0a7237a --- /dev/null +++ b/lib/aws-xray-sdk/facets/aws_sdk.rb @@ -0,0 +1,127 @@ +require 'aws-sdk-core' +require 'aws-xray-sdk/facets/helper' +require 'aws-xray-sdk/facets/resources/aws_params_whitelist' +require 'aws-xray-sdk/facets/resources/aws_services_whitelist' + +module XRay + class AwsSDKPlugin < Seahorse::Client::Plugin + option :xray_recorder, default: XRay.recorder + + def add_handlers(handlers, config) + # run before Seahorse::Client::Plugin::ParamValidator (priority 50) + handlers.add Handler, step: :validate, priority: 49 + end + + # Handler to capture AWS API calls as subsegments + class Handler < Seahorse::Client::Handler + include XRay::Facets::Helper + + def call(context) + recorder = Aws.config[:xray_recorder] + operation = context.operation_name + service_name = context.client.class.api.metadata['serviceAbbreviation'] || + context.client.class.to_s.split('::')[1] + recorder.capture service_name, namespace: 'aws' do |subsegment| + # inject header string before calling downstream AWS services + context.http_request.headers[TRACE_HEADER] = prep_header_str entity: subsegment + response = @handler.call(context) + http_response = context.http_response + resp_meta = { + status: http_response.status_code, + content_length: http_response.headers['content-length'].to_i + } + aws = { + # XRay back-end right now has strict operation name matching + operation: sanitize_op_name(operation), + region: context.client.config.region, + retries: context.retries, + request_id: http_response.headers['x-amzn-requestid'] + } + # S3 returns special request id in response headers + if service_name == 'S3' + aws[:id_2] = http_response.headers['x-amz-id-2'] + end + + operation_h = AwsParams.whitelist[:services] + .fetch(service_name.to_sym, {}) + .fetch(:operations, {})[operation] + unless operation_h.nil? + params_capture req_params: context.params, resp_params: response.to_h, + capture: operation_h, meta: aws + end + subsegment.aws = aws + if err = response.error + subsegment.add_exception exception: err, remote: true + end + subsegment.merge_http_response response: resp_meta + response + end + end + + private + + def inject_headers(request:, entity:) + request.headers[TRACE_HEADER] = prep_header_str entity: entity + end + + def sanitize_op_name(opname) + opname.to_s.split('_').collect(&:capitalize).join if opname + end + + def params_capture(req_params:, resp_params:, capture:, meta:) + if norm = capture[:request_parameters] + capture_normal params: req_params, capture: norm, meta: meta + end + + if norm = capture[:response_parameters] + capture_normal params: resp_params, capture: norm, meta: meta + end + + if spec = capture[:request_descriptors] + capture_special params: req_params, capture: spec, meta: meta + end + + if spec = capture[:response_descriptors] + capture_special params: resp_params, capture: spec, meta: meta + end + end + + def capture_normal(params:, capture:, meta:) + params.each_key do |key| + meta[key] = params[key] if capture.include?(key) + end + end + + def capture_special(params:, capture:, meta:) + params.each_key do |key| + process_descriptor(target: params[key], descriptor: capture[key], meta: meta) if capture.include?(key) + end + end + + def process_descriptor(target:, descriptor:, meta:) + # "get_count" = true + v = target.length if descriptor[:get_count] + # "get_keys" = true + v = target.keys if descriptor[:get_keys] + meta[descriptor[:rename_to]] = v + end + end + end + + # Add X-Ray plugin to AWS SDK clients + module AwsSDKPatcher + def self.patch(services: nil, recorder: XRay.recorder) + force = services.nil? + services ||= AwsServices.whitelist + services.each do |s| + begin + Aws.const_get(%(#{s}::Client)).add_plugin XRay::AwsSDKPlugin + Aws.config.update xray_recorder: recorder + rescue NameError + # swallow the error if no explicit user config + raise unless force + end + end + end + end +end diff --git a/lib/aws-xray-sdk/facets/helper.rb b/lib/aws-xray-sdk/facets/helper.rb new file mode 100644 index 0000000..681f682 --- /dev/null +++ b/lib/aws-xray-sdk/facets/helper.rb @@ -0,0 +1,61 @@ +require 'aws-xray-sdk/model/trace_header' + +module XRay + module Facets + # Hepler functions shared for all external frameworks/libraries + # like make sampling decisions from incoming http requests etc. + module Helper + TRACE_HEADER = 'X-Amzn-Trace-Id'.freeze + TRACE_HEADER_PROXY = 'HTTP_X_AMZN_TRACE_ID'.freeze + + # Construct a `TraceHeader` object from headers + # of the incoming request. This method should always return + # a `TraceHeader` object regardless of tracing header's presence + # in the incoming request. + # @param [Hash] headers Hash that contains X-Ray trace header key. + # @return [TraceHeader] The new constructed trace header object. + def construct_header(headers:) + if v = headers[TRACE_HEADER_PROXY] || headers[TRACE_HEADER] + TraceHeader.from_header_string header_str: v + else + TraceHeader.empty_header + end + end + + # The sampling decision coming from `trace_header` always has + # the highest precedence. If the `trace_header` doesn't contain + # sampling decision then it checks if sampling is enabled or not + # in the recorder. If not enbaled it returns 'true'. Otherwise it uses + # sampling rule to decide. + def should_sample?(header_obj:, recorder:, + host: nil, method: nil, path: nil, + **args) + # check outside decision + if i = header_obj.sampled + if i.zero? + false + else + true + end + # check sampling rules + elsif recorder.sampling_enabled? + recorder.sampler.sample_request?(service_name: host, url_path: path, + http_method: method) + # sample if no reason not to + else + true + end + end + + # Prepares a X-Ray header string based on the provided Segment/Subsegment. + def prep_header_str(entity:) + return '' if entity.nil? + root = entity.segment.trace_id + parent_id = entity.id + sampled = entity.sampled ? 1 : 0 + header = TraceHeader.new root: root, parent_id: parent_id, sampled: sampled + header.header_string + end + end + end +end diff --git a/lib/aws-xray-sdk/facets/net_http.rb b/lib/aws-xray-sdk/facets/net_http.rb new file mode 100644 index 0000000..2511b59 --- /dev/null +++ b/lib/aws-xray-sdk/facets/net_http.rb @@ -0,0 +1,61 @@ +require 'net/http' +require 'aws-xray-sdk/facets/helper' + +module XRay + # Patch net/http to be traced by X-Ray + module NetHttp + # Class level interceptor to capture http requests as subsegments + module HTTPClassInterceptor + def new(*options) + o = super(*options) + o + end + end + + # Instance level interceptor to capture http requests as subsegments + module HTTPInstanceInterceptor + include XRay::Facets::Helper + + def initialize(*options) + super(*options) + end + + def request(req, body = nil, &block) + entity = XRay.recorder.current_entity + capture = !(entity && entity.namespace && entity.namespace == 'aws'.freeze) + if started? && capture && entity + XRay.recorder.capture(address, namespace: 'remote') do |subsegment| + protocol = use_ssl? ? 'https'.freeze : 'http'.freeze + # avoid modifying original variable + iport = port.nil? ? nil : %(:#{port}) + # do not capture query string + path = req.path.split('?')[0] if req.path + uri = %(#{protocol}://#{address}#{iport}#{path}) + req_meta = { + url: uri, + method: req.method + } + subsegment.merge_http_request request: req_meta + req[TRACE_HEADER] = prep_header_str entity: subsegment + begin + res = super + res_meta = { + status: res.code.to_i, + content_length: res.content_length + } + subsegment.merge_http_response response: res_meta + rescue Exception => e + subsegment.add_exception exception: e + raise e + end + end + else + super + end + end + end + + ::Net::HTTP.singleton_class.prepend HTTPClassInterceptor + ::Net::HTTP.prepend HTTPInstanceInterceptor + end +end diff --git a/lib/aws-xray-sdk/facets/rack.rb b/lib/aws-xray-sdk/facets/rack.rb new file mode 100644 index 0000000..f29f63e --- /dev/null +++ b/lib/aws-xray-sdk/facets/rack.rb @@ -0,0 +1,87 @@ +require 'rack/request' +require 'aws-xray-sdk' +require 'aws-xray-sdk/facets/helper' + +module XRay + module Rack + # Rack middleware that generates a segment for each request/response cycle. + class Middleware + include XRay::Facets::Helper + X_FORWARD = 'HTTP_X_FORWARDED_FOR'.freeze + + def initialize(app, recorder: nil) + @app = app + @recorder = recorder || XRay.recorder + end + + def call(env) + header = construct_header(headers: env) + req = ::Rack::Request.new(env) + + # params required for path based sampling + host = req.host + url_path = req.path + method = req.request_method + + # get sampling decision + sampled = should_sample?( + header_obj: header, recorder: @recorder, + host: host, method: method, path: url_path + ) + + # get segment name from host header if applicable + seg_name = @recorder.segment_naming.provide_name(host: req.host) + + # begin the segment + segment = @recorder.begin_segment seg_name, trace_id: header.root, parent_id: header.parent_id, + sampled: sampled + + # add neccessary http request metadata to the segment + req_meta = extract_request_meta(req) + segment.merge_http_request request: req_meta unless req_meta.empty? + begin + status, headers, body = @app.call env + resp_meta = {} + resp_meta[:status] = status + # Don't set content_length if it is not available on headers. + resp_obj = ::Rack::Response.new body: body, status: status, headers: headers + if len = resp_obj.content_length + resp_meta[:content_length] = len + end + segment.merge_http_response response: resp_meta + [status, headers, body] + rescue Exception => e + segment.apply_status_code status: 500 + segment.add_exception exception: e + raise e + ensure + @recorder.end_segment + end + end + + private + + def extract_request_meta(req) + req_meta = {} + req_meta[:url] = req.url if req.url + req_meta[:user_agent] = req.user_agent if req.user_agent + req_meta[:method] = req.request_method if req.request_method + if req.has_header?(X_FORWARD) + req_meta[:client_ip] = get_ip(req.get_header(X_FORWARD)) + req_meta[:x_forwarded_for] = true + elsif v = req.ip + req_meta[:client_ip] = v + end + req_meta + end + + def get_ip(ips) + if ips.respond_to?(:length) + ips[ips.length - 1] + else + ips + end + end + end + end +end diff --git a/lib/aws-xray-sdk/facets/rails/active_record.rb b/lib/aws-xray-sdk/facets/rails/active_record.rb new file mode 100644 index 0000000..e9b4fd6 --- /dev/null +++ b/lib/aws-xray-sdk/facets/rails/active_record.rb @@ -0,0 +1,66 @@ +require 'active_record' + +module XRay + module Rails + # Recording Rails database transactions as subsegments. + module ActiveRecord + class << self + IGNORE_OPS = ['SCHEMA', 'ActiveRecord::SchemaMigration Load', + 'ActiveRecord::InternalMetadata Load'].freeze + DB_TYPE_MAPPING = { + mysql2: 'MySQL', + postgresql: 'PostgreSQL' + }.freeze + + def record(transaction) + payload = transaction.payload + pool, conn = get_pool_n_conn(payload[:connection_id]) + + return if IGNORE_OPS.include?(payload[:name]) || pool.nil? || conn.nil? + db_config = pool.spec.config + name, sql = build_name_sql_meta config: db_config, conn: conn + subsegment = XRay.recorder.begin_subsegment name, namespace: 'remote' + subsegment.start_time = transaction.time.to_f + subsegment.sql = sql + XRay.recorder.end_subsegment end_time: transaction.end.to_f + end + + private + + def build_name_sql_meta(config:, conn:) + # extract all available info + adapter = config[:adapter] + database = config[:database] + host = config[:host].nil? ? nil : %(@#{config[:host]}) + port = config[:port].nil? ? nil : %(:#{config[:port]}) + username = config[:username] + + # assemble subsegment name + name = %(#{database}#{host}) + # assemble sql meta + sql = {} + sql[:user] = username + sql[:url] = %(#{adapter}://#{username}#{host}#{port}/#{database}) + sql[:database_type] = DB_TYPE_MAPPING[adapter.to_sym] + [name, sql] + end + + def get_pool_n_conn(conn_id) + pool, conn = nil, nil + ::ActiveRecord::Base.connection_handler.connection_pool_list.each do |p| + conn = p.connections.select { |c| c.object_id == conn_id } + pool = p unless conn.nil? + end + [pool, conn] + end + end + end + end +end + +# Add a hook on database transactions using Rails instrumentation API +ActiveSupport::Notifications.subscribe('sql.active_record') do |*args| + # We need the full event which has all the timing info + transaction = ActiveSupport::Notifications::Event.new(*args) + XRay::Rails::ActiveRecord.record(transaction) +end diff --git a/lib/aws-xray-sdk/facets/rails/ex_middleware.rb b/lib/aws-xray-sdk/facets/rails/ex_middleware.rb new file mode 100644 index 0000000..101b618 --- /dev/null +++ b/lib/aws-xray-sdk/facets/rails/ex_middleware.rb @@ -0,0 +1,24 @@ +require 'aws-xray-sdk' + +module XRay + module Rails + # Middleware for capturing unhandled exceptions from views/controller. + # To properly capture exceptions this middleware needs to be placed + # after the default exception handling middleware. Otherwise they will + # be swallowed. + class ExceptionMiddleware + def initialize(app, recorder: nil) + @app = app + @recorder = recorder || XRay.recorder + end + + def call(env) + @app.call(env) + rescue Exception => e + segment = @recorder.current_segment + segment.add_exception exception: e if segment + raise e + end + end + end +end diff --git a/lib/aws-xray-sdk/facets/rails/railtie.rb b/lib/aws-xray-sdk/facets/rails/railtie.rb new file mode 100644 index 0000000..b3b364f --- /dev/null +++ b/lib/aws-xray-sdk/facets/rails/railtie.rb @@ -0,0 +1,23 @@ +require 'aws-xray-sdk/facets/rack' +require 'aws-xray-sdk/facets/rails/ex_middleware' + +module XRay + # configure X-Ray instrumentation for rails framework + class Railtie < ::Rails::Railtie + RAILS_OPTIONS = %I[active_record].freeze + + initializer "aws-xray-sdk.rack_middleware" do |app| + app.middleware.insert 0, Rack::Middleware + app.middleware.use XRay::Rails::ExceptionMiddleware + end + + config.after_initialize do |app| + if app.config.respond_to?('xray') + options = app.config.xray + require 'aws-xray-sdk/facets/rails/active_record' if options[:active_record] + general_options = options.reject { |k, v| RAILS_OPTIONS.include?(k) } + XRay.recorder.configure(general_options) + end + end + end +end diff --git a/lib/aws-xray-sdk/facets/resources/aws_params_whitelist.rb b/lib/aws-xray-sdk/facets/resources/aws_params_whitelist.rb new file mode 100644 index 0000000..cb4949c --- /dev/null +++ b/lib/aws-xray-sdk/facets/resources/aws_params_whitelist.rb @@ -0,0 +1,340 @@ +module XRay + # AWS SDK parameters whitelisted will be recorded + # as metadata on AWS subsegments + module AwsParams + @whitelist = { + services: { + DynamoDB: { + operations: { + batch_get_item: { + request_descriptors: { + request_items: { + map: true, + get_keys: true, + rename_to: :table_names + } + }, + response_parameters: %I[ + consumed_capacity + ] + }, + batch_write_item: { + request_descriptors: { + request_items: { + map: true, + get_keys: true, + rename_to: :table_names + } + }, + response_parameters: %I[ + consumed_capacity + item_collection_metrics + ] + }, + create_table: { + request_parameters: %I[ + global_secondary_indexes + local_secondary_indexes + provisioned_throughput + table_name + ] + }, + delete_item: { + request_parameters: %I[ + table_name + ], + response_parameters: %I[ + consumed_capacity + item_collection_metrics + ] + }, + delete_table: { + request_parameters: %I[ + table_name + ] + }, + describe_table: { + request_parameters: %I[ + table_name + ] + }, + get_item: { + request_parameters: %I[ + consistent_read + projection_expression + table_name + ], + response_parameters: %I[ + consumed_capacity + ] + }, + list_tables: { + request_parameters: %I[ + exclusive_start_table_name + limit + ], + response_descriptors: { + table_names: { + list: true, + get_count: true, + rename_to: :table_count + } + } + }, + put_item: { + request_parameters: %I[ + table_name + ], + response_parameters: %I[ + consumed_capacity + item_collection_metrics + ] + }, + query: { + request_parameters: %I[ + attributes_to_get + consistent_read + index_name + limit + projection_expression + scan_index_forward + select + table_name + ], + response_parameters: %I[ + consumed_capacity + ] + }, + scan: { + request_parameters: %I[ + attributes_to_get + consistent_read + index_name + limit + projection_expression + segment + select + table_name + total_segments + ], + response_parameters: %I[ + consumed_capacity + count + scanned_count + ] + }, + update_item: { + request_parameters: %I[ + table_name + ], + response_parameters: %I[ + consumed_capacity + item_collection_metrics + ] + }, + update_table: { + request_parameters: %I[ + attribute_definitions + global_secondary_index_updates + provisioned_throughput + table_name + ] + } + } + }, + SQS: { + operations: { + add_permission: { + request_parameters: %I[ + label + queue_url + ] + }, + change_message_visibility: { + request_parameters: %I[ + queue_url + visibility_timeout + ] + }, + change_message_visibility_batch: { + request_parameters: %I[ + queue_url + ], + response_parameters: %I[ + failed + ] + }, + create_queue: { + request_parameters: %I[ + attributes + queue_name + ] + }, + delete_message: { + request_parameters: %I[ + queue_urls + ] + }, + delete_message_batch: { + request_parameters: %I[ + queue_url + ], + response_parameters: %I[ + failed + ] + }, + delete_queue: { + request_parameters: %I[ + queue_url + ] + }, + get_queue_attributes: { + request_parameters: %I[ + queue_url + ], + response_parameters: %I[ + attributes + ] + }, + get_queue_url: { + request_parameters: %I[ + queue_name + queue_owner_aws_account_id + ], + response_parameters: %I[ + queue_url + ] + }, + list_dead_letter_source_queues: { + request_parameters: %I[ + queue_url + ], + response_parameters: %I[ + queue_urls + ] + }, + list_queues: { + request_parameters: %I[ + queue_name_prefix + ], + response_descriptors: { + queue_urls: { + list: true, + get_count: true, + rename_to: :queue_count + } + } + }, + purge_queue: { + request_parameters: %I[ + queue_url + ] + }, + receive_message: { + request_parameters: %I[ + attribute_names + max_number_of_messages + message_attribute_names + queue_url + visibility_timeout + wait_time_seconds + ], + response_descriptors: { + messages: { + list: true, + get_count: true, + rename_to: :message_count + } + } + }, + remove_permission: { + request_parameters: %I[ + queue_url + ] + }, + send_message: { + request_parameters: %I[ + delay_seconds + queue_url + ], + request_descriptors: { + message_attributes: { + map: true, + get_keys: true, + rename_to: :message_attribute_names + } + }, + response_parameters: %I[ + message_id + ] + }, + send_message_batch: { + request_parameters: %I[ + queue_url + ], + request_descriptors: { + entries: { + list: true, + get_count: true, + rename_to: :message_count + } + }, + response_descriptors: { + failed: { + list: true, + get_count: true, + rename_to: :failed_count + }, + successful: { + list: true, + get_count: true, + rename_to: :successful_count + } + } + }, + set_queue_attributes: { + request_parameters: %I[ + queue_url + ], + request_descriptors: { + attributes: { + map: true, + get_keys: true, + rename_to: :attribute_names + } + } + } + } + }, + Lambda: { + operations: { + invoke: { + request_parameters: %I[ + function_name + invocation_type + log_type + qualifier + ], + response_parameters: %I[ + function_error + status_code + ] + }, + invoke_async: { + request_parameters: %I[ + function_name + ], + response_parameters: %I[ + status + ] + } + } + } + } + } + + def self.whitelist + @whitelist + end + end +end diff --git a/lib/aws-xray-sdk/facets/resources/aws_services_whitelist.rb b/lib/aws-xray-sdk/facets/resources/aws_services_whitelist.rb new file mode 100644 index 0000000..37e275e --- /dev/null +++ b/lib/aws-xray-sdk/facets/resources/aws_services_whitelist.rb @@ -0,0 +1,147 @@ +module XRay + # AWS Services listed below will be recorded as subsegments + module AwsServices + # exausted list can be tracked at http://docs.aws.amazon.com/sdk-for-ruby/v3/api/Seahorse/Client/Base.html + @whitelist = %I[ + ACM + APIGateway + AlexaForBusiness + AppStream + AppSync + ApplicationAutoScaling + ApplicationDiscoveryService + Athena + AutoScaling + Batch + Budgets + Cloud9 + CloudDirectory + CloudFormatioin + CloudFront + CloudHSM + CloudHSMV2 + CloudSearch + CloudSearchDomain + CloudTrail + CloudWatch + CloudWatchEvents + CloudWatchLogs + CodeBuild + CodeCommit + CodeDeploy + CodePipeline + CodeStar + CognitoIdentity + CognitoIdentityProvider + CognitoSync + Comprehend + ConfigService + CostExplore + CostandUsageReportService + DAX + DataPipeline + DatabaseMigrationService + DeviceFarm + DirectConnect + DirectoryService + DynamoDB + DynamoDBStreams + EC2 + ECR + ECS + EFS + EMR + ElastiCache + ElasticBeanstalk + ElasticLoadBalancing + ElasticLoadBalancingV2 + ElasticTranscoder + ElasticsearchService + Firehost + GameLift + Glacier + Glue + Greengrass + GuardDuty + Health + IAM + ImportExport + Inspector + IoT + IoTDataPlane + IoTJobsDataPlane + KMS + Kinesis + KinesisAnalytics + KinesisVideo + KinesisVideoArchiveMedia + KinesisVideoMedia + Lambda + LambdaPreview + Lex + LexModelBuildingService + LexRuntimeService + Lightsail + MQ + MTurk + MachineLearning + MarketplaceCommerceAnalytics + MarketplaceEntitlementService + MarketplaceMetering + MediaConvert + MediaLive + MediaPackage + MediaStore + MediaStoreData + MigrationHub + Mobile + OpsWorks + OpsWorksCM + Organizations + Pinpoint + Polly + Pricing + RDS + Redshift + Rekognition + ResourceGroups + ResourceGroupsTaggingAPI + Route53 + Route53Domains + S3 + SES + SFN + SMS + SNS + SQS + SSM + STS + SWF + SageMaker + SageMakerRuntime + ServerlessApplicationRepository + ServiceCatalog + ServiceDiscovery + Shield + SimpleDB + Snowball + States + StorageGateway + Support + Translate + WAF + WAFRegional + WorkDocs + WorkSpaces + XRay + ] + + def self.whitelist + @whitelist + end + + def self.whitelist=(v) + @whitelist = v + end + end +end diff --git a/lib/aws-xray-sdk/logger.rb b/lib/aws-xray-sdk/logger.rb new file mode 100644 index 0000000..4775028 --- /dev/null +++ b/lib/aws-xray-sdk/logger.rb @@ -0,0 +1,19 @@ +require 'logger' + +module XRay + # Provide global logger for classes that include this module. + # It serves as a proxy to global recorder's logger. + module Logging + def logger + Logging.logger + end + + def self.logger + @logger ||= Logger.new($stdout).tap { |l| l.level = Logger::INFO } + end + + def self.logger=(v) + @logger = v + end + end +end diff --git a/lib/aws-xray-sdk/model/annotations.rb b/lib/aws-xray-sdk/model/annotations.rb new file mode 100644 index 0000000..1c27d3c --- /dev/null +++ b/lib/aws-xray-sdk/model/annotations.rb @@ -0,0 +1,97 @@ +require 'aws-xray-sdk/logger' +require 'aws-xray-sdk/exceptions' + +module XRay + # Annotations are simple key-value pairs that are indexed for use with filter expressions. + # Use annotations to record data that you want to use to group traces in the console, + # or when calling the GetTraceSummaries API. + class Annotations + include Logging + + def initialize(entity) + @entity = entity + @data = {} + end + + def [](key) + @data[key] + end + + # @param [Symbol] k Only characters in `A-Za-z0-9_` are supported. + # @param v Only `Numeric`, `String` true/false is supported. + def []=(k, v) + raise EntityClosedError if @entity.closed? + if key_supported?(k) && value_supported?(v) + @data[k] = v + else + logger.warn %(Dropping annotation with key #{k} due to invalid characters.) + end + end + + # @param [Hash] h Update annotations with a single input hash. + def update(h) + raise EntityClosedError if @entity.closed? + filtered = filter_annotations(h) + @data.merge!(filtered) + end + + def to_h + sanitize_values(@data) + end + + private + + def filter_annotations(h) + h.delete_if do |k, v| + drop = !key_supported?(k) || !value_supported?(v) + logger.warn %(Dropping annotation with key #{k} due to invalid characters.) if drop + drop + end + end + + def sanitize_values(h) + h.each_pair do |k, v| + if v.is_a?(Float) + h[k] = v.to_s if v.nan? || v.infinite? == 1 || v.infinite? == -1 + end + end + end + + def key_supported?(k) + k.match(/[A-Za-z0-9_]+/) + end + + def value_supported?(v) + case v + when Numeric + true + when true, false + true + else + v.is_a?(String) + end + end + end + + # Singleton facade annotations class doing no-op for performance + # in case of not sampled X-Ray entities. + module FacadeAnnotations + class << self + def [](key) + # no-op + end + + def []=(k, v) + # no-op + end + + def update(h) + # no-op + end + + def to_h + # no-op + end + end + end +end diff --git a/lib/aws-xray-sdk/model/cause.rb b/lib/aws-xray-sdk/model/cause.rb new file mode 100644 index 0000000..3fe0849 --- /dev/null +++ b/lib/aws-xray-sdk/model/cause.rb @@ -0,0 +1,70 @@ +require 'oj' + +module XRay + # Represents cause section in segment and subsegment document. + # It records information about application runtime exceptions. + class Cause + attr_reader :id + @@depth = 15 + + def initialize(exception: nil, id: nil, remote: false) + if exception + @exception_h = normalize e: exception, remote: remote + end + @id = id + end + + def to_h + return id if id + h = { + working_directory: Dir.pwd, + paths: Gem.paths.path, + exceptions: @exception_h + } + h + end + + def to_json + @to_json ||= begin + Oj.dump to_h, mode: :compat, use_as_json: true + end + end + + private + + def normalize(e:, remote: false) + exceptions = [] + exceptions << normalize_exception(e: e, remote: remote) + + # don't propagate remote flag + while e.cause + exceptions << normalize_exception(e: e.cause) + e = e.cause + end + + exceptions + end + + def normalize_exception(e:, remote: false) + h = { + message: e.to_s, + type: e.class.to_s + } + h[:remote] = true if remote + + backtrace = e.backtrace_locations + return h unless backtrace + h[:stack] = backtrace.first(@@depth).collect do |t| + { + path: t.path, + line: t.lineno, + label: t.label + } + end + + truncated = backtrace.size - @@depth + h[:truncated] = truncated if truncated > 0 + h + end + end +end diff --git a/lib/aws-xray-sdk/model/dummy_entities.rb b/lib/aws-xray-sdk/model/dummy_entities.rb new file mode 100644 index 0000000..c6d20d2 --- /dev/null +++ b/lib/aws-xray-sdk/model/dummy_entities.rb @@ -0,0 +1,72 @@ +require 'aws-xray-sdk/model/segment' +require 'aws-xray-sdk/model/subsegment' +require 'aws-xray-sdk/model/annotations' +require 'aws-xray-sdk/model/metadata' + +module XRay + # defines common no-op methods for dummy segments/subsegments + module DummyEntity + def sampled + false + end + + def annotations + FacadeAnnotations + end + + def metadata(namespace: :default) + FacadeMetadata + end + + def apply_status_code(status:) + # no-op + end + + def merge_http_request(request:) + # no-op + end + + def merge_http_response(response:) + # no-op + end + + def add_exception(exception:, remote: false) + # no-op + end + + def aws=(v) + # no-op + end + + def to_h + # no-op + end + + def to_json + # no-op + end + end + + # A dummy segment is created when ``xray_recorder`` decides to not sample + # the segment based on sampling decisions. + # Adding data to a dummy segment becomes a no-op except for + # subsegments. This is to reduce the memory footprint of the SDK. + # A dummy segment will not be sent to the X-Ray daemon by the default emitter. + # Manually create dummy segments is not recommended. + class DummySegment < Segment + include DummyEntity + end + + # A dummy subsegment will be created when ``xray_recorder`` tries + # to create a subsegment under a not sampled segment. Adding data + # to a dummy subsegment becomes no-op except for child subsegments. + # Dummy subsegment will not be sent to the X-Ray daemon by the default emitter. + # Manually create dummy subsegments is not recommended. + class DummySubsegment < Subsegment + include DummyEntity + + def sql=(v) + # no-op + end + end +end diff --git a/lib/aws-xray-sdk/model/entity.rb b/lib/aws-xray-sdk/model/entity.rb new file mode 100644 index 0000000..708093d --- /dev/null +++ b/lib/aws-xray-sdk/model/entity.rb @@ -0,0 +1,187 @@ +require 'securerandom' +require 'bigdecimal' +require 'oj' +require 'aws-xray-sdk/exceptions' +require 'aws-xray-sdk/model/cause' +require 'aws-xray-sdk/model/annotations' +require 'aws-xray-sdk/model/metadata' + +module XRay + # This module contains common properties and methods + # used by segment and subsegment class. + module Entity + attr_reader :name, :exception, :cause, :namespace, + :http_request, :http_response + attr_accessor :parent, :throttle, :error, :fault, :sampled, :aws, + :start_time, :end_time + + HTTP_REQUEST_KEY = %I[url method user_agent client_ip x_forwarded_for].freeze + HTTP_RESPONSE_KEY = %I[status content_length].freeze + + # Generates a random 8-digit hex number as the entity id and returns it. + def id + @id ||= begin + SecureRandom.hex(8) + end + end + + def closed? + @closed ||= false + end + + # @param [Float] end_time End time on epoch. + def close(end_time: nil) + raise EntityClosedError if closed? + @end_time = end_time || Time.now.to_f + @closed = true + end + + # @return [Array] The children subsegments of this entity. + def subsegments + @subsegments ||= [] + end + + # @param [Subsegment] subsegment Append the provided subsegment to children subsegments. + def add_subsegment(subsegment:) + raise EntityClosedError if closed? + subsegment.sampled = sampled + subsegment.parent = self + subsegments << subsegment + nil + end + + # @param [Subsegment] subsegment Remove the provided subsegment from children subsegments. + # @return [Subsegment] The deleted subsegment if the deletion is successful. + def remove_subsegment(subsegment:) + subsegments.delete(subsegment) + subsegment + end + + def annotations + @annotations ||= Annotations.new(self) + end + + def metadata(namespace: :default) + @metadata ||= Metadata.new(self) + @metadata.sub_meta(namespace) + end + + # Set error/fault/throttle flags based on http status code. + # This method is idempotent. + # @param [Integer] status + def apply_status_code(status:) + raise EntityClosedError if closed? + case status.to_i + when 429 + @throttle = true + @error = true + @fault = false + when 400..499 + @error = true + @throttle = false + @fault = false + when 500..599 + @fault = true + @error = false + @throttle = false + end + + @http_response ||= {} + @http_response[:status] = status.to_i + end + + # @param [Hash] request Supported keys are `:url`, `:user_agent`, `:client_ip`, + # `:x_forwarded_for`, `:method`. Value can be one of + # String or Integer or Boolean types depend on the key. + def merge_http_request(request:) + raise EntityClosedError if closed? + request.delete_if { |k| !HTTP_REQUEST_KEY.include?(k) } + @http_request ||= {} + @http_request.merge!(request) + end + + # @param [Hash] response Supported keys are `:status`, `:content_length`. + # Value can be one of String or Integer types depend on the key. + def merge_http_response(response:) + raise EntityClosedError if closed? + response.delete_if { |k| !HTTP_RESPONSE_KEY.include?(k) } + @http_response ||= {} + @http_response.merge!(response) + apply_status_code status: response[:status] if response.key?(:status) + end + + # @param [Exception] exception The exception object to capture. + # @param remote A boolean flag indicates whether the exception is + # returned from the downstream service. + def add_exception(exception:, remote: false) + raise EntityClosedError if closed? + @fault = true + @exception = exception + if cause_id = find_root_cause(exception) + @cause = Cause.new id: cause_id + else + @cause = Cause.new exception: exception, remote: remote + end + end + + # @return [String] Cause id is the id of the subsegment where + # the exception originally comes from. + def cause_id + return @cause.id if @cause + end + + # @return [Hash] The hash that contains all attributes that will + # be later serialized and sent out. + def to_h + h = { + name: name, + id: id, + start_time: start_time + } + if closed? + h[:end_time] = end_time + else + h[:in_progress] = true + end + h[:subsegments] = subsegments unless subsegments.empty? + h[:aws] = aws if aws + if http_request || http_response + h[:http] = {} + h[:http][:request] = http_request if http_request + h[:http][:response] = http_response if http_response + end + if (a = annotations.to_h) && !a.empty? + h[:annotations] = a + end + if (m = @metadata) && !m.to_h.empty? + h[:metadata] = m.to_h + end + + h[:parent_id] = parent.id if parent + # make sure the value in hash can only be boolean true + h[:fault] = !!fault if fault + h[:error] = !!error if error + h[:throttle] = !!throttle if throttle + h[:cause] = cause.to_h if cause + h + end + + def to_json + @to_json ||= begin + Oj.dump to_h, mode: :compat, use_as_json: true + end + end + + private + + def find_root_cause(e) + subsegment = subsegments.find { |i| i.exception.hash == e.hash } + return nil unless subsegment + if cause_id = subsegment.cause_id + cause_id + else + subsegment.id + end + end + end +end diff --git a/lib/aws-xray-sdk/model/metadata.rb b/lib/aws-xray-sdk/model/metadata.rb new file mode 100644 index 0000000..ec6e3ce --- /dev/null +++ b/lib/aws-xray-sdk/model/metadata.rb @@ -0,0 +1,77 @@ +require 'oj' +require 'aws-xray-sdk/exceptions' + +module XRay + # Metadata are key-value pairs with values of any type, including objects + # and lists, but that are not indexed. Use metadata to record data + # you want to store in the trace but don't need to use for searching traces. + class Metadata + def initialize(entity) + @data = {} + @entity = entity + end + + def sub_meta(namespace) + @data[namespace] = SubMeta.new(@entity) unless @data[namespace] + @data[namespace] + end + + def to_h + @data + end + end + + # The actual class that stores all data under a certain namespace. + class SubMeta + def initialize(entity) + @data = {} + @entity = entity + end + + def [](key) + @data[key] + end + + def []=(k, v) + raise EntityClosedError if @entity.closed? + @data[k] = v + end + + def update(h) + raise EntityClosedError if @entity.closed? + @data.merge!(h) + end + + def to_h + @data + end + + def to_json + @to_json ||= begin + Oj.dump to_h, mode: :compat, use_as_json: true + end + end + end + + # Singleton facade metadata class doing no-op for performance + # in case of not sampled X-Ray entities. + module FacadeMetadata + class << self + def [](key) + # no-op + end + + def []=(k, v) + # no-op + end + + def update(h) + # no-op + end + + def to_h + # no-op + end + end + end +end diff --git a/lib/aws-xray-sdk/model/segment.rb b/lib/aws-xray-sdk/model/segment.rb new file mode 100644 index 0000000..9306116 --- /dev/null +++ b/lib/aws-xray-sdk/model/segment.rb @@ -0,0 +1,63 @@ +require 'aws-xray-sdk/model/entity' + +module XRay + # The compute resources running your application logic send data + # about their work as segments. A segment provides the resource's name, + # details about the request, and details about the work done. + class Segment + include Entity + attr_accessor :ref_counter, :subsegment_size, :origin, :user + + # @param [String] trace_id Manually crafted trace id. + # @param [String] name Must be specified either on object creation or + # on environment variable `AWS_TRACING_NAME`. The latter has higher precedence. + # @param [String] parent_id ID of the segment/subsegment representing the upstream caller. + def initialize(trace_id: nil, name: nil, parent_id: nil) + @trace_id = trace_id + @name = ENV['AWS_TRACING_NAME'] || name + @parent_id = parent_id + @start_time = Time.now.to_f + @ref_counter = 0 + @subsegment_size = 0 + @sampled = true + end + + def trace_id + @trace_id ||= begin + %[1-#{Time.now.to_i.to_s(16)}-#{SecureRandom.hex(12)}] + end + end + + def add_subsegment(subsegment:) + super subsegment: subsegment + @ref_counter += 1 + @subsegment_size += 1 + end + + def remove_subsegment(subsegment:) + super subsegment: subsegment + @subsegment_size = subsegment_size - subsegment.all_children_count - 1 + end + + def decrement_ref_counter + @ref_counter -= 1 + end + + def ready_to_send? + closed? && ref_counter.zero? + end + + def to_h + h = super + h[:trace_id] = trace_id + h[:origin] = origin if origin + h[:parent_id] = @parent_id if @parent_id + h[:user] = user if user + h + end + + def segment + self + end + end +end diff --git a/lib/aws-xray-sdk/model/subsegment.rb b/lib/aws-xray-sdk/model/subsegment.rb new file mode 100644 index 0000000..7abe59a --- /dev/null +++ b/lib/aws-xray-sdk/model/subsegment.rb @@ -0,0 +1,67 @@ +require 'aws-xray-sdk/model/entity' + +module XRay + # The work done in a single segment can be broke down into subsegments. + # Subsegments provide more granular timing information and details about + # downstream calls that your application made to fulfill the original request. + # A subsegment can contain additional details about a call to an AWS service, + # an external HTTP API, or an SQL database. + class Subsegment + include Entity + + attr_reader :segment + attr_accessor :sql + + # @param [String] name The subsegment name. + # @param [Segment] segment The root parent segment. This segment + # may not be its direct parent. + # @param [String] namespace Currently supported namespaces are + # 'remote', 'aws', 'local'. + def initialize(name:, segment:, namespace: 'local') + @name = name + @segment = segment + @namespace = namespace + @start_time = Time.now.to_f + @sampled = true + end + + def add_subsegment(subsegment:) + super subsegment: subsegment + segment.ref_counter += 1 + segment.subsegment_size += 1 + end + + def remove_subsegment(subsegment:) + super subsegment: subsegment + cur = segment.subsegment_size + segment.subsegment_size = cur - subsegment.all_children_count - 1 + end + + def close(end_time: nil) + super end_time: end_time + segment.decrement_ref_counter + end + + def sql + @sql ||= {} + end + + # Returns the number of its direct and indirect children. + # This is useful when we remove the reference to a subsegment + # and need to keep remaining subsegment size accurate. + def all_children_count + size = subsegments.count + subsegments.each { |v| size += v.all_children_count } + size + end + + def to_h + h = super + h[:trace_id] = segment.trace_id + h[:sql] = sql unless sql.empty? + h[:type] = 'subsegment' + h[:namespace] = namespace if namespace + h + end + end +end diff --git a/lib/aws-xray-sdk/model/trace_header.rb b/lib/aws-xray-sdk/model/trace_header.rb new file mode 100644 index 0000000..4d0f390 --- /dev/null +++ b/lib/aws-xray-sdk/model/trace_header.rb @@ -0,0 +1,54 @@ +require 'aws-xray-sdk/logger' + +module XRay + # The sampling decision and trace ID are added to HTTP requests in + # tracing headers named ``X-Amzn-Trace-Id``. The first X-Ray-integrated + # service that the request hits adds a tracing header, which is read + # by the X-Ray SDK and included in the response. + class TraceHeader + include Logging + attr_accessor :root, :parent_id, :sampled + + # @param [String] root Trace id. + # @param [String] parent_id The id of the parent segment or subsegment. + # @param [Integer] sampled 0 means not sampled. + def initialize(root:, parent_id:, sampled:) + @root = root + @parent_id = parent_id + @sampled = sampled.to_i if sampled + end + + def self.from_header_string(header_str:) + empty_header if header_str.to_s.empty? + header = header_str.delete(' ').downcase + tmp = {} + begin + fields = header.split(';') + fields.each do |f| + pair = f.split('=') + tmp[pair[0].to_sym] = pair[1] + end + new root: tmp[:root], parent_id: tmp[:parent], sampled: tmp[:sampled] + rescue StandardError + logger.warn %(Invalid trace header #{header}. Ignored.) + empty_header + end + end + + # @return [String] The heading string constructed based on this header object. + def header_string + return '' unless root + if !parent_id + %(Root=#{root};Sampled=#{sampled}) + elsif !sampled + %(Root=#{root};Parent=#{parent_id}) + else + %(Root=#{root};Parent=#{parent_id};Sampled=#{sampled}) + end + end + + def self.empty_header + new root: nil, parent_id: nil, sampled: nil + end + end +end diff --git a/lib/aws-xray-sdk/patcher.rb b/lib/aws-xray-sdk/patcher.rb new file mode 100644 index 0000000..1a25fe5 --- /dev/null +++ b/lib/aws-xray-sdk/patcher.rb @@ -0,0 +1,21 @@ +require 'aws-xray-sdk/exceptions' + +module XRay + # Patching external libraries/frameworks to be traced by X-Ray recorder. + module Patcher + # @param [Array] targets A list of libraries/frameworks to patch. + def patch(targets) + targets.each do |l| + case l + when :net_http + require 'aws-xray-sdk/facets/net_http' + when :aws_sdk + require 'aws-xray-sdk/facets/aws_sdk' + XRay::AwsSDKPatcher.patch + else + raise UnsupportedPatchingTargetError.new(%(#{l} is not supported by X-Ray SDK.)) + end + end + end + end +end diff --git a/lib/aws-xray-sdk/plugins/ec2.rb b/lib/aws-xray-sdk/plugins/ec2.rb new file mode 100644 index 0000000..bfb6cb2 --- /dev/null +++ b/lib/aws-xray-sdk/plugins/ec2.rb @@ -0,0 +1,39 @@ +require 'open-uri' +require 'aws-xray-sdk/logger' + +module XRay + module Plugins + # A plugin that gets the EC2 instance-id and AZ if running on an EC2 instance. + module EC2 + include Logging + + ORIGIN = 'AWS::EC2::Instance'.freeze + # http://docs.aws.amazon.com/AWSEC2/latest/UserGuide/ec2-instance-metadata.html#instancedata-data-retrieval + ID_ADDR = 'http://169.254.169.254/latest/meta-data/instance-id'.freeze + AZ_ADDR = 'http://169.254.169.254/latest/meta-data/placement/availability-zone'.freeze + + def self.aws + @@aws ||= begin + instance_id = open(ID_ADDR, open_timeout: 1).read + az = open(AZ_ADDR, open_timeout: 1).read + { + ec2: { + instance_id: instance_id, + availability_zone: az + } + } + rescue StandardError => e + # Two attempts in total to get EC2 metadata + @retries ||= 0 + if @retries < 1 + @retries += 1 + retry + else + @@aws = {} + Logging.logger.warn %(can not get the ec2 instance metadata due to: #{e.message}.) + end + end + end + end + end +end diff --git a/lib/aws-xray-sdk/plugins/ecs.rb b/lib/aws-xray-sdk/plugins/ecs.rb new file mode 100644 index 0000000..c2eb47f --- /dev/null +++ b/lib/aws-xray-sdk/plugins/ecs.rb @@ -0,0 +1,23 @@ +require 'socket' +require 'aws-xray-sdk/logger' + +module XRay + module Plugins + # Due to lack of ECS container metadata service, the only host information + # available is the host name. + module ECS + include Logging + + ORIGIN = 'AWS::ECS::Container'.freeze + + def self.aws + @@aws ||= begin + { ecs: { container: Socket.gethostname } } + rescue StandardError => e + @@aws = {} + Logging.logger.warn %(can not get the ecs container hostname due to: #{e.message}.) + end + end + end + end +end diff --git a/lib/aws-xray-sdk/plugins/elastic_beanstalk.rb b/lib/aws-xray-sdk/plugins/elastic_beanstalk.rb new file mode 100644 index 0000000..b23b7d7 --- /dev/null +++ b/lib/aws-xray-sdk/plugins/elastic_beanstalk.rb @@ -0,0 +1,25 @@ +require 'oj' +require 'aws-xray-sdk/logger' + +module XRay + module Plugins + # A plugin that records information about the elastic beanstalk environment + # hosting your application. + module ElasticBeanstalk + include Logging + + CONF_PATH = '/var/elasticbeanstalk/xray/environment.conf'.freeze + ORIGIN = 'AWS::ElasticBeanstalk::Environment'.freeze + + def self.aws + @@aws ||= begin + file = File.open(CONF_PATH) + { elastic_beanstalk: Oj.load(file) } + rescue StandardError => e + @@aws = {} + Logging.logger.warn %(can not get the environment config due to: #{e.message}.) + end + end + end + end +end diff --git a/lib/aws-xray-sdk/recorder.rb b/lib/aws-xray-sdk/recorder.rb new file mode 100644 index 0000000..de5e24d --- /dev/null +++ b/lib/aws-xray-sdk/recorder.rb @@ -0,0 +1,209 @@ +require 'aws-xray-sdk/configuration' +require 'aws-xray-sdk/exceptions' +require 'aws-xray-sdk/model/segment' +require 'aws-xray-sdk/model/subsegment' +require 'aws-xray-sdk/model/dummy_entities' +require 'aws-xray-sdk/model/annotations' +require 'aws-xray-sdk/model/metadata' + +module XRay + # A global AWS X-Ray recorder that will begin/end segments/subsegments + # and send them to the X-Ray daemon. It is also responsible for managing + # context. + class Recorder + attr_reader :config + + def initialize(user_config: nil) + @config = Configuration.new + @config.configure(user_config) unless user_config.nil? + end + + # Begin a segment for the current context. The recorder + # only keeps one segment at a time. Create a second one without + # closing existing one will overwrite the existing one. + # @return [Segment] thew newly created segment. + def begin_segment(name, trace_id: nil, parent_id: nil, sampled: nil) + seg_name = name || config.name + raise SegmentNameMissingError if seg_name.to_s.empty? + + # sampling decision comes from outside has higher precedence. + sample = sampled.nil? ? config.sample? : sampled + if sample + segment = Segment.new name: seg_name, trace_id: trace_id, parent_id: parent_id + populate_runtime_context(segment) + else + segment = DummySegment.new name: seg_name, trace_id: trace_id, parent_id: parent_id + end + context.store_entity entity: segment + segment + end + + # @return [Segment] the active segment tied to the current context. + # If the current context is under a subsegment, it returns its parent segment. + def current_segment + entity = current_entity + entity.segment if entity + end + + # End the current segment and send it to X-Ray daemon if it is ready. + def end_segment(end_time: nil) + segment = current_segment + return unless segment + segment.close end_time: end_time + context.clear! + emitter.send_entity entity: segment if segment.ready_to_send? + end + + # Begin a new subsegment and add it to be the child of the current active + # subsegment or segment. Also tie the new created subsegment to the current context. + # Its sampling decision will follow its parent. + # @return [Subsegment] the newly created subsegment. + def begin_subsegment(name, namespace: nil, segment: nil) + entity = segment || current_entity + return unless entity + if entity.sampled + subsegment = Subsegment.new name: name, segment: entity.segment, namespace: namespace + else + subsegment = DummySubsegment.new name: name, segment: entity.segment + end + # attach the new created subsegment under the current active entity + entity.add_subsegment subsegment: subsegment + # associate the new subsegment to the current context + context.store_entity entity: subsegment + subsegment + end + + # @return [Subsegment] the active subsegment tied to the current context. + # Returns nil if the current context has no associated subsegment. + def current_subsegment + entity = context.current_entity + entity.is_a?(Subsegment) ? entity : nil + end + + # End the current active subsegment. It also send the entire segment if + # this subsegment is the last one open or stream out subsegments of its + # parent segment if the stream threshold is breached. + def end_subsegment(end_time: nil) + entity = current_entity + return unless entity.is_a?(Subsegment) + entity.close end_time: end_time + # update current context + if entity.parent.closed? + context.clear! + else + context.store_entity entity: entity.parent + end + # check if the entire segment can be send. + # If not, stream subsegments when threshold is reached. + segment = entity.segment + if segment.ready_to_send? + emitter.send_entity entity: segment + elsif streamer.eligible? segment: segment + streamer.stream_subsegments root: segment, emitter: emitter + end + end + + # Record the passed block as a subsegment. + def capture(name, namespace: nil, segment: nil) + subsegment = begin_subsegment name, namespace: namespace, segment: segment + begin + yield subsegment + rescue Exception => e + subsegment.add_exception exception: e + raise e + ensure + end_subsegment + end + end + + # Returns current segment or subsegment that associated to the current context. + # This is a proxy method to Context class current_entity. + def current_entity + context.current_entity + end + + def inject_context(entity, target_ctx: nil) + context.inject_context entity, target_ctx: target_ctx + return unless block_given? + yield + context.clear! + end + + def clear_context + context.clear! + end + + def sampled? + entity = current_entity + if block_given? + yield if entity && entity.sampled + else + entity && entity.sampled + end + end + + # A proxy method to get the annotations from the current active entity. + def annotations + entity = current_entity + if entity + entity.annotations + else + FacadeAnnotations + end + end + + # A proxy method to get the metadata under provided namespace + # from the current active entity. + def metadata(namespace: :default) + entity = current_entity + if entity + entity.metadata(namespace: namespace) + else + FacadeMetadata + end + end + + # A proxy method to XRay::Configuration.configure + def configure(user_config) + config.configure(user_config) + end + + def context + config.context + end + + def sampler + config.sampler + end + + def emitter + config.emitter + end + + def streamer + config.streamer + end + + def segment_naming + config.segment_naming + end + + def sampling_enabled? + config.sampling + end + + private_class_method + + def populate_runtime_context(segment) + aws = {} + config.plugins.each do |p| + meta = p.aws + if meta.is_a?(Hash) && !meta.empty? + aws.merge! meta + segment.origin = p::ORIGIN + end + end + segment.aws = aws unless aws.empty? + end + end +end diff --git a/lib/aws-xray-sdk/sampling/default_sampler.rb b/lib/aws-xray-sdk/sampling/default_sampler.rb new file mode 100644 index 0000000..7ef227f --- /dev/null +++ b/lib/aws-xray-sdk/sampling/default_sampler.rb @@ -0,0 +1,105 @@ +require 'aws-xray-sdk/sampling/sampler' +require 'aws-xray-sdk/sampling/sampling_rule' +require 'aws-xray-sdk/exceptions' + +module XRay + # The default sampler that uses internally defined + # sampling rule and reservoir models to decide sampling decision. + # It also uses the default sampling rule. + # An example definition: + # { + # version: 1, + # rules: [ + # { + # description: 'Player moves.', + # service_name: '*', + # http_method: '*', + # url_path: '/api/move/*', + # fixed_target: 0, + # rate: 0.05 + # } + # ], + # default: { + # fixed_target: 1, + # rate: 0.1 + # } + # } + # This example defines one custom rule and a default rule. + # The custom rule applies a five-percent sampling rate with no minimum + # number of requests to trace for paths under /api/move/. The default + # rule traces the first request each second and 10 percent of additional requests. + # The SDK applies custom rules in the order in which they are defined. + # If a request matches multiple custom rules, the SDK applies only the first rule. + class DefaultSampler + include Sampler + DEFAULT_RULES = { + version: 1, + default: { + fixed_target: 1, + rate: 0.05 + }, + rules: [] + }.freeze + + def initialize + load_sampling_rules(DEFAULT_RULES) + end + + # Return True if the sampler decide to sample based on input + # information and sampling rules. It will first check if any + # custom rule should be applied, if not it falls back to the + # default sampling rule. + # All arugments are extracted from incoming requests by + # X-Ray middleware to perform path based sampling. + def sample_request?(service_name:, url_path:, http_method:) + # directly fallback to non-path-based if all arguments are nil + return sample? unless service_name || url_path || http_method + @custom_rules ||= [] + @custom_rules.each do |c| + return should_sample?(c) if c.applies?(target_name: service_name, target_path: url_path, target_method: http_method) + end + sample? + end + + # Decides if should sample based on non-path-based rule. + # Currently only the default rule is not path-based. + def sample? + should_sample?(@default_rule) + end + + # @param [Hash] v The sampling rules definition. + def sampling_rules=(v) + load_sampling_rules(v) + end + + # @return [Array] An array of [SamplingRule] + def sampling_rules + all_rules = [] + all_rules << @default_rule + all_rules << @custom_rules unless @custom_rules.empty? + all_rules + end + + private + + def should_sample?(rule) + return true if rule.reservoir.take + Random.rand <= rule.rate + end + + def load_sampling_rules(v) + version = v[:version] + if version != 1 + raise InvalidSamplingConfigError, %('Sampling rule version #{version} is not supported.') + end + unless v[:default] + raise InvalidSamplingConfigError, 'A default rule must be provided.' + end + @default_rule = SamplingRule.new rule_definition: v[:default], default: true + @custom_rules = [] + v[:rules].each do |d| + @custom_rules << SamplingRule.new(rule_definition: d) + end + end + end +end diff --git a/lib/aws-xray-sdk/sampling/reservoir.rb b/lib/aws-xray-sdk/sampling/reservoir.rb new file mode 100644 index 0000000..ee3ceb3 --- /dev/null +++ b/lib/aws-xray-sdk/sampling/reservoir.rb @@ -0,0 +1,35 @@ +module XRay + # Keeps track of the number of sampled segments within + # a single second. This class is implemented to be + # thread-safe to achieve accurate sampling. + class Reservoir + # @param [Integer] traces_per_sec The number of guranteed sampled + # segments per second. + def initialize(traces_per_sec: 0) + @traces_per_sec = traces_per_sec + @used_this_sec = 0 + @this_sec = Time.now.to_i + @lock = Mutex.new + end + + # Returns `true` if there are segments left within the + # current second, otherwise returns `false`. + def take + # nothing to provide if reserved is set to 0 + return false if @traces_per_sec.zero? + @lock.synchronize do + now = Time.now.to_i + # refresh time frame + if now != @this_sec + @used_this_sec = 0 + @this_sec = now + end + # return false if reserved item ran out + return false unless @used_this_sec < @traces_per_sec + # otherwise increment used counter and return true + @used_this_sec += 1 + return true + end + end + end +end diff --git a/lib/aws-xray-sdk/sampling/sampler.rb b/lib/aws-xray-sdk/sampling/sampler.rb new file mode 100644 index 0000000..a2d0cca --- /dev/null +++ b/lib/aws-xray-sdk/sampling/sampler.rb @@ -0,0 +1,27 @@ +module XRay + # The sampler interface that calculates if a segment + # should be sampled or not upon creation based on the + # sampling rules it holds. It doesn't respect sampling decision + # from upstream. + module Sampler + # Decides if a segment should be sampled for an incoming request. + # Used in case of middleware. + def sample_request?(service_name:, url_path:, http_method:) + raise 'Not implemented' + end + + # Decides if a segment should be sampled merely based on internal + # sampling rules. + def sample? + raise 'Not implemented' + end + + def sampling_rules=(v) + raise 'Not implemented' + end + + def sampling_rules + raise 'Not implemented' + end + end +end diff --git a/lib/aws-xray-sdk/sampling/sampling_rule.rb b/lib/aws-xray-sdk/sampling/sampling_rule.rb new file mode 100644 index 0000000..1947ea0 --- /dev/null +++ b/lib/aws-xray-sdk/sampling/sampling_rule.rb @@ -0,0 +1,57 @@ +require 'aws-xray-sdk/exceptions' +require 'aws-xray-sdk/sampling/reservoir' +require 'aws-xray-sdk/search_pattern' + +module XRay + # One SamplingRule object represents one rule defined from the rules hash definition. + # It can be either a custom rule or the default rule. + class SamplingRule + attr_reader :fixed_target, :rate, :service_name, + :method, :path, :reservoir, :default + + # @param [Hash] rule_definition Hash that defines a single rule. + # @param default A boolean flag indicates if this rule is the default rule. + def initialize(rule_definition:, default: false) + @fixed_target = rule_definition[:fixed_target] + @rate = rule_definition[:rate] + + @service_name = rule_definition[:service_name] + @method = rule_definition[:http_method] + @path = rule_definition[:url_path] + + @default = default + validate + @reservoir = Reservoir.new traces_per_sec: @fixed_target + end + + # Determines whether or not this sampling rule applies to + # the incoming request based on some of the request's parameters. + # Any None parameters provided will be considered an implicit match. + def applies?(target_name:, target_path:, target_method:) + name_match = !target_name || SearchPattern.wildcard_match?(pattern: @service_name, text: target_name) + path_match = !target_path || SearchPattern.wildcard_match?(pattern: @path, text: target_path) + method_match = !target_method || SearchPattern.wildcard_match?(pattern: @method, text: target_method) + name_match && path_match && method_match + end + + private + + def validate + if @fixed_target < 0 || @rate < 0 + raise InvalidSamplingConfigError, 'All rules must have non-negative values for fixed_target and rate.' + end + + if @default + # validate default rule + if @service_name || @method || @path + raise InvalidSamplingConfigError, 'The default rule must not specify values for url_path, service_name, or http_method.' + end + else + # validate custom rule + unless @service_name && @method && @path + raise InvalidSamplingConfigError, 'All non-default rules must have values for url_path, service_name, and http_method.' + end + end + end + end +end diff --git a/lib/aws-xray-sdk/search_pattern.rb b/lib/aws-xray-sdk/search_pattern.rb new file mode 100644 index 0000000..a17a3ce --- /dev/null +++ b/lib/aws-xray-sdk/search_pattern.rb @@ -0,0 +1,82 @@ +module XRay + # custom pattern matching for performance and the SDK use cases. + module SearchPattern + # Performs a case-insensitive wildcard match against two strings. + # This method works with pseduo-regex chars; specifically ? and * are supported. + # An asterisk (*) represents any combination of characters. + # A question mark (?) represents any single character. + # @param [String] pattern The regex-like pattern to be compared against. + # @param [String] text The string to compare against the pattern. + # @param case_insensitive A boolean flag. Default is true. + def self.wildcard_match?(pattern:, text:, case_insensitive: true) + return false unless pattern && text + pattern_len = pattern.length + text_len = text.length + return text_len.zero? if pattern_len.zero? + # Check the special case of a single * pattern, as it's common + return true if pattern == '*' + + if case_insensitive + # do not mutate original input + pattern = pattern.downcase + text = text.downcase + end + # Infix globs are relatively rare, and the below search is expensive. + # Check for infix globs and, in their absence, do the simple thing. + if !pattern.include?('*') || pattern.index('*') == pattern_len - 1 + return simple_wildcard_match? pattern: pattern, text: text + end + + # The res[i] is used to record if there is a match between + # the first i chars in text and the first j chars in pattern. + # So will return res[textLength+1] in the end + # Loop from the beginning of the pattern + # case not '*': if text[i]==pattern[j] or pattern[j] is '?', + # and res[i] is true, set res[i+1] to true, otherwise false. + # case '*': since '*' can match any globing, as long as there is a true + # in res before i, all the res[i+1], res[i+2],...,res[textLength] + # could be true + res = Array.new(text_len + 1) + res[0] = true + (0...pattern_len).each do |j| + p = pattern[j] + if p != '*' + (text_len - 1).downto(0) do |i| + res[i + 1] = res[i] && (p == '?' || (p == text[i])) + end + else + i = 0 + i += 1 while i <= text_len && !res[i] + (i..text_len + 1).each do |m| + res[m] = true + end + end + res[0] = res[0] && (p == '*') + end + res[text_len] + end + + private_class_method + + def self.simple_wildcard_match?(pattern:, text:) + j = 0 + pattern_len = pattern.length + text_len = text.length + (0...pattern_len).each do |i| + p = pattern[i] + # Presumption for this method is that globs only occur at end + return true if p == '*' + if p == '?' + # No character to match + return false if j == text_len + else + return false if j >= text_len || p != text[j] + end + j += 1 + end + # Ate up all the pattern and didn't end at a glob, so a match + # will have consumed all the text + j == text_len + end + end +end diff --git a/lib/aws-xray-sdk/segment_naming/dynamic_naming.rb b/lib/aws-xray-sdk/segment_naming/dynamic_naming.rb new file mode 100644 index 0000000..a896e2a --- /dev/null +++ b/lib/aws-xray-sdk/segment_naming/dynamic_naming.rb @@ -0,0 +1,26 @@ +require 'aws-xray-sdk/segment_naming/segment_naming' +require 'aws-xray-sdk/search_pattern' + +module XRay + # Decides what name to use on a segment generated from an incoming request. + # This default naming takes the host name and compares it to a pre-defined pattern. + # If the host name matches that pattern, it returns the host name, otherwise + # it returns the fallback name. The host name usually comes from the incoming + # request's headers. + class DynamicNaming + include SegmentNaming + + # @param [String] fallback The fallback name used when there is no match + # between host name and specified pattern. + def initialize(fallback:) + @fallback = fallback + end + + # @param [String] host The host name fetched from the incoming request's header. + def provide_name(host:) + # use fallback name when either the pattern or host name is unavailable. + return fallback unless pattern && !pattern.empty? && host && !host.empty? + SearchPattern.wildcard_match?(pattern: pattern, text: host) ? host : fallback + end + end +end diff --git a/lib/aws-xray-sdk/segment_naming/segment_naming.rb b/lib/aws-xray-sdk/segment_naming/segment_naming.rb new file mode 100644 index 0000000..7d6a176 --- /dev/null +++ b/lib/aws-xray-sdk/segment_naming/segment_naming.rb @@ -0,0 +1,10 @@ +module XRay + # The interface that provides the segment name + # based on host name, pattern and the fallback name. + module SegmentNaming + attr_accessor :fallback, :pattern + def provide_name(host:) + raise 'Not implemented' + end + end +end diff --git a/lib/aws-xray-sdk/streaming/default_streamer.rb b/lib/aws-xray-sdk/streaming/default_streamer.rb new file mode 100644 index 0000000..ebebe6b --- /dev/null +++ b/lib/aws-xray-sdk/streaming/default_streamer.rb @@ -0,0 +1,53 @@ +require 'aws-xray-sdk/streaming/streamer' +require 'aws-xray-sdk/logger' + +module XRay + # The default streamer use subsegment count as the threshold + # for performance reasons and it streams out subtrees + # where all the nodes in it are completed. + class DefaultStreamer + include Streamer + include Logging + + def initialize + @stream_threshold = 50 + end + + # @param [Segment] segment Check if the provided segment exceeds + # the threshold to stream. + def eligible?(segment:) + # only get subsegments to stream from sampled segments. + segment && segment.sampled && segment.subsegment_size >= stream_threshold + end + + # @param [Segment] root The target segment to stream subsegments from. + # @param [Emitter] emitter The emitter employed to send data to the daemon. + def stream_subsegments(root:, emitter:) + children = root.subsegments + children_ready = [] + + unless children.empty? + # Collect ready subtrees from root. + children.each do |child| + children_ready << child if stream_subsegments root: child, emitter: emitter + end + end + + # If this subtree is ready, go back to the root's parent + # to try to find a bigger subtree + return true if children_ready.length == children.length && root.closed? + # Otherwise this subtree has at least one non-ready node. + # Only stream its ready child subtrees. + children_ready.each do |child| + root.remove_subsegment subsegment: child + emitter.send_entity entity: child + end + # Return false so this node won't be added to its parent's ready children. + false + end + + # @return [Integer] The maximum number of subsegments a segment + # can hold before streaming. + attr_accessor :stream_threshold + end +end diff --git a/lib/aws-xray-sdk/streaming/streamer.rb b/lib/aws-xray-sdk/streaming/streamer.rb new file mode 100644 index 0000000..1c52140 --- /dev/null +++ b/lib/aws-xray-sdk/streaming/streamer.rb @@ -0,0 +1,17 @@ +module XRay + # The interface used by the X-Ray recoder to get eligible subsegments + # to be streamed out from a given segment. + module Streamer + def eligible?(segment:) + raise 'Not implemented' + end + + def subsegments_to_stream(segment:, emitter:, force: false) + raise 'Not implemented' + end + + def stream_threshold=(v) + raise 'Not implemented' + end + end +end diff --git a/lib/aws-xray-sdk/version.rb b/lib/aws-xray-sdk/version.rb new file mode 100644 index 0000000..cab21bb --- /dev/null +++ b/lib/aws-xray-sdk/version.rb @@ -0,0 +1,3 @@ +module XRay + VERSION = '0.9.0' +end diff --git a/test/aws-xray-sdk/tc_aws_sdk.rb b/test/aws-xray-sdk/tc_aws_sdk.rb new file mode 100644 index 0000000..7fcbc67 --- /dev/null +++ b/test/aws-xray-sdk/tc_aws_sdk.rb @@ -0,0 +1,113 @@ +require_relative '../test_helper' +require 'aws-xray-sdk' +require 'aws-sdk-s3' +require 'aws-sdk-dynamodb' + +# Test subsegments recording on AWS Ruby SDK +class TestAwsSdk < Minitest::Test + @@recorder = XRay::Recorder.new + config = { + sampling: false, + emitter: XRay::TestHelper::StubbedEmitter.new, + patch: %I[aws_sdk] + } + @@recorder.configure(config) + Aws.config.update xray_recorder: @@recorder + + def setup + @@recorder.context.clear! + @@recorder.emitter.clear + end + + def teardown + @@recorder.context.clear! + @@recorder.emitter.clear + end + + # By default the X-Ray SDK doesn't have parameter whitelisting for S3 APIs. + def test_simple_s3_call + @@recorder.begin_segment name + s3 = Aws::S3::Client.new(stub_responses: true) + bucket_data = s3.stub_data(:list_buckets, buckets: [{ name: '1' }, { name: '2' }]) + s3.stub_responses(:list_buckets, bucket_data) + s3.list_buckets + @@recorder.end_segment + subsegment = @@recorder.emitter.entities[0].subsegments[0] + + assert_equal 'aws', subsegment.namespace + aws_meta = subsegment.aws + assert_equal 'ListBuckets', aws_meta[:operation] + end + + def test_return_list_count + @@recorder.begin_segment name + dynamodb = Aws::DynamoDB::Client.new(stub_responses: true) + table_list = dynamodb.stub_data(:list_tables, table_names: %w[t1 t2]) + dynamodb.stub_responses(:list_tables, table_list) + dynamodb.list_tables + @@recorder.end_segment + subsegment = @@recorder.emitter.entities[0].subsegments[0] + + assert_equal 'aws', subsegment.namespace + aws_meta = subsegment.aws + assert_equal 'ListTables', aws_meta[:operation] + assert_equal 2, aws_meta[:table_count] + refute aws_meta.key?(:table_names) + end + + def test_capture_map_keys + @@recorder.begin_segment name + dynamodb = Aws::DynamoDB::Client.new(stub_responses: true) + + mocked_req = { + request_items: { + table1: { + keys: [{ id: 1 }] + }, + table2: { + keys: [{ id: 2 }] + } + } + } + + mocked_resp = { + consumed_capacity: [ + { + table_name: 'table1', + capacity_units: 1.0 + }, + { + table_name: 'table2', + capacity_units: 2.0 + } + ] + } + + resp = dynamodb.stub_data(:batch_get_item, mocked_resp) + dynamodb.stub_responses(:batch_get_item, resp) + dynamodb.batch_get_item mocked_req + @@recorder.end_segment + subsegment = @@recorder.emitter.entities[0].subsegments[0] + + assert_equal 'aws', subsegment.namespace + aws_meta = subsegment.aws + assert_equal 'BatchGetItem', aws_meta[:operation] + assert_equal %w[table1 table2], aws_meta[:table_names].sort + assert_equal mocked_resp[:consumed_capacity], aws_meta[:consumed_capacity] + end + + def test_capiture_client_error + @@recorder.begin_segment name + s3 = Aws::S3::Client.new(stub_responses: true) + s3.stub_responses(:head_bucket, Timeout::Error) + # makes sure the capture code doesn't swallow the error + assert_raises Timeout::Error do + s3.head_bucket bucket: 'my_bucket' + end + @@recorder.end_segment + subsegment = @@recorder.emitter.entities[0].subsegments[0] + ex_h = subsegment.to_h[:cause][:exceptions][0] + assert_equal 'Timeout::Error', ex_h[:message] + assert_equal 'Timeout::Error', ex_h[:type] + end +end diff --git a/test/aws-xray-sdk/tc_cause.rb b/test/aws-xray-sdk/tc_cause.rb new file mode 100644 index 0000000..b7f9e41 --- /dev/null +++ b/test/aws-xray-sdk/tc_cause.rb @@ -0,0 +1,87 @@ +require_relative '../test_helper' +require 'aws-xray-sdk/model/segment' +require 'aws-xray-sdk/model/subsegment' + +# Test exception recording +class TestCauses < Minitest::Test + def test_simple_exception + segment = XRay::Segment.new name: name + begin + 1 / 0 + rescue ZeroDivisionError => e + segment.add_exception exception: e + end + + assert segment.fault + # check top level fields + h = segment.cause.to_h + assert_equal 1, h[:exceptions].count + assert_nil h[:remote] + refute_nil h[:working_directory] + refute_nil h[:paths] + + # check exception entry + eh = h[:exceptions][0] + assert_equal 'ZeroDivisionError', eh[:type] + refute_nil eh[:message] + + # check stack entry + stack = eh[:stack][0] + assert stack[:line].is_a?(Integer) + refute_nil stack[:label] + refute_nil stack[:path] + end + + def test_remote_flag + segment = XRay::Segment.new name: name + begin + raise StandardError + rescue StandardError => e + segment.add_exception exception: e, remote: true + end + exception = segment.cause.to_h[:exceptions][0] + assert_equal true, exception[:remote] + end + + def test_chained_exception + segment = XRay::Segment.new name: name + begin + fail_and_raise + rescue StandardError => e + segment.add_exception exception: e + end + + # check top level fields + h = segment.cause.to_h + assert_equal 2, h[:exceptions].count + + # check exceptions type + exceptions = h[:exceptions] + assert_equal 'StandardError', exceptions[0][:type] + assert_equal 'ZeroDivisionError', exceptions[1][:type] + end + + def test_duplicate_exception + segment = XRay::Segment.new name: name + subsegment = XRay::Subsegment.new name: name, segment: segment + segment.add_subsegment subsegment: subsegment + begin + 1 / 0 + rescue ZeroDivisionError => e + subsegment.add_exception exception: e + segment.add_exception exception: e + end + + assert_equal subsegment.id, segment.cause_id + cause_id = segment.to_h[:cause] + assert_equal subsegment.id, cause_id + end + + private + + def fail_and_raise + raise ZeroDivisionError + rescue + raise StandardError + end +end diff --git a/test/aws-xray-sdk/tc_context.rb b/test/aws-xray-sdk/tc_context.rb new file mode 100644 index 0000000..9374b28 --- /dev/null +++ b/test/aws-xray-sdk/tc_context.rb @@ -0,0 +1,42 @@ +require 'aws-xray-sdk/exceptions' +require 'aws-xray-sdk/model/segment' +require 'aws-xray-sdk/context/default_context' + +# Test context management +class TestContext < Minitest::Test + def test_segment_crud + context = XRay::DefaultContext.new + context.clear! + segment = XRay::Segment.new name: name + context.store_entity entity: segment + assert_equal segment, context.current_entity + segment2 = XRay::Segment.new name: name + context.store_entity entity: segment2 + assert_equal segment2, context.current_entity + context.clear! + end + + def test_change_context_missing + context = XRay::DefaultContext.new + context.clear! + context.context_missing = 'UNKWON' + assert_equal 'RUNTIME_ERROR', context.context_missing + context.context_missing = 'LOG_ERROR' + assert_equal 'LOG_ERROR', context.context_missing + end + + def test_runtime_error + context = XRay::DefaultContext.new + context.clear! + assert_raises XRay::ContextMissingError do + context.current_entity + end + end + + def test_log_error + context = XRay::DefaultContext.new + context.clear! + context.context_missing = 'LOG_ERROR' + refute context.current_entity + end +end diff --git a/test/aws-xray-sdk/tc_dummy_entities.rb b/test/aws-xray-sdk/tc_dummy_entities.rb new file mode 100644 index 0000000..58c6b09 --- /dev/null +++ b/test/aws-xray-sdk/tc_dummy_entities.rb @@ -0,0 +1,45 @@ +require 'aws-xray-sdk/model/dummy_entities' + +# Test dummy segments and dummy subsegments +class TestDummyEntities < Minitest::Test + def test_no_sample + segment = XRay::DummySegment.new name: name + refute segment.sampled + subegment = XRay::DummySubsegment.new name: segment, segment: segment + refute subegment.sampled + end + + def test_no_meta + segment = XRay::DummySegment.new name: name + subegment = XRay::DummySubsegment.new name: 'dummy', segment: segment + entities = [segment, subegment] + entities.each do |e| + e.metadata.update k: 'v' + e.annotations.update k: 'v' + e.merge_http_request request: { url: '/ping' } + e.merge_http_response response: { status: 200 } + e.aws = { sdk: 'ruby' } + end + + entities.each do |e| + refute e.aws + refute e.http_request + refute e.http_response + refute e.annotations.to_h + refute e.metadata.to_h + end + end + + def test_structure_intact + segment = XRay::DummySegment.new name: name + subsegment1 = XRay::DummySubsegment.new name: 'dummy', segment: segment + subsegment2 = XRay::DummySubsegment.new name: 'dummy', segment: segment + segment.add_subsegment subsegment: subsegment1 + subsegment1.add_subsegment subsegment: subsegment2 + + assert_equal 2, segment.subsegment_size + assert_equal 2, segment.ref_counter + assert_equal subsegment1, segment.subsegments[0] + assert_equal subsegment2, subsegment1.subsegments[0] + end +end diff --git a/test/aws-xray-sdk/tc_dynamic_naming.rb b/test/aws-xray-sdk/tc_dynamic_naming.rb new file mode 100644 index 0000000..dd9411b --- /dev/null +++ b/test/aws-xray-sdk/tc_dynamic_naming.rb @@ -0,0 +1,30 @@ +require 'aws-xray-sdk/segment_naming/dynamic_naming' + +# Test dynamic naming suite +class TestDynamicNaming < Minitest::Test + def test_no_pattern_specified + fallback = 'test' + naming = XRay::DynamicNaming.new fallback: fallback + assert_equal fallback, naming.provide_name(host: 'example.com') + assert_equal fallback, naming.provide_name(host: nil) + assert_equal fallback, naming.provide_name(host: '') + end + + def test_hostname_unavailable + fallback = 'test' + naming = XRay::DynamicNaming.new fallback: fallback + naming.pattern = '*' + assert_equal fallback, naming.provide_name(host: nil) + assert_equal fallback, naming.provide_name(host: '') + end + + def test_pattern_matching + fallback = 'test' + naming = XRay::DynamicNaming.new fallback: fallback + naming.pattern = '*mydomain*' + host = 'www.mydomain.com' + assert_equal host, naming.provide_name(host: host) + refute_equal host, naming.provide_name(host: '127.0.0.1') + assert_equal fallback, naming.provide_name(host: '127.0.0.1') + end +end diff --git a/test/aws-xray-sdk/tc_emitter.rb b/test/aws-xray-sdk/tc_emitter.rb new file mode 100644 index 0000000..9307abe --- /dev/null +++ b/test/aws-xray-sdk/tc_emitter.rb @@ -0,0 +1,25 @@ +require 'aws-xray-sdk/emitter/default_emitter' +require 'aws-xray-sdk/model/segment' +require 'aws-xray-sdk/exceptions' + +class TestEmitter < Minitest::Test + def test_pass_through + segment = XRay::Segment.new name: name + segment.close + emitter = XRay::DefaultEmitter.new + emitter.send_entity entity: segment + end + + def test_invalid_daemon_address + segment = XRay::Segment.new name: name + segment.close + assert_raises XRay::InvalidDaemonAddressError do + emitter = XRay::DefaultEmitter.new + emitter.daemon_address = 'blah' + end + assert_raises XRay::InvalidDaemonAddressError do + emitter = XRay::DefaultEmitter.new + emitter.daemon_address = '127.0.0.1' + end + end +end diff --git a/test/aws-xray-sdk/tc_plugin.rb b/test/aws-xray-sdk/tc_plugin.rb new file mode 100644 index 0000000..0e349a6 --- /dev/null +++ b/test/aws-xray-sdk/tc_plugin.rb @@ -0,0 +1,42 @@ +require 'webmock/minitest' +require 'aws-xray-sdk/plugins/elastic_beanstalk' +require 'aws-xray-sdk/plugins/ec2' +require 'aws-xray-sdk/plugins/ecs' +require 'aws-xray-sdk/recorder' + +# test AWS service plugins pass through +class TestPlugins < Minitest::Test + def test_origin_all_set + assert XRay::Plugins::ElasticBeanstalk::ORIGIN + assert XRay::Plugins::EC2::ORIGIN + assert XRay::Plugins::ECS::ORIGIN + end + + # all plugins should have method 'aws' and it should not break + # when running on any machine. + def test_get_runtime_context + XRay::Plugins::ElasticBeanstalk.aws + stub_request(:any, XRay::Plugins::EC2::ID_ADDR).to_raise(StandardError) + stub_request(:any, XRay::Plugins::EC2::AZ_ADDR).to_raise(StandardError) + XRay::Plugins::EC2.aws + XRay::Plugins::ECS.aws + WebMock.reset! + end + + def test_mocked_ec2_metadata + instance_id = "abc" + az = "us-east-1a" + stub_request(:any, XRay::Plugins::EC2::ID_ADDR) + .to_return(body: instance_id, status: 200) + stub_request(:any, XRay::Plugins::EC2::AZ_ADDR) + .to_return(body: az, status: 200) + expected = { + ec2: { + instance_id: instance_id, + avaliablity_zone: az + } + } + assert expected, XRay::Plugins::EC2.aws + WebMock.reset! + end +end diff --git a/test/aws-xray-sdk/tc_recorder.rb b/test/aws-xray-sdk/tc_recorder.rb new file mode 100644 index 0000000..e0b5a59 --- /dev/null +++ b/test/aws-xray-sdk/tc_recorder.rb @@ -0,0 +1,143 @@ +require_relative '../test_helper' +require 'aws-xray-sdk/recorder' +require 'aws-xray-sdk/exceptions' + +# Test global X-Ray recorder +class TestRecorder < Minitest::Test + @@recorder = XRay::Recorder.new + @@recorder.config.sampling = false + @@recorder.config.emitter = XRay::TestHelper::StubbedEmitter.new + + def setup + @@recorder.context.clear! + @@recorder.emitter.clear + end + + def teardown + @@recorder.context.clear! + @@recorder.emitter.clear + end + + def test_get_segment + segment = @@recorder.begin_segment name: name + assert_equal segment, @@recorder.current_segment + assert segment.sampled + end + + def test_get_subsegment + segment = @@recorder.begin_segment name: name + subsegment = @@recorder.begin_subsegment name: name + assert_equal segment, @@recorder.current_segment + assert_equal subsegment, @@recorder.current_subsegment + assert segment.sampled + end + + def test_send_segment + segment = @@recorder.begin_segment name: name + @@recorder.end_segment + assert_equal segment, @@recorder.emitter.entities[0] + assert_raises XRay::ContextMissingError do + @@recorder.current_segment + end + end + + def test_subsegment_capture + segment = @@recorder.begin_segment name + at = { k: 'v' } + @@recorder.capture('compute') do |subsegment| + subsegment.annotations.update at + end + subsegment = segment.subsegments[0] + assert_equal at, subsegment.annotations.to_h + assert subsegment.closed? + end + + def test_nested_subsegments + @@recorder.begin_segment name + subsegment1 = @@recorder.begin_subsegment name + @@recorder.begin_subsegment name + @@recorder.begin_subsegment name + @@recorder.end_subsegment + @@recorder.end_subsegment + assert_equal subsegment1, @@recorder.current_subsegment + end + + def test_subsegments_streaming + threshold = @@recorder.streamer.stream_threshold + segment = @@recorder.begin_segment name + (threshold + 1).times do |i| + @@recorder.begin_subsegment i.to_s + end + (threshold + 1).times do + @@recorder.end_subsegment + end + assert_equal 2, @@recorder.emitter.entities.count + # segment should not be prematurely sent in this case + refute @@recorder.emitter.entities.include?(segment) + @@recorder.end_segment + + assert_equal 3, @@recorder.emitter.entities.count + assert_equal segment, @@recorder.emitter.entities[2] + end + + def test_sampled_block + @@recorder.begin_segment name + x = 0 + @@recorder.sampled? do + x = 1 + end + assert_equal 1, x + assert @@recorder.sampled? + end + + def test_add_annotation + segment = @@recorder.begin_segment name + @@recorder.annotations[:k] = 1 + assert_equal 1, segment.annotations.to_h[:k] + + subsegment = @@recorder.begin_subsegment name, segment: segment + @@recorder.annotations[:k2] = 2 + assert_equal 2, subsegment.annotations.to_h[:k2] + refute segment.annotations.to_h.key?(:k2) + end + + def test_add_metadata + segment = @@recorder.begin_segment name + @@recorder.metadata[:k] = 1 + assert_equal 1, segment.to_h[:metadata][:default][:k] + subsegment = @@recorder.begin_subsegment name, segment: segment + @@recorder.metadata(namespace: :ns)[:k] = 1 + @@recorder.metadata(namespace: :ns).update k2: 2 + assert_equal 1, subsegment.to_h[:metadata][:ns][:k] + assert_equal 2, subsegment.to_h[:metadata][:ns][:k2] + end + + def test_thread_infection + segment = @@recorder.begin_segment name + thread = Thread.new { + @@recorder.inject_context segment do + @@recorder.begin_subsegment 'my_sub' + @@recorder.end_subsegment + end + } + thread.join + @@recorder.end_segment + + sent_entity = @@recorder.emitter.entities[0] + assert_equal segment, sent_entity + subsegment = sent_entity.subsegments[0] + assert_equal 'my_sub', subsegment.name + assert_equal segment.id, subsegment.parent.id + end + + def test_context_missing_passthrough + recorder = XRay::Recorder.new + recorder.config.context_missing = 'LOG_ERROR' + recorder.annotations[:k] = 1 + recorder.sampled? do + recorder.annotations.update k2: 2 + recorder.metadata.update k3: 3 + end + recorder.metadata[:foo] = 'bar' + end +end diff --git a/test/aws-xray-sdk/tc_sampling.rb b/test/aws-xray-sdk/tc_sampling.rb new file mode 100644 index 0000000..b874316 --- /dev/null +++ b/test/aws-xray-sdk/tc_sampling.rb @@ -0,0 +1,115 @@ +require 'aws-xray-sdk/sampling/sampling_rule' +require 'aws-xray-sdk/sampling/reservoir' +require 'aws-xray-sdk/sampling/default_sampler' +require 'aws-xray-sdk/exceptions' + +# Test sampling models and the default sampler +class TestSampling < Minitest::Test + VALID_RULE_DEF = { + fixed_target: 1, + rate: 0.5, + service_name: '*', + url_path: '*/ping', + http_method: 'PUT' + }.freeze + + def test_reservoir_pass_through + reservoir = XRay::Reservoir.new traces_per_sec: 1 + assert reservoir.take + reservoir2 = XRay::Reservoir.new + refute reservoir2.take + end + + def test_simple_single_rule + rule = XRay::SamplingRule.new rule_definition: VALID_RULE_DEF + + assert_equal 1, rule.fixed_target + assert_equal 0.5, rule.rate + assert_equal '*', rule.service_name + assert_equal '*/ping', rule.path + assert_equal 'PUT', rule.method + assert rule.reservoir.take + end + + def test_rule_request_matching + rule = XRay::SamplingRule.new rule_definition: VALID_RULE_DEF + + assert rule.applies? target_name: nil, target_path: '/ping', target_method: 'put' + assert rule.applies? target_name: 'a', target_path: nil, target_method: 'put' + assert rule.applies? target_name: 'a', target_path: '/ping', target_method: nil + assert rule.applies? target_name: 'a', target_path: '/ping', target_method: 'PUT' + refute rule.applies? target_name: 'a', target_path: '/sping', target_method: 'PUT' + end + + def test_invalid_single_rule + # missing path + rule_def1 = { + fixed_target: 1, + rate: 0.5, + service_name: '*', + http_method: 'GET' + } + assert_raises XRay::InvalidSamplingConfigError do + XRay::SamplingRule.new rule_definition: rule_def1 + end + # extra field for default rule + rule_def2 = { + fixed_target: 1, + rate: 0.5, + service_name: '*' + } + assert_raises XRay::InvalidSamplingConfigError do + XRay::SamplingRule.new rule_definition: rule_def2, default: true + end + # invalid value + rule_def3 = { + fixed_target: 1, + rate: -0.5 + } + assert_raises XRay::InvalidSamplingConfigError do + XRay::SamplingRule.new rule_definition: rule_def3, default: true + end + end + + EXAMPLE_CONFIG = { + version: 1, + rules: [ + { + description: 'Player moves.', + service_name: '*', + http_method: '*', + url_path: '*/ping', + fixed_target: 0, + rate: 0 + } + ], + default: { + fixed_target: 1, + rate: 0.1 + } + }.freeze + + def test_default_sampler + sampler = XRay::DefaultSampler.new + assert sampler.sample? + # should only has default rule + assert_equal 1, sampler.sampling_rules.count + sampler.sampling_rules = EXAMPLE_CONFIG + # now has one extra custom rule + assert_equal 2, sampler.sampling_rules.count + # don't sample health checks based on the custom rule + refute sampler.sample_request? service_name: '*', url_path: '/ping', http_method: 'GET' + end + + def test_invalid_rules_config + sampler = XRay::DefaultSampler.new + config1 = EXAMPLE_CONFIG.merge(version: nil) + assert_raises XRay::InvalidSamplingConfigError do + sampler.sampling_rules = config1 + end + config2 = EXAMPLE_CONFIG.merge(default: nil) + assert_raises XRay::InvalidSamplingConfigError do + sampler.sampling_rules = config2 + end + end +end diff --git a/test/aws-xray-sdk/tc_search_pattern.rb b/test/aws-xray-sdk/tc_search_pattern.rb new file mode 100644 index 0000000..a787fcb --- /dev/null +++ b/test/aws-xray-sdk/tc_search_pattern.rb @@ -0,0 +1,40 @@ +require 'aws-xray-sdk/search_pattern' + +# Test wildcard matching +class TestSearchPattern < Minitest::Test + def test_corner_case + assert XRay::SearchPattern.wildcard_match? pattern: '*', text: '' + assert XRay::SearchPattern.wildcard_match? pattern: '', text: '' + refute XRay::SearchPattern.wildcard_match? pattern: '', text: 'a' + refute XRay::SearchPattern.wildcard_match? pattern: '*', text: nil + refute XRay::SearchPattern.wildcard_match? pattern: nil, text: '' + refute XRay::SearchPattern.wildcard_match? pattern: nil, text: nil + end + + def test_no_special_character + pattern = 'test' + assert XRay::SearchPattern.wildcard_match? pattern: pattern, text: pattern + refute XRay::SearchPattern.wildcard_match? pattern: pattern, text: "a#{pattern}" + refute XRay::SearchPattern.wildcard_match? pattern: pattern, text: "#{pattern}a" + end + + def test_star + assert XRay::SearchPattern.wildcard_match? pattern: '*', text: 'test' + assert XRay::SearchPattern.wildcard_match? pattern: 'www.*', text: 'www.test.com' + assert XRay::SearchPattern.wildcard_match? pattern: '*.com', text: 'test.com' + assert XRay::SearchPattern.wildcard_match? pattern: 'www.*.com', text: 'www.test.com' + assert XRay::SearchPattern.wildcard_match? pattern: '*test.*', text: 'www.test.org' + end + + def test_question_mark + assert XRay::SearchPattern.wildcard_match? pattern: 'te?t', text: 'test' + assert XRay::SearchPattern.wildcard_match? pattern: '??st', text: 'test' + assert XRay::SearchPattern.wildcard_match? pattern: 'te??', text: 'test' + refute XRay::SearchPattern.wildcard_match? pattern: 'te?t', text: 'tet' + end + + def test_mixed_characters + assert XRay::SearchPattern.wildcard_match? pattern: '*test?.*', text: 'www.test3.com' + refute XRay::SearchPattern.wildcard_match? pattern: '*test?.*', text: 'www.test.com' + end +end diff --git a/test/aws-xray-sdk/tc_segment.rb b/test/aws-xray-sdk/tc_segment.rb new file mode 100644 index 0000000..ffbb284 --- /dev/null +++ b/test/aws-xray-sdk/tc_segment.rb @@ -0,0 +1,140 @@ +require 'bigdecimal' +require 'aws-xray-sdk/model/segment' +require 'aws-xray-sdk/model/subsegment' + +# Segment data model test suite +class TestSegment < Minitest::Test + def test_minimal_segment + segment = XRay::Segment.new name: name + assert_equal name, segment.name + assert segment.sampled + assert_nil segment.end_time + refute_nil segment.start_time + refute_nil segment.trace_id + refute_nil segment.id + end + + def test_minimal_json + segment = XRay::Segment.new name: name + segment.close + json = segment.to_json + h = eval(json) + refute_nil h[:trace_id] + refute_nil h[:id] + refute_nil h[:start_time] + refute_nil h[:end_time] + refute h.key?(:error) + refute h.key?(:throttle) + refute h.key?(:fault) + refute h.key?(:cause) + refute h.key?(:metadata) + refute h.key?(:annotations) + refute h.key?(:user) + refute h.key?(:parent_id) + refute h.key?(:in_progress) + refute h.key?(:subsegments) + refute h.key?(:http) + refute h.key?(:aws) + end + + def test_apply_status_code + segment1 = XRay::Segment.new name: name + segment1.apply_status_code status: 200 + refute segment1.fault + refute segment1.error + refute segment1.throttle + assert_equal 200, segment1.http_response[:status] + + segment2 = XRay::Segment.new name: name + segment2.apply_status_code status: 500 + assert segment2.fault + refute segment2.error + refute segment2.throttle + + segment3 = XRay::Segment.new name: name + segment3.apply_status_code status: 400 + assert segment3.error + refute segment3.fault + refute segment3.throttle + + segment4 = XRay::Segment.new name: name + segment4.apply_status_code status: 429 + assert segment4.error + assert segment4.throttle + refute segment4.fault + end + + def test_annotations + segment = XRay::Segment.new name: name + segment.annotations.update key1: 'value', key2: 2 + assert_equal segment.annotations[:key1], 'value' + assert_equal segment.annotations[:key2], 2 + + segment.annotations[:key2] = 3 + assert_equal segment.annotations.to_h, { key1: 'value' }.merge(key2: 3) + + # annotation key contains invalid character should be dropped. + at3 = { 福: true } + segment.annotations.update at3 + refute segment.annotations.to_h.key?(:福) + + # annotation value with unsupported type should be dropped. + segment.annotations[:key3] = {} + refute segment.annotations.to_h.key?(:key3) + end + + def test_numeric_annotation_value + segment = XRay::Segment.new name: name + annotations = { + k1: Rational(1 / 2), + k2: BigDecimal(1), + k3: 1 / 0.0, # Infinity + k4: 0 / 0.0, # NaN + } + segment.annotations.update annotations + h = eval(segment.to_json) + at_h = h[:annotations] + assert_equal 'Infinity', at_h[:k3] + assert_equal 'NaN', at_h[:k4] + end + + def test_add_subsegment + segment = XRay::Segment.new name: name + subsegment = XRay::Subsegment.new name: name, segment: segment + segment.add_subsegment subsegment: subsegment + assert_equal segment.ref_counter, 1 + assert_equal segment.subsegment_size, 1 + assert_equal segment.subsegments.count, 1 + assert_equal segment.subsegments[0], subsegment + + subsegment.close + assert_equal segment.ref_counter, 0 + refute segment.ready_to_send? + end + + def test_remove_subsegment + segment = XRay::Segment.new name: name + subsegment = XRay::Subsegment.new name: name, segment: segment + segment.add_subsegment subsegment: subsegment + segment.remove_subsegment subsegment: subsegment + assert segment.subsegments.empty? + assert_equal segment.subsegment_size, 0 + end + + def test_mutate_closed + segment = XRay::Segment.new name: name + segment.close + assert_raises XRay::EntityClosedError do + segment.close + end + + assert_raises XRay::EntityClosedError do + segment.annotations[:k] = 1 + end + + assert_raises XRay::EntityClosedError do + subsegment = XRay::Subsegment.new name: name, segment: segment + segment.add_subsegment subsegment: subsegment + end + end +end diff --git a/test/aws-xray-sdk/tc_streaming.rb b/test/aws-xray-sdk/tc_streaming.rb new file mode 100644 index 0000000..38a36c3 --- /dev/null +++ b/test/aws-xray-sdk/tc_streaming.rb @@ -0,0 +1,88 @@ +require_relative '../test_helper' +require 'aws-xray-sdk/model/segment' +require 'aws-xray-sdk/model/subsegment' +require 'aws-xray-sdk/streaming/default_streamer' + +# Subtree streaming test suite +class TestStreaming < Minitest::Test + + def test_segment_eligibility + segment = XRay::Segment.new name: name + subsegment = XRay::Subsegment.new name: name, segment: segment + segment.add_subsegment subsegment: subsegment + streamer = XRay::DefaultStreamer.new + streamer.stream_threshold = 1 + + refute streamer.eligible? segment: nil + assert streamer.eligible? segment: segment + end + + def test_single_subsegment + segment = XRay::Segment.new name: name + subsegment = XRay::Subsegment.new name: name, segment: segment + segment.add_subsegment subsegment: subsegment + subsegment.close + + streamer = XRay::DefaultStreamer.new + streamer.stream_threshold = 1 + emitter = XRay::TestHelper::StubbedEmitter.new + streamer.stream_subsegments root: segment, emitter: emitter + assert_equal 1, emitter.entities.count + assert_equal 0, segment.subsegment_size + end + + # all segment/subsegments has only one child subsegment. + def test_single_path + segment = XRay::Segment.new name: name + subsegment1 = XRay::Subsegment.new name: name, segment: segment + subsegment2 = XRay::Subsegment.new name: name, segment: segment + subsegment3 = XRay::Subsegment.new name: name, segment: segment + segment.add_subsegment subsegment: subsegment1 + subsegment1.add_subsegment subsegment: subsegment2 + subsegment2.add_subsegment subsegment: subsegment3 + subsegment3.close + subsegment2.close + + streamer = XRay::DefaultStreamer.new + streamer.stream_threshold = 1 + emitter = XRay::TestHelper::StubbedEmitter.new + streamer.stream_subsegments root: segment, emitter: emitter + + streamed_out = emitter.entities + # subtree with root node subsegment2 should be streamed out + assert_equal 1, streamed_out.count + assert_equal subsegment2, streamed_out[0] + assert_equal 1, segment.subsegment_size + assert_equal subsegment1, segment.subsegments[0] + # check reference removal + refute segment.subsegments.include?(subsegment2) + end + + # root segment has two subtrees eligible to stream + def test_multi_subtrees + segment = XRay::Segment.new name: name + subsegments = [] + 4.times do + subsegments << XRay::Subsegment.new(name: name, segment: segment) + end + subsegments[0].add_subsegment subsegment: subsegments[2] + subsegments[1].add_subsegment subsegment: subsegments[3] + segment.add_subsegment subsegment: subsegments[0] + segment.add_subsegment subsegment: subsegments[1] + subsegments.each &:close + + streamer = XRay::DefaultStreamer.new + streamer.stream_threshold = 1 + emitter = XRay::TestHelper::StubbedEmitter.new + streamer.stream_subsegments root: segment, emitter: emitter + + streamed_out = emitter.entities + # subtree with root node subsegment0 and subsegment1 should be streamed out + assert_equal 2, streamed_out.count + assert streamed_out.include?(subsegments[0]) + assert streamed_out.include?(subsegments[1]) + assert_equal 0, segment.subsegment_size + # check reference removal + assert segment.subsegments.empty? + end +end diff --git a/test/aws-xray-sdk/tc_subsegment.rb b/test/aws-xray-sdk/tc_subsegment.rb new file mode 100644 index 0000000..e29e465 --- /dev/null +++ b/test/aws-xray-sdk/tc_subsegment.rb @@ -0,0 +1,56 @@ +require 'aws-xray-sdk/model/segment' +require 'aws-xray-sdk/model/subsegment' + +# Subsegment data model test suite +class TestSubsegment < Minitest::Test + def test_minimal_subsegment + segment = XRay::Segment.new name: name + subsegment = XRay::Subsegment.new name: name, segment: segment + assert_equal segment, subsegment.segment + assert subsegment.sampled + assert_nil subsegment.end_time + refute_nil subsegment.start_time + refute_nil subsegment.id + end + + def test_minimal_json + segment = XRay::Segment.new name: name + subsegment = XRay::Subsegment.new name: name, segment: segment + segment.add_subsegment subsegment: subsegment + json = segment.to_json + h = eval(json) + refute_nil h[:subsegments] + refute h[:subsegments].empty? + + sub_h = h[:subsegments][0] + assert_equal 'subsegment', sub_h[:type] + assert_equal segment.id, sub_h[:parent_id] + assert_equal segment.trace_id, sub_h[:trace_id] + assert sub_h[:in_progress] + refute_nil sub_h[:start_time] + refute sub_h.key?(:sql) + refute sub_h.key?(:end_time) + end + + def test_nested_subsegments + segment = XRay::Segment.new name: name + subseg1 = XRay::Subsegment.new name: name, segment: segment + subseg2 = XRay::Subsegment.new name: name, segment: segment + subseg3 = XRay::Subsegment.new name: name, segment: segment + subseg4 = XRay::Subsegment.new name: name, segment: segment + segment.add_subsegment subsegment: subseg1 + subseg1.add_subsegment subsegment: subseg2 + subseg1.add_subsegment subsegment: subseg3 + subseg3.add_subsegment subsegment: subseg4 + + assert_equal 4, segment.subsegment_size + assert_equal 4, segment.ref_counter + + subseg4.close + subseg3.close + assert_equal 2, segment.ref_counter + + subseg1.remove_subsegment subsegment: subseg3 + assert_equal 2, segment.subsegment_size + end +end diff --git a/test/aws-xray-sdk/tc_trace_header.rb b/test/aws-xray-sdk/tc_trace_header.rb new file mode 100644 index 0000000..e263fee --- /dev/null +++ b/test/aws-xray-sdk/tc_trace_header.rb @@ -0,0 +1,63 @@ +require 'aws-xray-sdk/model/trace_header' + +# Test TraceHeader data model +class TestTraceHeader < Minitest::Test + TRACE_ID = '1-5759e988-bd862e3fe1be46a994272793'.freeze + PARENT_ID = '53995c3f42cd8ad8'.freeze + + def test_no_sample + header = XRay::TraceHeader.new root: TRACE_ID, parent_id: PARENT_ID, sampled: nil + refute header.sampled + assert_equal TRACE_ID, header.root + assert_equal PARENT_ID, header.parent_id + assert_equal %(Root=#{TRACE_ID};Parent=#{PARENT_ID}), header.header_string + end + + def test_no_parent + header = XRay::TraceHeader.new root: TRACE_ID, parent_id: nil, sampled: 1 + assert_equal 1, header.sampled + assert_equal TRACE_ID, header.root + assert_equal %(Root=#{TRACE_ID};Sampled=1), header.header_string + end + + def test_from_full_header_str + header_str = %(Root=#{TRACE_ID};Parent=#{PARENT_ID};Sampled=0) + header = XRay::TraceHeader.from_header_string header_str: header_str + assert_equal 0, header.sampled + assert_equal TRACE_ID, header.root + assert_equal PARENT_ID, header.parent_id + end + + def test_from_partial_header_str + # missing parent_id + header_str1 = %(Root=#{TRACE_ID};Sampled=0) + header = XRay::TraceHeader.from_header_string header_str: header_str1 + assert_equal 0, header.sampled + assert_equal TRACE_ID, header.root + refute header.parent_id + + # missing sampling + header_str2 = %(Root=#{TRACE_ID};Parent=#{PARENT_ID}) + header = XRay::TraceHeader.from_header_string header_str: header_str2 + assert_equal PARENT_ID, header.parent_id + assert_equal TRACE_ID, header.root + refute header.sampled + end + + def test_invalid_header_str + header_str = 'some random header string' + header = XRay::TraceHeader.from_header_string header_str: header_str + refute header.sampled + refute header.root + refute header.parent_id + end + + def test_header_str_variant + # casing and whitespaces + header_str = %(ROOT=#{TRACE_ID}; PARENT=#{PARENT_ID}; SAMPLED=1) + header = XRay::TraceHeader.from_header_string header_str: header_str + assert_equal 1, header.sampled + assert_equal TRACE_ID, header.root + assert_equal PARENT_ID, header.parent_id + end +end diff --git a/test/test_helper.rb b/test/test_helper.rb new file mode 100644 index 0000000..cfdc8e6 --- /dev/null +++ b/test/test_helper.rb @@ -0,0 +1,26 @@ +require 'simplecov' +SimpleCov.start + +require 'minitest/autorun' +require 'aws-xray-sdk/emitter/emitter' + +module XRay + # holds all testing needed classes and methods + module TestHelper + # Emitter for testing that holds all entity it is about to send. + class StubbedEmitter + include Emitter + + attr_reader :entities + + def send_entity(entity:) + @entities ||= [] + @entities << entity + end + + def clear + @entities = [] + end + end + end +end