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

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 (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