fluent-plugin-splunkapi 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/.gitignore ADDED
@@ -0,0 +1,17 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in fluent-plugin-splunk.gemspec
4
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,18 @@
1
+ Copyright (C) 2013 Keisuke Nishida
2
+
3
+ Licensed to the Apache Software Foundation (ASF) under one
4
+ or more contributor license agreements. See the NOTICE file
5
+ distributed with this work for additional information
6
+ regarding copyright ownership. The ASF licenses this file
7
+ to you under the Apache License, Version 2.0 (the
8
+ "License"); you may not use this file except in compliance
9
+ with the License. You may obtain a copy of the License at
10
+
11
+ http://www.apache.org/licenses/LICENSE-2.0
12
+
13
+ Unless required by applicable law or agreed to in writing,
14
+ software distributed under the License is distributed on an
15
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16
+ KIND, either express or implied. See the License for the
17
+ specific language governing permissions and limitations
18
+ under the License.
data/README.md ADDED
@@ -0,0 +1,192 @@
1
+ # Fluent::Plugin::SplunkAPI
2
+
3
+ Splunk output plugin for Fluent event collector.
4
+
5
+ This plugin makes use of the following APIs:
6
+
7
+ Splunk REST API:
8
+
9
+ http://docs.splunk.com/Documentation/Splunk/latest/RESTAPI/RESTinput
10
+
11
+ Splunk Storm API:
12
+
13
+ http://docs.splunk.com/Documentation/Storm/latest/User/UseStormsRESTAPI
14
+
15
+ ## Installation
16
+
17
+ Add this line to your application's Gemfile:
18
+
19
+ gem 'fluent-plugin-splunkapi'
20
+
21
+ And then execute:
22
+
23
+ $ bundle
24
+
25
+ Or install it yourself as:
26
+
27
+ $ gem install fluent-plugin-splunkapi
28
+
29
+ ## Configuration
30
+
31
+ Put the following lines to your fluent.conf:
32
+
33
+ <match **>
34
+ type splunkapi
35
+
36
+ #
37
+ # Splnk Server
38
+ #
39
+
40
+ # protocol: API protocol version
41
+ # values: rest, storm
42
+ # default: rest
43
+ protocol rest
44
+
45
+ # server: Splunk server host and port
46
+ # default: localhost:8089
47
+ server localhost:8089
48
+
49
+ # verify: SSL server verification
50
+ # default: true
51
+ #verify false
52
+
53
+ # auth: username and password
54
+ auth admin:pass
55
+
56
+ #
57
+ # Splnk Storm
58
+ #
59
+
60
+ # protocol: API protocol version.
61
+ # values: rest, storm
62
+ # default: rest
63
+ #protocol storm
64
+
65
+ # access_token: for Splunk Storm
66
+ #access_token YOUR-ACCESS-TOKEN
67
+
68
+ # access_token: for Splunk Storm
69
+ #project_id YOUR-PROJECT-ID
70
+
71
+ #
72
+ # Event Parameters
73
+ #
74
+
75
+ # host: 'host' parameter passed to Splunk
76
+ host YOUR-HOSTNAME
77
+
78
+ # host: 'source' parameter passed to Splunk
79
+ # default: {TAG}
80
+ #
81
+ # "{TAG}" will be replaced by fluent tags at runtime
82
+ source {TAG}
83
+
84
+ # sourcetype: 'sourcetype' parameter passed to Splunk
85
+ # default: fluent
86
+ sourcetype fluent
87
+
88
+ #
89
+ # Formatting Parameters
90
+ #
91
+
92
+ # time_format: the time format of each event
93
+ # value: none, unixtime, or any time format string
94
+ # default: %Y-%M-%d %H:%M:%S
95
+ #time_format %Y-%M-%d %H:%M:%S
96
+
97
+ # format: the text format of each event
98
+ # value: json, kvp, or text
99
+ # default: json
100
+ #
101
+ # input = {"x":1, "y":"xyz", "message":"Hello, world!"}
102
+ #
103
+ # 'json' is JSON encoding:
104
+ # {"x":1,"y":"xyz","message":"Hello, world!"}
105
+ #
106
+ # 'kvp' is "key=value" pairs, which is automatically detected as fields by Splunk:
107
+ # x="1" y="xyz" message="Hello, world!"
108
+ #
109
+ # 'text' outputs the value of "message" as is, with "key=value" pairs for others:
110
+ # [x="1" y="xyz"] Hello, world!
111
+ format json
112
+
113
+ #
114
+ # Buffering Parameters
115
+ #
116
+
117
+ # Standard parameters for buffering. See documentation for details:
118
+ # http://docs.fluentd.org/articles/buffer-plugin-overview
119
+ buffer_type memory
120
+ buffer_queue_limit 16
121
+
122
+ # buffer_chunk_limit: The maxium size of POST data in a single API call.
123
+ #
124
+ # This value should be reasonablly small since the current implementation
125
+ # of out_splunkapi converts a chunk to POST data on memory before API calls.
126
+ # The default value should be good enough.
127
+ buffer_chunk_limit 8m
128
+
129
+ # flush_interval: The interval of API requests.
130
+ #
131
+ # Make sure that this value is large enough to make successive API calls.
132
+ # Note that a different source produces a different API POST, each of which
133
+ # costs two or more seconds. If you include "{TAG}" in the source parameter and
134
+ # this 'match' section recieves many tags, a single flush may take long time.
135
+ # (Run fluentd with -v to see verbose logs.)
136
+ flush_interval 60s
137
+ </match>
138
+
139
+ ## Example
140
+
141
+ # Input from applications
142
+ <source>
143
+ type forward
144
+ </source>
145
+
146
+ # Input from log files
147
+ <source>
148
+ type tail
149
+ path /var/log/apache2/ssl_access.log
150
+ tag ssl_access.log
151
+ format /(?<message>.*)/
152
+ pos_file /var/log/td-agent/ssl_access.log.pos
153
+ </source>
154
+
155
+ # fluent logs in text format
156
+ <match fluent.*>
157
+ type splunkapi
158
+ protocol rest
159
+ server splunk.example.com:8089
160
+ auth admin:pass
161
+ sourcetype fluentd
162
+ format text
163
+ </match>
164
+
165
+ # log files in text format without timestamp
166
+ <match *.log>
167
+ type splunkapi
168
+ protocol rest
169
+ server splunk.example.com:8089
170
+ auth admin:pass
171
+ sourcetype log
172
+ time_format none
173
+ format text
174
+ </match>
175
+
176
+ # application logs in kvp format
177
+ <match app.**>
178
+ type splunkapi
179
+ protocol rest
180
+ server splunk.example.com:8089
181
+ auth admin:pass
182
+ sourcetype app
183
+ format kvp
184
+ </match>
185
+
186
+ ## Contributing
187
+
188
+ 1. Fork it
189
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
190
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
191
+ 4. Push to the branch (`git push origin my-new-feature`)
192
+ 5. Create new Pull Request
data/Rakefile ADDED
@@ -0,0 +1,10 @@
1
+ require "bundler/gem_tasks"
2
+
3
+ require 'rake/testtask'
4
+ Rake::TestTask.new(:test) do |test|
5
+ test.libs << 'lib' << 'test'
6
+ test.pattern = 'test/**/test_*.rb'
7
+ test.verbose = true
8
+ end
9
+
10
+ task :default => :test
@@ -0,0 +1,23 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+
4
+ Gem::Specification.new do |gem|
5
+ gem.name = "fluent-plugin-splunkapi"
6
+ gem.version = "0.1.0"
7
+ gem.authors = ["Keisuke Nishida"]
8
+ gem.email = ["knishida@bizmobile.co.jp"]
9
+ gem.description = %q{Splunk output plugin for Fluent event collector}
10
+ gem.summary = %q{Splunk output plugin for Fluent event collector}
11
+ gem.homepage = ""
12
+
13
+ gem.files = `git ls-files`.split($/)
14
+ gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
15
+ gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
16
+ gem.require_paths = ["lib"]
17
+
18
+ gem.rubyforge_project = "fluent-plugin-splunkapi"
19
+ gem.add_development_dependency "fluentd"
20
+ gem.add_development_dependency "net-http-persistent"
21
+ gem.add_runtime_dependency "fluentd"
22
+ gem.add_runtime_dependency "net-http-persistent"
23
+ end
@@ -0,0 +1,162 @@
1
+ =begin
2
+
3
+ Copyright (C) 2013 Keisuke Nishida
4
+
5
+ Licensed to the Apache Software Foundation (ASF) under one
6
+ or more contributor license agreements. See the NOTICE file
7
+ distributed with this work for additional information
8
+ regarding copyright ownership. The ASF licenses this file
9
+ to you under the Apache License, Version 2.0 (the
10
+ "License"); you may not use this file except in compliance
11
+ with the License. You may obtain a copy of the License at
12
+
13
+ http://www.apache.org/licenses/LICENSE-2.0
14
+
15
+ Unless required by applicable law or agreed to in writing,
16
+ software distributed under the License is distributed on an
17
+ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
18
+ KIND, either express or implied. See the License for the
19
+ specific language governing permissions and limitations
20
+ under the License.
21
+
22
+ =end
23
+
24
+ module Fluent
25
+
26
+ class SplunkAPIOutput < BufferedOutput
27
+ Plugin.register_output('splunkapi', self)
28
+
29
+ config_param :protocol, :string, :default => 'rest'
30
+
31
+ # for Splunk REST API
32
+ config_param :server, :string, :default => 'localhost:8089'
33
+ config_param :verify, :bool, :default => true
34
+ config_param :auth, :string, :default => nil # TODO: required with rest
35
+
36
+ # for Splunk Storm API
37
+ config_param :access_token, :string, :default => nil # TODO: required with storm
38
+ config_param :api_hostname, :string, :default => 'api.splunkstorm.com'
39
+ config_param :project_id, :string, :default => nil # TODO: required with storm
40
+
41
+ # Event parameters
42
+ config_param :host, :string, :default => nil # TODO: auto-detect
43
+ config_param :source, :string, :default => '{TAG}'
44
+ config_param :sourcetype, :string, :default => 'fluent'
45
+
46
+ # Formatting
47
+ config_param :time_format, :string, :default => "%Y-%M-%d %H:%M:%S"
48
+ config_param :format, :string, :default => 'json'
49
+
50
+ def initialize
51
+ super
52
+ require 'net/http/persistent'
53
+ require 'time'
54
+ end
55
+
56
+ def configure(conf)
57
+ super
58
+
59
+ case @source
60
+ when '{TAG}'
61
+ @source_formatter = lambda { |tag| tag }
62
+ else
63
+ @source_formatter = lambda { |tag| @source.sub('{TAG}', tag) }
64
+ end
65
+
66
+ case @time_format
67
+ when 'none'
68
+ @time_formatter = nil
69
+ when 'unixtime'
70
+ @time_formatter = lambda { |time| time.to_s }
71
+ else
72
+ @timef = TimeFormatter.new(@time_format, @localtime)
73
+ @time_formatter = lambda { |time| @timef.format(time) }
74
+ end
75
+
76
+ case @format
77
+ when 'json'
78
+ @formatter = lambda { |record|
79
+ record.to_json
80
+ }
81
+ when 'kvp'
82
+ @formatter = lambda { |record|
83
+ record_to_kvp(record)
84
+ }
85
+ when 'text'
86
+ @formatter = lambda { |record|
87
+ message = record['message']
88
+ record.delete('message')
89
+ if record.length == 0
90
+ message
91
+ else
92
+ "[#{record_to_kvp(record)}] #{message}"
93
+ end
94
+ }
95
+ end
96
+
97
+ if @protocol == 'rest'
98
+ @username, @password = @auth.split(':')
99
+ @base_url = "https://#{@server}/services/receivers/simple?sourcetype=#{@sourcetype}"
100
+ @base_url += "&host=#{@host}" if @host
101
+ elsif @protocol == 'storm'
102
+ @username, @password = 'x', @access_token
103
+ @base_url = "https://#{@api_hostname}/1/inputs/http?index=#{@project_id}&sourcetype=#{@sourcetype}"
104
+ @base_url += "&host=#{@host}" if @host
105
+ end
106
+ end
107
+
108
+ def record_to_kvp(record)
109
+ record.map {|k,v| v == nil ? "#{k}=" : "#{k}=\"#{v}\""}.join(' ')
110
+ end
111
+
112
+ def start
113
+ super
114
+ @http = Net::HTTP::Persistent.new 'fluentd-plugin-splunkapi'
115
+ @http.verify_mode = OpenSSL::SSL::VERIFY_NONE unless @verify
116
+ @http.headers['Content-Type'] = 'text/plain'
117
+ $log.debug "initialized for #{@base_url}"
118
+ end
119
+
120
+ def shutdown
121
+ # NOTE: call super before @http.shutdown because super may flush final output
122
+ super
123
+
124
+ @http.shutdown
125
+ $log.debug "shutdown from #{@base_url}"
126
+ end
127
+
128
+ def format(tag, time, record)
129
+ if @time_formatter
130
+ time_str = "#{@time_formatter.call(time)}: "
131
+ else
132
+ time_str = ''
133
+ end
134
+
135
+ record.delete('time')
136
+ event = time_str + @formatter.call(record) + "\n"
137
+
138
+ [tag, event].to_msgpack
139
+ end
140
+
141
+ def chunk_to_buffers(chunk)
142
+ buffers = {}
143
+ chunk.msgpack_each do |tag, event|
144
+ (buffers[@source_formatter.call(tag)] ||= []) << event
145
+ end
146
+ return buffers
147
+ end
148
+
149
+ def write(chunk)
150
+ chunk_to_buffers(chunk).each do |source, messages|
151
+ uri = URI @base_url + "&source=#{source}"
152
+ post = Net::HTTP::Post.new uri.request_uri
153
+ post.basic_auth @username, @password
154
+ post.body = messages.join('')
155
+ $log.debug "POST #{uri}"
156
+ response = @http.request uri, post
157
+ $log.error response.message if response.code != "200"
158
+ end
159
+ end
160
+ end
161
+
162
+ end
data/test/helper.rb ADDED
@@ -0,0 +1,28 @@
1
+ require 'rubygems'
2
+ require 'bundler'
3
+ begin
4
+ Bundler.setup(:default, :development)
5
+ rescue Bundler::BundlerError => e
6
+ $stderr.puts e.message
7
+ $stderr.puts "Run `bundle install` to install missing gems"
8
+ exit e.status_code
9
+ end
10
+ require 'test/unit'
11
+
12
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
13
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
14
+ require 'fluent/test'
15
+ unless ENV.has_key?('VERBOSE')
16
+ nulllogger = Object.new
17
+ nulllogger.instance_eval {|obj|
18
+ def method_missing(method, *args)
19
+ # pass
20
+ end
21
+ }
22
+ $log = nulllogger
23
+ end
24
+
25
+ require 'fluent/plugin/out_splunkapi'
26
+
27
+ class Test::Unit::TestCase
28
+ end
@@ -0,0 +1,36 @@
1
+ require 'helper'
2
+
3
+ class SplunkAPIOutputTest < Test::Unit::TestCase
4
+ def setup
5
+ Fluent::Test.setup
6
+ end
7
+
8
+ CONFIG = %[
9
+ protocol rest
10
+ server localhost:8089
11
+ verify false
12
+ auth admin:changeme
13
+ ]
14
+
15
+ def create_driver(conf=CONFIG, tag='test')
16
+ Fluent::Test::BufferedOutputTestDriver.new(Fluent::SplunkAPIOutput, tag).configure(conf)
17
+ end
18
+
19
+ def test_configure
20
+ # default
21
+ d = create_driver
22
+ assert_equal 'rest', d.instance.protocol
23
+ assert_equal '{TAG}', d.instance.source
24
+ assert_equal 'fluent', d.instance.sourcetype
25
+ end
26
+
27
+ def test_write
28
+ d = create_driver
29
+
30
+ time = Time.parse("2010-01-02 13:14:15 UTC").to_i
31
+ d.emit({"a"=>1}, time)
32
+ d.emit({"a"=>2}, time)
33
+
34
+ d.run
35
+ end
36
+ end
metadata ADDED
@@ -0,0 +1,120 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: fluent-plugin-splunkapi
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Keisuke Nishida
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2013-01-24 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: fluentd
16
+ requirement: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: '0'
22
+ type: :development
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ! '>='
28
+ - !ruby/object:Gem::Version
29
+ version: '0'
30
+ - !ruby/object:Gem::Dependency
31
+ name: net-http-persistent
32
+ requirement: !ruby/object:Gem::Requirement
33
+ none: false
34
+ requirements:
35
+ - - ! '>='
36
+ - !ruby/object:Gem::Version
37
+ version: '0'
38
+ type: :development
39
+ prerelease: false
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ! '>='
44
+ - !ruby/object:Gem::Version
45
+ version: '0'
46
+ - !ruby/object:Gem::Dependency
47
+ name: fluentd
48
+ requirement: !ruby/object:Gem::Requirement
49
+ none: false
50
+ requirements:
51
+ - - ! '>='
52
+ - !ruby/object:Gem::Version
53
+ version: '0'
54
+ type: :runtime
55
+ prerelease: false
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ none: false
58
+ requirements:
59
+ - - ! '>='
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ - !ruby/object:Gem::Dependency
63
+ name: net-http-persistent
64
+ requirement: !ruby/object:Gem::Requirement
65
+ none: false
66
+ requirements:
67
+ - - ! '>='
68
+ - !ruby/object:Gem::Version
69
+ version: '0'
70
+ type: :runtime
71
+ prerelease: false
72
+ version_requirements: !ruby/object:Gem::Requirement
73
+ none: false
74
+ requirements:
75
+ - - ! '>='
76
+ - !ruby/object:Gem::Version
77
+ version: '0'
78
+ description: Splunk output plugin for Fluent event collector
79
+ email:
80
+ - knishida@bizmobile.co.jp
81
+ executables: []
82
+ extensions: []
83
+ extra_rdoc_files: []
84
+ files:
85
+ - .gitignore
86
+ - Gemfile
87
+ - LICENSE.txt
88
+ - README.md
89
+ - Rakefile
90
+ - fluent-plugin-splunkapi.gemspec
91
+ - lib/fluent/plugin/out_splunkapi.rb
92
+ - test/helper.rb
93
+ - test/plugin/test_out_splunkapi.rb
94
+ homepage: ''
95
+ licenses: []
96
+ post_install_message:
97
+ rdoc_options: []
98
+ require_paths:
99
+ - lib
100
+ required_ruby_version: !ruby/object:Gem::Requirement
101
+ none: false
102
+ requirements:
103
+ - - ! '>='
104
+ - !ruby/object:Gem::Version
105
+ version: '0'
106
+ required_rubygems_version: !ruby/object:Gem::Requirement
107
+ none: false
108
+ requirements:
109
+ - - ! '>='
110
+ - !ruby/object:Gem::Version
111
+ version: '0'
112
+ requirements: []
113
+ rubyforge_project: fluent-plugin-splunkapi
114
+ rubygems_version: 1.8.23
115
+ signing_key:
116
+ specification_version: 3
117
+ summary: Splunk output plugin for Fluent event collector
118
+ test_files:
119
+ - test/helper.rb
120
+ - test/plugin/test_out_splunkapi.rb