epsagon 0.0.22 → 0.0.27

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: 73d94ad48a8190e090468c50a16752fca266145da6dacaa9f050c7c9709f4a08
4
- data.tar.gz: ce72f581f6e6c7cb1923685c86619750dad00d3c902477e76fd7fd0fce9ddaeb
3
+ metadata.gz: 74f7a24306d3840c295345b485169ffa8bb4bfa2285f7d43ae69440d0b685e53
4
+ data.tar.gz: e043b98f22a6d804fc5692b8ab60c8eb1e05d74de736412fdfef58179afce086
5
5
  SHA512:
6
- metadata.gz: e1fcdc94c369e2dad0f40c6e50c788a7fde75f9f61f8188769fb24b6ca67b3dadc8519e2964720758129ffcd462fb1a573afa9e192503d72a2532b614f537fde
7
- data.tar.gz: 7fa7173eb476645bf4b3b116719bf9cdca8f84f63e301e82ff7925ea3240438ddb8ca00b6e84074a4540e5694518269d71bf5e243f47cfa50c1a9aab438f0365
6
+ metadata.gz: bb53cf200be779ba464a58a41bc62d03849ec8be6e161fca6a8d1f8594b124d38c3445155ac3e2582a318dc513415e6a236fd43f9a75b782f9642a29d5795c71
7
+ data.tar.gz: de6b92f478e04c362911160c29eec775553ef16f3714b9f6b78c062d60ee46161291b08d89f254f8bdf0f0d80884e4e41a79146114d87fc84ef758f246710c04
data/lib/epsagon.rb CHANGED
@@ -1,10 +1,11 @@
1
1
  # frozen_string_literal: true
2
-
2
+ require 'json'
3
3
  require 'rubygems'
4
4
  require 'net/http'
5
5
  require 'bundler/setup'
6
6
  require 'opentelemetry/sdk'
7
7
  require 'opentelemetry/exporter/otlp'
8
+ require 'opentelemetry/instrumentation/sidekiq'
8
9
 
9
10
  require_relative 'instrumentation/sinatra'
10
11
  require_relative 'instrumentation/net_http'
@@ -13,27 +14,38 @@ require_relative 'instrumentation/aws_sdk'
13
14
  require_relative 'instrumentation/rails'
14
15
  require_relative 'util'
15
16
  require_relative 'epsagon_constants'
17
+ require_relative 'exporter_extension'
16
18
 
17
19
  Bundler.require
18
20
 
19
21
  # Epsagon tracing main entry point
20
22
  module Epsagon
21
-
22
23
  DEFAULT_BACKEND = 'opentelemetry.tc.epsagon.com:443/traces'
24
+ DEFAULT_IGNORE_DOMAINS = ['newrelic.com']
23
25
 
24
- @@epsagon_config = {
25
- metadata_only: ENV['EPSAGON_METADATA']&.to_s&.downcase != 'false',
26
- debug: ENV['EPSAGON_DEBUG']&.to_s&.downcase == 'true',
27
- token: ENV['EPSAGON_TOKEN'],
28
- app_name: ENV['EPSAGON_APP_NAME'],
29
- max_attribute_size: ENV['EPSAGON_MAX_ATTRIBUTE_SIZE'] || 5000,
30
- backend: ENV['EPSAGON_BACKEND'] || DEFAULT_BACKEND
31
- }
26
+ @@epsagon_config = {}
32
27
 
33
28
  module_function
34
29
 
35
30
  def init(**args)
31
+ @@epsagon_config = {
32
+ metadata_only: ENV['EPSAGON_METADATA']&.to_s&.downcase != 'false',
33
+ debug: ENV['EPSAGON_DEBUG']&.to_s&.downcase == 'true',
34
+ token: ENV['EPSAGON_TOKEN'] || '',
35
+ app_name: ENV['EPSAGON_APP_NAME'] || '',
36
+ max_attribute_size: ENV['EPSAGON_MAX_ATTRIBUTE_SIZE'] || 5000,
37
+ backend: ENV['EPSAGON_BACKEND'] || DEFAULT_BACKEND,
38
+ ignore_domains: ENV['EPSAGON_IGNORE_DOMAINS'] || DEFAULT_IGNORE_DOMAINS
39
+ }
36
40
  @@epsagon_config.merge!(args)
41
+
42
+ Util.validate_value(@@epsagon_config, :metadata_only, 'Must be a boolean') {|v| !!v == v}
43
+ Util.validate_value(@@epsagon_config, :debug, 'Must be a boolean') {|v| !!v == v}
44
+ Util.validate_value(@@epsagon_config, :token, 'Must be a valid Epsagon token') {|v| v.is_a? String and v.size > 10}
45
+ Util.validate_value(@@epsagon_config, :app_name, 'Must be a String') {|v| v.is_a? String}
46
+ Util.validate_value(@@epsagon_config, :max_attribute_size, 'Must be an Integer') {|v| v.is_a? Integer}
47
+ Util.validate_value(@@epsagon_config, :ignore_domains, 'Must be iterable') {|v| v.respond_to?(:each)}
48
+
37
49
  OpenTelemetry::SDK.configure
38
50
  end
39
51
 
@@ -45,9 +57,10 @@ module Epsagon
45
57
 
46
58
  def epsagon_confs(configurator)
47
59
  configurator.resource = OpenTelemetry::SDK::Resources::Resource.telemetry_sdk.merge(
48
- OpenTelemetry::SDK::Resources::Resource.create({
60
+ OpenTelemetry::SDK::Resources::Resource.create({
49
61
  'application' => @@epsagon_config[:app_name],
50
- 'epsagon.version' => EpsagonConstants::VERSION
62
+ 'epsagon.version' => EpsagonConstants::VERSION,
63
+ 'epsagon.metadata_only' => @@epsagon_config[:metadata_only]
51
64
  })
52
65
  )
53
66
  configurator.use 'EpsagonSinatraInstrumentation', { epsagon: @@epsagon_config }
@@ -55,30 +68,21 @@ module Epsagon
55
68
  configurator.use 'EpsagonFaradayInstrumentation', { epsagon: @@epsagon_config }
56
69
  configurator.use 'EpsagonAwsSdkInstrumentation', { epsagon: @@epsagon_config }
57
70
  configurator.use 'EpsagonRailsInstrumentation', { epsagon: @@epsagon_config }
58
- configurator.use 'OpenTelemetry::Instrumentation::Sidekiq'
59
-
71
+ configurator.use 'OpenTelemetry::Instrumentation::Sidekiq', { epsagon: @@epsagon_config }
60
72
 
61
73
  if @@epsagon_config[:debug]
62
- configurator.add_span_processor OpenTelemetry::SDK::Trace::Export::SimpleSpanProcessor.new(
63
- OpenTelemetry::Exporter::OTLP::Exporter.new(headers: {
64
- 'x-epsagon-token' => @@epsagon_config[:token]
65
- },
66
- endpoint: @@epsagon_config[:backend],
67
- insecure: @@epsagon_config[:insecure] || false)
68
- )
69
-
70
74
  configurator.add_span_processor OpenTelemetry::SDK::Trace::Export::SimpleSpanProcessor.new(
71
75
  OpenTelemetry::SDK::Trace::Export::ConsoleSpanExporter.new
72
76
  )
73
- else
74
- configurator.add_span_processor OpenTelemetry::SDK::Trace::Export::BatchSpanProcessor.new(
75
- exporter: OpenTelemetry::Exporter::OTLP::Exporter.new(headers: {
76
- 'x-epsagon-token' => @@epsagon_config[:token]
77
- },
78
- endpoint: @@epsagon_config[:backend],
79
- insecure: @@epsagon_config[:insecure] || false)
80
- )
81
77
  end
78
+
79
+ configurator.add_span_processor OpenTelemetry::SDK::Trace::Export::BatchSpanProcessor.new(
80
+ exporter: OpenTelemetry::Exporter::OTLP::Exporter.new(headers: {
81
+ 'x-epsagon-token' => @@epsagon_config[:token]
82
+ },
83
+ endpoint: @@epsagon_config[:backend],
84
+ insecure: @@epsagon_config[:insecure] || false)
85
+ )
82
86
  end
83
87
  end
84
88
 
@@ -97,15 +101,89 @@ module SpanExtension
97
101
  def initialize(*args)
98
102
  super(*args)
99
103
  if @attributes
100
- @attributes = Hash[@attributes.map { |k,v|
104
+ @attributes = Hash[@attributes.select {|k,v| not BLANKS.include? v}.map { |k,v|
101
105
  [k, Util.trim_attr(v, Epsagon.get_config[:max_attribute_size])]
102
106
  }]
103
107
  end
104
-
108
+ end
109
+ end
110
+
111
+ module SidekiqClientMiddlewareExtension
112
+ def call(_worker_class, job, _queue, _redis_pool)
113
+ config = OpenTelemetry::Instrumentation::Sidekiq::Instrumentation.instance.config[:epsagon] || {}
114
+ attributes = {
115
+ 'operation' => job['at'] ? 'perform_at' : 'perform_async',
116
+ 'messaging.system' => 'sidekiq',
117
+ 'messaging.sidekiq.job_class' => job['wrapped']&.to_s || job['class'],
118
+ 'messaging.message_id' => job['jid'],
119
+ 'messaging.destination' => job['queue'],
120
+ 'messaging.destination_kind' => 'queue',
121
+ 'messaging.sidekiq.redis_url' => Sidekiq.options['url'] || Util.redis_default_url
122
+ }
123
+ unless config[:metadata_only]
124
+ attributes.merge!({
125
+ 'messaging.sidekiq.args' => JSON.dump(job['args'])
126
+ })
127
+ end
128
+ tracer.in_span(
129
+ job['queue'],
130
+ attributes: attributes,
131
+ kind: :producer
132
+ ) do |span|
133
+ OpenTelemetry.propagation.text.inject(job)
134
+ span.add_event('created_at', timestamp: job['created_at'])
135
+ Util.untraced {yield}
136
+ end
137
+ end
138
+ end
139
+
140
+ module SidekiqServerMiddlewareExtension
141
+ def call(_worker, msg, _queue)
142
+ inner_exception = nil
143
+ config = OpenTelemetry::Instrumentation::Sidekiq::Instrumentation.instance.config[:epsagon] || {}
144
+ parent_context = OpenTelemetry.propagation.text.extract(msg)
145
+ attributes = {
146
+ 'operation' => 'perform',
147
+ 'messaging.system' => 'sidekiq',
148
+ 'messaging.sidekiq.job_class' => msg['wrapped']&.to_s || msg['class'],
149
+ 'messaging.message_id' => msg['jid'],
150
+ 'messaging.destination' => msg['queue'],
151
+ 'messaging.destination_kind' => 'queue',
152
+ 'messaging.sidekiq.redis_url' => Sidekiq.options['url'] || Util.redis_default_url
153
+ }
154
+ runner_attributes = {
155
+ 'type' => 'sidekiq_worker',
156
+ 'messaging.sidekiq.redis_url' => Sidekiq.options['url'] || Util.redis_default_url,
157
+
158
+ }
159
+ unless config[:metadata_only]
160
+ attributes.merge!({
161
+ 'messaging.sidekiq.args' => JSON.dump(msg['args'])
162
+ })
163
+ end
164
+ tracer.in_span(
165
+ msg['queue'],
166
+ attributes: attributes,
167
+ with_parent: parent_context,
168
+ kind: :consumer
169
+ ) do |trigger_span|
170
+ trigger_span.add_event('created_at', timestamp: msg['created_at'])
171
+ trigger_span.add_event('enqueued_at', timestamp: msg['enqueued_at'])
172
+ tracer.in_span(msg['wrapped']&.to_s || msg['class'],
173
+ attributes: runner_attributes,
174
+ kind: :consumer
175
+ ) do |runner_span|
176
+ yield
177
+ end
178
+ rescue Exception => e
179
+ inner_exception = e
180
+ end
181
+ raise inner_exception if inner_exception
105
182
  end
106
183
  end
107
184
 
108
185
  # monkey patch to include epsagon confs
186
+
109
187
  module OpenTelemetry
110
188
  # monkey patch inner SDK module
111
189
  module SDK
@@ -122,4 +200,35 @@ module OpenTelemetry
122
200
  end
123
201
  end
124
202
  end
203
+ module Instrumentation
204
+ module Sidekiq
205
+ class Instrumentation
206
+ def add_server_middleware
207
+ ::Sidekiq.configure_server do |config|
208
+ config.server_middleware do |chain|
209
+ chain.add Middlewares::Server::TracerMiddleware
210
+ end
211
+ end
212
+
213
+ if defined?(::Sidekiq::Testing) # rubocop:disable Style/GuardClause
214
+ ::Sidekiq::Testing.server_middleware do |chain|
215
+ chain.add Middlewares::Server::TracerMiddleware
216
+ end
217
+ end
218
+ end
219
+ end
220
+ module Middlewares
221
+ module Client
222
+ class TracerMiddleware
223
+ prepend SidekiqClientMiddlewareExtension
224
+ end
225
+ end
226
+ module Server
227
+ class TracerMiddleware
228
+ prepend SidekiqServerMiddlewareExtension
229
+ end
230
+ end
231
+ end
232
+ end
233
+ end
125
234
  end
@@ -1,3 +1,3 @@
1
1
  module EpsagonConstants
2
- VERSION = '0.0.22'
2
+ VERSION = '0.0.27'
3
3
  end
@@ -0,0 +1,78 @@
1
+
2
+
3
+ module OpenTelemetry
4
+ module Exporter
5
+ module OTLP
6
+ # An OpenTelemetry trace exporter that sends spans over HTTP as Protobuf encoded OTLP ExportTraceServiceRequests.
7
+ class Exporter # rubocop:disable Metrics/ClassLength
8
+ def send_bytes(bytes, timeout:) # rubocop:disable Metrics/AbcSize, Metrics/CyclomaticComplexity, Metrics/MethodLength, Metrics/PerceivedComplexity
9
+ retry_count = 0
10
+ timeout ||= @timeout
11
+ start_time = Time.now
12
+ untraced do # rubocop:disable Metrics/BlockLength
13
+ request = Net::HTTP::Post.new(@path)
14
+ request.body = if @compression == 'gzip'
15
+ request.add_field('Content-Encoding', 'gzip')
16
+ Zlib.gzip(bytes)
17
+ else
18
+ bytes
19
+ end
20
+ request.add_field('Content-Type', 'application/x-protobuf')
21
+ @headers&.each { |key, value| request.add_field(key, value) }
22
+
23
+ remaining_timeout = OpenTelemetry::Common::Utilities.maybe_timeout(timeout, start_time)
24
+ return TIMEOUT if remaining_timeout.zero?
25
+
26
+ @http.open_timeout = remaining_timeout
27
+ @http.read_timeout = remaining_timeout
28
+ @http.write_timeout = remaining_timeout if WRITE_TIMEOUT_SUPPORTED
29
+ @http.start unless @http.started?
30
+ response = measure_request_duration { @http.request(request) }
31
+
32
+ case response
33
+ when Net::HTTPOK
34
+ response.body # Read and discard body
35
+ SUCCESS
36
+ when Net::HTTPServiceUnavailable, Net::HTTPTooManyRequests
37
+ response.body # Read and discard body
38
+ redo if backoff?(retry_after: response['Retry-After'], retry_count: retry_count += 1, reason: response.code)
39
+ FAILURE
40
+ when Net::HTTPRequestTimeOut, Net::HTTPGatewayTimeOut, Net::HTTPBadGateway
41
+ response.body # Read and discard body
42
+ redo if backoff?(retry_count: retry_count += 1, reason: response.code)
43
+ FAILURE
44
+ when Net::HTTPBadRequest, Net::HTTPClientError, Net::HTTPServerError
45
+ # TODO: decode the body as a google.rpc.Status Protobuf-encoded message when https://github.com/open-telemetry/opentelemetry-collector/issues/1357 is fixed.
46
+ response.body # Read and discard body
47
+ FAILURE
48
+ when Net::HTTPRedirection
49
+ @http.finish
50
+ handle_redirect(response['location'])
51
+ redo if backoff?(retry_after: 0, retry_count: retry_count += 1, reason: response.code)
52
+ else
53
+ @http.finish
54
+ FAILURE
55
+ end
56
+ rescue Net::OpenTimeout, Net::ReadTimeout
57
+ puts "Epsagon: timeout while sending trace" if Epsagon.get_config[:debug]
58
+ retry if backoff?(retry_count: retry_count += 1, reason: 'timeout')
59
+ return FAILURE
60
+ ensure
61
+ if Epsagon.get_config[:debug] && response && response.code.to_i >= 400
62
+ puts "Epsagon: Error while sending trace:"
63
+ puts "#{response.code} #{response.class.name} #{response.message}"
64
+ puts "Headers: #{response.to_hash.inspect}"
65
+ puts response.body
66
+ end
67
+ end
68
+ ensure
69
+ # Reset timeouts to defaults for the next call.
70
+ @http.open_timeout = @timeout
71
+ @http.read_timeout = @timeout
72
+ @http.write_timeout = @timeout if WRITE_TIMEOUT_SUPPORTED
73
+ end
74
+ end
75
+ end
76
+ end
77
+ end
78
+
@@ -4,6 +4,7 @@ require 'aws-sdk-core'
4
4
  require 'opentelemetry/common'
5
5
  require 'opentelemetry/sdk'
6
6
 
7
+
7
8
  def untraced(&block)
8
9
  OpenTelemetry::Trace.with_span(OpenTelemetry::Trace::Span.new, &block)
9
10
  end
@@ -17,14 +18,120 @@ end
17
18
 
18
19
  # Generates Spans for all uses of AWS SDK
19
20
  class EpsagonAwsHandler < Seahorse::Client::Handler
21
+ SPAN_KIND = {
22
+ 'ReceiveMessage' => :consumer,
23
+ 'SendMessage' => :producer,
24
+ 'SendMessageBatch' => :producer,
25
+ 'Publish' => :producer,
26
+ }
27
+
20
28
  def call(context)
21
- tracer.in_span('', kind: :client) do |span|
29
+ span_name = ''
30
+ span_kind = :client
31
+ attributes = {
32
+ 'aws.service' => context.client.class.to_s.split('::')[1].downcase,
33
+ 'aws.operation' => context.operation.name
34
+ }
35
+ attributes['aws.region'] = context.client.config.region unless attributes['aws.service'] == 's3'
36
+
37
+ span_kind = SPAN_KIND[attributes['aws.operation']] || span_kind
38
+ if attributes['aws.service'] == 's3'
39
+ attributes['aws.s3.bucket'] = context.params[:bucket]
40
+ span_name = attributes['aws.s3.bucket'] if attributes['aws.s3.bucket']
41
+ attributes['aws.s3.key'] = context.params[:key]
42
+ attributes['aws.s3.copy_source'] = context.params[:copy_source]
43
+ elsif attributes['aws.service'] == 'sqs'
44
+ queue_url = context.params[:queue_url]
45
+ queue_name = queue_url ? queue_url[queue_url.rindex('/')+1..-1] : context.params[:queue_name]
46
+ attributes['aws.sqs.max_number_of_messages'] = context.params[:max_number_of_messages]
47
+ attributes['aws.sqs.wait_time_seconds'] = context.params[:wait_time_seconds]
48
+ attributes['aws.sqs.visibility_timeout'] = context.params[:visibility_timeout]
49
+ attributes['aws.sqs.message_id'] = context.params[:message_id]
50
+ if queue_name
51
+ attributes['aws.sqs.queue_name'] = queue_name
52
+ span_name = attributes['aws.sqs.queue_name'] if attributes['aws.sqs.queue_name']
53
+ end
54
+ unless config[:epsagon][:metadata_only]
55
+ if attributes['aws.operation'] == 'SendMessageBatch'
56
+ messages_attributes = context.params[:entries].map do |m|
57
+ record = {
58
+ 'message_attributes' => m[:message_attributes].map {|k,v| [k, v.to_h]},
59
+ 'message_body' => m[:message_body],
60
+ }
61
+ end
62
+ attributes['aws.sqs.record'] = JSON.dump(messages_attributes) if messages_attributes
63
+ end
64
+ attributes['aws.sqs.record.message_body'] = context.params[:message_body]
65
+ attributes['aws.sqs.record.message_attributes'] = JSON.dump(context.params[:message_attributes]) if context.params[:message_attributes]
66
+ end
67
+ elsif attributes['aws.service'] == 'sns'
68
+ topic_arn = context.params[:topic_arn]
69
+ topic_name = topic_arn ? topic_arn[topic_arn.rindex(':')+1..-1] : context.params[:name]
70
+ span_name = attributes['aws.sns.topic_name'] = topic_name if topic_name
71
+ unless config[:epsagon][:metadata_only]
72
+ attributes['aws.sns.subject'] = context.params[:subject]
73
+ attributes['aws.sns.message'] = context.params[:message]
74
+ attributes['aws.sns.message_attributes'] = JSON.dump(context.params[:message_attributes]) if context.params[:message_attributes]
75
+ end
76
+ end
77
+ tracer.in_span(span_name, kind: span_kind, attributes: attributes) do |span|
22
78
  untraced do
23
- @handler.call(context).tap do
24
- span.set_attribute('aws.service', context.client.class.to_s.split('::')[1].downcase)
25
- span.set_attribute('aws.operation', context.operation.name)
26
- span.set_attribute('aws.region', context.client.config.region)
79
+ @handler.call(context).tap do |result|
80
+ if attributes['aws.service'] == 's3'
81
+ modified = context.http_response.headers[:'last-modified']
82
+ reformatted_modified = modified ?
83
+ Time.strptime(modified, '%a, %d %b %Y %H:%M:%S %Z')
84
+ .strftime('%Y-%m-%dT%H:%M:%SZ') :
85
+ nil
86
+ if context.operation.name == 'GetObject'
87
+ span.set_attribute('aws.s3.content_length', context.http_response.headers[:'content-length']&.to_i)
88
+ end
89
+ span.set_attribute('aws.s3.etag', context.http_response.headers[:etag]&.tr('"',''))
90
+ span.set_attribute('aws.s3.last_modified', reformatted_modified)
91
+ elsif attributes['aws.service'] == 'sqs'
92
+ if context.operation.name == 'SendMessage'
93
+ span.set_attribute('aws.sqs.record.message_id', result.message_id)
94
+ end
95
+ if context.operation.name == 'SendMessageBatch'
96
+ messages_attributes = result.successful.map do |m|
97
+ record = {'message_id' => m.message_id}
98
+ unless config[:epsagon][:metadata_only]
99
+ context.params[:entries].each do |e|
100
+ record.merge!({
101
+ 'message_attributes' => e[:message_attributes].map {|k,v| [k, v.to_h]},
102
+ 'message_body' => e[:message_body],
103
+ }) if e[:id] == m.id
104
+ end
105
+ end
106
+ record
107
+ end
108
+ span.set_attribute('aws.sqs.record', JSON.dump(messages_attributes)) if messages_attributes
109
+ end
110
+ if context.operation.name == 'ReceiveMessage'
111
+ messages_attributes = result.messages.map do |m|
112
+ record = {
113
+ 'message_id' => m.message_id,
114
+ 'attributes' => {
115
+ 'sender_id' => m.attributes['SenderId'],
116
+ 'sent_timestamp' => m.attributes['SentTimestamp'],
117
+ 'aws_trace_header' => m.attributes['AWSTraceHeader'],
118
+ }
119
+ }
120
+ unless config[:epsagon][:metadata_only]
121
+ record['message_attributes'] = m.message_attributes.map {|k,v| [k, v.to_h]}
122
+ record['message_body'] = m.body
123
+ end
124
+ record
125
+ end
126
+ span.set_attribute('aws.sqs.record', JSON.dump(messages_attributes)) if messages_attributes
127
+ end
128
+ elsif attributes['aws.service'] == 'sns'
129
+ span.set_attribute('aws.sns.message_id', result.message_id) if context.operation.name == 'Publish'
130
+ end
27
131
  span.set_attribute('http.status_code', context.http_response.status_code)
132
+ span.status = OpenTelemetry::Trace::Status.http_to_status(
133
+ context.http_response.status_code
134
+ )
28
135
  end
29
136
  end
30
137
  end
@@ -33,4 +140,8 @@ class EpsagonAwsHandler < Seahorse::Client::Handler
33
140
  def tracer
34
141
  EpsagonAwsSdkInstrumentation.instance.tracer()
35
142
  end
143
+
144
+ def config
145
+ EpsagonAwsSdkInstrumentation.instance.config
146
+ end
36
147
  end
@@ -37,11 +37,11 @@ class EpsagonFaradayMiddleware < ::Faraday::Middleware
37
37
  unless config[:epsagon][:metadata_only]
38
38
  attributes.merge!(Util.epsagon_query_attributes(env.url.query))
39
39
  attributes.merge!({
40
- 'http.request.path_params' => path_params,
41
- 'http.request.headers' => env.request_headers.to_json,
42
- 'http.request.body' => env.body,
43
- 'http.request.headers.User-Agent' => env.request_headers['User-Agent']
44
- })
40
+ 'http.request.path_params' => path_params,
41
+ 'http.request.headers' => env.request_headers.to_json,
42
+ 'http.request.body' => env.body,
43
+ 'http.request.headers.User-Agent' => env.request_headers['User-Agent']
44
+ })
45
45
  end
46
46
 
47
47
  tracer.in_span(
@@ -130,7 +130,7 @@ class EpsagonRackMiddleware
130
130
  def request_span_attributes(env:)
131
131
  request = Rack::Request.new(env)
132
132
  path, path_params = request.path.split(';')
133
- request_headers = JSON.generate(Hash[*env.select { |k, _v| k.start_with? 'HTTP_' }
133
+ request_headers = JSON.generate(Hash[*env.select { |k, _v| k.to_s.start_with? 'HTTP_' }
134
134
  .collect { |k, v| [k.sub(/^HTTP_/, ''), v] }
135
135
  .collect { |k, v| [k.split('_').collect(&:capitalize).join('-'), v] }
136
136
  .sort
@@ -17,6 +17,7 @@ module EpsagonNetHTTPExtension
17
17
  def request(req, body = nil, &block)
18
18
  # Do not trace recursive call for starting the connection
19
19
  return super(req, body, &block) unless started?
20
+ return super(req, body, &block) if config[:epsagon][:ignore_domains].any? {|d| @address.include? d}
20
21
 
21
22
  attributes = Hash[OpenTelemetry::Common::HTTP::ClientContext.attributes]
22
23
  path_with_params, query = req.path.split('?')
@@ -55,6 +56,7 @@ module EpsagonNetHTTPExtension
55
56
 
56
57
  def annotate_span_with_response!(span, response)
57
58
  return unless response&.code
59
+ return unless span.respond_to?(:set_attribute)
58
60
 
59
61
  status_code = response.code.to_i
60
62
 
File without changes
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,9 +29,18 @@ 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
32
36
  end
37
+
38
+ def self.redis_default_url
39
+ @@redis_default_url ||= "#{Redis::Client::DEFAULTS[:scheme]}://#{Redis::Client::DEFAULTS[:host]}:#{Redis::Client::DEFAULTS[:port]}/#{Redis::Client::DEFAULTS[:db]}"
40
+ end
41
+
42
+ def self.untraced(&block)
43
+ OpenTelemetry::Trace.with_span(OpenTelemetry::Trace::Span.new, &block)
44
+ end
45
+
33
46
  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.22
4
+ version: 0.0.27
5
5
  platform: ruby
6
6
  authors:
7
7
  - Epsagon
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2021-05-04 00:00:00.000000000 Z
11
+ date: 2021-06-08 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: opentelemetry-api
@@ -92,6 +92,7 @@ extra_rdoc_files: []
92
92
  files:
93
93
  - lib/epsagon.rb
94
94
  - lib/epsagon_constants.rb
95
+ - lib/exporter_extension.rb
95
96
  - lib/instrumentation/aws_sdk.rb
96
97
  - lib/instrumentation/aws_sdk_plugin.rb
97
98
  - lib/instrumentation/epsagon_faraday_middleware.rb
@@ -100,6 +101,7 @@ files:
100
101
  - lib/instrumentation/net_http.rb
101
102
  - lib/instrumentation/rails.rb
102
103
  - lib/instrumentation/sinatra.rb
104
+ - lib/instrumentation/version.rb
103
105
  - lib/util.rb
104
106
  homepage: https://github.com/epsagon/epsagon-ruby
105
107
  licenses: