fluent-plugin-windows-eventlog 0.2.2 → 0.3.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 +5 -5
- data/.gitignore +14 -14
- data/CHANGELOG.md +16 -0
- data/Gemfile +4 -4
- data/LICENSE.txt +203 -203
- data/README.md +279 -132
- data/Rakefile +10 -10
- data/appveyor.yml +24 -34
- data/fluent-plugin-winevtlog.gemspec +27 -25
- data/lib/fluent/plugin/in_windows_eventlog.rb +234 -234
- data/lib/fluent/plugin/in_windows_eventlog2.rb +169 -0
- data/lib/fluent/plugin/parser_winevt_xml.rb +34 -0
- data/test/data/eventlog.xml +1 -0
- data/test/generate-windows-event.rb +47 -47
- data/test/helper.rb +35 -32
- data/test/plugin/test_in_windows_eventlog2.rb +182 -0
- data/test/plugin/test_in_winevtlog.rb +48 -48
- data/test/plugin/test_parser_winevt_xml.rb +42 -0
- metadata +41 -4
@@ -0,0 +1,169 @@
|
|
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
|
+
"ThreadID" => ["ThreadID", :string],
|
25
|
+
"Channel" => ["Channel", :string],
|
26
|
+
"Computer" => ["Computer", :string],
|
27
|
+
"UserID" => ["UserID", :string],
|
28
|
+
"Version" => ["Version", :string],
|
29
|
+
"Description" => ["Description", :string],
|
30
|
+
"EventData" => ["EventData", :array]}
|
31
|
+
|
32
|
+
config_param :tag, :string
|
33
|
+
config_param :read_interval, :time, default: 2
|
34
|
+
config_param :channels, :array, default: ['application']
|
35
|
+
config_param :keys, :array, default: []
|
36
|
+
config_param :read_from_head, :bool, default: false
|
37
|
+
config_param :parse_description, :bool, default: false
|
38
|
+
|
39
|
+
config_section :storage do
|
40
|
+
config_set_default :usage, "bookmarks"
|
41
|
+
config_set_default :@type, DEFAULT_STORAGE_TYPE
|
42
|
+
config_set_default :persistent, true
|
43
|
+
end
|
44
|
+
|
45
|
+
config_section :parse do
|
46
|
+
config_set_default :@type, 'winevt_xml'
|
47
|
+
config_set_default :estimate_current_event, false
|
48
|
+
end
|
49
|
+
|
50
|
+
def initalize
|
51
|
+
super
|
52
|
+
@chs = []
|
53
|
+
@keynames = []
|
54
|
+
end
|
55
|
+
|
56
|
+
def configure(conf)
|
57
|
+
super
|
58
|
+
@chs = @channels.map {|ch| ch.strip.downcase }.uniq
|
59
|
+
@keynames = @keys.map {|k| k.strip }.uniq
|
60
|
+
if @keynames.empty?
|
61
|
+
@keynames = KEY_MAP.keys
|
62
|
+
end
|
63
|
+
@keynames.delete('EventData') if @parse_description
|
64
|
+
|
65
|
+
@tag = tag
|
66
|
+
@tailing = @read_from_head ? false : true
|
67
|
+
@bookmarks_storage = storage_create(usage: "bookmarks")
|
68
|
+
@parser = parser_create
|
69
|
+
end
|
70
|
+
|
71
|
+
def start
|
72
|
+
super
|
73
|
+
|
74
|
+
@chs.each do |ch|
|
75
|
+
bookmarkXml = @bookmarks_storage.get(ch) || ""
|
76
|
+
subscribe = Winevt::EventLog::Subscribe.new
|
77
|
+
bookmark = Winevt::EventLog::Bookmark.new(bookmarkXml)
|
78
|
+
subscribe.tail = @tailing
|
79
|
+
subscribe.subscribe(ch, "*", bookmark)
|
80
|
+
timer_execute("in_windows_eventlog_#{escape_channel(ch)}".to_sym, @read_interval) do
|
81
|
+
on_notify(ch, subscribe)
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
def escape_channel(ch)
|
87
|
+
ch.gsub(/[^a-zA-Z0-9]/, '_')
|
88
|
+
end
|
89
|
+
|
90
|
+
def on_notify(ch, subscribe)
|
91
|
+
es = Fluent::MultiEventStream.new
|
92
|
+
subscribe.each do |xml, message, string_inserts|
|
93
|
+
@parser.parse(xml) do |time, record|
|
94
|
+
# record.has_key?("EventData") for none parser checking.
|
95
|
+
if record.has_key?("EventData")
|
96
|
+
record["Description"] = message
|
97
|
+
record["EventData"] = string_inserts
|
98
|
+
|
99
|
+
h = {}
|
100
|
+
@keynames.each do |k|
|
101
|
+
type = KEY_MAP[k][1]
|
102
|
+
value = record[KEY_MAP[k][0]]
|
103
|
+
h[k]=case type
|
104
|
+
when :string
|
105
|
+
value.to_s
|
106
|
+
when :array
|
107
|
+
value.map {|v| v.to_s}
|
108
|
+
else
|
109
|
+
raise "Unknown value type: #{type}"
|
110
|
+
end
|
111
|
+
end
|
112
|
+
parse_desc(h) if @parse_description
|
113
|
+
es.add(Fluent::Engine.now, h)
|
114
|
+
else
|
115
|
+
# for none parser
|
116
|
+
es.add(Fluent::Engine.now, record)
|
117
|
+
end
|
118
|
+
end
|
119
|
+
end
|
120
|
+
router.emit_stream(@tag, es)
|
121
|
+
@bookmarks_storage.put(ch, subscribe.bookmark)
|
122
|
+
end
|
123
|
+
|
124
|
+
#### These lines copied from in_windows_eventlog plugin:
|
125
|
+
#### https://github.com/fluent/fluent-plugin-windows-eventlog/blob/528290d896a885c7721f850943daa3a43a015f3d/lib/fluent/plugin/in_windows_eventlog.rb#L192-L232
|
126
|
+
GROUP_DELIMITER = "\r\n\r\n".freeze
|
127
|
+
RECORD_DELIMITER = "\r\n\t".freeze
|
128
|
+
FIELD_DELIMITER = "\t\t".freeze
|
129
|
+
NONE_FIELD_DELIMITER = "\t".freeze
|
130
|
+
|
131
|
+
def parse_desc(record)
|
132
|
+
desc = record.delete("Description".freeze)
|
133
|
+
return if desc.nil?
|
134
|
+
|
135
|
+
elems = desc.split(GROUP_DELIMITER)
|
136
|
+
record['DescriptionTitle'] = elems.shift
|
137
|
+
elems.each { |elem|
|
138
|
+
parent_key = nil
|
139
|
+
elem.split(RECORD_DELIMITER).each { |r|
|
140
|
+
key, value = if r.index(FIELD_DELIMITER)
|
141
|
+
r.split(FIELD_DELIMITER)
|
142
|
+
else
|
143
|
+
r.split(NONE_FIELD_DELIMITER)
|
144
|
+
end
|
145
|
+
key.chop! # remove ':' from key
|
146
|
+
if value.nil?
|
147
|
+
parent_key = to_key(key)
|
148
|
+
else
|
149
|
+
# parsed value sometimes contain unexpected "\t". So remove it.
|
150
|
+
value.strip!
|
151
|
+
if parent_key.nil?
|
152
|
+
record[to_key(key)] = value
|
153
|
+
else
|
154
|
+
k = "#{parent_key}.#{to_key(key)}"
|
155
|
+
record[k] = value
|
156
|
+
end
|
157
|
+
end
|
158
|
+
}
|
159
|
+
}
|
160
|
+
end
|
161
|
+
|
162
|
+
def to_key(key)
|
163
|
+
key.downcase!
|
164
|
+
key.gsub!(' '.freeze, '_'.freeze)
|
165
|
+
key
|
166
|
+
end
|
167
|
+
####
|
168
|
+
end
|
169
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
require 'fluent/plugin/parser'
|
2
|
+
require 'nokogiri'
|
3
|
+
|
4
|
+
module Fluent::Plugin
|
5
|
+
class WinevtXMLparser < Parser
|
6
|
+
Fluent::Plugin.register_parser('winevt_xml', self)
|
7
|
+
|
8
|
+
def parse(text)
|
9
|
+
record = {}
|
10
|
+
doc = Nokogiri::XML(text)
|
11
|
+
system_elem = doc/'Event'/'System'
|
12
|
+
record["ProviderName"] = (system_elem/"Provider").attribute("Name").text rescue nil
|
13
|
+
record["ProviderGUID"] = (system_elem/"Provider").attribute("Guid").text rescue nil
|
14
|
+
record["EventID"] = (system_elem/'EventID').text rescue nil
|
15
|
+
record["Qualifiers"] = (system_elem/'EventID').attribute("Qualifiers").text rescue nil
|
16
|
+
record["Level"] = (system_elem/'Level').text rescue nil
|
17
|
+
record["Task"] = (system_elem/'Task').text rescue nil
|
18
|
+
record["Opcode"] = (system_elem/'Opcode').text rescue nil
|
19
|
+
record["Keywords"] = (system_elem/'Keywords').text rescue nil
|
20
|
+
record["TimeCreated"] = (system_elem/'TimeCreated').attribute("SystemTime").text rescue nil
|
21
|
+
record["EventRecordID"] = (system_elem/'EventRecordID').text rescue nil
|
22
|
+
record["ActivityID"] = (system_elem/'ActivityID').text rescue nil
|
23
|
+
record["RelatedActivityID"] = (system_elem/'Correlation').attribute("ActivityID").text rescue nil
|
24
|
+
record["ThreadID"] = (system_elem/'Execution').attribute("ThreadID").text rescue nil
|
25
|
+
record["Channel"] = (system_elem/'Channel').text rescue nil
|
26
|
+
record["Computer"] = (system_elem/"Computer").text rescue nil
|
27
|
+
record["UserID"] = (system_elem/'Security').attribute("UserID").text rescue nil
|
28
|
+
record["Version"] = (system_elem/'Version').text rescue nil
|
29
|
+
record["EventData"] = [] # These parameters are processed in winevt_c.
|
30
|
+
time = @estimate_current_event ? Fluent::EventTime.now : nil
|
31
|
+
yield time, record
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1 @@
|
|
1
|
+
<Event xmlns='http://schemas.microsoft.com/win/2004/08/events/event'><System><Provider Name='Microsoft-Windows-Security-Auditing' Guid='{54849625-5478-4994-A5BA-3E3B0328C30D}'/><EventID>4624</EventID><Version>2</Version><Level>0</Level><Task>12544</Task><Opcode>0</Opcode><Keywords>0x8020000000000000</Keywords><TimeCreated SystemTime='2019-06-13T09:21:23.345889600Z'/><EventRecordID>80688</EventRecordID><Correlation ActivityID='{587F0743-1F71-0006-5007-7F58711FD501}'/><Execution ProcessID='912' ThreadID='24708'/><Channel>Security</Channel><Computer>Fluentd-Developing-Windows</Computer><Security/></System><EventData><Data Name='SubjectUserSid'>S-1-5-18</Data><Data Name='SubjectUserName'>Fluentd-Developing-Windows$</Data><Data Name='SubjectDomainName'>WORKGROUP</Data><Data Name='SubjectLogonId'>0x3e7</Data><Data Name='TargetUserSid'>S-1-5-18</Data><Data Name='TargetUserName'>SYSTEM</Data><Data Name='TargetDomainName'>NT AUTHORITY</Data><Data Name='TargetLogonId'>0x3e7</Data><Data Name='LogonType'>5</Data><Data Name='LogonProcessName'>Advapi </Data><Data Name='AuthenticationPackageName'>Negotiate</Data><Data Name='WorkstationName'>-</Data><Data Name='LogonGuid'>{00000000-0000-0000-0000-000000000000}</Data><Data Name='TransmittedServices'>-</Data><Data Name='LmPackageName'>-</Data><Data Name='KeyLength'>0</Data><Data Name='ProcessId'>0x344</Data><Data Name='ProcessName'>C:\Windows\System32\services.exe</Data><Data Name='IpAddress'>-</Data><Data Name='IpPort'>-</Data><Data Name='ImpersonationLevel'>%%1833</Data><Data Name='RestrictedAdminMode'>-</Data><Data Name='TargetOutboundUserName'>-</Data><Data Name='TargetOutboundDomainName'>-</Data><Data Name='VirtualAccount'>%%1843</Data><Data Name='TargetLinkedLogonId'>0x0</Data><Data Name='ElevatedToken'>%%1842</Data></EventData></Event>
|
@@ -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
|
data/test/helper.rb
CHANGED
@@ -1,32 +1,35 @@
|
|
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/
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
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/test/driver/parser'
|
27
|
+
require 'fluent/plugin/in_windows_eventlog'
|
28
|
+
require 'fluent/plugin/in_windows_eventlog2'
|
29
|
+
require 'fluent/plugin/parser_winevt_xml'
|
30
|
+
|
31
|
+
class Test::Unit::TestCase
|
32
|
+
end
|
33
|
+
require 'fluent/test/helpers'
|
34
|
+
|
35
|
+
include Fluent::Test::Helpers
|
@@ -0,0 +1,182 @@
|
|
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
|
+
end
|
29
|
+
|
30
|
+
def test_parse_desc
|
31
|
+
d = create_driver
|
32
|
+
desc =<<-DESC
|
33
|
+
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
|
34
|
+
DESC
|
35
|
+
h = {"Description" => desc}
|
36
|
+
expected = {"DescriptionTitle" => "A user's local group membership was enumerated.",
|
37
|
+
"subject.security_id" => "S-X-Y-XX-WWWWWW-VVVV",
|
38
|
+
"subject.account_name" => "Administrator",
|
39
|
+
"subject.account_domain" => "DESKTOP-FLUENTTEST",
|
40
|
+
"subject.logon_id" => "0x3185B1",
|
41
|
+
"user.security_id" => "S-X-Y-XX-WWWWWW-VVVV",
|
42
|
+
"user.account_name" => "Administrator",
|
43
|
+
"user.account_domain" => "DESKTOP-FLUENTTEST",
|
44
|
+
"process_information.process_id" => "0x50b8",
|
45
|
+
"process_information.process_name" => "C:\\msys64\\usr\\bin\\make.exe"}
|
46
|
+
d.instance.parse_desc(h)
|
47
|
+
assert_equal(expected, h)
|
48
|
+
end
|
49
|
+
|
50
|
+
def test_write
|
51
|
+
d = create_driver
|
52
|
+
|
53
|
+
service = Fluent::Plugin::EventService.new
|
54
|
+
|
55
|
+
d.run(expect_emits: 1) do
|
56
|
+
service.run
|
57
|
+
end
|
58
|
+
|
59
|
+
assert(d.events.length >= 1)
|
60
|
+
event = d.events.last
|
61
|
+
record = event.last
|
62
|
+
|
63
|
+
assert_equal("Application", record["Channel"])
|
64
|
+
assert_equal("65500", record["EventID"])
|
65
|
+
assert_equal("4", record["Level"])
|
66
|
+
assert_equal("fluent-plugins", record["ProviderName"])
|
67
|
+
end
|
68
|
+
|
69
|
+
CONFIG_KEYS = config_element("ROOT", "", {
|
70
|
+
"tag" => "fluent.eventlog",
|
71
|
+
"keys" => ["EventID", "Level", "Channel", "ProviderName"]
|
72
|
+
}, [
|
73
|
+
config_element("storage", "", {
|
74
|
+
'@type' => 'local',
|
75
|
+
'persistent' => false
|
76
|
+
})
|
77
|
+
])
|
78
|
+
def test_write_with_keys
|
79
|
+
d = create_driver(CONFIG_KEYS)
|
80
|
+
|
81
|
+
service = Fluent::Plugin::EventService.new
|
82
|
+
|
83
|
+
d.run(expect_emits: 1) do
|
84
|
+
service.run
|
85
|
+
end
|
86
|
+
|
87
|
+
assert(d.events.length >= 1)
|
88
|
+
event = d.events.last
|
89
|
+
record = event.last
|
90
|
+
|
91
|
+
expected = {"EventID" => "65500",
|
92
|
+
"Level" => "4",
|
93
|
+
"Channel" => "Application",
|
94
|
+
"ProviderName" => "fluent-plugins"}
|
95
|
+
|
96
|
+
assert_equal(expected, record)
|
97
|
+
end
|
98
|
+
|
99
|
+
class PersistBookMark < self
|
100
|
+
TEST_PLUGIN_STORAGE_PATH = File.join( File.dirname(File.dirname(__FILE__)), 'tmp', 'in_windows_eventlog2', 'store' )
|
101
|
+
CONFIG2 = config_element("ROOT", "", {"tag" => "fluent.eventlog"}, [
|
102
|
+
config_element("storage", "", {
|
103
|
+
'@type' => 'local',
|
104
|
+
'@id' => 'test-02',
|
105
|
+
'path' => File.join(TEST_PLUGIN_STORAGE_PATH,
|
106
|
+
'json', 'test-02.json'),
|
107
|
+
'persistent' => true,
|
108
|
+
})
|
109
|
+
])
|
110
|
+
|
111
|
+
def setup
|
112
|
+
FileUtils.rm_rf(TEST_PLUGIN_STORAGE_PATH)
|
113
|
+
FileUtils.mkdir_p(File.join(TEST_PLUGIN_STORAGE_PATH, 'json'))
|
114
|
+
FileUtils.chmod_R(0755, File.join(TEST_PLUGIN_STORAGE_PATH, 'json'))
|
115
|
+
end
|
116
|
+
|
117
|
+
def test_write
|
118
|
+
d = create_driver(CONFIG2)
|
119
|
+
|
120
|
+
assert !File.exist?(File.join(TEST_PLUGIN_STORAGE_PATH, 'json', 'test-02.json'))
|
121
|
+
|
122
|
+
service = Fluent::Plugin::EventService.new
|
123
|
+
|
124
|
+
d.run(expect_emits: 1) do
|
125
|
+
service.run
|
126
|
+
end
|
127
|
+
|
128
|
+
assert(d.events.length >= 1)
|
129
|
+
event = d.events.last
|
130
|
+
record = event.last
|
131
|
+
|
132
|
+
prev_id = record["EventRecordID"].to_i
|
133
|
+
assert_equal("Application", record["Channel"])
|
134
|
+
assert_equal("65500", record["EventID"])
|
135
|
+
assert_equal("4", record["Level"])
|
136
|
+
assert_equal("fluent-plugins", record["ProviderName"])
|
137
|
+
|
138
|
+
assert File.exist?(File.join(TEST_PLUGIN_STORAGE_PATH, 'json', 'test-02.json'))
|
139
|
+
|
140
|
+
d2 = create_driver(CONFIG2)
|
141
|
+
d2.run(expect_emits: 1) do
|
142
|
+
service.run
|
143
|
+
end
|
144
|
+
|
145
|
+
assert(d2.events.length == 1) # should be tailing after previous context.
|
146
|
+
event2 = d2.events.last
|
147
|
+
record2 = event2.last
|
148
|
+
|
149
|
+
curr_id = record2["EventRecordID"].to_i
|
150
|
+
assert(curr_id > prev_id)
|
151
|
+
|
152
|
+
assert File.exist?(File.join(TEST_PLUGIN_STORAGE_PATH, 'json', 'test-02.json'))
|
153
|
+
end
|
154
|
+
end
|
155
|
+
|
156
|
+
def test_write_with_none_parser
|
157
|
+
d = create_driver(config_element("ROOT", "", {"tag" => "fluent.eventlog"}, [
|
158
|
+
config_element("storage", "", {
|
159
|
+
'@type' => 'local',
|
160
|
+
'persistent' => false
|
161
|
+
}),
|
162
|
+
config_element("parse", "", {
|
163
|
+
'@type' => 'none',
|
164
|
+
}),
|
165
|
+
]))
|
166
|
+
|
167
|
+
service = Fluent::Plugin::EventService.new
|
168
|
+
|
169
|
+
d.run(expect_emits: 1) do
|
170
|
+
service.run
|
171
|
+
end
|
172
|
+
|
173
|
+
assert(d.events.length >= 1)
|
174
|
+
event = d.events.last
|
175
|
+
record = event.last
|
176
|
+
|
177
|
+
assert do
|
178
|
+
# record should be {message: <RAW XML EventLog>}.
|
179
|
+
record["message"]
|
180
|
+
end
|
181
|
+
end
|
182
|
+
end
|