fluent-plugin-windows-eventlog 0.8.1 → 0.8.3

Sign up to get free protection for your applications and to get access to all the features.
data/Rakefile CHANGED
@@ -1,10 +1,10 @@
1
- require "bundler/gem_tasks"
2
- require "rake/testtask"
3
-
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
1
+ require "bundler/gem_tasks"
2
+ require "rake/testtask"
3
+
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
@@ -1,28 +1,28 @@
1
- # coding: utf-8
2
- lib = File.expand_path('../lib', __FILE__)
3
- $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
-
5
- Gem::Specification.new do |spec|
6
- spec.name = "fluent-plugin-windows-eventlog"
7
- spec.version = "0.8.1"
8
- spec.authors = ["okahashi117", "Hiroshi Hatake", "Masahiro Nakagawa"]
9
- spec.email = ["naruki_okahashi@jbat.co.jp", "cosmo0920.oucc@gmail.com", "repeatedly@gmail.com"]
10
- spec.summary = %q{Fluentd Input plugin to read windows event log.}
11
- spec.description = %q{Fluentd Input plugin to read windows event log.}
12
- spec.homepage = "https://github.com/fluent/fluent-plugin-windows-eventlog"
13
- spec.license = "Apache-2.0"
14
-
15
- spec.files = `git ls-files -z`.split("\x0")
16
- spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
17
- spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
18
- spec.require_paths = ["lib"]
19
-
20
- spec.add_development_dependency "bundler"
21
- spec.add_development_dependency "rake"
22
- spec.add_development_dependency "test-unit", "~> 3.4.0"
23
- spec.add_development_dependency "nokogiri", [">= 1.10", "< 1.12"]
24
- spec.add_development_dependency "fluent-plugin-parser-winevt_xml", ">= 0.1.2"
25
- spec.add_runtime_dependency "fluentd", [">= 0.14.12", "< 2"]
26
- spec.add_runtime_dependency "win32-eventlog"
27
- spec.add_runtime_dependency "winevt_c", ">= 0.9.1"
28
- end
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+
5
+ Gem::Specification.new do |spec|
6
+ spec.name = "fluent-plugin-windows-eventlog"
7
+ spec.version = "0.8.3"
8
+ spec.authors = ["okahashi117", "Hiroshi Hatake", "Masahiro Nakagawa"]
9
+ spec.email = ["naruki_okahashi@jbat.co.jp", "cosmo0920.oucc@gmail.com", "repeatedly@gmail.com"]
10
+ spec.summary = %q{Fluentd Input plugin to read windows event log.}
11
+ spec.description = %q{Fluentd Input plugin to read windows event log.}
12
+ spec.homepage = "https://github.com/fluent/fluent-plugin-windows-eventlog"
13
+ spec.license = "Apache-2.0"
14
+
15
+ spec.files = `git ls-files -z`.split("\x0")
16
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
17
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
18
+ spec.require_paths = ["lib"]
19
+
20
+ spec.add_development_dependency "bundler"
21
+ spec.add_development_dependency "rake"
22
+ spec.add_development_dependency "test-unit", "~> 3.4.0"
23
+ spec.add_development_dependency "nokogiri", [">= 1.10", "< 1.15"]
24
+ spec.add_development_dependency "fluent-plugin-parser-winevt_xml", ">= 0.1.2"
25
+ spec.add_runtime_dependency "fluentd", [">= 0.14.12", "< 2"]
26
+ spec.add_runtime_dependency "win32-eventlog"
27
+ spec.add_runtime_dependency "winevt_c", ">= 0.10.1"
28
+ end
@@ -1,30 +1,30 @@
1
- require 'nokogiri'
2
-
3
- class WinevtBookmarkDocument < Nokogiri::XML::SAX::Document
4
- attr_reader :result
5
-
6
- def initialize
7
- @result = {}
8
- super
9
- end
10
-
11
- def start_document
12
- end
13
-
14
- def start_element(name, attributes = [])
15
- if name == "Bookmark"
16
- @result[:channel] = attributes[0][1] rescue nil
17
- @result[:record_id] = attributes[1][1].to_i rescue nil
18
- @result[:is_current] = attributes[2][1].downcase == "true" rescue nil
19
- end
20
- end
21
-
22
- def characters(string)
23
- end
24
-
25
- def end_element(name, attributes = [])
26
- end
27
-
28
- def end_document
29
- end
30
- end
1
+ require 'nokogiri'
2
+
3
+ class WinevtBookmarkDocument < Nokogiri::XML::SAX::Document
4
+ attr_reader :result
5
+
6
+ def initialize
7
+ @result = {}
8
+ super
9
+ end
10
+
11
+ def start_document
12
+ end
13
+
14
+ def start_element(name, attributes = [])
15
+ if name == "Bookmark"
16
+ @result[:channel] = attributes[0][1] rescue nil
17
+ @result[:record_id] = attributes[1][1].to_i rescue nil
18
+ @result[:is_current] = attributes[2][1].downcase == "true" rescue nil
19
+ end
20
+ end
21
+
22
+ def characters(string)
23
+ end
24
+
25
+ def end_element(name, attributes = [])
26
+ end
27
+
28
+ def end_document
29
+ end
30
+ end
@@ -1,241 +1,241 @@
1
- require 'win32/eventlog'
2
- require 'fluent/plugin/input'
3
- require 'fluent/plugin'
4
-
5
- module Fluent::Plugin
6
- class WindowsEventLogInput < Input
7
- Fluent::Plugin.register_input('windows_eventlog', self)
8
-
9
- helpers :timer, :storage
10
-
11
- DEFAULT_STORAGE_TYPE = 'local'
12
- KEY_MAP = {"record_number" => [:record_number, :string],
13
- "time_generated" => [:time_generated, :string],
14
- "time_written" => [:time_written, :string],
15
- "event_id" => [:event_id, :string],
16
- "event_type" => [:event_type, :string],
17
- "event_category" => [:category, :string],
18
- "source_name" => [:source, :string],
19
- "computer_name" => [:computer, :string],
20
- "user" => [:user, :string],
21
- "description" => [:description, :string],
22
- "string_inserts" => [:string_inserts, :array]}
23
-
24
- config_param :tag, :string
25
- config_param :read_interval, :time, default: 2
26
- config_param :pos_file, :string, default: nil,
27
- obsoleted: "This section is not used anymore. Use 'store_pos' instead."
28
- config_param :channels, :array, default: ['application']
29
- config_param :keys, :array, default: []
30
- config_param :read_from_head, :bool, default: false
31
- config_param :from_encoding, :string, default: nil
32
- config_param :encoding, :string, default: nil
33
- desc "Parse 'description' field and set parsed result into event record. 'description' and 'string_inserts' fields are removed from the record"
34
- config_param :parse_description, :bool, default: false
35
-
36
- config_section :storage do
37
- config_set_default :usage, "positions"
38
- config_set_default :@type, DEFAULT_STORAGE_TYPE
39
- config_set_default :persistent, true
40
- end
41
-
42
- attr_reader :chs
43
-
44
- def initialize
45
- super
46
- @chs = []
47
- @keynames = []
48
- @tails = {}
49
- end
50
-
51
- def configure(conf)
52
- log.warn "in_windows_eventlog is deprecated. It will be removed in the future version."
53
- super
54
- @chs = @channels.map {|ch| ch.strip.downcase }.uniq
55
- if @chs.empty?
56
- raise Fluent::ConfigError, "windows_eventlog: 'channels' parameter is required on windows_eventlog input"
57
- end
58
- @keynames = @keys.map {|k| k.strip }.uniq
59
- if @keynames.empty?
60
- @keynames = KEY_MAP.keys
61
- end
62
- @keynames.delete('string_inserts') if @parse_description
63
-
64
- @tag = tag
65
- @stop = false
66
- configure_encoding
67
- @receive_handlers = if @encoding
68
- method(:encode_record)
69
- else
70
- method(:no_encode_record)
71
- end
72
- @pos_storage = storage_create(usage: "positions")
73
- end
74
-
75
- def configure_encoding
76
- unless @encoding
77
- if @from_encoding
78
- raise Fluent::ConfigError, "windows_eventlog: 'from_encoding' parameter must be specied with 'encoding' parameter."
79
- end
80
- end
81
-
82
- @encoding = parse_encoding_param(@encoding) if @encoding
83
- @from_encoding = parse_encoding_param(@from_encoding) if @from_encoding
84
- end
85
-
86
- def parse_encoding_param(encoding_name)
87
- begin
88
- Encoding.find(encoding_name) if encoding_name
89
- rescue ArgumentError => e
90
- raise Fluent::ConfigError, e.message
91
- end
92
- end
93
-
94
- def encode_record(record)
95
- if @encoding
96
- if @from_encoding
97
- record.encode!(@encoding, @from_encoding)
98
- else
99
- record.force_encoding(@encoding)
100
- end
101
- end
102
- end
103
-
104
- def no_encode_record(record)
105
- record
106
- end
107
-
108
- def start
109
- super
110
- @chs.each do |ch|
111
- start, num = @pos_storage.get(ch)
112
- if @read_from_head || (!num || num.zero?)
113
- el = Win32::EventLog.open(ch)
114
- @pos_storage.put(ch, [el.oldest_record_number - 1, 1])
115
- el.close
116
- end
117
- timer_execute("in_windows_eventlog_#{escape_channel(ch)}".to_sym, @read_interval) do
118
- on_notify(ch)
119
- end
120
- end
121
- end
122
-
123
- def escape_channel(ch)
124
- ch.gsub(/[^a-zA-Z0-9]/, '_')
125
- end
126
-
127
- def receive_lines(ch, lines)
128
- return if lines.empty?
129
- begin
130
- for r in lines
131
- h = {"channel" => ch}
132
- @keynames.each do |k|
133
- type = KEY_MAP[k][1]
134
- value = r.send(KEY_MAP[k][0])
135
- h[k]=case type
136
- when :string
137
- @receive_handlers.call(value.to_s)
138
- when :array
139
- value.map {|v| @receive_handlers.call(v.to_s)}
140
- else
141
- raise "Unknown value type: #{type}"
142
- end
143
- end
144
- parse_desc(h) if @parse_description
145
- #h = Hash[@keynames.map {|k| [k, r.send(KEY_MAP[k][0]).to_s]}]
146
- router.emit(@tag, Fluent::Engine.now, h)
147
- end
148
- rescue => e
149
- log.error "unexpected error", error: e
150
- log.error_backtrace
151
- end
152
- end
153
-
154
- def on_notify(ch)
155
- begin
156
- el = Win32::EventLog.open(ch)
157
- rescue => e
158
- log.error "Failed to open Windows Event log.", error: e
159
- end
160
-
161
- current_oldest_record_number = el.oldest_record_number
162
- current_total_records = el.total_records
163
-
164
- read_start, read_num = @pos_storage.get(ch)
165
-
166
- # if total_records is zero, oldest_record_number has no meaning.
167
- if current_total_records == 0
168
- return
169
- end
170
-
171
- if read_start == 0 && read_num == 0
172
- @pos_storage.put(ch, [current_oldest_record_number, current_total_records])
173
- return
174
- end
175
-
176
- current_end = current_oldest_record_number + current_total_records - 1
177
- old_end = read_start + read_num - 1
178
-
179
- if current_oldest_record_number < read_start
180
- # may be a record number rotated.
181
- current_end += 0xFFFFFFFF
182
- end
183
-
184
- if current_end < old_end
185
- # something occured.
186
- @pos_storage.put(ch, [current_oldest_record_number, current_total_records])
187
- return
188
- end
189
-
190
- winlogs = el.read(Win32::EventLog::SEEK_READ | Win32::EventLog::FORWARDS_READ, old_end + 1)
191
- receive_lines(ch, winlogs)
192
- @pos_storage.put(ch, [read_start, read_num + winlogs.size])
193
- ensure
194
- if el
195
- el.close
196
- end
197
- end
198
-
199
- GROUP_DELIMITER = "\r\n\r\n".freeze
200
- RECORD_DELIMITER = "\r\n\t".freeze
201
- FIELD_DELIMITER = "\t\t".freeze
202
- NONE_FIELD_DELIMITER = "\t".freeze
203
-
204
- def parse_desc(record)
205
- desc = record.delete('description'.freeze)
206
- return if desc.nil?
207
-
208
- elems = desc.split(GROUP_DELIMITER)
209
- record['description_title'] = elems.shift
210
- elems.each { |elem|
211
- parent_key = nil
212
- elem.split(RECORD_DELIMITER).each { |r|
213
- key, value = if r.index(FIELD_DELIMITER)
214
- r.split(FIELD_DELIMITER)
215
- else
216
- r.split(NONE_FIELD_DELIMITER)
217
- end
218
- key.chop! # remove ':' from key
219
- if value.nil?
220
- parent_key = to_key(key)
221
- else
222
- # parsed value sometimes contain unexpected "\t". So remove it.
223
- value.strip!
224
- if parent_key.nil?
225
- record[to_key(key)] = value
226
- else
227
- k = "#{parent_key}.#{to_key(key)}"
228
- record[k] = value
229
- end
230
- end
231
- }
232
- }
233
- end
234
-
235
- def to_key(key)
236
- key.downcase!
237
- key.gsub!(' '.freeze, '_'.freeze)
238
- key
239
- end
240
- end
241
- end
1
+ require 'win32/eventlog'
2
+ require 'fluent/plugin/input'
3
+ require 'fluent/plugin'
4
+
5
+ module Fluent::Plugin
6
+ class WindowsEventLogInput < Input
7
+ Fluent::Plugin.register_input('windows_eventlog', self)
8
+
9
+ helpers :timer, :storage
10
+
11
+ DEFAULT_STORAGE_TYPE = 'local'
12
+ KEY_MAP = {"record_number" => [:record_number, :string],
13
+ "time_generated" => [:time_generated, :string],
14
+ "time_written" => [:time_written, :string],
15
+ "event_id" => [:event_id, :string],
16
+ "event_type" => [:event_type, :string],
17
+ "event_category" => [:category, :string],
18
+ "source_name" => [:source, :string],
19
+ "computer_name" => [:computer, :string],
20
+ "user" => [:user, :string],
21
+ "description" => [:description, :string],
22
+ "string_inserts" => [:string_inserts, :array]}
23
+
24
+ config_param :tag, :string
25
+ config_param :read_interval, :time, default: 2
26
+ config_param :pos_file, :string, default: nil,
27
+ obsoleted: "This section is not used anymore. Use 'store_pos' instead."
28
+ config_param :channels, :array, default: ['application']
29
+ config_param :keys, :array, default: []
30
+ config_param :read_from_head, :bool, default: false
31
+ config_param :from_encoding, :string, default: nil
32
+ config_param :encoding, :string, default: nil
33
+ desc "Parse 'description' field and set parsed result into event record. 'description' and 'string_inserts' fields are removed from the record"
34
+ config_param :parse_description, :bool, default: false
35
+
36
+ config_section :storage do
37
+ config_set_default :usage, "positions"
38
+ config_set_default :@type, DEFAULT_STORAGE_TYPE
39
+ config_set_default :persistent, true
40
+ end
41
+
42
+ attr_reader :chs
43
+
44
+ def initialize
45
+ super
46
+ @chs = []
47
+ @keynames = []
48
+ @tails = {}
49
+ end
50
+
51
+ def configure(conf)
52
+ log.warn "in_windows_eventlog is deprecated. It will be removed in the future version."
53
+ super
54
+ @chs = @channels.map {|ch| ch.strip.downcase }.uniq
55
+ if @chs.empty?
56
+ raise Fluent::ConfigError, "windows_eventlog: 'channels' parameter is required on windows_eventlog input"
57
+ end
58
+ @keynames = @keys.map {|k| k.strip }.uniq
59
+ if @keynames.empty?
60
+ @keynames = KEY_MAP.keys
61
+ end
62
+ @keynames.delete('string_inserts') if @parse_description
63
+
64
+ @tag = tag
65
+ @stop = false
66
+ configure_encoding
67
+ @receive_handlers = if @encoding
68
+ method(:encode_record)
69
+ else
70
+ method(:no_encode_record)
71
+ end
72
+ @pos_storage = storage_create(usage: "positions")
73
+ end
74
+
75
+ def configure_encoding
76
+ unless @encoding
77
+ if @from_encoding
78
+ raise Fluent::ConfigError, "windows_eventlog: 'from_encoding' parameter must be specied with 'encoding' parameter."
79
+ end
80
+ end
81
+
82
+ @encoding = parse_encoding_param(@encoding) if @encoding
83
+ @from_encoding = parse_encoding_param(@from_encoding) if @from_encoding
84
+ end
85
+
86
+ def parse_encoding_param(encoding_name)
87
+ begin
88
+ Encoding.find(encoding_name) if encoding_name
89
+ rescue ArgumentError => e
90
+ raise Fluent::ConfigError, e.message
91
+ end
92
+ end
93
+
94
+ def encode_record(record)
95
+ if @encoding
96
+ if @from_encoding
97
+ record.encode!(@encoding, @from_encoding)
98
+ else
99
+ record.force_encoding(@encoding)
100
+ end
101
+ end
102
+ end
103
+
104
+ def no_encode_record(record)
105
+ record
106
+ end
107
+
108
+ def start
109
+ super
110
+ @chs.each do |ch|
111
+ start, num = @pos_storage.get(ch)
112
+ if @read_from_head || (!num || num.zero?)
113
+ el = Win32::EventLog.open(ch)
114
+ @pos_storage.put(ch, [el.oldest_record_number - 1, 1])
115
+ el.close
116
+ end
117
+ timer_execute("in_windows_eventlog_#{escape_channel(ch)}".to_sym, @read_interval) do
118
+ on_notify(ch)
119
+ end
120
+ end
121
+ end
122
+
123
+ def escape_channel(ch)
124
+ ch.gsub(/[^a-zA-Z0-9]/, '_')
125
+ end
126
+
127
+ def receive_lines(ch, lines)
128
+ return if lines.empty?
129
+ begin
130
+ for r in lines
131
+ h = {"channel" => ch}
132
+ @keynames.each do |k|
133
+ type = KEY_MAP[k][1]
134
+ value = r.send(KEY_MAP[k][0])
135
+ h[k]=case type
136
+ when :string
137
+ @receive_handlers.call(value.to_s)
138
+ when :array
139
+ value.map {|v| @receive_handlers.call(v.to_s)}
140
+ else
141
+ raise "Unknown value type: #{type}"
142
+ end
143
+ end
144
+ parse_desc(h) if @parse_description
145
+ #h = Hash[@keynames.map {|k| [k, r.send(KEY_MAP[k][0]).to_s]}]
146
+ router.emit(@tag, Fluent::Engine.now, h)
147
+ end
148
+ rescue => e
149
+ log.error "unexpected error", error: e
150
+ log.error_backtrace
151
+ end
152
+ end
153
+
154
+ def on_notify(ch)
155
+ begin
156
+ el = Win32::EventLog.open(ch)
157
+ rescue => e
158
+ log.error "Failed to open Windows Event log.", error: e
159
+ end
160
+
161
+ current_oldest_record_number = el.oldest_record_number
162
+ current_total_records = el.total_records
163
+
164
+ read_start, read_num = @pos_storage.get(ch)
165
+
166
+ # if total_records is zero, oldest_record_number has no meaning.
167
+ if current_total_records == 0
168
+ return
169
+ end
170
+
171
+ if read_start == 0 && read_num == 0
172
+ @pos_storage.put(ch, [current_oldest_record_number, current_total_records])
173
+ return
174
+ end
175
+
176
+ current_end = current_oldest_record_number + current_total_records - 1
177
+ old_end = read_start + read_num - 1
178
+
179
+ if current_oldest_record_number < read_start
180
+ # may be a record number rotated.
181
+ current_end += 0xFFFFFFFF
182
+ end
183
+
184
+ if current_end < old_end
185
+ # something occured.
186
+ @pos_storage.put(ch, [current_oldest_record_number, current_total_records])
187
+ return
188
+ end
189
+
190
+ winlogs = el.read(Win32::EventLog::SEEK_READ | Win32::EventLog::FORWARDS_READ, old_end + 1)
191
+ receive_lines(ch, winlogs)
192
+ @pos_storage.put(ch, [read_start, read_num + winlogs.size])
193
+ ensure
194
+ if el
195
+ el.close
196
+ end
197
+ end
198
+
199
+ GROUP_DELIMITER = "\r\n\r\n".freeze
200
+ RECORD_DELIMITER = "\r\n\t".freeze
201
+ FIELD_DELIMITER = "\t\t".freeze
202
+ NONE_FIELD_DELIMITER = "\t".freeze
203
+
204
+ def parse_desc(record)
205
+ desc = record.delete('description'.freeze)
206
+ return if desc.nil?
207
+
208
+ elems = desc.split(GROUP_DELIMITER)
209
+ record['description_title'] = elems.shift
210
+ elems.each { |elem|
211
+ parent_key = nil
212
+ elem.split(RECORD_DELIMITER).each { |r|
213
+ key, value = if r.index(FIELD_DELIMITER)
214
+ r.split(FIELD_DELIMITER)
215
+ else
216
+ r.split(NONE_FIELD_DELIMITER)
217
+ end
218
+ key.chop! # remove ':' from key
219
+ if value.nil?
220
+ parent_key = to_key(key)
221
+ else
222
+ # parsed value sometimes contain unexpected "\t". So remove it.
223
+ value.strip!
224
+ if parent_key.nil?
225
+ record[to_key(key)] = value
226
+ else
227
+ k = "#{parent_key}.#{to_key(key)}"
228
+ record[k] = value
229
+ end
230
+ end
231
+ }
232
+ }
233
+ end
234
+
235
+ def to_key(key)
236
+ key.downcase!
237
+ key.gsub!(' '.freeze, '_'.freeze)
238
+ key
239
+ end
240
+ end
241
+ end