elastic-apm 2.0.1 → 2.1.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.

Files changed (40) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +13 -0
  3. data/README.md +9 -9
  4. data/docs/advanced.asciidoc +8 -0
  5. data/docs/api.asciidoc +1 -1
  6. data/docs/configuration.asciidoc +1 -1
  7. data/docs/context.asciidoc +0 -1
  8. data/docs/index.asciidoc +7 -25
  9. data/docs/introduction.asciidoc +33 -0
  10. data/lib/elastic_apm/agent.rb +0 -1
  11. data/lib/elastic_apm/config.rb +35 -7
  12. data/lib/elastic_apm/context.rb +0 -2
  13. data/lib/elastic_apm/context/request.rb +0 -2
  14. data/lib/elastic_apm/context/request/socket.rb +0 -2
  15. data/lib/elastic_apm/context/request/url.rb +0 -2
  16. data/lib/elastic_apm/context/response.rb +0 -2
  17. data/lib/elastic_apm/context/user.rb +1 -3
  18. data/lib/elastic_apm/context_builder.rb +2 -2
  19. data/lib/elastic_apm/metadata.rb +11 -13
  20. data/lib/elastic_apm/metadata/process_info.rb +5 -13
  21. data/lib/elastic_apm/metadata/service_info.rb +38 -33
  22. data/lib/elastic_apm/metadata/system_info.rb +7 -13
  23. data/lib/elastic_apm/railtie.rb +2 -2
  24. data/lib/elastic_apm/span/context.rb +0 -6
  25. data/lib/elastic_apm/spies/faraday.rb +41 -0
  26. data/lib/elastic_apm/spies/net_http.rb +25 -0
  27. data/lib/elastic_apm/stacktrace/frame.rb +2 -0
  28. data/lib/elastic_apm/transport/base.rb +1 -2
  29. data/lib/elastic_apm/transport/connection.rb +4 -5
  30. data/lib/elastic_apm/transport/serializers.rb +22 -3
  31. data/lib/elastic_apm/transport/serializers/context_serializer.rb +69 -0
  32. data/lib/elastic_apm/transport/serializers/error_serializer.rb +11 -7
  33. data/lib/elastic_apm/transport/serializers/metadata_serializer.rb +64 -0
  34. data/lib/elastic_apm/transport/serializers/span_serializer.rb +49 -5
  35. data/lib/elastic_apm/transport/serializers/transaction_serializer.rb +10 -6
  36. data/lib/elastic_apm/transport/worker.rb +5 -3
  37. data/lib/elastic_apm/util.rb +7 -0
  38. data/lib/elastic_apm/util/prefixed_logger.rb +18 -0
  39. data/lib/elastic_apm/version.rb +1 -1
  40. metadata +8 -2
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 4cefd73acad31a9561dbfb964d2654d8f702c8db1783df2fe7dee9849a87609f
4
- data.tar.gz: 8e4be6f5ee71a5972bc964f736d1cfaf652d307e2c080f7c9439273d966fc4d5
3
+ metadata.gz: df3dedd32ff6829fff4db353e0f6990d378d57313112cba444f717cb958565bd
4
+ data.tar.gz: 9e822ce49bc71ebf13ab70e0bf67e8c156a19f0f5c68e5dcca895fcebbf64edb
5
5
  SHA512:
6
- metadata.gz: d73c89eb559553948b4eb628d321e0aee6aeb9d754ba6b8737e628fd2858290b761800a1da761d65d3d83126e0a6cab634ead3a3a763174064d8a16d3ae8dda9
7
- data.tar.gz: 3b8b33c2083e82a433d650bf7708fe5686e47c3a4af71f233bbd52bafb202aa36ca1917e1f676deaeb587ad960b37286ac13e341e65e66c4d39246df99ac342f
6
+ metadata.gz: 9cd54124d6b4278d5cef85347eacc071a1a9d6511dc31c30fa551d9e9827d5288e1f91fad7c89cec32b4642897f13f92c16ed8a61b494f9e3b9bb4a60c8eacfc
7
+ data.tar.gz: ff0c2bff7f2d9f5d1a920a54a9dd120938c8a91e02cc024e3ecb9e420a50673f75b8ec2535643b794f34319ac2f2eb005a9a907dcc0e1b79869178fc3278305c
data/CHANGELOG.md CHANGED
@@ -4,6 +4,19 @@ All notable changes to this project will be documented in this file.
4
4
 
5
5
  This project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).
6
6
 
7
+ ## 2.1.0 (2018-12-04)
8
+
9
+ ### Added
10
+
11
+ - Support for Faraday ([#249](https://github.com/elastic/apm-agent-ruby/pull/249))
12
+
13
+ ### Fixed
14
+
15
+ - Truncate keyword fields to 1024 chars ([#240](https://github.com/elastic/apm-agent-ruby/pull/240))
16
+ - Lazy boot worker threads on first event. Fixes apps using Puma's `preload_app!` ([#239](https://github.com/elastic/apm-agent-ruby/pull/239))
17
+ - Fix missing `disable_send` implementation ([#257](https://github.com/elastic/apm-agent-ruby/pull/257))
18
+ - Add warnings for invalid config options ([#254](https://github.com/elastic/apm-agent-ruby/pull/254))
19
+
7
20
  ## 2.0.1 (2018-11-15)
8
21
 
9
22
  ### Fixed
data/README.md CHANGED
@@ -11,21 +11,21 @@ The official Rubygem for [Elastic][] [APM][].
11
11
 
12
12
  ## Documentation
13
13
 
14
- [Full documentation at Elasti.co](https://www.elastic.co/guide/en/apm/agent/ruby/index.html).
14
+ [Full documentation at Elasti.co](https://www.elastic.co/guide/en/apm/agent/ruby/2.x/index.html).
15
15
 
16
16
  <ul>
17
- <li><a href="https://www.elastic.co/guide/en/apm/agent/ruby/1.x/introduction.html">Introduction</a></li>
18
- <li><a href="https://www.elastic.co/guide/en/apm/agent/ruby/1.x/getting-started-rails.html">Getting started with Rails</a></li>
19
- <li><a href="https://www.elastic.co/guide/en/apm/agent/ruby/1.x/getting-started-rack.html">Getting started with Rack</a></li>
20
- <li><a href="https://www.elastic.co/guide/en/apm/agent/ruby/1.x/configuration.html">Configuration</a></li>
17
+ <li><a href="https://www.elastic.co/guide/en/apm/agent/ruby/2.x/introduction.html">Introduction</a></li>
18
+ <li><a href="https://www.elastic.co/guide/en/apm/agent/ruby/2.x/getting-started-rails.html">Getting started with Rails</a></li>
19
+ <li><a href="https://www.elastic.co/guide/en/apm/agent/ruby/2.x/getting-started-rack.html">Getting started with Rack</a></li>
20
+ <li><a href="https://www.elastic.co/guide/en/apm/agent/ruby/2.x/configuration.html">Configuration</a></li>
21
21
  <li>
22
- <a href="https://www.elastic.co/guide/en/apm/agent/ruby/1.x/advanced.html">Advanced Topics</a>
22
+ <a href="https://www.elastic.co/guide/en/apm/agent/ruby/2.x/advanced.html">Advanced Topics</a>
23
23
  <ul>
24
- <li><a href="https://www.elastic.co/guide/en/apm/agent/ruby/1.x/custom-instrumentation.html">Custom instrumentation</a></li>
25
- <li><a href="https://www.elastic.co/guide/en/apm/agent/ruby/1.x/spies.html">Spies — instrumented libraries</a></li>
24
+ <li><a href="https://www.elastic.co/guide/en/apm/agent/ruby/2.x/custom-instrumentation.html">Custom instrumentation</a></li>
26
25
  </ul>
27
26
  </li>
28
- <li><a href="https://www.elastic.co/guide/en/apm/agent/ruby/1.x/api.html">Public API</a></li>
27
+ <li><a href="https://www.elastic.co/guide/en/apm/agent/ruby/2.x/supported-technologies.html">Supported Technologies</a></li>
28
+ <li><a href="https://www.elastic.co/guide/en/apm/agent/ruby/2.x/api.html">Public API</a></li>
29
29
  </ul>
30
30
 
31
31
  ---
@@ -0,0 +1,8 @@
1
+ [[advanced]]
2
+ == Advanced Topics
3
+
4
+ * <<context>>
5
+ * <<custom-instrumentation>>
6
+
7
+ include::./context.asciidoc[]
8
+ include::./custom-instrumentation.asciidoc[]
data/docs/api.asciidoc CHANGED
@@ -98,7 +98,7 @@ Wrap a block in a Transaction, starting and ending around the block
98
98
 
99
99
  [source,ruby]
100
100
  ----
101
- ElasticAPM.transaction 'Do things' do |transaction|
101
+ ElasticAPM.with_transaction 'Do things' do |transaction|
102
102
  do_work # ...
103
103
 
104
104
  transaction.result = 'success'
@@ -319,7 +319,7 @@ Elastic APM can instrument your Rake tasks but given that they are used for a mu
319
319
  [options="header"]
320
320
  |============
321
321
  | Environment | `Config` key | Default
322
- | `ELASTIC_APM_LOG_LEVEL` | `log_level` | `Logger::DEBUG # => 0`
322
+ | `ELASTIC_APM_LOG_LEVEL` | `log_level` | `Logger::INFO # => 1`
323
323
  |============
324
324
 
325
325
  By default Elastic APM logs to `stdout` or uses `Rails.log` when used with Rails.
@@ -1,4 +1,3 @@
1
- [float]
2
1
  [[context]]
3
2
  === Adding additional context
4
3
 
data/docs/index.asciidoc CHANGED
@@ -2,42 +2,24 @@
2
2
  include::{asciidoc-dir}/../../shared/attributes.asciidoc[]
3
3
 
4
4
  ifdef::env-github[]
5
- NOTE: For the best reading experience, please view this documentation at
5
+ NOTE: For the best reading experience,
6
+ please view this documentation at
6
7
  https://www.elastic.co/guide/en/apm/agent/ruby[elastic.co]
7
8
  endif::[]
8
9
 
9
10
  = APM Ruby Agent Reference
10
11
 
11
- [[introduction]]
12
- == Introduction
13
-
14
- The Elastic APM Ruby Agent sends performance metrics and error logs to an
15
- Elastic APM Server.
16
-
17
- It has built-in support for <<getting-started-rails,Ruby on Rails>> and other
18
- <<getting-started-rack,Rack-compatible>> applications.
19
-
20
- This agent is one of several components you need to get started collecting APM
21
- data. See also:
22
-
23
- * {apm-server-ref}[APM Server]
24
-
25
- [[framework-support]]
26
- The Elastic APM Ruby Agent officially supports Ruby on Rails versions 4.x on
27
- onwards, see <<getting-started-rails,Getting started with Ruby on Rails>>.
28
-
29
- For Sinatra and other Rack compatible frameworks, see
30
- <<getting-started-rack,Getting started with Rack>>.
12
+ include::./introduction.asciidoc[]
31
13
 
32
14
  include::./getting-started-rails.asciidoc[]
15
+
33
16
  include::./getting-started-rack.asciidoc[]
17
+
34
18
  include::./configuration.asciidoc[]
35
19
 
36
- [[advanced]]
37
- == Advanced Topics
38
- include::./context.asciidoc[]
39
- include::./custom-instrumentation.asciidoc[]
20
+ include::./advanced.asciidoc[]
40
21
 
41
22
  include::./supported-technologies.asciidoc[]
23
+
42
24
  include::./api.asciidoc[]
43
25
 
@@ -0,0 +1,33 @@
1
+ [[introduction]]
2
+
3
+ ifdef::env-github[]
4
+ NOTE: For the best reading experience,
5
+ please view this documentation at
6
+ https://www.elastic.co/guide/en/apm/agent/ruby/current/introduction.html[elastic.co]
7
+ endif::[]
8
+
9
+ == Introduction
10
+
11
+ Welcome to the APM Ruby Agent documentation.
12
+
13
+ The Elastic APM Ruby Agent sends performance metrics and error logs to an
14
+ Elastic APM Server.
15
+ It has built-in support for <<getting-started-rails,Ruby on Rails>> and other
16
+ <<getting-started-rack,Rack-compatible>> applications.
17
+
18
+ [float]
19
+ [[additional-components]]
20
+ === Additional Components
21
+
22
+ APM Agents work in conjunction with the {apm-server-ref}/index.html[APM Server], {ref}/index.html[Elasticsearch], and {kibana-ref}/index.html[Kibana].
23
+ Please view the {apm-get-started-ref}/index.html[APM Overview] for details on how these components work together.
24
+
25
+ [float]
26
+ [[framework-support]]
27
+ === Framework Support
28
+
29
+ The Elastic APM Ruby Agent officially supports Ruby on Rails versions 4.x on
30
+ onwards, see <<getting-started-rails,Getting started with Ruby on Rails>>.
31
+
32
+ For Sinatra and other Rack compatible frameworks, see
33
+ <<getting-started-rack,Getting started with Rack>>.
@@ -1,6 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'elastic_apm/naively_hashable'
4
3
  require 'elastic_apm/context_builder'
5
4
  require 'elastic_apm/error_builder'
6
5
  require 'elastic_apm/stacktrace_builder'
@@ -3,10 +3,13 @@
3
3
  require 'logger'
4
4
  require 'yaml'
5
5
 
6
+ require 'elastic_apm/util/prefixed_logger'
6
7
  require 'elastic_apm/config/duration'
7
8
  require 'elastic_apm/config/size'
8
9
 
9
10
  module ElasticAPM
11
+ class ConfigError < StandardError; end
12
+
10
13
  # rubocop:disable Metrics/ClassLength
11
14
  # @api private
12
15
  class Config
@@ -144,10 +147,10 @@ module ElasticAPM
144
147
  attr_accessor :transaction_sample_rate
145
148
  attr_accessor :verify_server_cert
146
149
 
147
- attr_reader :custom_key_filters
148
- attr_reader :ignore_url_patterns
149
- attr_reader :span_frames_min_duration
150
- attr_reader :span_frames_min_duration_us
150
+ attr_reader :custom_key_filters
151
+ attr_reader :ignore_url_patterns
152
+ attr_reader :span_frames_min_duration
153
+ attr_reader :span_frames_min_duration_us
151
154
 
152
155
  attr_accessor :view_paths
153
156
  attr_accessor :root_path
@@ -157,6 +160,10 @@ module ElasticAPM
157
160
  alias :instrument? :instrument
158
161
  alias :verify_server_cert? :verify_server_cert
159
162
 
163
+ def alert_logger
164
+ @alert_logger ||= PrefixedLogger.new($stdout, prefix: Logging::PREFIX)
165
+ end
166
+
160
167
  def app=(app)
161
168
  case app_type?(app)
162
169
  when :sinatra
@@ -198,6 +205,7 @@ module ElasticAPM
198
205
  action_dispatch
199
206
  delayed_job
200
207
  elasticsearch
208
+ faraday
201
209
  http
202
210
  json
203
211
  mongo
@@ -234,12 +242,22 @@ module ElasticAPM
234
242
  ].freeze
235
243
 
236
244
  def respond_to_missing?(name)
237
- DEPRECATED_OPTIONS.include? name
245
+ return true if DEPRECATED_OPTIONS.include? name
246
+ return true if name.to_s.end_with?('=')
247
+ false
238
248
  end
239
249
 
240
250
  def method_missing(name, *args)
241
- return super unless DEPRECATED_OPTIONS.include?(name)
242
- warn "The option `#{name}' has been removed."
251
+ if DEPRECATED_OPTIONS.include?(name)
252
+ alert_logger.warn "The option `#{name}' has been removed."
253
+ return
254
+ end
255
+
256
+ if name.to_s.end_with?('=')
257
+ raise ConfigError, "No such option `#{name.to_s.delete('=')}'"
258
+ end
259
+
260
+ super
243
261
  end
244
262
 
245
263
  private
@@ -280,11 +298,21 @@ module ElasticAPM
280
298
 
281
299
  def set_from_args(options)
282
300
  assign(options)
301
+ rescue ConfigError => e
302
+ alert_logger.warn format(
303
+ 'Failed to configure from arguments: %s',
304
+ e.message
305
+ )
283
306
  end
284
307
 
285
308
  def set_from_config_file
286
309
  return unless File.exist?(config_file)
287
310
  assign(YAML.load_file(config_file) || {})
311
+ rescue ConfigError => e
312
+ alert_logger.warn format(
313
+ 'Failed to configure from config file: %s',
314
+ e.message
315
+ )
288
316
  end
289
317
 
290
318
  def set_sinatra(app)
@@ -9,8 +9,6 @@ require 'elastic_apm/context/user'
9
9
  module ElasticAPM
10
10
  # @api private
11
11
  class Context
12
- include NaivelyHashable
13
-
14
12
  attr_accessor :request, :response, :user
15
13
  attr_reader :custom, :tags
16
14
 
@@ -4,8 +4,6 @@ module ElasticAPM
4
4
  class Context
5
5
  # @api private
6
6
  class Request
7
- include NaivelyHashable
8
-
9
7
  attr_accessor :body, :cookies, :env, :headers, :http_version, :method,
10
8
  :socket, :url
11
9
  end
@@ -7,8 +7,6 @@ module ElasticAPM
7
7
  class Request
8
8
  # @api private
9
9
  class Socket
10
- include NaivelyHashable
11
-
12
10
  def initialize(req)
13
11
  @remote_addr = req.ip
14
12
  @encrypted = req.scheme == 'https'
@@ -7,8 +7,6 @@ module ElasticAPM
7
7
  class Request
8
8
  # @api private
9
9
  class Url
10
- include NaivelyHashable
11
-
12
10
  SKIPPED_PORTS = {
13
11
  'http' => 80,
14
12
  'https' => 443
@@ -4,8 +4,6 @@ module ElasticAPM
4
4
  class Context
5
5
  # @api private
6
6
  class Response
7
- include NaivelyHashable
8
-
9
7
  def initialize(
10
8
  status_code,
11
9
  headers: {},
@@ -4,12 +4,10 @@ module ElasticAPM
4
4
  class Context
5
5
  # @api private
6
6
  class User
7
- include NaivelyHashable
8
-
9
7
  def initialize(config, record)
10
8
  return unless record
11
9
 
12
- @id = safe_get(record, config.current_user_id_method)
10
+ @id = safe_get(record, config.current_user_id_method)&.to_s
13
11
  @email = safe_get(record, config.current_user_email_method)
14
12
  @username = safe_get(record, config.current_user_username_method)
15
13
  end
@@ -21,10 +21,10 @@ module ElasticAPM
21
21
  context.request = Context::Request.new unless context.request
22
22
  request = context.request
23
23
 
24
- request.socket = Context::Request::Socket.new(req).to_h
24
+ request.socket = Context::Request::Socket.new(req)
25
25
  request.http_version = build_http_version rack_env
26
26
  request.method = req.request_method
27
- request.url = Context::Request::Url.new(req).to_h
27
+ request.url = Context::Request::Url.new(req)
28
28
  request.headers, request.env = get_headers_and_env(rack_env)
29
29
  request.body = get_body(req)
30
30
 
@@ -1,20 +1,18 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'elastic_apm/metadata/service_info'
4
- require 'elastic_apm/metadata/system_info'
5
- require 'elastic_apm/metadata/process_info'
6
-
7
3
  module ElasticAPM
8
4
  # @api private
9
- module Metadata
10
- def self.build(config)
11
- {
12
- metadata: {
13
- service: Metadata::ServiceInfo.build(config),
14
- process: Metadata::ProcessInfo.build(config),
15
- system: Metadata::SystemInfo.build(config)
16
- }
17
- }.to_json
5
+ class Metadata
6
+ def initialize(config)
7
+ @service = ServiceInfo.new(config)
8
+ @process = ProcessInfo.new(config)
9
+ @system = SystemInfo.new(config)
18
10
  end
11
+
12
+ attr_reader :service, :process, :system
19
13
  end
20
14
  end
15
+
16
+ require 'elastic_apm/metadata/service_info'
17
+ require 'elastic_apm/metadata/system_info'
18
+ require 'elastic_apm/metadata/process_info'
@@ -1,26 +1,18 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module ElasticAPM
4
- module Metadata
4
+ class Metadata
5
5
  # @api private
6
6
  class ProcessInfo
7
7
  def initialize(config)
8
8
  @config = config
9
- end
10
9
 
11
- def build
12
- pid = $PID || Process.pid
13
- return unless pid
14
- {
15
- argv: ARGV,
16
- pid: pid,
17
- title: $PROGRAM_NAME
18
- }
10
+ @argv = ARGV
11
+ @pid = $PID || Process.pid
12
+ @title = $PROGRAM_NAME
19
13
  end
20
14
 
21
- def self.build(config)
22
- new(config).build
23
- end
15
+ attr_reader :argv, :pid, :title
24
16
  end
25
17
  end
26
18
  end
@@ -1,56 +1,61 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module ElasticAPM
4
- module Metadata
4
+ class Metadata
5
5
  # @api private
6
6
  class ServiceInfo
7
- def initialize(config)
8
- @config = config
7
+ # @api private
8
+ class Versioned
9
+ def initialize(name: nil, version: nil)
10
+ @name = name
11
+ @version = version
12
+ end
13
+
14
+ attr_reader :name, :version
9
15
  end
16
+ class Agent < Versioned; end
17
+ class Framework < Versioned; end
18
+ class Language < Versioned; end
19
+ class Runtime < Versioned; end
10
20
 
11
21
  # rubocop:disable Metrics/MethodLength
12
- def build
13
- base = {
14
- name: @config.service_name,
15
- environment: @config.environment,
16
- agent: {
17
- name: 'ruby',
18
- version: VERSION
19
- },
20
- framework: nil,
21
- language: {
22
- name: 'ruby',
23
- version: RUBY_VERSION
24
- },
25
- runtime: runtime,
26
- version: @config.service_version || Util.git_sha
27
- }
28
-
29
- if @config.framework_name
30
- base[:framework] = {
31
- name: @config.framework_name,
32
- version: @config.framework_version
33
- }
34
- end
22
+ def initialize(config)
23
+ @config = config
35
24
 
36
- base
25
+ @name = @config.service_name
26
+ @environment = @config.environment
27
+ @agent = Agent.new(name: 'ruby', version: VERSION)
28
+ @framework = Framework.new(
29
+ name: @config.framework_name,
30
+ version: @config.framework_version
31
+ )
32
+ @language = Language.new(name: 'ruby', version: RUBY_VERSION)
33
+ @runtime = lookup_runtime
34
+ @version = @config.service_version || Util.git_sha
37
35
  end
38
36
  # rubocop:enable Metrics/MethodLength
39
37
 
40
- def self.build(config)
41
- new(config).build
42
- end
38
+ attr_reader :name, :environment, :agent, :framework, :language, :runtime,
39
+ :version
43
40
 
44
41
  private
45
42
 
46
- def runtime
43
+ # rubocop:disable Metrics/MethodLength
44
+ def lookup_runtime
47
45
  case RUBY_ENGINE
48
46
  when 'ruby'
49
- { name: RUBY_ENGINE, version: RUBY_VERSION || RUBY_ENGINE_VERSION }
47
+ Runtime.new(
48
+ name: RUBY_ENGINE,
49
+ version: RUBY_VERSION || RUBY_ENGINE_VERSION
50
+ )
50
51
  when 'jruby'
51
- { name: RUBY_ENGINE, version: JRUBY_VERSION || RUBY_ENGINE_VERSION }
52
+ Runtime.new(
53
+ name: RUBY_ENGINE,
54
+ version: JRUBY_VERSION || RUBY_ENGINE_VERSION
55
+ )
52
56
  end
53
57
  end
58
+ # rubocop:enable Metrics/MethodLength
54
59
  end
55
60
  end
56
61
  end
@@ -1,29 +1,23 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module ElasticAPM
4
- module Metadata
4
+ class Metadata
5
5
  # @api private
6
6
  class SystemInfo
7
7
  def initialize(config)
8
8
  @config = config
9
- end
10
9
 
11
- def build
12
- {
13
- hostname: @config.hostname || `hostname`.chomp,
14
- architecture: platform.cpu,
15
- platform: platform.os
16
- }
10
+ @hostname = @config.hostname || `hostname`.chomp
11
+ @architecture = gem_platform.cpu
12
+ @platform = gem_platform.os
17
13
  end
18
14
 
19
- def self.build(config)
20
- new(config).build
21
- end
15
+ attr_reader :hostname, :architecture, :platform
22
16
 
23
17
  private
24
18
 
25
- def platform
26
- @platform ||= Gem::Platform.local
19
+ def gem_platform
20
+ @gem_platform ||= Gem::Platform.local
27
21
  end
28
22
  end
29
23
  end
@@ -26,8 +26,8 @@ module ElasticAPM
26
26
  app.middleware.insert 0, Middleware
27
27
  end
28
28
  rescue StandardError => e
29
- Rails.logger.error "#{Logging::PREFIX}Failed to start: #{e.message}"
30
- Rails.logger.debug e.backtrace.join("\n")
29
+ config.alert_logger.error format('Failed to start: %s', e.message)
30
+ config.alert_logger.debug e.backtrace.join("\n")
31
31
  end
32
32
  end
33
33
 
@@ -4,8 +4,6 @@ module ElasticAPM
4
4
  class Span
5
5
  # @api private
6
6
  class Context
7
- include NaivelyHashable
8
-
9
7
  def initialize(db: nil, http: nil)
10
8
  @db = db && Db.new(db)
11
9
  @http = http && Http.new(http)
@@ -15,8 +13,6 @@ module ElasticAPM
15
13
 
16
14
  # @api private
17
15
  class Db
18
- include NaivelyHashable
19
-
20
16
  def initialize(instance: nil, statement: nil, type: nil, user: nil)
21
17
  @instance = instance
22
18
  @statement = statement
@@ -29,8 +25,6 @@ module ElasticAPM
29
25
 
30
26
  # @api private
31
27
  class Http
32
- include NaivelyHashable
33
-
34
28
  def initialize(url: nil, status_code: nil, method: nil)
35
29
  @url = url
36
30
  @status_code = status_code
@@ -0,0 +1,41 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ElasticAPM
4
+ # @api private
5
+ module Spies
6
+ # @api private
7
+ class FaradaySpy
8
+ # rubocop:disable Metrics/MethodLength
9
+ def install
10
+ ::Faraday::Connection.class_eval do
11
+ alias run_request_without_apm run_request
12
+
13
+ def run_request(method, url, body, headers, &block)
14
+ unless (transaction = ElasticAPM.current_transaction)
15
+ return run_request_without_apm(method, url, body, headers, &block)
16
+ end
17
+
18
+ host = URI(url).host
19
+
20
+ name = "#{method.upcase} #{host}"
21
+ type = "ext.faraday.#{method}"
22
+
23
+ ElasticAPM.with_span name, type do |span|
24
+ ElasticAPM::Spies::NetHTTPSpy.disable_in do
25
+ run_request_without_apm(method, url, body, headers) do |req|
26
+ req['Elastic-Apm-Traceparent'] =
27
+ transaction.traceparent.to_header(span_id: span.id)
28
+
29
+ yield req if block_given?
30
+ end
31
+ end
32
+ end
33
+ end
34
+ end
35
+ end
36
+ # rubocop:enable Metrics/MethodLength
37
+ end
38
+
39
+ register 'Faraday', 'faraday', FaradaySpy.new
40
+ end
41
+ end
@@ -5,7 +5,29 @@ module ElasticAPM
5
5
  module Spies
6
6
  # @api private
7
7
  class NetHTTPSpy
8
+ KEY = :__elastic_apm_net_http_disabled
9
+
8
10
  # rubocop:disable Metrics/MethodLength, Metrics/AbcSize
11
+ class << self
12
+ def disabled=(disabled)
13
+ Thread.current[KEY] = disabled
14
+ end
15
+
16
+ def disabled?
17
+ Thread.current[KEY] ||= false
18
+ end
19
+
20
+ def disable_in
21
+ self.disabled = true
22
+
23
+ begin
24
+ yield
25
+ ensure
26
+ self.disabled = false
27
+ end
28
+ end
29
+ end
30
+
9
31
  def install
10
32
  Net::HTTP.class_eval do
11
33
  alias request_without_apm request
@@ -14,6 +36,9 @@ module ElasticAPM
14
36
  unless (transaction = ElasticAPM.current_transaction)
15
37
  return request_without_apm(req, body, &block)
16
38
  end
39
+ if ElasticAPM::Spies::NetHTTPSpy.disabled?
40
+ return request_without_apm(req, body, &block)
41
+ end
17
42
 
18
43
  host, = req['host'] && req['host'].split(':')
19
44
  method = req.method
@@ -1,5 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'elastic_apm/naively_hashable'
4
+
3
5
  module ElasticAPM
4
6
  class Stacktrace
5
7
  # @api private
@@ -1,8 +1,8 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'elastic_apm/metadata'
3
4
  require 'elastic_apm/transport/connection'
4
5
  require 'elastic_apm/transport/worker'
5
-
6
6
  require 'elastic_apm/transport/serializers'
7
7
  require 'elastic_apm/transport/filters'
8
8
 
@@ -25,7 +25,6 @@ module ElasticAPM
25
25
  attr_reader :config, :queue, :workers, :filters
26
26
 
27
27
  def start
28
- ensure_worker_count
29
28
  end
30
29
 
31
30
  def stop
@@ -4,8 +4,6 @@ require 'http'
4
4
  require 'concurrent'
5
5
  require 'zlib'
6
6
 
7
- require 'elastic_apm/metadata'
8
-
9
7
  module ElasticAPM
10
8
  module Transport
11
9
  # @api private
@@ -30,8 +28,9 @@ module ElasticAPM
30
28
  }.freeze
31
29
  GZIP_HEADERS = HEADERS.merge('Content-Encoding' => 'gzip').freeze
32
30
 
33
- def initialize(config)
31
+ def initialize(config, metadata)
34
32
  @config = config
33
+ @metadata = metadata.to_json
35
34
 
36
35
  @url = config.server_url + '/intake/v2/events'
37
36
 
@@ -44,12 +43,12 @@ module ElasticAPM
44
43
 
45
44
  @client = HTTP.headers(headers).persistent(@url)
46
45
 
47
- @metadata = Metadata.build(config)
48
-
49
46
  @mutex = Mutex.new
50
47
  end
51
48
 
52
49
  def write(str)
50
+ return if @config.disable_send
51
+
53
52
  connect_unless_connected
54
53
 
55
54
  @mutex.synchronize { append(str) }
@@ -15,11 +15,23 @@ module ElasticAPM
15
15
  @config = config
16
16
  end
17
17
 
18
+ attr_reader :config
19
+
18
20
  private
19
21
 
20
22
  def ms(micros)
21
23
  micros.to_f / 1_000
22
24
  end
25
+
26
+ def keyword_field(value)
27
+ Util.truncate(value)
28
+ end
29
+
30
+ def keyword_object(hash)
31
+ hash.tap do |h|
32
+ h.each { |k, v| hash[k] = keyword_field(v) }
33
+ end
34
+ end
23
35
  end
24
36
 
25
37
  # @api private
@@ -28,10 +40,12 @@ module ElasticAPM
28
40
  @transaction = Serializers::TransactionSerializer.new(config)
29
41
  @span = Serializers::SpanSerializer.new(config)
30
42
  @error = Serializers::ErrorSerializer.new(config)
43
+ @metadata = Serializers::MetadataSerializer.new(config)
31
44
  end
32
45
 
33
- attr_reader :transaction, :span, :error
46
+ attr_reader :transaction, :span, :error, :metadata
34
47
 
48
+ # rubocop:disable Metrics/MethodLength
35
49
  def serialize(resource)
36
50
  case resource
37
51
  when Transaction
@@ -40,10 +54,13 @@ module ElasticAPM
40
54
  span.build(resource)
41
55
  when Error
42
56
  error.build(resource)
57
+ when Metadata
58
+ metadata.build(resource)
43
59
  else
44
60
  raise UnrecognizedResource, resource.inspect
45
61
  end
46
62
  end
63
+ # rubocop:enable Metrics/MethodLength
47
64
  end
48
65
 
49
66
  def self.new(config)
@@ -53,6 +70,8 @@ module ElasticAPM
53
70
  end
54
71
  end
55
72
 
56
- require 'elastic_apm/transport/serializers/transaction_serializer'
57
- require 'elastic_apm/transport/serializers/span_serializer'
73
+ require 'elastic_apm/transport/serializers/context_serializer'
58
74
  require 'elastic_apm/transport/serializers/error_serializer'
75
+ require 'elastic_apm/transport/serializers/metadata_serializer'
76
+ require 'elastic_apm/transport/serializers/span_serializer'
77
+ require 'elastic_apm/transport/serializers/transaction_serializer'
@@ -0,0 +1,69 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ElasticAPM
4
+ module Transport
5
+ module Serializers
6
+ # @api private
7
+ class ContextSerializer < Serializer
8
+ def build(context)
9
+ {
10
+ custom: context.custom,
11
+ tags: keyword_object(context.tags),
12
+ request: build_request(context.request),
13
+ response: build_response(context.response),
14
+ user: build_user(context.user)
15
+ }
16
+ end
17
+
18
+ private
19
+
20
+ # rubocop:disable Metrics/MethodLength
21
+ def build_request(request)
22
+ return unless request
23
+
24
+ {
25
+ body: request.body,
26
+ cookies: request.cookies,
27
+ env: request.env,
28
+ headers: request.headers,
29
+ http_version: keyword_field(request.http_version),
30
+ method: keyword_field(request.method),
31
+ socket: build_socket(request.socket),
32
+ url: request.url
33
+ }
34
+ end
35
+ # rubocop:enable Metrics/MethodLength
36
+
37
+ def build_response(response)
38
+ return unless response
39
+
40
+ {
41
+ status_code: response.status_code,
42
+ headers: response.headers,
43
+ headers_sent: response.headers_sent,
44
+ finished: response.finished
45
+ }
46
+ end
47
+
48
+ def build_user(user)
49
+ return unless user
50
+
51
+ {
52
+ id: keyword_field(user.id),
53
+ email: keyword_field(user.email),
54
+ username: keyword_field(user.username)
55
+ }
56
+ end
57
+
58
+ def build_socket(socket)
59
+ return unless socket
60
+
61
+ {
62
+ remote_addr: socket.remote_addr,
63
+ encrypted: socket.encrypted
64
+ }
65
+ end
66
+ end
67
+ end
68
+ end
69
+ end
@@ -5,6 +5,10 @@ module ElasticAPM
5
5
  module Serializers
6
6
  # @api private
7
7
  class ErrorSerializer < Serializer
8
+ def context_serializer
9
+ @context_serializer ||= ContextSerializer.new(config)
10
+ end
11
+
8
12
  # rubocop:disable Metrics/MethodLength
9
13
  def build(error)
10
14
  base = {
@@ -15,7 +19,7 @@ module ElasticAPM
15
19
 
16
20
  culprit: error.culprit,
17
21
  timestamp: error.timestamp,
18
- context: error.context.to_h
22
+ context: context_serializer.build(error.context)
19
23
  }
20
24
 
21
25
  if (exception = error.exception)
@@ -35,9 +39,9 @@ module ElasticAPM
35
39
  def build_exception(exception)
36
40
  {
37
41
  message: exception.message,
38
- type: exception.type,
39
- module: exception.module,
40
- code: exception.code,
42
+ type: keyword_field(exception.type),
43
+ module: keyword_field(exception.module),
44
+ code: keyword_field(exception.code),
41
45
  attributes: exception.attributes,
42
46
  stacktrace: exception.stacktrace.to_a,
43
47
  handled: exception.handled
@@ -47,9 +51,9 @@ module ElasticAPM
47
51
  def build_log(log)
48
52
  {
49
53
  message: log.message,
50
- level: log.level,
51
- logger_name: log.logger_name,
52
- param_message: log.param_message,
54
+ level: keyword_field(log.level),
55
+ logger_name: keyword_field(log.logger_name),
56
+ param_message: keyword_field(log.param_message),
53
57
  stacktrace: log.stacktrace.to_a
54
58
  }
55
59
  end
@@ -0,0 +1,64 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ElasticAPM
4
+ module Transport
5
+ module Serializers
6
+ # @api private
7
+ class MetadataSerializer < Serializer
8
+ def build(metadata)
9
+ {
10
+ metadata: {
11
+ service: build_service(metadata.service),
12
+ process: build_process(metadata.process),
13
+ system: build_system(metadata.system)
14
+ }
15
+ }
16
+ end
17
+
18
+ private
19
+
20
+ # rubocop:disable Metrics/MethodLength, Metrics/AbcSize
21
+ def build_service(service)
22
+ {
23
+ name: keyword_field(service.name),
24
+ environment: keyword_field(service.environment),
25
+ version: keyword_field(service.version),
26
+ agent: {
27
+ name: keyword_field(service.agent.name),
28
+ version: keyword_field(service.agent.version)
29
+ },
30
+ framework: {
31
+ name: keyword_field(service.framework.name),
32
+ version: keyword_field(service.framework.version)
33
+ },
34
+ language: {
35
+ name: keyword_field(service.language.name),
36
+ version: keyword_field(service.language.version)
37
+ },
38
+ runtime: {
39
+ name: keyword_field(service.runtime.name),
40
+ version: keyword_field(service.runtime.version)
41
+ }
42
+ }
43
+ end
44
+ # rubocop:enable Metrics/MethodLength, Metrics/AbcSize
45
+
46
+ def build_process(process)
47
+ {
48
+ pid: process.pid,
49
+ title: keyword_field(process.title),
50
+ argv: process.argv
51
+ }
52
+ end
53
+
54
+ def build_system(system)
55
+ {
56
+ hostname: keyword_field(system.hostname),
57
+ architecture: keyword_field(system.architecture),
58
+ platform: keyword_field(system.platform)
59
+ }
60
+ end
61
+ end
62
+ end
63
+ end
64
+ end
@@ -5,24 +5,68 @@ module ElasticAPM
5
5
  module Serializers
6
6
  # @api private
7
7
  class SpanSerializer < Serializer
8
- # rubocop:disable Metrics/MethodLength
8
+ def initialize(config)
9
+ super
10
+
11
+ @context_serializer = ContextSerializer.new(config)
12
+ end
13
+
14
+ attr_reader :context_serializer
15
+
16
+ # rubocop:disable Metrics/MethodLength, Metrics/AbcSize
9
17
  def build(span)
10
18
  {
11
19
  span: {
12
20
  id: span.id,
13
21
  transaction_id: span.transaction_id,
14
22
  parent_id: span.parent_id,
15
- name: span.name,
16
- type: span.type,
23
+ name: keyword_field(span.name),
24
+ type: keyword_field(span.type),
17
25
  duration: ms(span.duration),
18
- context: span.context&.to_h,
26
+ context: context_serializer.build(span.context),
19
27
  stacktrace: span.stacktrace.to_a,
20
28
  timestamp: span.timestamp,
21
29
  trace_id: span.trace_id
22
30
  }
23
31
  }
24
32
  end
25
- # rubocop:enable Metrics/MethodLength
33
+ # rubocop:enable Metrics/MethodLength, Metrics/AbcSize
34
+
35
+ # @api private
36
+ class ContextSerializer < Serializer
37
+ def build(context)
38
+ return unless context
39
+
40
+ {
41
+ sync: context.sync,
42
+ db: build_db(context.db),
43
+ http: build_http(context.http)
44
+ }
45
+ end
46
+
47
+ private
48
+
49
+ def build_db(db)
50
+ return unless db
51
+
52
+ {
53
+ instance: db.instance,
54
+ statement: db.statement,
55
+ type: db.type,
56
+ user: db.user
57
+ }
58
+ end
59
+
60
+ def build_http(http)
61
+ return unless http
62
+
63
+ {
64
+ url: http.url,
65
+ status_code: http.status_code,
66
+ method: keyword_field(http.method)
67
+ }
68
+ end
69
+ end
26
70
  end
27
71
  end
28
72
  end
@@ -5,20 +5,24 @@ module ElasticAPM
5
5
  module Serializers
6
6
  # @api private
7
7
  class TransactionSerializer < Serializer
8
- # rubocop:disable Metrics/MethodLength
8
+ def context_serializer
9
+ @context_serializer ||= ContextSerializer.new(config)
10
+ end
11
+
12
+ # rubocop:disable Metrics/MethodLength, Metrics/AbcSize
9
13
  def build(transaction)
10
14
  {
11
15
  transaction: {
12
16
  id: transaction.id,
13
17
  trace_id: transaction.trace_id,
14
18
  parent_id: transaction.parent_id,
15
- name: transaction.name,
16
- type: transaction.type,
17
- result: transaction.result.to_s,
19
+ name: keyword_field(transaction.name),
20
+ type: keyword_field(transaction.type),
21
+ result: keyword_field(transaction.result.to_s),
18
22
  duration: ms(transaction.duration),
19
23
  timestamp: transaction.timestamp,
20
24
  sampled: transaction.sampled?,
21
- context: transaction.context.to_h,
25
+ context: context_serializer.build(transaction.context),
22
26
  span_count: {
23
27
  started: transaction.started_spans,
24
28
  dropped: transaction.dropped_spans
@@ -26,7 +30,7 @@ module ElasticAPM
26
30
  }
27
31
  }
28
32
  end
29
- # rubocop:enable Metrics/MethodLength
33
+ # rubocop:enable Metrics/MethodLength, Metrics/AbcSize
30
34
  end
31
35
  end
32
36
  end
@@ -24,9 +24,11 @@ module ElasticAPM
24
24
 
25
25
  @stopping = false
26
26
 
27
- @connection = conn_adapter.new(config)
28
27
  @serializers = serializers
29
28
  @filters = filters
29
+
30
+ metadata = serializers.serialize(Metadata.new(config))
31
+ @connection = conn_adapter.new(config, metadata)
30
32
  end
31
33
 
32
34
  attr_reader :queue, :filters, :name, :connection, :serializers
@@ -39,7 +41,7 @@ module ElasticAPM
39
41
  @stopping
40
42
  end
41
43
 
42
- # rubocop:disable Metrics/MethodLength, Metrics/AbcSize
44
+ # rubocop:disable Metrics/MethodLength
43
45
  def work_forever
44
46
  while (msg = queue.pop)
45
47
  case msg
@@ -59,7 +61,7 @@ module ElasticAPM
59
61
  warn 'Worker died with exception: %s', e.inspect
60
62
  debug e.backtrace.join("\n")
61
63
  end
62
- # rubocop:enable Metrics/MethodLength, Metrics/AbcSize
64
+ # rubocop:enable Metrics/MethodLength
63
65
 
64
66
  private
65
67
 
@@ -24,5 +24,12 @@ module ElasticAPM
24
24
  def self.reverse_merge!(first, second)
25
25
  first.merge!(second) { |_, old, _| old }
26
26
  end
27
+
28
+ def self.truncate(value, max_length: 1024)
29
+ return unless value
30
+ return value if value.length <= max_length
31
+
32
+ value[0...(max_length - 1)] + '…'
33
+ end
27
34
  end
28
35
  end
@@ -0,0 +1,18 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ElasticAPM
4
+ # @api private
5
+ class PrefixedLogger < Logger
6
+ def initialize(logdev, prefix: '', **args)
7
+ super(logdev, **args)
8
+
9
+ @prefix = prefix
10
+ end
11
+
12
+ attr_reader :prefix
13
+
14
+ def add(severity, message = nil, progname = nil)
15
+ super(severity, format('%s%s', prefix, message), progname)
16
+ end
17
+ end
18
+ end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module ElasticAPM
4
- VERSION = '2.0.1'
4
+ VERSION = '2.1.0'
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: 2.0.1
4
+ version: 2.1.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-11-15 00:00:00.000000000 Z
11
+ date: 2018-12-04 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: concurrent-ruby
@@ -66,6 +66,7 @@ files:
66
66
  - bin/console
67
67
  - bin/setup
68
68
  - bin/with_framework
69
+ - docs/advanced.asciidoc
69
70
  - docs/api.asciidoc
70
71
  - docs/configuration.asciidoc
71
72
  - docs/context.asciidoc
@@ -73,6 +74,7 @@ files:
73
74
  - docs/getting-started-rack.asciidoc
74
75
  - docs/getting-started-rails.asciidoc
75
76
  - docs/index.asciidoc
77
+ - docs/introduction.asciidoc
76
78
  - docs/supported-technologies.asciidoc
77
79
  - elastic-apm.gemspec
78
80
  - lib/elastic-apm.rb
@@ -115,6 +117,7 @@ files:
115
117
  - lib/elastic_apm/spies/action_dispatch.rb
116
118
  - lib/elastic_apm/spies/delayed_job.rb
117
119
  - lib/elastic_apm/spies/elasticsearch.rb
120
+ - lib/elastic_apm/spies/faraday.rb
118
121
  - lib/elastic_apm/spies/http.rb
119
122
  - lib/elastic_apm/spies/json.rb
120
123
  - lib/elastic_apm/spies/mongo.rb
@@ -138,13 +141,16 @@ files:
138
141
  - lib/elastic_apm/transport/filters/request_body_filter.rb
139
142
  - lib/elastic_apm/transport/filters/secrets_filter.rb
140
143
  - lib/elastic_apm/transport/serializers.rb
144
+ - lib/elastic_apm/transport/serializers/context_serializer.rb
141
145
  - lib/elastic_apm/transport/serializers/error_serializer.rb
146
+ - lib/elastic_apm/transport/serializers/metadata_serializer.rb
142
147
  - lib/elastic_apm/transport/serializers/span_serializer.rb
143
148
  - lib/elastic_apm/transport/serializers/transaction_serializer.rb
144
149
  - lib/elastic_apm/transport/worker.rb
145
150
  - lib/elastic_apm/util.rb
146
151
  - lib/elastic_apm/util/inflector.rb
147
152
  - lib/elastic_apm/util/lru_cache.rb
153
+ - lib/elastic_apm/util/prefixed_logger.rb
148
154
  - lib/elastic_apm/version.rb
149
155
  - vendor/.gitkeep
150
156
  homepage: https://github.com/elastic/apm-agent-ruby