jaeger-client 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 7ac7f048b966685822762e51884cf5122cc437d6
4
+ data.tar.gz: 7986fccb2c4c683e74777803639e4da546227dac
5
+ SHA512:
6
+ metadata.gz: 7978af583df71b1c50b76b60e9208d558f12f7a1f21876e9f55982a7d9e6f7ef14757457fb8a41a1a59a4865e7583e3f4b54c7b442e17135b1fc902dfd9e5206
7
+ data.tar.gz: b75781a93c93856c017d06b78e9c7119416752ab66a38b992b0de14e224823e74c7baff484bc8553d67c8722500dc70b98e10943e91088238d5c8c5b6ac70804
@@ -0,0 +1,12 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
10
+
11
+ # rspec failure tracking
12
+ .rspec_status
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --format documentation
2
+ --color
@@ -0,0 +1,5 @@
1
+ sudo: false
2
+ language: ruby
3
+ rvm:
4
+ - 2.4.0
5
+ before_install: gem install bundler -v 1.14.4
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in jaeger-client.gemspec
4
+ gemspec
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2017 Indrek Juhkam
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
@@ -0,0 +1,37 @@
1
+ # Jaeger::Client
2
+
3
+ OpenTracing Tracer implementation for Jaeger in Ruby
4
+
5
+ ## Installation
6
+
7
+ Add this line to your application's Gemfile:
8
+
9
+ ```ruby
10
+ gem 'jaeger-client'
11
+ ```
12
+
13
+ ## Usage
14
+
15
+ ```ruby
16
+ require 'jaeger/client'
17
+ OpenTracing.global_tracer = Jaeger::Client.build(host: 'localhost', port: 6831, service_name: 'echo')
18
+
19
+ span = OpenTracing.start_span('span name')
20
+ span.finish
21
+ ```
22
+
23
+ ## Development
24
+
25
+ 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.
26
+
27
+ 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).
28
+
29
+ ## Contributing
30
+
31
+ Bug reports and pull requests are welcome on GitHub at https://github.com/salemove/jaeger-client-ruby
32
+
33
+
34
+ ## License
35
+
36
+ The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
37
+
@@ -0,0 +1,6 @@
1
+ require 'bundler/gem_tasks'
2
+ require 'rspec/core/rake_task'
3
+
4
+ RSpec::Core::RakeTask.new(:spec)
5
+
6
+ task :default => :spec
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler/setup"
4
+ require "jaeger/client"
5
+
6
+ # You can add fixtures and/or initialization code here to make experimenting
7
+ # with your gem easier. You can also use a different console, if you like.
8
+
9
+ # (If you use this, don't forget to add pry to your Gemfile!)
10
+ # require "pry"
11
+ # Pry.start
12
+
13
+ require "irb"
14
+ IRB.start(__FILE__)
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
@@ -0,0 +1,28 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+
5
+ Gem::Specification.new do |spec|
6
+ spec.name = 'jaeger-client'
7
+ spec.version = '0.1.0'
8
+ spec.authors = ['SaleMove TechMovers']
9
+ spec.email = ['techmovers@salemove.com']
10
+
11
+ spec.summary = %q{OpenTracing Tracer implementation for Jaeger in Ruby}
12
+ spec.description = %q{}
13
+ spec.homepage = ''
14
+ spec.license = 'MIT'
15
+
16
+ spec.files = `git ls-files -z`.split("\x0").reject do |f|
17
+ f.match(%r{^(test|spec|features)/})
18
+ end
19
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
20
+ spec.require_paths = ['lib']
21
+
22
+ spec.add_development_dependency 'bundler', '~> 1.14'
23
+ spec.add_development_dependency 'rake', '~> 10.0'
24
+ spec.add_development_dependency 'rspec', '~> 3.0'
25
+
26
+ spec.add_dependency 'opentracing', '~> 0.3'
27
+ spec.add_dependency 'thrift'
28
+ end
@@ -0,0 +1,19 @@
1
+ $LOAD_PATH.push(File.dirname(__FILE__) + '/../../thrift/gen-rb')
2
+
3
+ require 'opentracing'
4
+
5
+ require_relative 'client/tracer'
6
+ require_relative 'client/span'
7
+ require_relative 'client/span_context'
8
+ require_relative 'client/carrier'
9
+ require_relative 'client/trace_id'
10
+ require_relative 'client/udp_sender'
11
+
12
+ module Jaeger
13
+ module Client
14
+ def self.build(host: '127.0.0.1', port: 6831, service_name:)
15
+ client = UdpSender.new(service_name, host, port)
16
+ Tracer.new(client, service_name)
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,27 @@
1
+ module Jaeger
2
+ module Client
3
+ # Carriers are used for inject and extract operations. A carrier should be a
4
+ # Hash or hash-like object. At a minimum, it should implement `[]`, `[]=`, and
5
+ # `each` shown here.
6
+ class Carrier
7
+ # [] retrieves a value by the given key
8
+ # @param key [String] key to retrieve the value
9
+ # @return [String] the desired value
10
+ def [](key)
11
+ end
12
+
13
+ # []= sets the value for the given key
14
+ # @param key [String] key to set
15
+ # @param value [String] value to set
16
+ def []=(key, value)
17
+ end
18
+
19
+ # each iterates over every key-value pair in the carrier
20
+ # @yield [key, value]
21
+ # @yieldparam key [String] the key of the tuple
22
+ # @yieldparam value [String] the value of the tuple
23
+ def each(&block)
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,74 @@
1
+ module Jaeger
2
+ module Client
3
+ class Span
4
+ attr_accessor :operation_name
5
+
6
+ attr_reader :context, :start_time, :tags, :logs
7
+
8
+ # Creates a new {Span}
9
+ #
10
+ # @param context [SpanContext] the context of the span
11
+ # @param context [String] the operation name
12
+ # @param client [UdpSender] span emitter
13
+ #
14
+ # @return [Span] a new Span
15
+ def initialize(context, operation_name, client, start_time: Time.now, tags: {})
16
+ @context = context
17
+ @operation_name = operation_name
18
+ @client = client
19
+ @start_time = start_time
20
+ @tags = tags
21
+ @logs = []
22
+ end
23
+
24
+ # Set a tag value on this span
25
+ #
26
+ # @param key [String] the key of the tag
27
+ # @param value [String, Numeric, Boolean] the value of the tag. If it's not
28
+ # a String, Numeric, or Boolean it will be encoded with to_s
29
+ def set_tag(key, value)
30
+ @tags = @tags.merge(key => value)
31
+ end
32
+
33
+ # Set a baggage item on the span
34
+ #
35
+ # @param key [String] the key of the baggage item
36
+ # @param value [String] the value of the baggage item
37
+ def set_baggage_item(key, value)
38
+ self
39
+ end
40
+
41
+ # Get a baggage item
42
+ #
43
+ # @param key [String] the key of the baggage item
44
+ #
45
+ # @return Value of the baggage item
46
+ def get_baggage_item(key)
47
+ nil
48
+ end
49
+
50
+ # Add a log entry to this span
51
+ #
52
+ # @param timestamp [Time] time of the log
53
+ # @param fields [Hash] Additional information to log
54
+ def log(timestamp: Time.now, **fields)
55
+ @logs << {timestamp: timestamp, fields: fields}
56
+ end
57
+
58
+ # Finish the {Span}
59
+ #
60
+ # @param end_time [Time] custom end time, if not now
61
+ def finish(end_time: Time.now)
62
+ @client.send_span(self, end_time)
63
+ end
64
+
65
+ private
66
+
67
+ def build_binary_annotations
68
+ @tags.map do |name, value|
69
+ {key: name, value: value.to_s}
70
+ end
71
+ end
72
+ end
73
+ end
74
+ end
@@ -0,0 +1,29 @@
1
+ module Jaeger
2
+ module Client
3
+ # SpanContext holds the data for a span that gets inherited to child spans
4
+ class SpanContext
5
+ def self.create_parent_context
6
+ trace_id = TraceId.generate
7
+ span_id = TraceId.generate
8
+ new(trace_id: trace_id, span_id: span_id)
9
+ end
10
+
11
+ def self.create_from_parent_context(span_context)
12
+ trace_id = span_context.trace_id
13
+ parent_id = span_context.span_id
14
+ span_id = TraceId.generate
15
+ new(span_id: span_id, parent_id: parent_id, trace_id: trace_id)
16
+ end
17
+
18
+ attr_reader :span_id, :parent_id, :trace_id, :baggage, :flags
19
+
20
+ def initialize(span_id:, parent_id: nil, trace_id:, baggage: {})
21
+ @span_id = span_id
22
+ @parent_id = parent_id
23
+ @trace_id = trace_id
24
+ @baggage = baggage
25
+ @flags = 0
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,11 @@
1
+ module Jaeger
2
+ module Client
3
+ module TraceId
4
+ TRACE_ID_UPPER_BOUND = 2 ** 63 - 1
5
+
6
+ def self.generate
7
+ rand(TRACE_ID_UPPER_BOUND)
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,85 @@
1
+ module Jaeger
2
+ module Client
3
+ class Tracer
4
+ def initialize(client, service_name)
5
+ @client = client
6
+ end
7
+
8
+ # Starts a new span.
9
+ #
10
+ # @param operation_name [String] The operation name for the Span
11
+ # @param child_of [SpanContext, Span] SpanContext that acts as a parent to
12
+ # the newly-started Span. If a Span instance is provided, its
13
+ # context is automatically substituted.
14
+ # @param start_time [Time] When the Span started, if not now
15
+ # @param tags [Hash] Tags to assign to the Span at start time
16
+ #
17
+ # @return [Span] The newly-started Span
18
+ def start_span(operation_name, child_of: nil, start_time: Time.now, tags: {}, **)
19
+ context =
20
+ if child_of
21
+ parent_context = child_of.respond_to?(:context) ? child_of.context : child_of
22
+ SpanContext.create_from_parent_context(parent_context)
23
+ else
24
+ SpanContext.create_parent_context
25
+ end
26
+ Span.new(context, operation_name, @client, start_time: start_time, tags: tags)
27
+ end
28
+
29
+ # Inject a SpanContext into the given carrier
30
+ #
31
+ # @param span_context [SpanContext]
32
+ # @param format [OpenTracing::FORMAT_TEXT_MAP, OpenTracing::FORMAT_BINARY, OpenTracing::FORMAT_RACK]
33
+ # @param carrier [Carrier] A carrier object of the type dictated by the specified `format`
34
+ def inject(span_context, format, carrier)
35
+ case format
36
+ when OpenTracing::FORMAT_TEXT_MAP, OpenTracing::FORMAT_RACK
37
+ carrier['uber-trace-id'] = [
38
+ span_context.trace_id,
39
+ span_context.span_id,
40
+ span_context.parent_id || 0,
41
+ span_context.flags
42
+ ].join(':')
43
+ else
44
+ warn "Jaeger::Client with format #{format} is not supported yet"
45
+ end
46
+ end
47
+
48
+ # Extract a SpanContext in the given format from the given carrier.
49
+ #
50
+ # @param format [OpenTracing::FORMAT_TEXT_MAP, OpenTracing::FORMAT_BINARY, OpenTracing::FORMAT_RACK]
51
+ # @param carrier [Carrier] A carrier object of the type dictated by the specified `format`
52
+ # @return [SpanContext] the extracted SpanContext or nil if none could be found
53
+ def extract(format, carrier)
54
+ case format
55
+ when OpenTracing::FORMAT_TEXT_MAP
56
+ parse_context(carrier['uber-trace-id'])
57
+ when OpenTracing::FORMAT_RACK
58
+ parse_context(carrier['HTTP_UBER_TRACE_ID'])
59
+ else
60
+ warn "Jaeger::Client with format #{format} is not supported yet"
61
+ nil
62
+ end
63
+ end
64
+
65
+ private
66
+
67
+ def parse_context(trace)
68
+ return nil if !trace || trace == ''
69
+ trace_id, span_id, parent_id, _flags = trace.split(':')
70
+
71
+ parent_id = parent_id.to_i
72
+ parent_id = nil if parent_id == 0
73
+
74
+ trace_id = trace_id.to_i
75
+ span_id = span_id.to_i
76
+
77
+ if trace_id != 0 && span_id != 0
78
+ SpanContext.new(trace_id: trace_id, parent_id: parent_id, span_id: span_id)
79
+ else
80
+ nil
81
+ end
82
+ end
83
+ end
84
+ end
85
+ end
@@ -0,0 +1,130 @@
1
+ require 'jaeger/thrift/agent'
2
+ require 'thread'
3
+
4
+ module Jaeger
5
+ module Client
6
+ class UdpSender
7
+ def initialize(service_name, host, port)
8
+ @service_name = service_name
9
+
10
+ emitter = SocketEmitter.new(host, port)
11
+ emitter.start
12
+ transport = Transport.new(emitter)
13
+ protocol = ::Thrift::CompactProtocol.new(transport)
14
+
15
+ @client = Jaeger::Thrift::Agent::Client.new(protocol)
16
+ end
17
+
18
+ def send_span(span, end_time)
19
+ context = span.context
20
+ start_ts, duration = build_timestamps(span, end_time)
21
+
22
+ thrift_span = Jaeger::Thrift::Span.new(
23
+ 'traceIdLow' => context.trace_id,
24
+ 'traceIdHigh' => context.trace_id,
25
+ 'spanId' => context.span_id,
26
+ 'parentSpanId' => context.parent_id || 0,
27
+ 'operationName' => span.operation_name,
28
+ 'references' => [],
29
+ 'flags' => context.flags,
30
+ 'startTime' => start_ts,
31
+ 'duration' => duration,
32
+ 'tags' => build_tags(span.tags),
33
+ 'logs' => build_logs(span.logs)
34
+ )
35
+ batch = Jaeger::Thrift::Batch.new(
36
+ 'process' => Jaeger::Thrift::Process.new(
37
+ 'serviceName' => @service_name,
38
+ 'tags' => [],
39
+ ),
40
+ 'spans' => [thrift_span]
41
+ )
42
+
43
+ @client.emitBatch(batch)
44
+ end
45
+
46
+ private
47
+
48
+ def build_tags(tags)
49
+ tags.map {|name, value| build_tag(name, value)}
50
+ end
51
+
52
+ def build_logs(logs)
53
+ logs.map do |timestamp:, fields:|
54
+ Jaeger::Thrift::Log.new(
55
+ 'timestamp' => (timestamp.to_f * 1_000_000).to_i,
56
+ 'fields' => fields.map {|name, value| build_tag(name, value)}
57
+ )
58
+ end
59
+ end
60
+
61
+ def build_tag(name, value)
62
+ Jaeger::Thrift::Tag.new(
63
+ 'key' => name.to_s,
64
+ 'vType' => Jaeger::Thrift::TagType::STRING,
65
+ 'vStr' => value.to_s
66
+ )
67
+ end
68
+
69
+ def build_timestamps(span, end_time)
70
+ start_ts = (span.start_time.to_f * 1_000_000).to_i
71
+ end_ts = (end_time.to_f * 1_000_000).to_i
72
+ duration = end_ts - start_ts
73
+ [start_ts, duration]
74
+ end
75
+
76
+ class Transport
77
+ def initialize(emitter)
78
+ @emitter = emitter
79
+ @buffer = ::Thrift::MemoryBufferTransport.new
80
+ end
81
+
82
+ def write(str)
83
+ @buffer.write(str)
84
+ end
85
+
86
+ def flush
87
+ @emitter.emit(@buffer.read(@buffer.available))
88
+ @buffer.reset_buffer
89
+ end
90
+
91
+ def open; end
92
+ def close; end
93
+ end
94
+
95
+ class SocketEmitter
96
+ FLAGS = 0
97
+
98
+ def initialize(host, port)
99
+ @socket = UDPSocket.new
100
+ @socket.connect(host, port)
101
+ @encoded_spans = Queue.new
102
+ end
103
+
104
+ def emit(encoded_spans)
105
+ @encoded_spans << encoded_spans
106
+ end
107
+
108
+ def start
109
+ # Sending spans in a separate thread to avoid blocking the main thread.
110
+ Thread.new do
111
+ while encoded_span = @encoded_spans.pop
112
+ send_bytes(encoded_span)
113
+ end
114
+ end
115
+ end
116
+
117
+ private
118
+
119
+ def send_bytes(bytes)
120
+ @socket.send(bytes, FLAGS)
121
+ @socket.flush
122
+ rescue Errno::ECONNREFUSED
123
+ warn 'Unable to connect to Jaeger Agent'
124
+ rescue => e
125
+ warn "Unable to send spans: #{e.message}"
126
+ end
127
+ end
128
+ end
129
+ end
130
+ end