elastic-apm 0.5.1 → 0.6.1

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.

Potentially problematic release.


This version of elastic-apm might be problematic. Click here for more details.

checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 2efd1832264da2eb5036fdbf6b3fcb26e47b0f3272779caed2d92c8315a0db7a
4
- data.tar.gz: c9f91f38ae3433566cdc16584ee862e7c8ac7f364c0fd05879c96ef87a6406d5
3
+ metadata.gz: da4eac00d32100e5f82cd8dfe3042278eb4f81865c797ee743b4e2fa89269c18
4
+ data.tar.gz: bd07824d56e89d645b08cfb2b5090a6ec4fcefbf360cb559420cd7f050629cd1
5
5
  SHA512:
6
- metadata.gz: 29c6d93909f2726527d09a62345ef920aba5b301a0fef1bd72e7739e2ace645afb22fd73039360e321e1d2cf68ac26745341deeada4f2e2aeb37e5f7bd3d70d3
7
- data.tar.gz: 48e84149066cb00fd66f2dc03d275626e7b4a9750b1cd97c6530f5cbf0c7d70f1fade887b228ad9d6e178f5e51c7225673e0f0a1a97a2ffe25dae9cab44721df
6
+ metadata.gz: 1b57fe86030e4f0aeb667cf886ac45708c4c41d26a0e85ed7736ee8c98319763cc15e0504bee2407f29a4503e98c32899061ad7a5b7fd035fcc5ed527cd7b410
7
+ data.tar.gz: bfc683d303d7b0a7c5400cd064c76f12dca648eeb6c6cecf73bc25cdb6bae4253e32e708aa57168aa9f0963e5531c7e4cdba10dd5f9d10782197c6bc3426e382
data/Gemfile CHANGED
@@ -40,7 +40,7 @@ else
40
40
  end
41
41
 
42
42
  # doesn't work with Rails master, so skip testing
43
- gem 'delayed_job' unless [framework, version] == %w[rails master]
43
+ gem 'delayed_job', '~> 4' unless [framework, version] == %w[rails 5.2]
44
44
 
45
45
  gem 'rails' if framework == 'sinatra'
46
46
  gem 'sinatra' if framework == 'rails'
@@ -7,7 +7,7 @@ require 'elastic_apm/error'
7
7
  require 'elastic_apm/http'
8
8
  require 'elastic_apm/injectors'
9
9
  require 'elastic_apm/serializers'
10
- require 'elastic_apm/worker'
10
+ require 'elastic_apm/timed_worker'
11
11
 
12
12
  module ElasticAPM
13
13
  # rubocop:disable Metrics/ClassLength
@@ -59,20 +59,17 @@ module ElasticAPM
59
59
  def initialize(config)
60
60
  @config = config
61
61
 
62
+ @messages = Queue.new
63
+ @pending_transactions = Queue.new
62
64
  @http = Http.new(config)
63
- @queue = Queue.new
64
65
 
65
66
  @instrumenter = Instrumenter.new(config, self)
66
67
  @context_builder = ContextBuilder.new(config)
67
68
  @error_builder = ErrorBuilder.new(config)
68
-
69
- @serializers = Struct.new(:transactions, :errors).new(
70
- Serializers::Transactions.new(config),
71
- Serializers::Errors.new(config)
72
- )
73
69
  end
74
70
 
75
- attr_reader :config, :queue, :instrumenter, :context_builder, :http
71
+ attr_reader :config, :messages, :pending_transactions, :instrumenter,
72
+ :context_builder, :http
76
73
 
77
74
  def start
78
75
  debug '[%s] Starting agent, reporting to %s', VERSION, config.server_url
@@ -98,20 +95,16 @@ module ElasticAPM
98
95
  stop
99
96
  end
100
97
 
101
- def enqueue_transactions(transactions)
98
+ def enqueue_transaction(transaction)
102
99
  boot_worker unless worker_running?
103
100
 
104
- data = @serializers.transactions.build_all(transactions)
105
- @queue << Worker::Request.new('/v1/transactions', data)
106
- transactions
101
+ pending_transactions.push(transaction)
107
102
  end
108
103
 
109
- def enqueue_errors(errors)
104
+ def enqueue_error(error)
110
105
  boot_worker unless worker_running?
111
106
 
112
- data = @serializers.errors.build_all(errors)
113
- @queue << Worker::Request.new('/v1/errors', data)
114
- errors
107
+ messages.push(TimedWorker::ErrorMsg.new(error))
115
108
  end
116
109
 
117
110
  # instrumentation
@@ -141,7 +134,7 @@ module ElasticAPM
141
134
  exception,
142
135
  handled: handled
143
136
  )
144
- enqueue_errors error
137
+ enqueue_error error
145
138
  end
146
139
 
147
140
  def report_message(message, backtrace: nil, **attrs)
@@ -150,7 +143,7 @@ module ElasticAPM
150
143
  backtrace: backtrace,
151
144
  **attrs
152
145
  )
153
- enqueue_errors error
146
+ enqueue_error error
154
147
  end
155
148
 
156
149
  # context
@@ -181,12 +174,17 @@ module ElasticAPM
181
174
  debug 'Booting worker'
182
175
 
183
176
  @worker_thread = Thread.new do
184
- Worker.new(@config, @queue, @http).run_forever
177
+ TimedWorker.new(
178
+ config,
179
+ messages,
180
+ pending_transactions,
181
+ http
182
+ ).run_forever
185
183
  end
186
184
  end
187
185
 
188
186
  def kill_worker
189
- @queue << Worker::StopMessage.new
187
+ messages << TimedWorker::StopMsg.new
190
188
 
191
189
  if @worker_thread && !@worker_thread.join(5) # 5 secs
192
190
  raise 'Failed to wait for worker, not all messages sent'
@@ -8,7 +8,6 @@ module ElasticAPM
8
8
  class Config
9
9
  DEFAULTS = {
10
10
  config_file: 'config/elastic_apm.yml',
11
- worker_process: false,
12
11
 
13
12
  server_url: 'http://localhost:8200',
14
13
  secret_token: nil,
@@ -31,18 +30,21 @@ module ElasticAPM
31
30
  transaction_max_spans: 500,
32
31
  filter_exception_types: [],
33
32
 
34
- http_timeout: 10,
35
- http_open_timeout: 10,
33
+ http_read_timeout: 120,
34
+ http_open_timeout: 60,
36
35
  debug_transactions: false,
37
36
  debug_http: false,
38
37
  verify_server_cert: true,
38
+ http_compression: true,
39
+ compression_minimum_size: 1024 * 5,
40
+ compression_level: 6,
39
41
 
40
42
  source_lines_error_app_frames: 5,
41
43
  source_lines_span_app_frames: 5,
42
44
  source_lines_error_library_frames: 0,
43
45
  source_lines_span_library_frames: 0,
44
46
 
45
- disabled_injectors: %w[],
47
+ disabled_injectors: %w[json],
46
48
 
47
49
  current_user_id_method: :id,
48
50
  current_user_email_method: :email,
@@ -94,7 +96,6 @@ module ElasticAPM
94
96
  end
95
97
 
96
98
  attr_accessor :config_file
97
- attr_accessor :worker_process
98
99
 
99
100
  attr_accessor :server_url
100
101
  attr_accessor :secret_token
@@ -117,10 +118,13 @@ module ElasticAPM
117
118
  attr_accessor :verify_server_cert
118
119
  attr_accessor :filter_exception_types
119
120
 
120
- attr_accessor :http_timeout
121
+ attr_accessor :http_read_timeout
121
122
  attr_accessor :http_open_timeout
122
123
  attr_accessor :debug_transactions
123
124
  attr_accessor :debug_http
125
+ attr_accessor :http_compression
126
+ attr_accessor :compression_minimum_size
127
+ attr_accessor :compression_level
124
128
 
125
129
  attr_accessor :source_lines_error_app_frames
126
130
  attr_accessor :source_lines_span_app_frames
@@ -1,6 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require 'net/http'
4
+ require 'zlib'
4
5
 
5
6
  require 'elastic_apm/service_info'
6
7
  require 'elastic_apm/system_info'
@@ -32,14 +33,15 @@ module ElasticAPM
32
33
  def post(path, payload = {})
33
34
  payload.merge! @base_payload
34
35
  filters.apply(payload)
36
+
35
37
  request = prepare_request path, payload.to_json
36
38
  response = @adapter.perform request
37
39
 
38
40
  status = response.code.to_i
39
41
  return response if status >= 200 && status <= 299
40
42
 
41
- error "POST returned an unsuccessful status code (#{response.code})"
42
- error response.body
43
+ error 'POST returned an unsuccessful status code (%d)', response.code
44
+ error "apm-server's response: %s", response.body
43
45
 
44
46
  response
45
47
  end
@@ -51,12 +53,25 @@ module ElasticAPM
51
53
  req['Accept'] = ACCEPT
52
54
  req['Content-Type'] = CONTENT_TYPE
53
55
  req['User-Agent'] = USER_AGENT
54
- req['Content-Length'] = data.bytesize.to_s
55
56
 
56
57
  if (token = @config.secret_token)
57
58
  req['Authorization'] = "Bearer #{token}"
58
59
  end
59
60
 
61
+ prepare_request_body! req, data
62
+ end
63
+ end
64
+
65
+ def prepare_request_body!(req, data)
66
+ if @config.http_compression &&
67
+ data.bytesize > @config.compression_minimum_size
68
+ deflated = Zlib.deflate data, @config.compression_level
69
+
70
+ req['Content-Encoding'] = 'deflate'
71
+ req['Content-Length'] = deflated.bytesize.to_s
72
+ req.body = deflated
73
+ else
74
+ req['Content-Length'] = data.bytesize.to_s
60
75
  req.body = data
61
76
  end
62
77
  end
@@ -92,7 +107,7 @@ module ElasticAPM
92
107
  http = Net::HTTP.new server_uri.host, server_uri.port
93
108
  http.use_ssl = @config.use_ssl?
94
109
  http.verify_mode = verify_mode
95
- http.read_timeout = @config.http_timeout
110
+ http.read_timeout = @config.http_read_timeout
96
111
  http.open_timeout = @config.http_open_timeout
97
112
 
98
113
  if @config.debug_http
@@ -5,27 +5,21 @@ module ElasticAPM
5
5
  module Injectors
6
6
  # @api private
7
7
  class RedisInjector
8
- # rubocop:disable Metrics/MethodLength
9
8
  def install
10
9
  ::Redis::Client.class_eval do
11
10
  alias call_without_apm call
12
11
 
13
12
  def call(command, &block)
14
13
  name = command[0].upcase
15
- statement =
16
- format('%s %s', name, command[1..command.length].join(' '))
17
- context = Span::Context.new(
18
- statement: statement,
19
- type: 'redis'
20
- )
21
14
 
22
- ElasticAPM.span(name.to_s, 'db.redis', context: context) do
15
+ return call_without_apm(command, &block) if command[0] == :auth
16
+
17
+ ElasticAPM.span(name.to_s, 'db.redis') do
23
18
  call_without_apm(command, &block)
24
19
  end
25
20
  end
26
21
  end
27
22
  end
28
- # rubocop:enable Metrics/MethodLength
29
23
  end
30
24
 
31
25
  register 'Redis', 'redis', RedisInjector.new
@@ -33,9 +33,6 @@ module ElasticAPM
33
33
  @transaction_info = TransactionInfo.new
34
34
 
35
35
  @subscriber = subscriber_class.new(config)
36
-
37
- @pending_transactions = []
38
- @last_sent_transactions = Time.now.utc
39
36
  end
40
37
 
41
38
  attr_reader :config, :pending_transactions
@@ -64,12 +61,10 @@ module ElasticAPM
64
61
  return transaction
65
62
  end
66
63
 
67
- sample = rand <= config.transaction_sample_rate
68
-
69
64
  if args.last.is_a? Hash
70
- args.last[:sampled] = sample
65
+ args.last[:sampled] = random_sample?
71
66
  else
72
- args.push(sampled: sample)
67
+ args.push(sampled: random_sample?)
73
68
  end
74
69
 
75
70
  transaction = Transaction.new self, *args
@@ -88,6 +83,10 @@ module ElasticAPM
88
83
  end
89
84
  # rubocop:enable Metrics/MethodLength
90
85
 
86
+ def random_sample?
87
+ rand <= config.transaction_sample_rate
88
+ end
89
+
91
90
  def span(*args, &block)
92
91
  unless current_transaction
93
92
  return yield if block_given?
@@ -113,36 +112,10 @@ module ElasticAPM
113
112
  end
114
113
 
115
114
  def submit_transaction(transaction)
116
- @pending_transactions << transaction
117
-
118
- if config.debug_transactions
119
- debug('Submitted transaction:') { Util.inspect_transaction transaction }
120
- end
121
-
122
- return unless should_flush_transactions?
123
- flush_transactions
124
- end
125
-
126
- def should_flush_transactions?
127
- interval = config.flush_interval
128
-
129
- return true if interval.nil?
130
- return true if @pending_transactions.length >= config.max_queue_size
131
-
132
- Time.now.utc - @last_sent_transactions >= interval
133
- end
134
-
135
- def flush_transactions
136
- return if @pending_transactions.empty?
137
-
138
- debug 'Sending %i transactions', @pending_transactions.length
139
-
140
- @agent.enqueue_transactions @pending_transactions
141
-
142
- @last_sent_transactions = Time.now.utc
143
- @pending_transactions = []
115
+ @agent.enqueue_transaction transaction
144
116
 
145
- true
117
+ return unless config.debug_transactions
118
+ debug('Submitted transaction:') { Util.inspect_transaction transaction }
146
119
  end
147
120
 
148
121
  def inspect
@@ -9,7 +9,7 @@ module ElasticAPM
9
9
  JAVA_FORMAT = /^(.+)\.([^\.]+)\(([^\:]+)\:(\d+)\)$/
10
10
  RUBY_FORMAT = /^(.+?):(\d+)(?::in `(.+?)')?$/
11
11
 
12
- RUBY_VERS_REGEX = %r{ruby[-/](\d+\.)+\d}
12
+ RUBY_VERS_REGEX = %r{ruby(/gems)?[-/](\d+\.)+\d}
13
13
  JRUBY_ORG_REGEX = %r{org/jruby}
14
14
 
15
15
  def initialize(backtrace)
@@ -61,7 +61,7 @@ module ElasticAPM
61
61
  return false unless abs_path
62
62
 
63
63
  if abs_path.start_with?(config.root_path)
64
- return true if abs_path.match(config.root_path + '/vendor')
64
+ return true if abs_path.start_with?(config.root_path + '/vendor')
65
65
  return false
66
66
  end
67
67
 
@@ -0,0 +1,117 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ElasticAPM
4
+ # @api private
5
+ class TimedWorker
6
+ include Log
7
+
8
+ SLEEP_INTERVAL = 0.1
9
+
10
+ # @api private
11
+ class StopMsg
12
+ end
13
+
14
+ # @api private
15
+ class ErrorMsg
16
+ def initialize(error)
17
+ @error = error
18
+ end
19
+
20
+ attr_reader :error
21
+ end
22
+
23
+ def initialize(config, messages, pending_transactions, adapter)
24
+ @config = config
25
+ @messages = messages
26
+ @pending_transactions = pending_transactions
27
+ @adapter = adapter
28
+
29
+ @last_sent_transactions = Time.now.utc
30
+
31
+ @serializers = Struct.new(:transactions, :errors).new(
32
+ Serializers::Transactions.new(config),
33
+ Serializers::Errors.new(config)
34
+ )
35
+ end
36
+
37
+ attr_reader :config, :messages, :pending_transactions
38
+
39
+ def run_forever
40
+ loop do
41
+ run_once
42
+ sleep SLEEP_INTERVAL
43
+ end
44
+ end
45
+
46
+ def run_once
47
+ collect_and_send_transactions if should_flush_transactions?
48
+ process_messages
49
+ end
50
+
51
+ private
52
+
53
+ # rubocop:disable Metrics/MethodLength
54
+ def process_messages
55
+ should_exit = false
56
+
57
+ while (msg = messages.pop(true))
58
+ case msg
59
+ when ErrorMsg
60
+ post_error msg
61
+ when StopMsg
62
+ should_exit = true
63
+
64
+ # empty collected transactions before exiting
65
+ collect_and_send_transactions
66
+ end
67
+ end
68
+ rescue ThreadError # queue empty
69
+ Thread.exit if should_exit
70
+ end
71
+ # rubocop:enable Metrics/MethodLength
72
+
73
+ def post_error(msg)
74
+ payload = @serializers.errors.build_all([msg.error])
75
+ @adapter.post('/v1/errors', payload)
76
+ end
77
+
78
+ def collect_and_send_transactions
79
+ return if pending_transactions.empty?
80
+
81
+ transactions = collect_batched_transactions
82
+
83
+ payload = @serializers.transactions.build_all(transactions)
84
+
85
+ begin
86
+ @adapter.post('/v1/transactions', payload)
87
+ rescue ::Exception => e
88
+ fatal 'Failed posting: %s', e.inspect
89
+ debug e.backtrace.join("\n")
90
+ nil
91
+ end
92
+ end
93
+
94
+ def collect_batched_transactions
95
+ batch = []
96
+
97
+ begin
98
+ while (transaction = pending_transactions.pop(true)) &&
99
+ batch.length <= config.max_queue_size
100
+ batch << transaction
101
+ end
102
+ rescue ThreadError # queue empty
103
+ end
104
+
105
+ batch
106
+ end
107
+
108
+ def should_flush_transactions?
109
+ interval = config.flush_interval
110
+
111
+ return true if interval.nil?
112
+ return true if pending_transactions.length >= config.max_queue_size
113
+
114
+ Time.now.utc - @last_sent_transactions >= interval
115
+ end
116
+ end
117
+ end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module ElasticAPM
4
- VERSION = '0.5.1'.freeze
4
+ VERSION = '0.6.1'.freeze
5
5
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: elastic-apm
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.5.1
4
+ version: 0.6.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Mikkel Malmberg
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2018-03-26 00:00:00.000000000 Z
11
+ date: 2018-04-12 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport
@@ -106,6 +106,7 @@ files:
106
106
  - lib/elastic_apm/stacktrace/line_cache.rb
107
107
  - lib/elastic_apm/subscriber.rb
108
108
  - lib/elastic_apm/system_info.rb
109
+ - lib/elastic_apm/timed_worker.rb
109
110
  - lib/elastic_apm/transaction.rb
110
111
  - lib/elastic_apm/util.rb
111
112
  - lib/elastic_apm/util/dig.rb
@@ -113,7 +114,6 @@ files:
113
114
  - lib/elastic_apm/util/inspector.rb
114
115
  - lib/elastic_apm/util/lru_cache.rb
115
116
  - lib/elastic_apm/version.rb
116
- - lib/elastic_apm/worker.rb
117
117
  - vendor/.gitkeep
118
118
  homepage: https://github.com/elastic/apm-agent-ruby
119
119
  licenses:
@@ -136,7 +136,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
136
136
  version: '0'
137
137
  requirements: []
138
138
  rubyforge_project:
139
- rubygems_version: 2.7.3
139
+ rubygems_version: 2.7.6
140
140
  signing_key:
141
141
  specification_version: 4
142
142
  summary: The official Elastic APM agent for Ruby
@@ -1,46 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module ElasticAPM
4
- # @api private
5
- class Worker
6
- include Log
7
-
8
- # @api private
9
- class StopMessage; end
10
-
11
- # @api private
12
- Request = Struct.new(:path, :payload) do
13
- # require all params
14
- def initialize(path, payload)
15
- super
16
- end
17
- end
18
-
19
- def initialize(config, queue, adapter)
20
- @config = config
21
- @adapter = adapter
22
- @queue = queue
23
- end
24
-
25
- def run_forever
26
- loop do
27
- while (item = @queue.pop)
28
- case item
29
- when Request
30
- process item
31
- when StopMessage
32
- Thread.exit
33
- end
34
- end
35
- end
36
- end
37
-
38
- def process(item)
39
- @adapter.post(item.path, item.payload)
40
- rescue ::Exception => e
41
- fatal 'Failed posting: %s', e.inspect
42
- debug e.backtrace.join("\n")
43
- nil
44
- end
45
- end
46
- end