logstash-codec-csv 0.1.2 → 1.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: a6f3dc6030f103dc961e34e2babeeccd8fe64884
4
- data.tar.gz: 7a26a6c91a3f5cfd272a7ce75e38e46c8fe53bc5
2
+ SHA256:
3
+ metadata.gz: b0ee61a8601e503967b9682792838e941d543a8513eea39e16d427dae4539154
4
+ data.tar.gz: dd950c0a24b7ad601ed0fba1cf3344e9cd460a59af3151eca02cb16b2f885d01
5
5
  SHA512:
6
- metadata.gz: ec21f23421f5da8349534a2c3f787a7a4f04be9166b078c296538679bac1da9f1fbbedb3b401dde2a5d274c7a00adbce71b27fc04642d6017379e82a0d06a952
7
- data.tar.gz: b5219a99d9bac584872604117631f99c1377195ea77317ffb867ff3f59d572cc60f3c50ee013a074ab86dd281998853e9883eb74e605fa35e6bd3c04e1312107
6
+ metadata.gz: 9d085d074262a43fcc4f68d5b8dc83b41c8a56625b8f15429c786c85e92916256b719ca5511650dbed860d765148b386a6a84f68f9dd038d60533d9d2b47489b
7
+ data.tar.gz: e908a388ad958441e3fd8c5452bc8b2b078e75353a02c2988198ab14b9b829c8dc95d4e0fcee5fcbc42ef75a39521575b654701c54999e64eb1326c2afa5597b
data/CHANGELOG.md CHANGED
@@ -1,3 +1,15 @@
1
+ ## 1.1.0
2
+ - Feat: added target => namespace support + ECS compatibility [#7](https://github.com/logstash-plugins/logstash-codec-csv/pull/7)
3
+
4
+ ## 1.0.0
5
+ - Fixed dependencies to work with logstash v6 and up. Overhauled to match features of the CSV Filter. Improved spec coverage [#4](https://github.com/logstash-plugins/logstash-codec-csv/pull/4)
6
+
7
+ ## 0.1.5
8
+ - Fixed asciidoc formatting for example [#3](https://github.com/logstash-plugins/logstash-codec-csv/pull/3)
9
+
10
+ ## 0.1.4
11
+ - Fix some documentation issues
12
+
1
13
  # 0.1.2
2
14
  - Depend on logstash-core-plugin-api instead of logstash-core, removing the need to mass update plugins on major releases of logstash
3
15
  # 0.1.1
data/Gemfile CHANGED
@@ -1,2 +1,11 @@
1
1
  source 'https://rubygems.org'
2
+
2
3
  gemspec
4
+
5
+ logstash_path = ENV["LOGSTASH_PATH"] || "../../logstash"
6
+ use_logstash_source = ENV["LOGSTASH_SOURCE"] && ENV["LOGSTASH_SOURCE"].to_s == "1"
7
+
8
+ if Dir.exist?(logstash_path) && use_logstash_source
9
+ gem 'logstash-core', :path => "#{logstash_path}/logstash-core"
10
+ gem 'logstash-core-plugin-api', :path => "#{logstash_path}/logstash-core-plugin-api"
11
+ end
data/LICENSE CHANGED
@@ -1,13 +1,202 @@
1
- Copyright (c) 2012-2015 Elasticsearch <http://www.elasticsearch.org>
2
1
 
3
- Licensed under the Apache License, Version 2.0 (the "License");
4
- you may not use this file except in compliance with the License.
5
- You may obtain a copy of the License at
2
+ Apache License
3
+ Version 2.0, January 2004
4
+ http://www.apache.org/licenses/
6
5
 
7
- http://www.apache.org/licenses/LICENSE-2.0
6
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
8
7
 
9
- Unless required by applicable law or agreed to in writing, software
10
- distributed under the License is distributed on an "AS IS" BASIS,
11
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
- See the License for the specific language governing permissions and
13
- limitations under the License.
8
+ 1. Definitions.
9
+
10
+ "License" shall mean the terms and conditions for use, reproduction,
11
+ and distribution as defined by Sections 1 through 9 of this document.
12
+
13
+ "Licensor" shall mean the copyright owner or entity authorized by
14
+ the copyright owner that is granting the License.
15
+
16
+ "Legal Entity" shall mean the union of the acting entity and all
17
+ other entities that control, are controlled by, or are under common
18
+ control with that entity. For the purposes of this definition,
19
+ "control" means (i) the power, direct or indirect, to cause the
20
+ direction or management of such entity, whether by contract or
21
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
22
+ outstanding shares, or (iii) beneficial ownership of such entity.
23
+
24
+ "You" (or "Your") shall mean an individual or Legal Entity
25
+ exercising permissions granted by this License.
26
+
27
+ "Source" form shall mean the preferred form for making modifications,
28
+ including but not limited to software source code, documentation
29
+ source, and configuration files.
30
+
31
+ "Object" form shall mean any form resulting from mechanical
32
+ transformation or translation of a Source form, including but
33
+ not limited to compiled object code, generated documentation,
34
+ and conversions to other media types.
35
+
36
+ "Work" shall mean the work of authorship, whether in Source or
37
+ Object form, made available under the License, as indicated by a
38
+ copyright notice that is included in or attached to the work
39
+ (an example is provided in the Appendix below).
40
+
41
+ "Derivative Works" shall mean any work, whether in Source or Object
42
+ form, that is based on (or derived from) the Work and for which the
43
+ editorial revisions, annotations, elaborations, or other modifications
44
+ represent, as a whole, an original work of authorship. For the purposes
45
+ of this License, Derivative Works shall not include works that remain
46
+ separable from, or merely link (or bind by name) to the interfaces of,
47
+ the Work and Derivative Works thereof.
48
+
49
+ "Contribution" shall mean any work of authorship, including
50
+ the original version of the Work and any modifications or additions
51
+ to that Work or Derivative Works thereof, that is intentionally
52
+ submitted to Licensor for inclusion in the Work by the copyright owner
53
+ or by an individual or Legal Entity authorized to submit on behalf of
54
+ the copyright owner. For the purposes of this definition, "submitted"
55
+ means any form of electronic, verbal, or written communication sent
56
+ to the Licensor or its representatives, including but not limited to
57
+ communication on electronic mailing lists, source code control systems,
58
+ and issue tracking systems that are managed by, or on behalf of, the
59
+ Licensor for the purpose of discussing and improving the Work, but
60
+ excluding communication that is conspicuously marked or otherwise
61
+ designated in writing by the copyright owner as "Not a Contribution."
62
+
63
+ "Contributor" shall mean Licensor and any individual or Legal Entity
64
+ on behalf of whom a Contribution has been received by Licensor and
65
+ subsequently incorporated within the Work.
66
+
67
+ 2. Grant of Copyright License. Subject to the terms and conditions of
68
+ this License, each Contributor hereby grants to You a perpetual,
69
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
70
+ copyright license to reproduce, prepare Derivative Works of,
71
+ publicly display, publicly perform, sublicense, and distribute the
72
+ Work and such Derivative Works in Source or Object form.
73
+
74
+ 3. Grant of Patent License. Subject to the terms and conditions of
75
+ this License, each Contributor hereby grants to You a perpetual,
76
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
77
+ (except as stated in this section) patent license to make, have made,
78
+ use, offer to sell, sell, import, and otherwise transfer the Work,
79
+ where such license applies only to those patent claims licensable
80
+ by such Contributor that are necessarily infringed by their
81
+ Contribution(s) alone or by combination of their Contribution(s)
82
+ with the Work to which such Contribution(s) was submitted. If You
83
+ institute patent litigation against any entity (including a
84
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
85
+ or a Contribution incorporated within the Work constitutes direct
86
+ or contributory patent infringement, then any patent licenses
87
+ granted to You under this License for that Work shall terminate
88
+ as of the date such litigation is filed.
89
+
90
+ 4. Redistribution. You may reproduce and distribute copies of the
91
+ Work or Derivative Works thereof in any medium, with or without
92
+ modifications, and in Source or Object form, provided that You
93
+ meet the following conditions:
94
+
95
+ (a) You must give any other recipients of the Work or
96
+ Derivative Works a copy of this License; and
97
+
98
+ (b) You must cause any modified files to carry prominent notices
99
+ stating that You changed the files; and
100
+
101
+ (c) You must retain, in the Source form of any Derivative Works
102
+ that You distribute, all copyright, patent, trademark, and
103
+ attribution notices from the Source form of the Work,
104
+ excluding those notices that do not pertain to any part of
105
+ the Derivative Works; and
106
+
107
+ (d) If the Work includes a "NOTICE" text file as part of its
108
+ distribution, then any Derivative Works that You distribute must
109
+ include a readable copy of the attribution notices contained
110
+ within such NOTICE file, excluding those notices that do not
111
+ pertain to any part of the Derivative Works, in at least one
112
+ of the following places: within a NOTICE text file distributed
113
+ as part of the Derivative Works; within the Source form or
114
+ documentation, if provided along with the Derivative Works; or,
115
+ within a display generated by the Derivative Works, if and
116
+ wherever such third-party notices normally appear. The contents
117
+ of the NOTICE file are for informational purposes only and
118
+ do not modify the License. You may add Your own attribution
119
+ notices within Derivative Works that You distribute, alongside
120
+ or as an addendum to the NOTICE text from the Work, provided
121
+ that such additional attribution notices cannot be construed
122
+ as modifying the License.
123
+
124
+ You may add Your own copyright statement to Your modifications and
125
+ may provide additional or different license terms and conditions
126
+ for use, reproduction, or distribution of Your modifications, or
127
+ for any such Derivative Works as a whole, provided Your use,
128
+ reproduction, and distribution of the Work otherwise complies with
129
+ the conditions stated in this License.
130
+
131
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
132
+ any Contribution intentionally submitted for inclusion in the Work
133
+ by You to the Licensor shall be under the terms and conditions of
134
+ this License, without any additional terms or conditions.
135
+ Notwithstanding the above, nothing herein shall supersede or modify
136
+ the terms of any separate license agreement you may have executed
137
+ with Licensor regarding such Contributions.
138
+
139
+ 6. Trademarks. This License does not grant permission to use the trade
140
+ names, trademarks, service marks, or product names of the Licensor,
141
+ except as required for reasonable and customary use in describing the
142
+ origin of the Work and reproducing the content of the NOTICE file.
143
+
144
+ 7. Disclaimer of Warranty. Unless required by applicable law or
145
+ agreed to in writing, Licensor provides the Work (and each
146
+ Contributor provides its Contributions) on an "AS IS" BASIS,
147
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
148
+ implied, including, without limitation, any warranties or conditions
149
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
150
+ PARTICULAR PURPOSE. You are solely responsible for determining the
151
+ appropriateness of using or redistributing the Work and assume any
152
+ risks associated with Your exercise of permissions under this License.
153
+
154
+ 8. Limitation of Liability. In no event and under no legal theory,
155
+ whether in tort (including negligence), contract, or otherwise,
156
+ unless required by applicable law (such as deliberate and grossly
157
+ negligent acts) or agreed to in writing, shall any Contributor be
158
+ liable to You for damages, including any direct, indirect, special,
159
+ incidental, or consequential damages of any character arising as a
160
+ result of this License or out of the use or inability to use the
161
+ Work (including but not limited to damages for loss of goodwill,
162
+ work stoppage, computer failure or malfunction, or any and all
163
+ other commercial damages or losses), even if such Contributor
164
+ has been advised of the possibility of such damages.
165
+
166
+ 9. Accepting Warranty or Additional Liability. While redistributing
167
+ the Work or Derivative Works thereof, You may choose to offer,
168
+ and charge a fee for, acceptance of support, warranty, indemnity,
169
+ or other liability obligations and/or rights consistent with this
170
+ License. However, in accepting such obligations, You may act only
171
+ on Your own behalf and on Your sole responsibility, not on behalf
172
+ of any other Contributor, and only if You agree to indemnify,
173
+ defend, and hold each Contributor harmless for any liability
174
+ incurred by, or claims asserted against, such Contributor by reason
175
+ of your accepting any such warranty or additional liability.
176
+
177
+ END OF TERMS AND CONDITIONS
178
+
179
+ APPENDIX: How to apply the Apache License to your work.
180
+
181
+ To apply the Apache License to your work, attach the following
182
+ boilerplate notice, with the fields enclosed by brackets "[]"
183
+ replaced with your own identifying information. (Don't include
184
+ the brackets!) The text should be enclosed in the appropriate
185
+ comment syntax for the file format. We also recommend that a
186
+ file or class name and description of purpose be included on the
187
+ same "printed page" as the copyright notice for easier
188
+ identification within third-party archives.
189
+
190
+ Copyright 2020 Elastic and contributors
191
+
192
+ Licensed under the Apache License, Version 2.0 (the "License");
193
+ you may not use this file except in compliance with the License.
194
+ You may obtain a copy of the License at
195
+
196
+ http://www.apache.org/licenses/LICENSE-2.0
197
+
198
+ Unless required by applicable law or agreed to in writing, software
199
+ distributed under the License is distributed on an "AS IS" BASIS,
200
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
201
+ See the License for the specific language governing permissions and
202
+ limitations under the License.
data/README.md CHANGED
@@ -82,7 +82,12 @@ gem build logstash-filter-awesome.gemspec
82
82
  ```
83
83
  - Install the plugin from the Logstash home
84
84
  ```sh
85
- bin/plugin install /your/local/plugin/logstash-filter-awesome.gem
85
+ # Logstash 2.3 and higher
86
+ bin/logstash-plugin install --no-verify
87
+
88
+ # Prior to Logstash 2.3
89
+ bin/plugin install --no-verify
90
+
86
91
  ```
87
92
  - Start Logstash and proceed to test the plugin
88
93
 
@@ -0,0 +1,185 @@
1
+ :plugin: csv
2
+ :type: codec
3
+
4
+ ///////////////////////////////////////////
5
+ START - GENERATED VARIABLES, DO NOT EDIT!
6
+ ///////////////////////////////////////////
7
+ :version: %VERSION%
8
+ :release_date: %RELEASE_DATE%
9
+ :changelog_url: %CHANGELOG_URL%
10
+ :include_path: ../../../../logstash/docs/include
11
+ ///////////////////////////////////////////
12
+ END - GENERATED VARIABLES, DO NOT EDIT!
13
+ ///////////////////////////////////////////
14
+
15
+ [id="plugins-{type}s-{plugin}"]
16
+
17
+ === Csv codec plugin
18
+
19
+ include::{include_path}/plugin_header.asciidoc[]
20
+
21
+ ==== Description
22
+
23
+ The csv codec takes CSV data, parses it and passes it along.
24
+
25
+ [id="plugins-{type}s-{plugin}-ecs"]
26
+ ==== Compatibility with the Elastic Common Schema (ECS)
27
+
28
+ The plugin behaves the same regardless of ECS compatibility, except giving a warning when ECS is enabled and `target` isn't set.
29
+
30
+ TIP: Set the `target` option to avoid potential schema conflicts.
31
+
32
+ [id="plugins-{type}s-{plugin}-options"]
33
+ ==== Csv Codec configuration options
34
+
35
+ [cols="<,<,<",options="header",]
36
+ |=======================================================================
37
+ |Setting |Input type|Required
38
+ | <<plugins-{type}s-{plugin}-autodetect_column_names>> |<<boolean,boolean>>|No
39
+ | <<plugins-{type}s-{plugin}-autogenerate_column_names>> |<<boolean,boolean>>|No
40
+ | <<plugins-{type}s-{plugin}-charset>> |<<string,string>>, one of `["ASCII-8BIT", "UTF-8", "US-ASCII", "Big5", "Big5-HKSCS", "Big5-UAO", "CP949", "Emacs-Mule", "EUC-JP", "EUC-KR", "EUC-TW", "GB2312", "GB18030", "GBK", "ISO-8859-1", "ISO-8859-2", "ISO-8859-3", "ISO-8859-4", "ISO-8859-5", "ISO-8859-6", "ISO-8859-7", "ISO-8859-8", "ISO-8859-9", "ISO-8859-10", "ISO-8859-11", "ISO-8859-13", "ISO-8859-14", "ISO-8859-15", "ISO-8859-16", "KOI8-R", "KOI8-U", "Shift_JIS", "UTF-16BE", "UTF-16LE", "UTF-32BE", "UTF-32LE", "Windows-31J", "Windows-1250", "Windows-1251", "Windows-1252", "IBM437", "IBM737", "IBM775", "CP850", "IBM852", "CP852", "IBM855", "CP855", "IBM857", "IBM860", "IBM861", "IBM862", "IBM863", "IBM864", "IBM865", "IBM866", "IBM869", "Windows-1258", "GB1988", "macCentEuro", "macCroatian", "macCyrillic", "macGreek", "macIceland", "macRoman", "macRomania", "macThai", "macTurkish", "macUkraine", "CP950", "CP951", "IBM037", "stateless-ISO-2022-JP", "eucJP-ms", "CP51932", "EUC-JIS-2004", "GB12345", "ISO-2022-JP", "ISO-2022-JP-2", "CP50220", "CP50221", "Windows-1256", "Windows-1253", "Windows-1255", "Windows-1254", "TIS-620", "Windows-874", "Windows-1257", "MacJapanese", "UTF-7", "UTF8-MAC", "UTF-16", "UTF-32", "UTF8-DoCoMo", "SJIS-DoCoMo", "UTF8-KDDI", "SJIS-KDDI", "ISO-2022-JP-KDDI", "stateless-ISO-2022-JP-KDDI", "UTF8-SoftBank", "SJIS-SoftBank", "BINARY", "CP437", "CP737", "CP775", "IBM850", "CP857", "CP860", "CP861", "CP862", "CP863", "CP864", "CP865", "CP866", "CP869", "CP1258", "Big5-HKSCS:2008", "ebcdic-cp-us", "eucJP", "euc-jp-ms", "EUC-JISX0213", "eucKR", "eucTW", "EUC-CN", "eucCN", "CP936", "ISO2022-JP", "ISO2022-JP2", "ISO8859-1", "ISO8859-2", "ISO8859-3", "ISO8859-4", "ISO8859-5", "ISO8859-6", "CP1256", "ISO8859-7", "CP1253", "ISO8859-8", "CP1255", "ISO8859-9", "CP1254", "ISO8859-10", "ISO8859-11", "CP874", "ISO8859-13", "CP1257", "ISO8859-14", "ISO8859-15", "ISO8859-16", "CP878", "MacJapan", "ASCII", "ANSI_X3.4-1968", "646", "CP65000", "CP65001", "UTF-8-MAC", "UTF-8-HFS", "UCS-2BE", "UCS-4BE", "UCS-4LE", "CP932", "csWindows31J", "SJIS", "PCK", "CP1250", "CP1251", "CP1252", "external", "locale"]`|No
41
+ | <<plugins-{type}s-{plugin}-columns>> |<<array,array>>|No
42
+ | <<plugins-{type}s-{plugin}-convert>> |<<hash,hash>>|No
43
+ | <<plugins-{type}s-{plugin}-ecs_compatibility>> |<<string,string>>|No
44
+ | <<plugins-{type}s-{plugin}-include_headers>> |<<boolean,boolean>>|No
45
+ | <<plugins-{type}s-{plugin}-quote_char>> |<<string,string>>|No
46
+ | <<plugins-{type}s-{plugin}-separator>> |<<string,string>>|No
47
+ | <<plugins-{type}s-{plugin}-skip_empty_columns>> |<<boolean,boolean>>|No
48
+ | <<plugins-{type}s-{plugin}-target>> |<<string,string>>|No
49
+ |=======================================================================
50
+
51
+ &nbsp;
52
+
53
+ [id="plugins-{type}s-{plugin}-autodetect_column_names"]
54
+ ===== `autodetect_column_names`
55
+
56
+ * Value type is <<boolean,boolean>>
57
+ * Default value is `false`
58
+
59
+ Define whether column names should be auto-detected from the header column or not.
60
+ Defaults to false.
61
+
62
+ [id="plugins-{type}s-{plugin}-autogenerate_column_names"]
63
+ ===== `autogenerate_column_names`
64
+
65
+ * Value type is <<boolean,boolean>>
66
+ * Default value is `true`
67
+
68
+ Define whether column names should be autogenerated or not.
69
+ Defaults to true. If set to false, columns not having a header specified will not be parsed.
70
+
71
+ [id="plugins-{type}s-{plugin}-charset"]
72
+ ===== `charset`
73
+
74
+ * Value can be any of: `ASCII-8BIT`, `UTF-8`, `US-ASCII`, `Big5`, `Big5-HKSCS`, `Big5-UAO`, `CP949`, `Emacs-Mule`, `EUC-JP`, `EUC-KR`, `EUC-TW`, `GB2312`, `GB18030`, `GBK`, `ISO-8859-1`, `ISO-8859-2`, `ISO-8859-3`, `ISO-8859-4`, `ISO-8859-5`, `ISO-8859-6`, `ISO-8859-7`, `ISO-8859-8`, `ISO-8859-9`, `ISO-8859-10`, `ISO-8859-11`, `ISO-8859-13`, `ISO-8859-14`, `ISO-8859-15`, `ISO-8859-16`, `KOI8-R`, `KOI8-U`, `Shift_JIS`, `UTF-16BE`, `UTF-16LE`, `UTF-32BE`, `UTF-32LE`, `Windows-31J`, `Windows-1250`, `Windows-1251`, `Windows-1252`, `IBM437`, `IBM737`, `IBM775`, `CP850`, `IBM852`, `CP852`, `IBM855`, `CP855`, `IBM857`, `IBM860`, `IBM861`, `IBM862`, `IBM863`, `IBM864`, `IBM865`, `IBM866`, `IBM869`, `Windows-1258`, `GB1988`, `macCentEuro`, `macCroatian`, `macCyrillic`, `macGreek`, `macIceland`, `macRoman`, `macRomania`, `macThai`, `macTurkish`, `macUkraine`, `CP950`, `CP951`, `IBM037`, `stateless-ISO-2022-JP`, `eucJP-ms`, `CP51932`, `EUC-JIS-2004`, `GB12345`, `ISO-2022-JP`, `ISO-2022-JP-2`, `CP50220`, `CP50221`, `Windows-1256`, `Windows-1253`, `Windows-1255`, `Windows-1254`, `TIS-620`, `Windows-874`, `Windows-1257`, `MacJapanese`, `UTF-7`, `UTF8-MAC`, `UTF-16`, `UTF-32`, `UTF8-DoCoMo`, `SJIS-DoCoMo`, `UTF8-KDDI`, `SJIS-KDDI`, `ISO-2022-JP-KDDI`, `stateless-ISO-2022-JP-KDDI`, `UTF8-SoftBank`, `SJIS-SoftBank`, `BINARY`, `CP437`, `CP737`, `CP775`, `IBM850`, `CP857`, `CP860`, `CP861`, `CP862`, `CP863`, `CP864`, `CP865`, `CP866`, `CP869`, `CP1258`, `Big5-HKSCS:2008`, `ebcdic-cp-us`, `eucJP`, `euc-jp-ms`, `EUC-JISX0213`, `eucKR`, `eucTW`, `EUC-CN`, `eucCN`, `CP936`, `ISO2022-JP`, `ISO2022-JP2`, `ISO8859-1`, `ISO8859-2`, `ISO8859-3`, `ISO8859-4`, `ISO8859-5`, `ISO8859-6`, `CP1256`, `ISO8859-7`, `CP1253`, `ISO8859-8`, `CP1255`, `ISO8859-9`, `CP1254`, `ISO8859-10`, `ISO8859-11`, `CP874`, `ISO8859-13`, `CP1257`, `ISO8859-14`, `ISO8859-15`, `ISO8859-16`, `CP878`, `MacJapan`, `ASCII`, `ANSI_X3.4-1968`, `646`, `CP65000`, `CP65001`, `UTF-8-MAC`, `UTF-8-HFS`, `UCS-2BE`, `UCS-4BE`, `UCS-4LE`, `CP932`, `csWindows31J`, `SJIS`, `PCK`, `CP1250`, `CP1251`, `CP1252`, `external`, `locale`
75
+ * Default value is `"UTF-8"`
76
+
77
+ List of valid conversion types used for the convert option
78
+ The character encoding used in this codec. Examples include "UTF-8" and
79
+ "CP1252".
80
+
81
+ [id="plugins-{type}s-{plugin}-columns"]
82
+ ===== `columns`
83
+
84
+ * Value type is <<array,array>>
85
+ * Default value is `[]`
86
+
87
+ **When decoding:**
88
+ Define a list of column names (in the order they appear in the CSV,
89
+ as if it were a header line). If `columns` is not configured, or there
90
+ are not enough columns specified, the default column names are
91
+ "column1", "column2", etc.
92
+
93
+ **When encoding:**
94
+ List of fields names to include in the encoded CSV, in the order listed.
95
+
96
+ [id="plugins-{type}s-{plugin}-convert"]
97
+ ===== `convert`
98
+
99
+ * Value type is <<hash,hash>>
100
+ * Default value is `{}`
101
+
102
+ Define a set of datatype conversions to be applied to columns.
103
+ Possible conversions are: `integer`, `float`, `date`, `date_time`, `boolean`
104
+
105
+
106
+ *Example*
107
+ [source,ruby]
108
+ filter {
109
+ csv {
110
+ convert => { "column1" => "integer", "column2" => "boolean" }
111
+ }
112
+ }
113
+
114
+ [id="plugins-{type}s-{plugin}-ecs_compatibility"]
115
+ ===== `ecs_compatibility`
116
+
117
+ * Value type is <<string,string>>
118
+ * Supported values are:
119
+ ** `disabled`: CSV data added at root level
120
+ ** `v1`,`v8`: Elastic Common Schema compliant behavior (`[event][original]` is also added)
121
+ * Default value depends on which version of Logstash is running:
122
+ ** When Logstash provides a `pipeline.ecs_compatibility` setting, its value is used as the default
123
+ ** Otherwise, the default value is `disabled`
124
+
125
+ Controls this plugin's compatibility with the {ecs-ref}[Elastic Common Schema (ECS)].
126
+
127
+ [id="plugins-{type}s-{plugin}-include_headers"]
128
+ ===== `include_headers`
129
+
130
+ * Value type is <<boolean,boolean>>
131
+ * Default value is `false`
132
+
133
+ When **encoding** in an output plugin, include headers in the encoded CSV
134
+ once per codec lifecyle (not for every event). Default => false
135
+
136
+ [id="plugins-{type}s-{plugin}-quote_char"]
137
+ ===== `quote_char`
138
+
139
+ * Value type is <<string,string>>
140
+ * Default value is `"\""`
141
+
142
+ Define the character used to quote CSV fields. If this is not specified
143
+ the default is a double quote `"`.
144
+ Optional.
145
+
146
+ [id="plugins-{type}s-{plugin}-separator"]
147
+ ===== `separator`
148
+
149
+ * Value type is <<string,string>>
150
+ * Default value is `","`
151
+
152
+ Define the column separator value. If this is not specified, the default
153
+ is a comma `,`.
154
+ Optional.
155
+
156
+ [id="plugins-{type}s-{plugin}-skip_empty_columns"]
157
+ ===== `skip_empty_columns`
158
+
159
+ * Value type is <<boolean,boolean>>
160
+ * Default value is `false`
161
+
162
+ Define whether empty columns should be skipped.
163
+ Defaults to false. If set to true, columns containing no value will not be included.
164
+
165
+ [id="plugins-{type}s-{plugin}-target"]
166
+ ===== `target`
167
+
168
+ * Value type is <<string,string>>
169
+ * There is no default value for this setting.
170
+
171
+ Define the target field for placing the row values. If this setting is not
172
+ set, the CSV data will be stored at the root (top level) of the event.
173
+
174
+ For example, if you want data to be put under the `document` field:
175
+ [source,ruby]
176
+ input {
177
+ file {
178
+ codec => csv {
179
+ autodetect_column_names => true
180
+ target => "[document]"
181
+ }
182
+ }
183
+ }
184
+
185
+
@@ -1,18 +1,34 @@
1
1
  # encoding: utf-8
2
2
  require "logstash/codecs/base"
3
3
  require "logstash/util/charset"
4
+ require "logstash/event"
5
+
6
+ require 'logstash/plugin_mixins/ecs_compatibility_support'
7
+ require 'logstash/plugin_mixins/ecs_compatibility_support/target_check'
8
+ require 'logstash/plugin_mixins/validator_support/field_reference_validation_adapter'
9
+ require 'logstash/plugin_mixins/event_support/event_factory_adapter'
10
+ require 'logstash/plugin_mixins/event_support/from_json_helper'
11
+
4
12
  require "csv"
5
13
 
6
14
  class LogStash::Codecs::CSV < LogStash::Codecs::Base
7
15
 
16
+ include LogStash::PluginMixins::ECSCompatibilitySupport(:disabled, :v1, :v8 => :v1)
17
+ include LogStash::PluginMixins::ECSCompatibilitySupport::TargetCheck
18
+
19
+ extend LogStash::PluginMixins::ValidatorSupport::FieldReferenceValidationAdapter
20
+
21
+ include LogStash::PluginMixins::EventSupport::EventFactoryAdapter
22
+
8
23
  config_name "csv"
9
24
 
10
- # Define a list of column names (in the order they appear in the CSV,
11
- # as if it were a header line). If `columns` is not configured, or there
12
- # are not enough columns specified, the default column names are
13
- # "column1", "column2", etc. In the case that there are more columns
14
- # in the data than specified in this column list, extra columns will be auto-numbered:
15
- # (e.g. "user_defined_1", "user_defined_2", "column3", "column4", etc.)
25
+ # When decoding:
26
+ # Define a list of column names (in the order they appear in the CSV,
27
+ # as if it were a header line). If `columns` is not configured, or there
28
+ # are not enough columns specified, the default column names are
29
+ # "column1", "column2", etc.
30
+ # When encoding:
31
+ # List of fields names to include in the encoded CSV, in the order listed.
16
32
  config :columns, :validate => :array, :default => []
17
33
 
18
34
  # Define the column separator value. If this is not specified, the default
@@ -25,20 +41,22 @@ class LogStash::Codecs::CSV < LogStash::Codecs::Base
25
41
  # Optional.
26
42
  config :quote_char, :validate => :string, :default => '"'
27
43
 
28
- # Treats the first line received as the hearder information, this information will
29
- # be used to compose the field names in the generated events. Note this information can
30
- # be reset on demand, useful for example when dealing with new files in the file input
31
- # or new request in the http_poller. Default => false
44
+ # When encoding in an output plugin, include headers in the encoded CSV
45
+ # once per codec lifecyle (not for every event). Default => false
32
46
  config :include_headers, :validate => :boolean, :default => false
33
47
 
34
- # Define whether column names should autogenerated or not.
48
+ # Define whether column names should be autogenerated or not.
35
49
  # Defaults to true. If set to false, columns not having a header specified will not be parsed.
36
50
  config :autogenerate_column_names, :validate => :boolean, :default => true
37
51
 
38
52
  # Define whether empty columns should be skipped.
39
- # Defaults to false. If set to true, columns containing no value will not get set.
53
+ # Defaults to false. If set to true, columns containing no value will not be included.
40
54
  config :skip_empty_columns, :validate => :boolean, :default => false
41
55
 
56
+ # Define whether column names should be auto-detected from the header column or not.
57
+ # Defaults to false.
58
+ config :autodetect_column_names, :validate => :boolean, :default => false
59
+
42
60
  # Define a set of datatype conversions to be applied to columns.
43
61
  # Possible conversions are integer, float, date, date_time, boolean
44
62
  #
@@ -51,113 +69,132 @@ class LogStash::Codecs::CSV < LogStash::Codecs::Base
51
69
  # }
52
70
  config :convert, :validate => :hash, :default => {}
53
71
 
54
- ##
55
- # List of valid conversion types used for the convert option
56
- ##
57
- VALID_CONVERT_TYPES = [ "integer", "float", "date", "date_time", "boolean" ].freeze
58
-
59
-
60
72
  # The character encoding used in this codec. Examples include "UTF-8" and
61
73
  # "CP1252".
62
74
  config :charset, :validate => ::Encoding.name_list, :default => "UTF-8"
63
75
 
64
- def register
76
+ # Defines a target field for placing decoded fields.
77
+ # If this setting is omitted, data gets stored at the root (top level) of the event.
78
+ #
79
+ # NOTE: the target is only relevant while decoding data into a new event.
80
+ config :target, :validate => :field_reference
81
+
82
+ CONVERTERS = {
83
+ :integer => lambda do |value|
84
+ CSV::Converters[:integer].call(value)
85
+ end,
86
+
87
+ :float => lambda do |value|
88
+ CSV::Converters[:float].call(value)
89
+ end,
90
+
91
+ :date => lambda do |value|
92
+ result = CSV::Converters[:date].call(value)
93
+ result.is_a?(Date) ? LogStash::Timestamp.new(result.to_time) : result
94
+ end,
95
+
96
+ :date_time => lambda do |value|
97
+ result = CSV::Converters[:date_time].call(value)
98
+ result.is_a?(DateTime) ? LogStash::Timestamp.new(result.to_time) : result
99
+ end,
100
+
101
+ :boolean => lambda do |value|
102
+ value = value.strip.downcase
103
+ return false if value == "false"
104
+ return true if value == "true"
105
+ return value
106
+ end
107
+ }
108
+ CONVERTERS.default = lambda {|v| v}
109
+ CONVERTERS.freeze
110
+
111
+ def initialize(*params)
112
+ super
113
+
114
+ @original_field = ecs_select[disabled: nil, v1: '[event][original]']
115
+
65
116
  @converter = LogStash::Util::Charset.new(@charset)
66
117
  @converter.logger = @logger
118
+ end
67
119
 
120
+ def register
68
121
  # validate conversion types to be the valid ones.
69
- @convert.each_pair do |column, type|
70
- if !VALID_CONVERT_TYPES.include?(type)
71
- raise LogStash::ConfigurationError, "#{type} is not a valid conversion type."
72
- end
73
- end
122
+ bad_types = @convert.values.select do |type|
123
+ !CONVERTERS.has_key?(type.to_sym)
124
+ end.uniq
125
+ raise(LogStash::ConfigurationError, "Invalid conversion types: #{bad_types.join(', ')}") unless bad_types.empty?
126
+
127
+ # @convert_symbols contains the symbolized types to avoid symbol conversion in the transform method
128
+ @convert_symbols = @convert.each_with_object({}) { |(k, v), result| result[k] = v.to_sym }
74
129
 
75
- @headers = false
76
- @options = { :col_sep => @separator, :quote_char => @quote_char }
130
+ # if the zero byte character is entered in the config, set the value
131
+ @quote_char = "\x00" if @quote_char == "\\x00"
132
+
133
+ @logger.debug? && @logger.debug("CSV parsing options", :col_sep => @separator, :quote_char => @quote_char)
77
134
  end
78
135
 
79
136
  def decode(data)
80
137
  data = @converter.convert(data)
81
138
  begin
82
- values = CSV.parse_line(data, @options)
83
- if @include_headers && !@headers
84
- @headers = true
85
- @options[:headers] = values
86
- else
87
- decoded = {}
88
- values.each_with_index do |fields, index|
89
- field_name, value = nil, nil
90
- if fields.is_a?(String) && !( @skip_empty_columns && fields.nil?) # No headers
91
- next if ignore_field?(index)
92
- field_name = ( !@columns[index].nil? ? @columns[index] : "column#{(index+1)}")
93
- value = fields
94
- elsif fields.is_a?(Array) # Got headers
95
- field_name = fields[0]
96
- value = fields[1]
139
+ values = CSV.parse_line(data, :col_sep => @separator, :quote_char => @quote_char)
140
+
141
+ if (@autodetect_column_names && @columns.empty?)
142
+ @columns = values
143
+ @logger.debug? && @logger.debug("Auto detected the following columns", :columns => @columns.inspect)
144
+ return
145
+ end
146
+
147
+ decoded = {}
148
+ values.each_with_index do |value, i|
149
+ unless (@skip_empty_columns && (value.nil? || value.empty?))
150
+ unless ignore_field?(i)
151
+ field_name = @columns[i] || "column#{i + 1}"
152
+ decoded[field_name] = transform(field_name, value)
97
153
  end
98
- next unless field_name
99
- decoded[field_name] = if should_transform?(field_name)
100
- transform(field_name, value)
101
- else
102
- value
103
- end
104
154
  end
105
- yield LogStash::Event.new(decoded) if block_given?
106
155
  end
156
+
157
+ event = targeted_event_factory.new_event(decoded)
158
+ event.set(@original_field, data.dup.freeze) if @original_field
159
+ yield event
107
160
  rescue CSV::MalformedCSVError => e
108
- @logger.info("CSV parse failure. Falling back to plain-text", :error => e, :data => data)
109
- yield LogStash::Event.new("message" => data, "tags" => ["_csvparsefailure"]) if block_given?
161
+ @logger.error("CSV parse failure. Falling back to plain-text", :exception => e.class, :message => e.message, :data => data)
162
+ yield event_factory.new_event("message" => data, "tags" => ["_csvparsefailure"])
110
163
  end
111
164
  end
112
165
 
113
166
  def encode(event)
114
- csv_data = CSV.generate_line(event.to_hash.values, @options)
115
- @on_event.call(event, csv_data)
116
- end
167
+ if @include_headers
168
+ csv_data = CSV.generate_line(select_keys(event), :col_sep => @separator, :quote_char => @quote_char, :headers => true)
169
+ @on_event.call(event, csv_data)
117
170
 
118
- def reset
119
- @headers = false
120
- @options.delete(:headers)
171
+ # output headers only once per codec lifecycle
172
+ @include_headers = false
173
+ end
174
+
175
+ csv_data = CSV.generate_line(select_values(event), :col_sep => @separator, :quote_char => @quote_char)
176
+ @on_event.call(event, csv_data)
121
177
  end
122
178
 
123
179
  private
124
180
 
125
- def ignore_field?(index)
126
- !@columns[index] && !@autogenerate_column_names
181
+ def select_values(event)
182
+ if @columns.empty?
183
+ event.to_hash.values
184
+ else
185
+ @columns.map {|column| event.get(column)}
186
+ end
127
187
  end
128
188
 
129
- def should_transform?(field_name)
130
- !@convert[field_name].nil?
189
+ def select_keys(event)
190
+ @columns.empty? ? event.to_hash.keys : @columns
131
191
  end
132
192
 
133
- def transform(field_name, value)
134
- transformation = @convert[field_name].to_sym
135
- converters[transformation].call(value)
193
+ def ignore_field?(index)
194
+ !@columns[index] && !@autogenerate_column_names
136
195
  end
137
196
 
138
- def converters
139
- @converters ||= {
140
- :integer => lambda do |value|
141
- CSV::Converters[:integer].call(value)
142
- end,
143
- :float => lambda do |value|
144
- CSV::Converters[:float].call(value)
145
-
146
- end,
147
- :date => lambda do |value|
148
- CSV::Converters[:date].call(value)
149
-
150
- end,
151
- :date_time => lambda do |value|
152
- CSV::Converters[:date_time].call(value)
153
- end,
154
- :boolean => lambda do |value|
155
- value = value.strip.downcase
156
- return false if value == "false"
157
- return true if value == "true"
158
- return value
159
- end
160
- }
197
+ def transform(field_name, value)
198
+ CONVERTERS[@convert_symbols[field_name]].call(value)
161
199
  end
162
-
163
- end # class LogStash::Codecs::Plain
200
+ end
@@ -1,17 +1,17 @@
1
1
  Gem::Specification.new do |s|
2
2
 
3
3
  s.name = 'logstash-codec-csv'
4
- s.version = '0.1.2'
4
+ s.version = '1.1.0'
5
5
  s.licenses = ['Apache License (2.0)']
6
6
  s.summary = "The csv codec take CSV data, parses it and passes it away"
7
- s.description = "This gem is a logstash plugin required to be installed on top of the Logstash core pipeline using $LS_HOME/bin/plugin install gemname. This gem is not a stand-alone program"
7
+ s.description = "This gem is a Logstash plugin required to be installed on top of the Logstash core pipeline using $LS_HOME/bin/logstash-plugin install gemname. This gem is not a stand-alone program"
8
8
  s.authors = ["Elasticsearch"]
9
9
  s.email = 'info@elasticsearch.com'
10
10
  s.homepage = "http://www.elasticsearch.org/guide/en/logstash/current/index.html"
11
11
  s.require_paths = ["lib"]
12
12
 
13
13
  # Files
14
- s.files = Dir['lib/**/*','spec/**/*','vendor/**/*','*.gemspec','*.md','CONTRIBUTORS','Gemfile','LICENSE','NOTICE.TXT']
14
+ s.files = Dir["lib/**/*","spec/**/*","*.gemspec","*.md","CONTRIBUTORS","Gemfile","LICENSE","NOTICE.TXT", "vendor/jar-dependencies/**/*.jar", "vendor/jar-dependencies/**/*.rb", "VERSION", "docs/**/*"]
15
15
 
16
16
  # Tests
17
17
  s.test_files = s.files.grep(%r{^(test|spec|features)/})
@@ -20,7 +20,10 @@ Gem::Specification.new do |s|
20
20
  s.metadata = { "logstash_plugin" => "true", "logstash_group" => "codec" }
21
21
 
22
22
  # Gem dependencies
23
- s.add_runtime_dependency "logstash-core-plugin-api", "~> 1.0"
23
+ s.add_runtime_dependency "logstash-core-plugin-api", ">= 1.60", "<= 2.99"
24
+ s.add_runtime_dependency 'logstash-mixin-ecs_compatibility_support', '~> 1.3'
25
+ s.add_runtime_dependency 'logstash-mixin-event_support', '~> 1.0'
26
+ s.add_runtime_dependency 'logstash-mixin-validator_support', '~> 1.0'
24
27
 
25
28
  s.add_development_dependency 'logstash-devutils'
26
29
  end
@@ -1,8 +1,10 @@
1
1
  # encoding: utf-8
2
+ require "logstash/devutils/rspec/spec_helper"
2
3
  require "logstash/codecs/csv"
3
- require "logstash/event"
4
4
 
5
- describe LogStash::Codecs::CSV do
5
+ require 'logstash/plugin_mixins/ecs_compatibility_support/spec_helper'
6
+
7
+ describe LogStash::Codecs::CSV, :ecs_compatibility_support do
6
8
 
7
9
  subject(:codec) { LogStash::Codecs::CSV.new(config) }
8
10
  let(:config) { Hash.new }
@@ -15,192 +17,281 @@ describe LogStash::Codecs::CSV do
15
17
 
16
18
  let(:data) { "big,bird,sesame street" }
17
19
 
18
- it "return an event from CSV data" do
19
- codec.decode(data) do |event|
20
- expect(event["column1"]).to eq("big")
21
- expect(event["column2"]).to eq("bird")
22
- expect(event["column3"]).to eq("sesame street")
23
- end
24
- end
20
+ ecs_compatibility_matrix(:disabled, :v1, :v8 => :v1) do |ecs_select|
25
21
 
26
- describe "given column names" do
27
- let(:doc) { "big,bird,sesame street" }
28
- let(:config) do
29
- { "columns" => ["first", "last", "address" ] }
30
- end
22
+ let(:config) { super().merge('ecs_compatibility' => ecs_select.active_mode.to_s) }
31
23
 
32
- it "extract all the values" do
24
+ it "return an event from CSV data" do
25
+ event_count = 0
33
26
  codec.decode(data) do |event|
34
- expect(event["first"]).to eq("big")
35
- expect(event["last"]).to eq("bird")
36
- expect(event["address"]).to eq("sesame street")
27
+ event_count += 1
28
+ expect(event.get("column1")).to eq("big")
29
+ expect(event.get("column2")).to eq("bird")
30
+ expect(event.get("column3")).to eq("sesame street")
37
31
  end
32
+ expect( event_count ).to eql 1
38
33
  end
39
34
 
40
- context "parse csv skipping empty columns" do
41
-
42
- let(:data) { "val1,,val3" }
43
-
35
+ describe "given column names" do
36
+ let(:doc) { "big,bird,sesame street" }
44
37
  let(:config) do
45
- { "skip_empty_columns" => true,
46
- "columns" => ["custom1", "custom2", "custom3"] }
38
+ { "columns" => ["first", "last", "address" ] }
47
39
  end
48
40
 
49
41
  it "extract all the values" do
50
42
  codec.decode(data) do |event|
51
- expect(event["custom1"]).to eq("val1")
52
- expect(event.to_hash).not_to include("custom2")
53
- expect(event["custom3"]).to eq("val3")
43
+ expect(event.get("first")).to eq("big")
44
+ expect(event.get("last")).to eq("bird")
45
+ expect(event.get("address")).to eq("sesame street")
54
46
  end
55
47
  end
48
+
49
+ context "parse csv skipping empty columns" do
50
+
51
+ let(:data) { "val1,,val3" }
52
+
53
+ let(:config) do
54
+ { "skip_empty_columns" => true,
55
+ "columns" => ["custom1", "custom2", "custom3"] }
56
+ end
57
+
58
+ it "extract all the values" do
59
+ codec.decode(data) do |event|
60
+ expect(event.get("custom1")).to eq("val1")
61
+ expect(event.to_hash).not_to include("custom2")
62
+ expect(event.get("custom3")).to eq("val3")
63
+ end
64
+ end
65
+ end
66
+
67
+ context "parse csv without autogeneration of names" do
68
+
69
+ let(:data) { "val1,val2,val3" }
70
+ let(:config) do
71
+ { "autogenerate_column_names" => false,
72
+ "columns" => ["custom1", "custom2"] }
73
+ end
74
+
75
+ it "extract all the values" do
76
+ codec.decode(data) do |event|
77
+ expect(event.get("custom1")).to eq("val1")
78
+ expect(event.get("custom2")).to eq("val2")
79
+ expect(event.get("column3")).to be_falsey
80
+ end
81
+ end
82
+ end
83
+
56
84
  end
57
85
 
58
- context "parse csv without autogeneration of names" do
86
+ describe "custom separator" do
87
+ let(:data) { "big,bird;sesame street" }
59
88
 
60
- let(:data) { "val1,val2,val3" }
61
89
  let(:config) do
62
- { "autogenerate_column_names" => false,
63
- "columns" => ["custom1", "custom2"] }
90
+ { "separator" => ";" }
64
91
  end
65
92
 
66
- it "extract all the values" do
93
+ it "return an event from CSV data" do
67
94
  codec.decode(data) do |event|
68
- expect(event["custom1"]).to eq("val1")
69
- expect(event["custom2"]).to eq("val2")
70
- expect(event["column3"]).to be_falsey
95
+ expect(event.get("column1")).to eq("big,bird")
96
+ expect(event.get("column2")).to eq("sesame street")
71
97
  end
72
98
  end
73
99
  end
74
100
 
75
- end
101
+ describe "quote char" do
102
+ let(:data) { "big,bird,'sesame street'" }
76
103
 
77
- describe "custom separator" do
78
- let(:data) { "big,bird;sesame street" }
104
+ let(:config) do
105
+ { "quote_char" => "'"}
106
+ end
79
107
 
80
- let(:config) do
81
- { "separator" => ";" }
82
- end
108
+ it "return an event from CSV data" do
109
+ codec.decode(data) do |event|
110
+ expect(event.get("column1")).to eq("big")
111
+ expect(event.get("column2")).to eq("bird")
112
+ expect(event.get("column3")).to eq("sesame street")
113
+ end
114
+ end
83
115
 
84
- it "return an event from CSV data" do
85
- codec.decode(data) do |event|
86
- expect(event["column1"]).to eq("big,bird")
87
- expect(event["column2"]).to eq("sesame street")
116
+ context "using the default one" do
117
+ let(:data) { 'big,bird,"sesame, street"' }
118
+ let(:config) { Hash.new }
119
+
120
+ it "return an event from CSV data" do
121
+ codec.decode(data) do |event|
122
+ expect(event.get("column1")).to eq("big")
123
+ expect(event.get("column2")).to eq("bird")
124
+ expect(event.get("column3")).to eq("sesame, street")
125
+ end
126
+ end
88
127
  end
89
- end
90
- end
91
128
 
92
- describe "quote char" do
93
- let(:data) { "big,bird,'sesame street'" }
129
+ context "using a null" do
130
+ let(:data) { 'big,bird,"sesame" street' }
131
+ let(:config) do
132
+ { "quote_char" => "\x00" }
133
+ end
94
134
 
95
- let(:config) do
96
- { "quote_char" => "'"}
135
+ it "return an event from CSV data" do
136
+ codec.decode(data) do |event|
137
+ expect(event.get("column1")).to eq("big")
138
+ expect(event.get("column2")).to eq("bird")
139
+ expect(event.get("column3")).to eq('"sesame" street')
140
+ end
141
+ end
142
+ end
97
143
  end
98
144
 
99
- it "return an event from CSV data" do
100
- codec.decode(data) do |event|
101
- expect(event["column1"]).to eq("big")
102
- expect(event["column2"]).to eq("bird")
103
- expect(event["column3"]).to eq("sesame street")
145
+ describe "having headers" do
146
+
147
+ let(:data) do
148
+ [ "size,animal,movie", "big,bird,sesame street"]
104
149
  end
105
- end
106
150
 
107
- context "using the default one" do
108
- let(:data) { 'big,bird,"sesame, street"' }
109
- let(:config) { Hash.new }
151
+ let(:new_data) do
152
+ [ "host,country,city", "example.com,germany,berlin"]
153
+ end
110
154
 
111
- it "return an event from CSV data" do
112
- codec.decode(data) do |event|
113
- expect(event["column1"]).to eq("big")
114
- expect(event["column2"]).to eq("bird")
115
- expect(event["column3"]).to eq("sesame, street")
155
+ let(:config) do
156
+ { "autodetect_column_names" => true }
157
+ end
158
+
159
+ it "include header information when requested" do
160
+ codec.decode(data[0]) # Read the headers
161
+ codec.decode(data[1]) do |event|
162
+ expect(event.get("size")).to eq("big")
163
+ expect(event.get("animal")).to eq("bird")
164
+ expect(event.get("movie")).to eq("sesame street")
116
165
  end
117
166
  end
118
167
  end
119
168
 
120
- context "using a null" do
121
- let(:data) { 'big,bird,"sesame" street' }
169
+ describe "using field conversion" do
170
+
122
171
  let(:config) do
123
- { "quote_char" => "\x00" }
172
+ { "convert" => { "column1" => "integer", "column3" => "boolean" } }
124
173
  end
174
+ let(:data) { "1234,bird,false" }
125
175
 
126
- it "return an event from CSV data" do
176
+ it "get converted values to the expected type" do
127
177
  codec.decode(data) do |event|
128
- expect(event["column1"]).to eq("big")
129
- expect(event["column2"]).to eq("bird")
130
- expect(event["column3"]).to eq('"sesame" street')
178
+ expect(event.get("column1")).to eq(1234)
179
+ expect(event.get("column2")).to eq("bird")
180
+ expect(event.get("column3")).to eq(false)
131
181
  end
132
182
  end
133
- end
134
- end
135
183
 
136
- describe "having headers" do
184
+ context "when using column names" do
137
185
 
138
- let(:data) do
139
- [ "size,animal,movie", "big,bird,sesame street"]
140
- end
186
+ let(:config) do
187
+ { "convert" => { "custom1" => "integer", "custom3" => "boolean" },
188
+ "columns" => ["custom1", "custom2", "custom3"] }
189
+ end
141
190
 
142
- let(:new_data) do
143
- [ "host,country,city", "example.com,germany,berlin"]
191
+ it "get converted values to the expected type" do
192
+ codec.decode(data) do |event|
193
+ expect(event.get("custom1")).to eq(1234)
194
+ expect(event.get("custom2")).to eq("bird")
195
+ expect(event.get("custom3")).to eq(false)
196
+ end
197
+ end
198
+ end
144
199
  end
145
200
 
146
- let(:config) do
147
- { "include_headers" => true }
201
+ context "with target" do
202
+
203
+ let(:config) { super().merge('target' => '[csv-root]') }
204
+
205
+ it "return an event from CSV data" do
206
+ event_count = 0
207
+ codec.decode(data) do |event|
208
+ event_count += 1
209
+ expect( event.include?("column1") ).to be false
210
+ expect( event.get("csv-root") ).to eql('column1' => 'big', 'column2' => 'bird', 'column3' => "sesame street")
211
+ end
212
+ expect( event_count ).to eql 1
213
+ end
214
+
215
+ it 'set event.original in ECS mode' do
216
+ codec.decode(data) do |event|
217
+ if ecs_select.active_mode == :disabled
218
+ expect( event.get("[event][original]") ).to be nil
219
+ else
220
+ expect( event.get("[event][original]") ).to eql data
221
+ end
222
+ end
223
+ end
224
+
148
225
  end
226
+ end
227
+
228
+ end
149
229
 
150
- it "include header information when requested" do
151
- codec.decode(data[0]) # Read the headers
152
- codec.decode(data[1]) do |event|
153
- expect(event["size"]).to eq("big")
154
- expect(event["animal"]).to eq("bird")
155
- expect(event["movie"]).to eq("sesame street")
230
+ describe "encode" do
231
+ context "not including headers" do
232
+ let(:event) { LogStash::Event.new({"f1" => "v1", "f2" => "v2"}) }
233
+
234
+ context "without columns" do
235
+ let(:config) do
236
+ { "include_headers" => false, "columns" => [] }
237
+ end
238
+
239
+ it "should encode to single CSV line" do
240
+ codec.on_event do |e, d|
241
+ expect(d.chomp.split(",").sort).to eq("v1,v2,1,#{event.timestamp}".split(",").sort)
242
+ end
243
+ codec.encode(event)
156
244
  end
157
245
  end
158
246
 
159
- it "reset headers and fetch the new ones" do
160
- data.each do |row|
161
- codec.decode(row)
247
+ context "with columns" do
248
+ let(:config) do
249
+ { "include_headers" => false, "columns" => ["f1", "f2"] }
162
250
  end
163
- codec.reset
164
- codec.decode(new_data[0]) # set the new headers
165
- codec.decode(new_data[1]) do |event|
166
- expect(event["host"]).to eq("example.com")
167
- expect(event["country"]).to eq("germany")
168
- expect(event["city"]).to eq("berlin")
251
+
252
+ it "should encode to single CSV line" do
253
+ codec.on_event do |e, d|
254
+ expect(d).to eq("v1,v2\n")
255
+ end
256
+ codec.encode(event)
169
257
  end
170
258
  end
171
259
  end
172
260
 
173
- describe "using field convertion" do
261
+ context "including headers" do
262
+ let(:event) { LogStash::Event.new({"f1" => "v1", "f2" => "v2"}) }
174
263
 
175
- let(:config) do
176
- { "convert" => { "column1" => "integer", "column3" => "boolean" } }
177
- end
178
- let(:data) { "1234,bird,false" }
264
+ context "without columns" do
265
+ let(:config) do
266
+ { "include_headers" => true, "columns" => [] }
267
+ end
179
268
 
180
- it "get converted values to the expected type" do
181
- codec.decode(data) do |event|
182
- expect(event["column1"]).to eq(1234)
183
- expect(event["column2"]).to eq("bird")
184
- expect(event["column3"]).to eq(false)
269
+ it "should encode to two CSV line" do
270
+ lines = []
271
+ codec.on_event do |e, d|
272
+ lines << d
273
+ end
274
+ codec.encode(event)
275
+ expect(lines[0].chomp.split(",").sort).to eq("f1,f2,@version,@timestamp".split(",").sort)
276
+ expect(lines[1].chomp.split(",").sort).to eq("v1,v2,1,#{event.timestamp}".split(",").sort)
185
277
  end
186
278
  end
187
279
 
188
- context "when using column names" do
189
-
280
+ context "with columns" do
190
281
  let(:config) do
191
- { "convert" => { "custom1" => "integer", "custom3" => "boolean" },
192
- "columns" => ["custom1", "custom2", "custom3"] }
282
+ { "include_headers" => true, "columns" => ["f1", "f2"] }
193
283
  end
194
284
 
195
- it "get converted values to the expected type" do
196
- codec.decode(data) do |event|
197
- expect(event["custom1"]).to eq(1234)
198
- expect(event["custom2"]).to eq("bird")
199
- expect(event["custom3"]).to eq(false)
285
+ it "should encode to two CSV line" do
286
+ lines = []
287
+ codec.on_event do |e, d|
288
+ lines << d
200
289
  end
290
+ codec.encode(event)
291
+ expect(lines[0]).to eq("f1,f2\n")
292
+ expect(lines[1]).to eq("v1,v2\n")
201
293
  end
202
294
  end
203
295
  end
204
-
205
296
  end
206
297
  end
metadata CHANGED
@@ -1,24 +1,72 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: logstash-codec-csv
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.2
4
+ version: 1.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Elasticsearch
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2016-03-24 00:00:00.000000000 Z
11
+ date: 2021-07-28 00:00:00.000000000 Z
12
12
  dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ requirement: !ruby/object:Gem::Requirement
15
+ requirements:
16
+ - - ">="
17
+ - !ruby/object:Gem::Version
18
+ version: '1.60'
19
+ - - "<="
20
+ - !ruby/object:Gem::Version
21
+ version: '2.99'
22
+ name: logstash-core-plugin-api
23
+ type: :runtime
24
+ prerelease: false
25
+ version_requirements: !ruby/object:Gem::Requirement
26
+ requirements:
27
+ - - ">="
28
+ - !ruby/object:Gem::Version
29
+ version: '1.60'
30
+ - - "<="
31
+ - !ruby/object:Gem::Version
32
+ version: '2.99'
33
+ - !ruby/object:Gem::Dependency
34
+ requirement: !ruby/object:Gem::Requirement
35
+ requirements:
36
+ - - "~>"
37
+ - !ruby/object:Gem::Version
38
+ version: '1.3'
39
+ name: logstash-mixin-ecs_compatibility_support
40
+ type: :runtime
41
+ prerelease: false
42
+ version_requirements: !ruby/object:Gem::Requirement
43
+ requirements:
44
+ - - "~>"
45
+ - !ruby/object:Gem::Version
46
+ version: '1.3'
13
47
  - !ruby/object:Gem::Dependency
14
48
  requirement: !ruby/object:Gem::Requirement
15
49
  requirements:
16
50
  - - "~>"
17
51
  - !ruby/object:Gem::Version
18
52
  version: '1.0'
19
- name: logstash-core-plugin-api
53
+ name: logstash-mixin-event_support
54
+ type: :runtime
20
55
  prerelease: false
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ requirements:
58
+ - - "~>"
59
+ - !ruby/object:Gem::Version
60
+ version: '1.0'
61
+ - !ruby/object:Gem::Dependency
62
+ requirement: !ruby/object:Gem::Requirement
63
+ requirements:
64
+ - - "~>"
65
+ - !ruby/object:Gem::Version
66
+ version: '1.0'
67
+ name: logstash-mixin-validator_support
21
68
  type: :runtime
69
+ prerelease: false
22
70
  version_requirements: !ruby/object:Gem::Requirement
23
71
  requirements:
24
72
  - - "~>"
@@ -31,14 +79,16 @@ dependencies:
31
79
  - !ruby/object:Gem::Version
32
80
  version: '0'
33
81
  name: logstash-devutils
34
- prerelease: false
35
82
  type: :development
83
+ prerelease: false
36
84
  version_requirements: !ruby/object:Gem::Requirement
37
85
  requirements:
38
86
  - - ">="
39
87
  - !ruby/object:Gem::Version
40
88
  version: '0'
41
- description: This gem is a logstash plugin required to be installed on top of the Logstash core pipeline using $LS_HOME/bin/plugin install gemname. This gem is not a stand-alone program
89
+ description: This gem is a Logstash plugin required to be installed on top of the
90
+ Logstash core pipeline using $LS_HOME/bin/logstash-plugin install gemname. This
91
+ gem is not a stand-alone program
42
92
  email: info@elasticsearch.com
43
93
  executables: []
44
94
  extensions: []
@@ -49,6 +99,7 @@ files:
49
99
  - Gemfile
50
100
  - LICENSE
51
101
  - README.md
102
+ - docs/index.asciidoc
52
103
  - lib/logstash/codecs/csv.rb
53
104
  - logstash-codec-csv.gemspec
54
105
  - spec/codecs/csv_spec.rb
@@ -73,8 +124,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
73
124
  - !ruby/object:Gem::Version
74
125
  version: '0'
75
126
  requirements: []
76
- rubyforge_project:
77
- rubygems_version: 2.4.8
127
+ rubygems_version: 3.0.6
78
128
  signing_key:
79
129
  specification_version: 4
80
130
  summary: The csv codec take CSV data, parses it and passes it away