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 +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
|