elastic-apm 4.1.0 → 4.5.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (49) hide show
  1. checksums.yaml +4 -4
  2. data/.ci/Jenkinsfile +1 -1
  3. data/.ci/docker/jruby/11-jdk/Dockerfile +1 -1
  4. data/.ci/docker/jruby/12-jdk/Dockerfile +1 -1
  5. data/.ci/docker/jruby/13-jdk/Dockerfile +1 -1
  6. data/.ci/docker/jruby/7-jdk/Dockerfile +1 -1
  7. data/.ci/docker/jruby/8-jdk/Dockerfile +1 -1
  8. data/.ci/jobs/apm-agent-ruby-mbp.yml +1 -0
  9. data/CHANGELOG.asciidoc +51 -0
  10. data/Gemfile +5 -4
  11. data/SECURITY.md +7 -0
  12. data/docker-compose.yml +1 -1
  13. data/docs/configuration.asciidoc +26 -2
  14. data/docs/metrics.asciidoc +92 -0
  15. data/elastic-apm.gemspec +8 -8
  16. data/lib/elastic_apm/agent.rb +4 -4
  17. data/lib/elastic_apm/central_config.rb +2 -2
  18. data/lib/elastic_apm/config.rb +20 -0
  19. data/lib/elastic_apm/context/request/socket.rb +1 -2
  20. data/lib/elastic_apm/fields.rb +98 -0
  21. data/lib/elastic_apm/instrumenter.rb +21 -19
  22. data/lib/elastic_apm/metadata/service_info.rb +1 -1
  23. data/lib/elastic_apm/metadata/system_info.rb +2 -1
  24. data/lib/elastic_apm/metrics/cpu_mem_set.rb +9 -6
  25. data/lib/elastic_apm/metrics/jvm_set.rb +88 -0
  26. data/lib/elastic_apm/metrics/metric.rb +2 -0
  27. data/lib/elastic_apm/metrics.rb +22 -8
  28. data/lib/elastic_apm/span/context/destination.rb +39 -58
  29. data/lib/elastic_apm/span.rb +11 -1
  30. data/lib/elastic_apm/spies/azure_storage_table.rb +148 -0
  31. data/lib/elastic_apm/spies/dynamo_db.rb +8 -9
  32. data/lib/elastic_apm/spies/faraday.rb +25 -11
  33. data/lib/elastic_apm/spies/http.rb +3 -4
  34. data/lib/elastic_apm/spies/mongo.rb +11 -3
  35. data/lib/elastic_apm/spies/net_http.rb +21 -14
  36. data/lib/elastic_apm/spies/s3.rb +11 -5
  37. data/lib/elastic_apm/spies/sequel.rb +1 -1
  38. data/lib/elastic_apm/spies/sns.rb +3 -9
  39. data/lib/elastic_apm/spies/sqs.rb +3 -11
  40. data/lib/elastic_apm/spies.rb +20 -0
  41. data/lib/elastic_apm/subscriber.rb +1 -0
  42. data/lib/elastic_apm/transport/connection/http.rb +3 -1
  43. data/lib/elastic_apm/transport/serializers/context_serializer.rb +1 -2
  44. data/lib/elastic_apm/transport/serializers/metadata_serializer.rb +6 -2
  45. data/lib/elastic_apm/transport/serializers/span_serializer.rb +4 -8
  46. data/lib/elastic_apm/transport/user_agent.rb +12 -6
  47. data/lib/elastic_apm/version.rb +1 -1
  48. data/lib/elastic_apm.rb +6 -2
  49. metadata +7 -3
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: b9c57d8f754de9ab6d024f95f2822b0eae82f17a00cd3edae13d290c9b850393
4
- data.tar.gz: 4d32a175d46dac86cabdf97de023a49e7e5eb155a21be11eb227bf61afdb8ffc
3
+ metadata.gz: c396bc981217532f2d6775de89e9a2aa7a1bcd501100588bbd700d0f4f2eb590
4
+ data.tar.gz: 94f859585a09fc99f02a4bbd261193e0ed1f74bdb9463968692a16f277b1e4f0
5
5
  SHA512:
6
- metadata.gz: '09cddb89d0d5265c7c12e8838702cde336754e197d8f4e6647f3885ccde3f6fc806f91b22ab054cf8c5c465373e34bf0f46580a2ab01513da5246d9ea443bb2c'
7
- data.tar.gz: 10732912f09d131204363dd0191e59f689d9560935af8df0b195cd4a145e9402c4a5f3cc3ed136399471f1a406efd7202a7d8384772bc6356b4a64752333cd03
6
+ metadata.gz: ae66a68c4651f695361b2028bf76132e8bc257fc141d3ad2872786bbab10b8e19bc3609a84478de53e56ad0c15695ad7debbcf62c4af8997833d0cf7cc7f6d23
7
+ data.tar.gz: c6b9fd6f80de4139cac0dd040e45d56f869eb2ff8b3db38df376e898490b8c2a5523a9bb3f1ba73a8583356ae5647ec67835b994597dc3db8d72c62ee506794f
data/.ci/Jenkinsfile CHANGED
@@ -39,7 +39,7 @@ pipeline {
39
39
  quietPeriod(10)
40
40
  }
41
41
  triggers {
42
- issueCommentTrigger('(?i).*(?:jenkins\\W+)?run\\W+(?:the\\W+)?(?:benchmark\\W+)?tests(?:\\W+please)?.*')
42
+ issueCommentTrigger("(${obltGitHubComments()}|^run benchmark tests)")
43
43
  }
44
44
  parameters {
45
45
  booleanParam(name: 'Run_As_Master_Branch', defaultValue: false, description: 'Allow to run any steps on a PR, some steps normally only run on master branch.')
@@ -5,7 +5,7 @@ RUN apt-get update \
5
5
  && apt-get install -y gcc \
6
6
  && rm -rf /var/lib/apt/lists/*
7
7
 
8
- ENV JRUBY_VERSION 9.2.10.0
8
+ ENV JRUBY_VERSION 9.2.16.0
9
9
  ENV JRUBY_SHA256 9199707712c683c525252ccb1de5cb8e75f53b790c5b57a18f6367039ec79553
10
10
 
11
11
  RUN mkdir -p /opt/jruby \
@@ -5,7 +5,7 @@ RUN apt-get update \
5
5
  && apt-get install -y gcc \
6
6
  && rm -rf /var/lib/apt/lists/*
7
7
 
8
- ENV JRUBY_VERSION 9.2.10.0
8
+ ENV JRUBY_VERSION 9.3.1.0
9
9
  ENV JRUBY_SHA256 9199707712c683c525252ccb1de5cb8e75f53b790c5b57a18f6367039ec79553
10
10
 
11
11
  RUN mkdir -p /opt/jruby \
@@ -5,7 +5,7 @@ RUN apt-get update \
5
5
  && apt-get install -y gcc \
6
6
  && rm -rf /var/lib/apt/lists/*
7
7
 
8
- ENV JRUBY_VERSION 9.2.10.0
8
+ ENV JRUBY_VERSION 9.3.1.0
9
9
  ENV JRUBY_SHA256 9199707712c683c525252ccb1de5cb8e75f53b790c5b57a18f6367039ec79553
10
10
 
11
11
  RUN mkdir -p /opt/jruby \
@@ -5,7 +5,7 @@ RUN apt-get update \
5
5
  && apt-get install -y gcc \
6
6
  && rm -rf /var/lib/apt/lists/*
7
7
 
8
- ENV JRUBY_VERSION 9.1.17.0
8
+ ENV JRUBY_VERSION 9.3.1.0
9
9
  ENV JRUBY_SHA256 6a22f7bf8fef1a52530a9c9781a9d374ad07bbbef0d3d8e2af0ff5cbead0dfd5
10
10
 
11
11
  RUN mkdir -p /opt/jruby \
@@ -5,7 +5,7 @@ RUN apt-get update \
5
5
  && apt-get install -y gcc \
6
6
  && rm -rf /var/lib/apt/lists/*
7
7
 
8
- ENV JRUBY_VERSION 9.2.10.0
8
+ ENV JRUBY_VERSION 9.2.14.0
9
9
  ENV JRUBY_SHA256 9199707712c683c525252ccb1de5cb8e75f53b790c5b57a18f6367039ec79553
10
10
 
11
11
  RUN mkdir -p /opt/jruby \
@@ -15,6 +15,7 @@
15
15
  repo: apm-agent-ruby
16
16
  repo-owner: elastic
17
17
  credentials-id: 2a9602aa-ab9f-4e52-baf3-b71ca88469c7-UserAndToken
18
+ head-filter-regex: '^(master|PR-.*|[3-4]\.x)$'
18
19
  ssh-checkout:
19
20
  credentials: f6c7695a-671e-4f4f-a331-acdce44ff9ba
20
21
  build-strategies:
data/CHANGELOG.asciidoc CHANGED
@@ -35,6 +35,57 @@ endif::[]
35
35
  [[release-notes-4.x]]
36
36
  === Ruby Agent version 4.x
37
37
 
38
+ [[release-notes-4.5.0]]
39
+ ==== 4.5.0
40
+
41
+ [float]
42
+ ===== Changed
43
+
44
+ - Stop collecting the field `http.request.socket.encrypted` {pull}1181[#1181]
45
+
46
+ [float]
47
+ ===== Fixed
48
+
49
+ - Fixed MongoDB spy thread safety {pull}1202[#1202]
50
+ - Fixed span context fields for DynamoDB instrumentation {pull}1178[#1178]
51
+ - Fixed span context fields for S3 instrumentation {pull}1179[#1179]
52
+ - Update user agent info to match spec {pull}1182[#1182]
53
+
54
+ [[release-notes-4.4.0]]
55
+ ==== 4.4.0
56
+
57
+ [float]
58
+ ===== Added
59
+ - Optional span to be ended instead of current span {pull}1039[#1039]
60
+ - Config option `log_ecs_formatting` {pull}1053[#1053]
61
+
62
+ [float]
63
+ ===== Fixed
64
+ - Fixed detecting Linux on Alpine for CPU/MEM metrics {pull}1057[#1057]
65
+
66
+ [[release-notes-4.3.0]]
67
+ ==== 4.3.0
68
+
69
+ [float]
70
+ ===== Added
71
+
72
+ - Add JVM memory metrics {pull}1040[#1040]
73
+
74
+ [[release-notes-4.2.0]]
75
+ ==== 4.2.0
76
+
77
+ [float]
78
+ ===== Added
79
+
80
+ - Add support for AWS Storage Table/CosmosDB {pull}999[#999]
81
+
82
+ [float]
83
+ ===== Fixed
84
+
85
+ - Align HTTP span types/subtypes with spec {pull}1014[#1014]
86
+ - Passing a full URL as a path to `Net::HTTP` {pull}1029[#1029]
87
+ - Fix growing number of open file descriptors {pull}1033[#1033]
88
+
38
89
  [[release-notes-4.1.0]]
39
90
  ==== 4.1.0
40
91
 
data/Gemfile CHANGED
@@ -36,6 +36,8 @@ gem 'aws-sdk-dynamodb', require: nil
36
36
  gem 'aws-sdk-s3', require: nil
37
37
  gem 'aws-sdk-sqs', require: nil
38
38
  gem 'aws-sdk-sns', require: nil
39
+ gem 'azure-storage-table', require: nil if RUBY_VERSION < '3.0'
40
+ gem 'ecs-logging', require: 'ecs_logging/logger'
39
41
  gem 'elasticsearch', require: nil
40
42
  gem 'fakeredis', require: nil
41
43
  gem 'faraday', require: nil
@@ -60,10 +62,6 @@ gem 'sucker_punch', '~> 2.0', require: nil
60
62
  gem 'yard', require: nil
61
63
  gem 'yarjuf'
62
64
 
63
- # See issue #6547 in the JRuby repo. When that bug is fixed,
64
- # we can use the latest version of the i18n gem.
65
- gem 'i18n', '< 1.8.8'
66
-
67
65
  ## Install Framework
68
66
  GITHUB_REPOS = {
69
67
  'grape' => 'ruby-grape/grape',
@@ -99,6 +97,9 @@ if frameworks_versions.key?('rails')
99
97
  end
100
98
 
101
99
  if RUBY_PLATFORM == 'java'
100
+ # See issue #6547 in the JRuby repo. It is fixed in JRuby 9.3
101
+ gem 'i18n', '< 1.8.8' if JRUBY_VERSION < '9.3'
102
+
102
103
  case rails = frameworks_versions['rails']
103
104
  when 'main'
104
105
  gem 'activerecord-jdbcsqlite3-adapter', git: 'https://github.com/jruby/activerecord-jdbc-adapter', glob: 'activerecord-jdbcsqlite3-adapter/*.gemspec'
data/SECURITY.md ADDED
@@ -0,0 +1,7 @@
1
+ # Security Policy
2
+
3
+ Thanks for your interest in the security of our products.
4
+ Our security policy can be found at [https://www.elastic.co/community/security](https://www.elastic.co/community/security).
5
+
6
+ ## Reporting a Vulnerability
7
+ Please send security vulnerability reports to security@elastic.co.
data/docker-compose.yml CHANGED
@@ -11,7 +11,7 @@ services:
11
11
  build:
12
12
  context: .
13
13
  args:
14
- BUNDLER_VERSION: '2.0.2'
14
+ BUNDLER_VERSION: '2.2.21'
15
15
  image: '$IMAGE_NAME'
16
16
  environment:
17
17
  HOME: '/tmp'
@@ -240,7 +240,7 @@ NOTE: This feature requires APM Server and Kibana >= 7.3.
240
240
  <<dynamic-configuration, image:./images/dynamic-config.svg[] >>
241
241
 
242
242
  |============
243
- | Environment | `Config` key | Default | Example |
243
+ | Environment | `Config` key | Default | Example
244
244
  | `ELASTIC_APM_CAPTURE_BODY` | `capture_body` | `"off"` | `"all"`
245
245
  |============
246
246
 
@@ -502,7 +502,31 @@ Use this option to ignore certain URL patterns such as healthchecks or admin sec
502
502
  | `ELASTIC_APM_INSTRUMENTED_RAKE_TASKS` | `instrumented_rake_tasks` | `[]` | `['my_task']`
503
503
  |============
504
504
 
505
- Elastic APM can instrument your Rake tasks. Theis is an opt-in field, as they are used are for a multitude of things.
505
+ Elastic APM can instrument your Rake tasks. This is an opt-in field, as they are used are for a multitude of things.
506
+
507
+ [float]
508
+ [[config-log-ecs-formatting]]
509
+ ==== `log_ecs_formatting`
510
+
511
+ [options="header"]
512
+ |============
513
+ | Environment | `Config` key | Default
514
+ | `ELASTIC_APM_LOG_ECS_FORMATTING` | `log_ecs_formatting` | `off`
515
+ |============
516
+
517
+ This is an experimental option that configures the agent to use the logger from the `ecs-logging` gem. The two
518
+ valid options are `off` and `override`.
519
+
520
+ Setting this option to `override` will set the agent logger to a `EcsLogging::Logger` instance and all logged output
521
+ will be in ECS-compatible json.
522
+
523
+ The `ecs-logging` gem must be installed before the agent is started. If `log_ecs_formatting` is set to `override`,
524
+ the agent will attempt to require the gem and if it cannot be loaded, it will fall back to using the standard Ruby
525
+ `::Logger` and log the load error.
526
+
527
+ Note that if you're using Rails, the agent will ignore this option and use the Rails logger. If you want to use a
528
+ `EcsLogging::Logger` when using Rails, set the agent's logger config option explicitly to a `EcsLogging::Logger`
529
+ instance.
506
530
 
507
531
  [float]
508
532
  [[config-log-level]]
@@ -141,3 +141,95 @@ The total time spent in garbage collection.
141
141
 
142
142
  **NB:** You need to enable Ruby's GC Profiler for this to get reported.
143
143
  You can do this at any time when your application boots by calling `GC::Profiler.enable`.
144
+
145
+ [float]
146
+ [[metrics-jvm-metrics]]
147
+ === JVM Metrics
148
+
149
+ The following metrics are available when using JRuby. They use the ruby java API to gather metrics via MXBean.
150
+
151
+ [float]
152
+ [[metric-jvm.memory.heap.used]]
153
+ ==== `jvm.memory.heap.used`
154
+
155
+ * *Type:* Long
156
+ * *Format:* Bytes
157
+
158
+ The amount of used heap memory in bytes.
159
+
160
+ [float]
161
+ [[metric-jvm.memory.heap.committed]]
162
+ ==== `jvm.memory.heap.committed`
163
+
164
+ * *Type:* Long
165
+ * *Format:* Bytes
166
+
167
+ The amount of heap memory in bytes that is committed for the Java virtual machine to use. This amount of memory is
168
+ guaranteed for the Java virtual machine to use.
169
+
170
+ [float]
171
+ [[metric-jvm.memory.heap.max]]
172
+ ==== `jvm.memory.heap.max`
173
+
174
+ * *Type:* Long
175
+ * *Format:* Bytes
176
+
177
+ The amount of heap memory in bytes that is committed for the Java virtual machine to use. This amount of memory is
178
+ guaranteed for the Java virtual machine to use.
179
+
180
+ [float]
181
+ [[metric-jvm.memory.non_heap.used]]
182
+ ==== `jvm.memory.non_heap.used`
183
+
184
+ * *Type:* Long
185
+ * *Format:* Bytes
186
+
187
+ The amount of used non-heap memory in bytes.
188
+
189
+ [float]
190
+ [[metric-jvm.memory.non_heap.committed]]
191
+ ==== `jvm.memory.non_heap.committed`
192
+
193
+ * *Type:* Long
194
+ * *Format:* Bytes
195
+
196
+ The amount of non-heap memory in bytes that is committed for the Java virtual machine to use. This amount of memory is
197
+ guaranteed for the Java virtual machine to use.
198
+
199
+ [float]
200
+ [[metric-jvm.memory.non_heap.max]]
201
+ ==== `jvm.memory.non_heap.max`
202
+
203
+ * *Type:* Long
204
+ * *Format:* Bytes
205
+
206
+ The maximum amount of non-heap memory in bytes that can be used for memory management. If the maximum memory size is
207
+ undefined, the value is -1.
208
+
209
+ [float]
210
+ [[metric-jvm.memory.heap.pool.used]]
211
+ ==== `jvm.memory.heap.pool.used`
212
+
213
+ * *Type:* Long
214
+ * *Format:* Bytes
215
+
216
+ The amount of used memory in bytes of the memory pool.
217
+
218
+ [float]
219
+ [[metric-jvm.memory.heap.pool.committed]]
220
+ ==== `jvm.memory.heap.pool.committed`
221
+
222
+ * *Type:* Long
223
+ * *Format:* Bytes
224
+
225
+ The amount of memory in bytes that is committed for the memory pool. This amount of memory is guaranteed for this
226
+ specific pool.
227
+
228
+ [float]
229
+ [[metric-jvm.memory.heap.pool.max]]
230
+ ==== `jvm.memory.heap.pool.max`
231
+
232
+ * *Type:* Long
233
+ * *Format:* Bytes
234
+
235
+ The maximum amount of memory in bytes that can be used for the memory pool.
data/elastic-apm.gemspec CHANGED
@@ -20,15 +20,15 @@ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
20
20
  require 'elastic_apm/version'
21
21
 
22
22
  Gem::Specification.new do |spec|
23
- spec.name = 'elastic-apm'
24
- spec.version = ElasticAPM::VERSION
25
- spec.authors = ['Mikkel Malmberg']
26
- spec.email = ['mikkel@elastic.co']
23
+ spec.name = 'elastic-apm'
24
+ spec.version = ElasticAPM::VERSION
25
+ spec.authors = ['Mikkel Malmberg']
26
+ spec.email = ['mikkel@elastic.co']
27
27
 
28
- spec.summary = 'The official Elastic APM agent for Ruby'
29
- spec.homepage = 'https://github.com/elastic/apm-agent-ruby'
30
- spec.metadata = { 'source_code_uri' => 'https://github.com/elastic/apm-agent-ruby' }
31
- spec.license = 'Apache-2.0'
28
+ spec.summary = 'The official Elastic APM agent for Ruby'
29
+ spec.homepage = 'https://github.com/elastic/apm-agent-ruby'
30
+ spec.metadata = { 'source_code_uri' => 'https://github.com/elastic/apm-agent-ruby' }
31
+ spec.license = 'Apache-2.0'
32
32
  spec.required_ruby_version = ">= 2.3.0"
33
33
 
34
34
  spec.files = `git ls-files -z`.split("\x0").reject do |f|
@@ -211,8 +211,8 @@ module ElasticAPM
211
211
  end
212
212
  # rubocop:enable Metrics/ParameterLists
213
213
 
214
- def end_span
215
- instrumenter.end_span
214
+ def end_span(span = nil)
215
+ instrumenter.end_span(span)
216
216
  end
217
217
 
218
218
  def set_label(key, value)
@@ -280,8 +280,8 @@ module ElasticAPM
280
280
  def detect_forking!
281
281
  return if @pid == Process.pid
282
282
 
283
- config.logger.debug "Forked process detected,
284
- restarting threads in process [PID:#{Process.pid}]"
283
+ config.logger.debug(
284
+ "Forked process detected, restarting threads in process [PID:#{Process.pid}]")
285
285
 
286
286
  central_config.handle_forking!
287
287
  transport.handle_forking!
@@ -119,7 +119,7 @@ module ElasticAPM
119
119
  end
120
120
 
121
121
  if resp.status == 304
122
- info 'Received 304 Not Modified'
122
+ debug 'Received 304 Not Modified'
123
123
  else
124
124
  if resp.body && !resp.body.empty?
125
125
  update = JSON.parse(resp.body.to_s)
@@ -169,7 +169,7 @@ module ElasticAPM
169
169
  end
170
170
 
171
171
  def headers
172
- { 'Etag': @etag }
172
+ { 'If-None-Match': @etag }
173
173
  end
174
174
 
175
175
  def schedule_next_fetch(resp = nil)
@@ -70,6 +70,7 @@ module ElasticAPM
70
70
  option :ignore_url_patterns, type: :list, default: [], converter: RegexpList.new
71
71
  option :instrument, type: :bool, default: true
72
72
  option :instrumented_rake_tasks, type: :list, default: []
73
+ option :log_ecs_formatting, type: :string, default: 'off'
73
74
  option :log_level, type: :int, default: Logger::INFO, converter: LogLevelMap.new
74
75
  option :log_path, type: :string
75
76
  option :metrics_interval, type: :int, default: '30s', converter: Duration.new
@@ -133,6 +134,7 @@ module ElasticAPM
133
134
  def available_instrumentations
134
135
  %w[
135
136
  action_dispatch
137
+ azure_storage_table
136
138
  delayed_job
137
139
  dynamo_db
138
140
  elasticsearch
@@ -245,11 +247,29 @@ module ElasticAPM
245
247
  end
246
248
 
247
249
  def build_logger
250
+ if self.log_ecs_formatting == 'override'
251
+ begin
252
+ return build_ecs_logger
253
+ rescue LoadError
254
+ logger.info "Attempted to use EcsLogging::Logger but the gem couldn't be " \
255
+ "loaded so a ::Logger was created instead. Check if you have the `ecs-logging` " \
256
+ "gem installed and attempt to start the agent again."
257
+ end
258
+ end
259
+
248
260
  Logger.new(log_path == '-' ? $stdout : log_path).tap do |logger|
249
261
  logger.level = log_level
250
262
  end
251
263
  end
252
264
 
265
+ def build_ecs_logger
266
+ require 'ecs_logging/logger'
267
+
268
+ ::EcsLogging::Logger.new(log_path == '-' ? $stdout : log_path).tap do |logger|
269
+ logger.level = log_level
270
+ end
271
+ end
272
+
253
273
  def app_type?(app)
254
274
  if defined?(::Rails::Application) && app.is_a?(::Rails::Application)
255
275
  return :rails
@@ -26,10 +26,9 @@ module ElasticAPM
26
26
  class Socket
27
27
  def initialize(req)
28
28
  @remote_addr = req.env['REMOTE_ADDR']
29
- @encrypted = req.scheme == 'https'
30
29
  end
31
30
 
32
- attr_reader :remote_addr, :encrypted
31
+ attr_reader :remote_addr
33
32
  end
34
33
  end
35
34
  end
@@ -0,0 +1,98 @@
1
+ # Licensed to Elasticsearch B.V. under one or more contributor
2
+ # license agreements. See the NOTICE file distributed with
3
+ # this work for additional information regarding copyright
4
+ # ownership. Elasticsearch B.V. licenses this file to you under
5
+ # the Apache License, Version 2.0 (the "License"); you may
6
+ # not use this file except in compliance with the License.
7
+ # You may obtain a copy of the License at
8
+ #
9
+ # http://www.apache.org/licenses/LICENSE-2.0
10
+ #
11
+ # Unless required by applicable law or agreed to in writing,
12
+ # software distributed under the License is distributed on an
13
+ # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14
+ # KIND, either express or implied. See the License for the
15
+ # specific language governing permissions and limitations
16
+ # under the License.
17
+ #
18
+ # frozen_string_literal: true
19
+
20
+ module ElasticAPM
21
+ # An interface for creating simple, value holding objects that correspond to
22
+ # object fields in the API.
23
+ #
24
+ # Example:
25
+ # class MyThing
26
+ # include Fields
27
+ # field :name
28
+ # field :address, default: 'There'
29
+ # end
30
+ #
31
+ # MyThing.new(name: 'AJ').to_h
32
+ # # => { name: 'AJ' }
33
+ # MyThing.new().empty?
34
+ # # => true
35
+ module Fields
36
+ class Field
37
+ def initialize(key, default: nil)
38
+ @key = key
39
+ @default = default
40
+ end
41
+
42
+ attr_reader :key, :default
43
+ end
44
+
45
+ module InstanceMethods
46
+ def initialize(**attrs)
47
+ schema.each do |key, field|
48
+ send(:"#{key}=", field.default)
49
+ end
50
+
51
+ attrs.each do |key, value|
52
+ send(:"#{key}=", value)
53
+ end
54
+
55
+ super()
56
+ end
57
+
58
+ def empty?
59
+ self.class.schema.each do |key, field|
60
+ next if send(key).nil?
61
+ return false
62
+ end
63
+
64
+ true
65
+ end
66
+
67
+ def to_h
68
+ schema.each_with_object({}) do |(key, field), hsh|
69
+ hsh[key] = send(key)
70
+ end
71
+ end
72
+
73
+ private
74
+
75
+ def schema
76
+ self.class.schema
77
+ end
78
+ end
79
+
80
+ module ClassMethods
81
+ def field(key, default: nil)
82
+ field = Field.new(key, default: default)
83
+ schema[key] = field
84
+
85
+ attr_accessor(key)
86
+ end
87
+
88
+ attr_reader :schema
89
+ end
90
+
91
+ def self.included(cls)
92
+ cls.extend(ClassMethods)
93
+ cls.include(InstanceMethods)
94
+
95
+ cls.instance_variable_set(:@schema, {})
96
+ end
97
+ end
98
+ end
@@ -179,7 +179,8 @@ module ElasticAPM
179
179
  context: nil,
180
180
  trace_context: nil,
181
181
  parent: nil,
182
- sync: nil
182
+ sync: nil,
183
+ exit_span: nil
183
184
  )
184
185
 
185
186
  transaction =
@@ -197,6 +198,15 @@ module ElasticAPM
197
198
 
198
199
  parent ||= (current_span || current_transaction)
199
200
 
201
+ # To not mess with breakdown metric stats, exit spans MUST not add
202
+ # sub-spans unless they share the same type and subtype.
203
+ if parent && parent.is_a?(Span) && parent.exit_span?
204
+ if parent.type != type || parent.subtype != subtype
205
+ debug "Skipping new span '#{name}' as its parent is an exit_span"
206
+ return
207
+ end
208
+ end
209
+
200
210
  span = Span.new(
201
211
  name: name,
202
212
  subtype: subtype,
@@ -207,7 +217,8 @@ module ElasticAPM
207
217
  type: type,
208
218
  context: context,
209
219
  stacktrace_builder: stacktrace_builder,
210
- sync: sync
220
+ sync: sync,
221
+ exit_span: exit_span
211
222
  )
212
223
 
213
224
  if backtrace && transaction.span_frames_min_duration
@@ -222,8 +233,14 @@ module ElasticAPM
222
233
  # rubocop:enable Metrics/CyclomaticComplexity
223
234
  # rubocop:enable Metrics/PerceivedComplexity
224
235
 
225
- def end_span
226
- return unless (span = current_spans.pop)
236
+ def end_span(span = nil)
237
+ if span
238
+ current_spans.delete(span)
239
+ else
240
+ span = current_spans.pop
241
+ end
242
+
243
+ return unless span
227
244
 
228
245
  span.done
229
246
 
@@ -273,24 +290,9 @@ module ElasticAPM
273
290
  'transaction.type': transaction.type
274
291
  }
275
292
 
276
- @metrics.get(:transaction).timer(
277
- :'transaction.duration.sum.us',
278
- tags: tags, reset_on_collect: true
279
- ).update(transaction.duration)
280
-
281
- @metrics.get(:transaction).counter(
282
- :'transaction.duration.count',
283
- tags: tags, reset_on_collect: true
284
- ).inc!
285
-
286
293
  return unless transaction.sampled?
287
294
  return unless transaction.breakdown_metrics
288
295
 
289
- @metrics.get(:breakdown).counter(
290
- :'transaction.breakdown.count',
291
- tags: tags, reset_on_collect: true
292
- ).inc!
293
-
294
296
  span_tags = tags.merge('span.type': 'app')
295
297
 
296
298
  @metrics.get(:breakdown).timer(
@@ -49,7 +49,7 @@ module ElasticAPM
49
49
  )
50
50
  @language = Language.new(name: 'ruby', version: RUBY_VERSION)
51
51
  @runtime = lookup_runtime
52
- @version = @config.service_version || Util.git_sha
52
+ @version = @config.service_version
53
53
  end
54
54
 
55
55
  attr_reader :name, :node_name, :environment, :agent, :framework,
@@ -50,7 +50,8 @@ module ElasticAPM
50
50
  private
51
51
 
52
52
  def detect_hostname
53
- `hostname`.chomp
53
+ Socket.gethostname.chomp
54
+ rescue
54
55
  end
55
56
  end
56
57
  end