elastic-apm 3.7.0 → 3.11.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.
Files changed (51) hide show
  1. checksums.yaml +4 -4
  2. data/.ci/Jenkinsfile +139 -96
  3. data/.ci/packer_cache.sh +12 -10
  4. data/.rspec +0 -1
  5. data/CHANGELOG.asciidoc +80 -0
  6. data/Gemfile +4 -3
  7. data/bin/run-tests +4 -1
  8. data/docker-compose.yml +2 -0
  9. data/docs/configuration.asciidoc +38 -0
  10. data/docs/debugging.asciidoc +14 -0
  11. data/docs/supported-technologies.asciidoc +2 -1
  12. data/lib/elastic_apm/config.rb +36 -13
  13. data/lib/elastic_apm/config/options.rb +2 -1
  14. data/lib/elastic_apm/config/round_float.rb +31 -0
  15. data/lib/elastic_apm/config/wildcard_pattern_list.rb +13 -1
  16. data/lib/elastic_apm/context_builder.rb +1 -1
  17. data/lib/elastic_apm/grpc.rb +2 -2
  18. data/lib/elastic_apm/instrumenter.rb +10 -3
  19. data/lib/elastic_apm/metadata.rb +3 -1
  20. data/lib/elastic_apm/metadata/cloud_info.rb +128 -0
  21. data/lib/elastic_apm/metadata/service_info.rb +5 -2
  22. data/lib/elastic_apm/metadata/system_info.rb +5 -3
  23. data/lib/elastic_apm/metadata/system_info/container_info.rb +28 -4
  24. data/lib/elastic_apm/middleware.rb +8 -2
  25. data/lib/elastic_apm/opentracing.rb +47 -23
  26. data/lib/elastic_apm/span.rb +7 -3
  27. data/lib/elastic_apm/spies.rb +16 -14
  28. data/lib/elastic_apm/spies/delayed_job.rb +4 -2
  29. data/lib/elastic_apm/spies/dynamo_db.rb +58 -0
  30. data/lib/elastic_apm/spies/elasticsearch.rb +26 -2
  31. data/lib/elastic_apm/spies/mongo.rb +1 -1
  32. data/lib/elastic_apm/spies/net_http.rb +6 -2
  33. data/lib/elastic_apm/spies/sequel.rb +1 -1
  34. data/lib/elastic_apm/trace_context.rb +1 -1
  35. data/lib/elastic_apm/trace_context/traceparent.rb +2 -4
  36. data/lib/elastic_apm/trace_context/tracestate.rb +112 -9
  37. data/lib/elastic_apm/transaction.rb +26 -5
  38. data/lib/elastic_apm/transport/connection.rb +1 -0
  39. data/lib/elastic_apm/transport/filters/hash_sanitizer.rb +70 -0
  40. data/lib/elastic_apm/transport/filters/secrets_filter.rb +14 -56
  41. data/lib/elastic_apm/transport/serializers.rb +8 -6
  42. data/lib/elastic_apm/transport/serializers/metadata_serializer.rb +56 -23
  43. data/lib/elastic_apm/transport/serializers/span_serializer.rb +2 -1
  44. data/lib/elastic_apm/transport/serializers/transaction_serializer.rb +1 -0
  45. data/lib/elastic_apm/transport/user_agent.rb +3 -3
  46. data/lib/elastic_apm/transport/worker.rb +5 -0
  47. data/lib/elastic_apm/util.rb +2 -0
  48. data/lib/elastic_apm/util/precision_validator.rb +46 -0
  49. data/lib/elastic_apm/version.rb +1 -1
  50. metadata +12 -8
  51. data/.ci/downstreamTests.groovy +0 -192
@@ -45,18 +45,20 @@ module ElasticAPM
45
45
  def keyword_object(hash)
46
46
  return unless hash
47
47
 
48
- hash.tap do |h|
49
- h.each { |k, v| hash[k] = keyword_field(v) }
48
+ hash.each do |k, v|
49
+ hash[k] =
50
+ case v
51
+ when Hash then keyword_object(v)
52
+ else keyword_field(v)
53
+ end
50
54
  end
51
55
  end
52
56
 
53
57
  def mixed_object(hash)
54
58
  return unless hash
55
59
 
56
- hash.tap do |h|
57
- h.each do |k, v|
58
- hash[k] = v.is_a?(String) ? keyword_field(v) : v
59
- end
60
+ hash.each do |k, v|
61
+ hash[k] = v.is_a?(String) ? keyword_field(v) : v
60
62
  end
61
63
  end
62
64
  end
@@ -23,40 +23,52 @@ module ElasticAPM
23
23
  # @api private
24
24
  class MetadataSerializer < Serializer
25
25
  def build(metadata)
26
- {
27
- metadata: {
26
+ base =
27
+ {
28
28
  service: build_service(metadata.service),
29
29
  process: build_process(metadata.process),
30
30
  system: build_system(metadata.system),
31
31
  labels: build_labels(metadata.labels)
32
32
  }
33
- }
33
+
34
+ if (metadata.cloud.provider)
35
+ base[:cloud] = build_cloud(metadata.cloud)
36
+ end
37
+
38
+ { metadata: base }
34
39
  end
35
40
 
36
41
  private
37
42
 
38
43
  def build_service(service)
39
- {
40
- name: keyword_field(service.name),
41
- environment: keyword_field(service.environment),
42
- version: keyword_field(service.version),
43
- agent: {
44
- name: keyword_field(service.agent.name),
45
- version: keyword_field(service.agent.version)
46
- },
47
- framework: {
48
- name: keyword_field(service.framework.name),
49
- version: keyword_field(service.framework.version)
50
- },
51
- language: {
52
- name: keyword_field(service.language.name),
53
- version: keyword_field(service.language.version)
54
- },
55
- runtime: {
56
- name: keyword_field(service.runtime.name),
57
- version: keyword_field(service.runtime.version)
44
+ base =
45
+ {
46
+ name: keyword_field(service.name),
47
+ environment: keyword_field(service.environment),
48
+ version: keyword_field(service.version),
49
+ agent: {
50
+ name: keyword_field(service.agent.name),
51
+ version: keyword_field(service.agent.version)
52
+ },
53
+ framework: {
54
+ name: keyword_field(service.framework.name),
55
+ version: keyword_field(service.framework.version)
56
+ },
57
+ language: {
58
+ name: keyword_field(service.language.name),
59
+ version: keyword_field(service.language.version)
60
+ },
61
+ runtime: {
62
+ name: keyword_field(service.runtime.name),
63
+ version: keyword_field(service.runtime.version)
64
+ }
58
65
  }
59
- }
66
+
67
+ if node_name = service.node_name
68
+ base[:node] = { name: keyword_field(node_name) }
69
+ end
70
+
71
+ base
60
72
  end
61
73
 
62
74
  def build_process(process)
@@ -76,6 +88,27 @@ module ElasticAPM
76
88
  }
77
89
  end
78
90
 
91
+ def build_cloud(cloud)
92
+ {
93
+ provider: cloud.provider,
94
+ account: {
95
+ id: keyword_field(cloud.account_id),
96
+ name: keyword_field(cloud.account_name),
97
+ },
98
+ availability_zone: keyword_field(cloud.availability_zone),
99
+ instance: {
100
+ id: keyword_field(cloud.instance_id),
101
+ name: keyword_field(cloud.instance_name),
102
+ },
103
+ machine: { type: keyword_field(cloud.machine_type) },
104
+ project: {
105
+ id: keyword_field(cloud.project_id),
106
+ name: keyword_field(cloud.project_name),
107
+ },
108
+ region: keyword_field(cloud.region)
109
+ }
110
+ end
111
+
79
112
  def build_labels(labels)
80
113
  keyword_object(labels)
81
114
  end
@@ -42,7 +42,8 @@ module ElasticAPM
42
42
  context: context_serializer.build(span.context),
43
43
  stacktrace: span.stacktrace.to_a,
44
44
  timestamp: span.timestamp,
45
- trace_id: span.trace_id
45
+ trace_id: span.trace_id,
46
+ sample_rate: span.sample_rate
46
47
  }
47
48
  }
48
49
  end
@@ -38,6 +38,7 @@ module ElasticAPM
38
38
  duration: ms(transaction.duration),
39
39
  timestamp: transaction.timestamp,
40
40
  sampled: transaction.sampled?,
41
+ sample_rate: transaction.sample_rate,
41
42
  context: context_serializer.build(transaction.context),
42
43
  span_count: {
43
44
  started: transaction.started_spans,
@@ -32,14 +32,14 @@ module ElasticAPM
32
32
  private
33
33
 
34
34
  def build(config)
35
- metadata = Metadata.new(config)
35
+ service = Metadata::ServiceInfo.new(config)
36
36
 
37
37
  [
38
38
  "elastic-apm-ruby/#{VERSION}",
39
39
  HTTP::Request::USER_AGENT,
40
40
  [
41
- metadata.service.runtime.name,
42
- metadata.service.runtime.version
41
+ service.runtime.name,
42
+ service.runtime.version
43
43
  ].join('/')
44
44
  ].join(' ')
45
45
  end
@@ -53,6 +53,7 @@ module ElasticAPM
53
53
  end
54
54
 
55
55
  attr_reader :queue, :filters, :name, :connection, :serializers
56
+
56
57
  def work_forever
57
58
  while (msg = queue.pop)
58
59
  case msg
@@ -77,6 +78,10 @@ module ElasticAPM
77
78
  private
78
79
 
79
80
  def serialize_and_filter(resource)
81
+ if resource.respond_to?(:prepare_for_serialization!)
82
+ resource.prepare_for_serialization!
83
+ end
84
+
80
85
  serialized = serializers.serialize(resource)
81
86
 
82
87
  # if a filter returns nil, it means skip the event
@@ -46,6 +46,8 @@ module ElasticAPM
46
46
 
47
47
  def self.truncate(value, max_length: 1024)
48
48
  return unless value
49
+
50
+ value = String(value)
49
51
  return value if value.length <= max_length
50
52
 
51
53
  value[0...(max_length - 1)] + '…'
@@ -0,0 +1,46 @@
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
+ module Util
22
+ # @api private
23
+ # Rounds half away from zero.
24
+ # If `minimum` is provided, and the value rounds to 0 (but was not zero to
25
+ # begin with), use the minimum instead.
26
+ module PrecisionValidator
27
+ extend self
28
+
29
+ def validate(value, precision: 0, minimum: nil)
30
+ float = Float(value)
31
+ return nil unless (0.0..1.0).cover?(float)
32
+ return float if float == 0
33
+
34
+ multiplier = Float(10**precision)
35
+ rounded = (float * multiplier + 0.5).floor / multiplier
36
+ if rounded == 0 && minimum
37
+ minimum
38
+ else
39
+ rounded
40
+ end
41
+ rescue ArgumentError
42
+ nil
43
+ end
44
+ end
45
+ end
46
+ end
@@ -18,5 +18,5 @@
18
18
  # frozen_string_literal: true
19
19
 
20
20
  module ElasticAPM
21
- VERSION = '3.7.0'
21
+ VERSION = '3.11.0'
22
22
  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: 3.7.0
4
+ version: 3.11.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Mikkel Malmberg
8
- autorequire:
8
+ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2020-05-15 00:00:00.000000000 Z
11
+ date: 2020-10-27 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: concurrent-ruby
@@ -38,7 +38,7 @@ dependencies:
38
38
  - - ">="
39
39
  - !ruby/object:Gem::Version
40
40
  version: '3.0'
41
- description:
41
+ description:
42
42
  email:
43
43
  - mikkel@elastic.co
44
44
  executables: []
@@ -59,7 +59,6 @@ files:
59
59
  - ".ci/docker/jruby/README.md"
60
60
  - ".ci/docker/jruby/run.sh"
61
61
  - ".ci/docker/jruby/test.sh"
62
- - ".ci/downstreamTests.groovy"
63
62
  - ".ci/jobs/apm-agent-ruby-downstream.yml"
64
63
  - ".ci/jobs/apm-agent-ruby-linting-mbp.yml"
65
64
  - ".ci/jobs/apm-agent-ruby-mbp.yml"
@@ -129,6 +128,7 @@ files:
129
128
  - lib/elastic_apm/config/duration.rb
130
129
  - lib/elastic_apm/config/options.rb
131
130
  - lib/elastic_apm/config/regexp_list.rb
131
+ - lib/elastic_apm/config/round_float.rb
132
132
  - lib/elastic_apm/config/wildcard_pattern_list.rb
133
133
  - lib/elastic_apm/context.rb
134
134
  - lib/elastic_apm/context/request.rb
@@ -149,6 +149,7 @@ files:
149
149
  - lib/elastic_apm/internal_error.rb
150
150
  - lib/elastic_apm/logging.rb
151
151
  - lib/elastic_apm/metadata.rb
152
+ - lib/elastic_apm/metadata/cloud_info.rb
152
153
  - lib/elastic_apm/metadata/process_info.rb
153
154
  - lib/elastic_apm/metadata/service_info.rb
154
155
  - lib/elastic_apm/metadata/system_info.rb
@@ -186,6 +187,7 @@ files:
186
187
  - lib/elastic_apm/spies.rb
187
188
  - lib/elastic_apm/spies/action_dispatch.rb
188
189
  - lib/elastic_apm/spies/delayed_job.rb
190
+ - lib/elastic_apm/spies/dynamo_db.rb
189
191
  - lib/elastic_apm/spies/elasticsearch.rb
190
192
  - lib/elastic_apm/spies/faraday.rb
191
193
  - lib/elastic_apm/spies/http.rb
@@ -220,6 +222,7 @@ files:
220
222
  - lib/elastic_apm/transport/connection/http.rb
221
223
  - lib/elastic_apm/transport/connection/proxy_pipe.rb
222
224
  - lib/elastic_apm/transport/filters.rb
225
+ - lib/elastic_apm/transport/filters/hash_sanitizer.rb
223
226
  - lib/elastic_apm/transport/filters/secrets_filter.rb
224
227
  - lib/elastic_apm/transport/headers.rb
225
228
  - lib/elastic_apm/transport/serializers.rb
@@ -234,6 +237,7 @@ files:
234
237
  - lib/elastic_apm/util.rb
235
238
  - lib/elastic_apm/util/inflector.rb
236
239
  - lib/elastic_apm/util/lru_cache.rb
240
+ - lib/elastic_apm/util/precision_validator.rb
237
241
  - lib/elastic_apm/util/throttle.rb
238
242
  - lib/elastic_apm/version.rb
239
243
  homepage: https://github.com/elastic/apm-agent-ruby
@@ -241,7 +245,7 @@ licenses:
241
245
  - Apache-2.0
242
246
  metadata:
243
247
  source_code_uri: https://github.com/elastic/apm-agent-ruby
244
- post_install_message:
248
+ post_install_message:
245
249
  rdoc_options: []
246
250
  require_paths:
247
251
  - lib
@@ -256,8 +260,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
256
260
  - !ruby/object:Gem::Version
257
261
  version: '0'
258
262
  requirements: []
259
- rubygems_version: 3.0.3
260
- signing_key:
263
+ rubygems_version: 3.1.4
264
+ signing_key:
261
265
  specification_version: 4
262
266
  summary: The official Elastic APM agent for Ruby
263
267
  test_files: []
@@ -1,192 +0,0 @@
1
- #!/usr/bin/env groovy
2
- @Library('apm@current') _
3
-
4
- import co.elastic.matrix.*
5
- import groovy.transform.Field
6
-
7
- /**
8
- This is the parallel tasks generator,
9
- it is need as field to store the results of the tests.
10
- */
11
- @Field def rubyTasksGen
12
-
13
- pipeline {
14
- agent { label 'linux && immutable' }
15
- environment {
16
- REPO="git@github.com:elastic/apm-agent-ruby.git"
17
- BASE_DIR="src/github.com/elastic/apm-agent-ruby"
18
- PIPELINE_LOG_LEVEL='INFO'
19
- NOTIFY_TO = credentials('notify-to')
20
- JOB_GCS_BUCKET = credentials('gcs-bucket')
21
- JOB_GIT_CREDENTIALS = "f6c7695a-671e-4f4f-a331-acdce44ff9ba"
22
- DOCKER_REGISTRY = 'docker.elastic.co'
23
- DOCKER_SECRET = 'secret/apm-team/ci/docker-registry/prod'
24
- CODECOV_SECRET = 'secret/apm-team/ci/apm-agent-ruby-codecov'
25
- }
26
- options {
27
- timeout(time: 2, unit: 'HOURS')
28
- buildDiscarder(logRotator(numToKeepStr: '300', artifactNumToKeepStr: '20', daysToKeepStr: '30'))
29
- timestamps()
30
- ansiColor('xterm')
31
- disableResume()
32
- durabilityHint('PERFORMANCE_OPTIMIZED')
33
- rateLimitBuilds(throttle: [count: 60, durationName: 'hour', userBoost: true])
34
- quietPeriod(10)
35
- }
36
- parameters {
37
- string(name: 'RUBY_VERSION', defaultValue: "ruby:2.6", description: "Ruby version to test")
38
- string(name: 'BRANCH_SPECIFIER', defaultValue: "master", description: "Git branch/tag to use")
39
- string(name: 'MERGE_TARGET', defaultValue: "master", description: "Git branch/tag where to merge this code")
40
- }
41
- stages {
42
- /**
43
- Checkout the code and stash it, to use it on other stages.
44
- */
45
- stage('Checkout') {
46
- options { skipDefaultCheckout() }
47
- when {
48
- beforeAgent true
49
- not {
50
- tag pattern: 'v\\d+.*', comparator: "REGEXP"
51
- }
52
- }
53
- steps {
54
- deleteDir()
55
- gitCheckout(basedir: "${BASE_DIR}",
56
- branch: "${params.BRANCH_SPECIFIER}",
57
- repo: "${REPO}",
58
- credentialsId: "${JOB_GIT_CREDENTIALS}",
59
- mergeTarget: "${params.MERGE_TARGET}")
60
- stash allowEmpty: true, name: 'source', useDefaultExcludes: false
61
- }
62
- }
63
- stage('Checkout tag') {
64
- options { skipDefaultCheckout() }
65
- when {
66
- beforeAgent true
67
- tag pattern: 'v\\d+.*', comparator: "REGEXP"
68
- }
69
- steps {
70
- deleteDir()
71
- gitCheckout(basedir: "${BASE_DIR}")
72
- stash allowEmpty: true, name: 'source', useDefaultExcludes: false
73
- }
74
- }
75
- stage('Test') {
76
- options { skipDefaultCheckout() }
77
- steps {
78
- runTests('.ci/.jenkins_framework.yml')
79
- }
80
- }
81
- stage('Master Test') {
82
- options { skipDefaultCheckout() }
83
- steps {
84
- catchError(buildResult: 'SUCCESS', stageResult: 'UNSTABLE', message: "The tests for the master framework have failed. Let's warn instead.") {
85
- runTests('.ci/.jenkins_master_framework.yml')
86
- }
87
- }
88
- }
89
- }
90
- post {
91
- cleanup {
92
- script{
93
- if(rubyTasksGen?.results){
94
- writeJSON(file: 'results.json', json: toJSON(rubyTasksGen.results), pretty: 2)
95
- def mapResults = ["Ruby": rubyTasksGen.results]
96
- def processor = new ResultsProcessor()
97
- processor.processResults(mapResults)
98
- archiveArtifacts allowEmptyArchive: true, artifacts: 'results.json,results.html', defaultExcludes: false
99
- catchError(buildResult: 'SUCCESS') {
100
- def datafile = readFile(file: "results.json")
101
- def json = getVaultSecret(secret: 'secret/apm-team/ci/jenkins-stats-cloud')
102
- sendDataToElasticsearch(es: json.data.url, data: datafile, restCall: '/jenkins-builds-ruby-test-results/_doc/')
103
- }
104
- }
105
- }
106
- notifyBuildResult(prComment: false)
107
- }
108
- }
109
- }
110
-
111
- /**
112
- Parallel task generator for the integration tests.
113
- */
114
- class RubyParallelTaskGenerator extends DefaultParallelTaskGenerator {
115
-
116
- public RubyParallelTaskGenerator(Map params){
117
- super(params)
118
- }
119
-
120
- /**
121
- build a clousure that launch and agent and execute the corresponding test script,
122
- then store the results.
123
- */
124
- public Closure generateStep(x, y){
125
- return {
126
- steps.sleep steps.randomNumber(min:10, max: 30)
127
- steps.node('linux && immutable'){
128
- // Label is transformed to avoid using the internal docker registry in the x coordinate
129
- // TODO: def label = "${tag}:${x?.drop(x?.lastIndexOf('/')+1)}#${y}"
130
- def label = "${tag}:${x}#${y}"
131
- try {
132
- steps.runScript(label: label, ruby: x, framework: y)
133
- saveResult(x, y, 1)
134
- } catch(e){
135
- saveResult(x, y, 0)
136
- steps.error("${label} tests failed : ${e.toString()}\n")
137
- } finally {
138
- steps.junit(allowEmptyResults: true,
139
- keepLongStdio: true,
140
- testResults: "**/spec/junit-reports/**/ruby-agent-junit.xml")
141
- }
142
- }
143
- }
144
- }
145
- }
146
- /**
147
- Run tests for a Ruby version and framework version.
148
- */
149
- def runScript(Map params = [:]){
150
- def label = params.label
151
- def ruby = params.ruby
152
- def framework = params.framework
153
- log(level: 'INFO', text: "${label}")
154
- env.HOME = "${env.WORKSPACE}"
155
- env.PATH = "${env.PATH}:${env.WORKSPACE}/bin"
156
- deleteDir()
157
- unstash 'source'
158
- dir("${BASE_DIR}"){
159
- retry(2){
160
- sleep randomNumber(min:10, max: 30)
161
- dockerLogin(secret: "${DOCKER_SECRET}", registry: "${DOCKER_REGISTRY}")
162
- sh("./spec/scripts/spec.sh ${ruby} ${framework}")
163
- script{
164
- archiveArtifacts(artifacts: "coverage/matrix_results/", defaultExcludes: false)
165
- }
166
- }
167
- }
168
- }
169
-
170
- /**
171
- Run all the tests for the given ruby version and file with the frameworks to test
172
- */
173
- def runTests(frameworkFile) {
174
- deleteDir()
175
- unstash "source"
176
- dir("${BASE_DIR}"){
177
- script {
178
- rubyTasksGen = new RubyParallelTaskGenerator(
179
- xVersions: [ "${params.RUBY_VERSION}" ],
180
- xKey: 'RUBY_VERSION',
181
- yKey: 'FRAMEWORK',
182
- yFile: frameworkFile,
183
- exclusionFile: '.ci/.jenkins_exclude.yml',
184
- tag: 'Ruby',
185
- name: 'Ruby',
186
- steps: this
187
- )
188
- def mapParallelTasks = rubyTasksGen.generateParallelTests()
189
- parallel(mapParallelTasks)
190
- }
191
- }
192
- }