logstash-input-drupal_dblog 0.1.1-java

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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: ab1d14e9119a017b64d778d9b8f84b76898556ad
4
+ data.tar.gz: 74b57346691258168e9832c9ae3d8731986d0569
5
+ SHA512:
6
+ metadata.gz: d823b5c58ef1f8c6574d6c0fdf7ce9fef18e3d0ce533c5ee523161b0942a86c87ed9a2dfb984ec4a3d5d993fa30c03d00bfe864b84bc0999701a67b3536d97ff
7
+ data.tar.gz: c576c6d730e8f697091edbb9d9a6529cdbfb652f959f09ecd94b202534be3d55e603db8ee09ff60d8e6387dbd5d73fab129b9dccb040670f2eb8ddef73015cfe
data/.gitignore ADDED
@@ -0,0 +1,4 @@
1
+ *.gem
2
+ Gemfile.lock
3
+ .bundle
4
+ vendor
data/Gemfile ADDED
@@ -0,0 +1,3 @@
1
+ source 'https://rubygems.org'
2
+ gemspec
3
+ gem "logstash", :github => "elasticsearch/logstash", :branch => "1.5"
data/LICENSE ADDED
@@ -0,0 +1,13 @@
1
+ Copyright (c) 2012-2014 Elasticsearch <http://www.elasticsearch.org>
2
+
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
6
+
7
+ http://www.apache.org/licenses/LICENSE-2.0
8
+
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.
data/Rakefile ADDED
@@ -0,0 +1,7 @@
1
+ @files=[]
2
+
3
+ task :default do
4
+ system("rake -T")
5
+ end
6
+
7
+ require "logstash/devutils/rake"
@@ -0,0 +1,324 @@
1
+ # encoding: utf-8
2
+ require "date"
3
+ require "logstash/inputs/base"
4
+ require "logstash/namespace"
5
+
6
+
7
+ # Retrieve watchdog log events from a Drupal installation with DBLog enabled.
8
+ # The events are pulled out directly from the database.
9
+ # The original events are not deleted, and on every consecutive run only new
10
+ # events are pulled.
11
+ #
12
+ # The last watchdog event id that was processed is stored in the Drupal
13
+ # variable table with the name "logstash_last_wid". Delete this variable or
14
+ # set it to 0 if you want to re-import all events.
15
+ #
16
+ # More info on DBLog: http://drupal.org/documentation/modules/dblog
17
+ #
18
+ class LogStash::Inputs::DrupalDblog < LogStash::Inputs::Base
19
+ config_name "drupal_dblog"
20
+ milestone 1
21
+
22
+ default :codec, "plain"
23
+
24
+ # Specify all drupal databases that you whish to import from.
25
+ # This can be as many as you whish.
26
+ # The format is a hash, with a unique site name as the key, and a databse
27
+ # url as the value.
28
+ #
29
+ # Example:
30
+ # [
31
+ # "site1", "mysql://user1:password@host1.com/databasename",
32
+ # "other_site", "mysql://user2:password@otherhost.com/databasename",
33
+ # ...
34
+ # ]
35
+ config :databases, :validate => :hash
36
+
37
+ # By default, the event only contains the current user id as a field.
38
+ # If you whish to add the username as an additional field, set this to true.
39
+ config :add_usernames, :validate => :boolean, :default => false
40
+
41
+ # Time between checks in minutes.
42
+ config :interval, :validate => :number, :default => 10
43
+
44
+ # The amount of log messages that should be fetched with each query.
45
+ # Bulk fetching is done to prevent querying huge data sets when lots of
46
+ # messages are in the database.
47
+ config :bulksize, :validate => :number, :default => 5000
48
+
49
+ # Label this input with a type.
50
+ # Types are used mainly for filter activation.
51
+ #
52
+ #
53
+ # If you create an input with type "foobar", then only filters
54
+ # which also have type "foobar" will act on them.
55
+ #
56
+ # The type is also stored as part of the event itself, so you
57
+ # can also use the type to search for in the web interface.
58
+ config :type, :validate => :string, :default => 'watchdog'
59
+
60
+ public
61
+ def register
62
+ require "php_serialize"
63
+
64
+ if RUBY_PLATFORM == 'java'
65
+ require "logstash/inputs/drupal_dblog/jdbcconnection"
66
+ else
67
+ require "mysql2"
68
+ end
69
+ end # def register
70
+
71
+ public
72
+ def config_init(params)
73
+ super
74
+
75
+ dbs = {}
76
+ valid = true
77
+
78
+ @databases.each do |name, rawUri|
79
+ uri = URI(rawUri)
80
+
81
+ dbs[name] = {
82
+ "site" => name,
83
+ "scheme" => uri.scheme,
84
+ "host" => uri.host,
85
+ "user" => uri.user,
86
+ "password" => uri.password,
87
+ "database" => uri.path.sub('/', ''),
88
+ "port" => uri.port.to_i
89
+ }
90
+
91
+ if not (
92
+ uri.scheme and not uri.scheme.empty?\
93
+ and uri.host and not uri.host.empty?\
94
+ and uri.user and not uri.user.empty?\
95
+ and uri.password\
96
+ and uri.path and not uri.path.sub('/', '').empty?
97
+ )
98
+ @logger.error("Drupal DBLog: Invalid database URI for #{name} : #{rawUri}")
99
+ valid = false
100
+ end
101
+ if not uri.scheme == 'mysql'
102
+ @logger.error("Drupal DBLog: Only mysql databases are supported.")
103
+ valid = false
104
+ end
105
+ end
106
+
107
+ if not valid
108
+ @logger.error("Config validation failed.")
109
+ exit 1
110
+ end
111
+
112
+ @databases = dbs
113
+ end #def config_init
114
+
115
+ public
116
+ def run(output_queue)
117
+ @logger.info("Initializing drupal_dblog")
118
+
119
+ loop do
120
+ @logger.debug("Drupal DBLog: Starting to fetch new watchdog entries")
121
+ start = Time.now.to_i
122
+
123
+ @databases.each do |name, db|
124
+ @logger.debug("Drupal DBLog: Checking database #{name}")
125
+ check_database(output_queue, db)
126
+ @logger.info("Drupal DBLog: Retrieved all new watchdog messages from #{name}")
127
+ end
128
+
129
+ timeTaken = Time.now.to_i - start
130
+ @logger.info("Drupal DBLog: Fetched all new watchdog entries in #{timeTaken} seconds")
131
+
132
+ # If fetching of all databases took less time than the interval,
133
+ # sleep a bit.
134
+ sleepTime = @interval * 60 - timeTaken
135
+ if sleepTime > 0
136
+ @logger.debug("Drupal DBLog: Sleeping for #{sleepTime} seconds")
137
+ sleep(sleepTime)
138
+ end
139
+ end # loop
140
+ end # def run
141
+
142
+ private
143
+ def initialize_client(db)
144
+ if db["scheme"] == 'mysql'
145
+
146
+ if not db["port"] > 0
147
+ db["port"] = 3306
148
+ end
149
+
150
+ if RUBY_PLATFORM == 'java'
151
+ @client = LogStash::DrupalDblogJavaMysqlConnection.new(
152
+ db["host"],
153
+ db["user"],
154
+ db["password"],
155
+ db["database"],
156
+ db["port"]
157
+ )
158
+ else
159
+ @client = Mysql2::Client.new(
160
+ :host => db["host"],
161
+ :port => db["port"],
162
+ :username => db["user"],
163
+ :password => db["password"],
164
+ :database => db["database"]
165
+ )
166
+ end
167
+ end
168
+ end #def get_client
169
+
170
+ private
171
+ def check_database(output_queue, db)
172
+
173
+ begin
174
+ # connect to the MySQL server
175
+ initialize_client(db)
176
+ rescue Exception => e
177
+ @logger.error("Could not connect to database: " + e.message)
178
+ return
179
+ end #begin
180
+
181
+ begin
182
+ @sitename = db["site"]
183
+
184
+ @usermap = @add_usernames ? get_usermap : nil
185
+
186
+ # Retrieve last pulled watchdog entry id
187
+ initialLastWid = get_last_wid
188
+ lastWid = nil
189
+
190
+
191
+ if initialLastWid == false
192
+ lastWid = 0
193
+ set_last_wid(0, true)
194
+ else
195
+ lastWid = initialLastWid
196
+ end
197
+
198
+ # Fetch new entries, and create the event
199
+ while true
200
+ results = get_db_rows(lastWid)
201
+ if results.length() < 1
202
+ break
203
+ end
204
+
205
+ @logger.debug("Fetched " + results.length().to_s + " database rows")
206
+
207
+ results.each do |row|
208
+ event = build_event(row)
209
+ if event
210
+ decorate(event)
211
+ output_queue << event
212
+ lastWid = row['wid'].to_s
213
+ end
214
+ end
215
+
216
+ set_last_wid(lastWid, false)
217
+ end
218
+ rescue Exception => e
219
+ @logger.error("Error while fetching messages: ", :error => e.message)
220
+ end # begin
221
+
222
+ # Close connection
223
+ @client.close
224
+ end # def check_database
225
+
226
+ def get_db_rows(lastWid)
227
+ query = 'SELECT * from watchdog WHERE wid > ' + lastWid.to_s + " ORDER BY wid asc LIMIT " + @bulksize.to_s
228
+ return @client.query(query)
229
+ end # def get_db_rows
230
+
231
+ private
232
+ def update_sitename
233
+ if @sitename == ""
234
+ result = @client.query('SELECT value FROM variable WHERE name="site_name"')
235
+ if result.first()
236
+ @sitename = PHP.unserialize(result.first()['value'])
237
+ end
238
+ end
239
+ end # def update_sitename
240
+
241
+ private
242
+ def get_last_wid
243
+ result = @client.query('SELECT value FROM variable WHERE name="logstash_last_wid"')
244
+ lastWid = false
245
+
246
+ if result.count() > 0
247
+ tmp = result.first()["value"].gsub("i:", "").gsub(";", "")
248
+ lastWid = tmp.to_i.to_s == tmp ? tmp : "0"
249
+ end
250
+
251
+ return lastWid
252
+ end # def get_last_wid
253
+
254
+ private
255
+ def set_last_wid(wid, insert)
256
+ wid = PHP.serialize(wid.to_i)
257
+
258
+ # Update last import wid variable
259
+ if insert
260
+ # Does not exist yet, so insert
261
+ @client.query('INSERT INTO variable (name, value) VALUES("logstash_last_wid", "' + wid + '")')
262
+ else
263
+ @client.query('UPDATE variable SET value="' + wid + '" WHERE name="logstash_last_wid"')
264
+ end
265
+ end # def set_last_wid
266
+
267
+ private
268
+ def get_usermap
269
+ map = {}
270
+
271
+ @client.query("SELECT uid, name FROM users").each do |row|
272
+ map[row["uid"]] = row["name"]
273
+ end
274
+
275
+ map[0] = "guest"
276
+ return map
277
+ end # def get_usermap
278
+
279
+ private
280
+ def build_event(row)
281
+ # Convert unix timestamp
282
+ timestamp = Time.at(row["timestamp"])
283
+
284
+ msg = row["message"]
285
+ vars = {}
286
+
287
+ # Unserialize the variables, and construct the message
288
+ if row['variables'] != 'N;'
289
+ vars = PHP.unserialize(row["variables"])
290
+
291
+ if vars.is_a?(Hash)
292
+ vars.each_pair do |k, v|
293
+ if msg.scan(k).length() > 0
294
+ msg = msg.gsub(k.to_s, v.to_s)
295
+ else
296
+ # If not inside the message, add var as an additional field
297
+ row["variable_" + k] = v
298
+ end
299
+ end
300
+ end
301
+ end
302
+
303
+ row.delete("message")
304
+ row.delete("variables")
305
+ row.delete("timestamp")
306
+
307
+ row["severity"] = row["severity"].to_i
308
+
309
+ if @add_usernames and @usermap.has_key?(row["uid"])
310
+ row["user"] = @usermap[row["uid"]]
311
+ end
312
+
313
+ entry = {
314
+ "@timestamp" => timestamp,
315
+ "tags" => [],
316
+ "type" => "watchdog",
317
+ "site" => @sitename,
318
+ "message" => msg
319
+ }.merge(row)
320
+
321
+ return LogStash::Event.new(entry)
322
+ end # def build_event
323
+
324
+ end # class LogStash::Inputs::DrupalDblog
@@ -0,0 +1,66 @@
1
+ # encoding: utf-8
2
+ require "java"
3
+ require "rubygems"
4
+ require "jdbc/mysql"
5
+
6
+ java_import "com.mysql.jdbc.Driver"
7
+
8
+ # A JDBC mysql connection class.
9
+ # The interface is compatible with the mysql2 API.
10
+ class LogStash::DrupalDblogJavaMysqlConnection
11
+
12
+ def initialize(host, username, password, database, port = nil)
13
+ port ||= 3306
14
+
15
+ address = "jdbc:mysql://#{host}:#{port}/#{database}"
16
+ @connection = java.sql.DriverManager.getConnection(address, username, password)
17
+ end # def initialize
18
+
19
+ def query(sql)
20
+ if sql =~ /select/i
21
+ return select(sql)
22
+ else
23
+ return update(sql)
24
+ end
25
+ end # def query
26
+
27
+ def select(sql)
28
+ stmt = @connection.createStatement
29
+ resultSet = stmt.executeQuery(sql)
30
+
31
+ meta = resultSet.getMetaData
32
+ column_count = meta.getColumnCount
33
+
34
+ rows = []
35
+
36
+ while resultSet.next
37
+ res = {}
38
+
39
+ (1..column_count).each do |i|
40
+ name = meta.getColumnName(i)
41
+ case meta.getColumnType(i)
42
+ when java.sql.Types::INTEGER
43
+ res[name] = resultSet.getInt(name)
44
+ else
45
+ res[name] = resultSet.getString(name)
46
+ end
47
+ end
48
+
49
+ rows << res
50
+ end
51
+
52
+ stmt.close
53
+ return rows
54
+ end # def select
55
+
56
+ def update(sql)
57
+ stmt = @connection.createStatement
58
+ stmt.execute_update(sql)
59
+ stmt.close
60
+ end # def update
61
+
62
+ def close
63
+ @connection.close
64
+ end # def close
65
+
66
+ end # class LogStash::DrupalDblogJavaMysqlConnection
@@ -0,0 +1,41 @@
1
+ Gem::Specification.new do |s|
2
+
3
+ s.name = 'logstash-input-drupal_dblog'
4
+ s.version = '0.1.1'
5
+ s.licenses = ['Apache License (2.0)']
6
+ s.summary = "Retrieve watchdog log events from a Drupal installation with DBLog enabled"
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"
8
+ s.authors = ["Elasticsearch"]
9
+ s.email = 'info@elasticsearch.com'
10
+ s.homepage = "http://www.elasticsearch.org/guide/en/logstash/current/index.html"
11
+ s.require_paths = ["lib"]
12
+
13
+ # Files
14
+ s.files = `git ls-files`.split($\)+::Dir.glob('vendor/*')
15
+
16
+ # Tests
17
+ s.test_files = s.files.grep(%r{^(test|spec|features)/})
18
+
19
+ # Special flag to let us know this is actually a logstash plugin
20
+ s.metadata = { "logstash_plugin" => "true", "logstash_group" => "input" }
21
+
22
+ # Jar dependencies
23
+ s.requirements << "jar 'mysql:mysql-connector-java', '5.1.33'"
24
+
25
+ # Gem dependencies
26
+ s.add_runtime_dependency 'logstash', '>= 1.4.0', '< 2.0.0'
27
+ s.add_runtime_dependency 'jar-dependencies'
28
+
29
+ s.add_runtime_dependency 'logstash-codec-plain'
30
+ s.add_runtime_dependency 'php_serialize'
31
+
32
+ if RUBY_PLATFORM == 'java'
33
+ s.platform = RUBY_PLATFORM
34
+ s.add_runtime_dependency 'jdbc-mysql'
35
+ else
36
+ s.add_runtime_dependency 'mysql2'
37
+ end
38
+
39
+ s.add_development_dependency 'logstash-devutils'
40
+ end
41
+
@@ -0,0 +1 @@
1
+ require "logstash/devutils/rspec/spec_helper"
metadata ADDED
@@ -0,0 +1,145 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: logstash-input-drupal_dblog
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.1
5
+ platform: java
6
+ authors:
7
+ - Elasticsearch
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2014-11-19 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: logstash
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: jar-dependencies
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-codec-plain
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: :runtime
61
+ - !ruby/object:Gem::Dependency
62
+ name: php_serialize
63
+ version_requirements: !ruby/object:Gem::Requirement
64
+ requirements:
65
+ - - '>='
66
+ - !ruby/object:Gem::Version
67
+ version: '0'
68
+ requirement: !ruby/object:Gem::Requirement
69
+ requirements:
70
+ - - '>='
71
+ - !ruby/object:Gem::Version
72
+ version: '0'
73
+ prerelease: false
74
+ type: :runtime
75
+ - !ruby/object:Gem::Dependency
76
+ name: jdbc-mysql
77
+ version_requirements: !ruby/object:Gem::Requirement
78
+ requirements:
79
+ - - '>='
80
+ - !ruby/object:Gem::Version
81
+ version: '0'
82
+ requirement: !ruby/object:Gem::Requirement
83
+ requirements:
84
+ - - '>='
85
+ - !ruby/object:Gem::Version
86
+ version: '0'
87
+ prerelease: false
88
+ type: :runtime
89
+ - !ruby/object:Gem::Dependency
90
+ name: logstash-devutils
91
+ version_requirements: !ruby/object:Gem::Requirement
92
+ requirements:
93
+ - - '>='
94
+ - !ruby/object:Gem::Version
95
+ version: '0'
96
+ requirement: !ruby/object:Gem::Requirement
97
+ requirements:
98
+ - - '>='
99
+ - !ruby/object:Gem::Version
100
+ version: '0'
101
+ prerelease: false
102
+ type: :development
103
+ 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
104
+ email: info@elasticsearch.com
105
+ executables: []
106
+ extensions: []
107
+ extra_rdoc_files: []
108
+ files:
109
+ - .gitignore
110
+ - Gemfile
111
+ - LICENSE
112
+ - Rakefile
113
+ - lib/logstash/inputs/drupal_dblog.rb
114
+ - lib/logstash/inputs/drupal_dblog/jdbcconnection.rb
115
+ - logstash-input-drupal_dblog.gemspec
116
+ - spec/inputs/drupal_dblog_spec.rb
117
+ homepage: http://www.elasticsearch.org/guide/en/logstash/current/index.html
118
+ licenses:
119
+ - Apache License (2.0)
120
+ metadata:
121
+ logstash_plugin: 'true'
122
+ logstash_group: input
123
+ post_install_message:
124
+ rdoc_options: []
125
+ require_paths:
126
+ - lib
127
+ required_ruby_version: !ruby/object:Gem::Requirement
128
+ requirements:
129
+ - - '>='
130
+ - !ruby/object:Gem::Version
131
+ version: '0'
132
+ required_rubygems_version: !ruby/object:Gem::Requirement
133
+ requirements:
134
+ - - '>='
135
+ - !ruby/object:Gem::Version
136
+ version: '0'
137
+ requirements:
138
+ - jar 'mysql:mysql-connector-java', '5.1.33'
139
+ rubyforge_project:
140
+ rubygems_version: 2.2.2
141
+ signing_key:
142
+ specification_version: 4
143
+ summary: Retrieve watchdog log events from a Drupal installation with DBLog enabled
144
+ test_files:
145
+ - spec/inputs/drupal_dblog_spec.rb