fluent-plugin-watch-objectspace 0.1.0 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +2 -2
- data/fluent-plugin-watch-objectspace.gemspec +1 -1
- data/lib/fluent/plugin/in_watch_objectspace.rb +50 -15
- data/test/plugin/test_in_watch_objectspace.rb +178 -9
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: f79b912a0f6dc31b0b0a11ebec4780ecdb56abd848842b1250440afe15995ab1
|
4
|
+
data.tar.gz: 343fceb8323cc98295d3c0fe1b52a5b0d8eff59f061bbd8f8191f876af2af3d0
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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
|
-
|
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
|
64
|
+
focused on used plugin's resource usage especially object and memory.
|
65
65
|
|
66
66
|
## Copyright
|
67
67
|
|
@@ -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 "
|
38
|
-
config_param :
|
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
|
-
|
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["
|
106
|
+
record["gc_raw_data"] = GC::Profiler.raw_data
|
92
107
|
end
|
93
|
-
@watch_class
|
94
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
31
|
-
|
32
|
-
|
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", "
|
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.
|
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-
|
11
|
+
date: 2021-09-30 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|