fluent-plugin-top 0.1.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.
@@ -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: []