elastic-apm 0.4.5 → 0.5.0

Sign up to get free protection for your applications and to get access to all the features.

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: 329562f592fbece5d73eb15b09d06085abf6f243ff93626b7fed6429b29d7b82
4
- data.tar.gz: 269658c639e1459453f953dff804d97de8ed4a18093b4dfca76cedb1282d614f
3
+ metadata.gz: 98b3dde3453861c6e77db385bea9d0bd54c6d76dd2cb8dd253c046a4f42beb8c
4
+ data.tar.gz: a1b948919538e6e5c3eecd1db3fc6bcccff95eef8be335c7cc8913183b47f9fc
5
5
  SHA512:
6
- metadata.gz: ed067d07389e9187e290b2e47000f016e4ff72e4e717a9530d5dac64e73caa1648cd57b35413fffb03ee21780a65402ff7b64bf59ed468085576294e6dd38171
7
- data.tar.gz: b7425968e951aa1161812a3a15c9205904984d215081b273cb24ce59132e9a069dc59ac7a88d86d4a50f0d1de9db2aac948ec2037722f5dfdc7f20a0b011bc86
6
+ metadata.gz: d454b1c732c8acc5b2e5ce5cb1b3d5243f563cd0697ae126a104a9bd1c1be8c9b1adbdcadc50d45e9c9e6e9e31aebb6ed6dfc74e68620d859d591d4b7597e9aa
7
+ data.tar.gz: 77b6649b0b80b2a1294e1d628cee48b47db707c688766fa517ad2e360ae88ae9ff75645f8b8d9fe50a69afb17dd4f15f94e2c7320c5a5521dd9333dc3360c82d
@@ -17,6 +17,9 @@ Layout/EmptyLinesAroundArguments:
17
17
  Lint/RescueException:
18
18
  Enabled: false
19
19
 
20
+ Lint/HandleExceptions:
21
+ Enabled: false
22
+
20
23
  Metrics/BlockLength:
21
24
  Exclude:
22
25
  - 'spec/**/*.rb'
data/Gemfile CHANGED
@@ -6,6 +6,7 @@ git_source(:github) { |repo_name| "https://github.com/#{repo_name}" }
6
6
 
7
7
  gemspec
8
8
 
9
+ gem 'elasticsearch'
9
10
  gem 'fakeredis', require: nil,
10
11
  github: 'guilleiguaran/fakeredis' # needs master right now
11
12
  gem 'json-schema'
@@ -15,6 +16,7 @@ gem 'redis', require: nil
15
16
  gem 'rspec'
16
17
  gem 'rubocop'
17
18
  gem 'sequel'
19
+ gem 'sidekiq'
18
20
  gem 'timecop'
19
21
  gem 'webmock'
20
22
  gem 'yard'
@@ -36,3 +38,9 @@ when /.+/
36
38
  else
37
39
  gem framework
38
40
  end
41
+
42
+ # doesn't work with Rails master, so skip testing
43
+ gem 'delayed_job' unless [framework, version] == %w[rails master]
44
+
45
+ gem 'rails' if framework == 'sinatra'
46
+ gem 'sinatra' if framework == 'rails'
@@ -23,15 +23,15 @@ module ElasticAPM
23
23
  @instance
24
24
  end
25
25
 
26
- # rubocop:disable Metrics/MethodLength
27
- def self.start(config)
26
+ def self.start(config) # rubocop:disable Metrics/MethodLength
28
27
  return @instance if @instance
29
28
 
30
- config = Config.new(config) if config.is_a?(Hash)
29
+ config = Config.new(config) unless config.is_a?(Config)
31
30
 
32
31
  unless config.enabled_environments.include?(config.environment)
33
- config.logger && config.logger.info(
34
- format('Not tracking anything in "%s" env', config.environment)
32
+ puts format(
33
+ '%sNot tracking anything in "%s" env',
34
+ Log::PREFIX, config.environment
35
35
  )
36
36
  return
37
37
  end
@@ -39,10 +39,9 @@ module ElasticAPM
39
39
  LOCK.synchronize do
40
40
  return @instance if @instance
41
41
 
42
- @instance = new(config.freeze).start
42
+ @instance = new(config).start
43
43
  end
44
44
  end
45
- # rubocop:enable Metrics/MethodLength
46
45
 
47
46
  def self.stop
48
47
  LOCK.synchronize do
@@ -7,6 +7,9 @@ module ElasticAPM
7
7
  # @api private
8
8
  class Config
9
9
  DEFAULTS = {
10
+ config_file: 'config/elastic_apm.yml',
11
+ worker_process: false,
12
+
10
13
  server_url: 'http://localhost:8200',
11
14
  secret_token: nil,
12
15
 
@@ -78,16 +81,19 @@ module ElasticAPM
78
81
  'ELASTIC_APM_TRANSACTION_MAX_SPANS' => [:int, 'transaction_max_spans']
79
82
  }.freeze
80
83
 
81
- def initialize(options = nil)
82
- options = {} if options.nil?
83
-
84
+ def initialize(options = {})
84
85
  set_defaults
85
- set_from_env
86
+
86
87
  set_from_args(options)
88
+ set_from_config_file
89
+ set_from_env
87
90
 
88
91
  yield self if block_given?
89
92
  end
90
93
 
94
+ attr_accessor :config_file
95
+ attr_accessor :worker_process
96
+
91
97
  attr_accessor :server_url
92
98
  attr_accessor :secret_token
93
99
 
@@ -167,12 +173,16 @@ module ElasticAPM
167
173
 
168
174
  private
169
175
 
170
- def set_defaults
171
- DEFAULTS.each do |key, value|
176
+ def assign(options)
177
+ options.each do |key, value|
172
178
  send("#{key}=", value)
173
179
  end
174
180
  end
175
181
 
182
+ def set_defaults
183
+ assign(DEFAULTS)
184
+ end
185
+
176
186
  # rubocop:disable Metrics/MethodLength, Metrics/CyclomaticComplexity
177
187
  def set_from_env
178
188
  ENV_TO_KEY.each do |env_key, key|
@@ -195,9 +205,12 @@ module ElasticAPM
195
205
  # rubocop:enable Metrics/MethodLength, Metrics/CyclomaticComplexity
196
206
 
197
207
  def set_from_args(options)
198
- options.each do |key, value|
199
- send("#{key}=", value)
200
- end
208
+ assign(options)
209
+ end
210
+
211
+ def set_from_config_file
212
+ return unless File.exist?(config_file)
213
+ assign(YAML.load_file(config_file) || {})
201
214
  end
202
215
 
203
216
  def set_sinatra(app)
@@ -208,11 +221,12 @@ module ElasticAPM
208
221
  self.root_path = Dir.pwd
209
222
  end
210
223
 
211
- def set_rails(app)
212
- self.service_name = format_name(service_name || app.class.parent_name)
213
- self.framework_name = 'Ruby on Rails'
214
- self.framework_version = Rails::VERSION::STRING
215
- self.logger = Rails.logger
224
+ def set_rails(app) # rubocop:disable Metrics/AbcSize
225
+ self.service_name ||= format_name(service_name || app.class.parent_name)
226
+ self.framework_name ||= 'Ruby on Rails'
227
+ self.framework_version ||= Rails::VERSION::STRING
228
+ self.logger ||= Rails.logger
229
+
216
230
  self.root_path = Rails.root.to_s
217
231
  self.view_paths = app.config.paths['app/views'].existent
218
232
  end
@@ -39,7 +39,7 @@ module ElasticAPM
39
39
  return response if status >= 200 && status <= 299
40
40
 
41
41
  error "POST returned an unsuccessful status code (#{response.code})"
42
- debug response.body
42
+ error response.body
43
43
 
44
44
  response
45
45
  end
@@ -75,7 +75,7 @@ module Kernel
75
75
 
76
76
  begin
77
77
  ElasticAPM::Injectors.hook_into(path)
78
- rescue ::Exception # rubocop:disable Lint/HandleExceptions
78
+ rescue ::Exception
79
79
  end
80
80
 
81
81
  res
@@ -0,0 +1,68 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ElasticAPM
4
+ # @api private
5
+ module Injectors
6
+ # @api private
7
+ class DelayedJobInjector
8
+ CLASS_SEPARATOR = '.'.freeze
9
+ METHOD_SEPARATOR = '#'.freeze
10
+ TYPE = 'Delayed::Job'.freeze
11
+
12
+ def install
13
+ ::Delayed::Backend::Base.class_eval do
14
+ alias invoke_job_without_apm invoke_job
15
+
16
+ def invoke_job(*args, &block)
17
+ ::ElasticAPM::Injectors::DelayedJobInjector
18
+ .invoke_job(self, *args, &block)
19
+ end
20
+ end
21
+ end
22
+
23
+ def self.invoke_job(job, *args, &block)
24
+ job_name = name_from_payload(job.payload_object)
25
+ transaction = ElasticAPM.transaction(job_name, TYPE)
26
+ job.invoke_job_without_apm(*args, &block)
27
+ transaction.submit 'success'
28
+ rescue ::Exception => e
29
+ ElasticAPM.report(e, handled: false)
30
+ transaction.submit 'error'
31
+ raise
32
+ ensure
33
+ transaction.release if transaction
34
+ end
35
+
36
+ def self.name_from_payload(payload_object)
37
+ if payload_object.is_a?(::Delayed::PerformableMethod)
38
+ performable_method_name(payload_object)
39
+ else
40
+ payload_object.class.name
41
+ end
42
+ end
43
+
44
+ def self.performable_method_name(payload_object)
45
+ class_name = object_name(payload_object)
46
+ separator = name_separator(payload_object)
47
+ method_name = payload_object.method_name
48
+ "#{class_name}#{separator}#{method_name}"
49
+ end
50
+
51
+ def self.object_name(payload_object)
52
+ object = payload_object.object
53
+ klass = object.is_a?(Class) ? object : object.class
54
+ klass.name
55
+ end
56
+
57
+ def self.name_separator(payload_object)
58
+ payload_object.object.is_a?(Class) ? CLASS_SEPARATOR : METHOD_SEPARATOR
59
+ end
60
+ end
61
+
62
+ register(
63
+ 'Delayed::Backend::Base',
64
+ 'delayed/backend/base',
65
+ DelayedJobInjector.new
66
+ )
67
+ end
68
+ end
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ElasticAPM
4
+ # @api private
5
+ module Injectors
6
+ # @api private
7
+ class ElasticsearchInjector
8
+ NAME_FORMAT = '%s %s'.freeze
9
+ TYPE = 'db.elasticsearch'.freeze
10
+
11
+ def install
12
+ ::Elasticsearch::Transport::Client.class_eval do
13
+ alias perform_request_without_apm perform_request
14
+
15
+ def perform_request(method, path, *args, &block)
16
+ name = format(NAME_FORMAT, method, path)
17
+ context = Span::Context.new(statement: args[0])
18
+
19
+ ElasticAPM.span name, TYPE, context: context do
20
+ perform_request_without_apm(method, path, *args, &block)
21
+ end
22
+ end
23
+ end
24
+ end
25
+ end
26
+
27
+ register(
28
+ 'Elasticsearch::Transport::Client',
29
+ 'elasticsearch-transport',
30
+ ElasticsearchInjector.new
31
+ )
32
+ end
33
+ end
@@ -0,0 +1,86 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ElasticAPM
4
+ # @api private
5
+ module Injectors
6
+ # @api private
7
+ class SidekiqInjector
8
+ ACTIVE_JOB_WRAPPER =
9
+ 'ActiveJob::QueueAdapters::SidekiqAdapter::JobWrapper'.freeze
10
+
11
+ # @api private
12
+ class Middleware
13
+ # rubocop:disable Metrics/MethodLength
14
+ def call(_worker, job, queue)
15
+ name = SidekiqInjector.name_for(job)
16
+ transaction = ElasticAPM.transaction(name, 'Sidekiq')
17
+ ElasticAPM.set_tag(:queue, queue)
18
+
19
+ yield
20
+
21
+ transaction.submit('success') if transaction
22
+ rescue ::Exception => e
23
+ ElasticAPM.report(e, handled: false)
24
+ transaction.submit(:error) if transaction
25
+ raise
26
+ ensure
27
+ transaction.release if transaction
28
+ end
29
+ # rubocop:enable Metrics/MethodLength
30
+ end
31
+
32
+ def self.name_for(job)
33
+ klass = job['class']
34
+
35
+ case klass
36
+ when ACTIVE_JOB_WRAPPER
37
+ job['wrapped']
38
+ else
39
+ klass
40
+ end
41
+ end
42
+
43
+ def install_middleware
44
+ Sidekiq.configure_server do |config|
45
+ config.server_middleware do |chain|
46
+ chain.add Middleware
47
+ end
48
+ end
49
+ end
50
+
51
+ # rubocop:disable Metrics/MethodLength
52
+ def install_processor
53
+ require 'sidekiq/processor'
54
+
55
+ Sidekiq::Processor.class_eval do
56
+ alias start_without_apm start
57
+ alias terminate_without_apm terminate
58
+
59
+ def start
60
+ result = start_without_apm
61
+ ElasticAPM.start # might already be running from railtie
62
+
63
+ return result unless ElasticAPM.running?
64
+ ElasticAPM.agent.config.logger = Sidekiq.logger
65
+
66
+ result
67
+ end
68
+
69
+ def terminate
70
+ terminate_without_apm
71
+
72
+ ElasticAPM.stop
73
+ end
74
+ end
75
+ end
76
+ # rubocop:enable Metrics/MethodLength
77
+
78
+ def install
79
+ install_processor
80
+ install_middleware
81
+ end
82
+ end
83
+
84
+ register 'Sidekiq', 'sidekiq', SidekiqInjector.new
85
+ end
86
+ end
@@ -89,19 +89,27 @@ module ElasticAPM
89
89
  # rubocop:enable Metrics/MethodLength
90
90
 
91
91
  def span(*args, &block)
92
- transaction.span(*args, &block)
92
+ unless current_transaction
93
+ return yield if block_given?
94
+ return
95
+ end
96
+
97
+ current_transaction.span(*args, &block)
93
98
  end
94
99
 
95
100
  def set_tag(key, value)
96
- transaction.context.tags[key] = value.to_s
101
+ return unless current_transaction
102
+ current_transaction.context.tags[key] = value.to_s
97
103
  end
98
104
 
99
105
  def set_custom_context(context)
100
- transaction.context.custom.merge!(context)
106
+ return unless current_transaction
107
+ current_transaction.context.custom.merge!(context)
101
108
  end
102
109
 
103
110
  def set_user(user)
104
- transaction.context.user = Context::User.new(config, user)
111
+ return unless current_transaction
112
+ current_transaction.context.user = Context::User.new(config, user)
105
113
  end
106
114
 
107
115
  def submit_transaction(transaction)
@@ -14,13 +14,16 @@ module ElasticAPM
14
14
  context: ElasticAPM.build_context(env)
15
15
 
16
16
  resp = @app.call env
17
+ status, headers, = resp
17
18
 
18
- transaction.submit(resp[0], headers: resp[1]) if transaction
19
+ if transaction
20
+ transaction.submit(status, status: status, headers: headers)
21
+ end
19
22
  rescue InternalError
20
23
  raise # Don't report ElasticAPM errors
21
24
  rescue ::Exception => e
22
25
  ElasticAPM.report(e, handled: false)
23
- transaction.submit(500) if transaction
26
+ transaction.submit(500, status: 500) if transaction
24
27
  raise
25
28
  ensure
26
29
  transaction.release if transaction
@@ -7,14 +7,7 @@ module ElasticAPM
7
7
  Config::DEFAULTS.each { |option, value| config.elastic_apm[option] = value }
8
8
 
9
9
  initializer 'elastic_apm.initialize' do |app|
10
- config = Config.new app.config.elastic_apm do |c|
11
- c.app = app
12
- end
13
-
14
- file_config = load_config(app)
15
- file_config.each do |option, value|
16
- config.send(:"#{option}=", value)
17
- end
10
+ config = app.config.elastic_apm.merge(app: app)
18
11
 
19
12
  begin
20
13
  ElasticAPM.start config
@@ -29,14 +22,5 @@ module ElasticAPM
29
22
  config.after_initialize do
30
23
  require 'elastic_apm/injectors/action_dispatch'
31
24
  end
32
-
33
- private
34
-
35
- def load_config(app)
36
- config_path = app.root.join('config', 'elastic_apm.yml')
37
- return {} unless File.exist?(config_path)
38
-
39
- YAML.load_file(config_path) || {}
40
- end
41
25
  end
42
26
  end
@@ -5,21 +5,21 @@ require 'elastic_apm/span/context'
5
5
  module ElasticAPM
6
6
  # @api private
7
7
  class Span
8
- DEFAULT_KIND = 'custom'.freeze
8
+ DEFAULT_TYPE = 'custom'.freeze
9
9
 
10
10
  # rubocop:disable Metrics/ParameterLists
11
11
  def initialize(
12
12
  transaction,
13
13
  id,
14
14
  name,
15
- type = DEFAULT_KIND,
15
+ type = nil,
16
16
  parent: nil,
17
17
  context: nil
18
18
  )
19
19
  @transaction = transaction
20
20
  @id = id
21
21
  @name = name
22
- @type = type
22
+ @type = type || DEFAULT_TYPE
23
23
  @parent = parent
24
24
  @context = context
25
25
  @stacktrace = nil
@@ -3,18 +3,14 @@
3
3
  module ElasticAPM
4
4
  # @api private
5
5
  class Transaction
6
+ DEFAULT_TYPE = 'custom'.freeze
7
+
6
8
  # rubocop:disable Metrics/MethodLength
7
- def initialize(
8
- instrumenter,
9
- name,
10
- type = 'custom',
11
- context: nil,
12
- sampled: true
13
- )
9
+ def initialize(instrumenter, name, type = nil, context: nil, sampled: true)
14
10
  @id = SecureRandom.uuid
15
11
  @instrumenter = instrumenter
16
12
  @name = name
17
- @type = type
13
+ @type = type || DEFAULT_TYPE
18
14
 
19
15
  @timestamp = Util.micros
20
16
 
@@ -51,10 +47,12 @@ module ElasticAPM
51
47
  !!(@result && @duration)
52
48
  end
53
49
 
54
- def submit(status = nil, headers: {})
55
- done status
50
+ def submit(result = nil, status: nil, headers: {})
51
+ done result
56
52
 
57
- context.response = Context::Response.new(status, headers: headers)
53
+ if status
54
+ context.response = Context::Response.new(status, headers: headers)
55
+ end
58
56
 
59
57
  release
60
58
 
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module ElasticAPM
4
- VERSION = '0.4.5'.freeze
4
+ VERSION = '0.5.0'.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.4.5
4
+ version: 0.5.0
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-13 00:00:00.000000000 Z
11
+ date: 2018-03-23 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport
@@ -73,10 +73,13 @@ files:
73
73
  - lib/elastic_apm/http.rb
74
74
  - lib/elastic_apm/injectors.rb
75
75
  - lib/elastic_apm/injectors/action_dispatch.rb
76
+ - lib/elastic_apm/injectors/delayed_job.rb
77
+ - lib/elastic_apm/injectors/elasticsearch.rb
76
78
  - lib/elastic_apm/injectors/json.rb
77
79
  - lib/elastic_apm/injectors/net_http.rb
78
80
  - lib/elastic_apm/injectors/redis.rb
79
81
  - lib/elastic_apm/injectors/sequel.rb
82
+ - lib/elastic_apm/injectors/sidekiq.rb
80
83
  - lib/elastic_apm/injectors/sinatra.rb
81
84
  - lib/elastic_apm/injectors/tilt.rb
82
85
  - lib/elastic_apm/instrumenter.rb