fluent-plugin-elasticsearch 1.9.4 → 5.0.3
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 +5 -5
- data/.github/ISSUE_TEMPLATE/bug_report.md +37 -0
- data/.github/ISSUE_TEMPLATE/feature_request.md +24 -0
- data/.github/workflows/issue-auto-closer.yml +12 -0
- data/.github/workflows/linux.yml +26 -0
- data/.github/workflows/macos.yml +26 -0
- data/.github/workflows/windows.yml +26 -0
- data/.travis.yml +33 -6
- data/CONTRIBUTING.md +24 -0
- data/Gemfile +4 -1
- data/History.md +445 -1
- data/ISSUE_TEMPLATE.md +19 -0
- data/README.ElasticsearchGenID.md +116 -0
- data/README.ElasticsearchInput.md +293 -0
- data/README.Troubleshooting.md +692 -0
- data/README.md +1013 -38
- data/appveyor.yml +20 -0
- data/fluent-plugin-elasticsearch.gemspec +15 -9
- data/{Gemfile.v0.12 → gemfiles/Gemfile.elasticsearch.v6} +6 -5
- data/lib/fluent/log-ext.rb +38 -0
- data/lib/fluent/plugin/default-ilm-policy.json +14 -0
- data/lib/fluent/plugin/elasticsearch_constants.rb +13 -0
- data/lib/fluent/plugin/elasticsearch_error.rb +5 -0
- data/lib/fluent/plugin/elasticsearch_error_handler.rb +129 -0
- data/lib/fluent/plugin/elasticsearch_fallback_selector.rb +9 -0
- data/lib/fluent/plugin/elasticsearch_index_lifecycle_management.rb +67 -0
- data/lib/fluent/plugin/elasticsearch_index_template.rb +186 -12
- data/lib/fluent/plugin/elasticsearch_simple_sniffer.rb +10 -0
- data/lib/fluent/plugin/elasticsearch_tls.rb +70 -0
- data/lib/fluent/plugin/filter_elasticsearch_genid.rb +77 -0
- data/lib/fluent/plugin/in_elasticsearch.rb +325 -0
- data/lib/fluent/plugin/oj_serializer.rb +22 -0
- data/lib/fluent/plugin/out_elasticsearch.rb +1008 -267
- data/lib/fluent/plugin/out_elasticsearch_data_stream.rb +218 -0
- data/lib/fluent/plugin/out_elasticsearch_dynamic.rb +232 -214
- data/test/plugin/test_alias_template.json +9 -0
- data/test/plugin/test_elasticsearch_error_handler.rb +646 -0
- data/test/plugin/test_elasticsearch_fallback_selector.rb +74 -0
- data/test/plugin/test_elasticsearch_index_lifecycle_management.rb +66 -0
- data/test/plugin/test_elasticsearch_tls.rb +145 -0
- data/test/plugin/test_filter_elasticsearch_genid.rb +215 -0
- data/test/plugin/test_in_elasticsearch.rb +459 -0
- data/test/plugin/test_index_alias_template.json +11 -0
- data/test/plugin/test_index_template.json +25 -0
- data/test/plugin/test_oj_serializer.rb +19 -0
- data/test/plugin/test_out_elasticsearch.rb +5029 -387
- data/test/plugin/test_out_elasticsearch_data_stream.rb +337 -0
- data/test/plugin/test_out_elasticsearch_dynamic.rb +681 -208
- data/test/test_log-ext.rb +35 -0
- metadata +97 -19
@@ -0,0 +1,218 @@
|
|
1
|
+
require_relative 'out_elasticsearch'
|
2
|
+
|
3
|
+
module Fluent::Plugin
|
4
|
+
class ElasticsearchOutputDataStream < ElasticsearchOutput
|
5
|
+
|
6
|
+
Fluent::Plugin.register_output('elasticsearch_data_stream', self)
|
7
|
+
|
8
|
+
helpers :event_emitter
|
9
|
+
|
10
|
+
config_param :data_stream_name, :string
|
11
|
+
# Elasticsearch 7.9 or later always support new style of index template.
|
12
|
+
config_set_default :use_legacy_template, false
|
13
|
+
|
14
|
+
INVALID_START_CHRACTERS = ["-", "_", "+", "."]
|
15
|
+
INVALID_CHARACTERS = ["\\", "/", "*", "?", "\"", "<", ">", "|", " ", ",", "#", ":"]
|
16
|
+
|
17
|
+
def configure(conf)
|
18
|
+
super
|
19
|
+
|
20
|
+
begin
|
21
|
+
require 'elasticsearch/api'
|
22
|
+
require 'elasticsearch/xpack'
|
23
|
+
rescue LoadError
|
24
|
+
raise Fluent::ConfigError, "'elasticsearch/api', 'elasticsearch/xpack' are required for <@elasticsearch_data_stream>."
|
25
|
+
end
|
26
|
+
|
27
|
+
# ref. https://www.elastic.co/guide/en/elasticsearch/reference/master/indices-create-data-stream.html
|
28
|
+
unless placeholder?(:data_stream_name_placeholder, @data_stream_name)
|
29
|
+
validate_data_stream_name
|
30
|
+
else
|
31
|
+
@use_placeholder = true
|
32
|
+
@data_stream_names = []
|
33
|
+
end
|
34
|
+
|
35
|
+
@client = client
|
36
|
+
unless @use_placeholder
|
37
|
+
begin
|
38
|
+
@data_stream_names = [@data_stream_name]
|
39
|
+
create_ilm_policy(@data_stream_name)
|
40
|
+
create_index_template(@data_stream_name)
|
41
|
+
create_data_stream(@data_stream_name)
|
42
|
+
rescue => e
|
43
|
+
raise Fluent::ConfigError, "Failed to create data stream: <#{@data_stream_name}> #{e.message}"
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def validate_data_stream_name
|
49
|
+
unless valid_data_stream_name?
|
50
|
+
unless start_with_valid_characters?
|
51
|
+
if not_dots?
|
52
|
+
raise Fluent::ConfigError, "'data_stream_name' must not start with #{INVALID_START_CHRACTERS.join(",")}: <#{@data_stream_name}>"
|
53
|
+
else
|
54
|
+
raise Fluent::ConfigError, "'data_stream_name' must not be . or ..: <#{@data_stream_name}>"
|
55
|
+
end
|
56
|
+
end
|
57
|
+
unless valid_characters?
|
58
|
+
raise Fluent::ConfigError, "'data_stream_name' must not contain invalid characters #{INVALID_CHARACTERS.join(",")}: <#{@data_stream_name}>"
|
59
|
+
end
|
60
|
+
unless lowercase_only?
|
61
|
+
raise Fluent::ConfigError, "'data_stream_name' must be lowercase only: <#{@data_stream_name}>"
|
62
|
+
end
|
63
|
+
if @data_stream_name.bytes.size > 255
|
64
|
+
raise Fluent::ConfigError, "'data_stream_name' must not be longer than 255 bytes: <#{@data_stream_name}>"
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
def create_ilm_policy(name)
|
70
|
+
return if data_stream_exist?(name)
|
71
|
+
params = {
|
72
|
+
policy_id: "#{name}_policy",
|
73
|
+
body: File.read(File.join(File.dirname(__FILE__), "default-ilm-policy.json"))
|
74
|
+
}
|
75
|
+
retry_operate(@max_retry_putting_template,
|
76
|
+
@fail_on_putting_template_retry_exceed,
|
77
|
+
@catch_transport_exception_on_retry) do
|
78
|
+
@client.xpack.ilm.put_policy(params)
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
def create_index_template(name)
|
83
|
+
return if data_stream_exist?(name)
|
84
|
+
body = {
|
85
|
+
"index_patterns" => ["#{name}*"],
|
86
|
+
"data_stream" => {},
|
87
|
+
"template" => {
|
88
|
+
"settings" => {
|
89
|
+
"index.lifecycle.name" => "#{name}_policy"
|
90
|
+
}
|
91
|
+
}
|
92
|
+
}
|
93
|
+
params = {
|
94
|
+
name: name,
|
95
|
+
body: body
|
96
|
+
}
|
97
|
+
retry_operate(@max_retry_putting_template,
|
98
|
+
@fail_on_putting_template_retry_exceed,
|
99
|
+
@catch_transport_exception_on_retry) do
|
100
|
+
@client.indices.put_index_template(params)
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
def data_stream_exist?(name)
|
105
|
+
params = {
|
106
|
+
"name": name
|
107
|
+
}
|
108
|
+
begin
|
109
|
+
response = @client.indices.get_data_stream(params)
|
110
|
+
return (not response.is_a?(Elasticsearch::Transport::Transport::Errors::NotFound))
|
111
|
+
rescue Elasticsearch::Transport::Transport::Errors::NotFound => e
|
112
|
+
log.info "Specified data stream does not exist. Will be created: <#{e}>"
|
113
|
+
return false
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
def create_data_stream(name)
|
118
|
+
return if data_stream_exist?(name)
|
119
|
+
params = {
|
120
|
+
"name": name
|
121
|
+
}
|
122
|
+
retry_operate(@max_retry_putting_template,
|
123
|
+
@fail_on_putting_template_retry_exceed,
|
124
|
+
@catch_transport_exception_on_retry) do
|
125
|
+
@client.indices.create_data_stream(params)
|
126
|
+
end
|
127
|
+
end
|
128
|
+
|
129
|
+
def valid_data_stream_name?
|
130
|
+
lowercase_only? and
|
131
|
+
valid_characters? and
|
132
|
+
start_with_valid_characters? and
|
133
|
+
not_dots? and
|
134
|
+
@data_stream_name.bytes.size <= 255
|
135
|
+
end
|
136
|
+
|
137
|
+
def lowercase_only?
|
138
|
+
@data_stream_name.downcase == @data_stream_name
|
139
|
+
end
|
140
|
+
|
141
|
+
def valid_characters?
|
142
|
+
not (INVALID_CHARACTERS.each.any? do |v| @data_stream_name.include?(v) end)
|
143
|
+
end
|
144
|
+
|
145
|
+
def start_with_valid_characters?
|
146
|
+
not (INVALID_START_CHRACTERS.each.any? do |v| @data_stream_name.start_with?(v) end)
|
147
|
+
end
|
148
|
+
|
149
|
+
def not_dots?
|
150
|
+
not (@data_stream_name == "." or @data_stream_name == "..")
|
151
|
+
end
|
152
|
+
|
153
|
+
def client_library_version
|
154
|
+
Elasticsearch::VERSION
|
155
|
+
end
|
156
|
+
|
157
|
+
def multi_workers_ready?
|
158
|
+
true
|
159
|
+
end
|
160
|
+
|
161
|
+
def write(chunk)
|
162
|
+
data_stream_name = @data_stream_name
|
163
|
+
if @use_placeholder
|
164
|
+
data_stream_name = extract_placeholders(@data_stream_name, chunk)
|
165
|
+
unless @data_stream_names.include?(data_stream_name)
|
166
|
+
begin
|
167
|
+
create_ilm_policy(data_stream_name)
|
168
|
+
create_index_template(data_stream_name)
|
169
|
+
create_data_stream(data_stream_name)
|
170
|
+
@data_stream_names << data_stream_name
|
171
|
+
rescue => e
|
172
|
+
raise Fluent::ConfigError, "Failed to create data stream: <#{data_stream_name}> #{e.message}"
|
173
|
+
end
|
174
|
+
end
|
175
|
+
end
|
176
|
+
|
177
|
+
bulk_message = ""
|
178
|
+
headers = {
|
179
|
+
CREATE_OP => {}
|
180
|
+
}
|
181
|
+
tag = chunk.metadata.tag
|
182
|
+
chunk.msgpack_each do |time, record|
|
183
|
+
next unless record.is_a? Hash
|
184
|
+
|
185
|
+
begin
|
186
|
+
record.merge!({"@timestamp" => Time.at(time).iso8601(@time_precision)})
|
187
|
+
bulk_message = append_record_to_messages(CREATE_OP, {}, headers, record, bulk_message)
|
188
|
+
rescue => e
|
189
|
+
router.emit_error_event(tag, time, record, e)
|
190
|
+
end
|
191
|
+
end
|
192
|
+
|
193
|
+
params = {
|
194
|
+
index: data_stream_name,
|
195
|
+
body: bulk_message
|
196
|
+
}
|
197
|
+
begin
|
198
|
+
response = @client.bulk(params)
|
199
|
+
if response['errors']
|
200
|
+
log.error "Could not bulk insert to Data Stream: #{data_stream_name} #{response}"
|
201
|
+
end
|
202
|
+
rescue => e
|
203
|
+
log.error "Could not bulk insert to Data Stream: #{data_stream_name} #{e.message}"
|
204
|
+
end
|
205
|
+
end
|
206
|
+
|
207
|
+
def append_record_to_messages(op, meta, header, record, msgs)
|
208
|
+
header[CREATE_OP] = meta
|
209
|
+
msgs << @dump_proc.call(header) << BODY_DELIMITER
|
210
|
+
msgs << @dump_proc.call(record) << BODY_DELIMITER
|
211
|
+
msgs
|
212
|
+
end
|
213
|
+
|
214
|
+
def retry_stream_retryable?
|
215
|
+
@buffer.storable?
|
216
|
+
end
|
217
|
+
end
|
218
|
+
end
|