fluent-plugin-anomalydetect 0.1.0 → 0.1.1
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 +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:
|