fluent-plugin-anomalydetect 0.1.0 → 0.1.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/README.rdoc +9 -0
- data/fluent-plugin-anormalydetect.gemspec +2 -2
- data/lib/fluent/plugin/change_finder.rb +5 -2
- data/lib/fluent/plugin/out_anomalydetect.rb +29 -14
- data/test/plugin/test_out_anomalydetect.rb +44 -2
- metadata +12 -19
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 00950f80d0252742704254c65f1f20a145fe967d
|
4
|
+
data.tar.gz: 7c9fd9003e576cbb8a51006ef7c5fad2a8b8d626
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: e0ca5a907d709856442ba77c26315bcb3d8777484e1920389f0cd1c20889bbd8625a4ad9087dd6ddaf8b03cf38815f64f84a368d2577d9a7c3e02451e4c16da8
|
7
|
+
data.tar.gz: 89b8175c1797a2800e92710fd9f84b0177239e8ab40dd27b7e9624a3786bb81bc813b98b0011d33503f3f09fac5c1301ed9f17fb401194417bfc922cb86668fc
|
data/README.rdoc
CHANGED
@@ -73,6 +73,15 @@ If you want to know detail of these parameters, see "Theory".
|
|
73
73
|
|
74
74
|
If "store_file" option was specified, a historical stat will be stored to the file at shutdown, and it will be restored on started.
|
75
75
|
|
76
|
+
|
77
|
+
<match access.**>
|
78
|
+
type anomalydetect
|
79
|
+
...
|
80
|
+
threshold 3
|
81
|
+
</match>
|
82
|
+
|
83
|
+
If "threshold" option was specified, plugin only ouput when the anomalyscore is more than threshold.
|
84
|
+
|
76
85
|
== Theory
|
77
86
|
"データマイニングによる異常検知" http://amzn.to/XHXNun
|
78
87
|
|
@@ -3,7 +3,7 @@ lib = File.expand_path('../lib', __FILE__)
|
|
3
3
|
|
4
4
|
Gem::Specification.new do |gem|
|
5
5
|
gem.name = "fluent-plugin-anomalydetect"
|
6
|
-
gem.version = "0.1.
|
6
|
+
gem.version = "0.1.1"
|
7
7
|
gem.authors = ["Muddy Dixon"]
|
8
8
|
gem.email = ["muddydixon@gmail.com"]
|
9
9
|
gem.description = %q{detect anomal sequential input casually}
|
@@ -16,6 +16,6 @@ Gem::Specification.new do |gem|
|
|
16
16
|
gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
|
17
17
|
gem.require_paths = ["lib"]
|
18
18
|
|
19
|
-
gem.add_development_dependency "
|
19
|
+
gem.add_development_dependency "rake"
|
20
20
|
gem.add_runtime_dependency "fluentd"
|
21
21
|
end
|
@@ -43,7 +43,10 @@ module Fluent
|
|
43
43
|
@data.shift
|
44
44
|
end
|
45
45
|
|
46
|
-
|
46
|
+
p = prob(xt, @sigma, x)
|
47
|
+
s = score(p)
|
48
|
+
$log.debug "change_finder:#{Thread.current.object_id} x:#{x} xt:#{xt} p:#{p} s:#{s} term:#{@term} r:#{@r} data:#{@data} mu:#{@mu} sigma:#{@sigma} c:#{@c}"
|
49
|
+
s
|
47
50
|
end
|
48
51
|
|
49
52
|
def prob(mu, sigma, v)
|
@@ -60,7 +63,7 @@ module Fluent
|
|
60
63
|
def smooth(size)
|
61
64
|
_end = @data.size
|
62
65
|
_begin = [_end - size, 0].max
|
63
|
-
@data.slice(_begin, _end).inject(
|
66
|
+
(_size = (_end - _begin)) == 0 ? 0.0 : @data.slice(_begin, _end).inject(:+).to_f / _size
|
64
67
|
end
|
65
68
|
|
66
69
|
def show_status
|
@@ -2,7 +2,7 @@ module Fluent
|
|
2
2
|
class AnomalyDetectOutput < Output
|
3
3
|
Fluent::Plugin.register_output('anomalydetect', self)
|
4
4
|
|
5
|
-
|
5
|
+
require_relative 'change_finder'
|
6
6
|
require 'pathname'
|
7
7
|
|
8
8
|
config_param :outlier_term, :integer, :default => 28
|
@@ -64,6 +64,8 @@ module Fluent
|
|
64
64
|
load_from_file
|
65
65
|
init_records
|
66
66
|
start_watch
|
67
|
+
rescue => e
|
68
|
+
$log.warn "anomalydetect: #{e.class} #{e.message} #{e.backtrace.first}"
|
67
69
|
end
|
68
70
|
|
69
71
|
def shutdown
|
@@ -73,6 +75,8 @@ module Fluent
|
|
73
75
|
@watcher.join
|
74
76
|
end
|
75
77
|
store_to_file
|
78
|
+
rescue => e
|
79
|
+
$log.warn "anomalydetect: #{e.class} #{e.message} #{e.backtrace.first}"
|
76
80
|
end
|
77
81
|
|
78
82
|
def load_from_file
|
@@ -93,11 +97,11 @@ module Fluent
|
|
93
97
|
@outlier_buf = stored[:outlier_buf]
|
94
98
|
@score = stored[:score]
|
95
99
|
else
|
96
|
-
$log.warn "configuration param was changed. ignore stored data"
|
100
|
+
$log.warn "anomalydetect: configuration param was changed. ignore stored data"
|
97
101
|
end
|
98
102
|
end
|
99
103
|
rescue => e
|
100
|
-
$log.warn "Can't load store_file #{e}"
|
104
|
+
$log.warn "anomalydetect: Can't load store_file #{e}"
|
101
105
|
end
|
102
106
|
end
|
103
107
|
|
@@ -117,7 +121,7 @@ module Fluent
|
|
117
121
|
}, f)
|
118
122
|
end
|
119
123
|
rescue => e
|
120
|
-
$log.warn "Can't write store_file #{e}"
|
124
|
+
$log.warn "anomalydetect: Can't write store_file #{e}"
|
121
125
|
end
|
122
126
|
end
|
123
127
|
|
@@ -126,16 +130,19 @@ module Fluent
|
|
126
130
|
end
|
127
131
|
|
128
132
|
def watch
|
129
|
-
|
130
133
|
@last_checked = Fluent::Engine.now
|
131
|
-
loop
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
134
|
+
loop do
|
135
|
+
begin
|
136
|
+
sleep 0.5
|
137
|
+
now = Fluent::Engine.now
|
138
|
+
if now - @last_checked >= @tick
|
139
|
+
flush_emit(now - @last_checked)
|
140
|
+
@last_checked = now
|
141
|
+
end
|
142
|
+
rescue => e
|
143
|
+
$log.warn "anomalydetect: #{e.class} #{e.message} #{e.backtrace.first}"
|
137
144
|
end
|
138
|
-
|
145
|
+
end
|
139
146
|
end
|
140
147
|
|
141
148
|
def init_records
|
@@ -155,14 +162,20 @@ module Fluent
|
|
155
162
|
val = if @record_count
|
156
163
|
flushed.size
|
157
164
|
else
|
158
|
-
flushed.
|
165
|
+
filtered = flushed.map {|record| record[@target] }.compact
|
166
|
+
return nil if filtered.empty?
|
167
|
+
filtered.inject(:+).to_f / filtered.size
|
159
168
|
end
|
160
169
|
|
161
170
|
outlier = @outlier.next(val)
|
171
|
+
|
162
172
|
@outlier_buf.push outlier
|
163
173
|
@outlier_buf.shift if @outlier_buf.size > @smooth_term
|
164
|
-
|
174
|
+
outlier_avg = @outlier_buf.empty? ? 0.0 : @outlier_buf.inject(:+).to_f / @outlier_buf.size
|
175
|
+
|
176
|
+
score = @score.next(outlier_avg)
|
165
177
|
|
178
|
+
$log.debug "out_anomalydetect:#{Thread.current.object_id} flushed:#{flushed} val:#{val} outlier:#{outlier} outlier_buf:#{@outlier_buf} score:#{score}"
|
166
179
|
if @threshold < 0 or (@threshold >= 0 and score > @threshold)
|
167
180
|
{"outlier" => outlier, "score" => score, "target" => val}
|
168
181
|
else
|
@@ -185,6 +198,8 @@ module Fluent
|
|
185
198
|
push_records records
|
186
199
|
|
187
200
|
chain.next
|
201
|
+
rescue => e
|
202
|
+
$log.warn "anomalydetect: #{e.class} #{e.message} #{e.backtrace.first}"
|
188
203
|
end
|
189
204
|
end
|
190
205
|
end
|
@@ -108,8 +108,7 @@ class AnomalyDetectOutputTest < Test::Unit::TestCase
|
|
108
108
|
smooth_term 3
|
109
109
|
]
|
110
110
|
|
111
|
-
data =
|
112
|
-
data.push 0
|
111
|
+
data = 10.times.map { (rand * 100).to_i } + [0]
|
113
112
|
d.run do
|
114
113
|
data.each do |val|
|
115
114
|
(0..val - 1).each do ||
|
@@ -121,6 +120,49 @@ class AnomalyDetectOutputTest < Test::Unit::TestCase
|
|
121
120
|
end
|
122
121
|
end
|
123
122
|
|
123
|
+
def test_emit_target
|
124
|
+
d = create_driver %[
|
125
|
+
tag test.anomaly
|
126
|
+
outlier_term 28
|
127
|
+
outlier_discount 0.05
|
128
|
+
score_term 28
|
129
|
+
score_discount 0.05
|
130
|
+
tick 10
|
131
|
+
smooth_term 3
|
132
|
+
target y
|
133
|
+
]
|
134
|
+
|
135
|
+
data = 10.times.map { (rand * 100).to_i } + [0]
|
136
|
+
d.run do
|
137
|
+
data.each do |val|
|
138
|
+
d.emit({'y' => val})
|
139
|
+
r = d.instance.flush
|
140
|
+
assert_equal val, r['target']
|
141
|
+
end
|
142
|
+
end
|
143
|
+
end
|
144
|
+
|
145
|
+
def test_emit_when_target_does_not_exist
|
146
|
+
d = create_driver %[
|
147
|
+
tag test.anomaly
|
148
|
+
outlier_term 28
|
149
|
+
outlier_discount 0.05
|
150
|
+
score_term 28
|
151
|
+
score_discount 0.05
|
152
|
+
tick 10
|
153
|
+
smooth_term 3
|
154
|
+
target y
|
155
|
+
]
|
156
|
+
|
157
|
+
d.run do
|
158
|
+
10.times do
|
159
|
+
d.emit({'foobar' => 999.99})
|
160
|
+
r = d.instance.flush
|
161
|
+
assert_equal nil, r
|
162
|
+
end
|
163
|
+
end
|
164
|
+
end
|
165
|
+
|
124
166
|
def test_emit_stock_data
|
125
167
|
require 'csv'
|
126
168
|
reader = CSV.open("test/stock.2432.csv", "r")
|
metadata
CHANGED
@@ -1,46 +1,41 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: fluent-plugin-anomalydetect
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
5
|
-
prerelease:
|
4
|
+
version: 0.1.1
|
6
5
|
platform: ruby
|
7
6
|
authors:
|
8
7
|
- Muddy Dixon
|
9
8
|
autorequire:
|
10
9
|
bindir: bin
|
11
10
|
cert_chain: []
|
12
|
-
date: 2013-
|
11
|
+
date: 2013-08-09 00:00:00.000000000 Z
|
13
12
|
dependencies:
|
14
13
|
- !ruby/object:Gem::Dependency
|
15
|
-
name:
|
14
|
+
name: rake
|
16
15
|
requirement: !ruby/object:Gem::Requirement
|
17
|
-
none: false
|
18
16
|
requirements:
|
19
|
-
- -
|
17
|
+
- - '>='
|
20
18
|
- !ruby/object:Gem::Version
|
21
19
|
version: '0'
|
22
20
|
type: :development
|
23
21
|
prerelease: false
|
24
22
|
version_requirements: !ruby/object:Gem::Requirement
|
25
|
-
none: false
|
26
23
|
requirements:
|
27
|
-
- -
|
24
|
+
- - '>='
|
28
25
|
- !ruby/object:Gem::Version
|
29
26
|
version: '0'
|
30
27
|
- !ruby/object:Gem::Dependency
|
31
28
|
name: fluentd
|
32
29
|
requirement: !ruby/object:Gem::Requirement
|
33
|
-
none: false
|
34
30
|
requirements:
|
35
|
-
- -
|
31
|
+
- - '>='
|
36
32
|
- !ruby/object:Gem::Version
|
37
33
|
version: '0'
|
38
34
|
type: :runtime
|
39
35
|
prerelease: false
|
40
36
|
version_requirements: !ruby/object:Gem::Requirement
|
41
|
-
none: false
|
42
37
|
requirements:
|
43
|
-
- -
|
38
|
+
- - '>='
|
44
39
|
- !ruby/object:Gem::Version
|
45
40
|
version: '0'
|
46
41
|
description: detect anomal sequential input casually
|
@@ -62,30 +57,28 @@ files:
|
|
62
57
|
- test/stock.2432.csv
|
63
58
|
homepage: https://github.com/muddydixon/fluent-plugin-anomalydetect
|
64
59
|
licenses: []
|
60
|
+
metadata: {}
|
65
61
|
post_install_message:
|
66
62
|
rdoc_options: []
|
67
63
|
require_paths:
|
68
64
|
- lib
|
69
65
|
required_ruby_version: !ruby/object:Gem::Requirement
|
70
|
-
none: false
|
71
66
|
requirements:
|
72
|
-
- -
|
67
|
+
- - '>='
|
73
68
|
- !ruby/object:Gem::Version
|
74
69
|
version: '0'
|
75
70
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
76
|
-
none: false
|
77
71
|
requirements:
|
78
|
-
- -
|
72
|
+
- - '>='
|
79
73
|
- !ruby/object:Gem::Version
|
80
74
|
version: '0'
|
81
75
|
requirements: []
|
82
76
|
rubyforge_project:
|
83
|
-
rubygems_version:
|
77
|
+
rubygems_version: 2.0.0
|
84
78
|
signing_key:
|
85
|
-
specification_version:
|
79
|
+
specification_version: 4
|
86
80
|
summary: detect anomal sequential input casually
|
87
81
|
test_files:
|
88
82
|
- test/helper.rb
|
89
83
|
- test/plugin/test_out_anomalydetect.rb
|
90
84
|
- test/stock.2432.csv
|
91
|
-
has_rdoc:
|