opentracing-instrumentation 0.1.2 → 0.1.3

Sign up to get free protection for your applications and to get access to all the features.
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