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 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.0"
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 "fluentd"
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
- score(prob xt, @sigma, x)
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(0.0) { |sum, v| sum += v } / (_end - _begin)
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
- require 'fluent/plugin/change_finder'
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
- sleep 0.5
133
- now = Fluent::Engine.now
134
- if now - @last_checked >= @tick
135
- flush_emit(now - @last_checked)
136
- @last_checked = now
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.inject(0.0) { |sum, record| sum += record[@target].to_f if record[@target] } / flushed.size
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
- score = @score.next(@outlier_buf.inject(0) { |sum, v| sum += v } / @outlier_buf.size)
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 = (0..10).map do || (rand * 100).to_i end
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.0
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-01-21 00:00:00.000000000 Z
11
+ date: 2013-08-09 00:00:00.000000000 Z
13
12
  dependencies:
14
13
  - !ruby/object:Gem::Dependency
15
- name: fluentd
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: 1.8.24
77
+ rubygems_version: 2.0.0
84
78
  signing_key:
85
- specification_version: 3
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: