atatus 1.3.0 → 1.4.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 (147) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +6 -0
  3. data/Gemfile +72 -22
  4. data/LICENSE +1 -1
  5. data/atatus.gemspec +2 -2
  6. data/lib/atatus.rb +76 -16
  7. data/lib/atatus/agent.rb +78 -29
  8. data/lib/atatus/central_config.rb +72 -27
  9. data/lib/atatus/central_config/cache_control.rb +18 -1
  10. data/lib/atatus/child_durations.rb +64 -0
  11. data/lib/atatus/collector/base.rb +61 -29
  12. data/lib/atatus/collector/builder.rb +46 -2
  13. data/lib/atatus/collector/hist.rb +54 -0
  14. data/lib/atatus/collector/transport.rb +41 -11
  15. data/lib/atatus/config.rb +129 -28
  16. data/lib/atatus/config/bytes.rb +17 -0
  17. data/lib/atatus/config/duration.rb +17 -0
  18. data/lib/atatus/config/options.rb +29 -9
  19. data/lib/atatus/config/regexp_list.rb +17 -0
  20. data/lib/atatus/config/wildcard_pattern_list.rb +64 -0
  21. data/lib/atatus/context.rb +32 -1
  22. data/lib/atatus/context/request.rb +17 -0
  23. data/lib/atatus/context/request/socket.rb +18 -1
  24. data/lib/atatus/context/request/url.rb +17 -0
  25. data/lib/atatus/context/response.rb +27 -2
  26. data/lib/atatus/context/user.rb +17 -0
  27. data/lib/atatus/context_builder.rb +19 -4
  28. data/lib/atatus/deprecations.rb +17 -0
  29. data/lib/atatus/error.rb +27 -0
  30. data/lib/atatus/error/exception.rb +24 -0
  31. data/lib/atatus/error/log.rb +17 -0
  32. data/lib/atatus/error_builder.rb +17 -2
  33. data/lib/atatus/grape.rb +62 -0
  34. data/lib/atatus/graphql.rb +91 -0
  35. data/lib/atatus/grpc.rb +99 -0
  36. data/lib/atatus/instrumenter.rb +135 -30
  37. data/lib/atatus/internal_error.rb +17 -0
  38. data/lib/atatus/logging.rb +17 -2
  39. data/lib/atatus/metadata.rb +17 -0
  40. data/lib/atatus/metadata/process_info.rb +17 -0
  41. data/lib/atatus/metadata/service_info.rb +21 -6
  42. data/lib/atatus/metadata/system_info.rb +22 -3
  43. data/lib/atatus/metadata/system_info/container_info.rb +49 -10
  44. data/lib/atatus/metadata/system_info/hw_info.rb +1 -1
  45. data/lib/atatus/metrics.rb +69 -27
  46. data/lib/atatus/metrics/breakdown_set.rb +31 -0
  47. data/lib/atatus/metrics/{cpu_mem.rb → cpu_mem_set.rb} +110 -63
  48. data/lib/atatus/metrics/metric.rb +140 -0
  49. data/lib/atatus/metrics/set.rb +123 -0
  50. data/lib/atatus/metrics/span_scoped_set.rb +56 -0
  51. data/lib/atatus/metrics/transaction_set.rb +26 -0
  52. data/lib/atatus/metrics/vm_set.rb +58 -0
  53. data/lib/atatus/metricset.rb +48 -4
  54. data/lib/atatus/middleware.rb +28 -8
  55. data/lib/atatus/naively_hashable.rb +17 -0
  56. data/lib/atatus/normalizers.rb +23 -9
  57. data/lib/atatus/normalizers/grape.rb +22 -0
  58. data/lib/atatus/normalizers/grape/endpoint_run.rb +65 -0
  59. data/lib/atatus/normalizers/rails.rb +27 -0
  60. data/lib/atatus/normalizers/rails/action_controller.rb +44 -0
  61. data/lib/atatus/normalizers/rails/action_mailer.rb +43 -0
  62. data/lib/atatus/normalizers/{action_view.rb → rails/action_view.rb} +17 -0
  63. data/lib/atatus/normalizers/rails/active_record.rb +80 -0
  64. data/lib/atatus/opentracing.rb +75 -42
  65. data/lib/atatus/rails.rb +29 -13
  66. data/lib/atatus/railtie.rb +19 -6
  67. data/lib/atatus/resque.rb +29 -0
  68. data/lib/atatus/sinatra.rb +53 -0
  69. data/lib/atatus/span.rb +44 -15
  70. data/lib/atatus/span/context.rb +43 -28
  71. data/lib/atatus/span/context/db.rb +43 -0
  72. data/lib/atatus/span/context/destination.rb +77 -0
  73. data/lib/atatus/span/context/http.rb +43 -0
  74. data/lib/atatus/span_helpers.rb +18 -1
  75. data/lib/atatus/spies.rb +33 -15
  76. data/lib/atatus/spies/action_dispatch.rb +27 -6
  77. data/lib/atatus/spies/delayed_job.rb +26 -5
  78. data/lib/atatus/spies/dynamo_db.rb +62 -0
  79. data/lib/atatus/spies/elasticsearch.rb +53 -7
  80. data/lib/atatus/spies/faraday.rb +54 -20
  81. data/lib/atatus/spies/http.rb +36 -6
  82. data/lib/atatus/spies/json.rb +18 -0
  83. data/lib/atatus/spies/mongo.rb +41 -10
  84. data/lib/atatus/spies/net_http.rb +52 -11
  85. data/lib/atatus/spies/rake.rb +42 -23
  86. data/lib/atatus/spies/redis.rb +17 -0
  87. data/lib/atatus/spies/resque.rb +57 -0
  88. data/lib/atatus/spies/sequel.rb +54 -17
  89. data/lib/atatus/spies/shoryuken.rb +69 -0
  90. data/lib/atatus/spies/sidekiq.rb +46 -25
  91. data/lib/atatus/spies/sinatra.rb +20 -4
  92. data/lib/atatus/spies/sneakers.rb +74 -0
  93. data/lib/atatus/spies/sucker_punch.rb +58 -0
  94. data/lib/atatus/spies/tilt.rb +20 -1
  95. data/lib/atatus/sql.rb +36 -0
  96. data/lib/atatus/sql/signature.rb +169 -0
  97. data/lib/atatus/sql/tokenizer.rb +264 -0
  98. data/lib/atatus/sql/tokens.rb +63 -0
  99. data/lib/atatus/sql_summarizer.rb +24 -6
  100. data/lib/atatus/stacktrace.rb +17 -0
  101. data/lib/atatus/stacktrace/frame.rb +17 -3
  102. data/lib/atatus/stacktrace_builder.rb +23 -3
  103. data/lib/atatus/subscriber.rb +23 -4
  104. data/lib/atatus/trace_context.rb +84 -51
  105. data/lib/atatus/trace_context/traceparent.rb +111 -0
  106. data/lib/atatus/trace_context/tracestate.rb +148 -0
  107. data/lib/atatus/transaction.rb +74 -18
  108. data/lib/atatus/transport/base.rb +44 -27
  109. data/lib/atatus/transport/connection.rb +28 -72
  110. data/lib/atatus/transport/connection/http.rb +58 -35
  111. data/lib/atatus/transport/connection/proxy_pipe.rb +24 -5
  112. data/lib/atatus/transport/filters.rb +18 -1
  113. data/lib/atatus/transport/filters/hash_sanitizer.rb +77 -0
  114. data/lib/atatus/transport/filters/secrets_filter.rb +30 -55
  115. data/lib/atatus/transport/headers.rb +83 -0
  116. data/lib/atatus/transport/serializers.rb +17 -5
  117. data/lib/atatus/transport/serializers/context_serializer.rb +30 -3
  118. data/lib/atatus/transport/serializers/error_serializer.rb +17 -2
  119. data/lib/atatus/transport/serializers/metadata_serializer.rb +44 -22
  120. data/lib/atatus/transport/serializers/metricset_serializer.rb +34 -6
  121. data/lib/atatus/transport/serializers/span_serializer.rb +47 -12
  122. data/lib/atatus/transport/serializers/transaction_serializer.rb +18 -2
  123. data/lib/atatus/transport/user_agent.rb +48 -0
  124. data/lib/atatus/transport/worker.rb +31 -7
  125. data/lib/atatus/util.rb +18 -1
  126. data/lib/atatus/util/inflector.rb +17 -0
  127. data/lib/atatus/util/lru_cache.rb +17 -0
  128. data/lib/atatus/util/throttle.rb +17 -0
  129. data/lib/atatus/version.rb +19 -1
  130. metadata +46 -26
  131. data/Rakefile +0 -19
  132. data/bench/.gitignore +0 -2
  133. data/bench/app.rb +0 -53
  134. data/bench/benchmark.rb +0 -36
  135. data/bench/report.rb +0 -55
  136. data/bench/rubyprof.rb +0 -39
  137. data/bench/stackprof.rb +0 -23
  138. data/bin/build_docs +0 -5
  139. data/bin/console +0 -15
  140. data/bin/setup +0 -8
  141. data/bin/with_framework +0 -7
  142. data/lib/atatus/metrics/vm.rb +0 -60
  143. data/lib/atatus/normalizers/action_controller.rb +0 -27
  144. data/lib/atatus/normalizers/action_mailer.rb +0 -26
  145. data/lib/atatus/normalizers/active_record.rb +0 -45
  146. data/lib/atatus/util/prefixed_logger.rb +0 -18
  147. data/vendor/.gitkeep +0 -0
@@ -1,13 +1,24 @@
1
- # frozen_string_literal: true
2
-
3
- require 'concurrent'
4
- require 'zlib'
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.
5
17
 
6
- require 'atatus/transport/connection/http'
18
+ # frozen_string_literal: true
7
19
 
8
20
  module Atatus
9
21
  module Transport
10
- # rubocop:disable Metrics/ClassLength
11
22
  # @api private
12
23
  class Connection
13
24
  include Logging
@@ -24,26 +35,18 @@ module Atatus
24
35
  # with ongoing write requests to `http`, write and close
25
36
  # requests have to be synchronized.
26
37
 
27
- HEADERS = {
28
- 'Content-Type' => 'application/x-ndjson',
29
- 'Transfer-Encoding' => 'chunked'
30
- }.freeze
31
- GZIP_HEADERS = HEADERS.merge(
32
- 'Content-Encoding' => 'gzip'
33
- ).freeze
34
-
35
- def initialize(config, metadata)
38
+ def initialize(config)
36
39
  @config = config
37
- @headers = build_headers(metadata)
38
- @metadata = JSON.fast_generate(metadata)
40
+ @metadata = JSON.fast_generate(
41
+ Serializers::MetadataSerializer.new(config).build(
42
+ Metadata.new(config)
43
+ )
44
+ )
39
45
  @url = config.server_url + '/intake/v2/events'
40
- @ssl_context = build_ssl_context
41
46
  @mutex = Mutex.new
42
47
  end
43
48
 
44
49
  attr_reader :http
45
-
46
- # rubocop:disable Metrics/MethodLength, Metrics/AbcSize
47
50
  def write(str)
48
51
  return false if @config.disable_send
49
52
 
@@ -69,7 +72,6 @@ module Atatus
69
72
  flush(:connection_error)
70
73
  end
71
74
  end
72
- # rubocop:enable Metrics/MethodLength, Metrics/AbcSize
73
75
 
74
76
  def flush(reason = :force)
75
77
  # Could happen from the timertask so we need to sync
@@ -81,9 +83,8 @@ module Atatus
81
83
 
82
84
  def inspect
83
85
  format(
84
- '@%s http connection closed? :%s>',
85
- super.split.first,
86
- http.closed?
86
+ '<%s url:%s closed:%s >',
87
+ super.split.first, @url, http&.closed?
87
88
  )
88
89
  end
89
90
 
@@ -93,11 +94,9 @@ module Atatus
93
94
  schedule_closing if @config.api_request_time
94
95
 
95
96
  @http =
96
- Http.open(
97
- @config, @url,
98
- headers: @headers,
99
- ssl_context: @ssl_context
100
- ).tap { |http| http.write(@metadata) }
97
+ Http.open(@config, @url).tap do |http|
98
+ http.write(@metadata)
99
+ end
101
100
  end
102
101
  # rubocop:enable
103
102
 
@@ -108,49 +107,6 @@ module Atatus
108
107
  flush(:timeout)
109
108
  end
110
109
  end
111
-
112
- def build_headers(metadata)
113
- (
114
- @config.http_compression? ? GZIP_HEADERS : HEADERS
115
- ).dup.tap do |headers|
116
- headers['User-Agent'] = build_user_agent(metadata)
117
-
118
- if (token = @config.secret_token)
119
- headers['Authorization'] = "Bearer #{token}"
120
- end
121
- end
122
- end
123
-
124
- def build_user_agent(metadata)
125
- runtime = metadata.dig(:metadata, :service, :runtime)
126
-
127
- [
128
- "atatus-ruby/#{VERSION}",
129
- HTTP::Request::USER_AGENT,
130
- [runtime[:name], runtime[:version]].join('/')
131
- ].join(' ')
132
- end
133
-
134
- def build_ssl_context # rubocop:disable Metrics/MethodLength
135
- return unless @config.use_ssl?
136
-
137
- OpenSSL::SSL::SSLContext.new.tap do |context|
138
- if @config.server_ca_cert
139
- context.ca_file = @config.server_ca_cert
140
- else
141
- context.cert_store =
142
- OpenSSL::X509::Store.new.tap(&:set_default_paths)
143
- end
144
-
145
- context.verify_mode =
146
- if @config.verify_server_cert
147
- OpenSSL::SSL::VERIFY_PEER
148
- else
149
- OpenSSL::SSL::VERIFY_NONE
150
- end
151
- end
152
- end
153
110
  end
154
- # rubocop:enable Metrics/ClassLength
155
111
  end
156
112
  end
@@ -1,8 +1,21 @@
1
- # frozen_string_literal: true
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.
2
17
 
3
- require 'http'
4
- require 'concurrent'
5
- require 'zlib'
18
+ # frozen_string_literal: true
6
19
 
7
20
  require 'atatus/transport/connection/proxy_pipe'
8
21
 
@@ -13,23 +26,43 @@ module Atatus
13
26
  class Http
14
27
  include Logging
15
28
 
16
- def initialize(config)
29
+ def initialize(config, headers: nil)
17
30
  @config = config
18
- @closed = Concurrent::AtomicBoolean.new
19
-
20
- @rd, @wr = ProxyPipe.pipe(compress: @config.http_compression?)
31
+ @headers = headers || Headers.new(config)
32
+ @client = build_client
33
+ @closed = Concurrent::AtomicBoolean.new(true)
21
34
  end
22
35
 
23
- def open(url, headers: {}, ssl_context: nil)
24
- @request = open_request_in_thread(url, headers, ssl_context)
36
+ def open(url)
37
+ @closed.make_false
38
+ @rd, @wr = ProxyPipe.pipe(compress: @config.http_compression?)
39
+ @request = open_request_in_thread(url)
25
40
  end
26
41
 
27
- def self.open(config, url, headers: {}, ssl_context: nil)
42
+ def self.open(config, url)
28
43
  new(config).tap do |http|
29
- http.open(url, headers: headers, ssl_context: ssl_context)
44
+ http.open(url)
30
45
  end
31
46
  end
32
47
 
48
+ def post(url, body: nil, headers: nil)
49
+ request(:post, url, body: body, headers: headers)
50
+ end
51
+
52
+ def get(url, headers: nil)
53
+ request(:get, url, headers: headers)
54
+ end
55
+
56
+ def request(method, url, body: nil, headers: nil)
57
+ @client.send(
58
+ method,
59
+ url,
60
+ body: body,
61
+ headers: (headers ? @headers.merge(headers) : @headers).to_h,
62
+ ssl_context: @config.ssl_context
63
+ ).flush
64
+ end
65
+
33
66
  def write(str)
34
67
  @wr.write(str)
35
68
  @wr.bytes_sent
@@ -69,23 +102,27 @@ module Atatus
69
102
  format('[THREAD:%s]', Thread.current.object_id)
70
103
  end
71
104
 
72
- # rubocop:disable Metrics/LineLength
73
- def open_request_in_thread(url, headers, ssl_context)
74
- client = build_client(headers)
75
-
105
+ def open_request_in_thread(url)
76
106
  debug '%s: Opening new request', thread_str
77
107
  Thread.new do
78
108
  begin
79
- post(client, url, ssl_context)
109
+ resp = post(url, body: @rd, headers: @headers.chunked.to_h)
110
+
111
+ if resp&.status == 202
112
+ debug 'APM Server responded with status 202'
113
+ elsif resp
114
+ error "APM Server responded with an error:\n%p", resp.body.to_s
115
+ end
80
116
  rescue Exception => e
81
- error "Couldn't establish connection to APM Server:\n%p", e.inspect
117
+ error(
118
+ "Couldn't establish connection to APM Server:\n%p", e.inspect
119
+ )
82
120
  end
83
121
  end
84
122
  end
85
- # rubocop:enable Metrics/LineLength
86
123
 
87
- def build_client(headers)
88
- client = HTTP.headers(headers)
124
+ def build_client
125
+ client = HTTP.headers(@headers)
89
126
  return client unless @config.proxy_address && @config.proxy_port
90
127
 
91
128
  client.via(
@@ -96,20 +133,6 @@ module Atatus
96
133
  @config.proxy_headers
97
134
  )
98
135
  end
99
-
100
- def post(client, url, ssl_context)
101
- resp = client.post(
102
- url,
103
- body: @rd,
104
- ssl_context: ssl_context
105
- ).flush
106
-
107
- if resp&.status == 202
108
- debug 'APM Server responded with status 202'
109
- elsif resp
110
- error "APM Server responded with an error:\n%p", resp.body.to_s
111
- end
112
- end
113
136
  end
114
137
  end
115
138
  end
@@ -1,7 +1,21 @@
1
- # frozen_string_literal: true
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.
2
17
 
3
- require 'concurrent'
4
- require 'zlib'
18
+ # frozen_string_literal: true
5
19
 
6
20
  module Atatus
7
21
  module Transport
@@ -34,6 +48,11 @@ module Atatus
34
48
 
35
49
  return unless compress
36
50
  enable_compression!
51
+ ObjectSpace.define_finalizer(self, self.class.finalize(@io))
52
+ end
53
+
54
+ def self.finalize(io)
55
+ proc { io.close }
37
56
  end
38
57
 
39
58
  attr_reader :io
@@ -65,8 +84,8 @@ module Atatus
65
84
  end
66
85
  end
67
86
 
68
- def self.pipe(*args)
69
- pipe = new(*args)
87
+ def self.pipe(**args)
88
+ pipe = new(**args)
70
89
  [pipe.read, pipe.write]
71
90
  end
72
91
  end
@@ -1,3 +1,20 @@
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
+
1
18
  # frozen_string_literal: true
2
19
 
3
20
  require 'atatus/transport/filters/secrets_filter'
@@ -19,7 +36,7 @@ module Atatus
19
36
  end
20
37
 
21
38
  def add(key, filter)
22
- @filters[key] = filter
39
+ @filters = @filters.merge(key => filter)
23
40
  end
24
41
 
25
42
  def remove(key)
@@ -0,0 +1,77 @@
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 Atatus
21
+ module Transport
22
+ module Filters
23
+ class HashSanitizer
24
+ FILTERED = '[FILTERED]'
25
+
26
+ KEY_FILTERS = [
27
+ /passw(or)?d/i,
28
+ /auth/i,
29
+ /^pw$/,
30
+ /secret/i,
31
+ /token/i,
32
+ /api[-._]?key/i,
33
+ /session[-._]?id/i,
34
+ /(set[-_])?cookie/i
35
+ ].freeze
36
+
37
+ VALUE_FILTERS = [
38
+ # (probably) credit card number
39
+ /^\d{4}[- ]?\d{4}[- ]?\d{4}[- ]?\d{4}$/
40
+ ].freeze
41
+
42
+ attr_accessor :key_filters
43
+
44
+ def initialize
45
+ @key_filters = KEY_FILTERS
46
+ end
47
+
48
+ def strip_from!(obj, key_filters = KEY_FILTERS)
49
+ return unless obj&.is_a?(Hash)
50
+
51
+ obj.each do |k, v|
52
+ if filter_key?(k)
53
+ next obj[k] = FILTERED
54
+ end
55
+
56
+ case v
57
+ when Hash
58
+ strip_from!(v)
59
+ when String
60
+ if filter_value?(v)
61
+ obj[k] = FILTERED
62
+ end
63
+ end
64
+ end
65
+ end
66
+
67
+ def filter_key?(key)
68
+ @key_filters.any? { |regex| regex.match(key) }
69
+ end
70
+
71
+ def filter_value?(value)
72
+ VALUE_FILTERS.any? { |regex| regex.match(value) }
73
+ end
74
+ end
75
+ end
76
+ end
77
+ end
@@ -1,73 +1,48 @@
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
+
1
18
  # frozen_string_literal: true
2
19
 
20
+ require 'atatus/transport/filters/hash_sanitizer'
21
+
3
22
  module Atatus
4
23
  module Transport
5
24
  module Filters
6
25
  # @api private
7
26
  class SecretsFilter
8
- FILTERED = '[FILTERED]'
9
-
10
- KEY_FILTERS = [
11
- /passw(or)?d/i,
12
- /auth/i,
13
- /^pw$/,
14
- /secret/i,
15
- /token/i,
16
- /api[-._]?key/i,
17
- /session[-._]?id/i,
18
- /(set[-_])?cookie/i
19
- ].freeze
20
-
21
- VALUE_FILTERS = [
22
- # (probably) credit card number
23
- /^\d{4}[- ]?\d{4}[- ]?\d{4}[- ]?\d{4}$/
24
- ].freeze
25
-
26
27
  def initialize(config)
27
28
  @config = config
28
- @key_filters = KEY_FILTERS + config.custom_key_filters
29
+ @sanitizer = HashSanitizer.new
30
+ @sanitizer.key_filters += config.custom_key_filters +
31
+ config.sanitize_field_names
29
32
  end
30
33
 
31
34
  def call(payload)
32
- strip_from! payload.dig(:transaction, :context, :request, :headers)
33
- strip_from! payload.dig(:transaction, :context, :request, :env)
34
- strip_from! payload.dig(:transaction, :context, :request, :cookies)
35
- strip_from! payload.dig(:transaction, :context, :response, :headers)
36
- strip_from! payload.dig(:error, :context, :request, :headers)
37
- strip_from! payload.dig(:error, :context, :response, :headers)
38
- strip_from! payload.dig(:transaction, :context, :request, :body)
35
+ @sanitizer.strip_from! payload.dig(:transaction, :context, :request, :headers)
36
+ @sanitizer.strip_from! payload.dig(:transaction, :context, :request, :env)
37
+ @sanitizer.strip_from! payload.dig(:transaction, :context, :request, :cookies)
38
+ @sanitizer.strip_from! payload.dig(:transaction, :context, :response, :headers)
39
+ @sanitizer.strip_from! payload.dig(:error, :context, :request, :headers)
40
+ @sanitizer.strip_from! payload.dig(:error, :context, :request, :cookies)
41
+ @sanitizer.strip_from! payload.dig(:error, :context, :response, :headers)
42
+ @sanitizer.strip_from! payload.dig(:transaction, :context, :request, :body)
39
43
 
40
44
  payload
41
45
  end
42
-
43
- # rubocop:disable Metrics/MethodLength, Metrics/CyclomaticComplexity
44
- def strip_from!(obj)
45
- return unless obj && obj.is_a?(Hash)
46
-
47
- obj.each do |k, v|
48
- if filter_key?(k)
49
- next obj[k] = FILTERED
50
- end
51
-
52
- case v
53
- when Hash
54
- strip_from!(v)
55
- when String
56
- if filter_value?(v)
57
- obj[k] = FILTERED
58
- end
59
- end
60
- end
61
- end
62
- # rubocop:enable Metrics/MethodLength, Metrics/CyclomaticComplexity
63
-
64
- def filter_key?(key)
65
- @key_filters.any? { |regex| key.match regex }
66
- end
67
-
68
- def filter_value?(value)
69
- VALUE_FILTERS.any? { |regex| value.match regex }
70
- end
71
46
  end
72
47
  end
73
48
  end