logstash-output-amazon_es 0.1.0-java

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 322512b786e158f8b0d3d967e156005a651e6772
4
+ data.tar.gz: 5d33246af18db502092ffa182f9d19a09ab1d450
5
+ SHA512:
6
+ metadata.gz: 74685d7f1dde00fa3caf823b4d6920452979d85bb0e51375612e2cb57ad84d52639017619b5a05a3bc74c04a9c4dbceb0df65dfa751c907500cbe6107ce6b1f4
7
+ data.tar.gz: bb815689dc9d1ffad044598142a1dbe769f4acf908f26cda23738d204834d6b35cd0deabfb7b2f6085f68f2ccd15cf0ff5d54471216867fb7a46c1e3913d0b62
data/Gemfile ADDED
@@ -0,0 +1,3 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gemspec
data/LICENSE ADDED
@@ -0,0 +1,202 @@
1
+ Apache License
2
+ Version 2.0, January 2004
3
+ http://www.apache.org/licenses/
4
+
5
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
6
+
7
+ 1. Definitions.
8
+
9
+ "License" shall mean the terms and conditions for use, reproduction,
10
+ and distribution as defined by Sections 1 through 9 of this document.
11
+
12
+ "Licensor" shall mean the copyright owner or entity authorized by
13
+ the copyright owner that is granting the License.
14
+
15
+ "Legal Entity" shall mean the union of the acting entity and all
16
+ other entities that control, are controlled by, or are under common
17
+ control with that entity. For the purposes of this definition,
18
+ "control" means (i) the power, direct or indirect, to cause the
19
+ direction or management of such entity, whether by contract or
20
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
21
+ outstanding shares, or (iii) beneficial ownership of such entity.
22
+
23
+ "You" (or "Your") shall mean an individual or Legal Entity
24
+ exercising permissions granted by this License.
25
+
26
+ "Source" form shall mean the preferred form for making modifications,
27
+ including but not limited to software source code, documentation
28
+ source, and configuration files.
29
+
30
+ "Object" form shall mean any form resulting from mechanical
31
+ transformation or translation of a Source form, including but
32
+ not limited to compiled object code, generated documentation,
33
+ and conversions to other media types.
34
+
35
+ "Work" shall mean the work of authorship, whether in Source or
36
+ Object form, made available under the License, as indicated by a
37
+ copyright notice that is included in or attached to the work
38
+ (an example is provided in the Appendix below).
39
+
40
+ "Derivative Works" shall mean any work, whether in Source or Object
41
+ form, that is based on (or derived from) the Work and for which the
42
+ editorial revisions, annotations, elaborations, or other modifications
43
+ represent, as a whole, an original work of authorship. For the purposes
44
+ of this License, Derivative Works shall not include works that remain
45
+ separable from, or merely link (or bind by name) to the interfaces of,
46
+ the Work and Derivative Works thereof.
47
+
48
+ "Contribution" shall mean any work of authorship, including
49
+ the original version of the Work and any modifications or additions
50
+ to that Work or Derivative Works thereof, that is intentionally
51
+ submitted to Licensor for inclusion in the Work by the copyright owner
52
+ or by an individual or Legal Entity authorized to submit on behalf of
53
+ the copyright owner. For the purposes of this definition, "submitted"
54
+ means any form of electronic, verbal, or written communication sent
55
+ to the Licensor or its representatives, including but not limited to
56
+ communication on electronic mailing lists, source code control systems,
57
+ and issue tracking systems that are managed by, or on behalf of, the
58
+ Licensor for the purpose of discussing and improving the Work, but
59
+ excluding communication that is conspicuously marked or otherwise
60
+ designated in writing by the copyright owner as "Not a Contribution."
61
+
62
+ "Contributor" shall mean Licensor and any individual or Legal Entity
63
+ on behalf of whom a Contribution has been received by Licensor and
64
+ subsequently incorporated within the Work.
65
+
66
+ 2. Grant of Copyright License. Subject to the terms and conditions of
67
+ this License, each Contributor hereby grants to You a perpetual,
68
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
69
+ copyright license to reproduce, prepare Derivative Works of,
70
+ publicly display, publicly perform, sublicense, and distribute the
71
+ Work and such Derivative Works in Source or Object form.
72
+
73
+ 3. Grant of Patent License. Subject to the terms and conditions of
74
+ this License, each Contributor hereby grants to You a perpetual,
75
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
76
+ (except as stated in this section) patent license to make, have made,
77
+ use, offer to sell, sell, import, and otherwise transfer the Work,
78
+ where such license applies only to those patent claims licensable
79
+ by such Contributor that are necessarily infringed by their
80
+ Contribution(s) alone or by combination of their Contribution(s)
81
+ with the Work to which such Contribution(s) was submitted. If You
82
+ institute patent litigation against any entity (including a
83
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
84
+ or a Contribution incorporated within the Work constitutes direct
85
+ or contributory patent infringement, then any patent licenses
86
+ granted to You under this License for that Work shall terminate
87
+ as of the date such litigation is filed.
88
+
89
+ 4. Redistribution. You may reproduce and distribute copies of the
90
+ Work or Derivative Works thereof in any medium, with or without
91
+ modifications, and in Source or Object form, provided that You
92
+ meet the following conditions:
93
+
94
+ (a) You must give any other recipients of the Work or
95
+ Derivative Works a copy of this License; and
96
+
97
+ (b) You must cause any modified files to carry prominent notices
98
+ stating that You changed the files; and
99
+
100
+ (c) You must retain, in the Source form of any Derivative Works
101
+ that You distribute, all copyright, patent, trademark, and
102
+ attribution notices from the Source form of the Work,
103
+ excluding those notices that do not pertain to any part of
104
+ the Derivative Works; and
105
+
106
+ (d) If the Work includes a "NOTICE" text file as part of its
107
+ distribution, then any Derivative Works that You distribute must
108
+ include a readable copy of the attribution notices contained
109
+ within such NOTICE file, excluding those notices that do not
110
+ pertain to any part of the Derivative Works, in at least one
111
+ of the following places: within a NOTICE text file distributed
112
+ as part of the Derivative Works; within the Source form or
113
+ documentation, if provided along with the Derivative Works; or,
114
+ within a display generated by the Derivative Works, if and
115
+ wherever such third-party notices normally appear. The contents
116
+ of the NOTICE file are for informational purposes only and
117
+ do not modify the License. You may add Your own attribution
118
+ notices within Derivative Works that You distribute, alongside
119
+ or as an addendum to the NOTICE text from the Work, provided
120
+ that such additional attribution notices cannot be construed
121
+ as modifying the License.
122
+
123
+ You may add Your own copyright statement to Your modifications and
124
+ may provide additional or different license terms and conditions
125
+ for use, reproduction, or distribution of Your modifications, or
126
+ for any such Derivative Works as a whole, provided Your use,
127
+ reproduction, and distribution of the Work otherwise complies with
128
+ the conditions stated in this License.
129
+
130
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
131
+ any Contribution intentionally submitted for inclusion in the Work
132
+ by You to the Licensor shall be under the terms and conditions of
133
+ this License, without any additional terms or conditions.
134
+ Notwithstanding the above, nothing herein shall supersede or modify
135
+ the terms of any separate license agreement you may have executed
136
+ with Licensor regarding such Contributions.
137
+
138
+ 6. Trademarks. This License does not grant permission to use the trade
139
+ names, trademarks, service marks, or product names of the Licensor,
140
+ except as required for reasonable and customary use in describing the
141
+ origin of the Work and reproducing the content of the NOTICE file.
142
+
143
+ 7. Disclaimer of Warranty. Unless required by applicable law or
144
+ agreed to in writing, Licensor provides the Work (and each
145
+ Contributor provides its Contributions) on an "AS IS" BASIS,
146
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
147
+ implied, including, without limitation, any warranties or conditions
148
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
149
+ PARTICULAR PURPOSE. You are solely responsible for determining the
150
+ appropriateness of using or redistributing the Work and assume any
151
+ risks associated with Your exercise of permissions under this License.
152
+
153
+ 8. Limitation of Liability. In no event and under no legal theory,
154
+ whether in tort (including negligence), contract, or otherwise,
155
+ unless required by applicable law (such as deliberate and grossly
156
+ negligent acts) or agreed to in writing, shall any Contributor be
157
+ liable to You for damages, including any direct, indirect, special,
158
+ incidental, or consequential damages of any character arising as a
159
+ result of this License or out of the use or inability to use the
160
+ Work (including but not limited to damages for loss of goodwill,
161
+ work stoppage, computer failure or malfunction, or any and all
162
+ other commercial damages or losses), even if such Contributor
163
+ has been advised of the possibility of such damages.
164
+
165
+ 9. Accepting Warranty or Additional Liability. While redistributing
166
+ the Work or Derivative Works thereof, You may choose to offer,
167
+ and charge a fee for, acceptance of support, warranty, indemnity,
168
+ or other liability obligations and/or rights consistent with this
169
+ License. However, in accepting such obligations, You may act only
170
+ on Your own behalf and on Your sole responsibility, not on behalf
171
+ of any other Contributor, and only if You agree to indemnify,
172
+ defend, and hold each Contributor harmless for any liability
173
+ incurred by, or claims asserted against, such Contributor by reason
174
+ of your accepting any such warranty or additional liability.
175
+
176
+ END OF TERMS AND CONDITIONS
177
+
178
+ APPENDIX: How to apply the Apache License to your work.
179
+
180
+ To apply the Apache License to your work, attach the following
181
+ boilerplate notice, with the fields enclosed by brackets "{}"
182
+ replaced with your own identifying information. (Don't include
183
+ the brackets!) The text should be enclosed in the appropriate
184
+ comment syntax for the file format. We also recommend that a
185
+ file or class name and description of purpose be included on the
186
+ same "printed page" as the copyright notice for easier
187
+ identification within third-party archives.
188
+
189
+ Copyright {yyyy} {name of copyright owner}
190
+
191
+ Licensed under the Apache License, Version 2.0 (the "License");
192
+ you may not use this file except in compliance with the License.
193
+ You may obtain a copy of the License at
194
+
195
+ http://www.apache.org/licenses/LICENSE-2.0
196
+
197
+ Unless required by applicable law or agreed to in writing, software
198
+ distributed under the License is distributed on an "AS IS" BASIS,
199
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
200
+ See the License for the specific language governing permissions and
201
+ limitations under the License.
202
+
data/NOTICE.TXT ADDED
@@ -0,0 +1,14 @@
1
+ Logstash-output-amazon_es Plugin
2
+ Copyright 2015 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3
+
4
+ Licensed under the Apache License, Version 2.0 (the "License");
5
+ you may not use this file except in compliance with the License.
6
+ You may obtain a copy of the License at
7
+
8
+ http://www.apache.org/licenses/LICENSE-2.0
9
+
10
+ Unless required by applicable law or agreed to in writing, software
11
+ distributed under the License is distributed on an "AS IS" BASIS,
12
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either expressed or implied.
13
+ See the License for the specific language governing permissions and
14
+ limitations under the License.
data/README.md ADDED
@@ -0,0 +1,158 @@
1
+ # Logstash Plugin
2
+
3
+ This is a plugin for [Logstash](https://github.com/elastic/logstash).
4
+
5
+ It is fully free and fully open source. The license is Apache 2.0, meaning you are pretty much free to use it however you want in whatever way.
6
+
7
+ # Setting Up
8
+
9
+ ## Installation
10
+ One command installation
11
+ `bin/plugin install logstash-output-amazon_es`
12
+
13
+ While we are in the process of getting this plugin fully integrated within logstash to make installation simpler,
14
+ if above does not work, or you would like to patch code here is a workaround to install this plugin within your logstash:
15
+
16
+ 1. Check out/clone this code from github
17
+ 2. Build plugin using - `gem build logstash-output-amazon_es.gemspec` ( this works with jruby and rubygem versions > 1.9)
18
+ 3. Install plugin using `<logstash-home>/bin/plugin install logstash-output-amazon_es-0.1.0-java.gem` (or the non java variant)
19
+
20
+ ## Configuration for Amazon Elasticsearch Output plugin
21
+
22
+ To run the Logstash output Amazon Elasticsearch plugin simply add a configuration following the below documentation.
23
+
24
+ An example configuration:
25
+
26
+ output {
27
+ amazon_es {
28
+ hosts => ["foo.us-east-1.es.amazonaws.com"]
29
+ region => "us-east-1"
30
+ access_key => 'ACCESS_KEY' (Will be made optional in next release to support instance profiles)
31
+ secret_key => 'SECRET_KEY'
32
+ index => "production-logs-%{+YYYY.MM.dd}"
33
+ }
34
+ }
35
+
36
+ * Required Parameters
37
+ * hosts (array of string) - Amazon Elasticsearch domain endpoint. eg ["foo.us-east-1.es.amazonaws.com"]
38
+ * region (string, :default => "us-east-1") - region where the domain is located
39
+
40
+ * Optional Parameters
41
+ * Credential parameters
42
+ * aws_access_key_id, :validate => :string - Optional AWS Access key
43
+ * aws_secret_access_key, :validate => :string - Optional AWS Secret Key
44
+ The credential resolution logic can be described as follows:
45
+ - User passed aws_access_key_id and aws_secret_access_key in aes configuration
46
+ - Environment Variables - AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY
47
+ (RECOMMENDED since they are recognized by all the AWS SDKs and CLI except for .NET),
48
+ or AWS_ACCESS_KEY and AWS_SECRET_KEY (only recognized by Java SDK)
49
+ - Credential profiles file at the default location (~/.aws/credentials) shared by all AWS SDKs and the AWS CLI
50
+ - Instance profile credentials delivered through the Amazon EC2 metadata service
51
+ * Retry Parameters
52
+ * max_retries (number, default => 3) - Set max retry for each event
53
+ * retry_max_items (number, default => 5000) - Set retry queue size for events that failed to send
54
+ * retry_max_interval (number, default => 5) - Set max interval between bulk retries
55
+ * index (string, default => "logstash-%{+YYYY.MM.dd}") - Elasticsearch index to write events into
56
+ * flush_size (number , default => 500) - This setting controls how many events will be buffered before sending a batch of events in bulk API
57
+ * idle_flush_time (number, default => 1) - The amount of time in seconds since last flush before a flush is forced.
58
+ This setting helps ensure slow event rates don't get stuck in Logstash.
59
+ For example, if your `flush_size` is 100, and you have received 10 events,
60
+ and it has been more than `idle_flush_time` seconds since the last flush,
61
+ Logstash will flush those 10 events automatically.
62
+ This helps keep both fast and slow log streams moving along in near-real-time.
63
+ * template (path) - You can set the path to your own template here, if you so desire. If not set, the included template will be used.
64
+ * template_name (string, default => "logstash") - defines how the template is named inside Elasticsearch
65
+ * port (string, default 80) - Amazon Elasticsearch Service listens on port 80, if you have a custom proxy fronting elasticsearch, this parameter may need tweaking.
66
+
67
+ ## Documentation
68
+
69
+ Logstash provides infrastructure to automatically generate documentation for this plugin. We use the asciidoc format to write documentation so any comments in the source code will be first converted into asciidoc and then into html. All plugin documentation are placed under one [central location](http://www.elastic.co/guide/en/logstash/current/).
70
+
71
+ - For formatting code or config example, you can use the asciidoc `[source,ruby]` directive
72
+ - For more asciidoc formatting tips, see the excellent reference here https://github.com/elastic/docs#asciidoc-guide
73
+
74
+ ## Need Help?
75
+
76
+ Need help? Try #logstash on freenode IRC or the https://discuss.elastic.co/c/logstash discussion forum.
77
+
78
+ ## Developing
79
+
80
+ ### 1. Plugin Developement and Testing
81
+
82
+ #### Code
83
+ - To get started, you'll need JRuby with the Bundler gem installed.
84
+
85
+ - Create a new plugin or clone and existing from the GitHub [logstash-plugins](https://github.com/logstash-plugins) organization. We also provide [example plugins](https://github.com/logstash-plugins?query=example).
86
+
87
+ - Install dependencies
88
+ ```sh
89
+ bundle install
90
+ ```
91
+
92
+ #### Test
93
+
94
+ - Update your dependencies
95
+
96
+ ```sh
97
+ bundle install
98
+ ```
99
+
100
+ - Run unit tests
101
+
102
+ ```sh
103
+ bundle exec rspec
104
+ ```
105
+
106
+ - Run integration tests
107
+
108
+ Dependencies: [Docker](http://docker.com)
109
+
110
+ Before the test suite is run, we will load and run an
111
+ Elasticsearch instance within a docker container. This container
112
+ will be cleaned up when suite has finished.
113
+
114
+ ```sh
115
+ bundle exec rspec --tag integration
116
+ ```
117
+
118
+ ### 2. Running your unpublished Plugin in Logstash
119
+
120
+ #### 2.1 Run in a local Logstash clone
121
+
122
+ - Edit Logstash `Gemfile` and add the local plugin path, for example:
123
+ ```ruby
124
+ gem "logstash-filter-awesome", :path => "/your/local/logstash-filter-awesome"
125
+ ```
126
+ - Install plugin
127
+ ```sh
128
+ bin/plugin install --no-verify
129
+ ```
130
+ - Run Logstash with your plugin
131
+ ```sh
132
+ bin/logstash -e 'filter {awesome {}}'
133
+ ```
134
+ At this point any modifications to the plugin code will be applied to this local Logstash setup. After modifying the plugin, simply rerun Logstash.
135
+
136
+ #### 2.2 Run in an installed Logstash
137
+
138
+ You can use the same **2.1** method to run your plugin in an installed Logstash by editing its `Gemfile` and pointing the `:path` to your local plugin development directory or you can build the gem and install it using:
139
+
140
+ - Build your plugin gem
141
+ ```sh
142
+ gem build logstash-filter-awesome.gemspec
143
+ ```
144
+ - Install the plugin from the Logstash home
145
+ ```sh
146
+ bin/plugin install /your/local/plugin/logstash-filter-awesome.gem
147
+ ```
148
+ - Start Logstash and proceed to test the plugin
149
+
150
+ ## Contributing
151
+
152
+ All contributions are welcome: ideas, patches, documentation, bug reports, complaints, and even something you drew up on a napkin.
153
+
154
+ Programming is not a required skill. Whatever you've seen about open source and maintainers or community members saying "send patches or die" - you will not see that here.
155
+
156
+ It is more important to the community that you are able to contribute.
157
+
158
+ For more information about contributing, see the [CONTRIBUTING](https://github.com/elastic/logstash/blob/master/CONTRIBUTING.md) file.
@@ -0,0 +1,472 @@
1
+ # encoding: utf-8
2
+ require "logstash/namespace"
3
+ require "logstash/environment"
4
+ require "logstash/outputs/base"
5
+ require "logstash/outputs/amazon_es/http_client"
6
+ require "logstash/json"
7
+ require "concurrent"
8
+ require "stud/buffer"
9
+ require "socket"
10
+ require "thread"
11
+ require "uri"
12
+
13
+ # This output plugin emits data to Amazon Elasticsearch with support for signing requests using AWS V4 Signatures
14
+ #
15
+ #
16
+ # The configuration and experience is similar to logstash-output-elasticsearch plugin and we have added Signature V4 support for the same
17
+ # Some of the default configurations like connection timeouts have been tuned for optimal performance with Amazon Elasticsearch
18
+ #
19
+ # ==== Retry Policy
20
+ #
21
+ # This plugin uses the same retry policy as logstash-output-elasticsearch, It uses bulk API to optimize its
22
+ # imports into Elasticsearch.. These requests may experience either partial or total failures.
23
+ # Events are retried if they fail due to either a network error or the status codes
24
+ # 429 (the server is busy), 409 (Version Conflict), or 503 (temporary overloading/maintenance).
25
+ #
26
+ # The retry policy's logic can be described as follows:
27
+ #
28
+ # - Block and retry all events in the bulk response that experience transient network exceptions until
29
+ # a successful submission is received by Elasticsearch.
30
+ # - Retry the subset of sent events which resulted in ES errors of a retryable nature.
31
+ # - Events which returned retryable error codes will be pushed onto a separate queue for
32
+ # retrying events. Events in this queue will be retried a maximum of 5 times by default (configurable through :max_retries).
33
+ # The size of this queue is capped by the value set in :retry_max_items.
34
+ # - Events from the retry queue are submitted again when the queue reaches its max size or when
35
+ # the max interval time is reached. The max interval time is configurable via :retry_max_interval.
36
+ # - Events which are not retryable or have reached their max retry count are logged to stderr.
37
+
38
+ class LogStash::Outputs::AmazonES < LogStash::Outputs::Base
39
+ attr_reader :client
40
+
41
+ include Stud::Buffer
42
+ RETRYABLE_CODES = [409, 429, 503]
43
+ SUCCESS_CODES = [200, 201]
44
+
45
+ config_name "amazon_es"
46
+
47
+ # The index to write events to. This can be dynamic using the `%{foo}` syntax.
48
+ # The default value will partition your indices by day so you can more easily
49
+ # delete old data or only search specific date ranges.
50
+ # Indexes may not contain uppercase characters.
51
+ # For weekly indexes ISO 8601 format is recommended, eg. logstash-%{+xxxx.ww}
52
+ config :index, :validate => :string, :default => "logstash-%{+YYYY.MM.dd}"
53
+
54
+ # The index type to write events to. Generally you should try to write only
55
+ # similar events to the same 'type'. String expansion `%{foo}` works here.
56
+ #
57
+ # Deprecated in favor of `document_type` field.
58
+ config :index_type, :validate => :string, :deprecated => "Please use the 'document_type' setting instead. It has the same effect, but is more appropriately named."
59
+
60
+ # The document type to write events to. Generally you should try to write only
61
+ # similar events to the same 'type'. String expansion `%{foo}` works here.
62
+ # Unless you set 'document_type', the event 'type' will be used if it exists
63
+ # otherwise the document type will be assigned the value of 'logs'
64
+ config :document_type, :validate => :string
65
+
66
+ # Starting in Logstash 1.3 (unless you set option `manage_template` to false)
67
+ # a default mapping template for Elasticsearch will be applied, if you do not
68
+ # already have one set to match the index pattern defined (default of
69
+ # `logstash-%{+YYYY.MM.dd}`), minus any variables. For example, in this case
70
+ # the template will be applied to all indices starting with `logstash-*`
71
+ #
72
+ # If you have dynamic templating (e.g. creating indices based on field names)
73
+ # then you should set `manage_template` to false and use the REST API to upload
74
+ # your templates manually.
75
+ config :manage_template, :validate => :boolean, :default => true
76
+
77
+ # This configuration option defines how the template is named inside Elasticsearch.
78
+ # Note that if you have used the template management features and subsequently
79
+ # change this, you will need to prune the old template manually, e.g.
80
+ #
81
+ # `curl -XDELETE <http://localhost:9200/_template/OldTemplateName?pretty>`
82
+ #
83
+ # where `OldTemplateName` is whatever the former setting was.
84
+ config :template_name, :validate => :string, :default => "logstash"
85
+
86
+ # You can set the path to your own template here, if you so desire.
87
+ # If not set, the included template will be used.
88
+ config :template, :validate => :path
89
+
90
+ # Overwrite the current template with whatever is configured
91
+ # in the `template` and `template_name` directives.
92
+ config :template_overwrite, :validate => :boolean, :default => false
93
+
94
+ # The document ID for the index. Useful for overwriting existing entries in
95
+ # Elasticsearch with the same ID.
96
+ config :document_id, :validate => :string
97
+
98
+ # A routing override to be applied to all processed events.
99
+ # This can be dynamic using the `%{foo}` syntax.
100
+ config :routing, :validate => :string
101
+
102
+
103
+ # Set the endpoint of your Amazon Elasticsearch domain. This will always be array of size 1
104
+ # ["foo.us-east-1.es.amazonaws.com"]
105
+ config :hosts, :validate => :array
106
+
107
+ # You can set the remote port as part of the host, or explicitly here as well
108
+ config :port, :validate => :string, :default => 80
109
+
110
+ #Signing specific details
111
+ config :region, :validate => :string, :default => "us-east-1"
112
+
113
+ # aws_access_key_id and aws_secret_access_key are currently needed for this plugin to work right.
114
+ # Subsequent versions will have the credential resolution logic as follows:
115
+ #
116
+ # - User passed aws_access_key_id and aws_secret_access_key in aes configuration
117
+ # - Environment Variables - AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY
118
+ # (RECOMMENDED since they are recognized by all the AWS SDKs and CLI except for .NET),
119
+ # or AWS_ACCESS_KEY and AWS_SECRET_KEY (only recognized by Java SDK)
120
+ # - Credential profiles file at the default location (~/.aws/credentials) shared by all AWS SDKs and the AWS CLI
121
+ # - Instance profile credentials delivered through the Amazon EC2 metadata service
122
+ config :aws_access_key_id, :validate => :string
123
+ config :aws_secret_access_key, :validate => :string
124
+
125
+
126
+ # This plugin uses the bulk index api for improved indexing performance.
127
+ # To make efficient bulk api calls, we will buffer a certain number of
128
+ # events before flushing that out to Elasticsearch. This setting
129
+ # controls how many events will be buffered before sending a batch
130
+ # of events.
131
+ config :flush_size, :validate => :number, :default => 500
132
+
133
+ # The amount of time since last flush before a flush is forced.
134
+ #
135
+ # This setting helps ensure slow event rates don't get stuck in Logstash.
136
+ # For example, if your `flush_size` is 100, and you have received 10 events,
137
+ # and it has been more than `idle_flush_time` seconds since the last flush,
138
+ # Logstash will flush those 10 events automatically.
139
+ #
140
+ # This helps keep both fast and slow log streams moving along in
141
+ # near-real-time.
142
+ config :idle_flush_time, :validate => :number, :default => 1
143
+
144
+ # The Elasticsearch action to perform. Valid actions are: `index`, `delete`.
145
+ #
146
+ # Use of this setting *REQUIRES* you also configure the `document_id` setting
147
+ # because `delete` actions all require a document id.
148
+ #
149
+ # What does each action do?
150
+ #
151
+ # - index: indexes a document (an event from Logstash).
152
+ # - delete: deletes a document by id
153
+ # - create: indexes a document, fails if a document by that id already exists in the index.
154
+ # - update: updates a document by id
155
+ # following action is not supported by HTTP protocol
156
+ #
157
+ # For more details on actions, check out the http://www.elasticsearch.org/guide/en/elasticsearch/reference/current/docs-bulk.html[Elasticsearch bulk API documentation]
158
+ config :action, :validate => %w(index delete create update), :default => "index"
159
+
160
+ # Username and password (only valid when protocol is HTTP; this setting works with HTTP or HTTPS auth)
161
+ config :user, :validate => :string
162
+ config :password, :validate => :password
163
+
164
+ # HTTP Path at which the Elasticsearch server lives. Use this if you must run ES behind a proxy that remaps
165
+ # the root path for the Elasticsearch HTTP API lives.
166
+ config :path, :validate => :string, :default => "/"
167
+
168
+ # Set max retry for each event
169
+ config :max_retries, :validate => :number, :default => 3
170
+
171
+ # Set retry policy for events that failed to send
172
+ config :retry_max_items, :validate => :number, :default => 5000
173
+
174
+ # Set max interval between bulk retries
175
+ config :retry_max_interval, :validate => :number, :default => 5
176
+
177
+ # Set the address of a forward HTTP proxy. Must be used with the 'http' protocol
178
+ # Can be either a string, such as 'http://localhost:123' or a hash in the form
179
+ # {host: 'proxy.org' port: 80 scheme: 'http'}
180
+ # Note, this is NOT a SOCKS proxy, but a plain HTTP proxy
181
+ config :proxy
182
+
183
+ # Enable doc_as_upsert for update mode
184
+ # create a new document with source if document_id doesn't exists
185
+ config :doc_as_upsert, :validate => :boolean, :default => false
186
+
187
+ # Set upsert content for update mode
188
+ # create a new document with this parameter as json string if document_id doesn't exists
189
+ config :upsert, :validate => :string, :default => ""
190
+
191
+ public
192
+ def register
193
+ @hosts = Array(@hosts)
194
+ # retry-specific variables
195
+ @retry_flush_mutex = Mutex.new
196
+ @retry_teardown_requested = Concurrent::AtomicBoolean.new(false)
197
+ # needs flushing when interval
198
+ @retry_queue_needs_flushing = ConditionVariable.new
199
+ @retry_queue_not_full = ConditionVariable.new
200
+ @retry_queue = Queue.new
201
+ @submit_mutex = Mutex.new
202
+
203
+ client_settings = {}
204
+ common_options = {
205
+ :client_settings => client_settings
206
+ }
207
+
208
+ client_settings[:path] = "/#{@path}/".gsub(/\/+/, "/") # Normalize slashes
209
+ @logger.debug? && @logger.debug("Normalizing http path", :path => @path, :normalized => client_settings[:path])
210
+
211
+ if @hosts.nil? || @hosts.empty?
212
+ @logger.info("No 'host' set in elasticsearch output. Defaulting to localhost")
213
+ @hosts = ["localhost"]
214
+ end
215
+
216
+ client_settings.merge! setup_proxy()
217
+ common_options.merge! setup_basic_auth()
218
+
219
+ # Update API setup
220
+ update_options = {
221
+ :upsert => @upsert,
222
+ :doc_as_upsert => @doc_as_upsert
223
+ }
224
+ common_options.merge! update_options if @action == 'update'
225
+
226
+ @client = LogStash::Outputs::AES::HttpClient.new(
227
+ common_options.merge(:hosts => @hosts, :port => @port, :region => @region, :aws_access_key_id => @aws_access_key_id, :aws_secret_access_key => @aws_secret_access_key)
228
+ )
229
+
230
+ if @manage_template
231
+ begin
232
+ @logger.info("Automatic template management enabled", :manage_template => @manage_template.to_s)
233
+ @client.template_install(@template_name, get_template, @template_overwrite)
234
+ rescue => e
235
+ @logger.error("Failed to install template: #{e.message}")
236
+ end
237
+ end
238
+
239
+ @logger.info("New Elasticsearch output", :hosts => @hosts, :port => @port)
240
+
241
+ @client_idx = 0
242
+
243
+ buffer_initialize(
244
+ :max_items => @flush_size,
245
+ :max_interval => @idle_flush_time,
246
+ :logger => @logger
247
+ )
248
+
249
+ @retry_timer_thread = Thread.new do
250
+ loop do
251
+ sleep(@retry_max_interval)
252
+ @retry_flush_mutex.synchronize { @retry_queue_needs_flushing.signal }
253
+ end
254
+ end
255
+
256
+ @retry_thread = Thread.new do
257
+ while @retry_teardown_requested.false?
258
+ @retry_flush_mutex.synchronize { @retry_queue_needs_flushing.wait(@retry_flush_mutex) }
259
+ retry_flush
260
+ end
261
+ end
262
+ end # def register
263
+
264
+ public
265
+ def get_template
266
+ if @template.nil?
267
+ @template = ::File.expand_path('amazon_es/elasticsearch-template.json', ::File.dirname(__FILE__))
268
+ if !File.exists?(@template)
269
+ raise "You must specify 'template => ...' in your elasticsearch output (I looked for '#{@template}')"
270
+ end
271
+ end
272
+ template_json = IO.read(@template).gsub(/\n/,'')
273
+ template = LogStash::Json.load(template_json)
274
+ @logger.info("Using mapping template", :template => template)
275
+ return template
276
+ end # def get_template
277
+
278
+ public
279
+ def receive(event)
280
+ return unless output?(event)
281
+
282
+ # block until we have not maxed out our
283
+ # retry queue. This is applying back-pressure
284
+ # to slow down the receive-rate
285
+ @retry_flush_mutex.synchronize {
286
+ @retry_queue_not_full.wait(@retry_flush_mutex) while @retry_queue.size > @retry_max_items
287
+ }
288
+
289
+ event['@metadata']['retry_count'] = 0
290
+
291
+ # Set the 'type' value for the index.
292
+ type = if @document_type
293
+ event.sprintf(@document_type)
294
+ elsif @index_type # deprecated
295
+ event.sprintf(@index_type)
296
+ else
297
+ event["type"] || "logs"
298
+ end
299
+
300
+ params = {
301
+ :_id => @document_id ? event.sprintf(@document_id) : nil,
302
+ :_index => event.sprintf(@index),
303
+ :_type => type,
304
+ :_routing => @routing ? event.sprintf(@routing) : nil
305
+ }
306
+
307
+ params[:_upsert] = LogStash::Json.load(event.sprintf(@upsert)) if @action == 'update' && @upsert != ""
308
+
309
+ buffer_receive([event.sprintf(@action), params, event])
310
+ end # def receive
311
+
312
+ public
313
+ # The submit method can be called from both the
314
+ # Stud::Buffer flush thread and from our own retry thread.
315
+ def submit(actions)
316
+ @submit_mutex.synchronize do
317
+ es_actions = actions.map { |a, doc, event| [a, doc, event.to_hash] }
318
+
319
+ bulk_response = @client.bulk(es_actions)
320
+
321
+ if bulk_response["errors"]
322
+ actions_to_retry = []
323
+
324
+ bulk_response['items'].each_with_index do |item,idx|
325
+ action = es_actions[idx]
326
+ action_type, props = item.first # These are all hashes with one value, so we destructure them here
327
+
328
+ status = props['status']
329
+ error = props['error']
330
+
331
+ if RETRYABLE_CODES.include?(status)
332
+ @logger.warn "retrying failed action with response code: #{status}"
333
+ actions_to_retry << action
334
+ elsif not SUCCESS_CODES.include?(status)
335
+ @logger.warn "failed action", status: status, error: error, action: action
336
+ end
337
+ end
338
+
339
+ retry_push(actions_to_retry) unless actions_to_retry.empty?
340
+ end
341
+ end
342
+ end
343
+
344
+ # When there are exceptions raised upon submission, we raise an exception so that
345
+ # Stud::Buffer will retry to flush
346
+ public
347
+ def flush(actions, teardown = false)
348
+ begin
349
+ submit(actions)
350
+ rescue Manticore::SocketException => e
351
+ # If we can't even connect to the server let's just print out the URL (:hosts is actually a URL)
352
+ # and let the user sort it out from there
353
+ @logger.error(
354
+ "Attempted to send a bulk request to Elasticsearch configured at '#{@client.client_options[:hosts]}',"+
355
+ " but Elasticsearch appears to be unreachable or down!",
356
+ :client_config => @client.client_options,
357
+ :error_message => e.message
358
+ )
359
+ @logger.debug("Failed actions for last bad bulk request!", :actions => actions)
360
+ rescue => e
361
+ # For all other errors print out full connection issues
362
+ @logger.error(
363
+ "Attempted to send a bulk request to Elasticsearch configured at '#{@client.client_options[:hosts]}'," +
364
+ " but an error occurred and it failed! Are you sure you can reach elasticsearch from this machine using " +
365
+ "the configuration provided?",
366
+ :client_config => @client.client_options,
367
+ :error_message => e.message,
368
+ :error_class => e.class.name,
369
+ :backtrace => e.backtrace
370
+ )
371
+
372
+ @logger.debug("Failed actions for last bad bulk request!", :actions => actions)
373
+
374
+ raise e
375
+ end
376
+ end # def flush
377
+
378
+ public
379
+ def teardown
380
+
381
+ @retry_teardown_requested.make_true
382
+ # First, make sure retry_timer_thread is stopped
383
+ # to ensure we do not signal a retry based on
384
+ # the retry interval.
385
+ Thread.kill(@retry_timer_thread)
386
+ @retry_timer_thread.join
387
+ # Signal flushing in the case that #retry_flush is in
388
+ # the process of waiting for a signal.
389
+ @retry_flush_mutex.synchronize { @retry_queue_needs_flushing.signal }
390
+ # Now, #retry_flush is ensured to not be in a state of
391
+ # waiting and can be safely joined into the main thread
392
+ # for further final execution of an in-process remaining call.
393
+ @retry_thread.join
394
+
395
+ # execute any final actions along with a proceeding retry for any
396
+ # final actions that did not succeed.
397
+ buffer_flush(:final => true)
398
+ retry_flush
399
+ end
400
+
401
+ private
402
+ def setup_proxy
403
+ return {} unless @proxy
404
+
405
+ # Symbolize keys
406
+ proxy = if @proxy.is_a?(Hash)
407
+ Hash[@proxy.map {|k,v| [k.to_sym, v]}]
408
+ elsif @proxy.is_a?(String)
409
+ @proxy
410
+ else
411
+ raise LogStash::ConfigurationError, "Expected 'proxy' to be a string or hash, not '#{@proxy}''!"
412
+ end
413
+
414
+ return {:proxy => proxy}
415
+ end
416
+
417
+ private
418
+ def setup_basic_auth
419
+ return {} unless @user && @password
420
+
421
+ {
422
+ :user => ::URI.escape(@user, "@:"),
423
+ :password => ::URI.escape(@password.value, "@:")
424
+ }
425
+ end
426
+
427
+
428
+ private
429
+ # in charge of submitting any actions in @retry_queue that need to be
430
+ # retried
431
+ #
432
+ # This method is not called concurrently. It is only called by @retry_thread
433
+ # and once that thread is ended during the teardown process, a final call
434
+ # to this method is done upon teardown in the main thread.
435
+ def retry_flush()
436
+ unless @retry_queue.empty?
437
+ buffer = @retry_queue.size.times.map do
438
+ next_action, next_doc, next_event = @retry_queue.pop
439
+ next_event['@metadata']['retry_count'] += 1
440
+
441
+ if next_event['@metadata']['retry_count'] > @max_retries
442
+ @logger.error "too many attempts at sending event. dropping: #{next_event}"
443
+ nil
444
+ else
445
+ [next_action, next_doc, next_event]
446
+ end
447
+ end.compact
448
+
449
+ submit(buffer) unless buffer.empty?
450
+ end
451
+
452
+ @retry_flush_mutex.synchronize {
453
+ @retry_queue_not_full.signal if @retry_queue.size < @retry_max_items
454
+ }
455
+ end
456
+
457
+ private
458
+ def retry_push(actions)
459
+ Array(actions).each{|action| @retry_queue << action}
460
+ @retry_flush_mutex.synchronize {
461
+ @retry_queue_needs_flushing.signal if @retry_queue.size >= @retry_max_items
462
+ }
463
+ end
464
+
465
+ @@plugins = Gem::Specification.find_all{|spec| spec.name =~ /logstash-output-amazon_es-/ }
466
+
467
+ @@plugins.each do |plugin|
468
+ name = plugin.name.split('-')[-1]
469
+ require "logstash/outputs/amazon_es/#{name}"
470
+ end
471
+
472
+ end