logstash-input-proc 0.1.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +5 -0
- data/ElasticSearch/README.md +9 -0
- data/ElasticSearch/templates/loadavg.json +55 -0
- data/ElasticSearch/templates/meminfo.json +162 -0
- data/ElasticSearch/templates/vmstats.json +393 -0
- data/Gemfile +3 -0
- data/Kibana/READEME.md +2 -0
- data/Kibana/export.json +526 -0
- data/README.md +130 -0
- data/Rakefile +1 -0
- data/lib/logstash/inputs/proc.rb +562 -0
- data/logstash-input-proc.gemspec +25 -0
- data/logstash.conf +55 -0
- data/spec/inputs/proc_spec.rb +1 -0
- metadata +122 -0
data/README.md
ADDED
@@ -0,0 +1,130 @@
|
|
1
|
+
![](https://github.com/eperry/logstash-input-proc/wiki/MemInfoDashboard.png)
|
2
|
+
|
3
|
+
# Logstash Plugin
|
4
|
+
|
5
|
+
This is a plugin for [Logstash](https://github.com/elasticsearch/logstash).
|
6
|
+
|
7
|
+
This plugin is to read the /proc virtual file system , decode the files in it.
|
8
|
+
I am using the following pages for reference
|
9
|
+
|
10
|
+
- http://man7.org/linux/man-pages/man5/proc.5.html
|
11
|
+
|
12
|
+
|
13
|
+
|
14
|
+
|
15
|
+
## Documentation
|
16
|
+
|
17
|
+
|
18
|
+
### 1. Plugin Developement and Testing
|
19
|
+
|
20
|
+
#### Code
|
21
|
+
- To get started, you'll need JRuby with the Bundler gem installed.
|
22
|
+
```sh
|
23
|
+
bundle install
|
24
|
+
```
|
25
|
+
|
26
|
+
- Then clone this repo
|
27
|
+
- You will need to either clone the logstash repo or download the binary
|
28
|
+
|
29
|
+
|
30
|
+
|
31
|
+
### 2. Running the unpublished Plugin in Logstash
|
32
|
+
|
33
|
+
#### 2.1 Run in a local Logstash clone
|
34
|
+
|
35
|
+
- Edit Logstash `Gemfile` and add the local plugin path, for example:
|
36
|
+
```ruby
|
37
|
+
gem "logstash-input-proc", :path => "/your/local/logstash-input-proc"
|
38
|
+
```
|
39
|
+
- Install plugin
|
40
|
+
```sh
|
41
|
+
bin/plugin install --no-verify
|
42
|
+
```
|
43
|
+
- install Ruby Debug
|
44
|
+
```sh
|
45
|
+
bin/plugin install logstash-codec-rubydebug
|
46
|
+
```
|
47
|
+
- Run Logstash with your plugin
|
48
|
+
```sh
|
49
|
+
bin/logstash -e 'input {proc {interval=>60}} output { stdout{ codec=>"rubydebug"}}'
|
50
|
+
```
|
51
|
+
At this point any modifications to the plugin code will be applied to this local Logstash setup. After modifying the plugin, simply rerun Logstash.
|
52
|
+
|
53
|
+
#### 2.2 Run in an installed Logstash
|
54
|
+
|
55
|
+
You can use the same **2.1** method to run your plugin in an installed Logstash by editing its `Gemfile` and pointing the `:path` to your local plugin development directory or you can build the gem and install it using:
|
56
|
+
|
57
|
+
- Build your plugin gem
|
58
|
+
```sh
|
59
|
+
gem build logstash-output-proc.gemspec
|
60
|
+
```
|
61
|
+
- Install the plugin from the Logstash home
|
62
|
+
```sh
|
63
|
+
bin/plugin install /your/local/plugin/logstash-input-proc.gem
|
64
|
+
```
|
65
|
+
- Start Logstash and proceed to test the plugin
|
66
|
+
-
|
67
|
+
# Example Config all features enabled
|
68
|
+
```ruby
|
69
|
+
input {
|
70
|
+
proc {
|
71
|
+
interval=>60
|
72
|
+
vmstats =>{ }
|
73
|
+
loadavg =>{ }
|
74
|
+
meminfo =>{ }
|
75
|
+
pidstats =>{
|
76
|
+
user => "root"
|
77
|
+
}
|
78
|
+
|
79
|
+
}
|
80
|
+
}
|
81
|
+
|
82
|
+
output {
|
83
|
+
stdout{
|
84
|
+
codec=>"rubydebug"
|
85
|
+
}
|
86
|
+
}
|
87
|
+
```
|
88
|
+
#Example Minimal
|
89
|
+
|
90
|
+
```ruby
|
91
|
+
input {
|
92
|
+
proc {
|
93
|
+
interval=>60
|
94
|
+
meminfo =>{ }
|
95
|
+
}
|
96
|
+
}
|
97
|
+
|
98
|
+
output {
|
99
|
+
stdout{
|
100
|
+
codec=>"rubydebug"
|
101
|
+
}
|
102
|
+
}
|
103
|
+
```
|
104
|
+
|
105
|
+
## 3.0 Kibana Dashboards
|
106
|
+
|
107
|
+
Still a work in progress but I have saved a copy of the Kibana 4.1 dashboards I have created
|
108
|
+
in the ~/Kibana Directory, you should be able to import them from the Kibana->settings->Objects pages
|
109
|
+
|
110
|
+
These dashboards are right now a way of me validating the data loaded in elasticsearch is usable and provide an example for others to work off of. They work with the setup
|
111
|
+
of elasticsearch as defined below.
|
112
|
+
|
113
|
+
|
114
|
+
## 4.0 Elasticsearch Templates
|
115
|
+
|
116
|
+
In the ~/ElasitcSearch Directory are all the Elasticsearch templates I am developing to work with this plugin.
|
117
|
+
While they may not be exactly what you need they are a good start.
|
118
|
+
|
119
|
+
I load them via the ${ES_HOME/config/templates directory but feel free to load them in your preffered way
|
120
|
+
|
121
|
+
These templates are based on the fact that your indexes for the data are created like so:
|
122
|
+
```
|
123
|
+
output {
|
124
|
+
elasticsearch {
|
125
|
+
host => localhost
|
126
|
+
index => "%{type}-%{+YYYY.MM.dd}"
|
127
|
+
}
|
128
|
+
}
|
129
|
+
```
|
130
|
+
|
data/Rakefile
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require "logstash/devutils/rake"
|
@@ -0,0 +1,562 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
require "logstash/inputs/base"
|
3
|
+
require "logstash/namespace"
|
4
|
+
require "stud/interval"
|
5
|
+
require "socket" # for Socket.gethostname
|
6
|
+
require 'etc'
|
7
|
+
|
8
|
+
# Generate a repeating message.
|
9
|
+
#
|
10
|
+
# This plugin is intented only as an example.
|
11
|
+
|
12
|
+
class LogStash::Inputs::Proc < LogStash::Inputs::Base
|
13
|
+
config_name "proc"
|
14
|
+
|
15
|
+
# If undefined, Logstash will complain, even if codec is unused.
|
16
|
+
default :codec, "plain"
|
17
|
+
|
18
|
+
# The message string to use in the event.
|
19
|
+
#config :message, :validate => :string, :default => "Hello World!"
|
20
|
+
|
21
|
+
# Set how frequently messages should be sent.
|
22
|
+
#
|
23
|
+
# The default, `60`, means send a message every 60 second.
|
24
|
+
config :interval, :validate => :number, :default => 60
|
25
|
+
config :vmstats, :validate => :hash
|
26
|
+
config :loadavg, :validate => :hash
|
27
|
+
config :meminfo, :validate => :hash
|
28
|
+
config :pidstats, :validate => :hash
|
29
|
+
config :diskstats, :validate => :hash
|
30
|
+
config :mounts, :validate => :hash
|
31
|
+
config :netdev, :validate => :hash
|
32
|
+
config :cpuinfo, :validate => :hash
|
33
|
+
config :crypto, :validate => :hash
|
34
|
+
config :wireless, :validate => :hash
|
35
|
+
config :sysipcshm, :validate => :hash
|
36
|
+
|
37
|
+
public
|
38
|
+
def register
|
39
|
+
@host = Socket.gethostname
|
40
|
+
@logger.info("Registering Proc Input", :type => @type, :interval => @interval)
|
41
|
+
|
42
|
+
end # def register
|
43
|
+
|
44
|
+
def readVmStats(queue)
|
45
|
+
file = Pathname.new("/proc/vmstat")
|
46
|
+
lines = file.readlines
|
47
|
+
vmstats = {}
|
48
|
+
lines.each { |line|
|
49
|
+
#@logger.info? && @logger.info("LINE: "+line)
|
50
|
+
m = /(\w+)\s+([\.\d]+)/.match(line)
|
51
|
+
if (m && m.length >= 3 )
|
52
|
+
vmstats[m[1]]=m[2].to_i
|
53
|
+
end
|
54
|
+
}
|
55
|
+
event = LogStash::Event.new( 'vmstats'=> vmstats, "file" => file.to_s,"host" => @host, "type" => "vmstats" )
|
56
|
+
decorate(event)
|
57
|
+
queue << event
|
58
|
+
|
59
|
+
end
|
60
|
+
|
61
|
+
def readLoadAverage(queue)
|
62
|
+
file = Pathname.new("/proc/loadavg")
|
63
|
+
lines = file.readlines
|
64
|
+
loadavg = {}
|
65
|
+
lines.each { |line|
|
66
|
+
m = /([\d\.]+)\s+([\d\.]+)\s+([\d\.])+\s+(\d+)\/(\d+)\s+(\d+)/.match(line)
|
67
|
+
if (m && m.length >=6 )
|
68
|
+
loadavg["1minute"] = m[1].to_i
|
69
|
+
loadavg["10minute"] = m[2].to_i
|
70
|
+
loadavg["15minute"] = m[3].to_i
|
71
|
+
loadavg["runnable"] = m[4].to_i
|
72
|
+
loadavg["existing"] = m[5].to_i
|
73
|
+
loadavg["lastcreatedpid"] = m[6].to_i
|
74
|
+
event = LogStash::Event.new( "loadavg" => loadavg, "file" => file.to_s,"host" => @host, "type" => "loadavg" )
|
75
|
+
decorate(event)
|
76
|
+
queue << event
|
77
|
+
end
|
78
|
+
|
79
|
+
}
|
80
|
+
end
|
81
|
+
|
82
|
+
def readMemInfo(queue)
|
83
|
+
file = Pathname.new("/proc/meminfo")
|
84
|
+
lines = file.readlines
|
85
|
+
meminfo={}
|
86
|
+
lines.each { |line|
|
87
|
+
m = /(\w+):\s+(\d+)/.match(line)
|
88
|
+
if (m && m.length >=3 )
|
89
|
+
meminfo[m[1]] = m[2].to_i
|
90
|
+
#else
|
91
|
+
# puts("#"+m.to_s)
|
92
|
+
end
|
93
|
+
}
|
94
|
+
meminfo["CalcMemUsed"]=meminfo["MemTotal"]-meminfo["MemFree"]
|
95
|
+
event = LogStash::Event.new("meminfo"=>meminfo, "file" => file.to_s,"host" => @host, "type" => "meminfo" )
|
96
|
+
decorate(event)
|
97
|
+
queue << event
|
98
|
+
|
99
|
+
end
|
100
|
+
|
101
|
+
def readPidStats(queue)
|
102
|
+
@logger.info? && @logger.info("in " + $0)
|
103
|
+
fuid = -1
|
104
|
+
|
105
|
+
if @pidstats.has_key?("user")
|
106
|
+
fuid = Etc.getpwnam(@pidstats["user"]).uid
|
107
|
+
@logger.info? && @logger.info("Filtering userid =" + @pidstats["user"] )
|
108
|
+
end
|
109
|
+
process = Hash.new
|
110
|
+
#Loosely based on the GEM ProcTable which was based on the Perl Module ProcTable
|
111
|
+
Dir.foreach("/proc"){ |file|
|
112
|
+
next if file =~ /\D/ # Skip non-numeric directories
|
113
|
+
if fuid >= 0
|
114
|
+
fileUid = File.stat("/proc/"+file).uid
|
115
|
+
next if fileUid != fuid
|
116
|
+
end
|
117
|
+
|
118
|
+
# Get /proc/<pid>/cmdline information. Strip out embedded nulls.
|
119
|
+
begin
|
120
|
+
data = IO.read("/proc/#{file}/cmdline").tr("\000", ' ').strip
|
121
|
+
process["cmdline"] = data
|
122
|
+
rescue
|
123
|
+
next # Process terminated, on to the next process
|
124
|
+
end
|
125
|
+
|
126
|
+
# Get /proc/<pid>/cwd information
|
127
|
+
process["cwd"] = File.readlink("/proc/#{file}/cwd") rescue nil
|
128
|
+
|
129
|
+
# Get /proc/<pid>/environ information. Environment information
|
130
|
+
# is represented as a Hash, with the environment variable as the
|
131
|
+
# key and its value as the hash value.
|
132
|
+
process["environ"] = Hash.new
|
133
|
+
|
134
|
+
begin
|
135
|
+
IO.read("/proc/#{file}/environ").split("\0").each{ |str|
|
136
|
+
key, value = str.split('=')
|
137
|
+
process["environ"][key] = value
|
138
|
+
}
|
139
|
+
rescue Errno::EACCES, Errno::ESRCH, Errno::ENOENT
|
140
|
+
# Ignore and move on.
|
141
|
+
end
|
142
|
+
|
143
|
+
# Get /proc/<pid>/exe information
|
144
|
+
process["exe"] = File.readlink("/proc/#{file}/exe") rescue nil
|
145
|
+
|
146
|
+
# Get /proc/<pid>/fd information. File descriptor information
|
147
|
+
# is represented as a Hash, with the fd as the key, and its
|
148
|
+
# symlink as the value.
|
149
|
+
process["fd"] = Hash.new
|
150
|
+
|
151
|
+
begin
|
152
|
+
Dir.foreach("/proc/#{file}/fd/") { |fd|
|
153
|
+
process["fd"][fd] = File.readlink("/proc/#{file}/fd/"+fd) rescue nil
|
154
|
+
}
|
155
|
+
rescue
|
156
|
+
process["fd"] = ""
|
157
|
+
# # Ignore and move on
|
158
|
+
end
|
159
|
+
|
160
|
+
# Get /proc/<pid>/root information
|
161
|
+
process["root"] = File.readlink("/proc/#{file}/root") rescue nil
|
162
|
+
|
163
|
+
# Get /proc/<pid>/stat information
|
164
|
+
stat = IO.read("/proc/#{file}/stat") rescue next
|
165
|
+
|
166
|
+
# Get number of LWP, one directory for each in /proc/<pid>/task/
|
167
|
+
# Every process has at least one thread, so if we fail to read the task directory, set nlwp to 1.
|
168
|
+
process["nlwp"] = Dir.glob("/proc/#{file}/task/*").length rescue process["nlwp"] = 1
|
169
|
+
|
170
|
+
# Deal with spaces in comm name. Courtesy of Ara Howard.
|
171
|
+
re = %r/\([^\)]+\)/
|
172
|
+
comm = stat[re]
|
173
|
+
comm.tr!(' ', '-')
|
174
|
+
stat[re] = comm
|
175
|
+
|
176
|
+
stat = stat.split
|
177
|
+
|
178
|
+
process["pid"] = stat[0].to_i
|
179
|
+
process["comm"] = stat[1].tr('()','') # Remove parens
|
180
|
+
process["state"] = stat[2]
|
181
|
+
process["ppid"] = stat[3].to_i
|
182
|
+
process["pgrp"] = stat[4].to_i
|
183
|
+
process["session"] = stat[5].to_i
|
184
|
+
process["tty_nr"] = stat[6].to_i
|
185
|
+
process["tpgid"] = stat[7].to_i
|
186
|
+
process["flags"] = stat[8].to_i
|
187
|
+
process["minflt"] = stat[9].to_i
|
188
|
+
process["cminflt"] = stat[10].to_i
|
189
|
+
process["majflt"] = stat[11].to_i
|
190
|
+
process["cmajflt"] = stat[12].to_i
|
191
|
+
process["utime"] = stat[13].to_i
|
192
|
+
process["stime"] = stat[14].to_i
|
193
|
+
process["cutime"] = stat[15].to_i
|
194
|
+
process["cstime"] = stat[16].to_i
|
195
|
+
process["priority"] = stat[17].to_i
|
196
|
+
process["nice"] = stat[18].to_i
|
197
|
+
# Skip 19
|
198
|
+
process["itrealvalue"] = stat[20].to_i
|
199
|
+
process["starttime"] = stat[21].to_i
|
200
|
+
process["vsize"] = stat[22].to_i
|
201
|
+
process["rss"] = stat[23].to_i
|
202
|
+
process["rlim"] = stat[24].to_i
|
203
|
+
process["startcode"] = stat[25].to_i
|
204
|
+
process["endcode"] = stat[26].to_i
|
205
|
+
process["startstack"] = stat[27].to_i
|
206
|
+
process["kstkesp"] = stat[28].to_i
|
207
|
+
process["kstkeip"] = stat[29].to_i
|
208
|
+
process["signal"] = stat[30].to_i
|
209
|
+
process["blocked"] = stat[31].to_i
|
210
|
+
process["sigignore"] = stat[32].to_i
|
211
|
+
process["sigcatch"] = stat[33].to_i
|
212
|
+
process["wchan"] = stat[34].to_i
|
213
|
+
process["nswap"] = stat[35].to_i
|
214
|
+
process["cnswap"] = stat[36].to_i
|
215
|
+
process["exit_signal"] = stat[37].to_i
|
216
|
+
process["processor"] = stat[38].to_i
|
217
|
+
process["rt_priority"] = stat[39].to_i
|
218
|
+
process["policy"] = stat[40].to_i
|
219
|
+
# Get /proc/<pid>/status information (name, uid, euid, gid, egid)
|
220
|
+
begin
|
221
|
+
IO.foreach("/proc/#{file}/status") do |line|
|
222
|
+
case line
|
223
|
+
when /Name:\s*?(\w+)/
|
224
|
+
process["name"] = $1
|
225
|
+
when /Uid:\s*?(\d+)\s*?(\d+)/
|
226
|
+
process["uid"] = $1.to_i
|
227
|
+
process["euid"] = $2.to_i
|
228
|
+
when /Gid:\s*?(\d+)\s*?(\d+)/
|
229
|
+
process["gid"] = $1.to_i
|
230
|
+
process["egid"] = $2.to_i
|
231
|
+
end
|
232
|
+
end
|
233
|
+
rescue Errno::ESRCH, Errno::ENOENT
|
234
|
+
next
|
235
|
+
end
|
236
|
+
|
237
|
+
# If cmdline is empty use comm instead
|
238
|
+
process["cmdline"] = process["comm"] if process["cmdline.empty?"]
|
239
|
+
|
240
|
+
|
241
|
+
event = LogStash::Event.new( "file" => "/proc" ,"host" => @host, "type" => "pidstats" , "process" => process);
|
242
|
+
decorate(event)
|
243
|
+
queue << event
|
244
|
+
|
245
|
+
}
|
246
|
+
|
247
|
+
|
248
|
+
end
|
249
|
+
def readDiskStats(queue)
|
250
|
+
# 1 - major number
|
251
|
+
# 2 - minor mumber
|
252
|
+
# 3 - device name
|
253
|
+
# 4 - reads completed successfully
|
254
|
+
# 5 - reads merged
|
255
|
+
# 6 - sectors read
|
256
|
+
# 7 - time spent reading (ms)
|
257
|
+
# 8 - writes completed
|
258
|
+
# 9 - writes merged
|
259
|
+
#10 - sectors written
|
260
|
+
#11 - time spent writing (ms)
|
261
|
+
#12 - I/Os currently in progress
|
262
|
+
#13 - time spent doing I/Os (ms)
|
263
|
+
#14 - weighted time spent doing I/Os (ms)
|
264
|
+
file = Pathname.new("/proc/diskstats")
|
265
|
+
lines = file.readlines
|
266
|
+
lines.each { |line|
|
267
|
+
#@logger.info? && @logger.info("LINE: "+line)
|
268
|
+
m = line.split(/\s+/)
|
269
|
+
if (m && m.length >= 13 )
|
270
|
+
event = LogStash::Event.new("raw"=>line,
|
271
|
+
"major" => m[1].to_i,
|
272
|
+
"minor" => m[2].to_i,
|
273
|
+
"dev" => m[3],
|
274
|
+
"readsCompleted" => m[4].to_i,
|
275
|
+
"readsMerged" => m[5].to_i,
|
276
|
+
"sectorsRead" => m[6].to_i,
|
277
|
+
"readsTimeSpentMS" => m[7].to_i,
|
278
|
+
"writesCompleted" => m[8].to_i,
|
279
|
+
"writesMerged" => m[9].to_i,
|
280
|
+
"sectorsWritten" => m[10].to_i,
|
281
|
+
"writesTimeSpentMS" => m[11].to_i,
|
282
|
+
"iosInProgress" => m[12].to_i,
|
283
|
+
"ioTimeSpentMS" => m[13].to_i,
|
284
|
+
"ioWeightedTimeSpentMS" => m[14].to_i,
|
285
|
+
"file" => file.to_s,
|
286
|
+
"host" => @host,
|
287
|
+
"type" => "diskstats" )
|
288
|
+
decorate(event)
|
289
|
+
queue << event
|
290
|
+
end
|
291
|
+
}
|
292
|
+
|
293
|
+
end
|
294
|
+
def readMounts(queue)
|
295
|
+
#The 1st column specifies the device that is mounted.
|
296
|
+
#The 2nd column reveals the mount point.
|
297
|
+
#The 3rd column tells the file-system type.
|
298
|
+
#The 4th column tells you if it is mounted read-only (ro) or read-write (rw).
|
299
|
+
#The 5th and 6th columns are dummy values
|
300
|
+
file = Pathname.new("/proc/mounts")
|
301
|
+
lines = file.readlines
|
302
|
+
lines.each { |line|
|
303
|
+
#@logger.info? && @logger.info("LINE: "+line)
|
304
|
+
m = line.split(/\s+/)
|
305
|
+
if (m && m.length >= 6 )
|
306
|
+
event = LogStash::Event.new("raw"=>line,
|
307
|
+
"device" => m[0],
|
308
|
+
"mountpoint" => m[1],
|
309
|
+
"fsType" => m[2],
|
310
|
+
"flagsRaw" => m[3],
|
311
|
+
"flags" => m[3].split(/\,/),
|
312
|
+
"dummy1" => m[4],
|
313
|
+
"dummy2" => m[5],
|
314
|
+
"file" => file.to_s,
|
315
|
+
"host" => @host,
|
316
|
+
"type" => "mounts" )
|
317
|
+
decorate(event)
|
318
|
+
queue << event
|
319
|
+
end
|
320
|
+
}
|
321
|
+
|
322
|
+
end
|
323
|
+
def readNetDev(queue)
|
324
|
+
# face |bytes packets errs drop fifo frame compressed multicast|bytes packets errs drop fifo colls carrier compressed
|
325
|
+
|
326
|
+
file = Pathname.new("/proc/net/dev")
|
327
|
+
lines = file.readlines
|
328
|
+
junk = lines.shift
|
329
|
+
junk = lines.shift
|
330
|
+
lines.each { |line|
|
331
|
+
#@logger.info? && @logger.info("LINE: "+line)
|
332
|
+
m = line.strip.split(/[:\s]+/)
|
333
|
+
if (m && m.length >= 17 )
|
334
|
+
event = LogStash::Event.new("raw"=>line,
|
335
|
+
"iface" => m[0],
|
336
|
+
"rxbytes" => m[1].to_i,
|
337
|
+
"rxpackets" => m[2].to_i,
|
338
|
+
"rxerrors" => m[3].to_i,
|
339
|
+
"rxdrops" => m[4].to_i,
|
340
|
+
"rxfifo" => m[5].to_i,
|
341
|
+
"rxframe" => m[6].to_i,
|
342
|
+
"rxcompressed" => m[7].to_i,
|
343
|
+
"rxmulticast" => m[8].to_i,
|
344
|
+
"txbytes" => m[9].to_i,
|
345
|
+
"txpackets" => m[10].to_i,
|
346
|
+
"txerrors" => m[11].to_i,
|
347
|
+
"txdrops" => m[12].to_i,
|
348
|
+
"txfifo" => m[13].to_i,
|
349
|
+
"txframe" => m[14].to_i,
|
350
|
+
"txcompressed" => m[15].to_i,
|
351
|
+
"txmulticast" => m[16].to_i,
|
352
|
+
"file" => file.to_s,
|
353
|
+
"host" => @host,
|
354
|
+
"type" => "netdev" )
|
355
|
+
decorate(event)
|
356
|
+
queue << event
|
357
|
+
end
|
358
|
+
}
|
359
|
+
|
360
|
+
end
|
361
|
+
def readCpuInfo(queue)
|
362
|
+
# face |bytes packets errs drop fifo frame compressed multicast|bytes packets errs drop fifo colls carrier compressed
|
363
|
+
cpuinfo = Hash.new
|
364
|
+
file = Pathname.new("/proc/cpuinfo")
|
365
|
+
lines = file.readlines
|
366
|
+
lines.each { |line|
|
367
|
+
#@logger.info? && @logger.info("LINE: "+line)
|
368
|
+
if ( line.length == 1 )
|
369
|
+
event = LogStash::Event.new(
|
370
|
+
"cpuinfo" => cpuinfo,
|
371
|
+
"file" => file.to_s,
|
372
|
+
"host" => @host,
|
373
|
+
"type" => "cpuinfo" )
|
374
|
+
decorate(event)
|
375
|
+
queue << event
|
376
|
+
cpuinfo = Hash.new
|
377
|
+
next
|
378
|
+
end
|
379
|
+
m = line.strip.split(/\s+:\s+/)
|
380
|
+
if ( m && m.length >= 2 )
|
381
|
+
if ( "flags" == m[0] )
|
382
|
+
cpuinfo[m[0]] = m[1].strip.split(/\s+/)
|
383
|
+
next
|
384
|
+
end
|
385
|
+
if ( /(processor|cpu MHz|physical id|siblings|core id|cpu cores|apicid|initial apicid|cpuid level|bogomips|clflush size|cache size)/.match(m[0]) )
|
386
|
+
cpuinfo[m[0]] = m[1].to_i
|
387
|
+
next
|
388
|
+
end
|
389
|
+
if ( /(cpu MHz|bogomips)/.match(m[0]) )
|
390
|
+
cpuinfo[m[0]] = m[1].to_f
|
391
|
+
next
|
392
|
+
end
|
393
|
+
if ( /(cache size)/.match(m[0]) )
|
394
|
+
cpuinfo[m[0]] = m[1].split(/\s+/)[0].to_i
|
395
|
+
next
|
396
|
+
end
|
397
|
+
cpuinfo[m[0]] = m[1]
|
398
|
+
end
|
399
|
+
}
|
400
|
+
end
|
401
|
+
def readCrypto(queue)
|
402
|
+
# face |bytes packets errs drop fifo frame compressed multicast|bytes packets errs drop fifo colls carrier compressed
|
403
|
+
#processor : 0
|
404
|
+
#vendor_id : GenuineIntel
|
405
|
+
#cpu family : 6
|
406
|
+
#model : 69
|
407
|
+
#model name : Intel(R) Core(TM) i5-4210U CPU @ 1.70GHz
|
408
|
+
#stepping : 1
|
409
|
+
#microcode : 0x17
|
410
|
+
#cpu MHz : 799.996
|
411
|
+
#cache size : 3072 KB
|
412
|
+
#physical id : 0
|
413
|
+
#siblings : 4
|
414
|
+
#core id : 0
|
415
|
+
#cpu cores : 2
|
416
|
+
#apicid : 0
|
417
|
+
#initial apicid : 0
|
418
|
+
#fpu : yes
|
419
|
+
#fpu_exception : yes
|
420
|
+
#cpuid level : 13
|
421
|
+
#wp : yes
|
422
|
+
#flags : fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov pat pse36 clflush dts acpi mmx fxsr sse sse2 ss ht tm pbe syscall nx pdpe1gb rdtscp lm constant_tsc arch_perfmo
|
423
|
+
#n pebs bts rep_good nopl xtopology nonstop_tsc aperfmperf eagerfpu pni pclmulqdq dtes64 monitor ds_cpl vmx est tm2 ssse3 fma cx16 xtpr pdcm pcid sse4_1 sse4_2 movbe popcnt tsc_deadline_timer
|
424
|
+
#aes xsave avx f16c rdrand lahf_lm abm ida arat epb xsaveopt pln pts dtherm tpr_shadow vnmi flexpriority ept vpid fsgsbase tsc_adjust bmi1 avx2 smep bmi2 erms invpcid
|
425
|
+
#bogomips : 3391.99
|
426
|
+
#clflush size : 64
|
427
|
+
#cache_alignment : 64
|
428
|
+
#address sizes : 39 bits physical, 48 bits virtual
|
429
|
+
#power management:
|
430
|
+
|
431
|
+
crypto = Hash.new
|
432
|
+
file = Pathname.new("/proc/crypto")
|
433
|
+
lines = file.readlines
|
434
|
+
lines.each { |line|
|
435
|
+
#@logger.info? && @logger.info("LINE: "+line)
|
436
|
+
if ( line.length == 1 )
|
437
|
+
event = LogStash::Event.new(
|
438
|
+
"crypto" => crypto,
|
439
|
+
"file" => file.to_s,
|
440
|
+
"host" => @host,
|
441
|
+
"type" => "crypto" )
|
442
|
+
decorate(event)
|
443
|
+
queue << event
|
444
|
+
next
|
445
|
+
end
|
446
|
+
m = line.strip.split(/[:\s]+/)
|
447
|
+
if ( m && m.length >= 2 )
|
448
|
+
crypto[m[0]] = m[1]
|
449
|
+
end
|
450
|
+
}
|
451
|
+
end
|
452
|
+
|
453
|
+
def readWireless(queue)
|
454
|
+
# face |bytes packets errs drop fifo frame compressed multicast|bytes packets errs drop fifo colls carrier compressed
|
455
|
+
file = Pathname.new("/proc/net/wireless")
|
456
|
+
#Inter-| sta-| Quality | Discarded packets | Missed | WE
|
457
|
+
#face | tus | link level noise | nwid crypt frag retry misc | beacon | 22
|
458
|
+
# wlan0: 0000 32. -78. -256 0 0 0 19 89 0
|
459
|
+
lines = file.readlines
|
460
|
+
junk = lines.shift
|
461
|
+
junk = lines.shift
|
462
|
+
lines.each { |line|
|
463
|
+
#@logger.info? && @logger.info("LINE: "+line)
|
464
|
+
m = line.strip.split(/[:\s]+/)
|
465
|
+
#puts(m)
|
466
|
+
if (m && m.length >= 11 )
|
467
|
+
event = LogStash::Event.new(
|
468
|
+
"raw" => line,
|
469
|
+
"iface" => m[0],
|
470
|
+
"status" => m[1].to_i,
|
471
|
+
"linkQuality" => m[2].to_f,
|
472
|
+
"levelQuality" => m[3].to_f,
|
473
|
+
"noiseQulity" => m[4].to_i,
|
474
|
+
"nwidDiscard" => m[5].to_i,
|
475
|
+
"cryptDiscard" => m[6].to_i,
|
476
|
+
"fragDiscard" => m[7].to_i,
|
477
|
+
"retryDiscard" => m[8].to_i,
|
478
|
+
"miscDiscard" => m[9].to_i,
|
479
|
+
"missedBeacon" => m[10].to_i,
|
480
|
+
"we22" => m[11].to_i,
|
481
|
+
"file" => file.to_s,
|
482
|
+
"host" => @host,
|
483
|
+
"type" => "wireless" )
|
484
|
+
decorate(event)
|
485
|
+
queue << event
|
486
|
+
end
|
487
|
+
}
|
488
|
+
|
489
|
+
end
|
490
|
+
def readSysIpcShm(queue)
|
491
|
+
# face |bytes packets errs drop fifo frame compressed multicast|bytes packets errs drop fifo colls carrier compressed
|
492
|
+
file = Pathname.new("/proc/sysvipc/shm")
|
493
|
+
# key shmid perms size cpid lpid nattch uid gid cuid cgid atime dtime ctime rss swap
|
494
|
+
# 0 589824 1600 524288 1708 1739 2 1000 1000 1000 1000 1433801660 1433801660 1433801659 12288 0
|
495
|
+
# 0 491521 1600 524288 1479 871 2 1000 1000 1000 1000 1433801655 0 1433801655 147456 0
|
496
|
+
lines = file.readlines
|
497
|
+
junk = lines.shift
|
498
|
+
lines.each { |line|
|
499
|
+
m = line.strip.split(/[\s]+/)
|
500
|
+
if (m && m.length >= 14 )
|
501
|
+
event = LogStash::Event.new(
|
502
|
+
"raw" => line,
|
503
|
+
"key" => m[0],
|
504
|
+
"shmid" => m[1].to_i,
|
505
|
+
"perms" => m[2].to_i,
|
506
|
+
"size" => m[3].to_i,
|
507
|
+
"cpid" => m[4].to_i,
|
508
|
+
"lpid" => m[5].to_i,
|
509
|
+
"nattch"=> m[6].to_i,
|
510
|
+
"uid" => m[7].to_i,
|
511
|
+
"gid" => m[8].to_i,
|
512
|
+
"cuid" => m[9].to_i,
|
513
|
+
"cgid" => m[10].to_i,
|
514
|
+
"atime" => m[11].to_i,
|
515
|
+
"dtime" => m[12].to_i,
|
516
|
+
"ctime" => m[13].to_i,
|
517
|
+
"rss" => m[14].to_i,
|
518
|
+
"file" => file.to_s,
|
519
|
+
"host" => @host,
|
520
|
+
"type" => "sysipcshm" )
|
521
|
+
decorate(event)
|
522
|
+
queue << event
|
523
|
+
end
|
524
|
+
}
|
525
|
+
|
526
|
+
end
|
527
|
+
|
528
|
+
def run(queue)
|
529
|
+
loop do
|
530
|
+
begin
|
531
|
+
start = Time.now
|
532
|
+
readVmStats(queue) if @vmstats
|
533
|
+
readLoadAverage(queue) if @loadavg
|
534
|
+
readMemInfo(queue) if @meminfo
|
535
|
+
readPidStats(queue) if @pidstats
|
536
|
+
readDiskStats(queue) if @diskstats
|
537
|
+
readMounts(queue) if @mounts
|
538
|
+
readNetDev(queue) if @netdev
|
539
|
+
readCpuInfo(queue) if @cpuinfo
|
540
|
+
readCrypto(queue) if @crypto
|
541
|
+
readWireless(queue) if @wireless
|
542
|
+
readSysIpcShm(queue) if @sysipcshm
|
543
|
+
duration = Time.now - start
|
544
|
+
@logger.info("Parsing completed", :duration => duration, :interval => @interval )
|
545
|
+
# Sleep for the remainder of the interval, or 0 if the duration ran
|
546
|
+
# longer than the interval.
|
547
|
+
sleeptime = [0, @interval - duration].max
|
548
|
+
if sleeptime == 0
|
549
|
+
@logger.warn("Parsing longer than the interval. Skipping sleep.",
|
550
|
+
:duration => duration,
|
551
|
+
:interval => @interval)
|
552
|
+
else
|
553
|
+
sleep(sleeptime)
|
554
|
+
end
|
555
|
+
rescue => exception
|
556
|
+
#puts exception.message
|
557
|
+
puts exception.backtrace
|
558
|
+
raise
|
559
|
+
end # rescue
|
560
|
+
end # loop
|
561
|
+
end # def run
|
562
|
+
end # class LogStash::Inputs::Example
|