logstash-output-jdbc 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.
- checksums.yaml +7 -0
- data/.gitignore +5 -0
- data/Gemfile +2 -0
- data/LICENSE.txt +21 -0
- data/README.md +97 -0
- data/Rakefile +1 -0
- data/lib/logstash/outputs/jdbc.rb +165 -0
- data/logstash-output-jdbc.gemspec +24 -0
- metadata +102 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 033e6d7684e87ec8494733ad218b09ad81145442
|
4
|
+
data.tar.gz: 4b2502b6210e89621a9416a32666d5e511082fe1
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 4584a4a7129bf1dcbcb30a2d7bb3b628aef7bacd7a9d8607768ffcb2f77f63ee3ee915ed72b80f378fd55efaa6d7dd48a7722003816790f64b85f61ac18985dc
|
7
|
+
data.tar.gz: a75c975b799472844afd9448e2f054ac8ce6d1b618ba77daae8a4ceb3acce7919c3ae912a6044594f4d608528e5a08255e163926ffa8a6f9b0f6fe3c88d389ed
|
data/.gitignore
ADDED
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright (c) 2014
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
13
|
+
copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
21
|
+
SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,97 @@
|
|
1
|
+
# logstash-jdbc
|
2
|
+
JDBC output plugin for Logstash.
|
3
|
+
This plugin is provided as an external plugin and is not part of the Logstash project.
|
4
|
+
|
5
|
+
## Warning
|
6
|
+
The master branch is for 1.5, is currently incomplete and should NOT be used (yet).
|
7
|
+
|
8
|
+
Please see the v1.4 branch for v1.4 of Logstash.
|
9
|
+
|
10
|
+
This has not yet been extensively tested with all JDBC drivers and may not yet work for you.
|
11
|
+
|
12
|
+
## Installation
|
13
|
+
- Copy lib directory contents into your logstash installation.
|
14
|
+
- Create the directory vendor/jar/jdbc in your logstash installation (`mkdir -p vendor/jar/jdbc/`)
|
15
|
+
- Add JDBC jar files to vendor/jar/jdbc in your logstash installation
|
16
|
+
- Configure
|
17
|
+
|
18
|
+
## Configuration options
|
19
|
+
* driver_class, string, JDBC driver class to load
|
20
|
+
* connection_string, string, JDBC connection string
|
21
|
+
* statement, array, an array of strings representing the SQL statement to run. Index 0 is the SQL statement that is prepared, all other array entries are passed in as parameters (in order). A parameter may either be a property of the event (i.e. "@timestamp", or "host") or a formatted string (i.e. "%{host} - %{message}" or "%{message}"). If a key is passed then it will be automatically converted as required for insertion into SQL. If it's a formatted string then it will be passed in verbatim.
|
22
|
+
* flush_size, number, default = 1000, number of entries to buffer before sending to SQL
|
23
|
+
* idle_flush_time, number, default = 1, number of idle seconds before sending data to SQL, even if the flush_size has not been reached. If you modify this value you should also consider altering max_repeat_exceptions_time
|
24
|
+
* max_repeat_exceptions, number, default = 5, number of times the same exception can repeat before we stop logstash. Set to a value less than 1 if you never want it to stop
|
25
|
+
* max_repeat_exceptions_time, number, default = 30, maxium number of seconds between exceptions before they're considered "different" exceptions. If you modify idle_flush_time you should consider this value
|
26
|
+
|
27
|
+
## Example configurations
|
28
|
+
### SQLite3
|
29
|
+
* Tested using https://bitbucket.org/xerial/sqlite-jdbc
|
30
|
+
* SQLite setup - `echo "CREATE table log (host text, timestamp datetime, message text);" | sqlite3 test.db`
|
31
|
+
```
|
32
|
+
input
|
33
|
+
{
|
34
|
+
stdin { }
|
35
|
+
}
|
36
|
+
output {
|
37
|
+
stdout { }
|
38
|
+
|
39
|
+
jdbc {
|
40
|
+
driver_class => 'org.sqlite.JDBC'
|
41
|
+
connection_string => 'jdbc:sqlite:test.db'
|
42
|
+
statement => [ "INSERT INTO log (host, timestamp, message) VALUES(?, ?, ?)", "host", "@timestamp", "message" ]
|
43
|
+
}
|
44
|
+
}
|
45
|
+
```
|
46
|
+
|
47
|
+
### SQL Server
|
48
|
+
* Tested using http://msdn.microsoft.com/en-gb/sqlserver/aa937724.aspx
|
49
|
+
```
|
50
|
+
input
|
51
|
+
{
|
52
|
+
stdin { }
|
53
|
+
}
|
54
|
+
output {
|
55
|
+
jdbc {
|
56
|
+
driver_class => 'com.microsoft.sqlserver.jdbc.SQLServerDriver'
|
57
|
+
connection_string => "jdbc:sqlserver://server:1433;databaseName=databasename;user=username;password=password;autoReconnect=true;"
|
58
|
+
statement => [ "INSERT INTO log (host, timestamp, message) VALUES(?, ?, ?)", "host", "@timestamp", "message" ]
|
59
|
+
}
|
60
|
+
}
|
61
|
+
```
|
62
|
+
|
63
|
+
### Postgres
|
64
|
+
With thanks to [@roflmao](https://github.com/roflmao)
|
65
|
+
```
|
66
|
+
input
|
67
|
+
{
|
68
|
+
stdin { }
|
69
|
+
}
|
70
|
+
output {
|
71
|
+
jdbc {
|
72
|
+
driver_class => 'org.postgresql.Driver'
|
73
|
+
connection_string => 'jdbc:postgresql://hostname:5432/database?user=username&password=password'
|
74
|
+
statement => [ "INSERT INTO log (host, timestamp, message) VALUES(?, CAST (? AS timestamp), ?)", "host", "@timestamp", "message" ]
|
75
|
+
}
|
76
|
+
}
|
77
|
+
```
|
78
|
+
|
79
|
+
### Oracle
|
80
|
+
With thanks to [@josemazo](https://github.com/josemazo)
|
81
|
+
* Tested with Express Edition 11g Release 2
|
82
|
+
* Tested using http://www.oracle.com/technetwork/database/enterprise-edition/jdbc-112010-090769.html (ojdbc6.jar)
|
83
|
+
```
|
84
|
+
input
|
85
|
+
{
|
86
|
+
stdin { }
|
87
|
+
}
|
88
|
+
output {
|
89
|
+
jdbc {
|
90
|
+
driver_class => "oracle.jdbc.driver.OracleDriver"
|
91
|
+
connection_string => "jdbc:oracle:thin:USER/PASS@HOST:PORT:SID"
|
92
|
+
statement => [ "INSERT INTO log (host, timestamp, message) VALUES(?, CAST (? AS timestamp), ?)", "host", "@timestamp", "message" ]
|
93
|
+
}
|
94
|
+
}
|
95
|
+
```
|
96
|
+
|
97
|
+
/* vim: set ts=4 sw=4 tw=0 :*/
|
data/Rakefile
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require "logstash/devutils/rake"
|
@@ -0,0 +1,165 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
require "logstash/outputs/base"
|
3
|
+
require "logstash/namespace"
|
4
|
+
require "stud/buffer"
|
5
|
+
require "java"
|
6
|
+
|
7
|
+
class LogStash::Outputs::Jdbc < LogStash::Outputs::Base
|
8
|
+
# Adds buffer support
|
9
|
+
include Stud::Buffer
|
10
|
+
|
11
|
+
config_name "jdbc"
|
12
|
+
|
13
|
+
# Driver class
|
14
|
+
config :driver_class, :validate => :string
|
15
|
+
|
16
|
+
# connection string
|
17
|
+
config :connection_string, :validate => :string, :required => true
|
18
|
+
|
19
|
+
# [ "insert into table (message) values(?)", "%{message}" ]
|
20
|
+
config :statement, :validate => :array, :required => true
|
21
|
+
|
22
|
+
# We buffer a certain number of events before flushing that out to SQL.
|
23
|
+
# This setting controls how many events will be buffered before sending a
|
24
|
+
# batch of events.
|
25
|
+
config :flush_size, :validate => :number, :default => 1000
|
26
|
+
|
27
|
+
# The amount of time since last flush before a flush is forced.
|
28
|
+
#
|
29
|
+
# This setting helps ensure slow event rates don't get stuck in Logstash.
|
30
|
+
# For example, if your `flush_size` is 100, and you have received 10 events,
|
31
|
+
# and it has been more than `idle_flush_time` seconds since the last flush,
|
32
|
+
# Logstash will flush those 10 events automatically.
|
33
|
+
#
|
34
|
+
# This helps keep both fast and slow log streams moving along in
|
35
|
+
# a timely manner.
|
36
|
+
#
|
37
|
+
# If you change this value please ensure that you change
|
38
|
+
# max_repeat_exceptions_time accordingly.
|
39
|
+
config :idle_flush_time, :validate => :number, :default => 1
|
40
|
+
|
41
|
+
# Maximum number of repeating (sequential) exceptions, before we stop retrying
|
42
|
+
# If set to < 1, then it will infinitely retry.
|
43
|
+
config :max_repeat_exceptions, :validate => :number, :default => 5
|
44
|
+
|
45
|
+
# The max number of seconds since the last exception, before we consider it
|
46
|
+
# a different cause.
|
47
|
+
# This value should be carefully considered in respect to idle_flush_time.
|
48
|
+
config :max_repeat_exceptions_time, :validate => :number, :default => 30
|
49
|
+
|
50
|
+
public
|
51
|
+
def register
|
52
|
+
|
53
|
+
@logger.info("JDBC - Starting up")
|
54
|
+
|
55
|
+
if ENV['LOGSTASH_HOME']
|
56
|
+
jarpath = File.join(ENV['LOGSTASH_HOME'], "/vendor/jar/jdbc/*.jar")
|
57
|
+
else
|
58
|
+
jarpath = File.join(File.dirname(__FILE__), "../../../vendor/jar/jdbc/*.jar")
|
59
|
+
end
|
60
|
+
|
61
|
+
@logger.debug("JDBC - jarpath", path: jarpath)
|
62
|
+
|
63
|
+
jars = Dir[jarpath]
|
64
|
+
raise Exception.new("JDBC - No jars found in jarpath. Have you read the README?") if jars.empty?
|
65
|
+
|
66
|
+
jars.each do |jar|
|
67
|
+
@logger.debug("JDBC - Loaded jar", :jar => jar)
|
68
|
+
require jar
|
69
|
+
end
|
70
|
+
|
71
|
+
import @driver_class
|
72
|
+
|
73
|
+
driver = Object.const_get(@driver_class[@driver_class.rindex('.') + 1, @driver_class.length]).new
|
74
|
+
@connection = driver.connect(@connection_string, java.util.Properties.new)
|
75
|
+
|
76
|
+
@logger.debug("JDBC - Created connection", :driver => driver, :connection => @connection)
|
77
|
+
|
78
|
+
if (@flush_size > 1000)
|
79
|
+
@logger.warn("JDBC - Flush size is set to > 1000. May have performance penalties, depending on your SQL engine.")
|
80
|
+
end
|
81
|
+
|
82
|
+
@repeat_exception_count = 0
|
83
|
+
@last_exception_time = Time.now
|
84
|
+
|
85
|
+
if (@max_repeat_exceptions > 0) and ((@idle_flush_time * @max_repeat_exceptions) > @max_repeat_exceptions_time)
|
86
|
+
@logger.warn("JDBC - max_repeat_exceptions_time is set such that it may still permit a looping exception. You probably changed idle_flush_time. Considering increasing max_repeat_exceptions_time.")
|
87
|
+
end
|
88
|
+
|
89
|
+
buffer_initialize(
|
90
|
+
:max_items => @flush_size,
|
91
|
+
:max_interval => @idle_flush_time,
|
92
|
+
:logger => @logger
|
93
|
+
)
|
94
|
+
end
|
95
|
+
|
96
|
+
def receive(event)
|
97
|
+
return unless output?(event)
|
98
|
+
return unless @statement.length > 0
|
99
|
+
|
100
|
+
buffer_receive(event)
|
101
|
+
end
|
102
|
+
|
103
|
+
def flush(events, teardown=false)
|
104
|
+
statement = @connection.prepareStatement(@statement[0])
|
105
|
+
|
106
|
+
events.each do |event|
|
107
|
+
next if @statement.length < 2
|
108
|
+
|
109
|
+
@statement[1..-1].each_with_index do |i, idx|
|
110
|
+
case event[i]
|
111
|
+
when Time, LogStash::Timestamp
|
112
|
+
# Most reliable solution, cross JDBC driver
|
113
|
+
statement.setString(idx + 1, event[i].iso8601())
|
114
|
+
when Fixnum, Integer
|
115
|
+
statement.setInt(idx + 1, event[i])
|
116
|
+
when Float
|
117
|
+
statement.setFloat(idx + 1, event[i])
|
118
|
+
when String
|
119
|
+
statement.setString(idx + 1, event[i])
|
120
|
+
else
|
121
|
+
statement.setString(idx + 1, event.sprintf(i))
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
statement.addBatch()
|
126
|
+
end
|
127
|
+
|
128
|
+
begin
|
129
|
+
@logger.debug("JDBC - Sending SQL", :sql => statement.toString())
|
130
|
+
statement.executeBatch()
|
131
|
+
rescue => e
|
132
|
+
# Raising an exception will incur a retry from Stud::Buffer.
|
133
|
+
# Since the exceutebatch failed this should mean any events failed to be
|
134
|
+
# inserted will be re-run. We're going to log it for the lols anyway.
|
135
|
+
@logger.warn("JDBC - Exception. Will automatically retry", :exception => e)
|
136
|
+
end
|
137
|
+
|
138
|
+
statement.close()
|
139
|
+
end
|
140
|
+
|
141
|
+
def on_flush_error(e)
|
142
|
+
return if @max_repeat_exceptions < 1
|
143
|
+
|
144
|
+
if @last_exception == e.to_s
|
145
|
+
@repeat_exception_count += 1
|
146
|
+
else
|
147
|
+
@repeat_exception_count = 0
|
148
|
+
end
|
149
|
+
|
150
|
+
if (@repeat_exception_count >= @max_repeat_exceptions) and (Time.now - @last_exception_time) < @max_repeat_exceptions_time
|
151
|
+
@logger.error("JDBC - Exception repeated more than the maximum configured", :exception => e, :max_repeat_exceptions => @max_repeat_exceptions, :max_repeat_exceptions_time => @max_repeat_exceptions_time)
|
152
|
+
raise e
|
153
|
+
end
|
154
|
+
|
155
|
+
@last_exception_time = Time.now
|
156
|
+
@last_exception = e.to_s
|
157
|
+
end
|
158
|
+
|
159
|
+
def teardown
|
160
|
+
buffer_flush(:final => true)
|
161
|
+
@connection.close()
|
162
|
+
super
|
163
|
+
end
|
164
|
+
|
165
|
+
end # class LogStash::Outputs::jdbc
|
@@ -0,0 +1,24 @@
|
|
1
|
+
Gem::Specification.new do |s|
|
2
|
+
s.name = 'logstash-output-jdbc'
|
3
|
+
s.version = "0.1.0"
|
4
|
+
s.licenses = [ "Apache License (2.0)" ]
|
5
|
+
s.summary = "This plugin allows you to output to SQL, via JDBC"
|
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 = ["the_angry_angel"]
|
8
|
+
s.email = "karl+github@theangryangel.co.uk"
|
9
|
+
s.homepage = "https://github.com/theangryangel/logstash-output-jdbc"
|
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
|
metadata
ADDED
@@ -0,0 +1,102 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: logstash-output-jdbc
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- the_angry_angel
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2015-06-26 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: logstash-core
|
15
|
+
requirement: !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
|
+
type: :runtime
|
24
|
+
prerelease: false
|
25
|
+
version_requirements: !ruby/object:Gem::Requirement
|
26
|
+
requirements:
|
27
|
+
- - ">="
|
28
|
+
- !ruby/object:Gem::Version
|
29
|
+
version: 1.4.0
|
30
|
+
- - "<"
|
31
|
+
- !ruby/object:Gem::Version
|
32
|
+
version: 2.0.0
|
33
|
+
- !ruby/object:Gem::Dependency
|
34
|
+
name: logstash-codec-plain
|
35
|
+
requirement: !ruby/object:Gem::Requirement
|
36
|
+
requirements:
|
37
|
+
- - ">="
|
38
|
+
- !ruby/object:Gem::Version
|
39
|
+
version: '0'
|
40
|
+
type: :runtime
|
41
|
+
prerelease: false
|
42
|
+
version_requirements: !ruby/object:Gem::Requirement
|
43
|
+
requirements:
|
44
|
+
- - ">="
|
45
|
+
- !ruby/object:Gem::Version
|
46
|
+
version: '0'
|
47
|
+
- !ruby/object:Gem::Dependency
|
48
|
+
name: logstash-devutils
|
49
|
+
requirement: !ruby/object:Gem::Requirement
|
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
|
+
requirements:
|
58
|
+
- - ">="
|
59
|
+
- !ruby/object:Gem::Version
|
60
|
+
version: '0'
|
61
|
+
description: This gem is a logstash plugin required to be installed on top of the
|
62
|
+
Logstash core pipeline using $LS_HOME/bin/plugin install gemname. This gem is not
|
63
|
+
a stand-alone program
|
64
|
+
email: karl+github@theangryangel.co.uk
|
65
|
+
executables: []
|
66
|
+
extensions: []
|
67
|
+
extra_rdoc_files: []
|
68
|
+
files:
|
69
|
+
- ".gitignore"
|
70
|
+
- Gemfile
|
71
|
+
- LICENSE.txt
|
72
|
+
- README.md
|
73
|
+
- Rakefile
|
74
|
+
- lib/logstash/outputs/jdbc.rb
|
75
|
+
- logstash-output-jdbc.gemspec
|
76
|
+
homepage: https://github.com/theangryangel/logstash-output-jdbc
|
77
|
+
licenses:
|
78
|
+
- Apache License (2.0)
|
79
|
+
metadata:
|
80
|
+
logstash_plugin: 'true'
|
81
|
+
logstash_group: output
|
82
|
+
post_install_message:
|
83
|
+
rdoc_options: []
|
84
|
+
require_paths:
|
85
|
+
- lib
|
86
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
87
|
+
requirements:
|
88
|
+
- - ">="
|
89
|
+
- !ruby/object:Gem::Version
|
90
|
+
version: '0'
|
91
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
92
|
+
requirements:
|
93
|
+
- - ">="
|
94
|
+
- !ruby/object:Gem::Version
|
95
|
+
version: '0'
|
96
|
+
requirements: []
|
97
|
+
rubyforge_project:
|
98
|
+
rubygems_version: 2.4.5
|
99
|
+
signing_key:
|
100
|
+
specification_version: 4
|
101
|
+
summary: This plugin allows you to output to SQL, via JDBC
|
102
|
+
test_files: []
|