fluent-plugin-windows-eventlog 0.2.2 → 0.4.3

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.
@@ -0,0 +1,242 @@
1
+ require 'winevt'
2
+ require 'fluent/plugin/input'
3
+ require 'fluent/plugin'
4
+
5
+ module Fluent::Plugin
6
+ class WindowsEventLog2Input < Input
7
+ Fluent::Plugin.register_input('windows_eventlog2', self)
8
+
9
+ helpers :timer, :storage, :parser
10
+
11
+ DEFAULT_STORAGE_TYPE = 'local'
12
+ KEY_MAP = {"ProviderName" => ["ProviderName", :string],
13
+ "ProviderGUID" => ["ProviderGUID", :string],
14
+ "EventID" => ["EventID", :string],
15
+ "Qualifiers" => ["Qualifiers", :string],
16
+ "Level" => ["Level", :string],
17
+ "Task" => ["Task", :string],
18
+ "Opcode" => ["Opcode", :string],
19
+ "Keywords" => ["Keywords", :string],
20
+ "TimeCreated" => ["TimeCreated", :string],
21
+ "EventRecordID" => ["EventRecordID", :string],
22
+ "ActivityID" => ["ActivityID", :string],
23
+ "RelatedActivityID" => ["RelatedActivityID", :string],
24
+ "ProcessID" => ["ProcessID", :string],
25
+ "ThreadID" => ["ThreadID", :string],
26
+ "Channel" => ["Channel", :string],
27
+ "Computer" => ["Computer", :string],
28
+ "UserID" => ["UserID", :string],
29
+ "Version" => ["Version", :string],
30
+ "Description" => ["Description", :string],
31
+ "EventData" => ["EventData", :array]}
32
+
33
+ config_param :tag, :string
34
+ config_param :read_interval, :time, default: 2
35
+ config_param :channels, :array, default: ['application']
36
+ config_param :keys, :array, default: []
37
+ config_param :read_from_head, :bool, default: false
38
+ config_param :parse_description, :bool, default: false
39
+ config_param :render_as_xml, :bool, default: true
40
+ config_param :rate_limit, :integer, default: Winevt::EventLog::Subscribe::RATE_INFINITE
41
+
42
+ config_section :storage do
43
+ config_set_default :usage, "bookmarks"
44
+ config_set_default :@type, DEFAULT_STORAGE_TYPE
45
+ config_set_default :persistent, true
46
+ end
47
+
48
+ config_section :parse do
49
+ config_set_default :@type, 'winevt_xml'
50
+ config_set_default :estimate_current_event, false
51
+ end
52
+
53
+ def initalize
54
+ super
55
+ @chs = []
56
+ @keynames = []
57
+ end
58
+
59
+ def configure(conf)
60
+ super
61
+ @chs = @channels.map {|ch| ch.strip.downcase }.uniq
62
+ @keynames = @keys.map {|k| k.strip }.uniq
63
+ if @keynames.empty?
64
+ @keynames = KEY_MAP.keys
65
+ end
66
+ @keynames.delete('Qualifiers') unless @render_as_xml
67
+ @keynames.delete('EventData') if @parse_description
68
+
69
+ @tag = tag
70
+ @tailing = @read_from_head ? false : true
71
+ @bookmarks_storage = storage_create(usage: "bookmarks")
72
+ @winevt_xml = false
73
+ if @render_as_xml
74
+ @parser = parser_create
75
+ @winevt_xml = @parser.respond_to?(:winevt_xml?) && @parser.winevt_xml?
76
+ class << self
77
+ alias_method :on_notify, :on_notify_xml
78
+ end
79
+ else
80
+ class << self
81
+ alias_method :on_notify, :on_notify_hash
82
+ end
83
+ end
84
+ end
85
+
86
+ def start
87
+ super
88
+
89
+ @chs.each do |ch|
90
+ bookmarkXml = @bookmarks_storage.get(ch) || ""
91
+ subscribe = Winevt::EventLog::Subscribe.new
92
+ bookmark = unless bookmarkXml.empty?
93
+ Winevt::EventLog::Bookmark.new(bookmarkXml)
94
+ else
95
+ nil
96
+ end
97
+ subscribe.tail = @tailing
98
+ begin
99
+ subscribe.subscribe(ch, "*", bookmark)
100
+ rescue Winevt::EventLog::Query::Error => e
101
+ raise Fluent::ConfigError, "Invalid Bookmark XML is loaded. #{e}"
102
+ end
103
+ subscribe.render_as_xml = @render_as_xml
104
+ subscribe.rate_limit = @rate_limit
105
+ timer_execute("in_windows_eventlog_#{escape_channel(ch)}".to_sym, @read_interval) do
106
+ on_notify(ch, subscribe)
107
+ end
108
+ end
109
+ end
110
+
111
+ def escape_channel(ch)
112
+ ch.gsub(/[^a-zA-Z0-9]/, '_')
113
+ end
114
+
115
+ def on_notify(ch, subscribe)
116
+ # for safety.
117
+ end
118
+
119
+ def on_notify_xml(ch, subscribe)
120
+ es = Fluent::MultiEventStream.new
121
+ begin
122
+ subscribe.each do |xml, message, string_inserts|
123
+ @parser.parse(xml) do |time, record|
124
+ # record.has_key?("EventData") for none parser checking.
125
+ if @winevt_xml
126
+ record["Description"] = message
127
+ record["EventData"] = string_inserts
128
+
129
+ h = {}
130
+ @keynames.each do |k|
131
+ type = KEY_MAP[k][1]
132
+ value = record[KEY_MAP[k][0]]
133
+ h[k]=case type
134
+ when :string
135
+ value.to_s
136
+ when :array
137
+ value.map {|v| v.to_s}
138
+ else
139
+ raise "Unknown value type: #{type}"
140
+ end
141
+ end
142
+ parse_desc(h) if @parse_description
143
+ es.add(Fluent::Engine.now, h)
144
+ else
145
+ record["Description"] = message
146
+ record["EventData"] = string_inserts
147
+ # for none parser
148
+ es.add(Fluent::Engine.now, record)
149
+ end
150
+ end
151
+ end
152
+ rescue Winevt::EventLog::Query::Error => e
153
+ log.warn "Invalid XML data", error: e
154
+ log.warn_backtrace
155
+ end
156
+ router.emit_stream(@tag, es)
157
+ @bookmarks_storage.put(ch, subscribe.bookmark)
158
+ end
159
+
160
+ def on_notify_hash(ch, subscribe)
161
+ es = Fluent::MultiEventStream.new
162
+ begin
163
+ subscribe.each do |record, message, string_inserts|
164
+ record["Description"] = message
165
+ record["EventData"] = string_inserts
166
+ h = {}
167
+ @keynames.each do |k|
168
+ type = KEY_MAP[k][1]
169
+ value = record[KEY_MAP[k][0]]
170
+ h[k]=case type
171
+ when :string
172
+ value.to_s
173
+ when :array
174
+ value.map {|v| v.to_s}
175
+ else
176
+ raise "Unknown value type: #{type}"
177
+ end
178
+ end
179
+ parse_desc(h) if @parse_description
180
+ es.add(Fluent::Engine.now, h)
181
+ end
182
+ rescue Winevt::EventLog::Query::Error => e
183
+ log.warn "Invalid Hash data", error: e
184
+ log.warn_backtrace
185
+ end
186
+ router.emit_stream(@tag, es)
187
+ @bookmarks_storage.put(ch, subscribe.bookmark)
188
+ end
189
+
190
+ #### These lines copied from in_windows_eventlog plugin:
191
+ #### https://github.com/fluent/fluent-plugin-windows-eventlog/blob/528290d896a885c7721f850943daa3a43a015f3d/lib/fluent/plugin/in_windows_eventlog.rb#L192-L232
192
+ GROUP_DELIMITER = "\r\n\r\n".freeze
193
+ RECORD_DELIMITER = "\r\n\t".freeze
194
+ FIELD_DELIMITER = "\t\t".freeze
195
+ NONE_FIELD_DELIMITER = "\t".freeze
196
+
197
+ def parse_desc(record)
198
+ desc = record.delete("Description".freeze)
199
+ return if desc.nil?
200
+
201
+ elems = desc.split(GROUP_DELIMITER)
202
+ record['DescriptionTitle'] = elems.shift
203
+ previous_key = nil
204
+ elems.each { |elem|
205
+ parent_key = nil
206
+ elem.split(RECORD_DELIMITER).each { |r|
207
+ key, value = if r.index(FIELD_DELIMITER)
208
+ r.split(FIELD_DELIMITER)
209
+ else
210
+ r.split(NONE_FIELD_DELIMITER)
211
+ end
212
+ key.chop! # remove ':' from key
213
+ if value.nil?
214
+ parent_key = to_key(key)
215
+ else
216
+ # parsed value sometimes contain unexpected "\t". So remove it.
217
+ value.strip!
218
+ # merge empty key values into the previous non-empty key record.
219
+ if key.empty?
220
+ record[previous_key] = [record[previous_key], value].flatten
221
+ elsif parent_key.nil?
222
+ record[to_key(key)] = value
223
+ else
224
+ k = "#{parent_key}.#{to_key(key)}"
225
+ record[k] = value
226
+ end
227
+ end
228
+ # XXX: This is for empty privileges record key.
229
+ # We should investigate whether an another case exists or not.
230
+ previous_key = to_key(key) unless key.empty?
231
+ }
232
+ }
233
+ end
234
+
235
+ def to_key(key)
236
+ key.downcase!
237
+ key.gsub!(' '.freeze, '_'.freeze)
238
+ key
239
+ end
240
+ ####
241
+ end
242
+ end
@@ -1,47 +1,47 @@
1
- require 'win32/eventlog'
2
-
3
- class EventLog
4
- def initialize
5
- @logger = Win32::EventLog.new
6
- @app_source = "fluent-plugins"
7
- end
8
-
9
- def info(event_id, message)
10
- @logger.report_event(
11
- source: @app_source,
12
- event_type: Win32::EventLog::INFO_TYPE,
13
- event_id: event_id,
14
- data: message
15
- )
16
- end
17
-
18
- def warn(event_id, message)
19
- @logger.report_event(
20
- source: @app_source,
21
- event_type: Win32::EventLog::WARN_TYPE,
22
- event_id: event_id,
23
- data: message
24
- )
25
- end
26
-
27
- def crit(event_id, message)
28
- @logger.report_event(
29
- source: @app_source,
30
- event_type: Win32::EventLog::ERROR_TYPE,
31
- event_id: event_id,
32
- data: message
33
- )
34
- end
35
-
36
- end
37
-
38
- module Fluent
39
- module Plugin
40
- class EventService
41
- def run
42
- eventlog = EventLog.new()
43
- eventlog.info(65500, "Hi, from fluentd-plugins!! at " + Time.now.strftime("%Y/%m/%d %H:%M:%S "))
44
- end
45
- end
46
- end
47
- end
1
+ require 'win32/eventlog'
2
+
3
+ class EventLog
4
+ def initialize
5
+ @logger = Win32::EventLog.new
6
+ @app_source = "fluent-plugins"
7
+ end
8
+
9
+ def info(event_id, message)
10
+ @logger.report_event(
11
+ source: @app_source,
12
+ event_type: Win32::EventLog::INFO_TYPE,
13
+ event_id: event_id,
14
+ data: message
15
+ )
16
+ end
17
+
18
+ def warn(event_id, message)
19
+ @logger.report_event(
20
+ source: @app_source,
21
+ event_type: Win32::EventLog::WARN_TYPE,
22
+ event_id: event_id,
23
+ data: message
24
+ )
25
+ end
26
+
27
+ def crit(event_id, message)
28
+ @logger.report_event(
29
+ source: @app_source,
30
+ event_type: Win32::EventLog::ERROR_TYPE,
31
+ event_id: event_id,
32
+ data: message
33
+ )
34
+ end
35
+
36
+ end
37
+
38
+ module Fluent
39
+ module Plugin
40
+ class EventService
41
+ def run
42
+ eventlog = EventLog.new()
43
+ eventlog.info(65500, "Hi, from fluentd-plugins!! at " + Time.now.strftime("%Y/%m/%d %H:%M:%S "))
44
+ end
45
+ end
46
+ end
47
+ end
@@ -1,32 +1,33 @@
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/test/driver/input'
26
- require 'fluent/plugin/in_windows_eventlog'
27
-
28
- class Test::Unit::TestCase
29
- end
30
- require 'fluent/test/helpers'
31
-
32
- include Fluent::Test::Helpers
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/test/driver/input'
26
+ require 'fluent/plugin/in_windows_eventlog'
27
+ require 'fluent/plugin/in_windows_eventlog2'
28
+
29
+ class Test::Unit::TestCase
30
+ end
31
+ require 'fluent/test/helpers'
32
+
33
+ include Fluent::Test::Helpers
@@ -0,0 +1,261 @@
1
+ require 'helper'
2
+ require 'fileutils'
3
+ require 'generate-windows-event'
4
+
5
+ class WindowsEventLog2InputTest < Test::Unit::TestCase
6
+
7
+ def setup
8
+ Fluent::Test.setup
9
+ end
10
+
11
+ CONFIG = config_element("ROOT", "", {"tag" => "fluent.eventlog"}, [
12
+ config_element("storage", "", {
13
+ '@type' => 'local',
14
+ 'persistent' => false
15
+ })
16
+ ])
17
+
18
+ def create_driver(conf = CONFIG)
19
+ Fluent::Test::Driver::Input.new(Fluent::Plugin::WindowsEventLog2Input).configure(conf)
20
+ end
21
+
22
+ def test_configure
23
+ d = create_driver CONFIG
24
+ assert_equal 'fluent.eventlog', d.instance.tag
25
+ assert_equal 2, d.instance.read_interval
26
+ assert_equal ['application'], d.instance.channels
27
+ assert_false d.instance.read_from_head
28
+ assert_true d.instance.render_as_xml
29
+ end
30
+
31
+ def test_parse_desc
32
+ d = create_driver
33
+ desc =<<-DESC
34
+ A user's local group membership was enumerated.\r\n\r\nSubject:\r\n\tSecurity ID:\t\tS-X-Y-XX-WWWWWW-VVVV\r\n\tAccount Name:\t\tAdministrator\r\n\tAccount Domain:\t\tDESKTOP-FLUENTTEST\r\n\tLogon ID:\t\t0x3185B1\r\n\r\nUser:\r\n\tSecurity ID:\t\tS-X-Y-XX-WWWWWW-VVVV\r\n\tAccount Name:\t\tAdministrator\r\n\tAccount Domain:\t\tDESKTOP-FLUENTTEST\r\n\r\nProcess Information:\r\n\tProcess ID:\t\t0x50b8\r\n\tProcess Name:\t\tC:\\msys64\\usr\\bin\\make.exe
35
+ DESC
36
+ h = {"Description" => desc}
37
+ expected = {"DescriptionTitle" => "A user's local group membership was enumerated.",
38
+ "subject.security_id" => "S-X-Y-XX-WWWWWW-VVVV",
39
+ "subject.account_name" => "Administrator",
40
+ "subject.account_domain" => "DESKTOP-FLUENTTEST",
41
+ "subject.logon_id" => "0x3185B1",
42
+ "user.security_id" => "S-X-Y-XX-WWWWWW-VVVV",
43
+ "user.account_name" => "Administrator",
44
+ "user.account_domain" => "DESKTOP-FLUENTTEST",
45
+ "process_information.process_id" => "0x50b8",
46
+ "process_information.process_name" => "C:\\msys64\\usr\\bin\\make.exe"}
47
+ d.instance.parse_desc(h)
48
+ assert_equal(expected, h)
49
+ end
50
+
51
+ def test_parse_privileges_description
52
+ d = create_driver
53
+ desc = ["Special privileges assigned to new logon.\r\n\r\nSubject:\r\n\tSecurity ID:\t\tS-X-Y-ZZ\r\n\t",
54
+ "AccountName:\t\tSYSTEM\r\n\tAccount Domain:\t\tNT AUTHORITY\r\n\tLogon ID:\t\t0x3E7\r\n\r\n",
55
+ "Privileges:\t\tSeAssignPrimaryTokenPrivilege\r\n\t\t\tSeTcbPrivilege\r\n\t\t\t",
56
+ "SeSecurityPrivilege\r\n\t\t\tSeTakeOwnershipPrivilege\r\n\t\t\tSeLoadDriverPrivilege\r\n\t\t\t",
57
+ "SeBackupPrivilege\r\n\t\t\tSeRestorePrivilege\r\n\t\t\tSeDebugPrivilege\r\n\t\t\t",
58
+ "SeAuditPrivilege\r\n\t\t\tSeSystemEnvironmentPrivilege\r\n\t\t\tSeImpersonatePrivilege\r\n\t\t\t",
59
+ "SeDelegateSessionUserImpersonatePrivilege"].join("")
60
+
61
+ h = {"Description" => desc}
62
+ expected = {"DescriptionTitle" => "Special privileges assigned to new logon.",
63
+ "subject.security_id" => "S-X-Y-ZZ",
64
+ "subject.accountname" => "SYSTEM",
65
+ "subject.account_domain" => "NT AUTHORITY",
66
+ "subject.logon_id" => "0x3E7",
67
+ "privileges" => ["SeAssignPrimaryTokenPrivilege",
68
+ "SeTcbPrivilege",
69
+ "SeSecurityPrivilege",
70
+ "SeTakeOwnershipPrivilege",
71
+ "SeLoadDriverPrivilege",
72
+ "SeBackupPrivilege",
73
+ "SeRestorePrivilege",
74
+ "SeDebugPrivilege",
75
+ "SeAuditPrivilege",
76
+ "SeSystemEnvironmentPrivilege",
77
+ "SeImpersonatePrivilege",
78
+ "SeDelegateSessionUserImpersonatePrivilege"]}
79
+ d.instance.parse_desc(h)
80
+ assert_equal(expected, h)
81
+ end
82
+
83
+ def test_write
84
+ d = create_driver
85
+
86
+ service = Fluent::Plugin::EventService.new
87
+
88
+ d.run(expect_emits: 1) do
89
+ service.run
90
+ end
91
+
92
+ assert(d.events.length >= 1)
93
+ event = d.events.select {|e| e.last["EventID"] == "65500" }.last
94
+ record = event.last
95
+
96
+ assert_equal("Application", record["Channel"])
97
+ assert_equal("65500", record["EventID"])
98
+ assert_equal("4", record["Level"])
99
+ assert_equal("fluent-plugins", record["ProviderName"])
100
+ end
101
+
102
+ CONFIG_KEYS = config_element("ROOT", "", {
103
+ "tag" => "fluent.eventlog",
104
+ "keys" => ["EventID", "Level", "Channel", "ProviderName"]
105
+ }, [
106
+ config_element("storage", "", {
107
+ '@type' => 'local',
108
+ 'persistent' => false
109
+ })
110
+ ])
111
+ def test_write_with_keys
112
+ d = create_driver(CONFIG_KEYS)
113
+
114
+ service = Fluent::Plugin::EventService.new
115
+
116
+ d.run(expect_emits: 1) do
117
+ service.run
118
+ end
119
+
120
+ assert(d.events.length >= 1)
121
+ event = d.events.last
122
+ record = event.last
123
+
124
+ expected = {"EventID" => "65500",
125
+ "Level" => "4",
126
+ "Channel" => "Application",
127
+ "ProviderName" => "fluent-plugins"}
128
+
129
+ assert_equal(expected, record)
130
+ end
131
+
132
+ class HashRendered < self
133
+ def test_write
134
+ d = create_driver(config_element("ROOT", "", {"tag" => "fluent.eventlog",
135
+ "render_as_xml" => false}, [
136
+ config_element("storage", "", {
137
+ '@type' => 'local',
138
+ 'persistent' => false
139
+ })
140
+ ]))
141
+
142
+ service = Fluent::Plugin::EventService.new
143
+
144
+ d.run(expect_emits: 1) do
145
+ service.run
146
+ end
147
+
148
+ assert(d.events.length >= 1)
149
+ event = d.events.select {|e| e.last["EventID"] == "65500" }.last
150
+ record = event.last
151
+
152
+ assert_false(d.instance.render_as_xml)
153
+ assert_equal("Application", record["Channel"])
154
+ assert_equal("65500", record["EventID"])
155
+ assert_equal("4", record["Level"])
156
+ assert_equal("fluent-plugins", record["ProviderName"])
157
+ end
158
+ end
159
+
160
+ class PersistBookMark < self
161
+ TEST_PLUGIN_STORAGE_PATH = File.join( File.dirname(File.dirname(__FILE__)), 'tmp', 'in_windows_eventlog2', 'store' )
162
+ CONFIG2 = config_element("ROOT", "", {"tag" => "fluent.eventlog"}, [
163
+ config_element("storage", "", {
164
+ '@type' => 'local',
165
+ '@id' => 'test-02',
166
+ 'path' => File.join(TEST_PLUGIN_STORAGE_PATH,
167
+ 'json', 'test-02.json'),
168
+ 'persistent' => true,
169
+ })
170
+ ])
171
+
172
+ def setup
173
+ FileUtils.rm_rf(TEST_PLUGIN_STORAGE_PATH)
174
+ FileUtils.mkdir_p(File.join(TEST_PLUGIN_STORAGE_PATH, 'json'))
175
+ FileUtils.chmod_R(0755, File.join(TEST_PLUGIN_STORAGE_PATH, 'json'))
176
+ end
177
+
178
+ def test_write
179
+ d = create_driver(CONFIG2)
180
+
181
+ assert !File.exist?(File.join(TEST_PLUGIN_STORAGE_PATH, 'json', 'test-02.json'))
182
+
183
+ service = Fluent::Plugin::EventService.new
184
+
185
+ d.run(expect_emits: 1) do
186
+ service.run
187
+ end
188
+
189
+ assert(d.events.length >= 1)
190
+ event = d.events.select {|e| e.last["EventID"] == "65500" }.last
191
+ record = event.last
192
+
193
+ prev_id = record["EventRecordID"].to_i
194
+ assert_equal("Application", record["Channel"])
195
+ assert_equal("65500", record["EventID"])
196
+ assert_equal("4", record["Level"])
197
+ assert_equal("fluent-plugins", record["ProviderName"])
198
+
199
+ assert File.exist?(File.join(TEST_PLUGIN_STORAGE_PATH, 'json', 'test-02.json'))
200
+
201
+ d2 = create_driver(CONFIG2)
202
+ d2.run(expect_emits: 1) do
203
+ service.run
204
+ end
205
+
206
+ assert(d2.events.length == 1) # should be tailing after previous context.
207
+ event2 = d2.events.last
208
+ record2 = event2.last
209
+
210
+ curr_id = record2["EventRecordID"].to_i
211
+ assert(curr_id > prev_id)
212
+
213
+ assert File.exist?(File.join(TEST_PLUGIN_STORAGE_PATH, 'json', 'test-02.json'))
214
+ end
215
+
216
+ def test_start_with_invalid_bookmark
217
+ invalid_storage_contents = <<-EOS
218
+ <BookmarkList>\r\n <Bookmark Channel='Application' RecordId='20063' IsCurrent='true'/>\r\n
219
+ EOS
220
+ d = create_driver(CONFIG2)
221
+ storage = d.instance.instance_variable_get(:@bookmarks_storage)
222
+ storage.put('application', invalid_storage_contents)
223
+ assert File.exist?(File.join(TEST_PLUGIN_STORAGE_PATH, 'json', 'test-02.json'))
224
+
225
+ d2 = create_driver(CONFIG2)
226
+ assert_raise(Fluent::ConfigError) do
227
+ d2.instance.start
228
+ end
229
+ end
230
+ end
231
+
232
+ def test_write_with_none_parser
233
+ d = create_driver(config_element("ROOT", "", {"tag" => "fluent.eventlog"}, [
234
+ config_element("storage", "", {
235
+ '@type' => 'local',
236
+ 'persistent' => false
237
+ }),
238
+ config_element("parse", "", {
239
+ '@type' => 'none',
240
+ }),
241
+ ]))
242
+
243
+ service = Fluent::Plugin::EventService.new
244
+
245
+ d.run(expect_emits: 1) do
246
+ service.run
247
+ end
248
+
249
+ assert(d.events.length >= 1)
250
+ event = d.events.last
251
+ record = event.last
252
+
253
+ assert do
254
+ # record should be {message: <RAW XML EventLog>}.
255
+ record["message"]
256
+ end
257
+
258
+ assert_true(record.has_key?("Description"))
259
+ assert_true(record.has_key?("EventData"))
260
+ end
261
+ end