epsagon 0.0.28 → 0.0.29

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