epsagon 0.0.28 → 0.0.29

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 1a17b55f2ce2ff35ca70dd51e8f8337041e202c528b6c691800d3e3b7b0d42c4
4
- data.tar.gz: d93046c571faa2d35a681217cb12952c57c7bd3232e77a136a5954a5534541b1
3
+ metadata.gz: 25cf09d9ea5babb3e7caa3380d6b8a78365909391ec3eb84ec150e07fbd9841a
4
+ data.tar.gz: bb3f2efc49e2b0930ffb677bfb5b8440fa16fe2a41a7adb96b33a35e89efdb67
5
5
  SHA512:
6
- metadata.gz: 9290a8dee89f24588f93bca0ab331855cab0cd46e09fdae10c4bb0c00c7c61c35c6045bc6b41971639767fc46f30d241b49a45a89a51c29baaa734113188aa7a
7
- data.tar.gz: 3c4c1b54d0b344bf7399f3d088cd22bebca010d2b063244b425693bcaa2ef0ced625ec3a0921d1d5ce0cf7effd81e90dc6e410d5c893f1062096c78ec66e795f
6
+ metadata.gz: b179e2cf5b65329b6a7a40dec43a379fb294ddee0e58f389cd4ffc105f503265ad655cbf50067a9995b04358ddb9d9d0f8441d47e2639befcc4744c8af3c290c
7
+ data.tar.gz: 5b8443325b4afadffed3f836ba9a7385cbdfbc2cd03eebfee007bb0543ddb5f2b3c279d22a614fa80ecabb7f4a18f6a1292298567d444a84c9a364ef361bb626
data/lib/epsagon.rb CHANGED
@@ -13,6 +13,7 @@ require_relative 'instrumentation/faraday'
13
13
  require_relative 'instrumentation/aws_sdk'
14
14
  require_relative 'instrumentation/rails'
15
15
  require_relative 'instrumentation/postgres'
16
+ require_relative 'instrumentation/resque'
16
17
  require_relative 'util'
17
18
  require_relative 'epsagon_constants'
18
19
  require_relative 'exporter_extension'
@@ -20,10 +21,13 @@ require_relative 'arn_parser'
20
21
 
21
22
  Bundler.require
22
23
 
24
+
23
25
  # Epsagon tracing main entry point
24
26
  module Epsagon
25
27
  DEFAULT_BACKEND = 'opentelemetry.tc.epsagon.com:443/traces'
26
28
  DEFAULT_IGNORE_DOMAINS = ['newrelic.com'].freeze
29
+ MUTABLE_CONF_KEYS = Set.new([:metadata_only, :max_attribute_size, :ignore_domains])
30
+
27
31
 
28
32
  @@epsagon_config = nil
29
33
 
@@ -31,7 +35,29 @@ module Epsagon
31
35
 
32
36
  def init(**args)
33
37
  get_config.merge!(args)
38
+ validate(get_config)
34
39
  OpenTelemetry::SDK.configure
40
+ @@initialized = true
41
+ end
42
+
43
+ def validate(config)
44
+ Util.validate_value(config, :metadata_only, 'Must be a boolean') {|v| !!v == v}
45
+ Util.validate_value(config, :debug, 'Must be a boolean') {|v| !!v == v}
46
+ Util.validate_value(config, :token, 'Must be a valid Epsagon token') {|v| v.is_a? String and v.size > 10}
47
+ Util.validate_value(config, :app_name, 'Must be a String') {|v| v.is_a? String}
48
+ Util.validate_value(config, :max_attribute_size, 'Must be an Integer') {|v| v.is_a? Integer}
49
+ Util.validate_value(config, :ignore_domains, 'Must be iterable') {|v| v.respond_to?(:each)}
50
+ Util.validate_value(config, :ignore_domains, 'Must be iterable') {|v| v.respond_to?(:each)}
51
+ end
52
+
53
+ def set_config(**args)
54
+ unless args.keys.all? {|a| MUTABLE_CONF_KEYS.include?(a)}
55
+ raise ArgumentError("only #{MUTABLE_CONF_KEYS.to_a} are mutable after `Epsagon.init`")
56
+ end
57
+ Epsagon.init unless @@initialized
58
+ new_conf = get_config.merge(args)
59
+ validate(new_conf)
60
+ @@epsagon_config = new_conf
35
61
  end
36
62
 
37
63
  def get_config
@@ -85,28 +111,22 @@ module Epsagon
85
111
  configurator.use 'EpsagonRailsInstrumentation', { epsagon: get_config }
86
112
  configurator.use 'OpenTelemetry::Instrumentation::Sidekiq', { epsagon: get_config }
87
113
  configurator.use 'EpsagonPostgresInstrumentation', { epsagon: get_config }
114
+ configurator.use 'EpsagonResqueInstrumentation', { epsagon: get_config }
88
115
 
89
- if get_config[:debug]
90
- configurator.add_span_processor OpenTelemetry::SDK::Trace::Export::SimpleSpanProcessor.new(
91
- OpenTelemetry::Exporter::OTLP::Exporter.new(headers: {
92
- 'x-epsagon-token' => get_config[:token]
93
- },
94
- endpoint: get_config[:backend],
95
- insecure: get_config[:insecure] || false)
96
- )
97
116
 
117
+ if get_config[:debug]
98
118
  configurator.add_span_processor OpenTelemetry::SDK::Trace::Export::SimpleSpanProcessor.new(
99
119
  OpenTelemetry::SDK::Trace::Export::ConsoleSpanExporter.new
100
120
  )
101
- else
102
- configurator.add_span_processor OpenTelemetry::SDK::Trace::Export::BatchSpanProcessor.new(
103
- exporter: OpenTelemetry::Exporter::OTLP::Exporter.new(headers: {
104
- 'x-epsagon-token' => get_config[:token]
105
- },
106
- endpoint: get_config[:backend],
107
- insecure: get_config[:insecure] || false)
108
- )
109
121
  end
122
+
123
+ configurator.add_span_processor OpenTelemetry::SDK::Trace::Export::BatchSpanProcessor.new(
124
+ exporter: OpenTelemetry::Exporter::OTLP::Exporter.new(headers: {
125
+ 'x-epsagon-token' => get_config[:token]
126
+ },
127
+ endpoint: get_config[:backend],
128
+ insecure: get_config[:insecure] || false)
129
+ )
110
130
  end
111
131
  end
112
132
 
@@ -1,3 +1,3 @@
1
1
  module EpsagonConstants
2
- VERSION = '0.0.28'
2
+ VERSION = '0.0.29'
3
3
  end
@@ -7,6 +7,7 @@ require_relative '../epsagon_constants'
7
7
  class EpsagonAwsSdkInstrumentation < OpenTelemetry::Instrumentation::Base
8
8
  VERSION = EpsagonConstants::VERSION
9
9
  SERVICES = %w[
10
+ SecretsManager
10
11
  ACM
11
12
  APIGateway
12
13
  AppStream
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'opentelemetry/trace/status'
4
+ require 'action_controller/railtie'
4
5
 
5
6
  module QueueTime
6
7
  REQUEST_START = 'HTTP_X_REQUEST_START'
@@ -284,3 +285,48 @@ class EpsagonRailtie < ::Rails::Railtie
284
285
  )
285
286
  end
286
287
  end
288
+
289
+ module RackExtension
290
+ module_function
291
+
292
+ CURRENT_SPAN_KEY = OpenTelemetry::Context.create_key('current-span')
293
+
294
+ private_constant :CURRENT_SPAN_KEY
295
+
296
+ # Returns the current span from the current or provided context
297
+ #
298
+ # @param [optional Context] context The context to lookup the current
299
+ # {Span} from. Defaults to Context.current
300
+ def current_span(context = nil)
301
+ context ||= OpenTelemetry::Context.current
302
+ context.value(CURRENT_SPAN_KEY) || OpenTelemetry::Trace::Span::INVALID
303
+ end
304
+
305
+ # Returns a context containing the span, derived from the optional parent
306
+ # context, or the current context if one was not provided.
307
+ #
308
+ # @param [optional Context] context The context to use as the parent for
309
+ # the returned context
310
+ def context_with_span(span, parent_context: OpenTelemetry::Context.current)
311
+ parent_context.set_value(CURRENT_SPAN_KEY, span)
312
+ end
313
+
314
+ # Activates/deactivates the Span within the current Context, which makes the "current span"
315
+ # available implicitly.
316
+ #
317
+ # On exit, the Span that was active before calling this method will be reactivated.
318
+ #
319
+ # @param [Span] span the span to activate
320
+ # @yield [span, context] yields span and a context containing the span to the block.
321
+ def with_span(span)
322
+ OpenTelemetry::Context.with_value(CURRENT_SPAN_KEY, span) { |c, s| yield s, c }
323
+ end
324
+ end
325
+
326
+ module MetalPatch
327
+ def dispatch(name, request, response)
328
+ rack_span = RackExtension.current_span
329
+ # rack_span.name = "#{self.class.name}##{name}" if rack_span.context.valid? && !request.env['action_dispatch.exception']
330
+ super(name, request, response)
331
+ end
332
+ end
@@ -0,0 +1,113 @@
1
+ require 'json'
2
+
3
+ module EpsagonResqueModule
4
+ def self.prepended(base)
5
+ class << base
6
+ prepend ClassMethods
7
+ end
8
+ end
9
+
10
+ # Module to prepend to Resque singleton class
11
+ module ClassMethods
12
+ def push(queue, item)
13
+ epsagon_conf = config[:epsagon] || {}
14
+ # Check if the job is being wrapped by ActiveJob
15
+ # before retrieving the job class name
16
+ job_class = if item[:class] == 'ActiveJob::QueueAdapters::ResqueAdapter::JobWrapper' && item[:args][0]&.is_a?(Hash)
17
+ item[:args][0]['job_class']
18
+ else
19
+ item[:class]
20
+ end
21
+ attributes = {
22
+ 'operation' => 'enqueue',
23
+ 'messaging.system' => 'resque',
24
+ 'messaging.resque.job_class' => job_class,
25
+ 'messaging.destination' => queue.to_s,
26
+ 'messaging.destination_kind' => 'queue',
27
+ 'messaging.resque.redis_url' => Resque.redis.connection[:id]
28
+ }
29
+ unless epsagon_conf[:metadata_only]
30
+ attributes.merge!({
31
+ 'messaging.resque.args' => JSON.dump(item)
32
+ })
33
+ end
34
+
35
+ tracer.in_span(queue.to_s, attributes: attributes, kind: :producer) do
36
+ OpenTelemetry.propagation.text.inject(item)
37
+ super
38
+ end
39
+ end
40
+
41
+ def tracer
42
+ EpsagonResqueInstrumentation.instance.tracer
43
+ end
44
+
45
+ def config
46
+ EpsagonResqueInstrumentation.instance.config
47
+ end
48
+ end
49
+ end
50
+
51
+ module EpsagonResqueJob
52
+ def perform # rubocop:disable Metrics/AbcSize, Metrics/MethodLength, Metrics/CyclomaticComplexity, Metrics/PerceivedComplexity
53
+ inner_exception = nil
54
+ epsagon_conf = config[:epsagon] || {}
55
+ job_args = args || []
56
+
57
+ # Check if the job is being wrapped by ActiveJob
58
+ # before retrieving the job class name
59
+ job_class = if payload_class_name == 'ActiveJob::QueueAdapters::ResqueAdapter::JobWrapper' && job_args[0]&.is_a?(Hash)
60
+ job_args[0]['job_class']
61
+ else
62
+ payload_class_name
63
+ end
64
+
65
+ attributes = {
66
+ 'operation' => 'perform',
67
+ 'messaging.system' => 'resque',
68
+ 'messaging.resque.job_class' => job_class,
69
+ 'messaging.destination' => queue.to_s,
70
+ 'messaging.destination_kind' => 'queue',
71
+ 'messaging.resque.redis_url' => Resque.redis.connection[:id]
72
+ }
73
+ runner_attributes = {
74
+ 'type' => 'resque_worker',
75
+ 'messaging.resque.redis_url' => Resque.redis.connection[:id],
76
+
77
+ }
78
+
79
+ extracted_context = OpenTelemetry.propagation.text.extract(@payload)
80
+
81
+ unless epsagon_conf[:metadata_only]
82
+ attributes.merge!({
83
+ 'messaging.resque.args' => JSON.dump(args)
84
+ })
85
+ end
86
+ tracer.in_span(
87
+ queue.to_s,
88
+ attributes: attributes,
89
+ with_parent: extracted_context,
90
+ kind: :consumer
91
+ ) do |trigger_span|
92
+ tracer.in_span(job_class,
93
+ attributes: runner_attributes,
94
+ kind: :consumer
95
+ ) do |runner_span|
96
+ super
97
+ end
98
+ rescue Exception => e
99
+ inner_exception = e
100
+ end
101
+ raise inner_exception if inner_exception
102
+ end
103
+
104
+ private
105
+
106
+ def tracer
107
+ EpsagonResqueInstrumentation.instance.tracer
108
+ end
109
+
110
+ def config
111
+ EpsagonResqueInstrumentation.instance.config
112
+ end
113
+ end
@@ -1,58 +1,11 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'opentelemetry'
4
- require 'rails'
5
- require 'action_controller/railtie'
6
4
 
7
5
  require_relative '../util'
8
6
  require_relative '../epsagon_constants'
9
7
 
10
8
 
11
- module RackExtension
12
- module_function
13
-
14
- CURRENT_SPAN_KEY = OpenTelemetry::Context.create_key('current-span')
15
-
16
- private_constant :CURRENT_SPAN_KEY
17
-
18
- # Returns the current span from the current or provided context
19
- #
20
- # @param [optional Context] context The context to lookup the current
21
- # {Span} from. Defaults to Context.current
22
- def current_span(context = nil)
23
- context ||= OpenTelemetry::Context.current
24
- context.value(CURRENT_SPAN_KEY) || OpenTelemetry::Trace::Span::INVALID
25
- end
26
-
27
- # Returns a context containing the span, derived from the optional parent
28
- # context, or the current context if one was not provided.
29
- #
30
- # @param [optional Context] context The context to use as the parent for
31
- # the returned context
32
- def context_with_span(span, parent_context: OpenTelemetry::Context.current)
33
- parent_context.set_value(CURRENT_SPAN_KEY, span)
34
- end
35
-
36
- # Activates/deactivates the Span within the current Context, which makes the "current span"
37
- # available implicitly.
38
- #
39
- # On exit, the Span that was active before calling this method will be reactivated.
40
- #
41
- # @param [Span] span the span to activate
42
- # @yield [span, context] yields span and a context containing the span to the block.
43
- def with_span(span)
44
- OpenTelemetry::Context.with_value(CURRENT_SPAN_KEY, span) { |c, s| yield s, c }
45
- end
46
- end
47
-
48
- module MetalPatch
49
- def dispatch(name, request, response)
50
- rack_span = RackExtension.current_span
51
- # rack_span.name = "#{self.class.name}##{name}" if rack_span.context.valid? && !request.env['action_dispatch.exception']
52
- super(name, request, response)
53
- end
54
- end
55
-
56
9
  class EpsagonRailsInstrumentation < OpenTelemetry::Instrumentation::Base
57
10
  install do |_config|
58
11
  require_relative 'epsagon_rails_middleware'
@@ -0,0 +1,21 @@
1
+ class EpsagonResqueInstrumentation < OpenTelemetry::Instrumentation::Base
2
+ install do |_config|
3
+ require_dependencies
4
+ patch
5
+ end
6
+
7
+ present do
8
+ defined?(::Resque)
9
+ end
10
+
11
+ private
12
+
13
+ def patch
14
+ ::Resque.prepend(EpsagonResqueModule)
15
+ ::Resque::Job.prepend(EpsagonResqueJob)
16
+ end
17
+
18
+ def require_dependencies
19
+ require_relative 'epsagon_resque_job'
20
+ end
21
+ end
data/lib/util.rb CHANGED
@@ -4,6 +4,10 @@ require 'cgi'
4
4
 
5
5
  # Utilities for epsagon opentelemetry solution
6
6
  module Util
7
+ def self.validate_value(h, k, message, &block)
8
+ raise ArgumentError.new( "#{k} #{message}. Got #{h[k].class}: #{h[k]}" ) unless yield(h[k])
9
+ end
10
+
7
11
  def self.epsagon_query_attributes(query_string)
8
12
  if query_string&.include? '='
9
13
  { 'http.request.query_params' => CGI.parse(query_string).to_json }
@@ -25,7 +29,7 @@ module Util
25
29
  end
26
30
  return value
27
31
  elsif value.instance_of? String then
28
- value[0, max_size]
32
+ (value.frozen? ? value.dup : value).force_encoding('utf-8')[0, max_size]
29
33
  else
30
34
  value
31
35
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: epsagon
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.28
4
+ version: 0.0.29
5
5
  platform: ruby
6
6
  authors:
7
7
  - Epsagon
8
- autorequire:
8
+ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2021-06-10 00:00:00.000000000 Z
11
+ date: 2021-07-01 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: opentelemetry-api
@@ -98,7 +98,7 @@ description: 'Epsagon provides tracing to Ruby applications for the collection o
98
98
  distributed tracing and performance metrics to simplify complex architectures, eliminate
99
99
  manual work, visualize and correlate data to identify and fix problems fast.
100
100
 
101
- '
101
+ '
102
102
  email: info@epsagon.com
103
103
  executables: []
104
104
  extensions: []
@@ -112,10 +112,12 @@ files:
112
112
  - lib/instrumentation/aws_sdk_plugin.rb
113
113
  - lib/instrumentation/epsagon_faraday_middleware.rb
114
114
  - lib/instrumentation/epsagon_rails_middleware.rb
115
+ - lib/instrumentation/epsagon_resque_job.rb
115
116
  - lib/instrumentation/faraday.rb
116
117
  - lib/instrumentation/net_http.rb
117
118
  - lib/instrumentation/postgres.rb
118
119
  - lib/instrumentation/rails.rb
120
+ - lib/instrumentation/resque.rb
119
121
  - lib/instrumentation/sinatra.rb
120
122
  - lib/instrumentation/version.rb
121
123
  - lib/util.rb
@@ -123,7 +125,7 @@ homepage: https://github.com/epsagon/epsagon-ruby
123
125
  licenses:
124
126
  - MIT
125
127
  metadata: {}
126
- post_install_message:
128
+ post_install_message:
127
129
  rdoc_options: []
128
130
  require_paths:
129
131
  - lib
@@ -138,8 +140,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
138
140
  - !ruby/object:Gem::Version
139
141
  version: '0'
140
142
  requirements: []
141
- rubygems_version: 3.0.3
142
- signing_key:
143
+ rubygems_version: 3.1.4
144
+ signing_key:
143
145
  specification_version: 4
144
146
  summary: Epsagon provides tracing to Ruby applications for the collection of distributed
145
147
  tracing and performance metrics.