pax_global_header00006660000000000000000000000064141477027710014524gustar00rootroot0000000000000052 comment=9a86e9739dbda5cc8f4952c64043e1fec91c9512 jaeger-client-ruby-1.2.0/000077500000000000000000000000001414770277100152145ustar00rootroot00000000000000jaeger-client-ruby-1.2.0/.github/000077500000000000000000000000001414770277100165545ustar00rootroot00000000000000jaeger-client-ruby-1.2.0/.github/workflows/000077500000000000000000000000001414770277100206115ustar00rootroot00000000000000jaeger-client-ruby-1.2.0/.github/workflows/ci.yml000066400000000000000000000011021414770277100217210ustar00rootroot00000000000000name: Ruby on: push: branches: [ master ] pull_request: branches: [ master ] jobs: test: runs-on: ubuntu-latest strategy: matrix: ruby-version: ['2.7.2', '3.0'] services: postgres: image: docker steps: - uses: actions/checkout@v2 with: submodules: true - name: Set up Ruby uses: ruby/setup-ruby@v1 with: ruby-version: ${{ matrix.ruby-version }} bundler-cache: true - name: Run tests run: bundle exec rake - name: Run crossdock run: make crossdock jaeger-client-ruby-1.2.0/.gitignore000066400000000000000000000001771414770277100172110ustar00rootroot00000000000000/.bundle/ /.yardoc /Gemfile.lock /_yardoc/ /coverage/ /doc/ /pkg/ /spec/reports/ /tmp/ # rspec failure tracking .rspec_status jaeger-client-ruby-1.2.0/.gitmodules000066400000000000000000000001251414770277100173670ustar00rootroot00000000000000[submodule "idl"] path = idl url = https://github.com/jaegertracing/jaeger-idl.git jaeger-client-ruby-1.2.0/.rspec000066400000000000000000000000371414770277100163310ustar00rootroot00000000000000--format documentation --color jaeger-client-ruby-1.2.0/.rubocop.yml000066400000000000000000000014371414770277100174730ustar00rootroot00000000000000require: rubocop-rspec AllCops: Exclude: - 'thrift/**/*' - 'vendor/bundle/**/*' NewCops: enable TargetRubyVersion: '2.7' Style/Documentation: Enabled: no Style/IfUnlessModifier: Enabled: no RSpec/NestedGroups: Max: 4 RSpec/ExampleLength: Enabled: no RSpec/MultipleExpectations: Enabled: no RSpec/MultipleMemoizedHelpers: Enabled: no RSpec/MessageSpies: Enabled: no Metrics/BlockLength: Enabled: no Metrics/MethodLength: Enabled: no Metrics/AbcSize: Enabled: no Metrics/ClassLength: Enabled: no Metrics/ParameterLists: Enabled: no Lint/UnusedMethodArgument: Enabled: no Style/FrozenStringLiteralComment: Enabled: yes EnforcedStyle: always Include: - 'lib/**/*' Layout/LineLength: Max: 120 Style/SingleLineMethods: Enabled: false jaeger-client-ruby-1.2.0/Gemfile000066400000000000000000000001421414770277100165040ustar00rootroot00000000000000source 'https://rubygems.org' # Specify your gem's dependencies in jaeger-client.gemspec gemspec jaeger-client-ruby-1.2.0/LICENSE.txt000066400000000000000000000020701414770277100170360ustar00rootroot00000000000000The MIT License (MIT) Copyright (c) 2017 Indrek Juhkam Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. jaeger-client-ruby-1.2.0/Makefile000066400000000000000000000000341414770277100166510ustar00rootroot00000000000000-include crossdock/rules.mk jaeger-client-ruby-1.2.0/README.md000066400000000000000000000175161414770277100165050ustar00rootroot00000000000000Jaeger::Client ================ [![Gem Version](https://badge.fury.io/rb/jaeger-client.svg)](https://rubygems.org/gems/jaeger-client) [![Build Status](https://github.com/salemove/jaeger-client-ruby/actions/workflows/ci.yml/badge.svg?branch=master)](https://github.com/salemove/jaeger-client-ruby/actions/workflows/ci.yml?query=branch%3Amaster) OpenTracing Tracer implementation for Jaeger in Ruby ## Installation Add this line to your application's Gemfile: ```ruby gem 'jaeger-client' ``` ## Usage ```ruby require 'jaeger/client' OpenTracing.global_tracer = Jaeger::Client.build(host: 'localhost', port: 6831, service_name: 'echo') OpenTracing.start_active_span('span name') do # do something OpenTracing.start_active_span('inner span name') do # do something else end end # don't kill the program too soon, allow time for the background reporter to send the traces sleep 2 ``` See [opentracing-ruby](https://github.com/opentracing/opentracing-ruby) for more examples. ### Reporters #### RemoteReporter (default) RemoteReporter buffers spans in memory and sends them out of process using Sender. There are two senders: `UdpSender` (default) and `HttpSender`. To use `HttpSender`: ```ruby OpenTracing.global_tracer = Jaeger::Client.build( service_name: 'service_name', reporter: Jaeger::Reporters::RemoteReporter.new( sender: Jaeger::HttpSender.new( url: 'http://localhost:14268/api/traces', headers: { 'key' => 'value' }, # headers key is optional encoder: Jaeger::Encoders::ThriftEncoder.new(service_name: 'service_name') ), flush_interval: 10 ) ) ``` #### NullReporter NullReporter ignores all spans. ```ruby OpenTracing.global_tracer = Jaeger::Client.build( service_name: 'service_name', reporter: Jaeger::Reporters::NullReporter.new ) ``` #### LoggingReporter LoggingReporter prints some details about the span using `logger`. This is meant only for debugging. Do not parse and use this information for anything critical. The implemenation can change at any time. ```ruby OpenTracing.global_tracer = Jaeger::Client.build( service_name: 'service_name', reporter: Jaeger::Reporters::LoggingReporter.new ) ``` LoggingReporter can also use a custom logger. For this provide logger using `logger` keyword argument. ### Samplers #### Const sampler `Const` sampler always makes the same decision for new traces depending on the initialization value. Set `sampler` to: `Jaeger::Samplers::Const.new(true)` to mark all new traces as sampled. #### Probabilistic sampler `Probabilistic` sampler samples traces with probability equal to `rate` (must be between 0.0 and 1.0). This can be enabled by setting `Jaeger::Samplers::Probabilistic.new(rate: 0.1)` #### RateLimiting sampler `RateLimiting` sampler samples at most `max_traces_per_second`. The distribution of sampled traces follows burstiness of the service, i.e. a service with uniformly distributed requests will have those requests sampled uniformly as well, but if requests are bursty, especially sub-second, then a number of sequential requests can be sampled each second. Set `sampler` to `Jaeger::Samplers::RateLimiting.new(max_traces_per_second: 100)` #### GuaranteedThroughputProbabilistic sampler `GuaranteedThroughputProbabilistic` is a sampler that guarantees a throughput by using a Probabilistic sampler and RateLimiting sampler The RateLimiting sampler is used to establish a lower_bound so that every operation is sampled at least once in the time interval defined by the lower_bound. Set `sampler` to `Jaeger::Samplers::GuaranteedThroughputProbabilistic.new(lower_bound: 10, rate: 0.001)` #### PerOperation sampler `PerOperation` sampler leverages both Probabilistic sampler and RateLimiting sampler via the GuaranteedThroughputProbabilistic sampler. This sampler keeps track of all operations and delegates calls the the respective GuaranteedThroughputProbabilistic sampler. Set `sampler` to ```ruby Jaeger::Samplers::PerOperation.new( strategies: { per_operation_strategies: [ { operation: 'GET /articles', probabilistic_sampling: { sampling_rate: 0.5 } }, { operation: 'POST /articles', probabilistic_sampling: { sampling_rate: 1.0 } } ], default_sampling_probability: 0.001, default_lower_bound_traces_per_second: 1.0 / (10.0 * 60.0) }, max_operations: 1000 ) ``` #### RemoteControlled sampler `RemoteControlled` sampler is a sampler that is controller by jaeger agent. It starts out with `Probabilistic` sampler. It polls the jaeger-agent and changes sampling strategy accordingly. Set `sampler` to `Jaeger::Client::Samplers::RemoteControlled.new(service_name: 'service_name')`. RemoteControlled sampler options: | Param | Required | Description | |-------------------|----------|-------------| | service_name | x | name of the current service / application, same as given to Tracer | | sampler | | initial sampler to use prior to retrieving strategies from Agent | | refresh_interval | | interval in seconds before sampling strategy refreshes (0 to not refresh, defaults to 60) | | host | | host for jaeger-agent (defaults to 'localhost') | | port | | port for jaeger-agent for SamplingManager endpoint (defaults to 5778) | | logger | | logger for communication between jaeger-agent (default to $stdout logger) | ### TraceContext compatible header propagation It is possible to use [W3C Trace Context](https://www.w3.org/TR/trace-context/#overview) headers to propagate the tracing information. To set it up you need to change FORMAT_RACK injector and extractor. ```ruby OpenTracing.global_tracer = Jaeger::Client.build( service_name: 'service_name', injectors: { OpenTracing::FORMAT_RACK => [Jaeger::Injectors::TraceContextRackCodec] }, extractors: { OpenTracing::FORMAT_RACK => [Jaeger::Extractors::TraceContextRackCodec] } ) ``` ### Zipkin HTTP B3 compatible header propagation Jaeger Tracer supports Zipkin B3 Propagation HTTP headers, which are used by a lot of Zipkin tracers. This means that you can use Jaeger in conjunction with OpenZipkin tracers. To set it up you need to change FORMAT_RACK injector and extractor. ```ruby OpenTracing.global_tracer = Jaeger::Client.build( service_name: 'service_name', injectors: { OpenTracing::FORMAT_RACK => [Jaeger::Injectors::B3RackCodec] }, extractors: { OpenTracing::FORMAT_RACK => [Jaeger::Extractors::B3RackCodec] } ) ``` It's also possible to set up multiple injectors and extractors. Each injector will be called in sequence. Note that if multiple injectors are using the same keys then the values will be overwritten. If multiple extractors is used then the span context from the first match will be returned. ### Process Tags Jaeger Tracer allows you to define process level tags. By default the tracer provides `jaeger.version`, `ip` and `hostname`. You may want to overwrite `ip` or `hostname` if the tracer cannot auto-detect them. ```ruby OpenTracing.global_tracer = Jaeger::Client.build( service_name: 'service_name', tags: { 'hostname' => 'custom-hostname', 'custom_tag' => 'custom-tag-value' } ) ``` ## Development After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment. To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org). ## Contributing Bug reports and pull requests are welcome on GitHub at https://github.com/salemove/jaeger-client-ruby ## License The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT). jaeger-client-ruby-1.2.0/Rakefile000066400000000000000000000002721414770277100166620ustar00rootroot00000000000000require 'bundler/gem_tasks' require 'rspec/core/rake_task' require 'rubocop/rake_task' RSpec::Core::RakeTask.new(:spec) RuboCop::RakeTask.new(:rubocop) task default: %i[rubocop spec] jaeger-client-ruby-1.2.0/bin/000077500000000000000000000000001414770277100157645ustar00rootroot00000000000000jaeger-client-ruby-1.2.0/bin/console000077500000000000000000000005341414770277100173560ustar00rootroot00000000000000#!/usr/bin/env ruby require 'bundler/setup' require 'jaeger/client' # You can add fixtures and/or initialization code here to make experimenting # with your gem easier. You can also use a different console, if you like. # (If you use this, don't forget to add pry to your Gemfile!) # require "pry" # Pry.start require 'irb' IRB.start(__FILE__) jaeger-client-ruby-1.2.0/bin/setup000077500000000000000000000002031414770277100170450ustar00rootroot00000000000000#!/usr/bin/env bash set -euo pipefail IFS=$'\n\t' set -vx bundle install # Do any other automated setup that you need to do here jaeger-client-ruby-1.2.0/crossdock/000077500000000000000000000000001414770277100172065ustar00rootroot00000000000000jaeger-client-ruby-1.2.0/crossdock/Dockerfile000066400000000000000000000016041414770277100212010ustar00rootroot00000000000000FROM ruby:2.7-alpine ENV APP_HOME /app # git is required by bundler to run jaeger gem with local path RUN apk add --no-cache git # Add only files needed for installing gem dependencies. This allows us to # change other files without needing to install gems every time when building # the docker image. ADD Gemfile Gemfile.lock jaeger-client.gemspec $APP_HOME/ ADD lib/jaeger/client/version.rb $APP_HOME/lib/jaeger/client/ ADD crossdock/Gemfile crossdock/Gemfile.lock $APP_HOME/crossdock/ RUN gem install bundler -v $(tail -n 1 $APP_HOME/Gemfile.lock | awk '{$1=$1};1') RUN apk add --no-cache --virtual .app-builddeps build-base \ && cd $APP_HOME && bundle install \ && cd $APP_HOME/crossdock && bundle install \ && apk del .app-builddeps ADD . $APP_HOME RUN chown -R nobody:nogroup $APP_HOME USER nobody WORKDIR $APP_HOME/crossdock CMD ["bundle", "exec", "./server"] EXPOSE 8080-8082 jaeger-client-ruby-1.2.0/crossdock/Gemfile000066400000000000000000000001631414770277100205010ustar00rootroot00000000000000source 'https://rubygems.org' gem 'jaeger-client', path: '../' gem 'rack' gem 'sinatra' gem 'webrick', '~> 1.4.2' jaeger-client-ruby-1.2.0/crossdock/Gemfile.lock000066400000000000000000000011141414770277100214250ustar00rootroot00000000000000PATH remote: .. specs: jaeger-client (1.1.0) opentracing (~> 0.3) thrift GEM remote: https://rubygems.org/ specs: mustermann (1.1.1) ruby2_keywords (~> 0.0.1) opentracing (0.5.0) rack (2.2.3) rack-protection (2.1.0) rack ruby2_keywords (0.0.4) sinatra (2.1.0) mustermann (~> 1.0) rack (~> 2.2) rack-protection (= 2.1.0) tilt (~> 2.0) thrift (0.14.0) tilt (2.0.10) webrick (1.4.4) PLATFORMS ruby DEPENDENCIES jaeger-client! rack sinatra webrick (~> 1.4.2) BUNDLED WITH 2.2.15 jaeger-client-ruby-1.2.0/crossdock/docker-compose.yml000066400000000000000000000024211414770277100226420ustar00rootroot00000000000000version: '2' services: crossdock: image: crossdock/crossdock links: - test_driver - go - python - java - ruby environment: - WAIT_FOR=test_driver,go,python,java,ruby - WAIT_FOR_TIMEOUT=60s - CALL_TIMEOUT=60s - AXIS_CLIENT=go - AXIS_S1NAME=go,python,java,ruby - AXIS_SAMPLED=true,false - AXIS_S2NAME=go,python,java,ruby - AXIS_S2TRANSPORT=http - AXIS_S3NAME=go,python,java,ruby - AXIS_S3TRANSPORT=http - BEHAVIOR_TRACE=client,s1name,sampled,s2name,s2transport,s3name,s3transport - AXIS_TESTDRIVER=test_driver - AXIS_SERVICES=ruby - BEHAVIOR_ENDTOEND=testdriver,services - REPORT=compact go: image: jaegertracing/xdock-go ports: - "8080-8082" java: image: jaegertracing/xdock-java depends_on: - jaeger-agent ports: - "8080-8082" python: image: jaegertracing/xdock-py depends_on: - jaeger-agent ports: - "8080-8082" ruby: build: context: ../. dockerfile: crossdock/Dockerfile ports: - "8080-8082" test_driver: image: jaegertracing/test-driver depends_on: - jaeger-query - jaeger-collector - jaeger-agent ports: - "8080" jaeger-client-ruby-1.2.0/crossdock/jaeger-docker-compose.yml000066400000000000000000000026331414770277100241020ustar00rootroot00000000000000version: '2' services: jaeger-collector: image: jaegertracing/jaeger-collector command: ["--es.num-shards=1", "--es.num-replicas=0", "--es.server-urls=http://elasticsearch:9200", "--collector.zipkin.host-port=:9411"] ports: - "14269" - "14268:14268" - "14250" - "9411:9411" environment: - SPAN_STORAGE_TYPE=elasticsearch - LOG_LEVEL=debug restart: on-failure depends_on: - elasticsearch jaeger-query: image: jaegertracing/jaeger-query command: ["--es.num-shards=1", "--es.num-replicas=0", "--es.server-urls=http://elasticsearch:9200"] ports: - "16686:16686" - "16687" environment: - SPAN_STORAGE_TYPE=elasticsearch - LOG_LEVEL=debug restart: on-failure depends_on: - elasticsearch jaeger-agent: image: jaegertracing/jaeger-agent command: ["--reporter.grpc.host-port=jaeger-collector:14250", "--reporter.grpc.retry.max=1000"] ports: - "5775:5775/udp" - "6831:6831/udp" - "6832:6832/udp" - "5778:5778" environment: - LOG_LEVEL=debug restart: on-failure depends_on: - jaeger-collector elasticsearch: image: docker.elastic.co/elasticsearch/elasticsearch-oss:6.8.3 environment: - discovery.type=single-node ports: - "9200:9200/tcp" jaeger-client-ruby-1.2.0/crossdock/rules.mk000066400000000000000000000027011414770277100206710ustar00rootroot00000000000000XDOCK_YAML=crossdock/docker-compose.yml TRACETEST_THRIFT=idl/thrift/crossdock/tracetest.thrift JAEGER_COMPOSE_URL=https://raw.githubusercontent.com/jaegertracing/jaeger/master/crossdock/jaeger-docker-compose.yml XDOCK_JAEGER_YAML=crossdock/jaeger-docker-compose.yml .PHONY: clean-compile clean-compile: find . -name '*.pyc' -exec rm {} \; .PHONY: docker docker: clean-compile crossdock-download-jaeger docker build -f crossdock/Dockerfile -t jaeger-client-ruby . .PHONY: crossdock crossdock: ${TRACETEST_THRIFT} crossdock-download-jaeger docker-compose -f $(XDOCK_YAML) -f $(XDOCK_JAEGER_YAML) kill ruby docker-compose -f $(XDOCK_YAML) -f $(XDOCK_JAEGER_YAML) rm -f ruby docker-compose -f $(XDOCK_YAML) -f $(XDOCK_JAEGER_YAML) build ruby docker-compose -f $(XDOCK_YAML) -f $(XDOCK_JAEGER_YAML) run crossdock .PHONY: crossdock-fresh crossdock-fresh: ${TRACETEST_THRIFT} crossdock-download-jaeger docker-compose -f $(XDOCK_YAML) -f $(XDOCK_JAEGER_YAML) kill docker-compose -f $(XDOCK_YAML) -f $(XDOCK_JAEGER_YAML) rm --force docker-compose -f $(XDOCK_YAML) -f $(XDOCK_JAEGER_YAML) pull docker-compose -f $(XDOCK_YAML) -f $(XDOCK_JAEGER_YAML) build docker-compose -f $(XDOCK_YAML) -f $(XDOCK_JAEGER_YAML) run crossdock .PHONY: crossdock-logs crossdock-download-jaeger crossdock-logs: docker-compose -f $(XDOCK_YAML) -f $(XDOCK_JAEGER_YAML) logs .PHONY: crossdock-download-jaeger crossdock-download-jaeger: curl -o $(XDOCK_JAEGER_YAML) $(JAEGER_COMPOSE_URL) jaeger-client-ruby-1.2.0/crossdock/server000077500000000000000000000103221414770277100204400ustar00rootroot00000000000000#!/usr/bin/env ruby $stdout.sync = true require 'sinatra/base' require 'webrick' require 'jaeger/client' require 'net/http' require 'uri' class HealthServer < Sinatra::Application get '/' do status 200 end end class HttpServer < Sinatra::Application post '/start_trace' do puts "Got request to start trace: #{trace_request}" parent_context = tracer.extract(OpenTracing::FORMAT_RACK, request.env) server_span = tracer.start_span('/start_trace', child_of: parent_context) server_span.set_baggage_item('crossdock-baggage-key', trace_request['baggage']) if trace_request.key?('sampled') server_span.set_tag('sampling.priority', trace_request['sampled'] ? 1 : 0) end response = { span: observe_span(server_span), notImplementedError: '' } if trace_request['downstream'] downstream = trace_request['downstream'] transport = downstream['transport'] response[:downstream] = case transport when 'HTTP' call_downstream_http(downstream, server_span) when 'DUMMY' { notImplementedError: 'Dummy has not been implemented' } else { notImplementedError: "Unrecognized transport received: #{transport}" } end end puts "Response: #{response}" server_span.finish body JSON.dump(response) end post '/join_trace' do puts 'Got request to join trace' \ "\n Params: #{trace_request}" \ "\n Headers: #{request_headers(request)}" parent_context = tracer.extract(OpenTracing::FORMAT_RACK, request.env) server_span = tracer.start_span('/join_trace', child_of: parent_context) response = { span: observe_span(server_span), notImplementedError: '' } if trace_request['downstream'] downstream = trace_request['downstream'] transport = downstream['transport'] response[:downstream] = case transport when 'HTTP' call_downstream_http(downstream, server_span) when 'DUMMY' { notImplementedError: 'Dummy has not been implemented' } else { notImplementedError: "Unrecognized transport received: #{transport}" } end end puts "Response: #{response}" server_span.finish body JSON.dump(response) end post '/create_traces' do puts "Got request to create traces: #{trace_request}" trace_request['count'].times do span = tracer.start_span(trace_request['operation'], tags: trace_request['tags']) span.finish end status 200 end private def tracer @tracer ||= Jaeger::Client.build( service_name: 'crossdock-ruby', host: 'jaeger-agent', port: 6831, flush_interval: 1, sampler: Jaeger::Samplers::Const.new(true) ) end def trace_request @trace_request ||= begin request.body.rewind JSON.parse(request.body.read) end end def observe_span(span) if span { traceId: span.context.to_trace_id, sampled: span.context.sampled?, baggage: span.get_baggage_item('crossdock-baggage-key') } else { traceId: 'no span found', sampled: false, baggage: 'no span found' } end end def call_downstream_http(downstream, server_span) downstream_url = "http://#{downstream['host']}:#{downstream['port']}/join_trace" client_span = tracer.start_span('client-span', child_of: server_span) headers = { 'Content-Type' => 'application/json' } tracer.inject(client_span.context, OpenTracing::FORMAT_RACK, headers) response = Net::HTTP.post( URI(downstream_url), JSON.dump( serverRole: downstream['serverRole'], downstream: downstream['downstream'] ), headers ) client_span.finish if response.is_a?(Net::HTTPSuccess) JSON.parse(response.body) else { error: response.body } end end def request_headers(request) request.env.select do |key, _value| key.start_with?('HTTP_') end end end threads = [] threads << Thread.new do Rack::Handler::WEBrick.run(HealthServer, Port: 8080, Host: '0.0.0.0') end threads << Thread.new do Rack::Handler::WEBrick.run(HttpServer, Port: 8081, Host: '0.0.0.0') end threads.each(&:join) jaeger-client-ruby-1.2.0/idl/000077500000000000000000000000001414770277100157645ustar00rootroot00000000000000jaeger-client-ruby-1.2.0/jaeger-client.gemspec000066400000000000000000000024641414770277100213000ustar00rootroot00000000000000lib = File.expand_path('lib', __dir__) $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib) require 'jaeger/client/version' Gem::Specification.new do |spec| spec.name = 'jaeger-client' spec.version = Jaeger::Client::VERSION spec.authors = ['SaleMove TechMovers'] spec.email = ['techmovers@salemove.com'] spec.summary = 'OpenTracing Tracer implementation for Jaeger in Ruby' spec.description = '' spec.homepage = 'https://github.com/salemove/jaeger-client-ruby' spec.license = 'MIT' spec.files = `git ls-files -z`.split("\x0").reject do |f| f.match(%r{^(test|spec|features)/}) end spec.test_files = spec.files.grep(%r{^(test|spec|features)/}) spec.require_paths = ['lib'] spec.required_ruby_version = ['>= 2.7', '< 3.1'] spec.add_development_dependency 'bundler' spec.add_development_dependency 'rake', '~> 13.0' spec.add_development_dependency 'rspec', '~> 3.0' spec.add_development_dependency 'rubocop', '~> 1.13' spec.add_development_dependency 'rubocop-rake', '~> 0.5.1' spec.add_development_dependency 'rubocop-rspec', '~> 2.3' spec.add_development_dependency 'timecop', '~> 0.9' spec.add_development_dependency 'webmock', '~> 3.4' spec.add_dependency 'opentracing', '~> 0.3' spec.add_dependency 'thrift' end jaeger-client-ruby-1.2.0/lib/000077500000000000000000000000001414770277100157625ustar00rootroot00000000000000jaeger-client-ruby-1.2.0/lib/jaeger/000077500000000000000000000000001414770277100172175ustar00rootroot00000000000000jaeger-client-ruby-1.2.0/lib/jaeger/client.rb000066400000000000000000000045141414770277100210260ustar00rootroot00000000000000# frozen_string_literal: true $LOAD_PATH.push("#{File.dirname(__FILE__)}/../../thrift/gen-rb") require 'opentracing' require 'jaeger/thrift/agent' require 'logger' require 'time' require 'net/http' require 'cgi' require 'json' require_relative 'tracer' require_relative 'span' require_relative 'span_context' require_relative 'scope' require_relative 'scope_manager' require_relative 'trace_id' require_relative 'udp_sender' require_relative 'http_sender' require_relative 'reporters' require_relative 'client/version' require_relative 'samplers' require_relative 'encoders/thrift_encoder' require_relative 'injectors' require_relative 'extractors' require_relative 'rate_limiter' require_relative 'thrift_tag_builder' require_relative 'recurring_executor' module Jaeger module Client # We initially had everything under Jaeger::Client namespace. This however # was not very useful and was removed. These assignments are here for # backwards compatibility. Fine to remove in the next major version. UdpSender = Jaeger::UdpSender HttpSender = Jaeger::HttpSender Encoders = Jaeger::Encoders Samplers = Jaeger::Samplers Reporters = Jaeger::Reporters Injectors = Jaeger::Injectors Extractors = Jaeger::Extractors DEFAULT_FLUSH_INTERVAL = 10 def self.build(service_name:, host: '127.0.0.1', port: 6831, flush_interval: DEFAULT_FLUSH_INTERVAL, sampler: Samplers::Const.new(true), logger: Logger.new($stdout), sender: nil, reporter: nil, injectors: {}, extractors: {}, tags: {}) encoder = Encoders::ThriftEncoder.new(service_name: service_name, tags: tags, logger: logger) if sender warn '[DEPRECATION] Passing `sender` directly to Jaeger::Client.build is deprecated.' \ 'Please use `reporter` instead.' end reporter ||= Reporters::RemoteReporter.new( sender: sender || UdpSender.new(host: host, port: port, encoder: encoder, logger: logger), flush_interval: flush_interval ) Tracer.new( reporter: reporter, sampler: sampler, injectors: Injectors.prepare(injectors), extractors: Extractors.prepare(extractors) ) end end end jaeger-client-ruby-1.2.0/lib/jaeger/client/000077500000000000000000000000001414770277100204755ustar00rootroot00000000000000jaeger-client-ruby-1.2.0/lib/jaeger/client/version.rb000066400000000000000000000001351414770277100225060ustar00rootroot00000000000000# frozen_string_literal: true module Jaeger module Client VERSION = '1.2.0' end end jaeger-client-ruby-1.2.0/lib/jaeger/encoders/000077500000000000000000000000001414770277100210215ustar00rootroot00000000000000jaeger-client-ruby-1.2.0/lib/jaeger/encoders/thrift_encoder.rb000066400000000000000000000125561414770277100243560ustar00rootroot00000000000000# frozen_string_literal: true module Jaeger module Encoders class ThriftEncoder def initialize(service_name:, logger:, tags: {}) @service_name = service_name @logger = logger @tags = prepare_tags(tags) @process = Jaeger::Thrift::Process.new('serviceName' => @service_name, 'tags' => @tags) end def encode(spans) encode_batch(spans.map(&method(:encode_span))) end def encode_limited_size(spans, protocol_class, max_message_length) batches = [] current_batch = [] current_batch_size = 0 max_spans_length = calculate_max_spans_length(protocol_class, max_message_length) spans.each do |span| encoded_span = encode_span(span) span_size = message_size(encoded_span, protocol_class) if span_size > max_spans_length @logger.warn("Skip span #{span.operation_name} with size #{span_size}") next end if (current_batch_size + span_size) > max_spans_length batches << encode_batch(current_batch) current_batch = [] current_batch_size = 0 end current_batch << encoded_span current_batch_size += span_size end batches << encode_batch(current_batch) unless current_batch.empty? batches end private def encode_batch(encoded_spans) Jaeger::Thrift::Batch.new('process' => @process, 'spans' => encoded_spans) end def encode_span(span) context = span.context start_ts, duration = build_timestamps(span) Jaeger::Thrift::Span.new( 'traceIdLow' => TraceId.uint64_id_to_int64(trace_id_to_low(context.trace_id)), 'traceIdHigh' => TraceId.uint64_id_to_int64(trace_id_to_high(context.trace_id)), 'spanId' => TraceId.uint64_id_to_int64(context.span_id), 'parentSpanId' => TraceId.uint64_id_to_int64(context.parent_id), 'operationName' => span.operation_name, 'references' => build_references(span.references || []), 'flags' => context.flags, 'startTime' => start_ts, 'duration' => duration, 'tags' => span.tags, 'logs' => span.logs ) end def build_references(references) references.map do |ref| Jaeger::Thrift::SpanRef.new( 'refType' => span_ref_type(ref.type), 'traceIdLow' => TraceId.uint64_id_to_int64(trace_id_to_low(ref.context.trace_id)), 'traceIdHigh' => TraceId.uint64_id_to_int64(trace_id_to_high(ref.context.trace_id)), 'spanId' => TraceId.uint64_id_to_int64(ref.context.span_id) ) end end def build_timestamps(span) start_ts = (span.start_time.to_f * 1_000_000).to_i end_ts = (span.end_time.to_f * 1_000_000).to_i duration = end_ts - start_ts [start_ts, duration] end def span_ref_type(type) case type when OpenTracing::Reference::CHILD_OF Jaeger::Thrift::SpanRefType::CHILD_OF when OpenTracing::Reference::FOLLOWS_FROM Jaeger::Thrift::SpanRefType::FOLLOWS_FROM else warn "Jaeger::Client with format #{type} is not supported yet" nil end end def prepare_tags(tags) with_default_tags = tags.dup with_default_tags['jaeger.version'] = "Ruby-#{Jaeger::Client::VERSION}" with_default_tags['hostname'] ||= Socket.gethostname unless with_default_tags['ip'] ipv4 = Socket.ip_address_list.find { |ai| ai.ipv4? && !ai.ipv4_loopback? } with_default_tags['ip'] = ipv4.ip_address unless ipv4.nil? end with_default_tags.map do |key, value| ThriftTagBuilder.build(key, value) end end # Returns the right most 64 bits of trace id def trace_id_to_low(trace_id) trace_id & TraceId::MAX_64BIT_UNSIGNED_INT end # Returns the left most 64 bits of trace id def trace_id_to_high(trace_id) (trace_id >> 64) & TraceId::MAX_64BIT_UNSIGNED_INT end class DummyTransport attr_accessor :size def initialize @size = 0 end def write(buf) @size += buf.size end def flush @size = 0 end def close; end end def message_size(message, protocol_class) transport = DummyTransport.new protocol = protocol_class.new(transport) message.write(protocol) transport.size end # Compact protocol have dynamic size of list header # https://erikvanoosten.github.io/thrift-missing-specification/#_list_and_set_2 BATCH_SPANS_SIZE_WINDOW = 4 def empty_batch_size_cache @empty_batch_size_cache ||= {} end def caclulate_empty_batch_size(protocol_class) empty_batch_size_cache[protocol_class] ||= message_size(encode_batch([]), protocol_class) + BATCH_SPANS_SIZE_WINDOW end def calculate_max_spans_length(protocol_class, max_message_length) empty_batch_size = caclulate_empty_batch_size(protocol_class) max_spans_length = max_message_length - empty_batch_size return max_spans_length if max_spans_length.positive? raise "Batch header have size #{empty_batch_size}, but limit #{max_message_length}" end end end end jaeger-client-ruby-1.2.0/lib/jaeger/extractors.rb000066400000000000000000000121741414770277100217470ustar00rootroot00000000000000# frozen_string_literal: true module Jaeger module Extractors class SerializedJaegerTrace def self.parse(trace) return nil if !trace || trace == '' trace_arguments = trace.split(':') return nil if trace_arguments.size != 4 trace_id = TraceId.base16_hex_id_to_uint128(trace_arguments[0]) span_id, parent_id, flags = trace_arguments[1..3].map(&TraceId.method(:base16_hex_id_to_uint64)) return nil if trace_id.zero? || span_id.zero? SpanContext.new( trace_id: trace_id, parent_id: parent_id, span_id: span_id, flags: flags ) end end class JaegerTextMapCodec def self.extract(carrier) context = SerializedJaegerTrace.parse(carrier['uber-trace-id']) return nil unless context carrier.each do |key, value| baggage_match = key.match(/\Auberctx-([\w-]+)\Z/) if baggage_match context.set_baggage_item(baggage_match[1], value) end end context end end class JaegerRackCodec def self.extract(carrier) serialized_trace = carrier['HTTP_UBER_TRACE_ID'] serialized_trace = CGI.unescape(serialized_trace) if serialized_trace context = SerializedJaegerTrace.parse(serialized_trace) return nil unless context carrier.each do |key, value| baggage_match = key.match(/\AHTTP_UBERCTX_(\w+)\Z/) if baggage_match key = baggage_match[1].downcase.tr('_', '-') context.set_baggage_item(key, CGI.unescape(value)) end end context end end class JaegerBinaryCodec def self.extract(_carrier) warn 'Jaeger::Client with binary format is not supported yet' end end class B3RackCodec class Keys TRACE_ID = 'HTTP_X_B3_TRACEID' SPAN_ID = 'HTTP_X_B3_SPANID' PARENT_SPAN_ID = 'HTTP_X_B3_PARENTSPANID' FLAGS = 'HTTP_X_B3_FLAGS' SAMPLED = 'HTTP_X_B3_SAMPLED' end.freeze def self.extract(carrier) B3CodecCommon.extract(carrier, Keys) end end class B3TextMapCodec class Keys TRACE_ID = 'x-b3-traceid' SPAN_ID = 'x-b3-spanid' PARENT_SPAN_ID = 'x-b3-parentspanid' FLAGS = 'x-b3-flags' SAMPLED = 'x-b3-sampled' end.freeze def self.extract(carrier) B3CodecCommon.extract(carrier, Keys) end end class B3CodecCommon def self.extract(carrier, keys) return nil if carrier[keys::TRACE_ID].nil? || carrier[keys::SPAN_ID].nil? trace_id = if carrier[keys::TRACE_ID].length <= 16 TraceId.base16_hex_id_to_uint64(carrier[keys::TRACE_ID]) else TraceId.base16_hex_id_to_uint128(carrier[keys::TRACE_ID]) end span_id = TraceId.base16_hex_id_to_uint64(carrier[keys::SPAN_ID]) parent_id = TraceId.base16_hex_id_to_uint64(carrier[keys::PARENT_SPAN_ID]) flags = parse_flags(carrier[keys::FLAGS], carrier[keys::SAMPLED]) return nil if span_id.zero? || trace_id.zero? SpanContext.new( trace_id: trace_id, parent_id: parent_id, span_id: span_id, flags: flags ) end # if the flags header is '1' then the sampled header should not be present def self.parse_flags(flags_header, sampled_header) if flags_header == '1' Jaeger::SpanContext::Flags::DEBUG else TraceId.base16_hex_id_to_uint64(sampled_header) end end private_class_method :parse_flags end class TraceContextRackCodec # Internal regex used to identify the TraceContext version VERSION_PATTERN = /^([0-9a-fA-F]{2})-(.+)$/.freeze # Internal regex used to parse fields in version 0 HEADER_V0_PATTERN = /^([0-9a-fA-F]{32})-([0-9a-fA-F]{16})(-([0-9a-fA-F]{2}))?$/.freeze def self.extract(carrier) header_value = carrier['HTTP_TRACEPARENT'] version_match = VERSION_PATTERN.match(header_value) return nil unless version_match # We currently only support version 0 return nil if version_match[1].to_i(16) != 0 match = HEADER_V0_PATTERN.match(version_match[2]) return nil unless match trace_id = TraceId.base16_hex_id_to_uint128(match[1]) span_id = TraceId.base16_hex_id_to_uint64(match[2]) flags = TraceId.base16_hex_id_to_uint64(match[4]) return nil if trace_id.zero? || span_id.zero? SpanContext.new(trace_id: trace_id, span_id: span_id, flags: flags) end end DEFAULT_EXTRACTORS = { OpenTracing::FORMAT_TEXT_MAP => JaegerTextMapCodec, OpenTracing::FORMAT_BINARY => JaegerBinaryCodec, OpenTracing::FORMAT_RACK => JaegerRackCodec }.freeze def self.prepare(extractors) DEFAULT_EXTRACTORS.reduce(extractors) do |acc, (format, default)| provided_extractors = Array(extractors[format]) provided_extractors += [default] if provided_extractors.empty? acc.merge(format => provided_extractors) end end end end jaeger-client-ruby-1.2.0/lib/jaeger/http_sender.rb000066400000000000000000000012551414770277100220660ustar00rootroot00000000000000# frozen_string_literal: true require 'logger' module Jaeger class HttpSender def initialize(url:, encoder:, headers: {}, logger: Logger.new($stdout)) @encoder = encoder @logger = logger @uri = URI(url) @uri.query = 'format=jaeger.thrift' @transport = ::Thrift::HTTPClientTransport.new(@uri.to_s) @transport.add_headers(headers) @serializer = ::Thrift::Serializer.new end def send_spans(spans) batch = @encoder.encode(spans) @transport.write(@serializer.serialize(batch)) @transport.flush rescue StandardError => e @logger.error("Failure while sending a batch of spans: #{e}") end end end jaeger-client-ruby-1.2.0/lib/jaeger/injectors.rb000066400000000000000000000051011414770277100215410ustar00rootroot00000000000000# frozen_string_literal: true module Jaeger module Injectors def self.context_as_jaeger_string(span_context) [ span_context.trace_id.to_s(16), span_context.span_id.to_s(16), span_context.parent_id.to_s(16), span_context.flags.to_s(16) ].join(':') end class JaegerTextMapCodec def self.inject(span_context, carrier) carrier['uber-trace-id'] = Injectors.context_as_jaeger_string(span_context) span_context.baggage.each do |key, value| carrier["uberctx-#{key}"] = value end end end class JaegerRackCodec def self.inject(span_context, carrier) carrier['uber-trace-id'] = CGI.escape(Injectors.context_as_jaeger_string(span_context)) span_context.baggage.each do |key, value| carrier["uberctx-#{key}"] = CGI.escape(value) end end end class JaegerBinaryCodec def self.inject(_span_context, _carrier) warn 'Jaeger::Client with binary format is not supported yet' end end class B3RackCodec def self.inject(span_context, carrier) carrier['x-b3-traceid'] = TraceId.to_hex(span_context.trace_id) carrier['x-b3-spanid'] = TraceId.to_hex(span_context.span_id) carrier['x-b3-parentspanid'] = TraceId.to_hex(span_context.parent_id) # flags (for debug) and sampled headers are mutually exclusive if span_context.flags == Jaeger::SpanContext::Flags::DEBUG carrier['x-b3-flags'] = '1' else carrier['x-b3-sampled'] = span_context.flags.to_s(16) end end end class TraceContextRackCodec def self.inject(span_context, carrier) flags = span_context.sampled? || span_context.debug? ? 1 : 0 carrier['traceparent'] = format( '%s-%s-%s-%s', version: '00', trace_id: span_context.trace_id.to_s(16).rjust(32, '0'), span_id: span_context.span_id.to_s(16).rjust(16, '0'), flags: flags.to_s(16).rjust(2, '0') ) end end DEFAULT_INJECTORS = { OpenTracing::FORMAT_TEXT_MAP => JaegerTextMapCodec, OpenTracing::FORMAT_BINARY => JaegerBinaryCodec, OpenTracing::FORMAT_RACK => JaegerRackCodec }.freeze def self.prepare(injectors) DEFAULT_INJECTORS.reduce(injectors) do |acc, (format, default)| provided_injectors = Array(injectors[format]) provided_injectors += [default] if provided_injectors.empty? acc.merge(format => provided_injectors) end end end end jaeger-client-ruby-1.2.0/lib/jaeger/rate_limiter.rb000066400000000000000000000037521414770277100222330ustar00rootroot00000000000000# frozen_string_literal: true module Jaeger # RateLimiter is based on leaky bucket algorithm, formulated in terms of a # credits balance that is replenished every time check_credit() method is # called (tick) by the amount proportional to the time elapsed since the # last tick, up to the max_balance. A call to check_credit() takes a cost # of an item we want to pay with the balance. If the balance exceeds the # cost of the item, the item is "purchased" and the balance reduced, # indicated by returned value of true. Otherwise the balance is unchanged # and return false. # # This can be used to limit a rate of messages emitted by a service by # instantiating the Rate Limiter with the max number of messages a service # is allowed to emit per second, and calling check_credit(1.0) for each # message to determine if the message is within the rate limit. # # It can also be used to limit the rate of traffic in bytes, by setting # credits_per_second to desired throughput as bytes/second, and calling # check_credit() with the actual message size. class RateLimiter def initialize(credits_per_second:, max_balance:) @credits_per_second = credits_per_second @max_balance = max_balance @balance = max_balance @last_tick = Time.now end def check_credit(item_cost) update_balance return false if @balance < item_cost @balance -= item_cost true end def update(credits_per_second:, max_balance:) update_balance @credits_per_second = credits_per_second # The new balance should be proportional to the old balance @balance = max_balance * @balance / @max_balance @max_balance = max_balance end private def update_balance current_time = Time.now elapsed_time = current_time - @last_tick @last_tick = current_time @balance += elapsed_time * @credits_per_second return if @balance <= @max_balance @balance = @max_balance end end end jaeger-client-ruby-1.2.0/lib/jaeger/recurring_executor.rb000066400000000000000000000011561414770277100234650ustar00rootroot00000000000000# frozen_string_literal: true module Jaeger # Executes a given block periodically. The block will be executed only once # when interval is set to 0. class RecurringExecutor def initialize(interval:) @interval = interval end def start(&block) raise 'Already running' if @thread @thread = Thread.new do if @interval <= 0 yield else loop do yield sleep @interval end end end end def running? @thread&.alive? end def stop @thread.kill @thread = nil end end end jaeger-client-ruby-1.2.0/lib/jaeger/reporters.rb000066400000000000000000000004051414770277100215700ustar00rootroot00000000000000# frozen_string_literal: true require_relative 'reporters/composite_reporter' require_relative 'reporters/in_memory_reporter' require_relative 'reporters/logging_reporter' require_relative 'reporters/null_reporter' require_relative 'reporters/remote_reporter' jaeger-client-ruby-1.2.0/lib/jaeger/reporters/000077500000000000000000000000001414770277100212445ustar00rootroot00000000000000jaeger-client-ruby-1.2.0/lib/jaeger/reporters/composite_reporter.rb000066400000000000000000000004541414770277100255200ustar00rootroot00000000000000# frozen_string_literal: true module Jaeger module Reporters class CompositeReporter def initialize(reporters:) @reporters = reporters end def report(span) @reporters.each do |reporter| reporter.report(span) end end end end end jaeger-client-ruby-1.2.0/lib/jaeger/reporters/in_memory_reporter.rb000066400000000000000000000007221414770277100255120ustar00rootroot00000000000000# frozen_string_literal: true module Jaeger module Reporters class InMemoryReporter def initialize @spans = [] @mutex = Mutex.new end def report(span) @mutex.synchronize do @spans << span end end def spans @mutex.synchronize do @spans end end def clear @mutex.synchronize do @spans.clear end end end end end jaeger-client-ruby-1.2.0/lib/jaeger/reporters/logging_reporter.rb000066400000000000000000000010301414770277100251330ustar00rootroot00000000000000# frozen_string_literal: true module Jaeger module Reporters class LoggingReporter def initialize(logger: Logger.new($stdout)) @logger = logger end def report(span) span_info = { operation_name: span.operation_name, start_time: span.start_time.iso8601, end_time: span.end_time.iso8601, trace_id: span.context.to_trace_id, span_id: span.context.to_span_id } @logger.info "Span reported: #{span_info}" end end end end jaeger-client-ruby-1.2.0/lib/jaeger/reporters/null_reporter.rb000066400000000000000000000002331414770277100244630ustar00rootroot00000000000000# frozen_string_literal: true module Jaeger module Reporters class NullReporter def report(_span) # no-op end end end end jaeger-client-ruby-1.2.0/lib/jaeger/reporters/remote_reporter.rb000066400000000000000000000015151414770277100250100ustar00rootroot00000000000000# frozen_string_literal: true require_relative './remote_reporter/buffer' module Jaeger module Reporters class RemoteReporter def initialize(sender:, flush_interval:) @sender = sender @flush_interval = flush_interval @buffer = Buffer.new end def flush spans = @buffer.retrieve @sender.send_spans(spans) if spans.any? spans end def report(span) return if !span.context.sampled? && !span.context.debug? init_reporter_thread @buffer << span end private def init_reporter_thread return if @initializer_pid == Process.pid @initializer_pid = Process.pid Thread.new do loop do flush sleep(@flush_interval) end end end end end end jaeger-client-ruby-1.2.0/lib/jaeger/reporters/remote_reporter/000077500000000000000000000000001414770277100244615ustar00rootroot00000000000000jaeger-client-ruby-1.2.0/lib/jaeger/reporters/remote_reporter/buffer.rb000066400000000000000000000010041414770277100262520ustar00rootroot00000000000000# frozen_string_literal: true module Jaeger module Reporters class RemoteReporter class Buffer def initialize @buffer = [] @mutex = Mutex.new end def <<(element) @mutex.synchronize do @buffer << element true end end def retrieve @mutex.synchronize do elements = @buffer.dup @buffer.clear elements end end end end end end jaeger-client-ruby-1.2.0/lib/jaeger/samplers.rb000066400000000000000000000004551414770277100213760ustar00rootroot00000000000000# frozen_string_literal: true require_relative 'samplers/const' require_relative 'samplers/guaranteed_throughput_probabilistic' require_relative 'samplers/per_operation' require_relative 'samplers/probabilistic' require_relative 'samplers/rate_limiting' require_relative 'samplers/remote_controlled' jaeger-client-ruby-1.2.0/lib/jaeger/samplers/000077500000000000000000000000001414770277100210455ustar00rootroot00000000000000jaeger-client-ruby-1.2.0/lib/jaeger/samplers/const.rb000066400000000000000000000010541414770277100225200ustar00rootroot00000000000000# frozen_string_literal: true module Jaeger module Samplers # Const sampler # # A sampler that always makes the same decision for new traces depending # on the initialization value. Use `Jaeger::Samplers::Const.new(true)` # to mark all new traces as sampled. class Const def initialize(decision) @decision = decision @tags = { 'sampler.type' => 'const', 'sampler.param' => @decision ? 1 : 0 } end def sample(*) [@decision, @tags] end end end end jaeger-client-ruby-1.2.0/lib/jaeger/samplers/guaranteed_throughput_probabilistic.rb000066400000000000000000000034311414770277100307110ustar00rootroot00000000000000# frozen_string_literal: true module Jaeger module Samplers # A sampler that leverages both Probabilistic sampler and RateLimiting # sampler. The RateLimiting is used as a guaranteed lower bound sampler # such that every operation is sampled at least once in a time interval # defined by the lower_bound. ie a lower_bound of 1.0 / (60 * 10) will # sample an operation at least once every 10 minutes. # # The Probabilistic sampler is given higher priority when tags are # emitted, ie. if is_sampled() for both samplers return true, the tags # for Probabilistic sampler will be used. class GuaranteedThroughputProbabilistic attr_reader :tags, :probabilistic_sampler, :lower_bound_sampler def initialize(lower_bound:, rate:, lower_bound_sampler: nil) @probabilistic_sampler = Probabilistic.new(rate: rate) @lower_bound_sampler = lower_bound_sampler || RateLimiting.new(max_traces_per_second: lower_bound) @lower_bound_tags = { 'sampler.type' => 'lowerbound', 'sampler.param' => rate } end def update(lower_bound:, rate:) is_updated = @probabilistic_sampler.update(rate: rate) is_updated = @lower_bound_sampler.update(max_traces_per_second: lower_bound) || is_updated @lower_bound_tags['sampler.param'] = rate is_updated end def sample(...) is_sampled, probabilistic_tags = @probabilistic_sampler.sample(...) if is_sampled # We still call lower_bound_sampler to update the rate limiter budget @lower_bound_sampler.sample(...) return [is_sampled, probabilistic_tags] end is_sampled, _tags = @lower_bound_sampler.sample(...) [is_sampled, @lower_bound_tags] end end end end jaeger-client-ruby-1.2.0/lib/jaeger/samplers/per_operation.rb000066400000000000000000000047111414770277100242430ustar00rootroot00000000000000# frozen_string_literal: true module Jaeger module Samplers # A sampler that leverages both Probabilistic sampler and RateLimiting # sampler via the GuaranteedThroughputProbabilistic sampler. This sampler # keeps track of all operations and delegates calls the the respective # GuaranteedThroughputProbabilistic sampler. class PerOperation DEFAULT_SAMPLING_PROBABILITY = 0.001 DEFAULT_LOWER_BOUND = 1.0 / (10.0 * 60.0) # sample once every 10 minutes' attr_reader :default_sampling_probability, :lower_bound, :samplers def initialize(strategies:, max_operations:) @max_operations = max_operations @samplers = {} update(strategies: strategies) end def update(strategies:) is_updated = false @default_sampling_probability = strategies[:default_sampling_probability] || DEFAULT_SAMPLING_PROBABILITY @lower_bound = strategies[:default_lower_bound_traces_per_second] || DEFAULT_LOWER_BOUND if @default_sampler is_updated = @default_sampler.update(rate: @default_sampling_probability) else @default_sampler = Probabilistic.new(rate: @default_sampling_probability) end update_operation_strategies(strategies) || is_updated end def sample(opts) operation_name = opts.fetch(:operation_name) sampler = @samplers[operation_name] return sampler.sample(opts) if sampler return @default_sampler.sample(opts) if @samplers.length >= @max_operations sampler = GuaranteedThroughputProbabilistic.new( lower_bound: @lower_bound, rate: @default_sampling_probability ) @samplers[operation_name] = sampler sampler.sample(opts) end private def update_operation_strategies(strategies) is_updated = false (strategies[:per_operation_strategies] || []).each do |strategy| operation = strategy.fetch(:operation) rate = strategy.fetch(:probabilistic_sampling).fetch(:sampling_rate) if (sampler = @samplers[operation]) is_updated = sampler.update(lower_bound: @lower_bound, rate: rate) || is_updated else @samplers[operation] = GuaranteedThroughputProbabilistic.new( lower_bound: @lower_bound, rate: rate ) is_updated = true end end is_updated end end end end jaeger-client-ruby-1.2.0/lib/jaeger/samplers/probabilistic.rb000066400000000000000000000016001414770277100242150ustar00rootroot00000000000000# frozen_string_literal: true module Jaeger module Samplers # Probabilistic sampler # # Sample a portion of traces using trace_id as the random decision class Probabilistic attr_reader :rate def initialize(rate: 0.001) update(rate: rate) end def update(rate:) if rate < 0.0 || rate > 1.0 raise "Sampling rate must be between 0.0 and 1.0, got #{rate.inspect}" end new_boundary = TraceId::TRACE_ID_UPPER_BOUND * rate return false if @boundary == new_boundary @rate = rate @boundary = TraceId::TRACE_ID_UPPER_BOUND * rate @tags = { 'sampler.type' => 'probabilistic', 'sampler.param' => rate } true end def sample(opts) trace_id = opts.fetch(:trace_id) [@boundary >= trace_id, @tags] end end end end jaeger-client-ruby-1.2.0/lib/jaeger/samplers/rate_limiting.rb000066400000000000000000000030541414770277100242230ustar00rootroot00000000000000# frozen_string_literal: true module Jaeger module Samplers # Samples at most max_traces_per_second. The distribution of sampled # traces follows burstiness of the service, i.e. a service with uniformly # distributed requests will have those requests sampled uniformly as # well, but if requests are bursty, especially sub-second, then a number # of sequential requests can be sampled each second. class RateLimiting attr_reader :tags, :max_traces_per_second def initialize(max_traces_per_second: 10) update(max_traces_per_second: max_traces_per_second) end def update(max_traces_per_second:) if max_traces_per_second < 0.0 raise "max_traces_per_second must not be negative, got #{max_traces_per_second}" end return false if max_traces_per_second == @max_traces_per_second @tags = { 'sampler.type' => 'ratelimiting', 'sampler.param' => max_traces_per_second } @max_traces_per_second = max_traces_per_second max_balance = [max_traces_per_second, 1.0].max if @rate_limiter @rate_limiter.update( credits_per_second: max_traces_per_second, max_balance: max_balance ) else @rate_limiter = RateLimiter.new( credits_per_second: max_traces_per_second, max_balance: [max_traces_per_second, 1.0].max ) end true end def sample(*) [@rate_limiter.check_credit(1.0), @tags] end end end end jaeger-client-ruby-1.2.0/lib/jaeger/samplers/remote_controlled.rb000066400000000000000000000100061414770277100251070ustar00rootroot00000000000000# frozen_string_literal: true require_relative 'remote_controlled/instructions_fetcher' module Jaeger module Samplers class RemoteControlled DEFAULT_REFRESH_INTERVAL = 60 DEFAULT_SAMPLING_HOST = 'localhost' DEFAULT_SAMPLING_PORT = 5778 attr_reader :sampler def initialize(opts = {}) @sampler = opts.fetch(:sampler, Probabilistic.new) @logger = opts.fetch(:logger, Logger.new($stdout)) @poll_executor = opts[:poll_executor] || begin refresh_interval = opts.fetch(:refresh_interval, DEFAULT_REFRESH_INTERVAL) RecurringExecutor.new(interval: refresh_interval) end @instructions_fetcher = opts[:instructions_fetcher] || begin service_name = opts.fetch(:service_name) host = opts.fetch(:host, DEFAULT_SAMPLING_HOST) port = opts.fetch(:port, DEFAULT_SAMPLING_PORT) InstructionsFetcher.new(host: host, port: port, service_name: service_name) end end def sample(*args) @poll_executor.start(&method(:poll)) unless @poll_executor.running? @sampler.sample(*args) end def poll @logger.debug 'Fetching sampling strategy' instructions = @instructions_fetcher.fetch handle_instructions(instructions) rescue InstructionsFetcher::FetchFailed => e @logger.warn "Fetching sampling strategy failed: #{e.message}" end private def handle_instructions(instructions) if instructions['operationSampling'] update_per_operation_sampler(instructions['operationSampling']) else update_rate_limiting_or_probabilistic_sampler(instructions['strategyType'], instructions) end end def update_per_operation_sampler(instructions) strategies = normalize(instructions) if @sampler.is_a?(PerOperation) @sampler.update(strategies: strategies) else @sampler = PerOperation.new(strategies: strategies, max_operations: 2000) end end def normalize(instructions) { default_sampling_probability: instructions['defaultSamplingProbability'], default_lower_bound_traces_per_second: instructions['defaultLowerBoundTracesPerSecond'], per_operation_strategies: instructions['perOperationStrategies'].map do |strategy| { operation: strategy['operation'], probabilistic_sampling: { sampling_rate: strategy['probabilisticSampling']['samplingRate'] } } end } end def update_rate_limiting_or_probabilistic_sampler(strategy, instructions) case strategy when 'PROBABILISTIC' update_probabilistic_strategy(instructions['probabilisticSampling']) when 'RATE_LIMITING' update_rate_limiting_strategy(instructions['rateLimitingSampling']) else @logger.warn "Unknown sampling strategy #{strategy}" end end def update_probabilistic_strategy(instructions) rate = instructions['samplingRate'] return unless rate if @sampler.is_a?(Probabilistic) @sampler.update(rate: rate) @logger.info "Updated Probabilistic sampler (rate=#{rate})" else @sampler = Probabilistic.new(rate: rate) @logger.info "Updated sampler to Probabilistic (rate=#{rate})" end end def update_rate_limiting_strategy(instructions) max_traces_per_second = instructions['maxTracesPerSecond'] return unless max_traces_per_second if @sampler.is_a?(RateLimiting) @sampler.update(max_traces_per_second: max_traces_per_second) @logger.info "Updated Ratelimiting sampler (max_traces_per_second=#{max_traces_per_second})" else @sampler = RateLimiting.new(max_traces_per_second: max_traces_per_second) @logger.info "Updated sampler to Ratelimiting (max_traces_per_second=#{max_traces_per_second})" end end end end end jaeger-client-ruby-1.2.0/lib/jaeger/samplers/remote_controlled/000077500000000000000000000000001414770277100245655ustar00rootroot00000000000000jaeger-client-ruby-1.2.0/lib/jaeger/samplers/remote_controlled/instructions_fetcher.rb000066400000000000000000000015331414770277100313600ustar00rootroot00000000000000# frozen_string_literal: true module Jaeger module Samplers class RemoteControlled class InstructionsFetcher FetchFailed = Class.new(StandardError) def initialize(host:, port:, service_name:) @host = host @port = port @service_name = service_name end def fetch http = Net::HTTP.new(@host, @port) path = "/sampling?service=#{CGI.escape(@service_name)}" response = begin http.request(Net::HTTP::Get.new(path)) rescue StandardError => e raise FetchFailed, e.inspect end unless response.is_a?(Net::HTTPSuccess) raise FetchFailed, "Unsuccessful response (code=#{response.code})" end JSON.parse(response.body) end end end end end jaeger-client-ruby-1.2.0/lib/jaeger/scope.rb000066400000000000000000000017451414770277100206640ustar00rootroot00000000000000# frozen_string_literal: true module Jaeger # Scope represents an OpenTracing Scope # # See http://www.opentracing.io for more information. class Scope def initialize(span, scope_stack, finish_on_close:) @span = span @scope_stack = scope_stack @finish_on_close = finish_on_close @closed = false end # Return the Span scoped by this Scope # # @return [Span] attr_reader :span # Close scope # # Mark the end of the active period for the current thread and Scope, # updating the ScopeManager#active in the process. def close raise "Tried to close already closed span: #{inspect}" if @closed @closed = true @span.finish if @finish_on_close removed_scope = @scope_stack.pop if removed_scope != self # rubocop:disable Style/GuardClause raise 'Removed non-active scope, ' \ "removed: #{removed_scope.inspect}, "\ "expected: #{inspect}" end end end end jaeger-client-ruby-1.2.0/lib/jaeger/scope_manager.rb000066400000000000000000000030321414770277100223450ustar00rootroot00000000000000# frozen_string_literal: true require_relative 'scope_manager/scope_stack' require_relative 'scope_manager/scope_identifier' module Jaeger # ScopeManager represents an OpenTracing ScopeManager # # See http://www.opentracing.io for more information. # # The ScopeManager interface abstracts both the activation of Span instances # via ScopeManager#activate and access to an active Span/Scope via # ScopeManager#active # class ScopeManager def initialize @scope_stack = ScopeStack.new end # Make a span instance active # # @param span [Span] the Span that should become active # @param finish_on_close [Boolean] whether the Span should automatically be # finished when Scope#close is called # @return [Scope] instance to control the end of the active period for the # Span. It is a programming error to neglect to call Scope#close on the # returned instance. def activate(span, finish_on_close: true) return active if active && active.span == span scope = Scope.new(span, @scope_stack, finish_on_close: finish_on_close) @scope_stack.push(scope) scope end # Return active scope # # If there is a non-null Scope, its wrapped Span becomes an implicit parent # (as Reference#CHILD_OF) of any newly-created Span at # Tracer#start_active_span or Tracer#start_span time. # # @return [Scope] the currently active Scope which can be used to access the # currently active Span. def active @scope_stack.peek end end end jaeger-client-ruby-1.2.0/lib/jaeger/scope_manager/000077500000000000000000000000001414770277100220225ustar00rootroot00000000000000jaeger-client-ruby-1.2.0/lib/jaeger/scope_manager/scope_identifier.rb000066400000000000000000000004351414770277100256640ustar00rootroot00000000000000# frozen_string_literal: true module Jaeger class ScopeManager # @api private class ScopeIdentifier def self.generate # 65..90.chr are characters between A and Z "opentracing_#{(0...8).map { rand(65..90).chr }.join}".to_sym end end end end jaeger-client-ruby-1.2.0/lib/jaeger/scope_manager/scope_stack.rb000066400000000000000000000012221414770277100246420ustar00rootroot00000000000000# frozen_string_literal: true module Jaeger class ScopeManager # @api private class ScopeStack def initialize # Generate a random identifier to use as the Thread.current key. This is # needed so that it would be possible to create multiple tracers in one # thread (mostly useful for testing purposes) @scope_identifier = ScopeIdentifier.generate end def push(scope) store << scope end def pop store.pop end def peek store.last end private def store Thread.current[@scope_identifier] ||= [] end end end end jaeger-client-ruby-1.2.0/lib/jaeger/span.rb000066400000000000000000000052371414770277100205140ustar00rootroot00000000000000# frozen_string_literal: true require_relative 'span/thrift_log_builder' module Jaeger class Span attr_accessor :operation_name attr_reader :context, :start_time, :end_time, :references, :tags, :logs # Creates a new {Span} # # @param context [SpanContext] the context of the span # @param operation_name [String] the operation name # @param reporter [#report] span reporter # # @return [Span] a new Span def initialize(context, operation_name, reporter, start_time: Time.now, references: [], tags: {}) @context = context @operation_name = operation_name @reporter = reporter @start_time = start_time @references = references @tags = [] @logs = [] tags.each { |key, value| set_tag(key, value) } end # Set a tag value on this span # # @param key [String] the key of the tag # @param value [String, Numeric, Boolean] the value of the tag. If it's not # a String, Numeric, or Boolean it will be encoded with to_s def set_tag(key, value) if key == 'sampling.priority' if value.to_i.positive? return self if @context.debug? @context.flags = @context.flags | SpanContext::Flags::SAMPLED | SpanContext::Flags::DEBUG else @context.flags = @context.flags & ~SpanContext::Flags::SAMPLED end return self end # Using Thrift::Tag to avoid unnecessary memory allocations @tags << ThriftTagBuilder.build(key, value) self end # Set a baggage item on the span # # @param key [String] the key of the baggage item # @param value [String] the value of the baggage item def set_baggage_item(key, value) @context.set_baggage_item(key, value) self end # Get a baggage item # # @param key [String] the key of the baggage item # # @return Value of the baggage item def get_baggage_item(key) @context.get_baggage_item(key) end # Add a log entry to this span # # @deprecated Use {#log_kv} instead. def log(...) warn 'Span#log is deprecated. Please use Span#log_kv instead.' log_kv(...) end # Add a log entry to this span # # @param timestamp [Time] time of the log # @param fields [Hash] Additional information to log def log_kv(timestamp: Time.now, **fields) # Using Thrift::Log to avoid unnecessary memory allocations @logs << ThriftLogBuilder.build(timestamp, fields) nil end # Finish the {Span} # # @param end_time [Time] custom end time, if not now def finish(end_time: Time.now) @end_time = end_time @reporter.report(self) end end end jaeger-client-ruby-1.2.0/lib/jaeger/span/000077500000000000000000000000001414770277100201605ustar00rootroot00000000000000jaeger-client-ruby-1.2.0/lib/jaeger/span/thrift_log_builder.rb000066400000000000000000000010171414770277100243530ustar00rootroot00000000000000# frozen_string_literal: true module Jaeger class Span class ThriftLogBuilder FIELDS = Jaeger::Thrift::Log::FIELDS TIMESTAMP = FIELDS[Jaeger::Thrift::Log::TIMESTAMP].fetch(:name) LOG_FIELDS = FIELDS[Jaeger::Thrift::Log::LOG_FIELDS].fetch(:name) def self.build(timestamp, fields) Jaeger::Thrift::Log.new( TIMESTAMP => (timestamp.to_f * 1_000_000).to_i, LOG_FIELDS => fields.map { |key, value| ThriftTagBuilder.build(key, value) } ) end end end end jaeger-client-ruby-1.2.0/lib/jaeger/span_context.rb000066400000000000000000000023231414770277100222510ustar00rootroot00000000000000# frozen_string_literal: true module Jaeger # SpanContext holds the data for a span that gets inherited to child spans class SpanContext module Flags NONE = 0x00 SAMPLED = 0x01 DEBUG = 0x02 end def self.create_from_parent_context(span_context) new( trace_id: span_context.trace_id, parent_id: span_context.span_id, span_id: TraceId.generate, flags: span_context.flags, baggage: span_context.baggage.dup ) end attr_accessor :flags attr_reader :span_id, :parent_id, :trace_id, :baggage def initialize(span_id:, trace_id:, flags:, parent_id: 0, baggage: {}) @span_id = span_id @parent_id = parent_id @trace_id = trace_id @baggage = baggage @flags = flags end def sampled? @flags & Flags::SAMPLED == Flags::SAMPLED end def debug? @flags & Flags::DEBUG == Flags::DEBUG end def to_trace_id @to_trace_id ||= @trace_id.to_s(16) end def to_span_id @to_span_id ||= @span_id.to_s(16) end def set_baggage_item(key, value) @baggage[key.to_s] = value.to_s end def get_baggage_item(key) @baggage[key.to_s] end end end jaeger-client-ruby-1.2.0/lib/jaeger/thrift_tag_builder.rb000066400000000000000000000023001414770277100234000ustar00rootroot00000000000000# frozen_string_literal: true module Jaeger class ThriftTagBuilder FIELDS = Jaeger::Thrift::Tag::FIELDS KEY = FIELDS[Jaeger::Thrift::Tag::KEY].fetch(:name) VTYPE = FIELDS[Jaeger::Thrift::Tag::VTYPE].fetch(:name) VLONG = FIELDS[Jaeger::Thrift::Tag::VLONG].fetch(:name) VDOUBLE = FIELDS[Jaeger::Thrift::Tag::VDOUBLE].fetch(:name) VBOOL = FIELDS[Jaeger::Thrift::Tag::VBOOL].fetch(:name) VSTR = FIELDS[Jaeger::Thrift::Tag::VSTR].fetch(:name) def self.build(key, value) case value when Integer Jaeger::Thrift::Tag.new( KEY => key.to_s, VTYPE => Jaeger::Thrift::TagType::LONG, VLONG => value ) when Float Jaeger::Thrift::Tag.new( KEY => key.to_s, VTYPE => Jaeger::Thrift::TagType::DOUBLE, VDOUBLE => value ) when TrueClass, FalseClass Jaeger::Thrift::Tag.new( KEY => key.to_s, VTYPE => Jaeger::Thrift::TagType::BOOL, VBOOL => value ) else Jaeger::Thrift::Tag.new( KEY => key.to_s, VTYPE => Jaeger::Thrift::TagType::STRING, VSTR => value.to_s ) end end end end jaeger-client-ruby-1.2.0/lib/jaeger/trace_id.rb000066400000000000000000000024551414770277100213240ustar00rootroot00000000000000# frozen_string_literal: true module Jaeger module TraceId MAX_64BIT_SIGNED_INT = (1 << 63) - 1 MAX_64BIT_UNSIGNED_INT = (1 << 64) - 1 MAX_128BIT_UNSIGNED_INT = (1 << 128) - 1 TRACE_ID_UPPER_BOUND = MAX_64BIT_UNSIGNED_INT + 1 def self.generate rand(TRACE_ID_UPPER_BOUND) end def self.base16_hex_id_to_uint64(id) return nil unless id value = id.to_i(16) value > MAX_64BIT_UNSIGNED_INT || value.negative? ? 0 : value end def self.base16_hex_id_to_uint128(id) return nil unless id value = id.to_i(16) value > MAX_128BIT_UNSIGNED_INT || value.negative? ? 0 : value end # Thrift defines ID fields as i64, which is signed, therefore we convert # large IDs (> 2^63) to negative longs def self.uint64_id_to_int64(id) id > MAX_64BIT_SIGNED_INT ? id - MAX_64BIT_UNSIGNED_INT - 1 : id end # Convert an integer id into a 0 padded hex string. # If the string is shorter than 16 characters, it will be padded to 16. # If it is longer than 16 characters, it is padded to 32. def self.to_hex(id) hex_str = id.to_s(16) # pad the string with '0's to 16 or 32 characters if hex_str.length > 16 hex_str.rjust(32, '0') else hex_str.rjust(16, '0') end end end end jaeger-client-ruby-1.2.0/lib/jaeger/tracer.rb000066400000000000000000000170201414770277100210240ustar00rootroot00000000000000# frozen_string_literal: true module Jaeger class Tracer def initialize(reporter:, sampler:, injectors:, extractors:) @reporter = reporter @sampler = sampler @injectors = injectors @extractors = extractors @scope_manager = ScopeManager.new end # @return [ScopeManager] the current ScopeManager, which may be a no-op # but may not be nil. attr_reader :scope_manager # @return [Span, nil] the active span. This is a shorthand for # `scope_manager.active.span`, and nil will be returned if # Scope#active is nil. def active_span scope = scope_manager.active scope&.span end # Starts a new span. # # This is similar to #start_active_span, but the returned Span will not # be registered via the ScopeManager. # # @param operation_name [String] The operation name for the Span # @param child_of [SpanContext, Span] SpanContext that acts as a parent to # the newly-started Span. If a Span instance is provided, its # context is automatically substituted. See [Reference] for more # information. # # If specified, the `references` parameter must be omitted. # @param references [Array] An array of reference # objects that identify one or more parent SpanContexts. # @param start_time [Time] When the Span started, if not now # @param tags [Hash] Tags to assign to the Span at start time # @param ignore_active_scope [Boolean] whether to create an implicit # References#CHILD_OF reference to the ScopeManager#active. # # @yield [Span] If passed an optional block, start_span will yield the # newly-created span to the block. The span will be finished automatically # after the block is executed. # @return [Span, Object] If passed an optional block, start_span will return # the block's return value, otherwise it returns the newly-started Span # instance, which has not been automatically registered via the # ScopeManager def start_span(operation_name, child_of: nil, references: nil, start_time: Time.now, tags: {}, ignore_active_scope: false, **) context, sampler_tags = prepare_span_context( operation_name: operation_name, child_of: child_of, references: references, ignore_active_scope: ignore_active_scope ) span = Span.new( context, operation_name, @reporter, start_time: start_time, references: references, tags: tags.merge(sampler_tags) ) if block_given? begin yield(span) ensure span.finish end else span end end # Creates a newly started and activated Scope # # If the Tracer's ScopeManager#active is not nil, no explicit references # are provided, and `ignore_active_scope` is false, then an inferred # References#CHILD_OF reference is created to the ScopeManager#active's # SpanContext when start_active is invoked. # # @param operation_name [String] The operation name for the Span # @param child_of [SpanContext, Span] SpanContext that acts as a parent to # the newly-started Span. If a Span instance is provided, its # context is automatically substituted. See [Reference] for more # information. # # If specified, the `references` parameter must be omitted. # @param references [Array] An array of reference # objects that identify one or more parent SpanContexts. # @param start_time [Time] When the Span started, if not now # @param tags [Hash] Tags to assign to the Span at start time # @param ignore_active_scope [Boolean] whether to create an implicit # References#CHILD_OF reference to the ScopeManager#active. # @param finish_on_close [Boolean] whether span should automatically be # finished when Scope#close is called # @yield [Scope] If an optional block is passed to start_active_span it # will yield the newly-started Scope. If `finish_on_close` is true then the # Span will be finished automatically after the block is executed. # @return [Scope, Object] If passed an optional block, start_active_span # returns the block's return value, otherwise it returns the newly-started # and activated Scope def start_active_span(operation_name, child_of: nil, references: nil, start_time: Time.now, tags: {}, ignore_active_scope: false, finish_on_close: true, **) span = start_span( operation_name, child_of: child_of, references: references, start_time: start_time, tags: tags, ignore_active_scope: ignore_active_scope ) scope = @scope_manager.activate(span, finish_on_close: finish_on_close) if block_given? begin yield scope ensure scope.close end else scope end end # Inject a SpanContext into the given carrier # # @param span_context [SpanContext] # @param format [OpenTracing::FORMAT_TEXT_MAP, OpenTracing::FORMAT_BINARY, OpenTracing::FORMAT_RACK] # @param carrier [Carrier] A carrier object of the type dictated by the specified `format` def inject(span_context, format, carrier) @injectors.fetch(format).each do |injector| injector.inject(span_context, carrier) end end # Extract a SpanContext in the given format from the given carrier. # # @param format [OpenTracing::FORMAT_TEXT_MAP, OpenTracing::FORMAT_BINARY, OpenTracing::FORMAT_RACK] # @param carrier [Carrier] A carrier object of the type dictated by the specified `format` # @return [SpanContext] the extracted SpanContext or nil if none could be found def extract(format, carrier) @extractors .fetch(format) .lazy .map { |extractor| extractor.extract(carrier) } .reject(&:nil?) .first end private def prepare_span_context(operation_name:, child_of:, references:, ignore_active_scope:) context = context_from_child_of(child_of) || context_from_references(references) || context_from_active_scope(ignore_active_scope) if context [SpanContext.create_from_parent_context(context), {}] else trace_id = TraceId.generate is_sampled, tags = @sampler.sample( trace_id: trace_id, operation_name: operation_name ) span_context = SpanContext.new( trace_id: trace_id, span_id: trace_id, flags: is_sampled ? SpanContext::Flags::SAMPLED : SpanContext::Flags::NONE ) [span_context, tags] end end def context_from_child_of(child_of) return nil unless child_of child_of.respond_to?(:context) ? child_of.context : child_of end def context_from_references(references) return nil if !references || references.none? # Prefer CHILD_OF reference if present ref = references.detect do |reference| reference.type == OpenTracing::Reference::CHILD_OF end (ref || references[0]).context end def context_from_active_scope(ignore_active_scope) return if ignore_active_scope active_scope = @scope_manager.active active_scope&.span&.context end end end jaeger-client-ruby-1.2.0/lib/jaeger/udp_sender.rb000066400000000000000000000014421414770277100216750ustar00rootroot00000000000000# frozen_string_literal: true require_relative './udp_sender/transport' require 'socket' module Jaeger class UdpSender def initialize(host:, port:, encoder:, logger:, max_packet_size: 65_000) @encoder = encoder @logger = logger transport = Transport.new(host, port, logger: logger) @protocol_class = ::Thrift::CompactProtocol protocol = @protocol_class.new(transport) @client = Jaeger::Thrift::Agent::Client.new(protocol) @max_packet_size = max_packet_size end def send_spans(spans) batches = @encoder.encode_limited_size(spans, @protocol_class, @max_packet_size) batches.each { |batch| @client.emitBatch(batch) } rescue StandardError => e @logger.error("Failure while sending a batch of spans: #{e}") end end end jaeger-client-ruby-1.2.0/lib/jaeger/udp_sender/000077500000000000000000000000001414770277100213475ustar00rootroot00000000000000jaeger-client-ruby-1.2.0/lib/jaeger/udp_sender/transport.rb000066400000000000000000000014751414770277100237370ustar00rootroot00000000000000# frozen_string_literal: true module Jaeger class UdpSender class Transport FLAGS = 0 def initialize(host, port, logger:) @socket = UDPSocket.new @host = host @port = port @logger = logger @buffer = ::Thrift::MemoryBufferTransport.new end def write(str) @buffer.write(str) end def flush data = @buffer.read(@buffer.available) send_bytes(data) end def open; end def close; end private def send_bytes(bytes) @socket.send(bytes, FLAGS, @host, @port) @socket.flush rescue Errno::ECONNREFUSED @logger.warn 'Unable to connect to Jaeger Agent' rescue StandardError => e @logger.warn "Unable to send spans: #{e.message}" end end end end jaeger-client-ruby-1.2.0/script/000077500000000000000000000000001414770277100165205ustar00rootroot00000000000000jaeger-client-ruby-1.2.0/script/create_follows_from_trace000077500000000000000000000021321414770277100236550ustar00rootroot00000000000000#!/usr/bin/env ruby require 'bundler' Bundler.setup require 'jaeger/client' host = ENV['JAEGER_HOST'] || '127.0.0.1' port = ENV['JAEGER_HOST'] || 6831 tracer1 = Jaeger::Client.build(host: host, port: port.to_i, service_name: 'test-service', flush_interval: 1) tracer2 = Jaeger::Client.build(host: host, port: port.to_i, service_name: 'downstream-service', flush_interval: 1) rpc_span = tracer1.start_span( 'receive request', tags: { 'span.kind' => 'server' } ) sleep 0.1 rpc_span.log_kv(event: 'woop di doop', count: 5) sleep 1 async_request_span = tracer1.start_span( 'request async action', references: [ OpenTracing::Reference.child_of(rpc_span.context) ], tags: { 'span.kind' => 'producer' } ) sleep 0.1 async_request_span.finish rpc_span.finish sleep 0.5 async_span = tracer2.start_span( 'async span started after rpc span', references: [ OpenTracing::Reference.follows_from(async_request_span.context) ], tags: { 'span.kind' => 'consumer', 'peer.service' => 'downstream-service' } ) sleep 0.3 # emulate network delay async_span.finish sleep 2 puts 'Finished' jaeger-client-ruby-1.2.0/script/create_trace000077500000000000000000000021731414770277100210720ustar00rootroot00000000000000#!/usr/bin/env ruby require 'bundler' Bundler.setup require 'jaeger/client' host = ENV['JAEGER_HOST'] || '127.0.0.1' port = ENV['JAEGER_HOST'] || 6831 tracer1 = Jaeger::Client.build(host: host, port: port.to_i, service_name: 'test-service', flush_interval: 1) tracer2 = Jaeger::Client.build(host: host, port: port.to_i, service_name: 'downstream-service', flush_interval: 1) outer_span = tracer1.start_span( 'receive request', tags: { 'span.kind' => 'server' } ) sleep 0.1 outer_span.log_kv(event: 'woop di doop', count: 5) sleep 1 inner_span = tracer1.start_span( 'fetch info from downstream', child_of: outer_span, tags: { 'span.kind' => 'client', 'peer.service' => 'downstream-service', 'peer.ipv4' => '6.6.6.6', 'peer.port' => 443 } ) inner_span.set_tag('error', false) sleep 0.3 # emulate network delay downstream_span = tracer2.start_span( 'downstream operation', child_of: inner_span, tags: { 'span.kind' => 'server' } ) sleep 0.5 downstream_span.finish sleep 0.2 # emulate network delay inner_span.finish sleep 0.1 # doing something with fetched info outer_span.finish sleep 2 puts 'Finished' jaeger-client-ruby-1.2.0/spec/000077500000000000000000000000001414770277100161465ustar00rootroot00000000000000jaeger-client-ruby-1.2.0/spec/jaeger/000077500000000000000000000000001414770277100174035ustar00rootroot00000000000000jaeger-client-ruby-1.2.0/spec/jaeger/encoders/000077500000000000000000000000001414770277100212055ustar00rootroot00000000000000jaeger-client-ruby-1.2.0/spec/jaeger/encoders/thrift_encoder_spec.rb000066400000000000000000000215571414770277100255550ustar00rootroot00000000000000require 'spec_helper' RSpec::Matchers.define :be_a_valid_thrift_span do |_| match do |actual| actual.instance_of?(::Jaeger::Thrift::Span) && !actual.instance_variable_get(:@traceIdLow).nil? && !actual.instance_variable_get(:@traceIdHigh).nil? && !actual.instance_variable_get(:@spanId).nil? && !actual.instance_variable_get(:@parentSpanId).nil? && !actual.instance_variable_get(:@operationName).nil? && !actual.instance_variable_get(:@flags).nil? && !actual.instance_variable_get(:@startTime).nil? && !actual.instance_variable_get(:@duration).nil? && !actual.instance_variable_get(:@tags).nil? end end RSpec.describe Jaeger::Encoders::ThriftEncoder do let(:logger) { instance_spy(Logger) } let(:encoder) { described_class.new(service_name: service_name, logger: logger, tags: tags) } let(:service_name) { 'service-name' } let(:tags) { {} } context 'without custom tags' do it 'has jaeger.version' do tags = encoder.encode([]).process.tags version_tag = tags.detect { |tag| tag.key == 'jaeger.version' } expect(version_tag.vStr).to match(/Ruby-/) end it 'has hostname' do tags = encoder.encode([]).process.tags hostname_tag = tags.detect { |tag| tag.key == 'hostname' } expect(hostname_tag.vStr).to be_a(String) end it 'has ip' do tags = encoder.encode([]).process.tags ip_tag = tags.detect { |tag| tag.key == 'ip' } expect(ip_tag.vStr).to be_a(String) end end context 'when hostname is provided' do let(:tags) { { 'hostname' => hostname } } let(:hostname) { 'custom-hostname' } it 'uses provided hostname in the process tags' do tags = encoder.encode([]).process.tags hostname_tag = tags.detect { |tag| tag.key == 'hostname' } expect(hostname_tag.vStr).to eq(hostname) end end context 'when ip is provided' do let(:tags) { { 'ip' => ip } } let(:ip) { 'custom-ip' } it 'uses provided ip in the process tags' do tags = encoder.encode([]).process.tags ip_tag = tags.detect { |tag| tag.key == 'ip' } expect(ip_tag.vStr).to eq(ip) end end context 'when spans are encoded without limit' do let(:context) do Jaeger::SpanContext.new( trace_id: Jaeger::TraceId.generate, span_id: Jaeger::TraceId.generate, flags: Jaeger::SpanContext::Flags::DEBUG ) end let(:example_span) { Jaeger::Span.new(context, 'example_op', nil) } it 'encodes spans into one batch' do encoded_batch = encoder.encode([example_span]) expect(encoded_batch.spans.first).to be_a_valid_thrift_span end end context 'when spans are encoded with limits' do let(:context) do Jaeger::SpanContext.new( trace_id: Jaeger::TraceId.generate, span_id: Jaeger::TraceId.generate, flags: Jaeger::SpanContext::Flags::DEBUG ) end let(:example_span) { Jaeger::Span.new(context, 'example_op', nil) } # example span have size of 63, so let's make an array of 100 span # and set the limit at 5000, so it should return a batch of 2 let(:example_spans) { Array.new(100, example_span) } it 'encodes spans into multiple batches' do encoded_batches = encoder.encode_limited_size(example_spans, ::Thrift::CompactProtocol, 5_000) expect(encoded_batches.length).to be(2) expect(encoded_batches.first.spans.first).to be_a_valid_thrift_span expect(encoded_batches.first.spans.last).to be_a_valid_thrift_span expect(encoded_batches.last.spans.first).to be_a_valid_thrift_span expect(encoded_batches.last.spans.last).to be_a_valid_thrift_span end end context 'when span have reference' do let(:context) do Jaeger::SpanContext.new( trace_id: Jaeger::TraceId.generate, span_id: Jaeger::TraceId.generate, flags: Jaeger::SpanContext::Flags::DEBUG ) end let(:reference_context) do Jaeger::SpanContext.new( trace_id: rand(Jaeger::TraceId::MAX_128BIT_UNSIGNED_INT), span_id: Jaeger::TraceId.generate, flags: Jaeger::SpanContext::Flags::DEBUG ) end let(:example_references) { [OpenTracing::Reference.follows_from(reference_context)] } let(:example_span) { Jaeger::Span.new(context, 'example_op', nil, references: example_references) } let(:example_spans) { [example_span] } it 'encode span with references' do batch = encoder.encode(example_spans) span = batch.spans.first reference = span.references.first trace_id_half_range = [-Jaeger::TraceId::MAX_64BIT_SIGNED_INT, Jaeger::TraceId::MAX_64BIT_SIGNED_INT] expect(reference.traceIdLow).to be_between(*trace_id_half_range) expect(reference.traceIdHigh).to be_between(*trace_id_half_range) expect(reference.traceIdLow).not_to eq 0 expect(reference.traceIdHigh).not_to eq 0 end end context 'when process have additional tags' do let(:context) do Jaeger::SpanContext.new( trace_id: Jaeger::TraceId.generate, span_id: Jaeger::TraceId.generate, flags: Jaeger::SpanContext::Flags::DEBUG ) end let(:example_span) { Jaeger::Span.new(context, 'example_op', nil) } let(:example_spans) { Array.new(150, example_span) } let(:tags) { Array.new(50) { |index| ["key#{index}", "value#{index}"] }.to_h } let(:max_length) { 5_000 } it 'size of every batch not exceed limit with compact protocol' do encoded_batches = encoder.encode_limited_size(example_spans, ::Thrift::CompactProtocol, max_length) expect(encoded_batches.count).to be > 2 encoded_batches.each do |encoded_batch| transport = ::Thrift::MemoryBufferTransport.new protocol = ::Thrift::CompactProtocol.new(transport) encoded_batch.write(protocol) expect(transport.available).to be < max_length end end it 'size of every batch not exceed limit with binary protocol' do encoded_batches = encoder.encode_limited_size(example_spans, ::Thrift::BinaryProtocol, max_length) expect(encoded_batches.count).to be > 2 encoded_batches.each do |encoded_batch| transport = ::Thrift::MemoryBufferTransport.new protocol = ::Thrift::CompactProtocol.new(transport) encoded_batch.write(protocol) expect(transport.available).to be < max_length end end context 'when limit to low' do let(:max_length) { 500 } it 'raise error' do expect { encoder.encode_limited_size(example_spans, ::Thrift::CompactProtocol, max_length) } .to raise_error(StandardError, /Batch header have size \d+, but limit #{max_length}/) end end end context 'when one span exceed max length' do let(:context) do Jaeger::SpanContext.new( trace_id: Jaeger::TraceId.generate, span_id: Jaeger::TraceId.generate, flags: Jaeger::SpanContext::Flags::DEBUG ) end let(:valid_span_before) { Jaeger::Span.new(context, 'first_span', nil) } let(:invalid_span_tags) { { 'key' => '0' * 3_000 } } let(:invalid_span) { Jaeger::Span.new(context, 'invalid_span', nil, tags: invalid_span_tags) } let(:valid_span_after) { Jaeger::Span.new(context, 'after_span', nil) } let(:example_spans) { [valid_span_before, invalid_span, valid_span_after] } let(:max_length) { 2_500 } it 'skip invalid span' do encoded_batches = encoder.encode_limited_size(example_spans, ::Thrift::BinaryProtocol, max_length) expect(encoded_batches.size).to eq 1 expect(encoded_batches.first.spans.size).to eq 2 expect(encoded_batches.first.spans.first.operationName).to eq valid_span_before.operation_name expect(encoded_batches.first.spans.last.operationName).to eq valid_span_after.operation_name end it 'log invalid span name' do encoder.encode_limited_size(example_spans, ::Thrift::BinaryProtocol, max_length) expect(logger).to have_received(:warn).with(/Skip span invalid_span with size \d+/) end it 'size of every batch not exceed limit with compact protocol' do encoded_batches = encoder.encode_limited_size(example_spans, ::Thrift::CompactProtocol, max_length) expect(encoded_batches.count).to eq 1 encoded_batches.each do |encoded_batch| transport = ::Thrift::MemoryBufferTransport.new protocol = ::Thrift::CompactProtocol.new(transport) encoded_batch.write(protocol) expect(transport.available).to be < max_length end end it 'size of every batch not exceed limit with binary protocol' do encoded_batches = encoder.encode_limited_size(example_spans, ::Thrift::BinaryProtocol, max_length) expect(encoded_batches.count).to eq 1 encoded_batches.each do |encoded_batch| transport = ::Thrift::MemoryBufferTransport.new protocol = ::Thrift::CompactProtocol.new(transport) encoded_batch.write(protocol) expect(transport.available).to be < max_length end end end end jaeger-client-ruby-1.2.0/spec/jaeger/extractors/000077500000000000000000000000001414770277100216015ustar00rootroot00000000000000jaeger-client-ruby-1.2.0/spec/jaeger/extractors/b3_rack_codec_spec.rb000066400000000000000000000046001414770277100256010ustar00rootroot00000000000000require 'spec_helper' describe Jaeger::Extractors::B3RackCodec do let(:span_context) { described_class.extract(carrier) } let(:operation_name) { 'operator-name' } let(:trace_id) { '58a515c97fd61fd7' } let(:parent_id) { '8e5a8c5509c8dcc1' } let(:span_id) { 'aba8be8d019abed2' } let(:flags) { '1' } let(:hexa_max_uint64) { 'f' * 16 } let(:hexa_max_uint128) { 'f' * 32 } let(:max_uint64) { 2**64 - 1 } let(:max_uint128) { 2**128 - 1 } context 'when header HTTP_X_B3_SAMPLED is present' do let(:carrier) do { 'HTTP_X_B3_TRACEID' => trace_id, 'HTTP_X_B3_SPANID' => span_id, 'HTTP_X_B3_PARENTSPANID' => parent_id, 'HTTP_X_B3_SAMPLED' => flags } end it 'has flags' do expect(span_context.flags).to eq(flags.to_i(16)) end context 'when trace-id is a max uint64' do let(:trace_id) { hexa_max_uint64 } it 'interprets it correctly' do expect(span_context.trace_id).to eq(max_uint64) end end context 'when trace-id is a max uint128' do let(:trace_id) { hexa_max_uint128 } it 'interprets it correctly' do expect(span_context.trace_id).to eq(max_uint128) end end context 'when parent-id is a max uint64' do let(:parent_id) { hexa_max_uint64 } it 'interprets it correctly' do expect(span_context.parent_id).to eq(max_uint64) end end context 'when span-id is a max uint64' do let(:span_id) { hexa_max_uint64 } it 'interprets it correctly' do expect(span_context.span_id).to eq(max_uint64) end end context 'when parent-id is 0' do let(:parent_id) { '0' } it 'sets parent_id to 0' do expect(span_context.parent_id).to eq(0) end end context 'when trace-id is missing' do let(:trace_id) { nil } it 'returns nil' do expect(span_context).to eq(nil) end end context 'when span-id is missing' do let(:span_id) { nil } it 'returns nil' do expect(span_context).to eq(nil) end end end context 'when header HTTP_X_B3_FLAGS is present' do let(:carrier) do { 'HTTP_X_B3_TRACEID' => trace_id, 'HTTP_X_B3_SPANID' => span_id, 'HTTP_X_B3_PARENTSPANID' => parent_id, 'HTTP_X_B3_FLAGS' => '1' } end it 'sets the DEBUG flag' do expect(span_context.flags).to eq(0x02) end end end jaeger-client-ruby-1.2.0/spec/jaeger/extractors/b3_text_map_codec_spec.rb000066400000000000000000000045211414770277100265040ustar00rootroot00000000000000require 'spec_helper' describe Jaeger::Extractors::B3TextMapCodec do let(:span_context) { described_class.extract(carrier) } let(:operation_name) { 'operator-name' } let(:trace_id) { '58a515c97fd61fd7' } let(:parent_id) { '8e5a8c5509c8dcc1' } let(:span_id) { 'aba8be8d019abed2' } let(:flags) { '1' } let(:hexa_max_uint64) { 'f' * 16 } let(:hexa_max_uint128) { 'f' * 32 } let(:max_uint64) { 2**64 - 1 } let(:max_uint128) { 2**128 - 1 } context 'when header x-b3-sampled is present' do let(:carrier) do { 'x-b3-traceid' => trace_id, 'x-b3-spanid' => span_id, 'x-b3-parentspanid' => parent_id, 'x-b3-sampled' => flags } end it 'has flags' do expect(span_context.flags).to eq(flags.to_i(16)) end context 'when trace-id is a max uint64' do let(:trace_id) { hexa_max_uint64 } it 'interprets it correctly' do expect(span_context.trace_id).to eq(max_uint64) end end context 'when trace-id is a max uint128' do let(:trace_id) { hexa_max_uint128 } it 'interprets it correctly' do expect(span_context.trace_id).to eq(max_uint128) end end context 'when parent-id is a max uint64' do let(:parent_id) { hexa_max_uint64 } it 'interprets it correctly' do expect(span_context.parent_id).to eq(max_uint64) end end context 'when span-id is a max uint64' do let(:span_id) { hexa_max_uint64 } it 'interprets it correctly' do expect(span_context.span_id).to eq(max_uint64) end end context 'when parent-id is 0' do let(:parent_id) { '0' } it 'sets parent_id to 0' do expect(span_context.parent_id).to eq(0) end end context 'when trace-id is missing' do let(:trace_id) { nil } it 'returns nil' do expect(span_context).to eq(nil) end end context 'when span-id is missing' do let(:span_id) { nil } it 'returns nil' do expect(span_context).to eq(nil) end end end context 'when header x-b3-flags is present' do let(:carrier) do { 'x-b3-traceid' => trace_id, 'x-b3-spanid' => span_id, 'x-b3-parentspanid' => parent_id, 'x-b3-flags' => '1' } end it 'sets the DEBUG flag' do expect(span_context.flags).to eq(0x02) end end end jaeger-client-ruby-1.2.0/spec/jaeger/extractors/jaeger_rack_codec_spec.rb000066400000000000000000000040631414770277100265350ustar00rootroot00000000000000require 'spec_helper' describe Jaeger::Extractors::JaegerRackCodec do let(:span_context) { described_class.extract(carrier) } let(:carrier) do { 'HTTP_UBER_TRACE_ID' => "#{trace_id}:#{span_id}:#{parent_id}:#{flags}", 'HTTP_UBERCTX_FOO_BAR' => 'baz' } end let(:trace_id) { '58a515c97fd61fd7' } let(:parent_id) { '8e5a8c5509c8dcc1' } let(:span_id) { 'aba8be8d019abed2' } let(:flags) { '1' } let(:hexa_max_uint64) { 'ff' * 8 } let(:max_uint64) { 2**64 - 1 } shared_examples 'a valid trace' do it 'has flags' do expect(span_context.flags).to eq(flags.to_i(16)) end it 'has baggage' do expect(span_context.get_baggage_item('foo-bar')).to eq('baz') end context 'when trace-id is a max uint64' do let(:trace_id) { hexa_max_uint64 } it 'interprets it correctly' do expect(span_context.trace_id).to eq(max_uint64) end end context 'when parent-id is a max uint64' do let(:parent_id) { hexa_max_uint64 } it 'interprets it correctly' do expect(span_context.parent_id).to eq(max_uint64) end end context 'when span-id is a max uint64' do let(:span_id) { hexa_max_uint64 } it 'interprets it correctly' do expect(span_context.span_id).to eq(max_uint64) end end context 'when parent-id is 0' do let(:parent_id) { '0' } it 'sets parent_id to 0' do expect(span_context.parent_id).to eq(0) end end context 'when trace-id missing' do let(:trace_id) { nil } it 'returns nil' do expect(span_context).to eq(nil) end end context 'when span-id missing' do let(:span_id) { nil } it 'returns nil' do expect(span_context).to eq(nil) end end end context 'when serialized trace is not encoded' do it_behaves_like 'a valid trace' end context 'when serialized trace is encoded' do before do carrier['HTTP_UBER_TRACE_ID'] = CGI.escape(carrier['HTTP_UBER_TRACE_ID']) end it_behaves_like 'a valid trace' end end jaeger-client-ruby-1.2.0/spec/jaeger/extractors/jaeger_text_map_codec_spec.rb000066400000000000000000000032141414770277100274330ustar00rootroot00000000000000require 'spec_helper' describe Jaeger::Extractors::JaegerTextMapCodec do let(:span_context) { described_class.extract(carrier) } let(:carrier) do { 'uber-trace-id' => "#{trace_id}:#{span_id}:#{parent_id}:#{flags}", 'uberctx-foo-bar' => 'baz' } end let(:trace_id) { '58a515c97fd61fd7' } let(:parent_id) { '8e5a8c5509c8dcc1' } let(:span_id) { 'aba8be8d019abed2' } let(:flags) { '1' } let(:hexa_max_uint64) { 'ff' * 8 } let(:max_uint64) { 2**64 - 1 } it 'has flags' do expect(span_context.flags).to eq(flags.to_i(16)) end it 'has baggage' do expect(span_context.get_baggage_item('foo-bar')).to eq('baz') end context 'when trace-id is a max uint64' do let(:trace_id) { hexa_max_uint64 } it 'interprets it correctly' do expect(span_context.trace_id).to eq(max_uint64) end end context 'when parent-id is a max uint64' do let(:parent_id) { hexa_max_uint64 } it 'interprets it correctly' do expect(span_context.parent_id).to eq(max_uint64) end end context 'when span-id is a max uint64' do let(:span_id) { hexa_max_uint64 } it 'interprets it correctly' do expect(span_context.span_id).to eq(max_uint64) end end context 'when parent-id is 0' do let(:parent_id) { '0' } it 'sets parent_id to 0' do expect(span_context.parent_id).to eq(0) end end context 'when trace-id missing' do let(:trace_id) { nil } it 'returns nil' do expect(span_context).to eq(nil) end end context 'when span-id missing' do let(:span_id) { nil } it 'returns nil' do expect(span_context).to eq(nil) end end end jaeger-client-ruby-1.2.0/spec/jaeger/extractors/trace_context_rack_codec_spec.rb000066400000000000000000000036321414770277100301430ustar00rootroot00000000000000require 'spec_helper' describe Jaeger::Extractors::TraceContextRackCodec do it 'parses valid sampled v0 traceparent' do carrier = { 'HTTP_TRACEPARENT' => '00-00000000030c22224787fd223b027d8d-000f5cf3018c0a93-01' } span_context = described_class.extract(carrier) expect(span_context.trace_id).to eq(943_123_332_103_493_452_342_394_253) expect(span_context.span_id).to eq(4_324_323_423_423_123) expect(span_context.sampled?).to eq(true) end it 'parses valid non-sampled v0 traceparent' do carrier = { 'HTTP_TRACEPARENT' => '00-00000000030c22224787fd223b027d8d-000f5cf3018c0a93-00' } span_context = described_class.extract(carrier) expect(span_context.trace_id).to eq(943_123_332_103_493_452_342_394_253) expect(span_context.span_id).to eq(4_324_323_423_423_123) expect(span_context.sampled?).to eq(false) end it 'parses valid traceparent with largest trace id and span id' do carrier = { 'HTTP_TRACEPARENT' => '00-ffffffffffffffffffffffffffffffff-ffffffffffffffff-01' } span_context = described_class.extract(carrier) expect(span_context.trace_id).to eq(2**128 - 1) expect(span_context.span_id).to eq(2**64 - 1) expect(span_context.sampled?).to eq(true) end it 'returns nil when unhandled version' do carrier = { 'HTTP_TRACEPARENT' => '01-00000000030c22224787fd223b027d8d-000f5cf3018c0a93-01' } span_context = described_class.extract(carrier) expect(span_context).to eq(nil) end it 'returns nil when trace id is 0' do carrier = { 'HTTP_TRACEPARENT' => '00-00000000000000000000000000000000-000f5cf3018c0a93-01' } span_context = described_class.extract(carrier) expect(span_context).to eq(nil) end it 'returns nil when span id is 0' do carrier = { 'HTTP_TRACEPARENT' => '00-00000000030c22224787fd223b027d8d-0000000000000000-01' } span_context = described_class.extract(carrier) expect(span_context).to eq(nil) end end jaeger-client-ruby-1.2.0/spec/jaeger/injectors/000077500000000000000000000000001414770277100214035ustar00rootroot00000000000000jaeger-client-ruby-1.2.0/spec/jaeger/injectors/b3_rack_codec_spec.rb000066400000000000000000000031311414770277100254010ustar00rootroot00000000000000require 'spec_helper' describe Jaeger::Injectors::B3RackCodec do let(:inject) { described_class.inject(span_context, carrier) } let(:span_context) { build_span_context } let(:carrier) { {} } it 'sets trace information' do inject expect(carrier['x-b3-traceid']).to eq(span_context.trace_id.to_s(16).rjust(16, '0')) expect(carrier['x-b3-spanid']).to eq(span_context.span_id.to_s(16).rjust(16, '0')) expect(carrier['x-b3-parentspanid']).to eq(span_context.parent_id.to_s(16).rjust(16, '0')) expect(carrier['x-b3-sampled']).to eq(span_context.flags.to_s(16)) end context 'when sampler flag is DEBUG' do let(:span_context) do Jaeger::SpanContext.new( span_id: Jaeger::TraceId.generate, trace_id: Jaeger::TraceId.generate, flags: 0x02 ) end it 'sets the x-b3-flags header' do inject expect(carrier).to have_key 'x-b3-flags' expect(carrier['x-b3-flags']).to eq '1' end it 'does not set the x-b3-sampled header' do inject expect(carrier).not_to have_key 'x-b3-sampled' end end context 'when span context IDs are longer than 16 characters' do let(:span_context) do Jaeger::SpanContext.new( span_id: 0xFFFFFFFFFFFFFFFFF, parent_id: 0xFFFFFFFFFFFFFFFFF, trace_id: 0xFFFFFFFFFFFFFFFFF, flags: 0 ) end it 'pads the hex id strings to 32 characters' do inject expect(carrier['x-b3-traceid'].length).to eq 32 expect(carrier['x-b3-spanid'].length).to eq 32 expect(carrier['x-b3-parentspanid'].length).to eq 32 end end end jaeger-client-ruby-1.2.0/spec/jaeger/injectors/jaeger_rack_codec_spec.rb000066400000000000000000000020041414770277100263300ustar00rootroot00000000000000require 'spec_helper' describe Jaeger::Injectors::JaegerRackCodec do let(:tracer) do Jaeger::Tracer.new( reporter: instance_spy(Jaeger::Reporters::RemoteReporter), sampler: Jaeger::Samplers::Const.new(true), injectors: Jaeger::Injectors.prepare({}), extractors: Jaeger::Extractors.prepare({}) ) end let(:span) { tracer.start_span('test') } it 'sets trace information' do carrier = {} inject(span, carrier) expect(carrier['uber-trace-id']).to eq( [ span.context.trace_id.to_s(16), span.context.span_id.to_s(16), span.context.parent_id.to_s(16), span.context.flags.to_s(16) ].join('%3A') ) end it 'sets baggage' do span.set_baggage_item('foo', 'bar') span.set_baggage_item('x', 'y') carrier = {} inject(span, carrier) expect(carrier['uberctx-foo']).to eq('bar') expect(carrier['uberctx-x']).to eq('y') end def inject(span, carrier) described_class.inject(span.context, carrier) end end jaeger-client-ruby-1.2.0/spec/jaeger/injectors/jaeger_text_map_codec_spec.rb000066400000000000000000000020051414770277100272320ustar00rootroot00000000000000require 'spec_helper' describe Jaeger::Injectors::JaegerTextMapCodec do let(:tracer) do Jaeger::Tracer.new( reporter: instance_spy(Jaeger::Reporters::RemoteReporter), sampler: Jaeger::Samplers::Const.new(true), injectors: Jaeger::Injectors.prepare({}), extractors: Jaeger::Extractors.prepare({}) ) end let(:span) { tracer.start_span('test') } it 'sets trace information' do carrier = {} inject(span, carrier) expect(carrier['uber-trace-id']).to eq( [ span.context.trace_id.to_s(16), span.context.span_id.to_s(16), span.context.parent_id.to_s(16), span.context.flags.to_s(16) ].join(':') ) end it 'sets baggage' do span.set_baggage_item('foo', 'bar') span.set_baggage_item('x', 'y') carrier = {} inject(span, carrier) expect(carrier['uberctx-foo']).to eq('bar') expect(carrier['uberctx-x']).to eq('y') end def inject(span, carrier) described_class.inject(span.context, carrier) end end jaeger-client-ruby-1.2.0/spec/jaeger/injectors/trace_context_rack_codec_spec.rb000066400000000000000000000023371414770277100277460ustar00rootroot00000000000000require 'spec_helper' describe Jaeger::Injectors::TraceContextRackCodec do let(:tracer) do Jaeger::Tracer.new( reporter: instance_spy(Jaeger::Reporters::RemoteReporter), sampler: Jaeger::Samplers::Const.new(true), injectors: Jaeger::Injectors.prepare({}), extractors: Jaeger::Extractors.prepare({}) ) end let(:span) { tracer.start_span('test') } it 'sets traceparent' do span_context = build_span_context( trace_id: 943_123_332_103_493_452_342_394_253, span_id: 4_324_323_423_423_123, flags: Jaeger::SpanContext::Flags::SAMPLED ) carrier = {} described_class.inject(span_context, carrier) expect(carrier['traceparent']).to eq('00-00000000030c22224787fd223b027d8d-000f5cf3018c0a93-01') end it 'sets traceparent with largest trace id and span id' do span_context = build_span_context( trace_id: 2**128 - 1, span_id: 2**64 - 1, flags: Jaeger::SpanContext::Flags::SAMPLED ) carrier = {} described_class.inject(span_context, carrier) expect(carrier['traceparent']).to eq('00-ffffffffffffffffffffffffffffffff-ffffffffffffffff-01') end def inject(span, carrier) described_class.inject(span.context, carrier) end end jaeger-client-ruby-1.2.0/spec/jaeger/rate_limiter_spec.rb000066400000000000000000000051551414770277100234300ustar00rootroot00000000000000require 'spec_helper' RSpec.describe Jaeger::RateLimiter do let(:start_time) { Time.now } before { Timecop.freeze(start_time) } after { Timecop.return } describe '#check_credit' do it 'returns false if item cost is higher than balance' do limiter = build_limiter(credits_per_second: 5) expect(limiter.check_credit(6)).to eq(false) end it 'returns true until there is credit left' do limiter = build_limiter(credits_per_second: 2) expect(limiter.check_credit(1.0)).to eq(true) expect(limiter.check_credit(1.0)).to eq(true) expect(limiter.check_credit(1.0)).to eq(false) end it 'returns true when there is enough credit' do limiter = build_limiter(credits_per_second: 2) # use all credit expect(limiter.check_credit(1.0)).to eq(true) expect(limiter.check_credit(1.0)).to eq(true) expect(limiter.check_credit(1.0)).to eq(false) # move time 250ms forward, not enough credits to pay for one sample Timecop.travel(start_time + 0.25) expect(limiter.check_credit(1.0)).to eq(false) # move time 250ms forward, now enough credits to pay for one sample Timecop.travel(start_time + 0.5) expect(limiter.check_credit(1.0)).to eq(true) expect(limiter.check_credit(1.0)).to eq(false) # move time 5s forward, enough to accumulate credits for 10 samples, # but it should still be capped at 2 Timecop.travel(start_time + 5.5) expect(limiter.check_credit(1.0)).to eq(true) expect(limiter.check_credit(1.0)).to eq(true) expect(limiter.check_credit(1.0)).to eq(false) end end describe '#update' do context 'when balance was full before the update' do it 'keeps the new balance full' do limiter = build_limiter(credits_per_second: 1) expect(limiter.check_credit(1.0)).to eq(true) limiter.update(credits_per_second: 2, max_balance: 2) expect(limiter.check_credit(1.0)).to eq(false) end end context 'when balance was half full before the update' do it 'marks the new balance half full' do limiter = build_limiter(credits_per_second: 2) expect(limiter.check_credit(1.0)).to eq(true) limiter.update(credits_per_second: 4, max_balance: 4) expect(limiter.check_credit(1.0)).to eq(true) expect(limiter.check_credit(1.0)).to eq(true) expect(limiter.check_credit(1.0)).to eq(false) end end end def build_limiter(credits_per_second:, **opts) described_class.new(**{ credits_per_second: credits_per_second, max_balance: credits_per_second }.merge(opts)) end end jaeger-client-ruby-1.2.0/spec/jaeger/recurring_executor_spec.rb000066400000000000000000000013521414770277100246610ustar00rootroot00000000000000require 'spec_helper' describe Jaeger::RecurringExecutor do let(:executor) { described_class.new(interval: interval) } let(:small_delay) { 0.05 } after { executor.stop } context 'when interval is set to 0' do let(:interval) { 0 } it 'executes block only once' do count = 0 executor.start { count += 1 } sleep(small_delay) expect(count).to eq(1) end end context 'when interval is above 0' do let(:interval) { 3 } it 'executes block periodically' do count = 0 allow(executor).to receive(:sleep).with(interval) do sleep(interval) if count >= 4 end executor.start { count += 1 } sleep(small_delay) expect(count).to eq(4) end end end jaeger-client-ruby-1.2.0/spec/jaeger/reporters/000077500000000000000000000000001414770277100214305ustar00rootroot00000000000000jaeger-client-ruby-1.2.0/spec/jaeger/reporters/composite_reporter_spec.rb000066400000000000000000000010551414770277100267140ustar00rootroot00000000000000require 'spec_helper' RSpec.describe Jaeger::Reporters::CompositeReporter do let(:reporter) { described_class.new(reporters: [reporter1, reporter2]) } let(:reporter1) { instance_spy(Jaeger::Reporters::InMemoryReporter) } let(:reporter2) { instance_spy(Jaeger::Reporters::RemoteReporter) } describe '#report' do it 'forwards span to all reporters' do span = build_span reporter.report(span) expect(reporter1).to have_received(:report).with(span) expect(reporter2).to have_received(:report).with(span) end end end jaeger-client-ruby-1.2.0/spec/jaeger/reporters/in_memory_reporter_spec.rb000066400000000000000000000011611414770277100267060ustar00rootroot00000000000000require 'spec_helper' RSpec.describe Jaeger::Reporters::InMemoryReporter do let(:reporter) { described_class.new } describe '#report' do it 'adds span to in memory spans list' do span1 = build_span span2 = build_span reporter.report(span1) reporter.report(span2) expect(reporter.spans).to eq([span1, span2]) end end describe '#clear' do it 'clears spans from the reporter' do span1 = build_span reporter.report(span1) reporter.clear span2 = build_span reporter.report(span2) expect(reporter.spans).to eq([span2]) end end end jaeger-client-ruby-1.2.0/spec/jaeger/reporters/logging_reporter_spec.rb000066400000000000000000000016171414770277100263440ustar00rootroot00000000000000require 'spec_helper' RSpec.describe Jaeger::Reporters::LoggingReporter do let(:reporter) { described_class.new(logger: logger) } let(:logger) { instance_spy(Logger) } describe '#report' do it 'logs out span information' do operation_name = 'my-op-name' start_time = Time.utc(2018, 11, 10, 15, 24, 30) end_time = Time.utc(2018, 11, 10, 15, 24, 33) span = build_span(operation_name: operation_name, start_time: start_time) span.finish(end_time: end_time) reporter.report(span) expect(logger).to have_received(:info).with( <<-STR.gsub(/\s+/, ' ').strip Span reported: {:operation_name=>"#{operation_name}", :start_time=>"#{start_time.iso8601}", :end_time=>"#{end_time.iso8601}", :trace_id=>"#{span.context.to_trace_id}", :span_id=>"#{span.context.to_span_id}"} STR ) end end end jaeger-client-ruby-1.2.0/spec/jaeger/reporters/null_reporter_spec.rb000066400000000000000000000003741414770277100256670ustar00rootroot00000000000000require 'spec_helper' RSpec.describe Jaeger::Reporters::NullReporter do describe '#report' do it 'does nothing' do span = instance_double(Jaeger::Span) expect { described_class.new.report(span) }.not_to raise_error end end end jaeger-client-ruby-1.2.0/spec/jaeger/reporters/remote_reporter_spec.rb000066400000000000000000000025131414770277100262050ustar00rootroot00000000000000require 'spec_helper' RSpec.describe Jaeger::Reporters::RemoteReporter do let(:reporter) { described_class.new(sender: sender, flush_interval: 1) } let(:sender) { spy } let(:operation_name) { 'op-name' } before { allow(Thread).to receive(:new) } describe '#report' do let(:context) do Jaeger::SpanContext.new( trace_id: Jaeger::TraceId.generate, span_id: Jaeger::TraceId.generate, flags: flags ) end let(:span) { Jaeger::Span.new(context, operation_name, reporter) } context 'when span has debug mode enabled' do let(:flags) { Jaeger::SpanContext::Flags::DEBUG } it 'buffers the span' do reporter.report(span) reporter.flush expect(sender).to have_received(:send_spans).once end end context 'when span is sampled' do let(:flags) { Jaeger::SpanContext::Flags::SAMPLED } it 'buffers the span' do reporter.report(span) reporter.flush expect(sender).to have_received(:send_spans).once end end context 'when span does not have debug mode nor is sampled' do let(:flags) { Jaeger::SpanContext::Flags::NONE } it 'does not buffer the span' do reporter.report(span) reporter.flush expect(sender).not_to have_received(:send_spans) end end end end jaeger-client-ruby-1.2.0/spec/jaeger/samplers/000077500000000000000000000000001414770277100212315ustar00rootroot00000000000000jaeger-client-ruby-1.2.0/spec/jaeger/samplers/const_spec.rb000066400000000000000000000016501414770277100237200ustar00rootroot00000000000000require 'spec_helper' RSpec.describe Jaeger::Samplers::Const do let(:sampler) { described_class.new(decision) } let(:sample_args) { { trace_id: Jaeger::TraceId.generate } } let(:sample_result) { sampler.sample(sample_args) } let(:is_sampled) { sample_result[0] } let(:tags) { sample_result[1] } context 'when decision is set to true' do let(:decision) { true } it 'sets sampling to always true' do expect(is_sampled).to eq(true) end it 'returns tags with param 1' do expect(tags).to eq( 'sampler.type' => 'const', 'sampler.param' => 1 ) end end context 'when decision is set to false' do let(:decision) { false } it 'sets sampling to always false' do expect(is_sampled).to eq(false) end it 'returns tags with param 0' do expect(tags).to eq( 'sampler.type' => 'const', 'sampler.param' => 0 ) end end end jaeger-client-ruby-1.2.0/spec/jaeger/samplers/guaranteed_throughput_probabilistic_spec.rb000066400000000000000000000041301414770277100321040ustar00rootroot00000000000000require 'spec_helper' RSpec.describe Jaeger::Samplers::GuaranteedThroughputProbabilistic do let(:sampler) do described_class.new( lower_bound: lower_bound, rate: rate, lower_bound_sampler: lower_bound_sampler ) end let(:lower_bound) { 5 } let(:rate) { 0.5 } let(:lower_bound_sampler) { instance_double(Jaeger::Samplers::RateLimiting) } let(:max_traces_per_second) { 10 } let(:sample_args) { { trace_id: trace_id } } let(:sample_result) { sampler.sample(**sample_args) } let(:is_sampled) { sample_result[0] } let(:tags) { sample_result[1] } context 'when rate is set to 0' do let(:rate) { 0 } let(:trace_id) { Jaeger::TraceId.generate } context 'when lower bound return false' do before do allow(lower_bound_sampler).to receive(:sample) .and_return([false, {}]) end it 'returns false for every trace' do expect(is_sampled).to eq(false) end it 'returns tags with param 0' do expect(tags).to eq( 'sampler.type' => 'lowerbound', 'sampler.param' => rate ) end end context 'when lower bound sampler returns true' do before do allow(lower_bound_sampler).to receive(:sample) .and_return([true, {}]) end it 'returns true' do expect(is_sampled).to eq(true) end it 'returns tags with lower bound param' do expect(tags).to eq( 'sampler.type' => 'lowerbound', 'sampler.param' => rate ) end end end context 'when rate is set to 1' do let(:rate) { 1 } let(:trace_id) { Jaeger::TraceId.generate } before do allow(lower_bound_sampler).to receive(:sample) end it 'returns true for every trace' do expect(is_sampled).to eq(true) end it 'returns tags with param 1' do expect(tags).to eq( 'sampler.type' => 'probabilistic', 'sampler.param' => rate ) end it 'calls lower bound sampler' do expect(lower_bound_sampler).to receive(:sample).with(sample_args) is_sampled end end end jaeger-client-ruby-1.2.0/spec/jaeger/samplers/per_operation_spec.rb000066400000000000000000000107741414770277100254470ustar00rootroot00000000000000require 'spec_helper' RSpec.describe Jaeger::Samplers::PerOperation do let(:sampler) { described_class.new(strategies: strategies, max_operations: max_operations) } let(:max_operations) { 100 } let(:start_time) { Time.now } before { Timecop.freeze(start_time) } after { Timecop.return } context 'when operation strategy is defined' do context 'when operation rate is set to 0' do let(:strategies) do { default_sampling_probability: 1.0, default_lower_bound_traces_per_second: 1, per_operation_strategies: [ { operation: 'foo', probabilistic_sampling: { sampling_rate: 0 } } ] } end it 'uses lower bound sampler' do is_sampled, _tags = sampler.sample(sample_args(operation_name: 'foo')) expect(is_sampled).to eq(true) # false because limit is full is_sampled, _tags = sampler.sample(sample_args(operation_name: 'foo')) expect(is_sampled).to eq(false) # true because different operation is_sampled, _tags = sampler.sample(sample_args(operation_name: 'bar')) expect(is_sampled).to eq(true) end it 'returns tags with lower bound param' do _is_sampled, tags = sampler.sample(sample_args(operation_name: 'foo')) expect(tags).to eq( 'sampler.type' => 'lowerbound', 'sampler.param' => 0 ) end end context 'when operation rate is set to 1' do let(:strategies) do { default_sampling_probability: 0, default_lower_bound_traces_per_second: 1, per_operation_strategies: [ { operation: 'foo', probabilistic_sampling: { sampling_rate: 1.0 } } ] } end it 'uses operation probabilistic sampler' do is_sampled, _tags = sampler.sample(sample_args(operation_name: 'foo')) expect(is_sampled).to eq(true) # true because rate is set to 1 is_sampled, _tags = sampler.sample(sample_args(operation_name: 'foo')) expect(is_sampled).to eq(true) is_sampled, _tags = sampler.sample(sample_args(operation_name: 'bar')) expect(is_sampled).to eq(true) # false because different operation and lower boundary is full is_sampled, _tags = sampler.sample(sample_args(operation_name: 'bar')) expect(is_sampled).to eq(false) end it 'returns tags with lower bound param' do _is_sampled, tags = sampler.sample(sample_args(operation_name: 'foo')) expect(tags).to eq( 'sampler.type' => 'probabilistic', 'sampler.param' => 1.0 ) end end end context 'when operation strategy is undefined' do context 'when default rate is set to 0' do let(:strategies) do { default_sampling_probability: 0, default_lower_bound_traces_per_second: 1 } end it 'uses lower bound sampler' do is_sampled, _tags = sampler.sample(sample_args(operation_name: 'foo')) expect(is_sampled).to eq(true) # false because limit is full is_sampled, _tags = sampler.sample(sample_args(operation_name: 'foo')) expect(is_sampled).to eq(false) # true because different operation is_sampled, _tags = sampler.sample(sample_args(operation_name: 'bar')) expect(is_sampled).to eq(true) end it 'returns tags with lower bound param' do _is_sampled, tags = sampler.sample(sample_args(operation_name: 'foo')) expect(tags).to eq( 'sampler.type' => 'lowerbound', 'sampler.param' => 0 ) end end context 'when default rate is set to 1' do let(:strategies) do { default_sampling_probability: 1, default_lower_bound_traces_per_second: 1 } end it 'uses probabilistic sampling which returns always true' do is_sampled, _tags = sampler.sample(sample_args(operation_name: 'foo')) expect(is_sampled).to eq(true) is_sampled, _tags = sampler.sample(sample_args(operation_name: 'foo')) expect(is_sampled).to eq(true) end it 'returns tags with lower bound param' do _is_sampled, tags = sampler.sample(sample_args(operation_name: 'foo')) expect(tags).to eq( 'sampler.type' => 'probabilistic', 'sampler.param' => 1 ) end end end def sample_args(opts = {}) { trace_id: Jaeger::TraceId.generate, operation_name: 'operation-name' }.merge(opts) end end jaeger-client-ruby-1.2.0/spec/jaeger/samplers/probabilistic_spec.rb000066400000000000000000000034541414770277100254240ustar00rootroot00000000000000require 'spec_helper' RSpec.describe Jaeger::Samplers::Probabilistic do let(:sampler) { described_class.new(rate: rate) } let(:sample_args) { { trace_id: trace_id } } let(:sample_result) { sampler.sample(**sample_args) } let(:is_sampled) { sample_result[0] } let(:tags) { sample_result[1] } context 'when rate is set to 0' do let(:rate) { 0 } let(:trace_id) { Jaeger::TraceId.generate } it 'returns false for every trace' do expect(is_sampled).to eq(false) end it 'returns tags with param 0' do expect(tags).to eq( 'sampler.type' => 'probabilistic', 'sampler.param' => 0 ) end end context 'when rate is set to 0.5' do let(:rate) { 0.5 } context 'when trace is over the boundary' do let(:trace_id) { Jaeger::TraceId::TRACE_ID_UPPER_BOUND / 2 + 1 } it 'returns false' do expect(is_sampled).to eq(false) end it 'returns tags with param 0.5' do expect(tags).to eq( 'sampler.type' => 'probabilistic', 'sampler.param' => 0.5 ) end end context 'when trace is under the boundary' do let(:trace_id) { Jaeger::TraceId::TRACE_ID_UPPER_BOUND / 2 - 1 } it 'returns true' do expect(is_sampled).to eq(true) end it 'returns tags with param 0.5' do expect(tags).to eq( 'sampler.type' => 'probabilistic', 'sampler.param' => 0.5 ) end end end context 'when rate is set to 1' do let(:rate) { 1 } let(:trace_id) { Jaeger::TraceId.generate } it 'returns true for every trace' do expect(is_sampled).to eq(true) end it 'returns tags with param 1' do expect(tags).to eq( 'sampler.type' => 'probabilistic', 'sampler.param' => 1 ) end end end jaeger-client-ruby-1.2.0/spec/jaeger/samplers/rate_limiting_spec.rb000066400000000000000000000016511414770277100254220ustar00rootroot00000000000000require 'spec_helper' RSpec.describe Jaeger::Samplers::RateLimiting do let(:sampler) { described_class.new(max_traces_per_second: max_traces_per_second) } let(:max_traces_per_second) { 10 } let(:sample_args) { { trace_id: Jaeger::TraceId.generate } } let(:sample_result) { sampler.sample(sample_args) } let(:is_sampled) { sample_result[0] } let(:tags) { sample_result[1] } context 'when max_traces_per_second is negative' do let(:max_traces_per_second) { -1 } it 'throws an error' do expect { sampler }.to raise_error( "max_traces_per_second must not be negative, got #{max_traces_per_second}" ) end end describe '#sample' do it 'returns a boolean' do expect(is_sampled).to be(true).or be(false) end it 'returns tags' do expect(tags).to eq( 'sampler.type' => 'ratelimiting', 'sampler.param' => max_traces_per_second ) end end end jaeger-client-ruby-1.2.0/spec/jaeger/samplers/remote_controlled/000077500000000000000000000000001414770277100247515ustar00rootroot00000000000000jaeger-client-ruby-1.2.0/spec/jaeger/samplers/remote_controlled/instructions_fetcher_spec.rb000066400000000000000000000023151414770277100325550ustar00rootroot00000000000000require 'spec_helper' RSpec.describe Jaeger::Samplers::RemoteControlled::InstructionsFetcher do let(:fetcher) { described_class.new(host: host, port: port, service_name: service_name) } let(:host) { 'some-host' } let(:port) { 1234 } let(:service_name) { 'test-service' } it 'returns parsed response on success' do body = { 'foo' => 'bar' } serialized_body = body.to_json stub_request(:get, "http://#{host}:#{port}/sampling?service=#{service_name}") .to_return(status: 200, body: serialized_body, headers: {}) expect(fetcher.fetch).to eq(body) end it 'raises FetchFailed when http code is not 2xx' do stub_request(:get, "http://#{host}:#{port}/sampling?service=#{service_name}") .to_return(status: 400, body: 'Bad Request', headers: {}) expect { fetcher.fetch } .to raise_error(described_class::FetchFailed, 'Unsuccessful response (code=400)') end it 'raises FetchFailed when request throws an exception' do stub_request(:get, "http://#{host}:#{port}/sampling?service=#{service_name}") .to_raise(StandardError.new('some error')) expect { fetcher.fetch } .to raise_error(described_class::FetchFailed, '#') end end jaeger-client-ruby-1.2.0/spec/jaeger/samplers/remote_controlled_spec.rb000066400000000000000000000072721414770277100263200ustar00rootroot00000000000000require 'spec_helper' RSpec.describe Jaeger::Samplers::RemoteControlled do let(:sampler) do described_class.new( logger: logger, poll_executor: poll_executor, instructions_fetcher: instructions_fetcher ) end let(:logger) { Logger.new('/dev/null') } let(:poll_executor) { {} } let(:instructions_fetcher) { instance_spy(described_class::InstructionsFetcher) } let(:trace_id) { Jaeger::TraceId.generate } let(:operation_name) { 'operation-name' } let(:sample_args) { { trace_id: trace_id, operation_name: operation_name } } let(:parsed_response) { nil } before do allow(instructions_fetcher).to receive(:fetch).and_return(parsed_response) end context 'when agent returns probabilistic strategy' do let(:rate) { 0.6 } let(:parsed_response) do { 'strategyType' => 'PROBABILISTIC', 'probabilisticSampling' => { 'samplingRate' => rate } } end it 'sets sampler to probabilistic sampler' do sampler.poll expect(underlying_sampler).to be_a(Jaeger::Samplers::Probabilistic) expect(underlying_sampler.rate).to eq(rate) end end context 'when agent returns rate limiting strategy' do let(:max_traces_per_second) { 6 } let(:parsed_response) do { 'strategyType' => 'RATE_LIMITING', 'rateLimitingSampling' => { 'maxTracesPerSecond' => max_traces_per_second } } end it 'sets sampler to ratelimiting sampler' do sampler.poll expect(underlying_sampler).to be_a(Jaeger::Samplers::RateLimiting) expect(underlying_sampler.max_traces_per_second).to eq(max_traces_per_second) end end context 'when agent returns per operation strategy' do let(:default_sampling_rate) { 0.002 } let(:op_sampling_rate) { 0.003 } let(:default_traces_per_second) { 2 } let(:parsed_response) do { 'strategyType' => 'PROBABILISTIC', 'operationSampling' => { 'defaultSamplingProbability' => default_sampling_rate, 'defaultLowerBoundTracesPerSecond' => default_traces_per_second, 'perOperationStrategies' => [ { 'operation' => operation_name, 'probabilisticSampling' => { 'samplingRate' => op_sampling_rate } } ] } } end it 'sets sampler to per operation sampler' do sampler.poll expect(underlying_sampler).to be_a(Jaeger::Samplers::PerOperation) expect(underlying_sampler.default_sampling_probability).to eq(default_sampling_rate) expect(underlying_sampler.lower_bound).to eq(default_traces_per_second) op_sampler = underlying_sampler.samplers[operation_name] expect(op_sampler).to be_a(Jaeger::Samplers::GuaranteedThroughputProbabilistic) expect(op_sampler.probabilistic_sampler.rate).to eq(op_sampling_rate) expect(op_sampler.lower_bound_sampler.max_traces_per_second).to eq(default_traces_per_second) end end context 'when agent returns unknown strategy' do let(:parsed_response) do { 'strategyType' => 'UH_WHAT_IS_THIS' } end it 'keeps the current strategy' do previous_sampler = underlying_sampler sampler.poll expect(underlying_sampler).to be(previous_sampler) end end context 'when fetching strategies fails' do before do allow(instructions_fetcher).to receive(:fetch) do raise Jaeger::Samplers::RemoteControlled::InstructionsFetcher::FetchFailed, 'ouch' end end it 'keeps the current strategy' do previous_sampler = underlying_sampler sampler.poll expect(underlying_sampler).to be(previous_sampler) end end def underlying_sampler sampler.sampler end end jaeger-client-ruby-1.2.0/spec/jaeger/scope_manager/000077500000000000000000000000001414770277100222065ustar00rootroot00000000000000jaeger-client-ruby-1.2.0/spec/jaeger/scope_manager/scope_identifier_spec.rb000066400000000000000000000004261414770277100270620ustar00rootroot00000000000000require 'spec_helper' RSpec.describe Jaeger::ScopeManager::ScopeIdentifier do describe '.generate' do it 'generates an identifier' do id = described_class.generate expect(id).to be_a(Symbol) expect(id).to match(/opentracing_[A-Z]{8}/) end end end jaeger-client-ruby-1.2.0/spec/jaeger/scope_manager_spec.rb000066400000000000000000000015171414770277100235510ustar00rootroot00000000000000require 'spec_helper' RSpec.describe Jaeger::ScopeManager do let(:scope_manager) { described_class.new } let(:span) { instance_spy(Jaeger::Span) } context 'when activating a span' do it 'marks the span active' do scope_manager.activate(span) expect(scope_manager.active.span).to eq(span) end it 'changes the active span' do span2 = instance_spy(Jaeger::Span) scope_manager.activate(span) scope_manager.activate(span2) expect(scope_manager.active.span).to eq(span2) end end context 'when closing an active span' do it 'reverts to the previous active span' do span2 = instance_spy(Jaeger::Span) scope_manager.activate(span) scope_manager.activate(span2) scope_manager.active.close expect(scope_manager.active.span).to eq(span) end end end jaeger-client-ruby-1.2.0/spec/jaeger/scope_spec.rb000066400000000000000000000030341414770277100220530ustar00rootroot00000000000000require 'spec_helper' RSpec.describe Jaeger::Scope do let(:span) { instance_spy(Jaeger::Span) } let(:scope_stack) { Jaeger::ScopeManager::ScopeStack.new } let(:finish_on_close) { true } let(:scope) { described_class.new(span, scope_stack, finish_on_close: finish_on_close) } before do scope_stack.push(scope) end describe '#span' do it 'returns scope span' do scope = described_class.new(span, scope_stack, finish_on_close: true) expect(scope.span).to eq(span) end end describe '#close' do context 'when finish_on_close is true' do let(:finish_on_close) { true } it 'finishes the span' do scope.close expect(scope.span).to have_received(:finish) end it 'removes the scope from the scope stack' do expect(scope_stack.peek).to eq(scope) scope.close expect(scope_stack.peek).to eq(nil) end end context 'when finish_on_close is false' do let(:finish_on_close) { false } it 'does not finish the span' do scope.close expect(scope.span).not_to have_received(:finish) end it 'removes the scope from the scope stack' do expect(scope_stack.peek).to eq(scope) scope.close expect(scope_stack.peek).to eq(nil) end end context 'when scope is already closed' do before { scope.close } it 'throws an exception' do expect { scope.close } .to raise_error("Tried to close already closed span: #{scope.inspect}") end end end end jaeger-client-ruby-1.2.0/spec/jaeger/span_context_spec.rb000066400000000000000000000040261414770277100234510ustar00rootroot00000000000000require 'spec_helper' RSpec.describe Jaeger::SpanContext do describe '.create_from_parent_context' do let(:parent) do described_class.new( trace_id: trace_id, parent_id: nil, span_id: parent_span_id, flags: parent_flags ) end let(:trace_id) { 'trace-id' } let(:parent_span_id) { 'span-id' } let(:parent_flags) { described_class::Flags::SAMPLED } it 'has same trace ID' do context = described_class.create_from_parent_context(parent) expect(context.trace_id).to eq(trace_id) end it 'has same parent span id as parent id' do context = described_class.create_from_parent_context(parent) expect(context.parent_id).to eq(parent_span_id) end it 'has same its own span id' do context = described_class.create_from_parent_context(parent) expect(context.span_id).not_to eq(parent_span_id) end it 'has parent flags' do context = described_class.create_from_parent_context(parent) expect(context.flags).to eq(parent_flags) end it 'has parent baggage' do parent.set_baggage_item('foo', 'bar') context = described_class.create_from_parent_context(parent) expect(context.baggage).to eq('foo' => 'bar') # Ensure changing parent baggage doesn't change the child parent.set_baggage_item('foo', 'bar2') expect(context.baggage).to eq('foo' => 'bar') end end describe '#to_trace_id' do it 'returns trace id in hex format' do span_context = build_span_context(trace_id: 67_667_974_448_284_343) expect(span_context.to_trace_id).to eq('f067aa0ba902b7') end end describe '#to_span_id' do it 'returns span id in hex format' do span_context = build_span_context(span_id: 67_667_974_448_284_343) expect(span_context.to_span_id).to eq('f067aa0ba902b7') end end def build_span_context(opts) described_class.new(**{ trace_id: Jaeger::TraceId.generate, span_id: Jaeger::TraceId.generate, flags: 0 }.merge(opts)) end end jaeger-client-ruby-1.2.0/spec/jaeger/span_spec.rb000066400000000000000000000047271414770277100217150ustar00rootroot00000000000000require 'spec_helper' RSpec.describe Jaeger::Span do describe '#log' do let(:span) { described_class.new(nil, 'operation_name', nil) } it 'is deprecated' do expect { span.log(key: 'value') } .to output(/Span#log is deprecated/).to_stderr end it 'delegates to #log_kv' do allow(span).to receive(:log_kv) args = { key: 'value' } span.log(**args) expect(span).to have_received(:log_kv).with(**args) end end describe '#log_kv' do let(:span) { described_class.new(nil, 'operation_name', nil) } let(:fields) { { key1: 'value1', key2: 69 } } let(:expected_thrift_fields) do [ Jaeger::Thrift::Tag.new(key: 'key1', vType: 0, vStr: 'value1'), Jaeger::Thrift::Tag.new(key: 'key2', vType: 3, vLong: 69) ] end it 'returns nil' do expect(span.log_kv(key: 'value')).to be_nil end it 'adds log to span' do span.log_kv(**fields) expect(span.logs.count).to eq(1) thrift_log = span.logs[0] expect(thrift_log.timestamp).to be_a(Integer) expect(thrift_log.fields).to match(expected_thrift_fields) end it 'adds log to span with specific timestamp' do timestamp = Time.now span.log_kv(**fields.merge(timestamp: timestamp)) expect(span.logs.count).to eq(1) thrift_log = span.logs[0] expect(thrift_log.timestamp).to eq((timestamp.to_f * 1_000_000).to_i) expect(thrift_log.fields).to match(expected_thrift_fields) end end it 'stores and retrieves baggage' do span_context = build_span_context span = described_class.new(span_context, 'operation_name', nil) span.set_baggage_item('foo', 'bar') expect(span.get_baggage_item('foo')).to eq('bar') span.set_baggage_item('foo', 'baz') expect(span.get_baggage_item('foo')).to eq('baz') end describe '#set_tag' do let(:span_context) { build_span_context } let(:span) { described_class.new(span_context, 'operation_name', nil) } context 'when sampling.priority' do it 'sets debug flag to true when sampling.priority is greater than 0' do span.set_tag('sampling.priority', 1) expect(span.context.debug?).to eq(true) expect(span.context.sampled?).to eq(true) end it 'sets sampled flag to false when sampling.priority is 0' do span.set_tag('sampling.priority', 0) expect(span.context.debug?).to eq(false) expect(span.context.sampled?).to eq(false) end end end end jaeger-client-ruby-1.2.0/spec/jaeger/trace_id_spec.rb000066400000000000000000000015651414770277100225230ustar00rootroot00000000000000require 'spec_helper' describe Jaeger::TraceId do describe '.base16_hex_id_to_uint64' do it 'returns 0 when negative number' do id = described_class.base16_hex_id_to_uint64('-1') expect(id).to eq(0) end it 'returns 0 when larger than 64 bit uint' do id = described_class.base16_hex_id_to_uint64('10000000000000000') expect(id).to eq(0) end it 'converts base16 encoded hex to uint64' do id = described_class.base16_hex_id_to_uint64('ff' * 8) expect(id).to eq(2**64 - 1) end end describe '.uint64_id_to_int64' do it 'converts large IDs to negative longs' do id = described_class.uint64_id_to_int64(2**64 - 1) expect(id).to eq(-1) end it 'converts non large IDs to positive longs' do id = described_class.uint64_id_to_int64(2**63 - 1) expect(id).to eq(2**63 - 1) end end end jaeger-client-ruby-1.2.0/spec/jaeger/tracer_spec.rb000066400000000000000000000222161414770277100222250ustar00rootroot00000000000000require 'spec_helper' describe Jaeger::Tracer do let(:tracer) do described_class.new( reporter: reporter, sampler: sampler, injectors: Jaeger::Injectors.prepare(injectors), extractors: Jaeger::Extractors.prepare(extractors) ) end let(:reporter) { instance_spy(Jaeger::Reporters::RemoteReporter) } let(:sampler) { Jaeger::Samplers::Const.new(true) } let(:injectors) { {} } let(:extractors) { {} } describe '#start_span' do let(:operation_name) { 'operator-name' } context 'when a root span' do let(:span) { tracer.start_span(operation_name) } describe 'span context' do it 'has span_id' do expect(span.context.span_id).not_to be_nil end it 'has trace_id' do expect(span.context.trace_id).not_to be_nil end it 'does not have parent' do expect(span.context.parent_id).to eq(0) end end end context 'when a child span context is provided' do let(:root_span) { tracer.start_span(root_operation_name) } let(:span) { tracer.start_span(operation_name, child_of: root_span.context) } let(:root_operation_name) { 'root-operation-name' } describe 'span context' do it 'has span_id' do expect(span.context.span_id).not_to be_nil end it 'has trace_id' do expect(span.context.trace_id).not_to be_nil end it 'does not have parent_id' do expect(span.context.parent_id).not_to eq(0) end end end context 'when a child span is provided' do let(:root_span) { tracer.start_span(root_operation_name) } let(:span) { tracer.start_span(operation_name, child_of: root_span) } let(:root_operation_name) { 'root-operation-name' } describe 'span context' do it 'has span_id' do expect(span.context.span_id).not_to be_nil end it 'has trace_id' do expect(span.context.trace_id).not_to be_nil end it 'does not have parent_id' do expect(span.context.parent_id).not_to eq(0) end end end context 'when block given' do let(:span) { tracer.start_span(operation_name) } it 'returns the block value' do block_value = 'block value' return_value = tracer.start_span(operation_name) do |_span| block_value end expect(return_value).to eq(block_value) end it 'finishes the span after executing the block' do span_in_block = nil tracer.start_span(operation_name) do |span| span_in_block = span end expect(span_in_block.end_time).to be_a(Time) end end end describe '#start_active_span' do let(:operation_name) { 'operator-name' } context 'when a root span' do let(:scope) { tracer.start_active_span(operation_name) } let(:span) { scope.span } describe 'span context' do it 'has span_id' do expect(span.context.span_id).not_to be_nil end it 'has trace_id' do expect(span.context.trace_id).not_to be_nil end it 'does not have parent_id' do expect(span.context.parent_id).to eq(0) end end end context 'when a child span context is provided' do let(:root_span) { tracer.start_span(root_operation_name) } let(:scope) { tracer.start_active_span(operation_name, child_of: root_span.context) } let(:span) { scope.span } let(:root_operation_name) { 'root-operation-name' } describe 'span context' do it 'has span_id' do expect(span.context.span_id).not_to be_nil end it 'has trace_id' do expect(span.context.trace_id).not_to be_nil end it 'does not have parent_id' do expect(span.context.parent_id).not_to eq(0) end end end context 'when a child span is provided' do let(:root_span) { tracer.start_span(root_operation_name) } let(:scope) { tracer.start_active_span(operation_name, child_of: root_span) } let(:span) { scope.span } let(:root_operation_name) { 'root-operation-name' } describe 'span context' do it 'has span_id' do expect(span.context.span_id).not_to be_nil end it 'has trace_id' do expect(span.context.trace_id).not_to be_nil end it 'does not have parent_id' do expect(span.context.parent_id).not_to eq(0) end end end context 'when already existing active span' do let(:root_operation_name) { 'root-operation-name' } it 'uses active span as a parent span' do tracer.start_active_span(root_operation_name) do |parent_scope| tracer.start_active_span(operation_name) do |scope| expect(scope.span.context.parent_id).to eq(parent_scope.span.context.span_id) end end end end context 'when block given' do it 'returns the block value' do block_value = 'block value' return_value = tracer.start_active_span(operation_name) do |_scope| block_value end expect(return_value).to eq(block_value) end it 'closes the scope after executing the block' do scope_in_block = nil tracer.start_active_span(operation_name) do |scope| scope_in_block = scope end expect(tracer.scope_manager.active).to be(nil) expect(scope_in_block.span.end_time).to be_a(Time) end end end describe '#active_span' do let(:root_operation_name) { 'root-operation-name' } let(:operation_name) { 'operation-name' } it 'returns the span of the active scope' do expect(tracer.active_span).to eq(nil) tracer.start_active_span(root_operation_name) do |parent_scope| expect(tracer.active_span).to eq(parent_scope.span) tracer.start_active_span(operation_name) do |scope| expect(tracer.active_span).to eq(scope.span) end expect(tracer.active_span).to eq(parent_scope.span) end expect(tracer.active_span).to eq(nil) end end describe '#inject' do let(:operation_name) { 'operator-name' } let(:span) { tracer.start_span(operation_name) } let(:span_context) { span.context } let(:carrier) { {} } context 'when default injectors' do it 'calls inject on JaegerTextMapCodec when FORMAT_TEXT_MAP' do expect(Jaeger::Injectors::JaegerTextMapCodec).to receive(:inject) .with(span_context, carrier) tracer.inject(span_context, OpenTracing::FORMAT_TEXT_MAP, carrier) end it 'calls inject on JaegerRackCodec when FORMAT_RACK' do expect(Jaeger::Injectors::JaegerRackCodec).to receive(:inject) .with(span_context, carrier) tracer.inject(span_context, OpenTracing::FORMAT_RACK, carrier) end end context 'when custom injectors' do let(:injectors) do { OpenTracing::FORMAT_RACK => [custom_injector1, custom_injector2] } end let(:custom_injector1) { class_double(Jaeger::Injectors::JaegerTextMapCodec, inject: nil) } let(:custom_injector2) { class_double(Jaeger::Injectors::JaegerTextMapCodec, inject: nil) } it 'calls all custom injectors' do tracer.inject(span_context, OpenTracing::FORMAT_RACK, carrier) expect(custom_injector1).to have_received(:inject).with(span_context, carrier) expect(custom_injector2).to have_received(:inject).with(span_context, carrier) end end end describe '#extract' do let(:carrier) { {} } let(:span_context) { instance_double(Jaeger::SpanContext) } context 'when default extractors' do it 'calls extract on JaegerTextMapCodec when FORMAT_TEXT_MAP' do allow(Jaeger::Extractors::JaegerTextMapCodec).to receive(:extract) .with(carrier) .and_return(span_context) expect(tracer.extract(OpenTracing::FORMAT_TEXT_MAP, carrier)).to eq(span_context) end it 'calls extract on JaegerRackCodec when FORMAT_RACK' do allow(Jaeger::Extractors::JaegerRackCodec).to receive(:extract) .with(carrier) .and_return(span_context) expect(tracer.extract(OpenTracing::FORMAT_RACK, carrier)).to eq(span_context) end end context 'when custom extractors' do let(:extractors) do { OpenTracing::FORMAT_RACK => [custom_extractor1, custom_extractor2] } end let(:custom_extractor1) { double } let(:custom_extractor2) { double } it 'calls all custom extractors when no results' do allow(custom_extractor1).to receive(:extract).with(carrier).and_return(nil) allow(custom_extractor2).to receive(:extract).with(carrier).and_return(nil) expect(tracer.extract(OpenTracing::FORMAT_RACK, carrier)).to eq(nil) expect(custom_extractor1).to have_received(:extract) expect(custom_extractor2).to have_received(:extract) end it 'returns result from the first matching extractor' do allow(custom_extractor1).to receive(:extract).with(carrier) { span_context } expect(tracer.extract(OpenTracing::FORMAT_RACK, carrier)).to eq(span_context) end end end end jaeger-client-ruby-1.2.0/spec/spec_helper.rb000066400000000000000000000015301414770277100207630ustar00rootroot00000000000000require 'bundler/setup' require 'jaeger/client' require 'timecop' require 'webmock/rspec' RSpec.configure do |config| # Enable flags like --only-failures and --next-failure config.example_status_persistence_file_path = '.rspec_status' config.expect_with :rspec do |c| c.syntax = :expect end def build_span_context(opts = {}) Jaeger::SpanContext.new(**{ trace_id: Jaeger::TraceId.generate, span_id: Jaeger::TraceId.generate, flags: Jaeger::SpanContext::Flags::SAMPLED }.merge(opts)) end def build_span(opts = {}) span_context = opts.delete(:span_context) || build_span_context operation_name = opts.delete(:operation_name) || 'operation-name' reporter = opts.delete(:reporter) || Jaeger::Reporters::NullReporter.new Jaeger::Span.new(span_context, operation_name, reporter, **opts) end end jaeger-client-ruby-1.2.0/thrift/000077500000000000000000000000001414770277100165145ustar00rootroot00000000000000jaeger-client-ruby-1.2.0/thrift/agent.thrift000066400000000000000000000025551414770277100210430ustar00rootroot00000000000000# The MIT License (MIT) # # Copyright (c) 2016 Uber Technologies, Inc. # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to deal # in the Software without restriction, including without limitation the rights # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell # copies of the Software, and to permit persons to whom the Software is # furnished to do so, subject to the following conditions: # # The above copyright notice and this permission notice shall be included in # all copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN # THE SOFTWARE. include "jaeger.thrift" include "zipkincore.thrift" namespace java com.uber.jaeger.agent.thrift namespace rb Jaeger.Thrift service Agent { oneway void emitZipkinBatch(1: list spans) oneway void emitBatch(1: jaeger.Batch batch) } jaeger-client-ruby-1.2.0/thrift/gen-rb/000077500000000000000000000000001414770277100176665ustar00rootroot00000000000000jaeger-client-ruby-1.2.0/thrift/gen-rb/jaeger/000077500000000000000000000000001414770277100211235ustar00rootroot00000000000000jaeger-client-ruby-1.2.0/thrift/gen-rb/jaeger/thrift/000077500000000000000000000000001414770277100224235ustar00rootroot00000000000000jaeger-client-ruby-1.2.0/thrift/gen-rb/jaeger/thrift/agent.rb000066400000000000000000000047221414770277100240530ustar00rootroot00000000000000# # Autogenerated by Thrift Compiler (0.10.0) # # DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING # require 'thrift' require 'jaeger/thrift/agent_types' module Jaeger module Thrift module Agent class Client include ::Thrift::Client def emitZipkinBatch(spans) send_emitZipkinBatch(spans) end def send_emitZipkinBatch(spans) send_oneway_message('emitZipkinBatch', EmitZipkinBatch_args, :spans => spans) end def emitBatch(batch) send_emitBatch(batch) end def send_emitBatch(batch) send_oneway_message('emitBatch', EmitBatch_args, :batch => batch) end end class Processor include ::Thrift::Processor def process_emitZipkinBatch(seqid, iprot, oprot) args = read_args(iprot, EmitZipkinBatch_args) @handler.emitZipkinBatch(args.spans) return end def process_emitBatch(seqid, iprot, oprot) args = read_args(iprot, EmitBatch_args) @handler.emitBatch(args.batch) return end end # HELPER FUNCTIONS AND STRUCTURES class EmitZipkinBatch_args include ::Thrift::Struct, ::Thrift::Struct_Union SPANS = 1 FIELDS = { SPANS => {:type => ::Thrift::Types::LIST, :name => 'spans', :element => {:type => ::Thrift::Types::STRUCT, :class => ::Jaeger::Thrift::Zipkin::Span}} } def struct_fields; FIELDS; end def validate end ::Thrift::Struct.generate_accessors self end class EmitZipkinBatch_result include ::Thrift::Struct, ::Thrift::Struct_Union FIELDS = { } def struct_fields; FIELDS; end def validate end ::Thrift::Struct.generate_accessors self end class EmitBatch_args include ::Thrift::Struct, ::Thrift::Struct_Union BATCH = 1 FIELDS = { BATCH => {:type => ::Thrift::Types::STRUCT, :name => 'batch', :class => ::Jaeger::Thrift::Batch} } def struct_fields; FIELDS; end def validate end ::Thrift::Struct.generate_accessors self end class EmitBatch_result include ::Thrift::Struct, ::Thrift::Struct_Union FIELDS = { } def struct_fields; FIELDS; end def validate end ::Thrift::Struct.generate_accessors self end end end end jaeger-client-ruby-1.2.0/thrift/gen-rb/jaeger/thrift/agent/000077500000000000000000000000001414770277100235215ustar00rootroot00000000000000jaeger-client-ruby-1.2.0/thrift/gen-rb/jaeger/thrift/agent/agent.rb000066400000000000000000000051771414770277100251560ustar00rootroot00000000000000# # Autogenerated by Thrift Compiler (0.10.0) # # DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING # require 'thrift' require 'jaeger/thrift/agent/agent_types' module Jaeger module Thrift module Agent module Agent class Client include ::Thrift::Client def emitZipkinBatch(spans) send_emitZipkinBatch(spans) end def send_emitZipkinBatch(spans) send_oneway_message('emitZipkinBatch', EmitZipkinBatch_args, :spans => spans) end def emitBatch(batch) send_emitBatch(batch) end def send_emitBatch(batch) send_oneway_message('emitBatch', EmitBatch_args, :batch => batch) end end class Processor include ::Thrift::Processor def process_emitZipkinBatch(seqid, iprot, oprot) args = read_args(iprot, EmitZipkinBatch_args) @handler.emitZipkinBatch(args.spans) return end def process_emitBatch(seqid, iprot, oprot) args = read_args(iprot, EmitBatch_args) @handler.emitBatch(args.batch) return end end # HELPER FUNCTIONS AND STRUCTURES class EmitZipkinBatch_args include ::Thrift::Struct, ::Thrift::Struct_Union SPANS = 1 FIELDS = { SPANS => {:type => ::Thrift::Types::LIST, :name => 'spans', :element => {:type => ::Thrift::Types::STRUCT, :class => ::Jaeger::Thrift::Zipkin::Span}} } def struct_fields; FIELDS; end def validate end ::Thrift::Struct.generate_accessors self end class EmitZipkinBatch_result include ::Thrift::Struct, ::Thrift::Struct_Union FIELDS = { } def struct_fields; FIELDS; end def validate end ::Thrift::Struct.generate_accessors self end class EmitBatch_args include ::Thrift::Struct, ::Thrift::Struct_Union BATCH = 1 FIELDS = { BATCH => {:type => ::Thrift::Types::STRUCT, :name => 'batch', :class => ::Jaeger::Thrift::Batch} } def struct_fields; FIELDS; end def validate end ::Thrift::Struct.generate_accessors self end class EmitBatch_result include ::Thrift::Struct, ::Thrift::Struct_Union FIELDS = { } def struct_fields; FIELDS; end def validate end ::Thrift::Struct.generate_accessors self end end end end end jaeger-client-ruby-1.2.0/thrift/gen-rb/jaeger/thrift/agent/agent_constants.rb000066400000000000000000000003631414770277100272420ustar00rootroot00000000000000# # Autogenerated by Thrift Compiler (0.10.0) # # DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING # require 'thrift' require 'jaeger/thrift/agent/agent_types' module Jaeger module Thrift module Agent end end end jaeger-client-ruby-1.2.0/thrift/gen-rb/jaeger/thrift/agent/agent_types.rb000066400000000000000000000004371414770277100263740ustar00rootroot00000000000000# # Autogenerated by Thrift Compiler (0.10.0) # # DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING # require 'thrift' require 'jaeger/thrift/jaeger_types' require 'jaeger/thrift/zipkin/zipkincore_types' module Jaeger module Thrift module Agent end end end jaeger-client-ruby-1.2.0/thrift/gen-rb/jaeger/thrift/agent_constants.rb000066400000000000000000000003241414770277100261410ustar00rootroot00000000000000# # Autogenerated by Thrift Compiler (0.10.0) # # DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING # require 'thrift' require 'jaeger/thrift/agent_types' module Jaeger module Thrift end end jaeger-client-ruby-1.2.0/thrift/gen-rb/jaeger/thrift/agent_types.rb000066400000000000000000000004061414770277100252720ustar00rootroot00000000000000# # Autogenerated by Thrift Compiler (0.10.0) # # DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING # require 'thrift' require 'jaeger/thrift/jaeger_types' require 'jaeger/thrift/zipkin/zipkincore_types' module Jaeger module Thrift end end jaeger-client-ruby-1.2.0/thrift/gen-rb/jaeger/thrift/collector.rb000066400000000000000000000041431414770277100247400ustar00rootroot00000000000000# # Autogenerated by Thrift Compiler (0.10.0) # # DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING # require 'thrift' require 'jaeger/thrift/jaeger_types' module Jaeger module Thrift module Collector class Client include ::Thrift::Client def submitBatches(batches) send_submitBatches(batches) return recv_submitBatches() end def send_submitBatches(batches) send_message('submitBatches', SubmitBatches_args, :batches => batches) end def recv_submitBatches() result = receive_message(SubmitBatches_result) return result.success unless result.success.nil? raise ::Thrift::ApplicationException.new(::Thrift::ApplicationException::MISSING_RESULT, 'submitBatches failed: unknown result') end end class Processor include ::Thrift::Processor def process_submitBatches(seqid, iprot, oprot) args = read_args(iprot, SubmitBatches_args) result = SubmitBatches_result.new() result.success = @handler.submitBatches(args.batches) write_result(result, oprot, 'submitBatches', seqid) end end # HELPER FUNCTIONS AND STRUCTURES class SubmitBatches_args include ::Thrift::Struct, ::Thrift::Struct_Union BATCHES = 1 FIELDS = { BATCHES => {:type => ::Thrift::Types::LIST, :name => 'batches', :element => {:type => ::Thrift::Types::STRUCT, :class => ::Jaeger::Thrift::Batch}} } def struct_fields; FIELDS; end def validate end ::Thrift::Struct.generate_accessors self end class SubmitBatches_result include ::Thrift::Struct, ::Thrift::Struct_Union SUCCESS = 0 FIELDS = { SUCCESS => {:type => ::Thrift::Types::LIST, :name => 'success', :element => {:type => ::Thrift::Types::STRUCT, :class => ::Jaeger::Thrift::BatchSubmitResponse}} } def struct_fields; FIELDS; end def validate end ::Thrift::Struct.generate_accessors self end end end end jaeger-client-ruby-1.2.0/thrift/gen-rb/jaeger/thrift/jaeger_constants.rb000066400000000000000000000003251414770277100263010ustar00rootroot00000000000000# # Autogenerated by Thrift Compiler (0.10.0) # # DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING # require 'thrift' require 'jaeger/thrift/jaeger_types' module Jaeger module Thrift end end jaeger-client-ruby-1.2.0/thrift/gen-rb/jaeger/thrift/jaeger_types.rb000066400000000000000000000212371414770277100254360ustar00rootroot00000000000000# # Autogenerated by Thrift Compiler (0.10.0) # # DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING # require 'thrift' module Jaeger module Thrift module TagType STRING = 0 DOUBLE = 1 BOOL = 2 LONG = 3 BINARY = 4 VALUE_MAP = {0 => "STRING", 1 => "DOUBLE", 2 => "BOOL", 3 => "LONG", 4 => "BINARY"} VALID_VALUES = Set.new([STRING, DOUBLE, BOOL, LONG, BINARY]).freeze end module SpanRefType CHILD_OF = 0 FOLLOWS_FROM = 1 VALUE_MAP = {0 => "CHILD_OF", 1 => "FOLLOWS_FROM"} VALID_VALUES = Set.new([CHILD_OF, FOLLOWS_FROM]).freeze end class Tag include ::Thrift::Struct, ::Thrift::Struct_Union KEY = 1 VTYPE = 2 VSTR = 3 VDOUBLE = 4 VBOOL = 5 VLONG = 6 VBINARY = 7 FIELDS = { KEY => {:type => ::Thrift::Types::STRING, :name => 'key'}, VTYPE => {:type => ::Thrift::Types::I32, :name => 'vType', :enum_class => ::Jaeger::Thrift::TagType}, VSTR => {:type => ::Thrift::Types::STRING, :name => 'vStr', :optional => true}, VDOUBLE => {:type => ::Thrift::Types::DOUBLE, :name => 'vDouble', :optional => true}, VBOOL => {:type => ::Thrift::Types::BOOL, :name => 'vBool', :optional => true}, VLONG => {:type => ::Thrift::Types::I64, :name => 'vLong', :optional => true}, VBINARY => {:type => ::Thrift::Types::STRING, :name => 'vBinary', :binary => true, :optional => true} } def struct_fields; FIELDS; end def validate raise ::Thrift::ProtocolException.new(::Thrift::ProtocolException::UNKNOWN, 'Required field key is unset!') unless @key raise ::Thrift::ProtocolException.new(::Thrift::ProtocolException::UNKNOWN, 'Required field vType is unset!') unless @vType unless @vType.nil? || ::Jaeger::Thrift::TagType::VALID_VALUES.include?(@vType) raise ::Thrift::ProtocolException.new(::Thrift::ProtocolException::UNKNOWN, 'Invalid value of field vType!') end end ::Thrift::Struct.generate_accessors self end class Log include ::Thrift::Struct, ::Thrift::Struct_Union TIMESTAMP = 1 LOG_FIELDS = 2 FIELDS = { TIMESTAMP => {:type => ::Thrift::Types::I64, :name => 'timestamp'}, LOG_FIELDS => {:type => ::Thrift::Types::LIST, :name => 'fields', :element => {:type => ::Thrift::Types::STRUCT, :class => ::Jaeger::Thrift::Tag}} } def struct_fields; FIELDS; end def validate raise ::Thrift::ProtocolException.new(::Thrift::ProtocolException::UNKNOWN, 'Required field timestamp is unset!') unless @timestamp raise ::Thrift::ProtocolException.new(::Thrift::ProtocolException::UNKNOWN, 'Required field fields is unset!') unless @fields end ::Thrift::Struct.generate_accessors self end class SpanRef include ::Thrift::Struct, ::Thrift::Struct_Union REFTYPE = 1 TRACEIDLOW = 2 TRACEIDHIGH = 3 SPANID = 4 FIELDS = { REFTYPE => {:type => ::Thrift::Types::I32, :name => 'refType', :enum_class => ::Jaeger::Thrift::SpanRefType}, TRACEIDLOW => {:type => ::Thrift::Types::I64, :name => 'traceIdLow'}, TRACEIDHIGH => {:type => ::Thrift::Types::I64, :name => 'traceIdHigh'}, SPANID => {:type => ::Thrift::Types::I64, :name => 'spanId'} } def struct_fields; FIELDS; end def validate raise ::Thrift::ProtocolException.new(::Thrift::ProtocolException::UNKNOWN, 'Required field refType is unset!') unless @refType raise ::Thrift::ProtocolException.new(::Thrift::ProtocolException::UNKNOWN, 'Required field traceIdLow is unset!') unless @traceIdLow raise ::Thrift::ProtocolException.new(::Thrift::ProtocolException::UNKNOWN, 'Required field traceIdHigh is unset!') unless @traceIdHigh raise ::Thrift::ProtocolException.new(::Thrift::ProtocolException::UNKNOWN, 'Required field spanId is unset!') unless @spanId unless @refType.nil? || ::Jaeger::Thrift::SpanRefType::VALID_VALUES.include?(@refType) raise ::Thrift::ProtocolException.new(::Thrift::ProtocolException::UNKNOWN, 'Invalid value of field refType!') end end ::Thrift::Struct.generate_accessors self end class Span include ::Thrift::Struct, ::Thrift::Struct_Union TRACEIDLOW = 1 TRACEIDHIGH = 2 SPANID = 3 PARENTSPANID = 4 OPERATIONNAME = 5 REFERENCES = 6 FLAGS = 7 STARTTIME = 8 DURATION = 9 TAGS = 10 LOGS = 11 FIELDS = { TRACEIDLOW => {:type => ::Thrift::Types::I64, :name => 'traceIdLow'}, TRACEIDHIGH => {:type => ::Thrift::Types::I64, :name => 'traceIdHigh'}, SPANID => {:type => ::Thrift::Types::I64, :name => 'spanId'}, PARENTSPANID => {:type => ::Thrift::Types::I64, :name => 'parentSpanId'}, OPERATIONNAME => {:type => ::Thrift::Types::STRING, :name => 'operationName'}, REFERENCES => {:type => ::Thrift::Types::LIST, :name => 'references', :element => {:type => ::Thrift::Types::STRUCT, :class => ::Jaeger::Thrift::SpanRef}, :optional => true}, FLAGS => {:type => ::Thrift::Types::I32, :name => 'flags'}, STARTTIME => {:type => ::Thrift::Types::I64, :name => 'startTime'}, DURATION => {:type => ::Thrift::Types::I64, :name => 'duration'}, TAGS => {:type => ::Thrift::Types::LIST, :name => 'tags', :element => {:type => ::Thrift::Types::STRUCT, :class => ::Jaeger::Thrift::Tag}, :optional => true}, LOGS => {:type => ::Thrift::Types::LIST, :name => 'logs', :element => {:type => ::Thrift::Types::STRUCT, :class => ::Jaeger::Thrift::Log}, :optional => true} } def struct_fields; FIELDS; end def validate raise ::Thrift::ProtocolException.new(::Thrift::ProtocolException::UNKNOWN, 'Required field traceIdLow is unset!') unless @traceIdLow raise ::Thrift::ProtocolException.new(::Thrift::ProtocolException::UNKNOWN, 'Required field traceIdHigh is unset!') unless @traceIdHigh raise ::Thrift::ProtocolException.new(::Thrift::ProtocolException::UNKNOWN, 'Required field spanId is unset!') unless @spanId raise ::Thrift::ProtocolException.new(::Thrift::ProtocolException::UNKNOWN, 'Required field parentSpanId is unset!') unless @parentSpanId raise ::Thrift::ProtocolException.new(::Thrift::ProtocolException::UNKNOWN, 'Required field operationName is unset!') unless @operationName raise ::Thrift::ProtocolException.new(::Thrift::ProtocolException::UNKNOWN, 'Required field flags is unset!') unless @flags raise ::Thrift::ProtocolException.new(::Thrift::ProtocolException::UNKNOWN, 'Required field startTime is unset!') unless @startTime raise ::Thrift::ProtocolException.new(::Thrift::ProtocolException::UNKNOWN, 'Required field duration is unset!') unless @duration end ::Thrift::Struct.generate_accessors self end class Process include ::Thrift::Struct, ::Thrift::Struct_Union SERVICENAME = 1 TAGS = 2 FIELDS = { SERVICENAME => {:type => ::Thrift::Types::STRING, :name => 'serviceName'}, TAGS => {:type => ::Thrift::Types::LIST, :name => 'tags', :element => {:type => ::Thrift::Types::STRUCT, :class => ::Jaeger::Thrift::Tag}, :optional => true} } def struct_fields; FIELDS; end def validate raise ::Thrift::ProtocolException.new(::Thrift::ProtocolException::UNKNOWN, 'Required field serviceName is unset!') unless @serviceName end ::Thrift::Struct.generate_accessors self end class Batch include ::Thrift::Struct, ::Thrift::Struct_Union PROCESS = 1 SPANS = 2 FIELDS = { PROCESS => {:type => ::Thrift::Types::STRUCT, :name => 'process', :class => ::Jaeger::Thrift::Process}, SPANS => {:type => ::Thrift::Types::LIST, :name => 'spans', :element => {:type => ::Thrift::Types::STRUCT, :class => ::Jaeger::Thrift::Span}} } def struct_fields; FIELDS; end def validate raise ::Thrift::ProtocolException.new(::Thrift::ProtocolException::UNKNOWN, 'Required field process is unset!') unless @process raise ::Thrift::ProtocolException.new(::Thrift::ProtocolException::UNKNOWN, 'Required field spans is unset!') unless @spans end ::Thrift::Struct.generate_accessors self end class BatchSubmitResponse include ::Thrift::Struct, ::Thrift::Struct_Union OK = 1 FIELDS = { OK => {:type => ::Thrift::Types::BOOL, :name => 'ok'} } def struct_fields; FIELDS; end def validate raise ::Thrift::ProtocolException.new(::Thrift::ProtocolException::UNKNOWN, 'Required field ok is unset!') if @ok.nil? end ::Thrift::Struct.generate_accessors self end end end jaeger-client-ruby-1.2.0/thrift/gen-rb/jaeger/thrift/zipkin/000077500000000000000000000000001414770277100237275ustar00rootroot00000000000000jaeger-client-ruby-1.2.0/thrift/gen-rb/jaeger/thrift/zipkin/zipkin_collector.rb000066400000000000000000000044421414770277100276320ustar00rootroot00000000000000# # Autogenerated by Thrift Compiler (0.10.0) # # DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING # require 'thrift' require 'jaeger/thrift/zipkin/zipkincore_types' module Jaeger module Thrift module Zipkin module ZipkinCollector class Client include ::Thrift::Client def submitZipkinBatch(spans) send_submitZipkinBatch(spans) return recv_submitZipkinBatch() end def send_submitZipkinBatch(spans) send_message('submitZipkinBatch', SubmitZipkinBatch_args, :spans => spans) end def recv_submitZipkinBatch() result = receive_message(SubmitZipkinBatch_result) return result.success unless result.success.nil? raise ::Thrift::ApplicationException.new(::Thrift::ApplicationException::MISSING_RESULT, 'submitZipkinBatch failed: unknown result') end end class Processor include ::Thrift::Processor def process_submitZipkinBatch(seqid, iprot, oprot) args = read_args(iprot, SubmitZipkinBatch_args) result = SubmitZipkinBatch_result.new() result.success = @handler.submitZipkinBatch(args.spans) write_result(result, oprot, 'submitZipkinBatch', seqid) end end # HELPER FUNCTIONS AND STRUCTURES class SubmitZipkinBatch_args include ::Thrift::Struct, ::Thrift::Struct_Union SPANS = 1 FIELDS = { SPANS => {:type => ::Thrift::Types::LIST, :name => 'spans', :element => {:type => ::Thrift::Types::STRUCT, :class => ::Jaeger::Thrift::Zipkin::Span}} } def struct_fields; FIELDS; end def validate end ::Thrift::Struct.generate_accessors self end class SubmitZipkinBatch_result include ::Thrift::Struct, ::Thrift::Struct_Union SUCCESS = 0 FIELDS = { SUCCESS => {:type => ::Thrift::Types::LIST, :name => 'success', :element => {:type => ::Thrift::Types::STRUCT, :class => ::Jaeger::Thrift::Zipkin::Response}} } def struct_fields; FIELDS; end def validate end ::Thrift::Struct.generate_accessors self end end end end end jaeger-client-ruby-1.2.0/thrift/gen-rb/jaeger/thrift/zipkin/zipkincore_constants.rb000066400000000000000000000012161414770277100305250ustar00rootroot00000000000000# # Autogenerated by Thrift Compiler (0.10.0) # # DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING # require 'thrift' require 'jaeger/thrift/zipkin/zipkincore_types' module Jaeger module Thrift module Zipkin CLIENT_SEND = %q"cs" CLIENT_RECV = %q"cr" SERVER_SEND = %q"ss" SERVER_RECV = %q"sr" WIRE_SEND = %q"ws" WIRE_RECV = %q"wr" CLIENT_SEND_FRAGMENT = %q"csf" CLIENT_RECV_FRAGMENT = %q"crf" SERVER_SEND_FRAGMENT = %q"ssf" SERVER_RECV_FRAGMENT = %q"srf" LOCAL_COMPONENT = %q"lc" CLIENT_ADDR = %q"ca" SERVER_ADDR = %q"sa" end end end jaeger-client-ruby-1.2.0/thrift/gen-rb/jaeger/thrift/zipkin/zipkincore_types.rb000066400000000000000000000211171414770277100276570ustar00rootroot00000000000000# # Autogenerated by Thrift Compiler (0.10.0) # # DO NOT EDIT UNLESS YOU ARE SURE THAT YOU KNOW WHAT YOU ARE DOING # require 'thrift' module Jaeger module Thrift module Zipkin module AnnotationType BOOL = 0 BYTES = 1 I16 = 2 I32 = 3 I64 = 4 DOUBLE = 5 STRING = 6 VALUE_MAP = {0 => "BOOL", 1 => "BYTES", 2 => "I16", 3 => "I32", 4 => "I64", 5 => "DOUBLE", 6 => "STRING"} VALID_VALUES = Set.new([BOOL, BYTES, I16, I32, I64, DOUBLE, STRING]).freeze end # Indicates the network context of a service recording an annotation with two # exceptions. # # When a BinaryAnnotation, and key is CLIENT_ADDR or SERVER_ADDR, # the endpoint indicates the source or destination of an RPC. This exception # allows zipkin to display network context of uninstrumented services, or # clients such as web browsers. class Endpoint include ::Thrift::Struct, ::Thrift::Struct_Union IPV4 = 1 PORT = 2 SERVICE_NAME = 3 FIELDS = { # IPv4 host address packed into 4 bytes. # # Ex for the ip 1.2.3.4, it would be (1 << 24) | (2 << 16) | (3 << 8) | 4 IPV4 => {:type => ::Thrift::Types::I32, :name => 'ipv4'}, # IPv4 port # # Note: this is to be treated as an unsigned integer, so watch for negatives. # # Conventionally, when the port isn't known, port = 0. PORT => {:type => ::Thrift::Types::I16, :name => 'port'}, # Service name in lowercase, such as "memcache" or "zipkin-web" # # Conventionally, when the service name isn't known, service_name = "unknown". SERVICE_NAME => {:type => ::Thrift::Types::STRING, :name => 'service_name'} } def struct_fields; FIELDS; end def validate end ::Thrift::Struct.generate_accessors self end # An annotation is similar to a log statement. It includes a host field which # allows these events to be attributed properly, and also aggregatable. class Annotation include ::Thrift::Struct, ::Thrift::Struct_Union TIMESTAMP = 1 VALUE = 2 HOST = 3 FIELDS = { # Microseconds from epoch. # # This value should use the most precise value possible. For example, # gettimeofday or syncing nanoTime against a tick of currentTimeMillis. TIMESTAMP => {:type => ::Thrift::Types::I64, :name => 'timestamp'}, VALUE => {:type => ::Thrift::Types::STRING, :name => 'value'}, # Always the host that recorded the event. By specifying the host you allow # rollup of all events (such as client requests to a service) by IP address. HOST => {:type => ::Thrift::Types::STRUCT, :name => 'host', :class => ::Jaeger::Thrift::Zipkin::Endpoint, :optional => true} } def struct_fields; FIELDS; end def validate end ::Thrift::Struct.generate_accessors self end # Binary annotations are tags applied to a Span to give it context. For # example, a binary annotation of "http.uri" could the path to a resource in a # RPC call. # # Binary annotations of type STRING are always queryable, though more a # historical implementation detail than a structural concern. # # Binary annotations can repeat, and vary on the host. Similar to Annotation, # the host indicates who logged the event. This allows you to tell the # difference between the client and server side of the same key. For example, # the key "http.uri" might be different on the client and server side due to # rewriting, like "/api/v1/myresource" vs "/myresource. Via the host field, # you can see the different points of view, which often help in debugging. class BinaryAnnotation include ::Thrift::Struct, ::Thrift::Struct_Union KEY = 1 VALUE = 2 ANNOTATION_TYPE = 3 HOST = 4 FIELDS = { KEY => {:type => ::Thrift::Types::STRING, :name => 'key'}, VALUE => {:type => ::Thrift::Types::STRING, :name => 'value', :binary => true}, ANNOTATION_TYPE => {:type => ::Thrift::Types::I32, :name => 'annotation_type', :enum_class => ::Jaeger::Thrift::Zipkin::AnnotationType}, # The host that recorded tag, which allows you to differentiate between # multiple tags with the same key. There are two exceptions to this. # # When the key is CLIENT_ADDR or SERVER_ADDR, host indicates the source or # destination of an RPC. This exception allows zipkin to display network # context of uninstrumented services, or clients such as web browsers. HOST => {:type => ::Thrift::Types::STRUCT, :name => 'host', :class => ::Jaeger::Thrift::Zipkin::Endpoint, :optional => true} } def struct_fields; FIELDS; end def validate unless @annotation_type.nil? || ::Jaeger::Thrift::Zipkin::AnnotationType::VALID_VALUES.include?(@annotation_type) raise ::Thrift::ProtocolException.new(::Thrift::ProtocolException::UNKNOWN, 'Invalid value of field annotation_type!') end end ::Thrift::Struct.generate_accessors self end # A trace is a series of spans (often RPC calls) which form a latency tree. # # The root span is where trace_id = id and parent_id = Nil. The root span is # usually the longest interval in the trace, starting with a SERVER_RECV # annotation and ending with a SERVER_SEND. class Span include ::Thrift::Struct, ::Thrift::Struct_Union TRACE_ID = 1 NAME = 3 ID = 4 PARENT_ID = 5 ANNOTATIONS = 6 BINARY_ANNOTATIONS = 8 DEBUG = 9 TIMESTAMP = 10 DURATION = 11 FIELDS = { TRACE_ID => {:type => ::Thrift::Types::I64, :name => 'trace_id'}, # Span name in lowercase, rpc method for example # # Conventionally, when the span name isn't known, name = "unknown". NAME => {:type => ::Thrift::Types::STRING, :name => 'name'}, ID => {:type => ::Thrift::Types::I64, :name => 'id'}, PARENT_ID => {:type => ::Thrift::Types::I64, :name => 'parent_id', :optional => true}, ANNOTATIONS => {:type => ::Thrift::Types::LIST, :name => 'annotations', :element => {:type => ::Thrift::Types::STRUCT, :class => ::Jaeger::Thrift::Zipkin::Annotation}}, BINARY_ANNOTATIONS => {:type => ::Thrift::Types::LIST, :name => 'binary_annotations', :element => {:type => ::Thrift::Types::STRUCT, :class => ::Jaeger::Thrift::Zipkin::BinaryAnnotation}}, DEBUG => {:type => ::Thrift::Types::BOOL, :name => 'debug', :default => false, :optional => true}, # Microseconds from epoch of the creation of this span. # # This value should be set directly by instrumentation, using the most # precise value possible. For example, gettimeofday or syncing nanoTime # against a tick of currentTimeMillis. # # For compatibilty with instrumentation that precede this field, collectors # or span stores can derive this via Annotation.timestamp. # For example, SERVER_RECV.timestamp or CLIENT_SEND.timestamp. # # This field is optional for compatibility with old data: first-party span # stores are expected to support this at time of introduction. TIMESTAMP => {:type => ::Thrift::Types::I64, :name => 'timestamp', :optional => true}, # Measurement of duration in microseconds, used to support queries. # # This value should be set directly, where possible. Doing so encourages # precise measurement decoupled from problems of clocks, such as skew or NTP # updates causing time to move backwards. # # For compatibilty with instrumentation that precede this field, collectors # or span stores can derive this by subtracting Annotation.timestamp. # For example, SERVER_SEND.timestamp - SERVER_RECV.timestamp. # # If this field is persisted as unset, zipkin will continue to work, except # duration query support will be implementation-specific. Similarly, setting # this field non-atomically is implementation-specific. # # This field is i64 vs i32 to support spans longer than 35 minutes. DURATION => {:type => ::Thrift::Types::I64, :name => 'duration', :optional => true} } def struct_fields; FIELDS; end def validate end ::Thrift::Struct.generate_accessors self end class Response include ::Thrift::Struct, ::Thrift::Struct_Union OK = 1 FIELDS = { OK => {:type => ::Thrift::Types::BOOL, :name => 'ok'} } def struct_fields; FIELDS; end def validate raise ::Thrift::ProtocolException.new(::Thrift::ProtocolException::UNKNOWN, 'Required field ok is unset!') if @ok.nil? end ::Thrift::Struct.generate_accessors self end end end end jaeger-client-ruby-1.2.0/thrift/jaeger.thrift000066400000000000000000000065711414770277100212040ustar00rootroot00000000000000# Copyright (c) 2016 Uber Technologies, Inc. # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to deal # in the Software without restriction, including without limitation the rights # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell # copies of the Software, and to permit persons to whom the Software is # furnished to do so, subject to the following conditions: # # The above copyright notice and this permission notice shall be included in # all copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN # THE SOFTWARE. namespace java com.uber.jaeger.thriftjava namespace rb Jaeger.Thrift # TagType denotes the type of a Tag's value. enum TagType { STRING, DOUBLE, BOOL, LONG, BINARY } # Tag is a basic strongly typed key/value pair. It has been flattened to reduce the use of pointers in golang struct Tag { 1: required string key 2: required TagType vType 3: optional string vStr 4: optional double vDouble 5: optional bool vBool 6: optional i64 vLong 7: optional binary vBinary } # Log is a timed even with an arbitrary set of tags. struct Log { 1: required i64 timestamp 2: required list fields } enum SpanRefType { CHILD_OF, FOLLOWS_FROM } # SpanRef describes causal relationship of the current span to another span (e.g. 'child-of') struct SpanRef { 1: required SpanRefType refType 2: required i64 traceIdLow 3: required i64 traceIdHigh 4: required i64 spanId } # Span represents a named unit of work performed by a service. struct Span { 1: required i64 traceIdLow # the least significant 64 bits of a traceID 2: required i64 traceIdHigh # the most significant 64 bits of a traceID; 0 when only 64bit IDs are used 3: required i64 spanId # unique span id (only unique within a given trace) 4: required i64 parentSpanId # since nearly all spans will have parents spans, CHILD_OF refs do not have to be explicit 5: required string operationName 6: optional list references # causal references to other spans 7: required i32 flags # tbd 8: required i64 startTime 9: required i64 duration 10: optional list tags 11: optional list logs } # Process describes the traced process/service that emits spans. struct Process { 1: required string serviceName 2: optional list tags } # Batch is a collection of spans reported out of process. struct Batch { 1: required Process process 2: required list spans } # BatchSubmitResponse is the response on submitting a batch. struct BatchSubmitResponse { 1: required bool ok # The Collector's client is expected to only log (or emit a counter) when not ok equals false } service Collector { list submitBatches(1: list batches) } jaeger-client-ruby-1.2.0/thrift/zipkincore.thrift000066400000000000000000000272241414770277100221220ustar00rootroot00000000000000# Copyright 2012 Twitter Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. namespace java com.twitter.zipkin.thriftjava #@namespace scala com.twitter.zipkin.thriftscala namespace rb Jaeger.Thrift.Zipkin #************** Annotation.value ************** /** * The client sent ("cs") a request to a server. There is only one send per * span. For example, if there's a transport error, each attempt can be logged * as a WIRE_SEND annotation. * * If chunking is involved, each chunk could be logged as a separate * CLIENT_SEND_FRAGMENT in the same span. * * Annotation.host is not the server. It is the host which logged the send * event, almost always the client. When logging CLIENT_SEND, instrumentation * should also log the SERVER_ADDR. */ const string CLIENT_SEND = "cs" /** * The client received ("cr") a response from a server. There is only one * receive per span. For example, if duplicate responses were received, each * can be logged as a WIRE_RECV annotation. * * If chunking is involved, each chunk could be logged as a separate * CLIENT_RECV_FRAGMENT in the same span. * * Annotation.host is not the server. It is the host which logged the receive * event, almost always the client. The actual endpoint of the server is * recorded separately as SERVER_ADDR when CLIENT_SEND is logged. */ const string CLIENT_RECV = "cr" /** * The server sent ("ss") a response to a client. There is only one response * per span. If there's a transport error, each attempt can be logged as a * WIRE_SEND annotation. * * Typically, a trace ends with a server send, so the last timestamp of a trace * is often the timestamp of the root span's server send. * * If chunking is involved, each chunk could be logged as a separate * SERVER_SEND_FRAGMENT in the same span. * * Annotation.host is not the client. It is the host which logged the send * event, almost always the server. The actual endpoint of the client is * recorded separately as CLIENT_ADDR when SERVER_RECV is logged. */ const string SERVER_SEND = "ss" /** * The server received ("sr") a request from a client. There is only one * request per span. For example, if duplicate responses were received, each * can be logged as a WIRE_RECV annotation. * * Typically, a trace starts with a server receive, so the first timestamp of a * trace is often the timestamp of the root span's server receive. * * If chunking is involved, each chunk could be logged as a separate * SERVER_RECV_FRAGMENT in the same span. * * Annotation.host is not the client. It is the host which logged the receive * event, almost always the server. When logging SERVER_RECV, instrumentation * should also log the CLIENT_ADDR. */ const string SERVER_RECV = "sr" /** * Optionally logs an attempt to send a message on the wire. Multiple wire send * events could indicate network retries. A lag between client or server send * and wire send might indicate queuing or processing delay. */ const string WIRE_SEND = "ws" /** * Optionally logs an attempt to receive a message from the wire. Multiple wire * receive events could indicate network retries. A lag between wire receive * and client or server receive might indicate queuing or processing delay. */ const string WIRE_RECV = "wr" /** * Optionally logs progress of a (CLIENT_SEND, WIRE_SEND). For example, this * could be one chunk in a chunked request. */ const string CLIENT_SEND_FRAGMENT = "csf" /** * Optionally logs progress of a (CLIENT_RECV, WIRE_RECV). For example, this * could be one chunk in a chunked response. */ const string CLIENT_RECV_FRAGMENT = "crf" /** * Optionally logs progress of a (SERVER_SEND, WIRE_SEND). For example, this * could be one chunk in a chunked response. */ const string SERVER_SEND_FRAGMENT = "ssf" /** * Optionally logs progress of a (SERVER_RECV, WIRE_RECV). For example, this * could be one chunk in a chunked request. */ const string SERVER_RECV_FRAGMENT = "srf" #***** BinaryAnnotation.key ****** /** * The value of "lc" is the component or namespace of a local span. * * BinaryAnnotation.host adds service context needed to support queries. * * Local Component("lc") supports three key features: flagging, query by * service and filtering Span.name by namespace. * * While structurally the same, local spans are fundamentally different than * RPC spans in how they should be interpreted. For example, zipkin v1 tools * center on RPC latency and service graphs. Root local-spans are neither * indicative of critical path RPC latency, nor have impact on the shape of a * service graph. By flagging with "lc", tools can special-case local spans. * * Zipkin v1 Spans are unqueryable unless they can be indexed by service name. * The only path to a service name is by (Binary)?Annotation.host.serviceName. * By logging "lc", a local span can be queried even if no other annotations * are logged. * * The value of "lc" is the namespace of Span.name. For example, it might be * "finatra2", for a span named "bootstrap". "lc" allows you to resolves * conflicts for the same Span.name, for example "finatra/bootstrap" vs * "finch/bootstrap". Using local component, you'd search for spans named * "bootstrap" where "lc=finch" */ const string LOCAL_COMPONENT = "lc" #***** BinaryAnnotation.key where value = [1] and annotation_type = BOOL ****** /** * Indicates a client address ("ca") in a span. Most likely, there's only one. * Multiple addresses are possible when a client changes its ip or port within * a span. */ const string CLIENT_ADDR = "ca" /** * Indicates a server address ("sa") in a span. Most likely, there's only one. * Multiple addresses are possible when a client is redirected, or fails to a * different server ip or port. */ const string SERVER_ADDR = "sa" /** * Indicates the network context of a service recording an annotation with two * exceptions. * * When a BinaryAnnotation, and key is CLIENT_ADDR or SERVER_ADDR, * the endpoint indicates the source or destination of an RPC. This exception * allows zipkin to display network context of uninstrumented services, or * clients such as web browsers. */ struct Endpoint { /** * IPv4 host address packed into 4 bytes. * * Ex for the ip 1.2.3.4, it would be (1 << 24) | (2 << 16) | (3 << 8) | 4 */ 1: i32 ipv4 /** * IPv4 port * * Note: this is to be treated as an unsigned integer, so watch for negatives. * * Conventionally, when the port isn't known, port = 0. */ 2: i16 port /** * Service name in lowercase, such as "memcache" or "zipkin-web" * * Conventionally, when the service name isn't known, service_name = "unknown". */ 3: string service_name } /** * An annotation is similar to a log statement. It includes a host field which * allows these events to be attributed properly, and also aggregatable. */ struct Annotation { /** * Microseconds from epoch. * * This value should use the most precise value possible. For example, * gettimeofday or syncing nanoTime against a tick of currentTimeMillis. */ 1: i64 timestamp 2: string value // what happened at the timestamp? /** * Always the host that recorded the event. By specifying the host you allow * rollup of all events (such as client requests to a service) by IP address. */ 3: optional Endpoint host // don't reuse 4: optional i32 OBSOLETE_duration // how long did the operation take? microseconds } enum AnnotationType { BOOL, BYTES, I16, I32, I64, DOUBLE, STRING } /** * Binary annotations are tags applied to a Span to give it context. For * example, a binary annotation of "http.uri" could the path to a resource in a * RPC call. * * Binary annotations of type STRING are always queryable, though more a * historical implementation detail than a structural concern. * * Binary annotations can repeat, and vary on the host. Similar to Annotation, * the host indicates who logged the event. This allows you to tell the * difference between the client and server side of the same key. For example, * the key "http.uri" might be different on the client and server side due to * rewriting, like "/api/v1/myresource" vs "/myresource. Via the host field, * you can see the different points of view, which often help in debugging. */ struct BinaryAnnotation { 1: string key, 2: binary value, 3: AnnotationType annotation_type, /** * The host that recorded tag, which allows you to differentiate between * multiple tags with the same key. There are two exceptions to this. * * When the key is CLIENT_ADDR or SERVER_ADDR, host indicates the source or * destination of an RPC. This exception allows zipkin to display network * context of uninstrumented services, or clients such as web browsers. */ 4: optional Endpoint host } /** * A trace is a series of spans (often RPC calls) which form a latency tree. * * The root span is where trace_id = id and parent_id = Nil. The root span is * usually the longest interval in the trace, starting with a SERVER_RECV * annotation and ending with a SERVER_SEND. */ struct Span { 1: i64 trace_id # unique trace id, use for all spans in trace /** * Span name in lowercase, rpc method for example * * Conventionally, when the span name isn't known, name = "unknown". */ 3: string name, 4: i64 id, # unique span id, only used for this span 5: optional i64 parent_id, # parent span id 6: list annotations, # all annotations/events that occured, sorted by timestamp 8: list binary_annotations # any binary annotations 9: optional bool debug = 0 # if true, we DEMAND that this span passes all samplers /** * Microseconds from epoch of the creation of this span. * * This value should be set directly by instrumentation, using the most * precise value possible. For example, gettimeofday or syncing nanoTime * against a tick of currentTimeMillis. * * For compatibilty with instrumentation that precede this field, collectors * or span stores can derive this via Annotation.timestamp. * For example, SERVER_RECV.timestamp or CLIENT_SEND.timestamp. * * This field is optional for compatibility with old data: first-party span * stores are expected to support this at time of introduction. */ 10: optional i64 timestamp, /** * Measurement of duration in microseconds, used to support queries. * * This value should be set directly, where possible. Doing so encourages * precise measurement decoupled from problems of clocks, such as skew or NTP * updates causing time to move backwards. * * For compatibilty with instrumentation that precede this field, collectors * or span stores can derive this by subtracting Annotation.timestamp. * For example, SERVER_SEND.timestamp - SERVER_RECV.timestamp. * * If this field is persisted as unset, zipkin will continue to work, except * duration query support will be implementation-specific. Similarly, setting * this field non-atomically is implementation-specific. * * This field is i64 vs i32 to support spans longer than 35 minutes. */ 11: optional i64 duration } # define TChannel service struct Response { 1: required bool ok } service ZipkinCollector { list submitZipkinBatch(1: list spans) }