elastic-apm 0.4.5 → 0.5.0

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: 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