logstash-output-scacsv 1.0.0

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: 02e5d4b3c9ceaa6e7968a2ec2e3563e1b9bb2119
4
+ data.tar.gz: a979b0ca6d5bb260769f9f5e83d5e315e7e04728
5
+ SHA512:
6
+ metadata.gz: f0b09ab0d67f2e4cee37eb3bd34c3e75c06ebf16e60ea346d511dccc90d402dd73e4b4d713d9dbb990ff54f52bbce0e28a8bc3d66b95fe7329377b090dcdd677
7
+ data.tar.gz: 9ba0172228e956cb5a0c7cb71a4851c6b8424f9d3d631fd2a5468e02c776fc0346f404f76fada3f6a2178c470381f107873d660792ffce9d8003d3d5f80ada29
data/Gemfile ADDED
@@ -0,0 +1,2 @@
1
+ source 'https://rubygems.org'
2
+ gemspec
data/README.md ADDED
@@ -0,0 +1,86 @@
1
+ # Logstash Plugin
2
+
3
+ This is a plugin for [Logstash](https://github.com/elasticsearch/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
+ ## Documentation
8
+
9
+ 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.elasticsearch.org/guide/en/logstash/current/).
10
+
11
+ - For formatting code or config example, you can use the asciidoc `[source,ruby]` directive
12
+ - For more asciidoc formatting tips, see the excellent reference here https://github.com/elasticsearch/docs#asciidoc-guide
13
+
14
+ ## Need Help?
15
+
16
+ Need help? Try #logstash on freenode IRC or the https://discuss.elastic.co/c/logstash discussion forum.
17
+
18
+ ## Developing
19
+
20
+ ### 1. Plugin Developement and Testing
21
+
22
+ #### Code
23
+ - To get started, you'll need JRuby with the Bundler gem installed.
24
+
25
+ - 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).
26
+
27
+ - Install dependencies
28
+ ```sh
29
+ bundle install
30
+ ```
31
+
32
+ #### Test
33
+
34
+ - Update your dependencies
35
+
36
+ ```sh
37
+ bundle install
38
+ ```
39
+
40
+ - Run tests
41
+
42
+ ```sh
43
+ bundle exec rspec
44
+ ```
45
+
46
+ ### 2. Running your unpublished Plugin in Logstash
47
+
48
+ #### 2.1 Run in a local Logstash clone
49
+
50
+ - Edit Logstash `Gemfile` and add the local plugin path, for example:
51
+ ```ruby
52
+ gem "logstash-filter-awesome", :path => "/your/local/logstash-filter-awesome"
53
+ ```
54
+ - Install plugin
55
+ ```sh
56
+ bin/plugin install --no-verify
57
+ ```
58
+ - Run Logstash with your plugin
59
+ ```sh
60
+ bin/logstash -e 'filter {awesome {}}'
61
+ ```
62
+ At this point any modifications to the plugin code will be applied to this local Logstash setup. After modifying the plugin, simply rerun Logstash.
63
+
64
+ #### 2.2 Run in an installed Logstash
65
+
66
+ 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:
67
+
68
+ - Build your plugin gem
69
+ ```sh
70
+ gem build logstash-filter-awesome.gemspec
71
+ ```
72
+ - Install the plugin from the Logstash home
73
+ ```sh
74
+ bin/plugin install /your/local/plugin/logstash-filter-awesome.gem
75
+ ```
76
+ - Start Logstash and proceed to test the plugin
77
+
78
+ ## Contributing
79
+
80
+ All contributions are welcome: ideas, patches, documentation, bug reports, complaints, and even something you drew up on a napkin.
81
+
82
+ 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.
83
+
84
+ It is more important to the community that you are able to contribute.
85
+
86
+ For more information about contributing, see the [CONTRIBUTING](https://github.com/elasticsearch/logstash/blob/master/CONTRIBUTING.md) file.
data/Rakefile ADDED
@@ -0,0 +1 @@
1
+ require "logstash/devutils/rake"
@@ -0,0 +1,292 @@
1
+ ############################################
2
+ #
3
+ # Scacsv
4
+ #
5
+ # Logstash mediation output for SCAPI
6
+ #
7
+ # Version 160215.1 Robert Mckeown
8
+ #
9
+ ############################################
10
+
11
+ require "csv"
12
+ require "logstash/namespace"
13
+ require "logstash/outputs/file"
14
+ require 'java' # for the java data format stuff
15
+
16
+ # SCACSV - based upon original Logstash CSV output.
17
+ #
18
+ # Write events to disk in CSV format
19
+ # Write a PI header as the first line in the file
20
+ # Name file per PI convention, based upon first and last timestamps encountered
21
+
22
+ class LogStash::Outputs::SCACSV < LogStash::Outputs::File
23
+
24
+ config_name "scacsv"
25
+ milestone 1
26
+
27
+ # The field names from the event that should be written to the CSV file.
28
+ # Fields are written to the CSV in the same order as the array.
29
+ # If a field does not exist on the event, an empty string will be written.
30
+ config :fields, :validate => :array, :required => true
31
+
32
+
33
+ # If present, the values here will over-ride the default header
34
+ # names. Useful if you simply want to provide other names
35
+ config :header, :validate => :array, :required => false
36
+
37
+ # Options for CSV output. This is passed directly to the Ruby stdlib to\_csv function.
38
+ # Full documentation is available here: [http://ruby-doc.org/stdlib-2.0.0/libdoc/csv/rdoc/index.html].
39
+ # A typical use case would be to use alternative column or row seperators eg: `csv_options => {"col_sep" => "\t" "row_sep" => "\r\n"}` gives tab seperated data with windows line endings
40
+ config :csv_options, :validate => :hash, :required => false, :default => Hash.new
41
+
42
+ # Name of the output group - used as a prefix in the renamed file
43
+ config :group, :validate => :string, :required => true
44
+ config :max_size, :validate => :number, :default => 0
45
+ config :flush_interval, :validate => :number, :default => 60
46
+ config :time_field, :validate => :string, :default => "timestamp"
47
+ # config :time_format, :validate => :string, :default => "%Y%m%d%H%M%S"
48
+ config :time_field_format, :validate => :string, :required => true
49
+ config :timestamp_output_format, :validate => :string, :default => "" # "yyyyMMddHHmmss" # java format
50
+
51
+
52
+
53
+ config :tz_offset, :validate => :number, :default => 0
54
+ config :increment_time, :validate => :boolean, :default => false
55
+
56
+ public
57
+ def register
58
+ super
59
+ @csv_options = Hash[@csv_options.map{|(k,v)|[k.to_sym, v]}]
60
+
61
+ # variables to hold the start and end times which we'll use to rename the files to
62
+ @startTime = "missingStartTime"
63
+ @endTime = "missingEndTime"
64
+ @recordCount = 0
65
+
66
+ @lastOutputTime = 0
67
+ @flushInterval = @flush_interval.to_i
68
+
69
+ @timerThread = Thread.new { flushWatchdog(@flush_interval) }
70
+
71
+ end
72
+
73
+ # This thread ensures that we output (close and rename) a file every so often
74
+ private
75
+ def flushWatchdog(delay)
76
+ begin
77
+ @logger.debug("SCACSVFlushWatchdog - Last output time = " + @lastOutputTime.to_s)
78
+ while true do
79
+ @logger.debug("SCACSVFlushWatchdog - Time.now = " + Time.now.to_s + " $lastOutputTime=" + @lastOutputTime.to_s + " delay=" + delay.to_s)
80
+
81
+ if ( (Time.now.to_i >= (@lastOutputTime.to_i + delay.to_i)) and (@recordCount > 0)) then
82
+ @logger.debug("SCACSVFlushWatchdog - closeAndRenameCurrentFile")
83
+ closeAndRenameCurrentFile
84
+ end
85
+ @logger.debug("SCACSVFlushWatchdog - Sleeping")
86
+ sleep 1
87
+ end
88
+ end
89
+ end
90
+
91
+ public
92
+ def receive(event)
93
+ return unless output?(event)
94
+
95
+ @logger.debug("in SCACSV receive")
96
+
97
+ if (event['SCAWindowMarker'])
98
+ # just eat the marker - don't output it
99
+ # if we had at least one record output, then close the file and move on
100
+ if @recordCount >= 1
101
+ closeAndRenameCurrentFile
102
+ end
103
+ else
104
+ @formattedPath = event.sprintf(@path)
105
+ fd = open(@formattedPath)
106
+ @logger.debug("SCACSVreceive - after opening fd=" + fd.to_s)
107
+
108
+ if @recordCount == 0
109
+ # output header on first line - note, need a minimum of one record for sensible output
110
+ if @header then
111
+ # csv_header = @fields.map { |name| name }
112
+ fd.write(@header.to_csv(@csv_options))
113
+ else
114
+ fd.write(@fields.to_csv(@csv_options))
115
+ end
116
+ end
117
+
118
+ csv_values = @fields.map {|name| get_value(name, event)}
119
+ fd.write(csv_values.to_csv(@csv_options))
120
+
121
+ flush(fd)
122
+ close_stale_files
123
+
124
+ # remember state
125
+ @recordCount = @recordCount + 1
126
+ @lastOutputTime = Time.now
127
+
128
+ # capture the earliest - assumption is that records are in order
129
+ if (@recordCount) == 1
130
+ @startTime = event[@time_field]
131
+ end
132
+
133
+ # for every record, update endTime - again, assumption is that records are in order
134
+ @endTime = event[@time_field]
135
+
136
+ if ((@max_size > 0) and (@recordCount >= max_size))
137
+ # Have enough records, close it out
138
+ closeAndRenameCurrentFile
139
+ end
140
+
141
+ end
142
+
143
+ end #def receive
144
+
145
+ private
146
+ def get_value(name, event)
147
+ val = event[name]
148
+ case val
149
+ when Hash
150
+ return val.to_json
151
+ else
152
+ return val
153
+ end
154
+ end
155
+
156
+ private
157
+ def epochAsJavaDate( epochTimestamp )
158
+
159
+ x = 0
160
+ if epochTimestamp.to_s.length == 13
161
+ x = java.util.Date.new(epochTimestamp.to_i)
162
+ else
163
+ # should be 10
164
+ x = java.util.Date.new(epochTimestamp.to_i * 1000)
165
+ end
166
+ x
167
+ end
168
+
169
+ def formatOutputTime( timestamp, time_field_format, timestamp_output_format, missingString )
170
+
171
+ outputString = ""
172
+
173
+ begin
174
+
175
+ if timestamp.nil? then
176
+ @logger.debug("SCACSV " + missingString + " for #{group}")
177
+ elsif timestamp_output_format == "epoch" then
178
+ outputString = timestamp.to_s
179
+ elsif timestamp_output_format == "" then
180
+ # use time_field format
181
+ if time_field_format == "epoch" then
182
+ outputString = timestamp.to_s
183
+ else
184
+ df = java.text.SimpleDateFormat.new(time_field_format)
185
+ outputString = df.format(epochAsJavaDate(timestamp))
186
+ end
187
+ else # explicit java timeformat supplied
188
+ df = java.text.SimpleDateFormat.new(timestamp_output_format)
189
+ outputString = df.format(epochAsJavaDate(timestamp))
190
+ end
191
+
192
+ rescue Exception => e
193
+ @logger.error("Exception determining output file timestamp. " + missingString, :exception => e)
194
+ outputString = missingString
195
+ end
196
+
197
+ outputString
198
+
199
+ end
200
+
201
+ def closeAndRenameCurrentFile
202
+
203
+ # cloned and changed from the 'file.rb' operator
204
+ # even though this is in a loop - assumption is that we have one file here for the SCA CSV use
205
+ @files.each do |path, fd|
206
+ begin
207
+ fd.close
208
+ @files.delete(path) # so it will be forgotten and we can open it up again if needed
209
+ @logger.debug("closeAndRenameCurrentFile #{path}", :fd => fd)
210
+
211
+ # Now the various time adjustments
212
+
213
+ begin # determine start&end times
214
+
215
+ if (@time_field_format != "epoch")
216
+ # if not epoch, then we expect java timestamp format
217
+ # so must convert start/end times
218
+
219
+ df = java.text.SimpleDateFormat.new(@time_field_format)
220
+ nStartTime = df.parse(@startTime)
221
+ nEndTime = df.parse(@endTime)
222
+
223
+ @startTime = df.parse(@startTime).getTime
224
+ @endTime = df.parse(@endTime).getTime
225
+
226
+ end
227
+
228
+ # Ensure epoch time from here on out
229
+
230
+ if (!@startTime.nil?)
231
+ @startTime = @startTime.to_i + @tz_offset
232
+ end
233
+
234
+ if (!@endTime.nil?)
235
+ @endTime = @endTime.to_i + @tz_offset
236
+ if (@increment_time)
237
+ # increment is used to ensure that the end-time on the filename is after the last data value
238
+
239
+ @endTime = @endTime.to_i + 1000 # 1000ms = 1sec
240
+
241
+ end
242
+ end
243
+
244
+ # then do conversion for output
245
+
246
+ # @startTime = formatOutputTime( time, time_field_format, timestamp_output_format, missingString )
247
+ @startTime = formatOutputTime( @startTime, @time_field_format, @timestamp_output_format, "noStartTime" )
248
+ @endTime = formatOutputTime( @endTime, @time_field_format, @timestamp_output_format, "noEndTime" )
249
+
250
+ rescue Exception => e
251
+ @logger.error("Exception while flushing and closing files - preparing start/end time", :exception => e)
252
+ raise
253
+ end
254
+
255
+ # timestamps are strings here
256
+
257
+ newFilename = "#{group}" + "__" + @startTime + "__" + @endTime + ".csv"
258
+
259
+ if newFilename.include? '/'
260
+ @logger.error("New filename " + newFilename + " cannot contain / characters. Check the timestamp format. / characters stripped from filename")
261
+ newFilename = newFilename.delete! '/'
262
+ end
263
+
264
+ realdirpath = File.dirname(File.realdirpath("#{path}"))
265
+ realdirpath = File.dirname(File.realdirpath(path))
266
+ oldFilename = File.basename(path)
267
+
268
+ File.rename(realdirpath + "/" + oldFilename, realdirpath + "/" + newFilename)
269
+
270
+ # reset record count so we'll pick up new start time, and put a header on next file
271
+ # when a new record comes in
272
+ @recordCount = 0
273
+ @lastOutputTime = Time.now
274
+
275
+ rescue Exception => e
276
+ @logger.error("Exception while flushing and closing files.", :exception => e)
277
+ end
278
+ end
279
+
280
+ end
281
+
282
+ def teardown
283
+ @logger.debug("SCACSV - Teardown: closing files")
284
+
285
+ Thread.kill(@timerThread)
286
+ closeAndRenameCurrentFile
287
+
288
+ finished
289
+ end
290
+
291
+ end # class LogStash::Outputs::SCACSV
292
+
@@ -0,0 +1,24 @@
1
+ Gem::Specification.new do |s|
2
+ s.name = 'logstash-output-scacsv'
3
+ s.version = "1.0.0"
4
+ s.licenses = ["Apache License (2.0)"]
5
+ s.summary = "Receives a stream of events and outputs files meeting the csv reqmts for IBM SmartCloudAnalytics Predictive Insights"
6
+ 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.authors = ["Robert Mckeown"]
8
+ s.email = "rmckeown@us.ibm.com"
9
+ s.homepage = "https://github.com/IBM-ITOAdev/logstash-output-scacsv/blob/master/README.md"
10
+ s.require_paths = ["lib"]
11
+
12
+ # Files
13
+ s.files = `git ls-files`.split($\)
14
+ # Tests
15
+ s.test_files = s.files.grep(%r{^(test|spec|features)/})
16
+
17
+ # Special flag to let us know this is actually a logstash plugin
18
+ s.metadata = { "logstash_plugin" => "true", "logstash_group" => "output" }
19
+
20
+ # Gem dependencies
21
+ s.add_runtime_dependency "logstash-core", ">= 1.4.0", "< 2.0.0"
22
+ s.add_runtime_dependency "logstash-codec-plain"
23
+ s.add_development_dependency "logstash-devutils"
24
+ end
@@ -0,0 +1,21 @@
1
+ require "logstash/devutils/rspec/spec_helper"
2
+ require "logstash/outputs/example"
3
+ require "logstash/codecs/plain"
4
+ require "logstash/event"
5
+
6
+ describe LogStash::Outputs::Example do
7
+ let(:sample_event) { LogStash::Event.new }
8
+ let(:output) { LogStash::Outputs::Example.new }
9
+
10
+ before do
11
+ output.register
12
+ end
13
+
14
+ describe "receive message" do
15
+ subject { output.receive(sample_event) }
16
+
17
+ it "returns a string" do
18
+ expect(subject).to eq("Event received")
19
+ end
20
+ end
21
+ end
metadata ADDED
@@ -0,0 +1,100 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: logstash-output-scacsv
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ platform: ruby
6
+ authors:
7
+ - Robert Mckeown
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2015-06-12 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: logstash-core
15
+ version_requirements: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - '>='
18
+ - !ruby/object:Gem::Version
19
+ version: 1.4.0
20
+ - - <
21
+ - !ruby/object:Gem::Version
22
+ version: 2.0.0
23
+ requirement: !ruby/object:Gem::Requirement
24
+ requirements:
25
+ - - '>='
26
+ - !ruby/object:Gem::Version
27
+ version: 1.4.0
28
+ - - <
29
+ - !ruby/object:Gem::Version
30
+ version: 2.0.0
31
+ prerelease: false
32
+ type: :runtime
33
+ - !ruby/object:Gem::Dependency
34
+ name: logstash-codec-plain
35
+ version_requirements: !ruby/object:Gem::Requirement
36
+ requirements:
37
+ - - '>='
38
+ - !ruby/object:Gem::Version
39
+ version: '0'
40
+ requirement: !ruby/object:Gem::Requirement
41
+ requirements:
42
+ - - '>='
43
+ - !ruby/object:Gem::Version
44
+ version: '0'
45
+ prerelease: false
46
+ type: :runtime
47
+ - !ruby/object:Gem::Dependency
48
+ name: logstash-devutils
49
+ version_requirements: !ruby/object:Gem::Requirement
50
+ requirements:
51
+ - - '>='
52
+ - !ruby/object:Gem::Version
53
+ version: '0'
54
+ requirement: !ruby/object:Gem::Requirement
55
+ requirements:
56
+ - - '>='
57
+ - !ruby/object:Gem::Version
58
+ version: '0'
59
+ prerelease: false
60
+ type: :development
61
+ 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
62
+ email: rmckeown@us.ibm.com
63
+ executables: []
64
+ extensions: []
65
+ extra_rdoc_files: []
66
+ files:
67
+ - Gemfile
68
+ - README.md
69
+ - Rakefile
70
+ - lib/logstash/outputs/scacsv.rb
71
+ - logstash-output-scacsv.gemspec
72
+ - spec/outputs/scacsv_spec.rb
73
+ homepage: https://github.com/IBM-ITOAdev/logstash-output-scacsv/blob/master/README.md
74
+ licenses:
75
+ - Apache License (2.0)
76
+ metadata:
77
+ logstash_plugin: 'true'
78
+ logstash_group: output
79
+ post_install_message:
80
+ rdoc_options: []
81
+ require_paths:
82
+ - lib
83
+ required_ruby_version: !ruby/object:Gem::Requirement
84
+ requirements:
85
+ - - '>='
86
+ - !ruby/object:Gem::Version
87
+ version: '0'
88
+ required_rubygems_version: !ruby/object:Gem::Requirement
89
+ requirements:
90
+ - - '>='
91
+ - !ruby/object:Gem::Version
92
+ version: '0'
93
+ requirements: []
94
+ rubyforge_project:
95
+ rubygems_version: 2.1.9
96
+ signing_key:
97
+ specification_version: 4
98
+ summary: Receives a stream of events and outputs files meeting the csv reqmts for IBM SmartCloudAnalytics Predictive Insights
99
+ test_files:
100
+ - spec/outputs/scacsv_spec.rb