fluent-plugin-top 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 68d92f3f4a576246b36676c92c36d930a29512a2
4
+ data.tar.gz: f171f9eb83e0999918432beb4a25c83b166347cd
5
+ SHA512:
6
+ metadata.gz: 74c90f3744cce92369344c10a1db9090a7484bd75e7c837f08df2617fb3cd0ebcf33148d9e94d904d97a6662037930ae49f33982489cac9d90c45a308a791fcf
7
+ data.tar.gz: 9b4019cfde8285e36f195e4d8ed92da80e6603e1214982d5631787790a7b0ffbf58c853d3160e46c6ad376bb6813e28abaeb5598eec6a7b22c5c4b41ef1241e5
@@ -0,0 +1,9 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in fluent-plugin-top.gemspec
4
+ gemspec
@@ -0,0 +1,13 @@
1
+ Copyright (c) 2016 Tetsu Izawa (@moccos)
2
+
3
+ Licensed under the Apache License, Version 2.0 (the "License");
4
+ you may not use this file except in compliance with the License.
5
+ You may obtain a copy of the License at
6
+
7
+ http://www.apache.org/licenses/LICENSE-2.0
8
+
9
+ Unless required by applicable law or agreed to in writing, software
10
+ distributed under the License is distributed on an "AS IS" BASIS,
11
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
+ See the License for the specific language governing permissions and
13
+ limitations under the License.
@@ -0,0 +1,39 @@
1
+ fluent-plugin-top
2
+ ===
3
+
4
+ Fluentd input plugin for top command.
5
+
6
+ If you focus on system metrics rather than each process,
7
+ you should use [dstat plugin](https://github.com/shun0102/fluent-plugin-dstat).
8
+
9
+ ## Configuration
10
+ <source>
11
+ @type top
12
+ tag local.top # *required*
13
+ interval 30 # default: 10.0
14
+ command_line true # default: true
15
+ extra_switch -w 200 # default: ""
16
+ cpu_percent 50 # default: nil
17
+ </source>
18
+
19
+ * **tag**: Output tag. [required]
20
+ * **interval**: Refresh interval in sec.
21
+ * **command_line**: Get command line (/usr/bin/ruby foo.rb -v) instead of simple name (ruby).
22
+ * **extra_switch**: Extra command line switch for top command.
23
+ * **cpu_percent**: Threshold - CPU usage (percent).
24
+ * **mem_percent**: Threshold - Memory usage (percent).
25
+ * **mem**: Threshold - Memory usage in megabytes.
26
+
27
+ At least one threshold parameter should be specified.
28
+ Otherwise, this plugin does not output anything.
29
+ If you specify multiple threshold parameters, they are "OR"ed.
30
+
31
+ <!--== Examples-->
32
+
33
+ <!--TODO: write here-->
34
+
35
+ ## Copyright
36
+
37
+ Copyright (c) 2016 Tetsu Izawa (@moccos)
38
+
39
+ Apache License, Version 2.0
@@ -0,0 +1,10 @@
1
+ require "bundler/gem_tasks"
2
+ require 'bundler/gem_tasks'
3
+ require 'rake/testtask'
4
+
5
+ Rake::TestTask.new do |t|
6
+ t.libs << "test"
7
+ t.test_files = FileList['test/plugin/test*.rb']
8
+ t.verbose = true
9
+ end
10
+ task :default => :test
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.0.1
@@ -0,0 +1,25 @@
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-top"
7
+ spec.version = "0.1.0"
8
+ spec.authors = ["Tetsu Izawa (@moccos)"]
9
+ spec.email = ["tt.izawa@gmail.com"]
10
+ spec.homepage = "https://github.com/moccos/fluent-plugin-top"
11
+
12
+ spec.summary = %q{Fluentd top command input plugin}
13
+ spec.description = %q{Fluentd top command input plugin}
14
+ spec.licenses = ["Apache License, Version 2.0"]
15
+
16
+ spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
17
+ spec.bindir = "exe"
18
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
19
+ spec.require_paths = ["lib"]
20
+
21
+ spec.add_development_dependency "bundler"
22
+ spec.add_development_dependency "rake"
23
+ spec.add_runtime_dependency "fluentd", ">= 0.12.0"
24
+ spec.add_runtime_dependency "fluent-mixin-rewrite-tag-name"
25
+ end
@@ -0,0 +1,108 @@
1
+ require 'thread'
2
+ require 'fluent/input'
3
+ require 'fluent/mixin/rewrite_tag_name'
4
+ require_relative 'in_top_parser'
5
+
6
+ module Fluent
7
+ class TopInput < Fluent::Input
8
+ INTERVAL_MIN = 0.5 # avoid too short interval
9
+
10
+ Fluent::Plugin.register_input('top', self)
11
+ desc "Top command. Don't forget to specify batch switch (-b)."
12
+ config_param :top, :string, :default => "top -b"
13
+ desc 'Output tag.'
14
+ config_param :tag, :string
15
+
16
+ ### Command line options
17
+ desc 'Refresh interval. (top -d)'
18
+ config_param :interval, :float, :default => 10.0
19
+ desc 'Get command line. (top -c)'
20
+ config_param :command_line, :bool, :default => true
21
+ desc 'Extra command line args for top command.'
22
+ config_param :extra_switch, :string, :default => ""
23
+
24
+ ### Conditions
25
+ desc "Threshold - CPU usage (percent)."
26
+ config_param :cpu_percent, :float, :default => nil
27
+ desc "Threshold - Memory usage (percent)."
28
+ config_param :mem_percent, :float, :default => nil
29
+ desc "Threshold - Memory usage in megabytes."
30
+ config_param :mem, :integer, :default => nil
31
+
32
+ ### Future works
33
+ #config_param :cpu_rank, :integer, :default => nil
34
+ #config_param :pid, :array, :default => []
35
+ #config_param :pid_file, :array, :default => []
36
+ #config_param :pid_file_watch_interval, :int, :default => 30
37
+
38
+ desc 'Command line and args. This overrides other settings.'
39
+ config_param :test_cmd, :string, :default => nil
40
+
41
+ # for fluent-mixin-rewrite-tag-name
42
+ include Fluent::Mixin::RewriteTagName
43
+ desc 'Command to get hostname. Typically it is "hostname" or "hostname -s".'
44
+ config_param :hostname_command, :string, :default => "hostname"
45
+
46
+ def configure(conf)
47
+ super
48
+ interval = @interval > INTERVAL_MIN ? @interval : INTERVAL_MIN
49
+ @top_command =
50
+ if @test_cmd then
51
+ @test_cmd
52
+ else
53
+ "#{@top} -d #{interval} #{@command_line ? "-c" : ""} #{@extra_switch}"
54
+ end
55
+ end
56
+
57
+ def start
58
+ super
59
+ @top_thread = Thread.new do
60
+ run_top()
61
+ end
62
+ end
63
+
64
+ def shutdown
65
+ super
66
+ $log.trace "Shutdown top command thread."
67
+ @top_thread.kill if @top_thread
68
+ end
69
+
70
+ private
71
+ def run_top
72
+ $log.trace "Top command thread is running: " + @top_command
73
+ IO.popen(@top_command, "r") {|io|
74
+ parser = Fluent::TopInputParser.new()
75
+ io.each {|line|
76
+ result, ps = parser.parse(line)
77
+ if result && check_ps_info(ps) then
78
+ ps.args = ps.args.join(' ')
79
+ emit_message(@tag, ps.to_h)
80
+ end
81
+ }
82
+ }
83
+ $log.info "Exit top command thread. (reached EOF)"
84
+ end
85
+
86
+ def check_ps_info(ps)
87
+ # For now, all conditions are "OR"ed
88
+ case
89
+ when @cpu_percent && ps.cpu >= @cpu_percent
90
+ true
91
+ when @mem_percent && ps.mem >= @mem_percent
92
+ true
93
+ when @mem && ps.res >= @mem * 1024 # megabytes
94
+ true
95
+ else
96
+ false
97
+ end
98
+ end
99
+
100
+ def emit_message(tag, message)
101
+ time = Engine.now
102
+ # for fluent-mixin-rewrite-tag-name
103
+ emit_tag = tag.dup
104
+ filter_record(emit_tag, time, message)
105
+ router.emit(emit_tag, time, message)
106
+ end
107
+ end
108
+ end
@@ -0,0 +1,80 @@
1
+ module Fluent
2
+ class TopInputParser
3
+ ### state
4
+ STATE_HEADER = 1
5
+ STATE_PROCESS = 2
6
+
7
+ @@PS_INFO = Struct.new(:pid, :user, :res, :cpu, :mem, :cmd, :args)
8
+
9
+ def initialize()
10
+ reset_state
11
+ end
12
+
13
+ def parse(line)
14
+ case @state
15
+ when STATE_HEADER then
16
+ return parse_header line
17
+ else
18
+ return parse_process line
19
+ end
20
+ end
21
+
22
+ private
23
+ def reset_state()
24
+ @state = STATE_HEADER
25
+ end
26
+
27
+ def parse_header(line)
28
+ # PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND
29
+ # 1 root 20 0 38004 5992 3916 S 0.0 0.6 0:03.43 systemd
30
+
31
+ ss = line.split("\s")
32
+ @state = STATE_PROCESS if ss[0] == "PID"
33
+ return false, nil
34
+ end
35
+
36
+ def parse_process(line)
37
+ ss = line.split("\s")
38
+ if !is_number?(ss[0]) then
39
+ reset_state
40
+ return parse_header(line)
41
+ end
42
+ begin
43
+ pid, user, _pr, _ni, _virt, res, _shr, _s, cpu, mem, _time, cmd = ss
44
+ args = ss.drop(12)
45
+ return true, @@PS_INFO.new(pid.to_i, user, res, cpu.to_f, mem.to_f, cmd, args)
46
+ rescue => e
47
+ $log.warn "parse error #{e.to_s}: " + line
48
+ reset_state
49
+ return false, nil
50
+ end
51
+ end
52
+
53
+ def is_number?(s)
54
+ begin
55
+ Float(s)
56
+ true
57
+ rescue
58
+ false
59
+ end
60
+ end
61
+
62
+ def parse_unit(s)
63
+ begin
64
+ case s[-1]
65
+ when "m"
66
+ s[0, s.length-1].to_f * 1024
67
+ when "g"
68
+ s[0, s.length-1].to_f * 1024 * 1024
69
+ #when "t" # I've never seen...
70
+ #s[0, s.length-1].to_f * 1024 * 1024 * 1024
71
+ else
72
+ s.to_i
73
+ end
74
+ rescue
75
+ $log.warn "Memory unit parse error: " + s
76
+ -1
77
+ end
78
+ end
79
+ end
80
+ end
metadata ADDED
@@ -0,0 +1,109 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: fluent-plugin-top
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Tetsu Izawa (@moccos)
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2016-09-14 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: fluentd
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: 0.12.0
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: 0.12.0
55
+ - !ruby/object:Gem::Dependency
56
+ name: fluent-mixin-rewrite-tag-name
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :runtime
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ description: Fluentd top command input plugin
70
+ email:
71
+ - tt.izawa@gmail.com
72
+ executables: []
73
+ extensions: []
74
+ extra_rdoc_files: []
75
+ files:
76
+ - ".gitignore"
77
+ - Gemfile
78
+ - LICENSE.txt
79
+ - README.md
80
+ - Rakefile
81
+ - VERSION
82
+ - fluent-plugin-top.gemspec
83
+ - lib/fluent/plugin/in_top.rb
84
+ - lib/fluent/plugin/in_top_parser.rb
85
+ homepage: https://github.com/moccos/fluent-plugin-top
86
+ licenses:
87
+ - Apache License, Version 2.0
88
+ metadata: {}
89
+ post_install_message:
90
+ rdoc_options: []
91
+ require_paths:
92
+ - lib
93
+ required_ruby_version: !ruby/object:Gem::Requirement
94
+ requirements:
95
+ - - ">="
96
+ - !ruby/object:Gem::Version
97
+ version: '0'
98
+ required_rubygems_version: !ruby/object:Gem::Requirement
99
+ requirements:
100
+ - - ">="
101
+ - !ruby/object:Gem::Version
102
+ version: '0'
103
+ requirements: []
104
+ rubyforge_project:
105
+ rubygems_version: 2.6.6
106
+ signing_key:
107
+ specification_version: 4
108
+ summary: Fluentd top command input plugin
109
+ test_files: []