elastic-apm 3.7.0 → 3.11.0

Sign up to get free protection for your applications and to get access to all the features.
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
- }