opentracing-instrumentation 0.1.2 → 0.1.3

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 (25) hide show
  1. checksums.yaml +4 -4
  2. data/GEM_VERSION +1 -1
  3. data/Gemfile.lock +26 -1
  4. data/lib/opentracing/instrumentation.rb +2 -0
  5. data/lib/opentracing/instrumentation/bunny.rb +36 -0
  6. data/lib/opentracing/instrumentation/bunny/consume_operation_name_builder.rb +53 -0
  7. data/lib/opentracing/instrumentation/bunny/consume_tags_builder.rb +55 -0
  8. data/lib/opentracing/instrumentation/bunny/consume_tracer.rb +107 -0
  9. data/lib/opentracing/instrumentation/bunny/consume_tracer_config.rb +29 -0
  10. data/lib/opentracing/instrumentation/bunny/headers_builder.rb +28 -0
  11. data/lib/opentracing/instrumentation/bunny/headers_injector.rb +29 -0
  12. data/lib/opentracing/instrumentation/bunny/publish_operation_name_builder.rb +50 -0
  13. data/lib/opentracing/instrumentation/bunny/publish_tags_builder.rb +78 -0
  14. data/lib/opentracing/instrumentation/bunny/publish_tracer.rb +122 -0
  15. data/lib/opentracing/instrumentation/bunny/publish_tracer_config.rb +31 -0
  16. data/lib/opentracing/instrumentation/bunny/regexp_routing_key_sanitazer.rb +52 -0
  17. data/lib/opentracing/instrumentation/hutch.rb +28 -0
  18. data/lib/opentracing/instrumentation/hutch/consume_operation_name_builder.rb +60 -0
  19. data/lib/opentracing/instrumentation/hutch/consume_tags_builder.rb +52 -0
  20. data/lib/opentracing/instrumentation/hutch/consume_tracer.rb +84 -0
  21. data/lib/opentracing/instrumentation/hutch/consume_tracer_builder.rb +38 -0
  22. data/lib/opentracing/instrumentation/hutch/consume_tracer_config.rb +33 -0
  23. data/lib/opentracing/instrumentation/hutch/global_properties_builder.rb +44 -0
  24. data/opentracing-instrumentation.gemspec +2 -0
  25. metadata +49 -2
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: acb5d0dd01cab906babfd39d9ad99949eefcfa7f586c00883c0c9a606e66f642
4
- data.tar.gz: 7051aa44a11889a29f33f853805734dc06efdc548e979fe83f8725a84d5f8b79
3
+ metadata.gz: 106a9dbe45448a23d7f3ae81c6088e48eeb3e5758b9092315d1c2e81ed3e27de
4
+ data.tar.gz: '0268cc6d0f0db99d52b447bda20115ea1fd04820ec14e2a59d8e941d29782797'
5
5
  SHA512:
6
- metadata.gz: 784b8bb87a3a7e1b2ae054b32e3c3313d55809efcf567af1d72bd1d5a7f88e671450ea188e4d862bd5fd8bdc8cc8a8d29bcd4bcfd3ca6db41ee3aade85429a23
7
- data.tar.gz: aff530e01d925b4dd77d587a37b48af25d57e51982ed25748621842b7c121422d7d3fe2cc35ebc2c5bd396708901c51927bc793dfaaa4fe2d0622a15dea33014
6
+ metadata.gz: 9292a2dd9483d8c34138eb68d5d4a8cd94cedc5d666d1df55a06af4acf7b1d4508deab4dc83436db798cd879cab1f6f9f014be0611b2efddf7a0990896a7ffaf
7
+ data.tar.gz: deb16198328e487a0d3200e6f5083c5c78ede07e3aaa75be1b25828ae2b817904cc39c268d39559f9518adc2c02108eb371a481bfc31ca7c48d0c357ac2be4ae
data/GEM_VERSION CHANGED
@@ -1 +1 @@
1
- 0.1.2
1
+ 0.1.3
data/Gemfile.lock CHANGED
@@ -18,23 +18,43 @@ GIT
18
18
  PATH
19
19
  remote: .
20
20
  specs:
21
- opentracing-instrumentation (0.1.1)
21
+ opentracing-instrumentation (0.1.3)
22
22
  json
23
23
  opentracing (~> 0.5.0)
24
24
 
25
25
  GEM
26
26
  remote: https://rubygems.org/
27
27
  specs:
28
+ activesupport (5.2.4.2)
29
+ concurrent-ruby (~> 1.0, >= 1.0.2)
30
+ i18n (>= 0.7, < 2)
31
+ minitest (~> 5.1)
32
+ tzinfo (~> 1.1)
33
+ amq-protocol (2.3.0)
28
34
  ast (2.4.0)
29
35
  bson (4.8.0)
36
+ bunny (2.14.4)
37
+ amq-protocol (~> 2.3, >= 2.3.0)
30
38
  byebug (11.1.1)
39
+ carrot-top (0.0.7)
40
+ json
31
41
  coderay (1.1.2)
42
+ concurrent-ruby (1.1.6)
32
43
  diff-lcs (1.3)
33
44
  faraday (0.9.2)
34
45
  multipart-post (>= 1.2, < 3)
46
+ hutch (0.26.0)
47
+ activesupport (>= 4.2, < 6)
48
+ bunny (>= 2.12, < 2.15)
49
+ carrot-top (~> 0.0.7)
50
+ multi_json (~> 1.12)
51
+ i18n (1.8.2)
52
+ concurrent-ruby (~> 1.0)
35
53
  jaro_winkler (1.5.4)
36
54
  json (2.3.0)
37
55
  method_source (0.9.2)
56
+ minitest (5.14.0)
57
+ multi_json (1.14.1)
38
58
  multipart-post (2.1.1)
39
59
  opentracing (0.5.0)
40
60
  parallel (1.19.1)
@@ -75,7 +95,10 @@ GEM
75
95
  rubocop-rspec (1.38.1)
76
96
  rubocop (>= 0.68.1)
77
97
  ruby-progressbar (1.10.1)
98
+ thread_safe (0.3.6)
78
99
  thrift (0.11.0.0)
100
+ tzinfo (1.2.6)
101
+ thread_safe (~> 0.1)
79
102
  unicode-display_width (1.6.1)
80
103
 
81
104
  PLATFORMS
@@ -84,7 +107,9 @@ PLATFORMS
84
107
  DEPENDENCIES
85
108
  bson (~> 4.0)
86
109
  bundler (= 2.1.4)
110
+ bunny (~> 2.0)
87
111
  faraday (~> 0.9.2)
112
+ hutch (~> 0.26.0)
88
113
  opentracing-instrumentation!
89
114
  pry-byebug (~> 3.8)
90
115
  rack (~> 2.2.2)
@@ -19,5 +19,7 @@ module OpenTracing
19
19
  autoload :Thrift, 'opentracing/instrumentation/thrift'
20
20
  autoload :Redis, 'opentracing/instrumentation/redis'
21
21
  autoload :Sidekiq, 'opentracing/instrumentation/sidekiq'
22
+ autoload :Hutch, 'opentracing/instrumentation/hutch'
23
+ autoload :Bunny, 'opentracing/instrumentation/bunny'
22
24
  end
23
25
  end
@@ -0,0 +1,36 @@
1
+ # frozen_string_literal: true
2
+
3
+ module OpenTracing
4
+ module Instrumentation
5
+ # Bunny tracing instrumentation
6
+ #
7
+ # @see ConsumeTracer
8
+ # @see PublishTracer
9
+ module Bunny
10
+ module_path = 'opentracing/instrumentation/bunny'
11
+
12
+ autoload :ConsumeOperationNameBuilder,
13
+ module_path + '/consume_operation_name_builder'
14
+ autoload :ConsumeTagsBuilder,
15
+ module_path + '/consume_tags_builder'
16
+ autoload :ConsumeTracer,
17
+ module_path + '/consume_tracer'
18
+ autoload :ConsumeTracerConfig,
19
+ module_path + '/consume_tracer_config'
20
+ autoload :HeadersBuilder,
21
+ module_path + '/headers_builder'
22
+ autoload :HeadersInjector,
23
+ module_path + '/headers_injector'
24
+ autoload :PublishOperationNameBuilder,
25
+ module_path + '/publish_operation_name_builder'
26
+ autoload :PublishTagsBuilder,
27
+ module_path + '/publish_tags_builder'
28
+ autoload :PublishTracer,
29
+ module_path + '/publish_tracer'
30
+ autoload :PublishTracerConfig,
31
+ module_path + '/publish_tracer_config'
32
+ autoload :RegexpRoutingKeySanitazer,
33
+ module_path + '/regexp_routing_key_sanitazer'
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,53 @@
1
+ # frozen_string_literal: true
2
+
3
+ module OpenTracing
4
+ module Instrumentation
5
+ module Bunny
6
+ # ConsumeOperationNameBuilder build consume command name from
7
+ # queue and delivery_info
8
+ class ConsumeOperationNameBuilder
9
+ DEFAULT_OPERATION_NAME_PATTERN = \
10
+ 'bunny_consume(' \
11
+ 'routing_key=%<routing_key>s, ' \
12
+ 'exchange=%<exchange>s, ' \
13
+ 'queue=%<queue>s' \
14
+ ')'
15
+
16
+ # @param routing_key_sanitazer [RegexpRoutingKeySanitazer]
17
+ # @param operation_name_pattern [String]
18
+ def initialize(
19
+ routing_key_sanitazer: RegexpRoutingKeySanitazer.new,
20
+ operation_name_pattern: DEFAULT_OPERATION_NAME_PATTERN
21
+ )
22
+ @routing_key_sanitazer = routing_key_sanitazer
23
+ @operation_name_pattern = operation_name_pattern
24
+ end
25
+
26
+ # @param delivery_info [Bunny::DeliveryInfo]
27
+ # @return [String] bunny consume operation name
28
+ def build_operation_name(delivery_info)
29
+ format_args = build_format_args(delivery_info)
30
+ format(@operation_name_pattern, format_args)
31
+ end
32
+
33
+ private
34
+
35
+ def build_format_args(delivery_info)
36
+ queue = delivery_info[:consumer].queue
37
+ routing_key = \
38
+ sanitaze_routing_key(delivery_info[:routing_key])
39
+ delivery_info.to_h.merge(
40
+ routing_key: routing_key,
41
+ queue: queue.name,
42
+ )
43
+ end
44
+
45
+ def sanitaze_routing_key(routing_key)
46
+ return if routing_key.nil?
47
+
48
+ @routing_key_sanitazer.sanitaze_routing_key(routing_key)
49
+ end
50
+ end
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,55 @@
1
+ # frozen_string_literal: true
2
+
3
+ module OpenTracing
4
+ module Instrumentation
5
+ module Bunny
6
+ # ConsumeTagsBuidler build consume tags from delivery_info and
7
+ # properties
8
+ class ConsumeTagsBuilder
9
+ DEFAULT_STATIC_TAGS = {
10
+ 'span.kind' => 'consumer',
11
+ 'component' => 'bunny',
12
+ }.freeze
13
+
14
+ # @param static_tags [Hash<String, String>]
15
+ def initialize(static_tags: DEFAULT_STATIC_TAGS)
16
+ @static_tags = static_tags
17
+ end
18
+
19
+ # @param delivery_info [Bunny::DeliveryInfo]
20
+ # @param properties [Bunny::MessageProperties]
21
+ # @return [Hash] consume span tags
22
+ def build_tags(delivery_info, properties)
23
+ @static_tags
24
+ .merge(build_properties_tags(properties))
25
+ .merge(build_delivery_info_tags(delivery_info))
26
+ .merge(build_consumer_tags(delivery_info[:consumer]))
27
+ end
28
+
29
+ private
30
+
31
+ def build_properties_tags(properties)
32
+ {
33
+ 'amqp.content_type' => properties[:content_type],
34
+ }
35
+ end
36
+
37
+ def build_delivery_info_tags(delivery_info)
38
+ {
39
+ 'amqp.redelivered' => delivery_info[:redelivered],
40
+ 'amqp.exchange' => delivery_info[:exchange],
41
+ 'amqp.routing_key' => delivery_info[:routing_key],
42
+ }
43
+ end
44
+
45
+ def build_consumer_tags(consumer)
46
+ {
47
+ 'amqp.queue' => consumer.queue.name,
48
+ 'amqp.no_ack' => consumer.no_ack,
49
+ 'amqp.exclusive' => consumer.exclusive,
50
+ }
51
+ end
52
+ end
53
+ end
54
+ end
55
+ end
@@ -0,0 +1,107 @@
1
+ # frozen_string_literal: true
2
+
3
+ module OpenTracing
4
+ module Instrumentation
5
+ module Bunny
6
+ # ConsumeTracer extract parent span from message headers and
7
+ # create span around passed block
8
+ #
9
+ # Usage:
10
+ # consumer_tracer = \
11
+ # OpenTracing::Instrumentation::Bunny::ConsumeTracer.new
12
+ #
13
+ # consumer_tracer = \
14
+ # OpenTracing::Instrumentation::Bunny::ConsumeTracer.new do |config|
15
+ # config.tracer = custom_tracer
16
+ # end
17
+ #
18
+ # queue.subscribe(block: true) do |delivery_info, properties, payload|
19
+ # consume_tracer.consume(delivery_info, properties) do
20
+ # end
21
+ # end
22
+ class ConsumeTracer
23
+ extend Forwardable
24
+
25
+ # @param config [ConsumeTracerConfig]
26
+ def initialize(config: ConsumeTracerConfig.new)
27
+ yield config if block_given?
28
+ @config = config
29
+ end
30
+
31
+ # Extract tracing parent from headers. Create span with tags.
32
+ # If block passed, then span closed after block exit, otherwise
33
+ # return active scope.
34
+ #
35
+ # @param delivery_info [Bunny::DeliveryInfo]
36
+ # @param properties [Bunny::MessageProperties]
37
+ # @yield if block passed, then it called and after scope closed
38
+ # @yieldparam scope [OpenTracing::Scope]
39
+ # @return [OpenTracing::Scope, nil] return active scope if called
40
+ # without block, otherwise return block result
41
+ def consume(delivery_info, properties)
42
+ span_scope = safe_start_active_span(delivery_info, properties)
43
+ return span_scope unless block_given?
44
+
45
+ handle_error(span_scope) do
46
+ yield span_scope
47
+ end
48
+ end
49
+
50
+ private
51
+
52
+ def_delegators(
53
+ :@config,
54
+ :tracer,
55
+ :operation_name_builder,
56
+ :tags_builder,
57
+ :error_writer,
58
+ :logger,
59
+ )
60
+
61
+ def handle_error(span_scope)
62
+ yield
63
+ rescue StandardError => e
64
+ error_writer.write_error(span_scope.span, e)
65
+ raise e
66
+ ensure
67
+ # Close span if exists
68
+ span_scope&.close
69
+ end
70
+
71
+ def safe_start_active_span(delivery_info, properties)
72
+ start_active_span(delivery_info, properties)
73
+ rescue StandardError => e
74
+ logger&.error(e)
75
+ nil
76
+ end
77
+
78
+ def start_active_span(delivery_info, properties)
79
+ operation_name = build_operation_name(delivery_info)
80
+ tags = build_tags(delivery_info, properties)
81
+ references = build_references(properties[:headers])
82
+ tracer.start_active_span(
83
+ operation_name,
84
+ tags: tags,
85
+ references: references,
86
+ )
87
+ end
88
+
89
+ def build_operation_name(delivery_info)
90
+ operation_name_builder.build_operation_name(delivery_info)
91
+ end
92
+
93
+ def build_tags(delivery_info, properties)
94
+ tags_builder.build_tags(delivery_info, properties)
95
+ end
96
+
97
+ def build_references(headers)
98
+ span_context = tracer.extract(
99
+ OpenTracing::FORMAT_TEXT_MAP,
100
+ headers,
101
+ )
102
+ [OpenTracing::Reference.follows_from(span_context)]
103
+ end
104
+ end
105
+ end
106
+ end
107
+ end
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+
3
+ module OpenTracing
4
+ module Instrumentation
5
+ module Bunny
6
+ # ConsumeTracerConfig for ConsumeTracer
7
+ class ConsumeTracerConfig
8
+ # @return [ConsumeOperationNameBuilder]
9
+ attr_accessor :operation_name_builder
10
+ # @return [ConsumeTagsBuilder]
11
+ attr_accessor :tags_builder
12
+ # @return [OpenTracing::Tracer]
13
+ attr_accessor :tracer
14
+ # @return [Common::ErrorWriter]
15
+ attr_accessor :error_writer
16
+ # @return [::Logger]
17
+ attr_accessor :logger
18
+
19
+ def initialize
20
+ @operation_name_builder = ConsumeOperationNameBuilder.new
21
+ @tags_builder = ConsumeTagsBuilder.new
22
+ @tracer = OpenTracing.global_tracer
23
+ @error_writer = Common::ErrorWriter.new
24
+ @logger = nil
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,28 @@
1
+ # frozen_string_literal: true
2
+
3
+ module OpenTracing
4
+ module Instrumentation
5
+ module Bunny
6
+ # HeadersBuilder build AMQP headers
7
+ class HeadersBuilder
8
+ # @param tracer [OpenTracing::Tracer]
9
+ # @param injector [HeadersInjector]
10
+ def initialize(
11
+ tracer: OpenTracing.global_tracer,
12
+ injector: HeadersInjector.new(tracer: tracer)
13
+ )
14
+ @tracer = tracer
15
+ @injector = injector
16
+ end
17
+
18
+ # @param active_span [OpenTracing::Span]
19
+ # @return [Hash<String, String>] return injected headers
20
+ def build(active_span: @tracer.active_span)
21
+ headers = {}
22
+ @injector.inject(headers, active_span: active_span)
23
+ headers
24
+ end
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+
3
+ module OpenTracing
4
+ module Instrumentation
5
+ module Bunny
6
+ # HeadersInjector inject tracing headers into bunny message headers
7
+ class HeadersInjector
8
+ # @param trace [OpenTracing::Tracer]
9
+ def initialize(tracer: OpenTracing.global_tracer)
10
+ @tracer = tracer
11
+ end
12
+
13
+ # inject tracing headers
14
+ # @param headers [Hash<String,String>]
15
+ # @param active_span [OpenTracing::Span]
16
+ def inject(
17
+ headers,
18
+ active_span: @tracer.active_span
19
+ )
20
+ @tracer.inject(
21
+ active_span.context,
22
+ OpenTracing::FORMAT_TEXT_MAP,
23
+ headers,
24
+ )
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,50 @@
1
+ # frozen_string_literal: true
2
+
3
+ module OpenTracing
4
+ module Instrumentation
5
+ module Bunny
6
+ # PublishOperationNameBuilder build publish command name from
7
+ # exchange and publish options
8
+ class PublishOperationNameBuilder
9
+ DEFAULT_OPERATION_NAME_PATTERN = \
10
+ 'bunny_publish(' \
11
+ 'routing_key=%<routing_key>s, ' \
12
+ 'exchange=%<exchange>s' \
13
+ ')'
14
+
15
+ # @param routing_key_sanitazer [RegexpRoutingKeySanitazer]
16
+ # @param operation_name_pattern [String]
17
+ def initialize(
18
+ routing_key_sanitazer: RegexpRoutingKeySanitazer.new,
19
+ operation_name_pattern: DEFAULT_OPERATION_NAME_PATTERN
20
+ )
21
+ @routing_key_sanitazer = routing_key_sanitazer
22
+ @operation_name_pattern = operation_name_pattern
23
+ end
24
+
25
+ # @param exchange [Bunny::Exchange]
26
+ # @param opts [Hash<Symbol, Object>]
27
+ # @option opts [String] :routing_key
28
+ # @return [String]
29
+ def build_operation_name(exchange, opts)
30
+ format_args = build_format_args(exchange, opts)
31
+ format(@operation_name_pattern, **format_args)
32
+ end
33
+
34
+ private
35
+
36
+ def build_format_args(exchange, opts)
37
+ opts
38
+ .merge(exchange: exchange.name)
39
+ .merge(routing_key: sanitaze_routing_key(opts[:routing_key]))
40
+ end
41
+
42
+ def sanitaze_routing_key(routing_key)
43
+ return if routing_key.nil?
44
+
45
+ @routing_key_sanitazer.sanitaze_routing_key(routing_key)
46
+ end
47
+ end
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,78 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'bunny'
4
+
5
+ module OpenTracing
6
+ module Instrumentation
7
+ module Bunny
8
+ # PublishTagsBuilder build span tags for Bunny::PublishTracer
9
+ class PublishTagsBuilder
10
+ DEFAULT_STATIC_TAGS = {
11
+ 'span.kind' => 'publiser',
12
+ 'component' => 'bunny',
13
+ }.freeze
14
+
15
+ DEFAULT_CONTENT_TYPE = ::Bunny::Channel::DEFAULT_CONTENT_TYPE
16
+ DEFAULT_PERSISTENT = true
17
+ DEFAAUL_PRIORITY = 0
18
+
19
+ # @param static_tags [Hash<String, String>]
20
+ def initialize(static_tags: DEFAULT_STATIC_TAGS)
21
+ @static_tags = static_tags
22
+ end
23
+
24
+ # @param exchange [Bunny::Exchange]
25
+ # @param opts [Hash<>]
26
+ # @option opts [String, nil] :routing_key
27
+ # @option opts [String, nil] :content_type
28
+ # @option opts [String, nil] :message_id
29
+ # @option opts [Integer, nil] :expiration
30
+ # @option opts [String, nil] :content_encoding
31
+ # @option opts [Boolean, nil] :persistent
32
+ # @option opts [Boolean, nil] :mandatory
33
+ # @option opts [Integer, nil] :priority
34
+ # @option opts [String, nil] :app_id
35
+ # @return [Hash<String, String>]
36
+ def build_tags(exchange, opts)
37
+ @static_tags
38
+ .merge(exchange_tags(exchange))
39
+ .merge(meta_tags(opts))
40
+ .merge(extended_tags(opts))
41
+ .compact
42
+ end
43
+
44
+ private
45
+
46
+ def exchange_tags(exchange)
47
+ {
48
+ 'amqp.exchange' => exchange.name,
49
+ 'amqp.exchange_type' => exchange.type,
50
+ }
51
+ end
52
+
53
+ def meta_tags(opts)
54
+ content_type = opts.fetch(:content_type, DEFAULT_CONTENT_TYPE)
55
+ {
56
+ 'amqp.routing_key' => opts[:routing_key],
57
+ 'amqp.content_type' => content_type,
58
+ 'amqp.message_id' => opts[:message_id],
59
+ 'amqp.expiration' => opts[:expiration],
60
+ 'amqp.content_encoding' => opts[:content_encoding],
61
+ }
62
+ end
63
+
64
+ def extended_tags(opts)
65
+ persistent = opts.fetch(:persistent, DEFAULT_PERSISTENT)
66
+ priority = opts.fetch(:priority, DEFAAUL_PRIORITY)
67
+
68
+ {
69
+ 'amqp.persistent' => persistent,
70
+ 'amqp.mandatory' => opts[:mandatory],
71
+ 'amqp.priority' => priority,
72
+ 'amqp.app_id' => opts[:app_id],
73
+ }
74
+ end
75
+ end
76
+ end
77
+ end
78
+ end
@@ -0,0 +1,122 @@
1
+ # frozen_string_literal: true
2
+
3
+ module OpenTracing
4
+ module Instrumentation
5
+ module Bunny
6
+ # PublishTracer trace publishing and inject trace headers into
7
+ # AMQP message
8
+ #
9
+ # Usage:
10
+ # exchange = channel.topic(BUNNY_EXCHANGE_NAME)
11
+ # default_publisher \
12
+ # = OpenTracing::Instrumentation::Bunny::PublishTracer.new(exchange)
13
+ # configured_publisher = \
14
+ # OpenTracing::Instrumentation::Bunny::PublishTracer.new(exchange) do |c|
15
+ # c.tracer = custom_tracer
16
+ # end
17
+ # publisher.publish(
18
+ # '{"message": 123}',
19
+ # routing_key: 'key',
20
+ # )
21
+ class PublishTracer
22
+ extend Forwardable
23
+
24
+ # @param exchange [Bunny::Exchange]
25
+ def initialize(exchange, config: PublishTracerConfig.new)
26
+ @exchange = exchange
27
+ yield config if block_given?
28
+ @config = config.dup
29
+ end
30
+
31
+ # Publish message via call `exchange.publish`, with injecting tracing
32
+ # into headers and create span around publising call.
33
+ #
34
+ # For deatils see:
35
+ # https://www.rubydoc.info/gems/bunny/Bunny/Exchange#publish-instance_method
36
+ #
37
+ # @param active [OpenTracing::Span] allow overide span
38
+ # @return [Bunny::Exchange]
39
+ def publish(
40
+ payload,
41
+ active_span: tracer.active_span,
42
+ **opts
43
+ )
44
+ trace_publish(span: active_span, **opts) do |scope|
45
+ publish_with_headers(
46
+ payload,
47
+ span: scope&.span || active_span,
48
+ **opts,
49
+ )
50
+ end
51
+ end
52
+
53
+ private
54
+
55
+ attr_reader :exchange
56
+
57
+ def_delegators(
58
+ :@config,
59
+ :tracer,
60
+ :operation_name_builder,
61
+ :tags_builder,
62
+ :injector,
63
+ :error_writer,
64
+ :logger,
65
+ )
66
+
67
+ def safe_inject_headers(span:, headers:)
68
+ injector.inject(headers, active_span: span)
69
+ end
70
+
71
+ def publish_with_headers(
72
+ payload,
73
+ span:,
74
+ headers: {},
75
+ **opts
76
+ )
77
+ safe_inject_headers(span: span, headers: headers)
78
+ exchange.publish(
79
+ payload,
80
+ headers: headers,
81
+ **opts,
82
+ )
83
+ end
84
+
85
+ def trace_publish(span:, **opts)
86
+ scope = safe_start_active_span(span: span, **opts)
87
+ yield scope
88
+ rescue StandardError => e
89
+ error_writer.write_error(scope.span, e) if scope
90
+ logger&.error(e)
91
+ raise e
92
+ ensure
93
+ scope&.close
94
+ end
95
+
96
+ def safe_start_active_span(span:, **opts)
97
+ start_active_span(span: span, **opts)
98
+ rescue StandardError
99
+ nil
100
+ end
101
+
102
+ def start_active_span(span:, **opts)
103
+ operation_name = build_operation_name(opts)
104
+ tags = build_tags(opts)
105
+ tracer.start_active_span(
106
+ operation_name,
107
+ tags: tags,
108
+ child_of: span,
109
+ )
110
+ end
111
+
112
+ def build_operation_name(opts)
113
+ operation_name_builder.build_operation_name(exchange, opts)
114
+ end
115
+
116
+ def build_tags(opts)
117
+ tags_builder.build_tags(exchange, opts)
118
+ end
119
+ end
120
+ end
121
+ end
122
+ end
@@ -0,0 +1,31 @@
1
+ # frozen_string_literal: true
2
+
3
+ module OpenTracing
4
+ module Instrumentation
5
+ module Bunny
6
+ # PublishTracerConfig for PublishTracer
7
+ class PublishTracerConfig
8
+ # @return [OpenTracing::Tracer]
9
+ attr_accessor :tracer
10
+ # @return [PublishOperationNameBuilder]
11
+ attr_accessor :operation_name_builder
12
+ # @return [PublishTagsBuilder]
13
+ attr_accessor :tags_builder
14
+ # @return [HeadersInjector]
15
+ attr_accessor :injector
16
+ # @return [Common::ErrorWriter]
17
+ attr_accessor :error_writer
18
+ # @return [::Logger]
19
+ attr_accessor :logger
20
+
21
+ def initialize
22
+ @tracer = OpenTracing.global_tracer
23
+ @operation_name_builder = PublishOperationNameBuilder.new
24
+ @tags_builder = PublishTagsBuilder.new
25
+ @injector = HeadersInjector.new
26
+ @error_writer = Common::ErrorWriter.new
27
+ end
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,52 @@
1
+ # frozen_string_literal: true
2
+
3
+ module OpenTracing
4
+ module Instrumentation
5
+ module Bunny
6
+ # Replace id into routing key with placeholder
7
+ #
8
+ # Example:
9
+ # sanitazer = RegexpRoutingKeySanitazer.new
10
+ # sanitazer.sanitaze_routing_key('prefix.1234567890abcdef12345678')
11
+ #
12
+ # # 'prefix.:object_id'
13
+ #
14
+ #
15
+ # sanitazer.sanitaze_routing_key('prefix.123.suffix')
16
+ #
17
+ # # 'prefix.:sequence_id.suffis'
18
+ class RegexpRoutingKeySanitazer
19
+ ROUTING_KEY_SEPARATOR = '.'
20
+ DEFAULT_REPLCE_REGEXP_MAP = {
21
+ ':sequence_id' => /^\d+$/,
22
+ ':object_id' => /^[0-9a-f]{24}$/,
23
+ }.freeze
24
+
25
+ # @param replace_regexp_map [Hash<String, String>]
26
+ def initialize(replace_regexp_map: DEFAULT_REPLCE_REGEXP_MAP)
27
+ @replace_regexp_map = replace_regexp_map
28
+ end
29
+
30
+ # @param routing_key [String] souce routing key
31
+ # @return [String] sanitazed routing key
32
+ def sanitaze_routing_key(routing_key)
33
+ routing_key
34
+ .split(ROUTING_KEY_SEPARATOR)
35
+ .map(&method(:filter_part))
36
+ .join(ROUTING_KEY_SEPARATOR)
37
+ end
38
+
39
+ private
40
+
41
+ attr_reader :replace_regexp_map
42
+
43
+ def filter_part(routing_key_part)
44
+ replace_regexp_map.each do |placeholder, regexp|
45
+ return placeholder if regexp =~ routing_key_part
46
+ end
47
+ routing_key_part
48
+ end
49
+ end
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,28 @@
1
+ # frozen_string_literal: true
2
+
3
+ module OpenTracing
4
+ module Instrumentation
5
+ # Module Hutch allow tracing Hutch consuming and inject
6
+ # tracing headers on publishing
7
+ #
8
+ # @see ConsumeTracer
9
+ # @see ConsumeTracerBuilder
10
+ # @see GlobalPropertiesBuilder
11
+ module Hutch
12
+ module_path = 'opentracing/instrumentation/hutch'
13
+
14
+ autoload :ConsumeOperationNameBuilder,
15
+ module_path + '/consume_operation_name_builder'
16
+ autoload :ConsumeTagsBuilder,
17
+ module_path + '/consume_tags_builder'
18
+ autoload :ConsumeTracer,
19
+ module_path + '/consume_tracer'
20
+ autoload :ConsumeTracerBuilder,
21
+ module_path + '/consume_tracer_builder'
22
+ autoload :ConsumeTracerConfig,
23
+ module_path + '/consume_tracer_config'
24
+ autoload :GlobalPropertiesBuilder,
25
+ module_path + '/global_properties_builder'
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,60 @@
1
+ # frozen_string_literal: true
2
+
3
+ module OpenTracing
4
+ module Instrumentation
5
+ module Hutch
6
+ # ConsumeOperationNameBuilder build span operation name of
7
+ # Hutch ConsumeTracer
8
+ class ConsumeOperationNameBuilder
9
+ DEFAULT_OPERATION_NAME_PATTERN = \
10
+ 'hutch_consume(consumer_class=%<consumer_class>s)'
11
+
12
+ # @param routing_key_sanitazer [Bunny::RegexpRoutingKeySanitazer]
13
+ # @param operation_name_pattern [String]
14
+ def initialize(
15
+ routing_key_sanitazer: Bunny::RegexpRoutingKeySanitazer.new,
16
+ operation_name_pattern: DEFAULT_OPERATION_NAME_PATTERN
17
+ )
18
+ @routing_key_sanitazer = routing_key_sanitazer
19
+ @operation_name_pattern = operation_name_pattern
20
+ end
21
+
22
+ # @param consume [Object] instance of consumer
23
+ # @param message [Hutch::Message]
24
+ # @return [String] operation_name
25
+ def build_operation_name(consumer, message)
26
+ format_args = build_format_args(consumer, message)
27
+ format(@operation_name_pattern, format_args)
28
+ end
29
+
30
+ private
31
+
32
+ def build_format_args(consumer, message)
33
+ consumer_format_args(consumer)
34
+ .merge(delivery_info_format_args(message.delivery_info))
35
+ .merge(message.properties.to_h)
36
+ end
37
+
38
+ def delivery_info_format_args(delivery_info)
39
+ routing_key = delivery_info[:routing_key]
40
+ sanitazed_routing_key = sanitaze_routing_key(routing_key)
41
+ delivery_info
42
+ .to_h
43
+ .merge(routing_key: sanitazed_routing_key)
44
+ end
45
+
46
+ def sanitaze_routing_key(routing_key)
47
+ return if routing_key.nil?
48
+
49
+ @routing_key_sanitazer.sanitaze_routing_key(routing_key)
50
+ end
51
+
52
+ def consumer_format_args(consumer)
53
+ {
54
+ consumer_class: consumer.class.to_s,
55
+ }
56
+ end
57
+ end
58
+ end
59
+ end
60
+ end
@@ -0,0 +1,52 @@
1
+ # frozen_string_literal: true
2
+
3
+ module OpenTracing
4
+ module Instrumentation
5
+ module Hutch
6
+ # ConsumeTagsBuilder build span tags for Hutch ConsumerTrace.
7
+ # Its use Bunny::ComsumerTagsBuilde for common tags and add
8
+ # hutch specific tags
9
+ class ConsumeTagsBuilder
10
+ DEFAULT_STATIC_TAGS = {
11
+ 'component' => 'hutch',
12
+ }.freeze
13
+
14
+ # @param bunny_consume_tags_builder [Bunny::ConsumeTagsBuilder]
15
+ # @param static_tags [Hash<String, String>]
16
+ def initialize(
17
+ bunny_consume_tags_builder: Bunny::ConsumeTagsBuilder.new,
18
+ static_tags: DEFAULT_STATIC_TAGS
19
+ )
20
+ @bunny_consume_tags_builder = bunny_consume_tags_builder
21
+ @static_tags = static_tags
22
+ end
23
+
24
+ # @param consumer [Object] instance of consumer
25
+ # @param message [Hutch::Message]
26
+ # @return [Hash<String, String>] tags
27
+ def build_tags(consumer, message)
28
+ build_bunny_tags(message)
29
+ .merge(hutch_tags(consumer))
30
+ .merge(@static_tags)
31
+ end
32
+
33
+ private
34
+
35
+ attr_reader :static_tags
36
+
37
+ def build_bunny_tags(message)
38
+ @bunny_consume_tags_builder.build_tags(
39
+ message.delivery_info,
40
+ message.properties,
41
+ )
42
+ end
43
+
44
+ def hutch_tags(consumer)
45
+ {
46
+ 'hutch.consumer_class' => consumer.class.to_s,
47
+ }
48
+ end
49
+ end
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,84 @@
1
+ # frozen_string_literal: true
2
+
3
+ module OpenTracing
4
+ module Instrumentation
5
+ module Hutch
6
+ # ConsumeTracer trace Hutch consumer. Cannot be configured,
7
+ # Use ConsumeTracerBuilder for configuration.
8
+ #
9
+ # Usage:
10
+ # Hutch::Config.set(
11
+ # :tracer,
12
+ # OpenTracing::Instrumentation::Hutch::ConsumeTracer,
13
+ # )
14
+ class ConsumeTracer
15
+ extend Forwardable
16
+
17
+ def initialize(consumer, config: ConsumeTracerConfig.new)
18
+ @consumer = consumer
19
+ @config = config
20
+ end
21
+
22
+ # Method handle called by Hutch on message. This method
23
+ # start active span, process consumer and close span.
24
+ def handle(message)
25
+ scope = safe_start_active_span(message)
26
+ consumer.process(message)
27
+ rescue StandardError => e
28
+ error_writer.write_error(scope.span, e) if scope
29
+ raise e
30
+ ensure
31
+ # Close scope if exists
32
+ scope&.close
33
+ end
34
+
35
+ private
36
+
37
+ attr_reader :consumer
38
+
39
+ def_delegators(
40
+ :@config,
41
+ :tracer,
42
+ :error_writer,
43
+ :operation_name_builder,
44
+ :tags_builder,
45
+ :logger,
46
+ )
47
+
48
+ def safe_start_active_span(message)
49
+ start_active_span(message)
50
+ rescue StandardError => e
51
+ logger.error(e)
52
+ nil
53
+ end
54
+
55
+ def start_active_span(message)
56
+ operation_name = build_operation_name(message)
57
+ tags = tags_builder.build_tags(consumer, message)
58
+ references = build_references(message.properties[:headers])
59
+
60
+ tracer.start_active_span(
61
+ operation_name,
62
+ tags: tags,
63
+ references: references,
64
+ )
65
+ end
66
+
67
+ def build_operation_name(message)
68
+ operation_name_builder.build_operation_name(
69
+ consumer,
70
+ message,
71
+ )
72
+ end
73
+
74
+ def build_references(headers)
75
+ span_context = tracer.extract(
76
+ OpenTracing::FORMAT_TEXT_MAP,
77
+ headers,
78
+ )
79
+ [OpenTracing::Reference.follows_from(span_context)]
80
+ end
81
+ end
82
+ end
83
+ end
84
+ end
@@ -0,0 +1,38 @@
1
+ # frozen_string_literal: true
2
+
3
+ module OpenTracing
4
+ module Instrumentation
5
+ module Hutch
6
+ # ConsumeTracerBuilder build and configure ConsumeTracer.
7
+ # Should be used for configuration of ConsumeTracer.
8
+ #
9
+ # Usage:
10
+ # hutch_tracer_builder = \
11
+ # OpenTracing::Instrumentation::Hutch::ConsumeTracerBuilder.new do |config|
12
+ # config.tracer = CustomTracer.new
13
+ # end
14
+ # Hutch::Config.set(:tracer, hutch_tracer_builder)
15
+ #
16
+ # @param config [ConsumeTracerConfig]
17
+ # @yield [ConsumeTracerConfig]
18
+ class ConsumeTracerBuilder
19
+ def initialize(config: ConsumeTracerConfig.new)
20
+ yield config if block_given?
21
+ @config = config.dup
22
+ end
23
+
24
+ # Build conifgured ConsumeTracer
25
+ #
26
+ # ! Its not constructor
27
+ #
28
+ # @return ConsumeTracer
29
+ def new(consumer)
30
+ ConsumeTracer.new(
31
+ consumer,
32
+ config: @config,
33
+ )
34
+ end
35
+ end
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ module OpenTracing
4
+ module Instrumentation
5
+ module Hutch
6
+ # Config for ConsumeTracer
7
+ class ConsumeTracerConfig
8
+ # @return [ConsumeOperationNameBuilder]
9
+ attr_accessor :operation_name_builder
10
+
11
+ # @return [ConsumeTagsBuilder]
12
+ attr_accessor :tags_builder
13
+
14
+ # @return [OpenTracing::Tracer]
15
+ attr_accessor :tracer
16
+
17
+ # @return [Common::ErrorWriter]
18
+ attr_accessor :error_writer
19
+
20
+ # @return [::Logger]
21
+ attr_accessor :logger
22
+
23
+ def initialize
24
+ @operation_name_builder = ConsumeOperationNameBuilder.new
25
+ @tags_builder = ConsumeTagsBuilder.new
26
+ @tracer = OpenTracing.global_tracer
27
+ @error_writer = Common::ErrorWriter.new
28
+ @logger = ::Hutch::Logging.logger
29
+ end
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,44 @@
1
+ # frozen_string_literal: true
2
+
3
+ module OpenTracing
4
+ module Instrumentation
5
+ module Hutch
6
+ # GlobalPropertiesBuilder build hutch global properties with
7
+ # tracing headers
8
+ #
9
+ # Can extend other global properties builders.
10
+ # Usage:
11
+ # Hutch.global_properties = \
12
+ # OpenTracing::Instrumentation::Hutch::GlobalPropertiesBuilder.new
13
+ #
14
+ class GlobalPropertiesBuilder
15
+ # EmptyPropertiesBuilder is deafult properties build. It return
16
+ # empty properties.
17
+ class EmptyPropertiesBuilder
18
+ def call
19
+ {}
20
+ end
21
+ end
22
+
23
+ # @param headers_injector [Bunny::HeadersInjector]
24
+ # @param global_properties_builder [EmptyPropertiesBuilder]
25
+ def initialize(
26
+ headers_injector: Bunny::HeadersInjector.new,
27
+ global_properties_builder: EmptyPropertiesBuilder.new
28
+ )
29
+ @headers_injector = headers_injector
30
+ @global_properties_builder = global_properties_builder
31
+ end
32
+
33
+ # @return [Hash<String, String>] properties with injected tracing
34
+ # headers
35
+ def call
36
+ properties = @global_properties_builder.call
37
+ headers = (properties[:headers] ||= {})
38
+ @headers_injector.inject(headers)
39
+ properties
40
+ end
41
+ end
42
+ end
43
+ end
44
+ end
@@ -29,7 +29,9 @@ Gem::Specification.new do |spec|
29
29
 
30
30
  spec.add_development_dependency 'bson', '~> 4.0'
31
31
  spec.add_development_dependency 'bundler', File.read('.BUNDLER_VERSION').strip
32
+ spec.add_development_dependency 'bunny', '~> 2.0'
32
33
  spec.add_development_dependency 'faraday', '~> 0.9.2'
34
+ spec.add_development_dependency 'hutch', '~> 0.26.0'
33
35
  spec.add_development_dependency 'rack', '~> 2.2.2'
34
36
  spec.add_development_dependency 'rake', '~> 10.0'
35
37
  spec.add_development_dependency 'redis', '~> 3.3.5'
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: opentracing-instrumentation
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.2
4
+ version: 0.1.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Fedorenko Dmitrij
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2020-03-26 00:00:00.000000000 Z
11
+ date: 2020-03-29 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: json
@@ -66,6 +66,20 @@ dependencies:
66
66
  - - '='
67
67
  - !ruby/object:Gem::Version
68
68
  version: 2.1.4
69
+ - !ruby/object:Gem::Dependency
70
+ name: bunny
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: '2.0'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: '2.0'
69
83
  - !ruby/object:Gem::Dependency
70
84
  name: faraday
71
85
  requirement: !ruby/object:Gem::Requirement
@@ -80,6 +94,20 @@ dependencies:
80
94
  - - "~>"
81
95
  - !ruby/object:Gem::Version
82
96
  version: 0.9.2
97
+ - !ruby/object:Gem::Dependency
98
+ name: hutch
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - "~>"
102
+ - !ruby/object:Gem::Version
103
+ version: 0.26.0
104
+ type: :development
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - "~>"
109
+ - !ruby/object:Gem::Version
110
+ version: 0.26.0
83
111
  - !ruby/object:Gem::Dependency
84
112
  name: rack
85
113
  requirement: !ruby/object:Gem::Requirement
@@ -200,11 +228,30 @@ files:
200
228
  - bin/console
201
229
  - bin/setup
202
230
  - lib/opentracing/instrumentation.rb
231
+ - lib/opentracing/instrumentation/bunny.rb
232
+ - lib/opentracing/instrumentation/bunny/consume_operation_name_builder.rb
233
+ - lib/opentracing/instrumentation/bunny/consume_tags_builder.rb
234
+ - lib/opentracing/instrumentation/bunny/consume_tracer.rb
235
+ - lib/opentracing/instrumentation/bunny/consume_tracer_config.rb
236
+ - lib/opentracing/instrumentation/bunny/headers_builder.rb
237
+ - lib/opentracing/instrumentation/bunny/headers_injector.rb
238
+ - lib/opentracing/instrumentation/bunny/publish_operation_name_builder.rb
239
+ - lib/opentracing/instrumentation/bunny/publish_tags_builder.rb
240
+ - lib/opentracing/instrumentation/bunny/publish_tracer.rb
241
+ - lib/opentracing/instrumentation/bunny/publish_tracer_config.rb
242
+ - lib/opentracing/instrumentation/bunny/regexp_routing_key_sanitazer.rb
203
243
  - lib/opentracing/instrumentation/common.rb
204
244
  - lib/opentracing/instrumentation/common/error_writer.rb
205
245
  - lib/opentracing/instrumentation/faraday.rb
206
246
  - lib/opentracing/instrumentation/faraday/response_logger.rb
207
247
  - lib/opentracing/instrumentation/faraday/trace_middleware.rb
248
+ - lib/opentracing/instrumentation/hutch.rb
249
+ - lib/opentracing/instrumentation/hutch/consume_operation_name_builder.rb
250
+ - lib/opentracing/instrumentation/hutch/consume_tags_builder.rb
251
+ - lib/opentracing/instrumentation/hutch/consume_tracer.rb
252
+ - lib/opentracing/instrumentation/hutch/consume_tracer_builder.rb
253
+ - lib/opentracing/instrumentation/hutch/consume_tracer_config.rb
254
+ - lib/opentracing/instrumentation/hutch/global_properties_builder.rb
208
255
  - lib/opentracing/instrumentation/mongo.rb
209
256
  - lib/opentracing/instrumentation/mongo/direct_sanitazer.rb
210
257
  - lib/opentracing/instrumentation/mongo/query_sanitazer.rb