logstash-output-elasticsearch-test 11.16.0-x86_64-linux

Sign up to get free protection for your applications and to get access to all the features.
Files changed (88) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +649 -0
  3. data/CONTRIBUTORS +34 -0
  4. data/Gemfile +16 -0
  5. data/LICENSE +202 -0
  6. data/NOTICE.TXT +5 -0
  7. data/README.md +106 -0
  8. data/docs/index.asciidoc +1369 -0
  9. data/lib/logstash/outputs/elasticsearch/data_stream_support.rb +282 -0
  10. data/lib/logstash/outputs/elasticsearch/default-ilm-policy.json +14 -0
  11. data/lib/logstash/outputs/elasticsearch/http_client/manticore_adapter.rb +155 -0
  12. data/lib/logstash/outputs/elasticsearch/http_client/pool.rb +534 -0
  13. data/lib/logstash/outputs/elasticsearch/http_client.rb +497 -0
  14. data/lib/logstash/outputs/elasticsearch/http_client_builder.rb +201 -0
  15. data/lib/logstash/outputs/elasticsearch/ilm.rb +92 -0
  16. data/lib/logstash/outputs/elasticsearch/license_checker.rb +52 -0
  17. data/lib/logstash/outputs/elasticsearch/template_manager.rb +131 -0
  18. data/lib/logstash/outputs/elasticsearch/templates/ecs-disabled/elasticsearch-6x.json +45 -0
  19. data/lib/logstash/outputs/elasticsearch/templates/ecs-disabled/elasticsearch-7x.json +44 -0
  20. data/lib/logstash/outputs/elasticsearch/templates/ecs-disabled/elasticsearch-8x.json +50 -0
  21. data/lib/logstash/outputs/elasticsearch.rb +699 -0
  22. data/lib/logstash/plugin_mixins/elasticsearch/api_configs.rb +237 -0
  23. data/lib/logstash/plugin_mixins/elasticsearch/common.rb +409 -0
  24. data/lib/logstash/plugin_mixins/elasticsearch/noop_license_checker.rb +9 -0
  25. data/logstash-output-elasticsearch.gemspec +40 -0
  26. data/spec/es_spec_helper.rb +225 -0
  27. data/spec/fixtures/_nodes/6x.json +81 -0
  28. data/spec/fixtures/_nodes/7x.json +92 -0
  29. data/spec/fixtures/htpasswd +2 -0
  30. data/spec/fixtures/license_check/active.json +16 -0
  31. data/spec/fixtures/license_check/inactive.json +5 -0
  32. data/spec/fixtures/nginx_reverse_proxy.conf +22 -0
  33. data/spec/fixtures/scripts/painless/scripted_update.painless +2 -0
  34. data/spec/fixtures/scripts/painless/scripted_update_nested.painless +1 -0
  35. data/spec/fixtures/scripts/painless/scripted_upsert.painless +1 -0
  36. data/spec/fixtures/template-with-policy-es6x.json +48 -0
  37. data/spec/fixtures/template-with-policy-es7x.json +45 -0
  38. data/spec/fixtures/template-with-policy-es8x.json +50 -0
  39. data/spec/fixtures/test_certs/ca.crt +29 -0
  40. data/spec/fixtures/test_certs/ca.der.sha256 +1 -0
  41. data/spec/fixtures/test_certs/ca.key +51 -0
  42. data/spec/fixtures/test_certs/renew.sh +13 -0
  43. data/spec/fixtures/test_certs/test.crt +30 -0
  44. data/spec/fixtures/test_certs/test.der.sha256 +1 -0
  45. data/spec/fixtures/test_certs/test.key +51 -0
  46. data/spec/fixtures/test_certs/test.p12 +0 -0
  47. data/spec/fixtures/test_certs/test_invalid.crt +36 -0
  48. data/spec/fixtures/test_certs/test_invalid.key +51 -0
  49. data/spec/fixtures/test_certs/test_invalid.p12 +0 -0
  50. data/spec/fixtures/test_certs/test_self_signed.crt +32 -0
  51. data/spec/fixtures/test_certs/test_self_signed.key +54 -0
  52. data/spec/fixtures/test_certs/test_self_signed.p12 +0 -0
  53. data/spec/integration/outputs/compressed_indexing_spec.rb +70 -0
  54. data/spec/integration/outputs/create_spec.rb +67 -0
  55. data/spec/integration/outputs/data_stream_spec.rb +68 -0
  56. data/spec/integration/outputs/delete_spec.rb +63 -0
  57. data/spec/integration/outputs/ilm_spec.rb +534 -0
  58. data/spec/integration/outputs/index_spec.rb +421 -0
  59. data/spec/integration/outputs/index_version_spec.rb +98 -0
  60. data/spec/integration/outputs/ingest_pipeline_spec.rb +75 -0
  61. data/spec/integration/outputs/metrics_spec.rb +66 -0
  62. data/spec/integration/outputs/no_es_on_startup_spec.rb +78 -0
  63. data/spec/integration/outputs/painless_update_spec.rb +99 -0
  64. data/spec/integration/outputs/parent_spec.rb +94 -0
  65. data/spec/integration/outputs/retry_spec.rb +182 -0
  66. data/spec/integration/outputs/routing_spec.rb +61 -0
  67. data/spec/integration/outputs/sniffer_spec.rb +94 -0
  68. data/spec/integration/outputs/templates_spec.rb +133 -0
  69. data/spec/integration/outputs/unsupported_actions_spec.rb +75 -0
  70. data/spec/integration/outputs/update_spec.rb +114 -0
  71. data/spec/spec_helper.rb +10 -0
  72. data/spec/support/elasticsearch/api/actions/delete_ilm_policy.rb +19 -0
  73. data/spec/support/elasticsearch/api/actions/get_alias.rb +18 -0
  74. data/spec/support/elasticsearch/api/actions/get_ilm_policy.rb +18 -0
  75. data/spec/support/elasticsearch/api/actions/put_alias.rb +24 -0
  76. data/spec/support/elasticsearch/api/actions/put_ilm_policy.rb +25 -0
  77. data/spec/unit/http_client_builder_spec.rb +185 -0
  78. data/spec/unit/outputs/elasticsearch/data_stream_support_spec.rb +612 -0
  79. data/spec/unit/outputs/elasticsearch/http_client/manticore_adapter_spec.rb +151 -0
  80. data/spec/unit/outputs/elasticsearch/http_client/pool_spec.rb +501 -0
  81. data/spec/unit/outputs/elasticsearch/http_client_spec.rb +339 -0
  82. data/spec/unit/outputs/elasticsearch/template_manager_spec.rb +189 -0
  83. data/spec/unit/outputs/elasticsearch_proxy_spec.rb +103 -0
  84. data/spec/unit/outputs/elasticsearch_spec.rb +1573 -0
  85. data/spec/unit/outputs/elasticsearch_ssl_spec.rb +197 -0
  86. data/spec/unit/outputs/error_whitelist_spec.rb +56 -0
  87. data/spec/unit/outputs/license_check_spec.rb +57 -0
  88. metadata +423 -0
@@ -0,0 +1,282 @@
1
+ module LogStash module Outputs class ElasticSearch
2
+ # DS specific behavior/configuration.
3
+ module DataStreamSupport
4
+
5
+ # @api private
6
+ ENABLING_ECS_GUIDANCE = <<~END.tr("\n", " ")
7
+ Elasticsearch data streams require that events adhere to the Elastic Common Schema.
8
+ While `ecs_compatibility` can be set for this individual Elasticsearch output plugin, doing so will not fix schema conflicts caused by upstream plugins in your pipeline.
9
+ To avoid mapping conflicts, you will need to use ECS-compatible field names and datatypes throughout your pipeline.
10
+ Many plugins support an `ecs_compatibility` mode, and the `pipeline.ecs_compatibility` setting can be used to opt-in for all plugins in a pipeline.
11
+ END
12
+ private_constant :ENABLING_ECS_GUIDANCE
13
+
14
+ def self.included(base)
15
+ # Defines whether data will be indexed into an Elasticsearch data stream,
16
+ # `data_stream_*` settings will only be used if this setting is enabled!
17
+ # This setting supports values `true`, `false`, and `auto`.
18
+ # Defaults to `false` in Logstash 7.x and `auto` starting in Logstash 8.0.
19
+ base.config :data_stream, :validate => ['true', 'false', 'auto']
20
+
21
+ base.config :data_stream_type, :validate => ['logs', 'metrics', 'synthetics', 'traces'], :default => 'logs'
22
+ base.config :data_stream_dataset, :validate => :dataset_identifier, :default => 'generic'
23
+ base.config :data_stream_namespace, :validate => :namespace_identifier, :default => 'default'
24
+
25
+ base.config :data_stream_sync_fields, :validate => :boolean, :default => true
26
+ base.config :data_stream_auto_routing, :validate => :boolean, :default => true
27
+
28
+ base.extend(Validator)
29
+ end
30
+
31
+ def data_stream_config?
32
+ @data_stream_config.nil? ? @data_stream_config = check_data_stream_config! : @data_stream_config
33
+ end
34
+
35
+ private
36
+
37
+ def data_stream_name(event)
38
+ data_stream = event.get('data_stream')
39
+ return @index if !data_stream_auto_routing || !data_stream.is_a?(Hash)
40
+
41
+ type = data_stream['type'] || data_stream_type
42
+ dataset = data_stream['dataset'] || data_stream_dataset
43
+ namespace = data_stream['namespace'] || data_stream_namespace
44
+ "#{type}-#{dataset}-#{namespace}"
45
+ end
46
+
47
+ DATA_STREAMS_AND_ECS_ENABLED_BY_DEFAULT_LS_VERSION = '8.0.0'
48
+
49
+ # @param params the user configuration for the ES output
50
+ # @note LS initialized configuration (with filled defaults) won't detect as data-stream
51
+ # compatible, only explicit (`original_params`) config should be tested.
52
+ # @return [Boolean] whether given configuration is data-stream compatible
53
+ def check_data_stream_config!(params = original_params)
54
+ case data_stream_explicit_value
55
+ when false
56
+ check_disabled_data_stream_config!(params)
57
+ return false
58
+ when true
59
+ check_enabled_data_stream_config!(params)
60
+ return true
61
+ else # data_stream => auto or not set
62
+ use_data_stream = data_stream_default(params)
63
+
64
+ check_disabled_data_stream_config!(params) unless use_data_stream
65
+
66
+ @logger.info("Data streams auto configuration (`data_stream => auto` or unset) resolved to `#{use_data_stream}`")
67
+ return use_data_stream
68
+ end
69
+ end
70
+
71
+ def check_enabled_data_stream_config!(params)
72
+ invalid_data_stream_params = invalid_data_stream_params(params)
73
+
74
+ if invalid_data_stream_params.any?
75
+ @logger.error "Invalid data stream configuration, the following parameters are not supported:", invalid_data_stream_params
76
+ raise LogStash::ConfigurationError, "Invalid data stream configuration: #{invalid_data_stream_params.keys}"
77
+ end
78
+
79
+ if ecs_compatibility == :disabled
80
+ if ecs_compatibility_required?
81
+ @logger.error "Invalid data stream configuration; `ecs_compatibility` must not be `disabled`. " + ENABLING_ECS_GUIDANCE
82
+ raise LogStash::ConfigurationError, "Invalid data stream configuration: `ecs_compatibility => disabled`"
83
+ end
84
+
85
+ @deprecation_logger.deprecated "In a future release of Logstash, the Elasticsearch output plugin's `data_stream => true` will require the plugin to be run in ECS compatibility mode. " + ENABLING_ECS_GUIDANCE
86
+ end
87
+ end
88
+
89
+ def check_disabled_data_stream_config!(params)
90
+ data_stream_params = data_stream_params(params)
91
+
92
+ if data_stream_params.any?
93
+ @logger.error "Ambiguous configuration; data stream settings must not be present when data streams are disabled (caused by `data_stream => false`, `data_stream => auto` or unset resolved to false). " \
94
+ "You can either manually set `data_stream => true` or remove the following specific data stream settings: ", data_stream_params
95
+
96
+ raise LogStash::ConfigurationError,
97
+ "Ambiguous configuration; data stream settings must not be present when data streams are disabled: #{data_stream_params.keys}"
98
+ end
99
+ end
100
+
101
+ def data_stream_params(params)
102
+ params.select { |name, _| name.start_with?('data_stream_') }
103
+ end
104
+
105
+ def data_stream_explicit_value
106
+ case @data_stream
107
+ when 'true'
108
+ return true
109
+ when 'false'
110
+ return false
111
+ else
112
+ return nil # 'auto' or not set by user
113
+ end
114
+ end
115
+
116
+ def invalid_data_stream_params(params)
117
+ shared_params = LogStash::PluginMixins::ElasticSearch::APIConfigs::CONFIG_PARAMS.keys.map(&:to_s)
118
+ params.reject do |name, value|
119
+ # NOTE: intentionally do not support explicit DS configuration like:
120
+ # - `index => ...` identifier provided by data_stream_xxx settings
121
+ case name
122
+ when 'action'
123
+ value == 'create'
124
+ when 'routing', 'pipeline'
125
+ true
126
+ when 'data_stream'
127
+ value.to_s == 'true'
128
+ when 'manage_template'
129
+ value.to_s == 'false'
130
+ when 'ecs_compatibility' then true # required for LS <= 6.x
131
+ else
132
+ name.start_with?('data_stream_') ||
133
+ shared_params.include?(name) ||
134
+ inherited_internal_config_param?(name) # 'id', 'enabled_metric' etc
135
+ end
136
+ end
137
+ end
138
+
139
+ def inherited_internal_config_param?(name)
140
+ self.class.superclass.get_config.key?(name.to_s) # superclass -> LogStash::Outputs::Base
141
+ end
142
+
143
+ DATA_STREAMS_ORIGIN_ES_VERSION = '7.9.0'
144
+
145
+ # @note assumes to be running AFTER {after_successful_connection} completed, due ES version checks
146
+ # @return [Gem::Version] if ES supports DS nil (or raise) otherwise
147
+ def assert_es_version_supports_data_streams
148
+ fail 'no last_es_version' unless last_es_version # assert - should not happen
149
+ es_version = ::Gem::Version.create(last_es_version)
150
+ if es_version < ::Gem::Version.create(DATA_STREAMS_ORIGIN_ES_VERSION)
151
+ @logger.error "Elasticsearch version does not support data streams, Logstash might end up writing to an index", es_version: es_version.version
152
+ # NOTE: when switching to synchronous check from register, this should be a ConfigurationError
153
+ raise LogStash::Error, "A data_stream configuration is only supported since Elasticsearch #{DATA_STREAMS_ORIGIN_ES_VERSION} " +
154
+ "(detected version #{es_version.version}), please upgrade your cluster"
155
+ end
156
+ es_version # return truthy
157
+ end
158
+
159
+ # when data_stream => is either 'auto' or not set
160
+ def data_stream_default(params)
161
+ if ecs_compatibility == :disabled
162
+ @logger.info("Not eligible for data streams because ecs_compatibility is not enabled. " + ENABLING_ECS_GUIDANCE)
163
+ return false
164
+ end
165
+
166
+ invalid_data_stream_params = invalid_data_stream_params(params)
167
+
168
+ if data_stream_and_ecs_enabled_by_default?
169
+ if invalid_data_stream_params.any?
170
+ @logger.info("Not eligible for data streams because config contains one or more settings that are not compatible with data streams: #{invalid_data_stream_params.inspect}")
171
+ return false
172
+ end
173
+
174
+ return true
175
+ end
176
+
177
+ # LS 7.x
178
+ if !invalid_data_stream_params.any? && !data_stream_params(params).any?
179
+ @logger.warn "Configuration is data stream compliant but due backwards compatibility Logstash 7.x will not assume " +
180
+ "writing to a data-stream, default behavior will change on Logstash 8.0 " +
181
+ "(set `data_stream => true/false` to disable this warning)"
182
+ end
183
+ false
184
+ end
185
+
186
+ def ecs_compatibility_required?
187
+ data_stream_and_ecs_enabled_by_default?
188
+ end
189
+
190
+ def data_stream_and_ecs_enabled_by_default?
191
+ ::Gem::Version.create(LOGSTASH_VERSION) >= ::Gem::Version.create(DATA_STREAMS_AND_ECS_ENABLED_BY_DEFAULT_LS_VERSION)
192
+ end
193
+
194
+ # an {event_action_tuple} replacement when a data-stream configuration is detected
195
+ def data_stream_event_action_tuple(event)
196
+ event_data = event.to_hash
197
+ data_stream_event_sync(event_data) if data_stream_sync_fields
198
+ EventActionTuple.new('create', common_event_params(event), event, event_data)
199
+ end
200
+
201
+ DATA_STREAM_SYNC_FIELDS = [ 'type', 'dataset', 'namespace' ].freeze
202
+
203
+ def data_stream_event_sync(event_data)
204
+ data_stream = event_data['data_stream']
205
+ if data_stream.is_a?(Hash)
206
+ unless data_stream_auto_routing
207
+ sync_fields = DATA_STREAM_SYNC_FIELDS.select { |name| data_stream.key?(name) && data_stream[name] != send(:"data_stream_#{name}") }
208
+ if sync_fields.any? # these fields will need to be overwritten
209
+ info = sync_fields.inject({}) { |info, name| info[name] = data_stream[name]; info }
210
+ info[:event] = event_data
211
+ @logger.warn "Some data_stream fields are out of sync, these will be updated to reflect data-stream name", info
212
+
213
+ # NOTE: we work directly with event.to_hash data thus fine to mutate the 'data_stream' hash
214
+ sync_fields.each { |name| data_stream[name] = nil } # fallback to ||= bellow
215
+ end
216
+ end
217
+ else
218
+ unless data_stream.nil?
219
+ @logger.warn "Invalid 'data_stream' field type, due fields sync will overwrite", value: data_stream, event: event_data
220
+ end
221
+ event_data['data_stream'] = data_stream = Hash.new
222
+ end
223
+
224
+ data_stream['type'] ||= data_stream_type
225
+ data_stream['dataset'] ||= data_stream_dataset
226
+ data_stream['namespace'] ||= data_stream_namespace
227
+
228
+ event_data
229
+ end
230
+
231
+ module Validator
232
+
233
+ # @override {LogStash::Config::Mixin::validate_value} to handle custom validators
234
+ # @param value [Array<Object>]
235
+ # @param validator [nil,Array,Symbol]
236
+ # @return [Array(true,Object)]: if validation is a success, a tuple containing `true` and the coerced value
237
+ # @return [Array(false,String)]: if validation is a failure, a tuple containing `false` and the failure reason.
238
+ def validate_value(value, validator)
239
+ case validator
240
+ when :dataset_identifier then validate_dataset_identifier(value)
241
+ when :namespace_identifier then validate_namespace_identifier(value)
242
+ else super
243
+ end
244
+ end
245
+
246
+ private
247
+
248
+ def validate_dataset_identifier(value)
249
+ valid, value = validate_value(value, :string)
250
+ return false, value unless valid
251
+
252
+ validate_identifier(value)
253
+ end
254
+
255
+ def validate_namespace_identifier(value)
256
+ valid, value = validate_value(value, :string)
257
+ return false, value unless valid
258
+
259
+ validate_identifier(value)
260
+ end
261
+
262
+ def validate_identifier(value, max_size = 100)
263
+ if value.empty?
264
+ return false, "Invalid identifier - empty string"
265
+ end
266
+ if value.bytesize > max_size
267
+ return false, "Invalid identifier - too long (#{value.bytesize} bytes)"
268
+ end
269
+ # cannot include \, /, *, ?, ", <, >, |, ' ' (space char), ',', #, :
270
+ if value.match? Regexp.union(INVALID_IDENTIFIER_CHARS)
271
+ return false, "Invalid characters detected #{INVALID_IDENTIFIER_CHARS.inspect} are not allowed"
272
+ end
273
+ return true, value
274
+ end
275
+
276
+ INVALID_IDENTIFIER_CHARS = [ '\\', '/', '*', '?', '"', '<', '>', '|', ' ', ',', '#', ':' ]
277
+ private_constant :INVALID_IDENTIFIER_CHARS
278
+
279
+ end
280
+
281
+ end
282
+ end end end
@@ -0,0 +1,14 @@
1
+ {
2
+ "policy" : {
3
+ "phases": {
4
+ "hot" : {
5
+ "actions" : {
6
+ "rollover" : {
7
+ "max_size" : "50gb",
8
+ "max_age":"30d"
9
+ }
10
+ }
11
+ }
12
+ }
13
+ }
14
+ }
@@ -0,0 +1,155 @@
1
+ require 'manticore'
2
+ require 'cgi'
3
+
4
+ module LogStash; module Outputs; class ElasticSearch; class HttpClient;
5
+ DEFAULT_HEADERS = { "Content-Type" => "application/json" }
6
+
7
+ class ManticoreAdapter
8
+ attr_reader :manticore, :logger
9
+
10
+ def initialize(logger, options)
11
+ @logger = logger
12
+ options = options.dup
13
+ options[:ssl] = options[:ssl] || {}
14
+
15
+ # We manage our own retries directly, so let's disable them here
16
+ options[:automatic_retries] = 0
17
+ # We definitely don't need cookies
18
+ options[:cookies] = false
19
+
20
+ @client_params = {:headers => DEFAULT_HEADERS.merge(options[:headers] || {})}
21
+
22
+ if options[:proxy]
23
+ options[:proxy] = manticore_proxy_hash(options[:proxy])
24
+ end
25
+
26
+ @manticore = ::Manticore::Client.new(options)
27
+ end
28
+
29
+ # Transform the proxy option to a hash. Manticore's support for non-hash
30
+ # proxy options is broken. This was fixed in https://github.com/cheald/manticore/commit/34a00cee57a56148629ed0a47c329181e7319af5
31
+ # but this is not yet released
32
+ def manticore_proxy_hash(proxy_uri)
33
+ [:scheme, :port, :user, :password, :path].reduce(:host => proxy_uri.host) do |acc,opt|
34
+ value = proxy_uri.send(opt)
35
+ acc[opt] = value unless value.nil? || (value.is_a?(String) && value.empty?)
36
+ acc
37
+ end
38
+ end
39
+
40
+ def client
41
+ @manticore
42
+ end
43
+
44
+ # Performs the request by invoking {Transport::Base#perform_request} with a block.
45
+ #
46
+ # @return [Response]
47
+ # @see Transport::Base#perform_request
48
+ #
49
+ def perform_request(url, method, path, params={}, body=nil)
50
+ # Perform 2-level deep merge on the params, so if the passed params and client params will both have hashes stored on a key they
51
+ # will be merged as well, instead of choosing just one of the values
52
+ params = (params || {}).merge(@client_params) { |key, oldval, newval|
53
+ (oldval.is_a?(Hash) && newval.is_a?(Hash)) ? oldval.merge(newval) : newval
54
+ }
55
+ params[:body] = body if body
56
+
57
+ if url.user
58
+ params[:auth] = {
59
+ :user => CGI.unescape(url.user),
60
+ # We have to unescape the password here since manticore won't do it
61
+ # for us unless its part of the URL
62
+ :password => CGI.unescape(url.password),
63
+ :eager => true
64
+ }
65
+ end
66
+
67
+ request_uri = format_url(url, path)
68
+ request_uri_as_string = remove_double_escaping(request_uri.to_s)
69
+ begin
70
+ resp = @manticore.send(method.downcase, request_uri_as_string, params)
71
+ # Manticore returns lazy responses by default
72
+ # We want to block for our usage, this will wait for the response to finish
73
+ resp.call
74
+ rescue ::Manticore::ManticoreException => e
75
+ log_request_error(e)
76
+ raise ::LogStash::Outputs::ElasticSearch::HttpClient::Pool::HostUnreachableError.new(e, request_uri_as_string)
77
+ end
78
+
79
+ # 404s are excluded because they are valid codes in the case of
80
+ # template installation. We might need a better story around this later
81
+ # but for our current purposes this is correct
82
+ code = resp.code
83
+ if code < 200 || code > 299 && code != 404
84
+ raise ::LogStash::Outputs::ElasticSearch::HttpClient::Pool::BadResponseCodeError.new(code, request_uri, body, resp.body)
85
+ end
86
+
87
+ resp
88
+ end
89
+
90
+ def log_request_error(e)
91
+ details = { message: e.message, exception: e.class }
92
+ details[:cause] = e.cause if e.respond_to?(:cause)
93
+ details[:backtrace] = e.backtrace if @logger.debug?
94
+
95
+ level = case e
96
+ when ::Manticore::Timeout
97
+ :debug
98
+ when ::Manticore::UnknownException
99
+ :warn
100
+ else
101
+ :info
102
+ end
103
+
104
+ @logger.send level, "Failed to perform request", details
105
+ log_java_exception(details[:cause], :debug) if details[:cause] && @logger.debug?
106
+ end
107
+
108
+ def log_java_exception(e, level = :debug)
109
+ return unless e.is_a?(java.lang.Exception)
110
+ # @logger.name using the same convention as LS does
111
+ logger = self.class.name.gsub('::', '.').downcase
112
+ logger = org.apache.logging.log4j.LogManager.getLogger(logger)
113
+ logger.send(level, '', e) # logger.error('', e) - prints nested causes
114
+ end
115
+
116
+ # Returned urls from this method should be checked for double escaping.
117
+ def format_url(url, path_and_query=nil)
118
+ request_uri = url.clone
119
+
120
+ # We excise auth info from the URL in case manticore itself tries to stick
121
+ # sensitive data in a thrown exception or log data
122
+ request_uri.user = nil
123
+ request_uri.password = nil
124
+
125
+ return request_uri.to_s if path_and_query.nil?
126
+
127
+ parsed_path_and_query = java.net.URI.new(path_and_query)
128
+
129
+ new_query_parts = [request_uri.query, parsed_path_and_query.query].select do |part|
130
+ part && !part.empty? # Skip empty nil and ""
131
+ end
132
+
133
+ request_uri.query = new_query_parts.join("&") unless new_query_parts.empty?
134
+
135
+ # use `raw_path`` as `path` will unescape any escaped '/' in the path
136
+ request_uri.path = "#{request_uri.path}/#{parsed_path_and_query.raw_path}".gsub(/\/{2,}/, "/")
137
+ request_uri
138
+ end
139
+
140
+ # Later versions of SafeURI will also escape the '%' sign in an already escaped URI.
141
+ # (If the path variable is used, it constructs a new java.net.URI object using the multi-arg constructor,
142
+ # which will escape any '%' characters in the path, as opposed to the single-arg constructor which requires illegal
143
+ # characters to be already escaped, and will throw otherwise)
144
+ # The URI needs to have been previously escaped, as it does not play nice with an escaped '/' in the
145
+ # middle of a URI, as required by date math, treating it as a path separator
146
+ def remove_double_escaping(url)
147
+ url.gsub(/%25([0-9A-F]{2})/i, '%\1')
148
+ end
149
+
150
+ def close
151
+ @manticore.close
152
+ end
153
+
154
+ end
155
+ end; end; end; end