fluent-plugin-splunk-ex 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ rvm:
2
+ - 1.9.3
3
+ - 2.0.0
4
+ - 2.1.*
5
+ gemfile:
6
+ - Gemfile
7
+
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source "http://rubygems.org"
2
+
3
+ gemspec
4
+
@@ -0,0 +1,56 @@
1
+ [![Build Status](https://travis-ci.org/gtrevg/fluent-plugin-splunk-ex.svg?branch=master)](https://travis-ci.org/gtrevg/fluent-plugin-splunk-ex)
2
+
3
+ ## Overview
4
+
5
+ This plugin will send your fluentd logs to a splunk server. It can send the data in either
6
+ key/value (k1=v1 k2=v2) or json format for easy splunk parsing.
7
+
8
+
9
+ ## Installation
10
+
11
+ gem install fluent-plugin-splunk-ex
12
+
13
+ ## Configuration
14
+
15
+ ### Plugin
16
+
17
+ <match pattern>
18
+ type splunk_ex
19
+ host <splunk_host> # default: localhost
20
+ port <splunk_port> # default: 9997
21
+ output_format json|kv # default: json
22
+ </match>
23
+
24
+ ### Splunk
25
+
26
+ You may need to open up a special TCP port just for the fluentd logs. To do that, go to
27
+ `Manager -> Data Inputs -> TCP -> New`. Then decide the following:
28
+
29
+ * Port
30
+ * Source Name
31
+ * Source Type
32
+ * Index ( default works well )
33
+
34
+ After enabling these settings, you'll be able to see your fluentd logs appear in your Splunk search interface.
35
+ The JSON format will automagically be parsed and indexed based on the keys passed in.
36
+
37
+ Because the plugin batch send data to Splunk, you'll want to update your `apps/search/local/props.conf`
38
+ file to specify that Splunk should split on newlines. If you do not update this setting, you find that
39
+ all logs from a similar time slice will be stacked upon each other. Because the kv & json formats do
40
+ not contain any newline characters, splitting on the newline will solve this problem. The values to
41
+ add to this file are:
42
+
43
+ [<source_type_here>]
44
+ SHOULD_LINEMERGE = false
45
+
46
+ This will make sure that the new source type you just set up for fluentd will always split on the newline character.
47
+
48
+ ## Copyright
49
+
50
+ Copyright (c) 2014 Trevor Gattis
51
+
52
+ ## License
53
+
54
+ Apache License, Version 2.0
55
+
56
+
@@ -0,0 +1,16 @@
1
+ # encoding: utf-8
2
+ require "bundler/gem_tasks"
3
+
4
+ require 'rspec/core'
5
+ require 'rspec/core/rake_task'
6
+ RSpec::Core::RakeTask.new(:spec) do |spec|
7
+ spec.pattern = FileList['spec/**/*_spec.rb']
8
+ end
9
+ task :default => :spec
10
+
11
+ desc 'Open an irb session preloaded with the gem library'
12
+ task :console do
13
+ sh 'irb -rubygems -I lib'
14
+ end
15
+ task :c => :console
16
+
@@ -0,0 +1,28 @@
1
+ # encoding: utf-8
2
+
3
+ Gem::Specification.new do |gem|
4
+ gem.name = "fluent-plugin-splunk-ex"
5
+ gem.version = "1.0.0"
6
+
7
+ gem.authors = ["Trevor Gattis"]
8
+ gem.email = "github@trevorgattis.com"
9
+ gem.description = "Splunk output plugin for Fluent event collector"
10
+ gem.homepage = "https://github.com/ggatti000/fluent-plugin-splunk-ex"
11
+ gem.summary = gem.description
12
+ gem.license = "APLv2"
13
+ gem.has_rdoc = false
14
+
15
+ gem.files = `git ls-files`.split("\n")
16
+ gem.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
17
+ gem.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
18
+ gem.require_paths = ['lib']
19
+
20
+ gem.add_dependency "fluentd", "~> 0.10.17"
21
+ gem.add_runtime_dependency "json"
22
+
23
+ gem.add_development_dependency "rake"
24
+ gem.add_development_dependency "rspec"
25
+ gem.add_development_dependency "pry"
26
+ gem.add_development_dependency "pry-nav"
27
+ end
28
+
@@ -0,0 +1,124 @@
1
+ require 'open-uri'
2
+ require 'json'
3
+
4
+ class Fluent::SplunkExOutput < Fluent::Output
5
+
6
+ SOCKET_TRY_MAX = 3
7
+
8
+ Fluent::Plugin.register_output('splunk_ex', self)
9
+
10
+ include Fluent::SetTagKeyMixin
11
+ config_set_default :include_tag_key, false
12
+
13
+ include Fluent::SetTimeKeyMixin
14
+ config_set_default :include_time_key, false
15
+
16
+ config_param :host, :string, :default => 'localhost'
17
+ config_param :port, :string, :default => 9997
18
+ config_param :output_format, :string, :default => 'json'
19
+
20
+ config_param :test_mode, :bool, :default => false
21
+
22
+ # To support log_level option implemented by Fluentd v0.10.43
23
+ unless method_defined?(:log)
24
+ define_method(:log) { $log }
25
+ end
26
+
27
+
28
+ def configure(conf)
29
+ super
30
+ end
31
+
32
+ def start
33
+ super
34
+
35
+ if @output_format == 'kv'
36
+ @format_fn = self.class.method(:format_kv)
37
+ else
38
+ @format_fn = self.class.method(:format_json)
39
+ end
40
+
41
+ if @test_mode
42
+ @send_data = proc { |text| log.trace("test mode text: #{text}") }
43
+ else
44
+ begin
45
+ @splunk_connection = TCPSocket.open(@host, @port)
46
+ rescue Errno::ECONNREFUSED
47
+ # we'll try again when data is sent
48
+ end
49
+ @send_data = self.method(:splunk_send)
50
+ end
51
+
52
+ end
53
+
54
+
55
+ def shutdown
56
+ super
57
+ if !@test_mode && @splunk_connection.respond_to?(:close)
58
+ @splunk_connection.close
59
+ end
60
+ end
61
+
62
+
63
+ def emit(tag, es, chain)
64
+ chain.next
65
+ es.each {|time,record|
66
+ @send_data.call( @format_fn.call(record) )
67
+ }
68
+ end
69
+
70
+ # =================================================================
71
+
72
+ protected
73
+
74
+ def self.format_kv(record)
75
+ kv_out_str = ''
76
+ record.each { |k, v|
77
+ kv_out_str << sprintf('%s=%s ', URI::encode(k), URI::encode(v.to_s))
78
+ }
79
+ kv_out_str
80
+ end
81
+
82
+ def self.format_json(record)
83
+ json_str = record.to_json
84
+ end
85
+
86
+ def splunk_send(text, try_count=0)
87
+ log.debug("splunk_send: #{text}")
88
+
89
+ successful_send = false
90
+ try_count = 0
91
+
92
+ while (!successful_send && try_count < SOCKET_TRY_MAX)
93
+ begin
94
+ @splunk_connection.puts(text)
95
+ successful_send = true
96
+
97
+ rescue NoMethodError, Errno::ECONNRESET, Errno::ECONNREFUSED, Errno::EPIPE => se
98
+ log.error("splunk_send - socket send retry (#{try_count}) failed: #{se}")
99
+ try_count = try_count + 1
100
+
101
+ successful_reopen = false
102
+ while (!successful_reopen && try_count < SOCKET_TRY_MAX)
103
+ begin
104
+ # Try reopening
105
+ @splunk_connection = TCPSocket.open(@host, @port)
106
+ successful_reopen = true
107
+ rescue Errno::ECONNRESET, Errno::ECONNREFUSED, Errno::EPIPE => se
108
+ log.error("splunk_send - socket open retry (#{try_count}) failed: #{se}")
109
+ try_count = try_count + 1
110
+ end
111
+ end
112
+ end
113
+ end
114
+
115
+ if !successful_send
116
+ log.fatal("splunk_send - retry of sending data failed after #{SOCKET_TRY_MAX} chances.")
117
+ log.warn(text)
118
+ end
119
+
120
+ end
121
+
122
+
123
+ end
124
+
@@ -0,0 +1,74 @@
1
+ # encoding: UTF-8
2
+ require_relative 'spec_helper'
3
+ require 'benchmark'
4
+ Fluent::Test.setup
5
+
6
+ def create_driver(config, tag = 'foo.bar')
7
+ Fluent::Test::OutputTestDriver.new(Fluent::SplunkExOutput, tag).configure(config)
8
+ end
9
+
10
+ # setup
11
+ single_key_message = {
12
+ 'msg' => 'testing some data'
13
+ }
14
+
15
+ multi_key_message = {
16
+ 'msg' => 'testing some data',
17
+ 'chars' => 'let"s put !@#$%^&*()-= some weird :\'?><,./ characters',
18
+ 'dt' => '2014/04/03T07:02:11.124202',
19
+ 'debug_line' => 24,
20
+ 'debug_file' => '/some/path/to/myFile.py',
21
+ 'statsd_key' => 'fluent_plugin_splunk_ex',
22
+ 'statsd_timing' => 0.234,
23
+ 'statsd_type' => 'timing',
24
+ 'tx' => '280c3e80-bb6c-11e3-a5e2-0800200c9a66',
25
+ 'host' => 'my01.cool.server.com'
26
+ }
27
+
28
+ time = Time.now.to_i
29
+
30
+ driver_kv = create_driver(%[
31
+ log_level fatal
32
+ test_mode true
33
+ output_format kv
34
+ ])
35
+
36
+ driver_json = create_driver(%[
37
+ log_level fatal
38
+ test_mode true
39
+ output_format json
40
+ ])
41
+
42
+ driver_kv_time = create_driver(%[
43
+ log_level fatal
44
+ test_mode true
45
+ output_format kv
46
+ time_key myKey
47
+ include_time_key true
48
+ ])
49
+
50
+ driver_json_time = create_driver(%[
51
+ log_level fatal
52
+ test_mode true
53
+ output_format json
54
+ time_key myKey
55
+ include_time_key true
56
+ ])
57
+
58
+
59
+
60
+ # bench
61
+ n = 10000
62
+ Benchmark.bm(7) do |x|
63
+ x.report("single_kv ") { driver_kv.run { n.times { driver_kv.emit( single_key_message, time) } } }
64
+ x.report("single_kv_time ") { driver_kv_time.run { n.times { driver_kv_time.emit( single_key_message, time) } } }
65
+ x.report("single_json ") { driver_json.run { n.times { driver_json.emit( single_key_message, time) } } }
66
+ x.report("single_json_time") { driver_json_time.run { n.times { driver_json_time.emit(single_key_message, time) } } }
67
+
68
+ x.report("multi_kv ") { driver_kv.run { n.times { driver_kv.emit( multi_key_message, time ) } } }
69
+ x.report("multi_kv_time ") { driver_kv_time.run { n.times { driver_kv_time.emit( multi_key_message, time ) } } }
70
+ x.report("multi_json ") { driver_json.run { n.times { driver_json.emit( multi_key_message, time ) } } }
71
+ x.report("multi_json_time ") { driver_json_time.run { n.times { driver_json_time.emit(multi_key_message, time ) } } }
72
+
73
+ end
74
+
@@ -0,0 +1,22 @@
1
+ # This file was generated by the `rspec --init` command. Conventionally, all
2
+ # specs live under a `spec` directory, which RSpec adds to the `$LOAD_PATH`.
3
+ # Require this file using `require "spec_helper"` to ensure that it is only
4
+ # loaded once.
5
+ #
6
+ # See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration
7
+ RSpec.configure do |config|
8
+ config.treat_symbols_as_metadata_keys_with_true_values = true
9
+ config.run_all_when_everything_filtered = true
10
+ config.filter_run :focus
11
+
12
+ # Run specs in random order to surface order dependencies. If you find an
13
+ # order dependency and want to debug it, you can fix the order by providing
14
+ # the seed, which is printed after each run.
15
+ # --seed 1234
16
+ config.order = 'random'
17
+ end
18
+
19
+ require 'fluent/test'
20
+
21
+ require 'fluent/plugin/out_splunk_ex'
22
+
metadata ADDED
@@ -0,0 +1,151 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: fluent-plugin-splunk-ex
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Trevor Gattis
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2014-04-14 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.10.17
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ~>
28
+ - !ruby/object:Gem::Version
29
+ version: 0.10.17
30
+ - !ruby/object:Gem::Dependency
31
+ name: json
32
+ requirement: !ruby/object:Gem::Requirement
33
+ none: false
34
+ requirements:
35
+ - - ! '>='
36
+ - !ruby/object:Gem::Version
37
+ version: '0'
38
+ type: :runtime
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: rake
48
+ requirement: !ruby/object:Gem::Requirement
49
+ none: false
50
+ requirements:
51
+ - - ! '>='
52
+ - !ruby/object:Gem::Version
53
+ version: '0'
54
+ type: :development
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: rspec
64
+ requirement: !ruby/object:Gem::Requirement
65
+ none: false
66
+ requirements:
67
+ - - ! '>='
68
+ - !ruby/object:Gem::Version
69
+ version: '0'
70
+ type: :development
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
+ - !ruby/object:Gem::Dependency
79
+ name: pry
80
+ requirement: !ruby/object:Gem::Requirement
81
+ none: false
82
+ requirements:
83
+ - - ! '>='
84
+ - !ruby/object:Gem::Version
85
+ version: '0'
86
+ type: :development
87
+ prerelease: false
88
+ version_requirements: !ruby/object:Gem::Requirement
89
+ none: false
90
+ requirements:
91
+ - - ! '>='
92
+ - !ruby/object:Gem::Version
93
+ version: '0'
94
+ - !ruby/object:Gem::Dependency
95
+ name: pry-nav
96
+ requirement: !ruby/object:Gem::Requirement
97
+ none: false
98
+ requirements:
99
+ - - ! '>='
100
+ - !ruby/object:Gem::Version
101
+ version: '0'
102
+ type: :development
103
+ prerelease: false
104
+ version_requirements: !ruby/object:Gem::Requirement
105
+ none: false
106
+ requirements:
107
+ - - ! '>='
108
+ - !ruby/object:Gem::Version
109
+ version: '0'
110
+ description: Splunk output plugin for Fluent event collector
111
+ email: github@trevorgattis.com
112
+ executables: []
113
+ extensions: []
114
+ extra_rdoc_files: []
115
+ files:
116
+ - .travis.yml
117
+ - Gemfile
118
+ - README.md
119
+ - Rakefile
120
+ - fluent-plugin-splunk-ex.gemspec
121
+ - lib/fluent/plugin/out_splunk_ex.rb
122
+ - spec/out_splunk_ex_spec.rb
123
+ - spec/spec_helper.rb
124
+ homepage: https://github.com/ggatti000/fluent-plugin-splunk-ex
125
+ licenses:
126
+ - APLv2
127
+ post_install_message:
128
+ rdoc_options: []
129
+ require_paths:
130
+ - lib
131
+ required_ruby_version: !ruby/object:Gem::Requirement
132
+ none: false
133
+ requirements:
134
+ - - ! '>='
135
+ - !ruby/object:Gem::Version
136
+ version: '0'
137
+ required_rubygems_version: !ruby/object:Gem::Requirement
138
+ none: false
139
+ requirements:
140
+ - - ! '>='
141
+ - !ruby/object:Gem::Version
142
+ version: '0'
143
+ requirements: []
144
+ rubyforge_project:
145
+ rubygems_version: 1.8.23
146
+ signing_key:
147
+ specification_version: 3
148
+ summary: Splunk output plugin for Fluent event collector
149
+ test_files:
150
+ - spec/out_splunk_ex_spec.rb
151
+ - spec/spec_helper.rb