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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +6 -0
- data/Gemfile +72 -22
- data/LICENSE +1 -1
- data/atatus.gemspec +2 -2
- data/lib/atatus.rb +76 -16
- data/lib/atatus/agent.rb +78 -29
- data/lib/atatus/central_config.rb +72 -27
- data/lib/atatus/central_config/cache_control.rb +18 -1
- data/lib/atatus/child_durations.rb +64 -0
- data/lib/atatus/collector/base.rb +61 -29
- data/lib/atatus/collector/builder.rb +46 -2
- data/lib/atatus/collector/hist.rb +54 -0
- data/lib/atatus/collector/transport.rb +41 -11
- data/lib/atatus/config.rb +129 -28
- data/lib/atatus/config/bytes.rb +17 -0
- data/lib/atatus/config/duration.rb +17 -0
- data/lib/atatus/config/options.rb +29 -9
- data/lib/atatus/config/regexp_list.rb +17 -0
- data/lib/atatus/config/wildcard_pattern_list.rb +64 -0
- data/lib/atatus/context.rb +32 -1
- data/lib/atatus/context/request.rb +17 -0
- data/lib/atatus/context/request/socket.rb +18 -1
- data/lib/atatus/context/request/url.rb +17 -0
- data/lib/atatus/context/response.rb +27 -2
- data/lib/atatus/context/user.rb +17 -0
- data/lib/atatus/context_builder.rb +19 -4
- data/lib/atatus/deprecations.rb +17 -0
- data/lib/atatus/error.rb +27 -0
- data/lib/atatus/error/exception.rb +24 -0
- data/lib/atatus/error/log.rb +17 -0
- data/lib/atatus/error_builder.rb +17 -2
- data/lib/atatus/grape.rb +62 -0
- data/lib/atatus/graphql.rb +91 -0
- data/lib/atatus/grpc.rb +99 -0
- data/lib/atatus/instrumenter.rb +135 -30
- data/lib/atatus/internal_error.rb +17 -0
- data/lib/atatus/logging.rb +17 -2
- data/lib/atatus/metadata.rb +17 -0
- data/lib/atatus/metadata/process_info.rb +17 -0
- data/lib/atatus/metadata/service_info.rb +21 -6
- data/lib/atatus/metadata/system_info.rb +22 -3
- data/lib/atatus/metadata/system_info/container_info.rb +49 -10
- data/lib/atatus/metadata/system_info/hw_info.rb +1 -1
- data/lib/atatus/metrics.rb +69 -27
- data/lib/atatus/metrics/breakdown_set.rb +31 -0
- data/lib/atatus/metrics/{cpu_mem.rb → cpu_mem_set.rb} +110 -63
- data/lib/atatus/metrics/metric.rb +140 -0
- data/lib/atatus/metrics/set.rb +123 -0
- data/lib/atatus/metrics/span_scoped_set.rb +56 -0
- data/lib/atatus/metrics/transaction_set.rb +26 -0
- data/lib/atatus/metrics/vm_set.rb +58 -0
- data/lib/atatus/metricset.rb +48 -4
- data/lib/atatus/middleware.rb +28 -8
- data/lib/atatus/naively_hashable.rb +17 -0
- data/lib/atatus/normalizers.rb +23 -9
- data/lib/atatus/normalizers/grape.rb +22 -0
- data/lib/atatus/normalizers/grape/endpoint_run.rb +65 -0
- data/lib/atatus/normalizers/rails.rb +27 -0
- data/lib/atatus/normalizers/rails/action_controller.rb +44 -0
- data/lib/atatus/normalizers/rails/action_mailer.rb +43 -0
- data/lib/atatus/normalizers/{action_view.rb → rails/action_view.rb} +17 -0
- data/lib/atatus/normalizers/rails/active_record.rb +80 -0
- data/lib/atatus/opentracing.rb +75 -42
- data/lib/atatus/rails.rb +29 -13
- data/lib/atatus/railtie.rb +19 -6
- data/lib/atatus/resque.rb +29 -0
- data/lib/atatus/sinatra.rb +53 -0
- data/lib/atatus/span.rb +44 -15
- data/lib/atatus/span/context.rb +43 -28
- data/lib/atatus/span/context/db.rb +43 -0
- data/lib/atatus/span/context/destination.rb +77 -0
- data/lib/atatus/span/context/http.rb +43 -0
- data/lib/atatus/span_helpers.rb +18 -1
- data/lib/atatus/spies.rb +33 -15
- data/lib/atatus/spies/action_dispatch.rb +27 -6
- data/lib/atatus/spies/delayed_job.rb +26 -5
- data/lib/atatus/spies/dynamo_db.rb +62 -0
- data/lib/atatus/spies/elasticsearch.rb +53 -7
- data/lib/atatus/spies/faraday.rb +54 -20
- data/lib/atatus/spies/http.rb +36 -6
- data/lib/atatus/spies/json.rb +18 -0
- data/lib/atatus/spies/mongo.rb +41 -10
- data/lib/atatus/spies/net_http.rb +52 -11
- data/lib/atatus/spies/rake.rb +42 -23
- data/lib/atatus/spies/redis.rb +17 -0
- data/lib/atatus/spies/resque.rb +57 -0
- data/lib/atatus/spies/sequel.rb +54 -17
- data/lib/atatus/spies/shoryuken.rb +69 -0
- data/lib/atatus/spies/sidekiq.rb +46 -25
- data/lib/atatus/spies/sinatra.rb +20 -4
- data/lib/atatus/spies/sneakers.rb +74 -0
- data/lib/atatus/spies/sucker_punch.rb +58 -0
- data/lib/atatus/spies/tilt.rb +20 -1
- data/lib/atatus/sql.rb +36 -0
- data/lib/atatus/sql/signature.rb +169 -0
- data/lib/atatus/sql/tokenizer.rb +264 -0
- data/lib/atatus/sql/tokens.rb +63 -0
- data/lib/atatus/sql_summarizer.rb +24 -6
- data/lib/atatus/stacktrace.rb +17 -0
- data/lib/atatus/stacktrace/frame.rb +17 -3
- data/lib/atatus/stacktrace_builder.rb +23 -3
- data/lib/atatus/subscriber.rb +23 -4
- data/lib/atatus/trace_context.rb +84 -51
- data/lib/atatus/trace_context/traceparent.rb +111 -0
- data/lib/atatus/trace_context/tracestate.rb +148 -0
- data/lib/atatus/transaction.rb +74 -18
- data/lib/atatus/transport/base.rb +44 -27
- data/lib/atatus/transport/connection.rb +28 -72
- data/lib/atatus/transport/connection/http.rb +58 -35
- data/lib/atatus/transport/connection/proxy_pipe.rb +24 -5
- data/lib/atatus/transport/filters.rb +18 -1
- data/lib/atatus/transport/filters/hash_sanitizer.rb +77 -0
- data/lib/atatus/transport/filters/secrets_filter.rb +30 -55
- data/lib/atatus/transport/headers.rb +83 -0
- data/lib/atatus/transport/serializers.rb +17 -5
- data/lib/atatus/transport/serializers/context_serializer.rb +30 -3
- data/lib/atatus/transport/serializers/error_serializer.rb +17 -2
- data/lib/atatus/transport/serializers/metadata_serializer.rb +44 -22
- data/lib/atatus/transport/serializers/metricset_serializer.rb +34 -6
- data/lib/atatus/transport/serializers/span_serializer.rb +47 -12
- data/lib/atatus/transport/serializers/transaction_serializer.rb +18 -2
- data/lib/atatus/transport/user_agent.rb +48 -0
- data/lib/atatus/transport/worker.rb +31 -7
- data/lib/atatus/util.rb +18 -1
- data/lib/atatus/util/inflector.rb +17 -0
- data/lib/atatus/util/lru_cache.rb +17 -0
- data/lib/atatus/util/throttle.rb +17 -0
- data/lib/atatus/version.rb +19 -1
- metadata +46 -26
- data/Rakefile +0 -19
- data/bench/.gitignore +0 -2
- data/bench/app.rb +0 -53
- data/bench/benchmark.rb +0 -36
- data/bench/report.rb +0 -55
- data/bench/rubyprof.rb +0 -39
- data/bench/stackprof.rb +0 -23
- data/bin/build_docs +0 -5
- data/bin/console +0 -15
- data/bin/setup +0 -8
- data/bin/with_framework +0 -7
- data/lib/atatus/metrics/vm.rb +0 -60
- data/lib/atatus/normalizers/action_controller.rb +0 -27
- data/lib/atatus/normalizers/action_mailer.rb +0 -26
- data/lib/atatus/normalizers/active_record.rb +0 -45
- data/lib/atatus/util/prefixed_logger.rb +0 -18
- data/vendor/.gitkeep +0 -0
|
@@ -1,13 +1,24 @@
|
|
|
1
|
-
#
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
@
|
|
38
|
-
|
|
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
|
-
'
|
|
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
|
-
@
|
|
98
|
-
|
|
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
|
-
#
|
|
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
|
-
|
|
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
|
-
@
|
|
19
|
-
|
|
20
|
-
@
|
|
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
|
|
24
|
-
@
|
|
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
|
|
42
|
+
def self.open(config, url)
|
|
28
43
|
new(config).tap do |http|
|
|
29
|
-
http.open(url
|
|
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
|
-
|
|
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(
|
|
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
|
|
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
|
|
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
|
-
#
|
|
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
|
-
|
|
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(
|
|
69
|
-
pipe = new(
|
|
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
|
|
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
|
-
@
|
|
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, :
|
|
38
|
-
strip_from! payload.dig(:
|
|
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
|