fluent-plugin-watch-objectspace 0.1.0 → 0.2.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 50ef80ccfe4236df352654ccbf415698e15bcf98927fc6bebba8ddbffd3d76bb
4
- data.tar.gz: 25efcc62552470384eebf776edcd6dc89e45d7550319dd2bd141b4f4608e6d7c
3
+ metadata.gz: f79b912a0f6dc31b0b0a11ebec4780ecdb56abd848842b1250440afe15995ab1
4
+ data.tar.gz: 343fceb8323cc98295d3c0fe1b52a5b0d8eff59f061bbd8f8191f876af2af3d0
5
5
  SHA512:
6
- metadata.gz: '0935e7413eefa8bc4302b0d151c403fa24131960f3aa9dddc87c2c815dab3321328b007a51d5b829a9019c3b5e2450d4fcb48d8aff23f5cbc29e226bc783a8bc'
7
- data.tar.gz: 73f7efa2832cce3b36f024b9c1feb4ac729a20aa63426b583b5012ff784391af7150c26a732471ff14aee03342d01647deb7101b35c95a06f5f016dc37c2001d
6
+ metadata.gz: 4d6b10a8abcbbe443c81f1f59bcc44610d62d451dedfeaeab0c458d76aee9459854c1197e6ad0f3a5b30fbc34d357b71815e40251189dba6921cc562dcdd397b
7
+ data.tar.gz: 606d74ca77ac3aa93ce6113d0c318e7777caed306feda3c80eba93ccce68dce0ef3b262672698783adef649c0a33cf7607b8e4891bf6348ada076fdb4670d3dd
data/README.md CHANGED
@@ -58,10 +58,10 @@ If memory usage is over 1.3 times, it raise an exception.
58
58
 
59
59
  ## FAQ
60
60
 
61
- # What is the different between fluent-plugin-watch-objectspace and fluent-plugin-watch-process
61
+ ### What is the difference between fluent-plugin-watch-objectspace and fluent-plugin-watch-process?
62
62
 
63
63
  fluent-plugin-watch-process is useful cron/batch process monitoring, In contrast to it, fluent-plugin-watch-objectspace is
64
- focused on used plugin's resource (memory) usage especially object and memory.
64
+ focused on used plugin's resource usage especially object and memory.
65
65
 
66
66
  ## Copyright
67
67
 
@@ -3,7 +3,7 @@ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
3
3
 
4
4
  Gem::Specification.new do |spec|
5
5
  spec.name = "fluent-plugin-watch-objectspace"
6
- spec.version = "0.1.0"
6
+ spec.version = "0.2.0"
7
7
  spec.authors = ["Kentaro Hayashi"]
8
8
  spec.email = ["kenhys@gmail.com"]
9
9
 
@@ -14,6 +14,8 @@
14
14
  # limitations under the License.
15
15
 
16
16
  require "fluent/plugin/input"
17
+ require "fluent/config/error"
18
+ require "objspace"
17
19
 
18
20
  module Fluent
19
21
  module Plugin
@@ -34,14 +36,25 @@ module Fluent
34
36
  config_param :watch_delay, :time, default: 60
35
37
  desc "Collect GC::Profiler.raw_data"
36
38
  config_param :gc_raw_data, :bool, default: false
37
- desc "Threshold rate which regards increased RES as memory leaks"
38
- config_param :res_incremental_threshold_rate, :float, default: 1.3
39
-
39
+ desc "Specify included fields of top command"
40
+ config_param :top_fields, :array, default: ["VIRT", "RES", "SHR", "%CPU", "%MEM", "TIME+"]
41
+
42
+ config_section :threshold, required: false, multi: false do
43
+ desc "Threshold rate which regards increased memsize as memory leaks"
44
+ config_param :memsize_of_all, :float, default: 1.3
45
+ desc "Threshold rate which regards increased RES as memory leaks"
46
+ config_param :res_of_top, :float, default: nil
47
+ end
48
+
40
49
  def configure(conf)
41
50
  super(conf)
42
51
  if @modules
43
52
  @modules.each do |mod|
44
- require mod
53
+ begin
54
+ require mod
55
+ rescue LoadError
56
+ raise Fluent::ConfigError.new("BUG: module <#{mod}> can't be loaded")
57
+ end
45
58
  end
46
59
  end
47
60
  @warmup_time = Time.now + @watch_delay
@@ -59,6 +72,7 @@ module Fluent
59
72
  fields = content.split("\n")[-2].split
60
73
  values = content.split("\n").last.split
61
74
  fields.each_with_index do |field, index|
75
+ next unless @top_fields.include?(field)
62
76
  case field
63
77
  when "USER", "S", "TIME+", "COMMAND"
64
78
  record[field.downcase] = values[index]
@@ -78,7 +92,8 @@ module Fluent
78
92
  record = {
79
93
  "pid" => pid,
80
94
  "count" => {},
81
- "memory_leaks" => false
95
+ "memory_leaks" => false,
96
+ "memsize_of_all" => ObjectSpace.memsize_of_all
82
97
  }
83
98
 
84
99
  begin
@@ -88,23 +103,20 @@ module Fluent
88
103
  record.merge!(parse_top_result(content))
89
104
 
90
105
  if @gc_raw_data
91
- record["raw_data"] = GC::Profiler.raw_data
106
+ record["gc_raw_data"] = GC::Profiler.raw_data
92
107
  end
93
- @watch_class.each do |klass|
94
- record["count"]["#{klass.downcase}"] = ObjectSpace.each_object(Object.const_get(klass)) { |x| x }
108
+ if @watch_class
109
+ @watch_class.each do |klass|
110
+ record["count"]["#{klass.downcase}"] = ObjectSpace.each_object(Object.const_get(klass)) { |x| x }
111
+ end
95
112
  end
96
113
 
97
114
  if @source.empty?
115
+ record["memsize_of_all"] = ObjectSpace.memsize_of_all
98
116
  @source = record
99
117
  end
100
118
 
101
- if @source["res"] * @res_incremental_threshold_rate < record["res"]
102
- record["memory_leaks"] = true
103
- message = sprintf("Memory leak is detected, threshold rate <%f>: %f > %f * %f",
104
- @res_incremental_threshold_rate, record["res"],
105
- @source["res"], @res_incremental_threshold_rate)
106
- raise message
107
- end
119
+ check_threshold(record)
108
120
  es = OneEventStream.new(Fluent::EventTime.now, record)
109
121
  router.emit_stream(@tag, es)
110
122
  rescue => e
@@ -112,6 +124,29 @@ module Fluent
112
124
  end
113
125
  end
114
126
 
127
+ def check_threshold(record)
128
+ return unless @threshold
129
+
130
+ if @threshold.res_of_top
131
+ if @source["res"] * @threshold.res_of_top < record["res"]
132
+ record["memory_leaks"] = true
133
+ message = sprintf("Memory leak is detected, threshold res_of_top rate <%f>: %f > %f * %f",
134
+ @threshold.res_of_top, record["res"],
135
+ @source["res"], @threshold.res_of_top)
136
+ raise message
137
+ end
138
+ end
139
+ if @threshold.memsize_of_all
140
+ if @source["memsize_of_all"] * @threshold.memsize_of_all < record["memsize_of_all"]
141
+ record["memory_leaks"] = true
142
+ message = sprintf("Memory leak is detected, threshold of memsize_of_all rate <%f>: %f > %f * %f",
143
+ @threshold.memsize_of_all, record["memsize_of_all"],
144
+ @source["memsize_of_all"], @threshold.memsize_of_all)
145
+ raise message
146
+ end
147
+ end
148
+ end
149
+
115
150
  def shutdown
116
151
  end
117
152
  end
@@ -14,28 +14,197 @@ class WatchObjectspaceInputTest < Test::Unit::TestCase
14
14
  Fluent::Test::Driver::Input.new(Fluent::Plugin::WatchObjectspaceInput).configure(conf)
15
15
  end
16
16
 
17
+ def create_config(params={})
18
+ config_element("ROOT", "", params)
19
+ end
20
+
21
+ def default_params(data={})
22
+ {
23
+ "watch_delay" => 0,
24
+ "watch_interval" => 1
25
+ }.merge(data)
26
+ end
27
+
17
28
  sub_test_case "configure" do
18
29
  def test_default_configuration
19
30
  d = create_driver
20
31
  assert_equal([
32
+ nil,
33
+ 60,
34
+ 60,
35
+ "watch_objectspace",
36
+ nil,
37
+ false
38
+ ],
39
+ [
40
+ d.instance.watch_class,
41
+ d.instance.watch_interval,
42
+ d.instance.watch_delay,
43
+ d.instance.tag,
44
+ d.instance.modules,
45
+ d.instance.gc_raw_data
46
+ ])
47
+ end
48
+
49
+ def test_customize
50
+ config = config_element("ROOT", "", {
51
+ "watch_delay" => 1,
52
+ "watch_interval" => 2,
53
+ "watch_class" => ["String"],
54
+ "tag" => "customized",
55
+ "modules" => ["objspace"],
56
+ "gc_raw_data" => true,
57
+ "res_incremental_threshold_rate" => 1.1,
58
+ "memsize_of_all_incremental_threshold_rate" => 1.5
59
+ })
60
+ d = create_driver(config)
61
+ assert_equal([
62
+ ["String"],
63
+ 2.0,
64
+ 1.0,
65
+ "customized",
66
+ ["objspace"],
67
+ true
68
+ ],
69
+ [
21
70
  d.instance.watch_class,
22
71
  d.instance.watch_interval,
23
72
  d.instance.watch_delay,
24
73
  d.instance.tag,
25
74
  d.instance.modules,
26
75
  d.instance.gc_raw_data,
27
- d.instance.res_incremental_threshold_rate
76
+ ])
77
+ end
78
+
79
+ def test_watch_class
80
+ config = create_config(default_params({"watch_class" => ["String"]}))
81
+ d = create_driver(config)
82
+ d.run(expect_records: 1, timeout: 1)
83
+ assert_equal([
84
+ 1,
85
+ ["string"],
86
+ true
28
87
  ],
29
88
  [
30
- nil,
31
- 60,
32
- 60,
33
- "watch_objectspace",
34
- nil,
35
- false,
36
- 1.3
89
+ d.events.size,
90
+ d.events.first.last["count"].keys,
91
+ d.events.first.last["count"]["string"] > 0
37
92
  ])
38
93
  end
94
+
95
+ def test_watch_interval
96
+ config = create_config(default_params({"watch_interval" => 5}))
97
+ d = create_driver(config)
98
+ d.run(expect_records: 1, timeout: 5)
99
+ assert_equal(1, d.events.size)
100
+ end
101
+
102
+ sub_test_case "watch_delay" do
103
+ data(
104
+ before_interval: [1, 0, 5],
105
+ after_interval: [0, 10, 5]
106
+ )
107
+ test "watch delay" do |(count, delay, interval)|
108
+ config = create_config(default_params({"watch_delay" => delay, "watch_interval" => interval}))
109
+ d = create_driver(config)
110
+ d.run(expect_records: 1, timeout: interval)
111
+ assert_equal(count, d.events.size)
112
+ end
113
+ end
114
+
115
+ sub_test_case "tag" do
116
+ data(
117
+ with_tag: ["changed", "changed"],
118
+ default: ["watch_objectspace", nil]
119
+ )
120
+ test "tag" do |(tag, specified)|
121
+ config = if specified
122
+ create_config(default_params({"tag" => specified}))
123
+ else
124
+ create_config(default_params)
125
+ end
126
+ d = create_driver(config)
127
+ d.run(expect_records: 1, timeout: 5)
128
+ assert_equal([tag], d.events.collect { |event| event.first })
129
+ end
130
+ end
131
+
132
+ sub_test_case "modules" do
133
+ def test_invalid_module
134
+ config = create_config(default_params({"modules" => "404",
135
+ "watch_class" => "404"}))
136
+ assert_raise do
137
+ create_driver(config)
138
+ end
139
+ end
140
+
141
+ def test_valid_module
142
+ config = create_config(default_params({"modules" => "fluent/plugin/in_watch_objectspace",
143
+ "watch_class" => "Fluent::Plugin::WatchObjectspaceInput"}))
144
+ d = create_driver(config)
145
+ d.run(expect_records: 1, timeout: 1)
146
+ assert_equal([
147
+ ["fluent::plugin::watchobjectspaceinput"],
148
+ true
149
+ ],
150
+ [
151
+ d.events.collect { |event| event.last["count"].keys }.flatten,
152
+ d.events.all? { |event| event.last["count"]["fluent::plugin::watchobjectspaceinput"] > 0 }
153
+ ])
154
+ end
155
+ end
156
+
157
+ def gc_raw_data_keys(data)
158
+ if data.empty?
159
+ data
160
+ else
161
+ data.collect do |raw_data|
162
+ raw_data.keys
163
+ end
164
+ end
165
+ end
166
+
167
+ sub_test_case "gc_raw_data" do
168
+ data(
169
+ without_gc: [false, [[]]],
170
+ with_gc: [true, [[%i(GC_FLAGS GC_TIME GC_INVOKE_TIME HEAP_USE_SIZE HEAP_TOTAL_SIZE HEAP_TOTAL_OBJECTS GC_IS_MARKED)]]]
171
+ )
172
+ test "gc" do |(gc, keys)|
173
+ config = create_config(default_params({"gc_raw_data" => "true"}))
174
+ d = create_driver(config)
175
+ GC::Profiler.clear
176
+ GC.start if gc
177
+ d.run(expect_records: 1, timeout: 1)
178
+ assert_equal([
179
+ 1,
180
+ keys
181
+ ],
182
+ [
183
+ d.events.size,
184
+ d.events.collect { |event| gc_raw_data_keys(event.last["gc_raw_data"])},
185
+ ])
186
+ end
187
+ end
188
+
189
+ sub_test_case "threshold" do
190
+ def test_memsize_of_all
191
+ config = config_element("ROOT", "", {
192
+ "watch_delay" => 0,
193
+ "watch_interval" => 2,
194
+ }, [config_element("threshold", "", {})])
195
+ d = create_driver(config)
196
+ assert_equal(1.3, d.instance.threshold.memsize_of_all)
197
+ end
198
+
199
+ def test_res_of_top
200
+ config = config_element("ROOT", "", {
201
+ "watch_delay" => 0,
202
+ "watch_interval" => 2,
203
+ }, [config_element("threshold", "", {"res_of_top" => 1.5})])
204
+ d = create_driver(config)
205
+ assert_equal(1.5, d.instance.threshold.res_of_top)
206
+ end
207
+ end
39
208
  end
40
209
 
41
210
  sub_test_case "parser" do
@@ -50,7 +219,7 @@ class WatchObjectspaceInputTest < Test::Unit::TestCase
50
219
  assert_equal([1,
51
220
  "watch_objectspace",
52
221
  Fluent::EventTime,
53
- ["pid", "count", "memory_leaks", "user", "pr", "ni", "virt", "res", "shr", "s", "%cpu", "%mem", "time+", "command"]
222
+ ["pid", "count", "memory_leaks", "memsize_of_all", "virt", "res", "shr", "%cpu", "%mem", "time+"]
54
223
  ],
55
224
  [d.events.size,
56
225
  event[0],
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: fluent-plugin-watch-objectspace
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Kentaro Hayashi
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2021-09-27 00:00:00.000000000 Z
11
+ date: 2021-09-30 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler