opentracing-instrumentation 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (46) hide show
  1. checksums.yaml +7 -0
  2. data/.BUNDLER_VERSION +1 -0
  3. data/.drone.jsonnet +35 -0
  4. data/.gitignore +1 -0
  5. data/.gitlab-ci.yml +80 -0
  6. data/.rubocop.yml +36 -0
  7. data/.ruby-version +1 -0
  8. data/GEM_VERSION +1 -0
  9. data/Gemfile +19 -0
  10. data/Gemfile.lock +101 -0
  11. data/LICENSE.txt +21 -0
  12. data/README.md +39 -0
  13. data/Rakefile +11 -0
  14. data/bin/console +16 -0
  15. data/bin/setup +8 -0
  16. data/lib/opentracing/instrumentation.rb +23 -0
  17. data/lib/opentracing/instrumentation/common.rb +13 -0
  18. data/lib/opentracing/instrumentation/common/error_writer.rb +56 -0
  19. data/lib/opentracing/instrumentation/faraday.rb +10 -0
  20. data/lib/opentracing/instrumentation/faraday/response_logger.rb +76 -0
  21. data/lib/opentracing/instrumentation/faraday/trace_middleware.rb +202 -0
  22. data/lib/opentracing/instrumentation/mongo.rb +12 -0
  23. data/lib/opentracing/instrumentation/mongo/direct_sanitazer.rb +16 -0
  24. data/lib/opentracing/instrumentation/mongo/query_sanitazer.rb +84 -0
  25. data/lib/opentracing/instrumentation/mongo/trace_subscriber.rb +107 -0
  26. data/lib/opentracing/instrumentation/object_wrapper.rb +59 -0
  27. data/lib/opentracing/instrumentation/rack.rb +11 -0
  28. data/lib/opentracing/instrumentation/rack/http_tagger.rb +69 -0
  29. data/lib/opentracing/instrumentation/rack/trace_middleware.rb +94 -0
  30. data/lib/opentracing/instrumentation/redis.rb +18 -0
  31. data/lib/opentracing/instrumentation/redis/config.rb +40 -0
  32. data/lib/opentracing/instrumentation/redis/span_builder.rb +85 -0
  33. data/lib/opentracing/instrumentation/redis/tracing_driver_wrapper.rb +117 -0
  34. data/lib/opentracing/instrumentation/sidekiq.rb +17 -0
  35. data/lib/opentracing/instrumentation/sidekiq/client_middleware.rb +66 -0
  36. data/lib/opentracing/instrumentation/sidekiq/job_tagger.rb +61 -0
  37. data/lib/opentracing/instrumentation/sidekiq/server_middleware.rb +70 -0
  38. data/lib/opentracing/instrumentation/sinatra.rb +11 -0
  39. data/lib/opentracing/instrumentation/sinatra/trace_middleware.rb +64 -0
  40. data/lib/opentracing/instrumentation/thrift.rb +15 -0
  41. data/lib/opentracing/instrumentation/thrift/config.rb +24 -0
  42. data/lib/opentracing/instrumentation/thrift/traced_protocol.rb +145 -0
  43. data/lib/opentracing/instrumentation/thrift/traced_protocol_factory.rb +48 -0
  44. data/lib/opentracing/instrumentation/version.rb +7 -0
  45. data/opentracing-instrumentation.gemspec +40 -0
  46. metadata +255 -0
@@ -0,0 +1,117 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'redis/connection/ruby'
4
+ require 'json'
5
+
6
+ module OpenTracing
7
+ module Instrumentation
8
+ module Redis
9
+ # TracingDriverWrapper wrap redis driver
10
+ class TracingDriverWrapper
11
+ extend Forwardable
12
+
13
+ class << self
14
+ DEAFULT_BACKGROUND_DRIVER = ::Redis::Connection::Ruby
15
+
16
+ def connect(options)
17
+ connection = connect_backround_driver(options)
18
+
19
+ span_builder = options.fetch(:span_builder, SpanBuilder.new)
20
+
21
+ new(
22
+ connection: connection,
23
+ span_builder: span_builder,
24
+ host: options[:host],
25
+ port: options[:port],
26
+ )
27
+ end
28
+
29
+ private
30
+
31
+ def connect_backround_driver(options)
32
+ background_driver =
33
+ options.fetch(:background_driver, DEAFULT_BACKGROUND_DRIVER)
34
+ background_driver.connect(options)
35
+ end
36
+ end
37
+
38
+ attr_reader :connection
39
+ attr_reader :span_builder
40
+
41
+ def initialize(
42
+ connection:,
43
+ span_builder:,
44
+ host:,
45
+ port:
46
+ )
47
+ @connection = connection
48
+ @span_builder = span_builder
49
+ @host = host
50
+ @port = port
51
+ end
52
+
53
+ def_delegators :connection,
54
+ :connected?,
55
+ :disconnect,
56
+ :timeout=
57
+
58
+ def write(command)
59
+ scope = start_pipeline_scope
60
+ connection.write(command).tap do
61
+ span_builder.write_log_command(scope.span, command) if scope
62
+ end
63
+ rescue StandardError => e
64
+ span_builder.write_error(scope.span, e, event: EVENT_WRITE) if scope
65
+ close_pipeline_scope
66
+ raise
67
+ end
68
+
69
+ def read
70
+ scope = pipeline_scope
71
+ connection.read.tap do |reply|
72
+ span_builder.write_log_reply(scope.span, reply) if scope
73
+ end
74
+ rescue StandardError => e
75
+ span_builder.write_error(scope.span, e, event: EVENT_READ) if scope
76
+ raise
77
+ ensure
78
+ close_pipeline_scope
79
+ end
80
+
81
+ private
82
+
83
+ attr_reader :pipeline_scope
84
+ attr_reader :error_writer
85
+
86
+ def peer_addr
87
+ "#{@host}:#{@port}"
88
+ end
89
+
90
+ def start_pipeline_scope
91
+ if @pipeline_scope
92
+ @pipeline_queue_size += 1
93
+ return @pipeline_scope
94
+ end
95
+
96
+ @pipeline_queue_size = 1
97
+ @pipeline_scope =
98
+ span_builder.start_active_scope(
99
+ connection.class,
100
+ peer_addr,
101
+ )
102
+ end
103
+
104
+ def close_pipeline_scope
105
+ return unless @pipeline_scope
106
+
107
+ @pipeline_queue_size -= 1
108
+
109
+ return if @pipeline_queue_size.positive?
110
+
111
+ @pipeline_scope.close
112
+ @pipeline_scope = nil
113
+ end
114
+ end
115
+ end
116
+ end
117
+ end
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ module OpenTracing
4
+ module Instrumentation
5
+ # Sidekiq instrumentation
6
+ module Sidekiq
7
+ autoload :ClientMiddleware,
8
+ 'opentracing/instrumentation/sidekiq/client_middleware'
9
+
10
+ autoload :JobTagger,
11
+ 'opentracing/instrumentation/sidekiq/job_tagger'
12
+
13
+ autoload :ServerMiddleware,
14
+ 'opentracing/instrumentation/sidekiq/server_middleware'
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,66 @@
1
+ # frozen_string_literal: true
2
+
3
+ module OpenTracing
4
+ module Instrumentation
5
+ module Sidekiq
6
+ # Sidekiq producer middleware
7
+ class ClientMiddleware
8
+ extend Forwardable
9
+
10
+ DEFAULT_SPAN_NAME = 'sidekiq_enqueue'
11
+ DEFAULT_SPAN_KIND = 'producer'
12
+
13
+ attr_reader :tracer
14
+ attr_reader :tagger
15
+ attr_reader :error_writter
16
+ attr_reader :span_kind
17
+ attr_reader :span_name
18
+
19
+ def initialize(
20
+ tracer: OpenTracing.global_tracer,
21
+ tagger: JobTagger.new,
22
+ error_writter: Common::ErrorWriter.new,
23
+ span_kind: DEFAULT_SPAN_KIND,
24
+ span_name: DEFAULT_SPAN_NAME
25
+ )
26
+ @tracer = tracer
27
+ @tagger = tagger
28
+ @error_writter = error_writter
29
+ @span_kind = span_kind
30
+ @span_name = span_name
31
+ end
32
+
33
+ def call(_worker_class, job, _queue, _redis_pool)
34
+ scope = tracer.start_active_span(span_name, **build_span_args(job))
35
+ inject(scope.span.context, job)
36
+ log(scope.span, job) do
37
+ yield
38
+ end
39
+ ensure
40
+ scope.close
41
+ end
42
+
43
+ private
44
+
45
+ def log(span, job)
46
+ tagger.write_args_log(span, job['jid'], job['args'])
47
+
48
+ yield
49
+ rescue StandardError => e
50
+ error_writter.write_error(span, e)
51
+ raise
52
+ end
53
+
54
+ def build_span_args(job)
55
+ {
56
+ tags: tagger.build_tags(job, span_kind),
57
+ }
58
+ end
59
+
60
+ def inject(span_context, carrier)
61
+ tracer.inject(span_context, OpenTracing::FORMAT_TEXT_MAP, carrier)
62
+ end
63
+ end
64
+ end
65
+ end
66
+ end
@@ -0,0 +1,61 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'json'
4
+
5
+ module OpenTracing
6
+ module Instrumentation
7
+ module Sidekiq
8
+ # Span tags and logs building mixin
9
+ class JobTagger
10
+ LOG_JID_EVENT = 'job_id'
11
+ LOG_ARGS_EVENT = 'job_args'
12
+
13
+ DEFAULT_COMPONENT = 'sidekiq'
14
+
15
+ attr_accessor :component
16
+ attr_accessor :log_args
17
+
18
+ # @param component [String] component name
19
+ # @param log_args [TrueClass, FalseClass] enable attribute logging
20
+ def initialize(
21
+ component: DEFAULT_COMPONENT,
22
+ log_args: false
23
+ )
24
+ @component = component
25
+ @log_args = log_args
26
+
27
+ yield self if block_given?
28
+ end
29
+
30
+ # build tags from job data and static attributes
31
+ def build_tags(job, span_kind)
32
+ {
33
+ 'component' => component,
34
+ 'span.kind' => span_kind,
35
+ 'sidekiq.queue' => job['queue'],
36
+ 'sidekiq.class' => job['class'],
37
+ 'sidekiq.retry' => job['retry'],
38
+ }
39
+ end
40
+
41
+ # write job jid and args if log_args enabled
42
+ # @param span [OpenTracing::Span] target span
43
+ # @param jid [String] job id
44
+ # @param args [Array<Object>] argument list
45
+ def write_args_log(span, jid, args)
46
+ span.log_kv(
47
+ event: LOG_JID_EVENT,
48
+ jid: jid,
49
+ )
50
+
51
+ return unless log_args
52
+
53
+ span.log_kv(
54
+ event: LOG_ARGS_EVENT,
55
+ args: JSON.dump(args),
56
+ )
57
+ end
58
+ end
59
+ end
60
+ end
61
+ end
@@ -0,0 +1,70 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'json'
4
+
5
+ module OpenTracing
6
+ module Instrumentation
7
+ module Sidekiq
8
+ # Sidekiq server middleware
9
+ class ServerMiddleware
10
+ extend Forwardable
11
+
12
+ DEFAULT_SPAN_NAME = 'sidekiq_perform'
13
+ DEFAULT_SPAN_KIND = 'consumer'
14
+
15
+ attr_reader :tracer
16
+ attr_reader :tagger
17
+ attr_reader :error_writter
18
+ attr_reader :span_kind
19
+ attr_reader :span_name
20
+
21
+ def initialize(
22
+ tracer: OpenTracing.global_tracer,
23
+ tagger: JobTagger.new,
24
+ error_writter: Common::ErrorWriter.new,
25
+ span_kind: DEFAULT_SPAN_KIND,
26
+ span_name: DEFAULT_SPAN_NAME
27
+ )
28
+ @tracer = tracer
29
+ @tagger = tagger
30
+ @error_writter = error_writter
31
+ @span_kind = span_kind
32
+ @span_name = span_name
33
+ end
34
+
35
+ def call(_worker, job, _queue)
36
+ scope = tracer.start_active_span(span_name, **build_span_args(job))
37
+
38
+ log(scope.span, job) do
39
+ yield
40
+ end
41
+ ensure
42
+ scope.close
43
+ end
44
+
45
+ private
46
+
47
+ def log(span, job)
48
+ tagger.write_args_log(span, job['jid'], job['args'])
49
+
50
+ yield
51
+ rescue StandardError => e
52
+ error_writter.write_error(span, e)
53
+ raise
54
+ end
55
+
56
+ def build_span_args(job)
57
+ {
58
+ references: extract_references(job),
59
+ tags: tagger.build_tags(job, span_kind),
60
+ }
61
+ end
62
+
63
+ def extract_references(job)
64
+ span_context = tracer.extract(OpenTracing::FORMAT_TEXT_MAP, job)
65
+ [OpenTracing::Reference.follows_from(span_context)]
66
+ end
67
+ end
68
+ end
69
+ end
70
+ end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ module OpenTracing
4
+ module Instrumentation
5
+ # OpenTracing instrumentation for Sinatra
6
+ module Sinatra
7
+ autoload :TraceMiddleware,
8
+ 'opentracing/instrumentation/sinatra/trace_middleware'
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,64 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'sinatra/base'
4
+
5
+ module OpenTracing
6
+ module Instrumentation
7
+ module Sinatra
8
+ # TraceMiddleware for sinatra
9
+ #
10
+ # Usage
11
+ # class MyApp < Sinatra::Base
12
+ # use OpenTracing::Instrumentation::Sinatra::TraceMiddleware
13
+ # set :tracer, MyTracer.instance # optionaly
14
+ # end
15
+ module TraceMiddleware
16
+ def self.registered(app)
17
+ app.helpers self
18
+
19
+ app.set :tracer, OpenTracing.global_tracer
20
+ app.set :command_name, 'sinatra_request'
21
+
22
+ app.before do
23
+ start_span
24
+ end
25
+
26
+ app.after do
27
+ close_span
28
+ end
29
+ end
30
+
31
+ private
32
+
33
+ def start_span
34
+ @scope = settings.tracer.start_active_span(
35
+ settings.command_name,
36
+ tags: build_base_tags,
37
+ )
38
+ end
39
+
40
+ def close_span
41
+ set_response_span_tags(@scope.span, env)
42
+ @scope.close
43
+ end
44
+
45
+ def build_base_tags
46
+ {
47
+ 'sinatra.app' => self.class.to_s,
48
+ 'sinatra.environment' => settings.environment,
49
+ 'sinatra.threaded' => settings.threaded,
50
+ 'sinatra.show_exceptions' => settings.show_exceptions,
51
+ }
52
+ end
53
+
54
+ def set_response_span_tags(span, env)
55
+ sinatra_route = env['sinatra.route']
56
+ span.set_tag 'sinatra.route', sinatra_route
57
+
58
+ sinatra_error = env['sinatra.error']
59
+ span.set_tag('error', true) if sinatra_error
60
+ end
61
+ end
62
+ end
63
+ end
64
+ end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ module OpenTracing
4
+ module Instrumentation
5
+ # OpenTracing instrumentation for Thrift client and server
6
+ module Thrift
7
+ autoload :Config,
8
+ 'opentracing/instrumentation/thrift/config'
9
+ autoload :TracedProtocol,
10
+ 'opentracing/instrumentation/thrift/traced_protocol'
11
+ autoload :TracedProtocolFactory,
12
+ 'opentracing/instrumentation/thrift/traced_protocol_factory'
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,24 @@
1
+ # frozen_string_literal: true
2
+
3
+ module OpenTracing
4
+ module Instrumentation
5
+ module Thrift
6
+ # Config for TracedProtocol
7
+ class Config
8
+ attr_accessor :tracer,
9
+ :write_operation_name,
10
+ :read_operation_name
11
+
12
+ def initialize(
13
+ tracer: OpenTracing.global_tracer,
14
+ write_operation_name: 'thrift_write',
15
+ read_operation_name: 'thrift_read'
16
+ )
17
+ @tracer = tracer
18
+ @write_operation_name = write_operation_name
19
+ @read_operation_name = read_operation_name
20
+ end
21
+ end
22
+ end
23
+ end
24
+ end