Class: Fluent::Plugin::WindowsEventLogInput

Inherits:
Input
  • Object
show all
Defined in:
lib/fluent/plugin/in_windows_eventlog.rb

Constant Summary collapse

DEFAULT_STORAGE_TYPE =
'local'
KEY_MAP =
{"record_number" => [:record_number, :string],
"time_generated" => [:time_generated, :string],
"time_written" => [:time_written, :string],
"event_id" => [:event_id, :string],
"event_type" => [:event_type, :string],
"event_category" => [:category, :string],
"source_name" => [:source, :string],
"computer_name" => [:computer, :string],
"user" => [:user, :string],
"description" => [:description, :string],
"string_inserts" => [:string_inserts, :array]}
GROUP_DELIMITER =
"\r\n\r\n".freeze
RECORD_DELIMITER =
"\r\n\t".freeze
FIELD_DELIMITER =
"\t\t".freeze
NONE_FIELD_DELIMITER =
"\t".freeze

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initializeWindowsEventLogInput

Returns a new instance of WindowsEventLogInput.



44
45
46
47
48
49
# File 'lib/fluent/plugin/in_windows_eventlog.rb', line 44

def initialize
  super
  @chs = []
  @keynames = []
  @tails = {}
end

Instance Attribute Details

#chsObject (readonly)

Returns the value of attribute chs.



42
43
44
# File 'lib/fluent/plugin/in_windows_eventlog.rb', line 42

def chs
  @chs
end

Instance Method Details

#configure(conf) ⇒ Object



51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
# File 'lib/fluent/plugin/in_windows_eventlog.rb', line 51

def configure(conf)
  log.warn "in_windows_eventlog is deprecated. It will be removed in the future version. Please use in_windows_eventlog2 bundled with this plugin instead."
  super
  @chs = @channels.map {|ch| ch.strip.downcase }.uniq
  if @chs.empty?
    raise Fluent::ConfigError, "windows_eventlog: 'channels' parameter is required on windows_eventlog input"
  end
  @keynames = @keys.map {|k| k.strip }.uniq
  if @keynames.empty?
    @keynames = KEY_MAP.keys
  end
  @keynames.delete('string_inserts') if @parse_description

  @tag = tag
  @stop = false
  configure_encoding
  @receive_handlers = if @encoding
                        method(:encode_record)
                      else
                        method(:no_encode_record)
                      end
  @pos_storage = storage_create(usage: "positions")
end

#configure_encodingObject



75
76
77
78
79
80
81
82
83
84
# File 'lib/fluent/plugin/in_windows_eventlog.rb', line 75

def configure_encoding
  unless @encoding
    if @from_encoding
      raise Fluent::ConfigError, "windows_eventlog: 'from_encoding' parameter must be specied with 'encoding' parameter."
    end
  end

  @encoding = parse_encoding_param(@encoding) if @encoding
  @from_encoding = parse_encoding_param(@from_encoding) if @from_encoding
end

#encode_record(record) ⇒ Object



94
95
96
97
98
99
100
101
102
# File 'lib/fluent/plugin/in_windows_eventlog.rb', line 94

def encode_record(record)
  if @encoding
    if @from_encoding
      record.encode!(@encoding, @from_encoding)
    else
      record.force_encoding(@encoding)
    end
  end
end

#escape_channel(ch) ⇒ Object



123
124
125
# File 'lib/fluent/plugin/in_windows_eventlog.rb', line 123

def escape_channel(ch)
  ch.gsub(/[^a-zA-Z0-9]/, '_')
end

#no_encode_record(record) ⇒ Object



104
105
106
# File 'lib/fluent/plugin/in_windows_eventlog.rb', line 104

def no_encode_record(record)
  record
end

#on_notify(ch) ⇒ Object



154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
# File 'lib/fluent/plugin/in_windows_eventlog.rb', line 154

def on_notify(ch)
  begin
    el = Win32::EventLog.open(ch)
  rescue => e
    log.error "Failed to open Windows Event log.", error: e
  end

  current_oldest_record_number = el.oldest_record_number
  current_total_records = el.total_records

  read_start, read_num = @pos_storage.get(ch)

  # if total_records is zero, oldest_record_number has no meaning.
  if current_total_records == 0
    return
  end

  if read_start == 0 && read_num == 0
    @pos_storage.put(ch, [current_oldest_record_number, current_total_records])
    return
  end

  current_end = current_oldest_record_number + current_total_records - 1
  old_end = read_start + read_num - 1

  if current_oldest_record_number < read_start
    # may be a record number rotated.
    current_end += 0xFFFFFFFF
  end

  if current_end < old_end
    # something occured.
    @pos_storage.put(ch, [current_oldest_record_number, current_total_records])
    return
  end

  winlogs = el.read(Win32::EventLog::SEEK_READ | Win32::EventLog::FORWARDS_READ, old_end + 1)
  receive_lines(ch, winlogs)
  @pos_storage.put(ch, [read_start, read_num + winlogs.size])
ensure
  if el
    el.close
  end
end

#parse_desc(record) ⇒ Object



204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
# File 'lib/fluent/plugin/in_windows_eventlog.rb', line 204

def parse_desc(record)
  desc = record.delete('description'.freeze)
  return if desc.nil?

  elems = desc.split(GROUP_DELIMITER)
  record['description_title'] = elems.shift
  elems.each { |elem|
    parent_key = nil
    elem.split(RECORD_DELIMITER).each { |r|
      key, value = if r.index(FIELD_DELIMITER)
                     r.split(FIELD_DELIMITER)
                   else
                     r.split(NONE_FIELD_DELIMITER)
                   end
      key.chop!  # remove ':' from key
      if value.nil?
        parent_key = to_key(key)
      else
        # parsed value sometimes contain unexpected "\t". So remove it.
        value.strip!
        if parent_key.nil?
          record[to_key(key)] = value
        else
          k = "#{parent_key}.#{to_key(key)}"
          record[k] = value
        end
      end
    }
  }
end

#parse_encoding_param(encoding_name) ⇒ Object



86
87
88
89
90
91
92
# File 'lib/fluent/plugin/in_windows_eventlog.rb', line 86

def parse_encoding_param(encoding_name)
  begin
    Encoding.find(encoding_name) if encoding_name
  rescue ArgumentError => e
    raise Fluent::ConfigError, e.message
  end
end

#receive_lines(ch, lines) ⇒ Object



127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
# File 'lib/fluent/plugin/in_windows_eventlog.rb', line 127

def receive_lines(ch, lines)
  return if lines.empty?
  begin
    for r in lines
      h = {"channel" => ch}
      @keynames.each do |k|
        type = KEY_MAP[k][1]
        value = r.send(KEY_MAP[k][0])
        h[k]=case type
             when :string
               @receive_handlers.call(value.to_s)
             when :array
               value.map {|v| @receive_handlers.call(v.to_s)}
             else
               raise "Unknown value type: #{type}"
             end
      end
      parse_desc(h) if @parse_description
      #h = Hash[@keynames.map {|k| [k, r.send(KEY_MAP[k][0]).to_s]}]
      router.emit(@tag, Fluent::Engine.now, h)
    end
  rescue => e
    log.error "unexpected error", error: e
    log.error_backtrace
  end
end

#startObject



108
109
110
111
112
113
114
115
116
117
118
119
120
121
# File 'lib/fluent/plugin/in_windows_eventlog.rb', line 108

def start
  super
  @chs.each do |ch|
    start, num = @pos_storage.get(ch)
    if @read_from_head || (!num || num.zero?)
      el = Win32::EventLog.open(ch)
      @pos_storage.put(ch, [el.oldest_record_number - 1, 1])
      el.close
    end
    timer_execute("in_windows_eventlog_#{escape_channel(ch)}".to_sym, @read_interval) do
      on_notify(ch)
    end
  end
end

#to_key(key) ⇒ Object



235
236
237
238
239
# File 'lib/fluent/plugin/in_windows_eventlog.rb', line 235

def to_key(key)
  key.downcase!
  key.gsub!(' '.freeze, '_'.freeze)
  key
end